1#!/usr/bin/env bash 2set -euo pipefail 3 4if (( $# < 1 )); then 5 echo "Usage: $0 TARGET_BRANCH" 6 echo "" 7 echo "TARGET_BRANCH: Branch to rebase the current branch onto, e.g. master or release-24.11" 8 exit 1 9fi 10 11targetBranch=$1 12 13# Loop through all autorebase-able commits in .git-blame-ignore-revs on the base branch 14readarray -t autoLines < <( 15 git show "$targetBranch":.git-blame-ignore-revs \ 16 | sed -n 's/^\([0-9a-f]\+\).*!autorebase \(.*\)$/\1 \2/p' 17) 18for line in "${autoLines[@]}"; do 19 read -r autoCommit autoCmd <<< "$line" 20 21 if ! git cat-file -e "$autoCommit"; then 22 echo "Not a valid commit: $autoCommit" 23 exit 1 24 elif git merge-base --is-ancestor "$autoCommit" HEAD; then 25 # Skip commits that we have already 26 continue 27 fi 28 29 echo -e "\e[32mAuto-rebasing commit $autoCommit with command '$autoCmd'\e[0m" 30 31 # The commit before the commit 32 parent=$(git rev-parse "$autoCommit"~) 33 34 echo "Rebasing on top of the previous commit, might need to manually resolve conflicts" 35 if ! git rebase --onto "$parent" "$(git merge-base "$targetBranch" HEAD)"; then 36 echo -e "\e[33m\e[1mRestart this script after resolving the merge conflict as described above\e[0m" 37 exit 1 38 fi 39 40 echo "Reapplying the commit on each commit of our branch" 41 # This does two things: 42 # - The parent filter inserts the auto commit between its parent and 43 # and our first commit. By itself, this causes our first commit to 44 # effectively "undo" the auto commit, since the tree of our first 45 # commit is unchanged. This is why the following is also necessary: 46 # - The tree filter runs the command on each of our own commits, 47 # effectively reapplying it. 48 FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch \ 49 --parent-filter "sed 's/$parent/$autoCommit/'" \ 50 --tree-filter "$autoCmd" \ 51 "$autoCommit"..HEAD 52 53 # A tempting alternative is something along the lines of 54 # git rebase --strategy-option=theirs --onto "$rev" "$parent" \ 55 # --exec '$autoCmd && git commit --all --amend --no-edit' \ 56 # but this causes problems because merges are not guaranteed to maintain the formatting. 57 # The ./test.sh exercises such a case. 58done 59 60echo "Rebasing on top of the latest target branch commit" 61git rebase --onto "$targetBranch" "$(git merge-base "$targetBranch" HEAD)"