Yegor Timoshenko | c2e4941 | 2018-10-07 01:58:27 +0000 | [diff] [blame] | 1 | #!/usr/bin/env bash |
Patrick Georgi | 4003283 | 2016-10-24 11:58:07 +0200 | [diff] [blame] | 2 | |
| 3 | # Copyright 2016 Google Inc. |
| 4 | # |
| 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. |
| 8 | # |
| 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. |
| 13 | |
Patrick Georgi | 519c4b7 | 2016-09-22 12:46:45 +0200 | [diff] [blame] | 14 | # $0 from-branch to-branch |
Patrick Georgi | 4003283 | 2016-10-24 11:58:07 +0200 | [diff] [blame] | 15 | # |
Patrick Georgi | 519c4b7 | 2016-09-22 12:46:45 +0200 | [diff] [blame] | 16 | # applies all commits that from-branch has over to-branch, |
| 17 | # based on a common ancestor and gerrit meta-data |
| 18 | from=$1 |
| 19 | to=$2 |
| 20 | |
| 21 | # match string: this is the git commit line that is used to |
| 22 | # identify commits that were already copied over. |
| 23 | # |
| 24 | # Must not contain spaces except for leading and trailing. |
| 25 | # |
| 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 |
| 29 | # on that system. |
Patrick Georgi | 8f8668d | 2017-06-13 15:14:09 +0200 | [diff] [blame] | 30 | match_string='[-A-Za-z]*[Rr]eviewed-on:' |
| 31 | |
| 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. |
| 35 | # |
| 36 | # Use: |
| 37 | # To the commit message of a commit on the "to" side, add |
| 38 | # $custom_root: match_string (the part coming after $match_string) |
| 39 | # |
| 40 | # For a $match_string of ~ "Reviewed-on: " this might |
| 41 | # be "$custom_root: https://example.com/12345" |
| 42 | # |
| 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:' |
Patrick Georgi | 519c4b7 | 2016-09-22 12:46:45 +0200 | [diff] [blame] | 46 | |
| 47 | # fetch common ancestor |
| 48 | common_base=$(git merge-base ${from} ${to} 2>/dev/null) |
| 49 | |
| 50 | if [ -z "${common_base}" ]; then |
| 51 | echo \"${from}\" or \"${to}\" is not a valid branch name. |
| 52 | exit 1 |
| 53 | fi |
| 54 | |
Patrick Georgi | 8f8668d | 2017-06-13 15:14:09 +0200 | [diff] [blame] | 55 | from_base=$common_base |
| 56 | |
| 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 |
| 62 | from_base=$( ( \ |
| 63 | git log --pretty=%H -1 \ |
| 64 | --grep "^${match_string}${croot_marker}" \ |
| 65 | ${from_base}..${from}; echo ${from_base} )| head -1) |
| 66 | fi |
| 67 | |
Patrick Georgi | 519c4b7 | 2016-09-22 12:46:45 +0200 | [diff] [blame] | 68 | # collect matches that are present on the target side |
| 69 | to_matches="$(git log ${common_base}..${to} | \ |
Patrick Georgi | 8f8668d | 2017-06-13 15:14:09 +0200 | [diff] [blame] | 70 | grep "^ ${match_string}" | \ |
Patrick Georgi | 519c4b7 | 2016-09-22 12:46:45 +0200 | [diff] [blame] | 71 | cut -d: -f2-)" |
| 72 | |
| 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 |
| 76 | |
| 77 | # write new rebase todo |
| 78 | # the appended "commit" line triggers handling of the last log entry |
| 79 | commit="" |
Patrick Georgi | 8f8668d | 2017-06-13 15:14:09 +0200 | [diff] [blame] | 80 | (git log --reverse ${from_base}..${from} | \ |
| 81 | grep -E "(^commit [0-9a-f]{40}\$|^ ${match_string})"; \ |
Patrick Georgi | 519c4b7 | 2016-09-22 12:46:45 +0200 | [diff] [blame] | 82 | echo "commit") | \ |
| 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} |
| 87 | fi |
| 88 | commit="${value}" |
| 89 | else |
| 90 | # if value was already found on the "to" side, skip this |
| 91 | # commit |
| 92 | if [[ ${to_matches} == *"${value}"* ]]; then |
| 93 | commit="" |
| 94 | fi |
| 95 | fi |
| 96 | done | GIT_SEQUENCE_EDITOR="cat >" git rebase --edit-todo |
| 97 | |
| 98 | # allow user to edit todo |
| 99 | git rebase --edit-todo |
| 100 | |
| 101 | # start processing todo to mimick git rebase -i behavior |
| 102 | git rebase --continue |