103 lines
3.3 KiB
Bash
Executable File
103 lines
3.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Copyright 2016 Google Inc.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; version 2 of the License.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
|
|
# $0 from-branch to-branch
|
|
#
|
|
# applies all commits that from-branch has over to-branch,
|
|
# based on a common ancestor and gerrit meta-data
|
|
from=$1
|
|
to=$2
|
|
|
|
# match string: this is the git commit line that is used to
|
|
# identify commits that were already copied over.
|
|
#
|
|
# Must not contain spaces except for leading and trailing.
|
|
#
|
|
# The first pick was Change-Id, but it was lost too often,
|
|
# so go for Reviewed-on instead. It's also unique because it
|
|
# contains the gerrit instance's host name and the change's number
|
|
# on that system.
|
|
match_string='[-A-Za-z]*[Rr]eviewed-on:'
|
|
|
|
# Custom root: allow a certain CL (identified by matching either side's
|
|
# match_string) to be the new root, instead of using git's common history only.
|
|
# This allows cutting down on commits that are re-evaluated on every run.
|
|
#
|
|
# Use:
|
|
# To the commit message of a commit on the "to" side, add
|
|
# $custom_root: match_string (the part coming after $match_string)
|
|
#
|
|
# For a $match_string of ~ "Reviewed-on: " this might
|
|
# be "$custom_root: https://example.com/12345"
|
|
#
|
|
# On traversal, the commit with "$match_string: https://example.com/12345"
|
|
# is then considered a base commit.
|
|
custom_root='^Gerrit-Rebase-Ignore-CLs-Before:'
|
|
|
|
# fetch common ancestor
|
|
common_base=$(git merge-base ${from} ${to} 2>/dev/null)
|
|
|
|
if [ -z "${common_base}" ]; then
|
|
echo \"${from}\" or \"${to}\" is not a valid branch name.
|
|
exit 1
|
|
fi
|
|
|
|
from_base=$common_base
|
|
|
|
# fetch custom root ID
|
|
croot_marker=$(git log --pretty=%b -1 --grep "${custom_root}" \
|
|
${common_base}..${to} | \
|
|
grep "${custom_root}" | cut -d: -f2-)
|
|
if [ -n "${croot_marker}" ]; then
|
|
from_base=$( ( \
|
|
git log --pretty=%H -1 \
|
|
--grep "^${match_string}${croot_marker}" \
|
|
${from_base}..${from}; echo ${from_base} )| head -1)
|
|
fi
|
|
|
|
# collect matches that are present on the target side
|
|
to_matches="$(git log ${common_base}..${to} | \
|
|
grep "^ ${match_string}" | \
|
|
cut -d: -f2-)"
|
|
|
|
# start rebase process, but fail immediately by enforcing an invalid todo
|
|
GIT_SEQUENCE_EDITOR="echo 'Ignore this error, it works around a git-rebase limitation'>" \
|
|
git rebase -i --onto ${to} ${from} ${to} 2>/dev/null
|
|
|
|
# write new rebase todo
|
|
# the appended "commit" line triggers handling of the last log entry
|
|
commit=""
|
|
(git log --reverse ${from_base}..${from} | \
|
|
grep -E "(^commit [0-9a-f]{40}\$|^ ${match_string})"; \
|
|
echo "commit") | \
|
|
while read key value; do
|
|
if [ "${key}" = "commit" ]; then
|
|
if [ -n "${commit}" ]; then
|
|
git log -n 1 --pretty="pick %h %s" ${commit}
|
|
fi
|
|
commit="${value}"
|
|
else
|
|
# if value was already found on the "to" side, skip this
|
|
# commit
|
|
if [[ ${to_matches} == *"${value}"* ]]; then
|
|
commit=""
|
|
fi
|
|
fi
|
|
done | GIT_SEQUENCE_EDITOR="cat >" git rebase --edit-todo
|
|
|
|
# allow user to edit todo
|
|
git rebase --edit-todo
|
|
|
|
# start processing todo to mimick git rebase -i behavior
|
|
git rebase --continue
|