Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I limit the log to all the descendants of a given commit?

Tags:

git

git-log

Given the history

      X-Y    <- feature
     /
A-B-C-D-E    <- master

I want to get the descendants of a given commit. One solution seems to be:

git log --all --ancestry-path <ref>^!

However, the behaviour is a bit strange:

  • when <rev> is C, the result is CDEXY
  • when <rev> is D or X, the result is DEXY (weird!)
  • when <rev> is E, the result is E

My understanding is that the command does not get all children of <rev>; instead, it gets all children of parent-of(ref). Am I right?

This is unintuitive, error prone, and, quite frankly, irritating. How command should I run instead, in order to limit the log to the descendants of a given commit.

like image 240
Henning Avatar asked Jul 07 '15 09:07

Henning


People also ask

How do I limit a git log?

By Amount. The most basic filtering option for git log is to limit the number of commits that are displayed. When you're only interested in the last few commits, this saves you the trouble of viewing all the commits in a page. You can limit git log 's output by including the - option.

What is the git command to view all the commits made by a specific person?

The git log command displays all of the commits in a repository's history.

How do you find a list of files that have been changed in a particular commit write a syntax?

Find what file changed in a commit To find out which files changed in a given commit, use the git log --raw command. It's the fastest and simplest way to get insight into which files a commit affects.

What is the git command to see all the changes since the last commit?

By default git diff will show you any uncommitted changes since the last commit.


1 Answers

How to limit the log to all the descendants of a given revision

As far as I know, there is no built-in Git command to do that. However, you're almost there. Try

git log --all --ancestry-path ^<rev>

instead. That should limit the log to the descendants of <rev>; note that, strictly speaking, <rev> is not a child of itself, so it doesn't get listed.

For instance, in my toy repo (I replicated yours; see the bottom of my answer),

git log --all --ancestry-path ^D

limits the log to commit E, and

git log --all --ancestry-path ^X

limits the log to commit Y.

What's wrong with git log --all --ancestry-path D^!?

TL; DR

My understanding is that the command does not get all children of <rev>; instead, it gets all children of parent-of(ref). Am I right?

Yes; your bottom commit is off by one.

Details

Because, in your example, commits D and X are symmetric, let's just focus on commit D and deconstruct the command

git log --all --ancestry-path D^!

According to the relevant Git man page,

A suffix ^ followed by an exclamation mark is the same as giving commit <rev> and then all its parents prefixed with ^ to exclude them (and their ancestors).

Furthermore, according to the git-log man page,

--all

Pretend as if all the refs in refs/ are listed on the command line as <commit>.

Therefore, in your case

git log --all --ancestry-path D^!

is equivalent to

git log --ancestry-path D ^C feature master

Moreover, because D is reachable from master, the latter command reduces to

git log --ancestry-path ^C feature master

This gives a log of all the commits reachable from either feature or master, but excluding C or any of its ancestors, and you get commits D, E, X, and Y.

As you can see, your bottom commit is off by one. What you really want to run

git log --ancestry-path ^D feature master

which is the same as

git log --all --ancestry-path ^D

Test

The following commands recreate your toy repo:

$ mkdir gittest
$ cd gittest/
$ git init

$ printf "A\n" > README
$ git add README
$ git commit -m "A"

$ printf "B\n" >> README
$ git commit -am "B"

$ printf "C\n" >> README
$ git commit -am "C"

$ git branch feature

$ printf "D\n" >> README
$ git commit -am "D"

$ printf "E\n" >> README
$ git commit -am "E"

$ git checkout feature
$ printf "X\n" >> README
$ git commit -am "X"

$ printf "Y\n" >> README
$ git commit -am "Y"

$ git log --all --oneline --graph --decorate
* e234427 (HEAD -> feature) Y
* cf98c6b X
| * b3d493a (master) E
| * e2bb266 D
|/  
* dfe0267 C
* 0be7d42 B
* 674356e A

(Note that commits D and X can be referred to by their SHAs, or more simply, by master~ and feature~, respectively.)

The command you suggested (I've added the --oneline flag, to reduce the output) indeed does not limit the log to the descendants of the given commit:

# master~ = D
$ git log --all --ancestry-path --oneline master~^!
e234427 Y
cf98c6b X
b3d493a E
e2bb266 D

# feature~ == X
$ git log --all --ancestry-path --oneline feature~^!
e234427 Y
cf98c6b X
b3d493a E
e2bb266 D

but the one I suggest does:

# master~ == D
$ git log --all --ancestry-path --oneline ^master~
b3d493a E

# feature~ == X
$ git log --all --ancestry-path --oneline ^feature~
e234427 Y
like image 111
jub0bs Avatar answered Sep 21 '22 15:09

jub0bs