xref: /aosp_15_r20/external/coreboot/util/scripts/gerrit-rebase (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1#!/usr/bin/env bash
2# SPDX-License-Identifier: GPL-2.0-only
3
4# $0 from-branch to-branch
5#
6# applies all commits that from-branch has over to-branch,
7# based on a common ancestor and gerrit meta-data
8from=$1
9to=$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.
20match_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.
35custom_root='^Gerrit-Rebase-Ignore-CLs-Before:'
36
37# fetch common ancestor
38common_base=$(git merge-base ${from} ${to} 2>/dev/null)
39
40if [ -z "${common_base}" ]; then
41	echo \"${from}\" or \"${to}\" is not a valid branch name.
42	exit 1
43fi
44
45from_base=$common_base
46
47# fetch custom root ID
48croot_marker=$(git log --pretty=%b -1 --grep "${custom_root}" \
49	${common_base}..${to} | \
50	grep "${custom_root}" | cut -d: -f2-)
51if [ -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)
56fi
57
58# collect matches that are present on the target side
59to_matches="$(git log ${common_base}..${to} | \
60	grep "^    ${match_string}" | \
61	cut -d: -f2-)"
62
63# start rebase process, but fail immediately by enforcing an invalid todo
64GIT_SEQUENCE_EDITOR="echo 'Ignore this error, it works around a git-rebase limitation'>" \
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
69commit=""
70(git log --reverse ${from_base}..${from} | \
71	grep -E "(^commit [0-9a-f]{40}\$|^    ${match_string})"; \
72	echo "commit") | \
73while 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
86done | GIT_SEQUENCE_EDITOR="cat >" git rebase --edit-todo
87
88# allow user to edit todo
89git rebase --edit-todo
90
91# start processing todo to mimick git rebase -i behavior
92git rebase --continue
93