blob: 276142d6b9bebe596200c6df05f2fb1d4ca82e17 [file] [log] [blame]
Yegor Timoshenkoc2e49412018-10-07 01:58:27 +00001#!/usr/bin/env bash
Patrick Georgi40032832016-10-24 11:58:07 +02002
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 Georgi519c4b72016-09-22 12:46:45 +020014# $0 from-branch to-branch
Patrick Georgi40032832016-10-24 11:58:07 +020015#
Patrick Georgi519c4b72016-09-22 12:46:45 +020016# applies all commits that from-branch has over to-branch,
17# based on a common ancestor and gerrit meta-data
18from=$1
19to=$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 Georgi8f8668d2017-06-13 15:14:09 +020030match_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.
45custom_root='^Gerrit-Rebase-Ignore-CLs-Before:'
Patrick Georgi519c4b72016-09-22 12:46:45 +020046
47# fetch common ancestor
48common_base=$(git merge-base ${from} ${to} 2>/dev/null)
49
50if [ -z "${common_base}" ]; then
51 echo \"${from}\" or \"${to}\" is not a valid branch name.
52 exit 1
53fi
54
Patrick Georgi8f8668d2017-06-13 15:14:09 +020055from_base=$common_base
56
57# fetch custom root ID
58croot_marker=$(git log --pretty=%b -1 --grep "${custom_root}" \
59 ${common_base}..${to} | \
60 grep "${custom_root}" | cut -d: -f2-)
61if [ -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)
66fi
67
Patrick Georgi519c4b72016-09-22 12:46:45 +020068# collect matches that are present on the target side
69to_matches="$(git log ${common_base}..${to} | \
Patrick Georgi8f8668d2017-06-13 15:14:09 +020070 grep "^ ${match_string}" | \
Patrick Georgi519c4b72016-09-22 12:46:45 +020071 cut -d: -f2-)"
72
73# start rebase process, but fail immediately by enforcing an invalid todo
74GIT_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
79commit=""
Patrick Georgi8f8668d2017-06-13 15:14:09 +020080(git log --reverse ${from_base}..${from} | \
81 grep -E "(^commit [0-9a-f]{40}\$|^ ${match_string})"; \
Patrick Georgi519c4b72016-09-22 12:46:45 +020082 echo "commit") | \
83while 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
96done | GIT_SEQUENCE_EDITOR="cat >" git rebase --edit-todo
97
98# allow user to edit todo
99git rebase --edit-todo
100
101# start processing todo to mimick git rebase -i behavior
102git rebase --continue