Guru Das Srinagesh

Fun with git rebase --interactive

02 Dec 2023

This is the second in a series of blog posts related to some powerful features of git I've used over the years. Previously, I wrote about git rebase --onto which you can read here.

git offers many powerful features, and one such feature I have used extensively in kernel development is git rebase --interactive. Here is what the man page has to say about it:

-i, --interactive
    Make a list of the commits which are about to be rebased. Let the user edit that
    list before rebasing. This mode can also be used to split commits (see SPLITTING
    COMMITS below).

That this feature exists is impressive on its own: in the list of commits that appears, one can reorder commits, delete them, rework their commit messages, combine ("squash", "fixup") them, and even merge them in recent versions of git. But where this really shines is how it allows, via an optional flag, for executing any arbitrary shell command for one or even all of the commits in the list. I'm talking about git rebase --interactive --exec.

Usecase: Pick a bunch of linux-next commits

Frequently we run into the situation where we need some patches that are accepted by maintainers into their own trees but are not merged by Linus into his main repository. This occurs when a developer posts a patch to the mailing lists ("pushes a patch upstream") that is an urgent fix of some kind, required in a product kernel ("downstream"). The downstream kernel, such as the one the Android Open Source Project uses (the Android Common Kernel), usually has strict requirements of the commit messages of patches it accepts for merging. One of these requirements is:

If the patch is not merged from an upstream branch, the subject must be tagged with the type of patch: UPSTREAM:, BACKPORT:, FROMGIT:, FROMLIST:, or ANDROID:

Some projects may even require specific git-trailers 1 in the commit message, e.g.:

Git-commit: 26b4ca3c39012368ab22b06cd35dd6b77f0f3e00

This means every single patch being pushed to the downstream kernel project would need to be tagged this way. If we need to get, say, 10 or 20 patches merged from a maintainer's tree or linux-next, we need a scalable way of tagging all of them with one of the above subject prefixes and adding those git trailers. This is where git rebase --interactive --exec really shines.

From within the downstream kernel, add the linux-next remote:

git remote add linux-next
git fetch linux-next

Gather list of commits required. These may be abbreviated SHAs too, for convenience.

base_sha=$(git rev-parse HEAD)
pick_commits=(<sha1> <sha2> ... <shaN>)

Cherry pick each commit and add the first trailer:

for commit in "${pick_commits[@]}"; do
    git cherry-pick $commit
    git commit --amend --no-edit --trailer "Git-commit: $(git rev-parse $commit)"

Now, add the subject prefix FROMGIT: to all the commits that were just cherry picked:

git rebase -i --exec 'git commit --amend -m "FROMGIT: $(git show -s --format=%B)"' $base_sha

Next, add the second trailer to all the commits that were cherry picked:

git rebase -i --exec 'git commit --amend --no-edit --trailer "Git-repo:"' $base_sha

Finally, add one's own Signed-off-by to all the cherry picked commits:

git rebase -i --exec 'git commit --amend --no-edit -s' $base_sha
git fixup and git rebase --interactive --autosquash

This blog post: Auto-squashing Git Commits goes into great detail on what this feature is, and how it may be made use of.

  1. Git - git-interpret-trailers Documentation