I am trying to use list all commits within a given range on a branch using the argument of git log
. For some reason it doesn't seem to be giving me the right result (or maybe I'm understanding the command wrong?).
Here's the steps for what I'm doing:
Clone the repo
git clone https://github.com/openstack/nova.git
Do git log
and these are the last 9 commits:
d5bde44 Merge "Make metadata password routines use Instance object"
6cbc9ee Merge "Fix object change detection"
39b7875 Merge "Fix object leak in nova.tests.objects.test_fields.TestObject"
94d1034 Merge "maint: correct docstring parameter description"
6407f17 Merge "Fix live_migration method's docstring"
7406661 Merge "Fix infinitely reschedule instance due to miss retry info"
9d8a34f Merge "Remove unused code from test_compute_cells"
429cd4b Fix object change detection
01381b8 Fix object leak in nova.tests.objects.test_fields.TestObject
...
Lets say I want to get all the commits starting after 01381b8
. I issue git log 01381b8..HEAD
and the following output is seen:
d5bde44 Merge "Make metadata password routines use Instance object"
6cbc9ee Merge "Fix object change detection"
39b7875 Merge "Fix object leak in nova.tests.objects.test_fields.TestObject"
94d1034 Merge "maint: correct docstring parameter description"
6407f17 Merge "Fix live_migration method's docstring"
7406661 Merge "Fix infinitely reschedule instance due to miss retry info"
9d8a34f Merge "Remove unused code from test_compute_cells"
429cd4b Fix object change detection
2214bc0 Remove unused code from test_compute_cells
9639b55 Fix infinitely reschedule instance due to miss retry info
a5184d3 Fix live_migration method's docstring
76729a3 maint: correct docstring parameter description
28224a6 Make metadata password routines use Instance object
Wow! I actually got 13 commits in that output when I expected 8. What is going on here? Is the revision range the correct mechanism to get show commits after a given commit? Or is this a bug?
The problem here lies with the slippery notion of "after".
Commits are not so much "before" and "after" as they are "embedded in a graph". In this case, since the repository is clone-able, I cloned it. Apparently it's fairly active:
$ git log --oneline -9
77bad25 Merge "Remove deprecated config option names: Juno Edition"
d4d712a Merge "Deprecate instance_get_by_uuid() from conductor"
d5bde44 Merge "Make metadata password routines use Instance object"
6cbc9ee Merge "Fix object change detection"
39b7875 Merge "Fix object leak in nova.tests.objects.test_fields.TestObject"
94d1034 Merge "maint: correct docstring parameter description"
6407f17 Merge "Fix live_migration method's docstring"
7406661 Merge "Fix infinitely reschedule instance due to miss retry info"
9d8a34f Merge "Remove unused code from test_compute_cells"
This is newer than your last-9 output. More interesting, though, is how these look if they are logged with --graph
added (and I'll bump the number to 10):
$ git log --oneline --graph -n 10
* 77bad25 Merge "Remove deprecated config option names: Juno Edition"
|\
| * d0a02fa Remove deprecated config option names: Juno Edition
* | d4d712a Merge "Deprecate instance_get_by_uuid() from conductor"
|\ \
| * | 1d340cc Deprecate instance_get_by_uuid() from conductor
* | | d5bde44 Merge "Make metadata password routines use Instance object"
|\ \ \
| |/ /
| * | 28224a6 Make metadata password routines use Instance object
* | | 6cbc9ee Merge "Fix object change detection"
|\ \ \
| * | | 429cd4b Fix object change detection
* | | | 39b7875 Merge "Fix object leak in nova.tests.objects.test_fields.TestO
|\ \ \ \
| |/ / /
| * | | 01381b8 Fix object leak in nova.tests.objects.test_fields.TestObject
(we get a different set of "topmost" commits because --graph
modifies the traversal, which is why I went to 10 commits).
To understand what's going on here, you need to look beyond git log
to git rev-list
. Like many git commands, git log
uses git rev-list
to pick revisions to display. (Some git commands literally run git rev-list
while others share its source code, but either way it works out the same.)
The git revision notation x..y
is shorthand for ^x y
(or y ^x
—these mean the same thing). Whether you write a name like master
or origin/stable/havana
, or an indirect name like HEAD
, or a raw commit-ID, or a shortened raw commit-ID like 77bad25
, the x
and y
parts are resolved to the underlying git object (which in our case should be a commit). You can observe the resolving step by using git rev-parse
:
$ git rev-parse master
77bad252096f7a4a8174340f0f2a3baf1fd52195
$ git rev-parse HEAD
77bad252096f7a4a8174340f0f2a3baf1fd52195
$ git rev-parse origin/stable/havana
0bf0bb4b5df64f7266c903a986d0b90a1f223822
What git rev-list
does with this is to work backwards from this commit to find its parent commit(s), and then from those commits to their parents, and so on. The result is an ancestor-set.
The ancestors of master
are, at this point, in no particular order:
master
itself: 77bad25...
master
's first parent, git rev-parse master^1
: d4d712a...
master
's second parent, git rev-parse master^2
: d0a02fa...
master
's first parent's first parent, git rev-parse master^1^1
: d5bde44...
master
's first parent's second parent, git rev-parse master^1^2
: 1d340cc...
and of course many more, going back for many commits:
$ git rev-list master | wc -l
27918
So git rev-list master
selects all 27-thousand-and-some commits, and git log master
would show you all of them (in some order, with the order modified based on additional options passed to git rev-list
via git log
).
To exclude some of those, you can tell git rev-list
to start with some particular revision—such as 01381b8
—and find all of its ancestors (including 01381b8
itself):
$ git rev-list 01381b8 | wc -l
27901
At this point, this is 17 fewer commits than are found by starting at master
and working backwards (and there are no commits in this second list that are not already in that first one). So if you tell git rev-list
to give you "all commits starting from master
, minus all commits starting from 01381b8
", you should get 17 commits:
$ git rev-list master ^01381b8 | wc -l
17
and indeed that's what we see. (The actual list is not all that interesting, but you can see it with git rev-list master ^01381b8
, or equivalently, git rev-list 01381b8..master
.)
These commits are the ones git log
will show you, given the same revision range.
You can spend days studying the git rev-list
documentation and still miss items (for instance, --graph
tells you that it "enables parent rewriting" and "implies --topo-order
" and until I checked just now, I had forgotten about the parent rewriting part. Fortunately that does not apply here anyway, just the need for --date-order
to force the graphed version to sort by date rather than topologically.)
Use this below command,
git log --graph --pretty=format:'%Cblue%h%Creset -%C(red)%d%Creset %s
%Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
It will give you log of commits in one line with branch name, author details, graph, date in a colourful way.
Try this !! its really amazing.