Reputation: 2039
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.
Upvotes: 30
Views: 9205
Reputation: 1986
I started out using @Lily Ballard's answer too (and big thanks to Lily and others who edited that answer, it works well!). But after starting there, I hit on a slightly different way to do things that's working better (running faster) for me. Here's what you'd put into the [alias] section of your ~/.gitconfig:
children = "!f() { git branch --contains "$1" --format='%(refname:short)' | git rev-list --not "$1^@" --children --stdin | grep -F "$(git rev-parse $1)" ; }; f"
What this is doing:
git branch
command that says "find all branches that are descendants of the inputted ref".git rev-list
command. This command is very similar to the one in @Lily Ballard's answer, except that it doesn't use --all
, which makes git rev-list
consider all commits in the entire repo. Instead it takes the branch names from the first command via stdin, and only considers commits that are ancestors of those branches.In my personal testing, this function is running ~8x faster than the one in @Lily Ballard's answer (~0.07s as opposed to ~0.57s). But obviously, the speed differences will depend entirely on the total size of your repo and size/complexity of the subtree at the commit you're interested in.
Upvotes: 1
Reputation: 185671
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
.
Upvotes: 38
Reputation: 46775
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 $*' -"
HEAD
commit-ish
- gives all children of that objectUpvotes: 3