Reputation: 1
I have a large git repo with many branches (normally, I work only with a small subset of them, related to features owned by my team). Suppose I have a hash of a commit (e.g. copied from a pull request). This commit might have been merged into one of the branches I am interested in, or not. It might have been merged directly, or cherry-picked or rebased. In the two latter cases its hash in the log is different (because it is now actually a brand new commit, although with the same diff).
Provided that I know the hash of the original commit, how can I find all branches that contain it directly or contain it either in cherry-picked or rebased form?
Upvotes: 0
Views: 261
Reputation: 489698
Provided that I know the hash of the original commit, how can I find all branches that contain it directly or contain it either in cherry-picked or rebased form?
The good news is that you can definitely do all of this (under an assumption or two I will describe in a moment). The "contain it directly" variant is the easiest: git branch --contains hash
produces the answer.
The "or ... in cherry-picked or rebase form part is the one that requires an assumption, and the bad news is that the "how" part might be pretty messy. Or it might be really easy!
There is a Git command, git patch-id
, that takes git diff
output (as produced directly by git diff
, or more conveniently by git show
) and computes a patch ID, which is essentially a checksum of the diff after stripping out line numbers and white-space. (For more, see the linked documentation page, or run git help patch-id
.) For instance:
$ git show HEAD | git patch-id
869f23f0e8b4813c88cb853fa2b4d415d25dc32c 8dca754b1e874719a732bc9ab7b0e14b21b1bc10
The second hash, if a second one appears, is the hash ID of the commit:
$ git rev-parse HEAD
8dca754b1e874719a732bc9ab7b0e14b21b1bc10
Hence, you can run git show
on the original commit and get its patch ID, then run git show
on each suspect commit and see if the patch ID matches.
That's the hard way (but easy enough to do for a single commit). The easy way is to have Git tell you about "equal commits". The git cherry
command, and its more flexible but harder to use companion git rev-list --cherry-mark
, work by running git show | git patch-id
on each commit in some set of commits, and git show | git patch-id
on each commit in another set of commits, and telling you which commits in the first set match which ones in the second set.
To use git rev-list
, you must choose a symmetric difference operation, i.e., use the A...B
syntax with the three dots. Git will compute the patch IDs of all the commits that are reachable from A but not B, and compare them to all the patch IDs of all the commits that are reachable from B but not A. The git cherry
command does much the same thing but with a different syntax and different output format. Consult the two documentation pages for details.
The caveat is pretty obvious: the patch-ID is based on the diff minus line numbers and (some) white-space. But sometimes during cherry-pick or rebase, the diff has to be changed to make it fit. In this case, the patch IDs will not match, and this kind of detection will fail. There is not much you can do about this.
Upvotes: 2