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