Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I tell if one commit is a descendant of another commit?

Tags:

git

From Git 1.8.0, this is supported as an option to merge-base:

git merge-base --is-ancestor <maybe-ancestor-commit> <descendant-commit>

From the man page:

--is-ancestor

Check if the first is an ancestor of the second , and exit with status 0 if true, or with status 1 if not. Errors are signaled by a non-zero status that is not 1.

For example:

git merge-base --is-ancestor origin/master master; echo $?

If you want to check this programmatically (e.g. in script), you can check if git merge-base A B is equal to git rev-parse --verify A (then A is reachable from B), or if it is git rev-parse --verify B (then B is reachable from A). git rev-parse is here needed to convert from commit name to commit SHA-1 / commit id.

Using git rev-list like in VonC answer is also possibility.

Edit: in modern Git there is explicit support for this query in the form of git merge-base --is-ancestor.


If one of commits you are asking about is a branch tip, then git branch --contains <commit> or git branch --merged <commit> might be better non-programmatic solution.


This kind of operations relies on the notion of range of revisions detailed in the SO question: "Difference in ‘git log origin/master’ vs ‘git log origin/master..’".

git rev-list should be able to walk back from a commit, up until another if reachable.

So I would try:

git rev-list --boundary 85e54e2408..0815fcf18a
0815fcf18a19441c1c26fc3495c4047cf59a06b9
8a1658147a460a0230fb1990f0bc61130ab624b2
-85e54e240836e6efb46978e4a1780f0b45516b20

(Boundary commits are prefixed with -)

If the last commit displayed is the same than the first commit in the git rev-list command, then it is a commit reachable from the second commit.

If the first commit is not reachable from the second, git rev-list should return nothing.

git rev-list --boundary A..B

would finish by A, if A is reachable from B.
It is the same as:

git rev-list --boundary B --not A

,with B a positive reference, and A a negative reference.
It will starts at B and walks back through the graph until it encounters a revision that is reachable from A.
I would argue that if A is directly reachable from B, it will encounter (and display, because of the --boundary option) A itself.


Another way would be to use git log and grep.

git log --pretty=format:%H abc123 | grep def456

This will produce one line of output if commit def456 is an ancestor of commit abc123, or no output otherwise.

You can usually get away with omitting the --pretty argument, but it is needed if you want to make sure that you only search through actual commit hashes and not through log comments and so on.