3 # Copyright 2016 Google Inc.
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; version 2 of the License.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # $0 from-branch to-branch
16 # applies all commits that from-branch has over to-branch,
17 # based on a common ancestor and gerrit meta-data
21 # match string: this is the git commit line that is used to
22 # identify commits that were already copied over.
24 # Must not contain spaces except for leading and trailing.
26 # The first pick was Change-Id, but it was lost too often,
27 # so go for Reviewed-on instead. It's also unique because it
28 # contains the gerrit instance's host name and the change's number
30 match_string
='[-A-Za-z]*[Rr]eviewed-on:'
32 # Custom root: allow a certain CL (identified by matching either side's
33 # match_string) to be the new root, instead of using git's common history only.
34 # This allows cutting down on commits that are re-evaluated on every run.
37 # To the commit message of a commit on the "to" side, add
38 # $custom_root: match_string (the part coming after $match_string)
40 # For a $match_string of ~ "Reviewed-on: " this might
41 # be "$custom_root: https://example.com/12345"
43 # On traversal, the commit with "$match_string: https://example.com/12345"
44 # is then considered a base commit.
45 custom_root
='^Gerrit-Rebase-Ignore-CLs-Before:'
47 # fetch common ancestor
48 common_base
=$
(git merge-base
${from} ${to} 2>/dev
/null
)
50 if [ -z "${common_base}" ]; then
51 echo \"${from}\" or
\"${to}\" is not a valid branch name.
55 from_base
=$common_base
57 # fetch custom root ID
58 croot_marker
=$
(git log
--pretty=%b
-1 --grep "${custom_root}" \
59 ${common_base}..
${to} | \
60 grep "${custom_root}" | cut
-d: -f2-)
61 if [ -n "${croot_marker}" ]; then
63 git log
--pretty=%H
-1 \
64 --grep "^${match_string}${croot_marker}" \
65 ${from_base}..${from}; echo ${from_base} )|
head -1)
68 # collect matches that are present on the target side
69 to_matches
="$(git log ${common_base}..${to} | \
70 grep "^
${match_string}" | \
73 # start rebase process, but fail immediately by enforcing an invalid todo
74 GIT_SEQUENCE_EDITOR
="echo foo >" \
75 git rebase
-i --onto ${to} ${from} ${to} 2>/dev
/null
77 # write new rebase todo
78 # the appended "commit" line triggers handling of the last log entry
80 (git log
--reverse ${from_base}..
${from} | \
81 grep -E "(^commit [0-9a-f]{40}\$|^ ${match_string})"; \
83 while read key value
; do
84 if [ "${key}" = "commit" ]; then
85 if [ -n "${commit}" ]; then
86 git log
-n 1 --pretty="pick %h %s" ${commit}
90 # if value was already found on the "to" side, skip this
92 if [[ ${to_matches} == *"${value}"* ]]; then
96 done | GIT_SEQUENCE_EDITOR
="cat >" git rebase
--edit-todo
98 # allow user to edit todo
99 git rebase
--edit-todo
101 # start processing todo to mimick git rebase -i behavior
102 git rebase
--continue