27

I was wondering whether there is an efficient way to retrieve the children of a given commit. Although a method was discussed in Referencing the child of a commit in Git, it is very inefficient.

I thought this would be a straightforward thing to be done in Git, but apparently, it is not.

codeforester
  • 39,467
  • 16
  • 112
  • 140
leco
  • 1,989
  • 16
  • 30

2 Answers2

32

git rev-list can show children, but these children have to be reachable from the commits you provide. Assuming you want to show all children reachable from all branches in your repo, you can use something like

git rev-list --all --not $COMMIT^@ --children | grep "^$COMMIT"

This should output a line that looks like

$COMMIT $child1 $child2 $child3 ...

For convenience, you can add turn the command into a git alias by adding the following line to the [alias] section of your ~/.gitconfig:

children = "!f() { git rev-list --all --not $1^@ --children | grep $(git rev-parse $1); }; f" # reachable children of a ref

The syntax $COMMIT^@ might be confusing, so I'll explain it. Hopefully $COMMIT is self-explanatory. This is then followed by ^@, which expands to all parents of the referenced commit. So $COMMIT^@ means "all parents of $COMMIT". Since this follows the --not flag, this instructs rev-list to stop processing after it hits any parent of $COMMIT. This is basically just an optimization, because any commit reachable from $COMMIT cannot possibly be a child.


Note: a previous version of this answer said tail -1 instead of grep "^$COMMIT". This may work in a simple test repo (which is why I initially said it), but there's no guarantee that git rev-list will emit $COMMIT last, if you have any branches that do not contain $COMMIT.

northben
  • 5,448
  • 4
  • 35
  • 47
Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
  • What does "reachable" mean? – HelloGoodbye Jul 02 '15 at 09:19
  • @HelloGoodbye "reachable" means if you start from a given commit, and you walk up all parents of that commit, and then walk up all parents of those commits, etc., you'll eventually hit the commit in question. – Lily Ballard Jul 02 '15 at 21:45
  • Why traversing the parents but not the children? – HelloGoodbye Jul 03 '15 at 08:16
  • 2
    @HelloGoodbye Because that's not what "reachable" means. Also, parents don't have any direct link to their children. Every commit embeds the SHA of its parents in the commit object, which is used to calculate the SHA of the commit. By definition, a commit cannot embed its children, and children are not reachable from parents. – Lily Ballard Jul 05 '15 at 01:09
  • I [tweaked this](http://stackoverflow.com/a/39565190/5353461) to not require an argument (uses HEAD in the default case), and to print only the children without the parent. – Tom Hale Sep 19 '16 at 04:06
2

I use the following alias (based on @Lily Ballard's answer:

# Get all children of current or specified commit-ish
children = "!bash -c 'c=${1:-HEAD}; set -- $(git rev-list --all --not \"$c\"^@ --children | grep $(git rev-parse \"$c\") ); shift; echo $*' -"
  • With no argument - gives all children of HEAD
  • With a commit-ish - gives all children of that object
Søren Løvborg
  • 8,354
  • 2
  • 47
  • 40
Tom Hale
  • 40,825
  • 36
  • 187
  • 242