git log revision range gives incorrect range of co

2019-07-21 08:44发布

问题:

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:

  1. Clone the repo

    git clone https://github.com/openstack/nova.git

  2. 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
    ...
    
  3. 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?

回答1:

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.)



回答2:

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.