Skip to main content
blog.philz.dev

Git Virtual Cherry-Pick

Diagram: a virtual three-way merge grafts commit c onto origin/main. On the left, "one commit in a stack": c is added (green) while the intervening commits are subtracted (red). On the right, "diverged branches": c and main's commits are added, and the feature content main lacks is subtracted.

Thing I learned recently from Josh Bleecher Snyder: there are enough git plumbing commands now that you can produce a commit object that's the same as "checkout origin/main and cherry-pick one-commit" with just plumbing, without creating worktrees, using temporary indexes, and so on.

The magic incantation is (where c is the commit to cherry-pick):

GIT_AUTHOR_NAME=$(git log -1 --format=%an c) \
GIT_AUTHOR_EMAIL=$(git log -1 --format=%ae c) \
GIT_AUTHOR_DATE=$(git log -1 --format=%aI c) \
git commit-tree \
  "$(git merge-tree --write-tree --no-messages --merge-base c^ origin/main c)" \
  -p origin/main \
  -F <(git log -1 --format=%B c)

That's a mouthful, but the core trick is specifying merge-base. Whereas I typically think of this cherry-pick as applying a single patch onto origin/main, you can also start with c's parent, subtract some commits to get back to origin/main and then add on c's patch.

This is handy if you haven't yet internalized jj and want to push one commit in a stack.

For more git tricks, see also: