问题
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>isC, the result isCDEXY - when
<rev>isDorX, the result isDEXY(weird!) - when
<rev>isE, the result isE
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.
回答1:
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,
--allPretend 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
来源:https://stackoverflow.com/questions/31265126/how-can-i-limit-the-log-to-all-the-descendants-of-a-given-commit