Yegor Timoshenko | c2e4941 | 2018-10-07 01:58:27 +0000 | [diff] [blame] | 1 | #!/usr/bin/env bash |
Patrick Georgi | 7333a11 | 2020-05-08 20:48:04 +0200 | [diff] [blame] | 2 | # SPDX-License-Identifier: GPL-2.0-only |
Patrick Georgi | 4003283 | 2016-10-24 11:58:07 +0200 | [diff] [blame] | 3 | |
Patrick Georgi | 519c4b7 | 2016-09-22 12:46:45 +0200 | [diff] [blame] | 4 | # $0 from-branch to-branch |
Patrick Georgi | 4003283 | 2016-10-24 11:58:07 +0200 | [diff] [blame] | 5 | # |
Patrick Georgi | 519c4b7 | 2016-09-22 12:46:45 +0200 | [diff] [blame] | 6 | # applies all commits that from-branch has over to-branch, |
| 7 | # based on a common ancestor and gerrit meta-data |
| 8 | from=$1 |
| 9 | to=$2 |
| 10 | |
| 11 | # match string: this is the git commit line that is used to |
| 12 | # identify commits that were already copied over. |
| 13 | # |
| 14 | # Must not contain spaces except for leading and trailing. |
| 15 | # |
| 16 | # The first pick was Change-Id, but it was lost too often, |
| 17 | # so go for Reviewed-on instead. It's also unique because it |
| 18 | # contains the gerrit instance's host name and the change's number |
| 19 | # on that system. |
Patrick Georgi | 8f8668d | 2017-06-13 15:14:09 +0200 | [diff] [blame] | 20 | match_string='[-A-Za-z]*[Rr]eviewed-on:' |
| 21 | |
| 22 | # Custom root: allow a certain CL (identified by matching either side's |
| 23 | # match_string) to be the new root, instead of using git's common history only. |
| 24 | # This allows cutting down on commits that are re-evaluated on every run. |
| 25 | # |
| 26 | # Use: |
| 27 | # To the commit message of a commit on the "to" side, add |
| 28 | # $custom_root: match_string (the part coming after $match_string) |
| 29 | # |
| 30 | # For a $match_string of ~ "Reviewed-on: " this might |
| 31 | # be "$custom_root: https://example.com/12345" |
| 32 | # |
| 33 | # On traversal, the commit with "$match_string: https://example.com/12345" |
| 34 | # is then considered a base commit. |
| 35 | custom_root='^Gerrit-Rebase-Ignore-CLs-Before:' |
Patrick Georgi | 519c4b7 | 2016-09-22 12:46:45 +0200 | [diff] [blame] | 36 | |
| 37 | # fetch common ancestor |
| 38 | common_base=$(git merge-base ${from} ${to} 2>/dev/null) |
| 39 | |
| 40 | if [ -z "${common_base}" ]; then |
| 41 | echo \"${from}\" or \"${to}\" is not a valid branch name. |
| 42 | exit 1 |
| 43 | fi |
| 44 | |
Patrick Georgi | 8f8668d | 2017-06-13 15:14:09 +0200 | [diff] [blame] | 45 | from_base=$common_base |
| 46 | |
| 47 | # fetch custom root ID |
| 48 | croot_marker=$(git log --pretty=%b -1 --grep "${custom_root}" \ |
| 49 | ${common_base}..${to} | \ |
| 50 | grep "${custom_root}" | cut -d: -f2-) |
| 51 | if [ -n "${croot_marker}" ]; then |
| 52 | from_base=$( ( \ |
| 53 | git log --pretty=%H -1 \ |
| 54 | --grep "^${match_string}${croot_marker}" \ |
| 55 | ${from_base}..${from}; echo ${from_base} )| head -1) |
| 56 | fi |
| 57 | |
Patrick Georgi | 519c4b7 | 2016-09-22 12:46:45 +0200 | [diff] [blame] | 58 | # collect matches that are present on the target side |
| 59 | to_matches="$(git log ${common_base}..${to} | \ |
Patrick Georgi | 8f8668d | 2017-06-13 15:14:09 +0200 | [diff] [blame] | 60 | grep "^ ${match_string}" | \ |
Patrick Georgi | 519c4b7 | 2016-09-22 12:46:45 +0200 | [diff] [blame] | 61 | cut -d: -f2-)" |
| 62 | |
| 63 | # start rebase process, but fail immediately by enforcing an invalid todo |
Patrick Georgi | 59e6f3c | 2020-03-09 10:15:39 +0100 | [diff] [blame] | 64 | GIT_SEQUENCE_EDITOR="echo 'Ignore this error, it works around a git-rebase limitation'>" \ |
Patrick Georgi | 519c4b7 | 2016-09-22 12:46:45 +0200 | [diff] [blame] | 65 | git rebase -i --onto ${to} ${from} ${to} 2>/dev/null |
| 66 | |
| 67 | # write new rebase todo |
| 68 | # the appended "commit" line triggers handling of the last log entry |
| 69 | commit="" |
Patrick Georgi | 8f8668d | 2017-06-13 15:14:09 +0200 | [diff] [blame] | 70 | (git log --reverse ${from_base}..${from} | \ |
| 71 | grep -E "(^commit [0-9a-f]{40}\$|^ ${match_string})"; \ |
Patrick Georgi | 519c4b7 | 2016-09-22 12:46:45 +0200 | [diff] [blame] | 72 | echo "commit") | \ |
| 73 | while read key value; do |
| 74 | if [ "${key}" = "commit" ]; then |
| 75 | if [ -n "${commit}" ]; then |
| 76 | git log -n 1 --pretty="pick %h %s" ${commit} |
| 77 | fi |
| 78 | commit="${value}" |
| 79 | else |
| 80 | # if value was already found on the "to" side, skip this |
| 81 | # commit |
| 82 | if [[ ${to_matches} == *"${value}"* ]]; then |
| 83 | commit="" |
| 84 | fi |
| 85 | fi |
| 86 | done | GIT_SEQUENCE_EDITOR="cat >" git rebase --edit-todo |
| 87 | |
| 88 | # allow user to edit todo |
| 89 | git rebase --edit-todo |
| 90 | |
| 91 | # start processing todo to mimick git rebase -i behavior |
| 92 | git rebase --continue |