Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine relationship between commits

Tags:

git

bash

In a linear history, two commits A and B can have one of three states:

  • A and B are the same commit
  • A strictly before B
  • B strictly before A

Git's nonlinear history allows for four additional options

  • A and B share both a parent and child
  • A and B share a parent but not a child
  • A and B share a child but not a parent
  • A and B share nothing

I'm not sure how to achieve the last two without using graft points, but it could happen.

What I'm unsure of how to accomplish is a determination of this relationship in a simple manner--at the moment I can only think of a somewhat odd rev-list and solution where one tests each case individually:

[[ "$a" = "$b" ]] && echo "same"
git rev-list "$b" | grep -q "$a" && echo "a before b"
git rev-list "$a" | grep -q "$b" && echo "b before a"
cat <(git rev-list "$a") <(git rev-list "$b") | sort | uniq -cd | grep -q 2 && echo "A and B share parents"
cat <(git rev-list --children "$a") <(git rev-list --children "$b") | sort | uniq -cd | grep -q 2 && echo "A and B share children"

There has to be a better, gittier way of doing this, so what is it?

like image 798
zebediah49 Avatar asked Jun 13 '13 16:06

zebediah49


1 Answers

Checking for parent-child relation is easy, use git merge-base. If the result is one of your commits, it's a parent, the other is a child.

I suggest ignoring the case when commits don't have a common ancestor. Sure, this can happen, but I'm not aware of any valid use-cases for this. I mean, you are solving a real problem for a real project, I'm sure you can assume that this won't happen. BTW, merge-base will exit with an error, and it's the only situation when a merge-base doesn't exist, so you still can detect this case.

Finding children for a commit is just impossible. Absoultely. You can't do that reliably. A commit has a “parent” reference, but it doesn't have “child” references. rev-list --children does a completely different thing, it has nothing to do with what you want. You’ll have to refine your problem.

What I can suggest instead is using git branch --contains <commit> or git tag --contains <commit> which will only list those branches/tags from which you can reach your commit.

Alternatively you can look at git for-each-ref that will allow you to test if a commit is reachable from any of your branches/tags.

like image 164
kirelagin Avatar answered Nov 03 '22 09:11

kirelagin