Home > Mobile >  Command "git status" listing more commits than "git log"
Command "git status" listing more commits than "git log"

Time:12-11

I did some work in a topic branch, with two intermediate commits before finishing the work. Then I went back to master and merged my topic branch. I'm in the habit of merging with --no-commit, so I can check that everything is ok before committing, but this was a fast-forward and I didn't stop it with --no-ff. Now "git status" tells me that there are 5 commits to push, but "git log" only shows 3 – the number of commits I did on the topic branch:

$ git status
On branch master
Your branch is ahead of 'origin/master' by 5 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean
$ git log
commit f4474af2add220b1c2f0bf07d92f5035cef622ef (HEAD -> notifiche, master)
Author: Francesco Marchetti-Stasi <f.marchettistasi@***>
Date:   Fri Dec 10 14:55:48 2021  0100

    [...]

commit b63a44f7c1266a53a0e5c91a3395fc9eb75d9bd2
Author: Francesco Marchetti-Stasi <f.marchettistasi@***>
Date:   Tue Dec 7 16:46:59 2021  0100

    [...]

commit beb5d335e0ac35184b6a8e78bf45627e705a2a9b
Merge: 1f16a0d 025f660
Author: Francesco Marchetti-Stasi <f.marchettistasi@***>
Date:   Tue Dec 7 16:46:18 2021  0100

    [...]

commit 025f660ebc22b2d3bee00c691657c6e7318fcf50 (origin/master, origin/HEAD)
Author: Francesco Marchetti-Stasi <f.marchettistasi@***>
Date:   Tue Dec 7 11:44:48 2021  0100

    [...]

commit 1f16a0dcd96862b0ca75d3f3ca896ad755ccf29e
Author: Francesco Marchetti-Stasi <f.marchettistasi@***>
Date:   Thu Nov 25 09:38:21 2021  0100

    [...]

Is this normal? Are there some "hidden" commits I am not seeing?

In your answers please consider I may need to push my commits, but I guess that shouldnt' change local commit history, correct?

EDIT: in reply to Lasse V. Karlsen question:

$ git log --format="%h %d"
8059943  (HEAD -> master)
b63a44f
beb5d33
025f660  (origin/master, origin/HEAD)
1f16a0d
0bd94aa
1e9d375  (dizione_e_sede)
c19c33a
c2bc958
d1801c3
1dd9a87
e4f0846
2091d2e  (origin/conv_pdf)
84076f3
[...]
$ git log --format="%h %d" origin/master..master
8059943  (HEAD -> master)
b63a44f
beb5d33
1f16a0d
0bd94aa
$

And on the server I see the following:

025f660e X 1e9d3754 c19c33a7

On hover on the red circled X a message "Pipeline: failed" appears.

I should also mention that we upgraded our gitlab server from a very old version to 14.4.1, right in the middle of these commits – may this be related?...

CodePudding user response:

Try using from CLI (command line):

git log --oneline --decorate --graph --all

or if you have a GUI (on windows or Linux or Mac):

gitk --all

This will allow you to see the history in a more visually appealing way. I believe this is what you're looking for.

CodePudding user response:

This is normal. The git status command that shows that you are five commits ahead is correct (modulo bugs in Git, which are relatively rare). The git log output you see is also correct, but contains a subtle kind of lie:

commit f4474af2add220b1c2f0bf07d92f5035cef622ef (HEAD -> notifiche, master)
Author: Francesco Marchetti-Stasi <f.marchettistasi@***>
Date:   Fri Dec 10 14:55:48 2021  0100

    [...]

commit b63a44f7c1266a53a0e5c91a3395fc9eb75d9bd2
Author: Francesco Marchetti-Stasi <f.marchettistasi@***>
Date:   Tue Dec 7 16:46:59 2021  0100

    [...]

commit beb5d335e0ac35184b6a8e78bf45627e705a2a9b
Merge: 1f16a0d 025f660

When git log shows commits, it does so using a fairly complicated internal algorithm that uses a priority queue. This handles the fact that the commit graph is a DAG, not a simple tree. In particular merge commits result in tricky traversals.

Note that the third commit in the list here is in fact a merge commit, with two parents: 1f16a0d (shortened) and 025f660 (also shortened).

The next commit that git log shows you, in its linearized sequence, is:

commit 025f660ebc22b2d3bee00c691657c6e7318fcf50 (origin/master, origin/HEAD)
Author: Francesco Marchetti-Stasi <f.marchettistasi@***>
Date:   Tue Dec 7 11:44:48 2021  0100

or 025f660: this is the second parent of the merge. This is the commit that origin/master names.

The commit that git log shows after 025f660 is:

commit 1f16a0dcd96862b0ca75d3f3ca896ad755ccf29e
Author: Francesco Marchetti-Stasi <f.marchettistasi@***>
Date:   Thu Nov 25 09:38:21 2021  0100

which is the first parent of that merge. The reason it gets shown after, rather than before, is that its time stamp (in late Nov) is earlier than the time stamp of 025f660 (7 Dec of the same year).

This is all clearer, in my opinion at least, if we draw a graph. Here's a partial graph of what you're seeing, where I have replaced each commit's big ugly hash ID with a simple o or * or :

...--●--o--o---*--o--o   <-- master
      \       /
       ●-----●   <-- origin/master

The commit marked * is the merge commit. The commits whose hash ID has been replaced by a black circle are on both branches; the other commits are only on master. There are four open circles and the one starred merge, so there are five commits on master that are not on origin/master.

This graph can be drawn any number of ways:1 using git log --graph, or git log --graph --oneline, will make Git draw one for you, vertically oriented with the newest commit at the top. Because git log outputs commits one at a time, any sequence that doesn't include the graph, but does include a merge commit, must necessarily be some kind of lie (of omission or commission, depending on what you'd prefer and how you view this) when it implies a simple linear chain of commits. That's because the merge commit ties together some otherwise separate-but-parallel chains. The log command must walk both sides of the chain, or omit one side of the chain, and whichever choice you tell it to make, the result will be either incomplete ("walk just the first-parent links") or incorrect-or-incomplete based on your viewpoint.


1Here's an alternative drawing that, I think, makes the situation clearer in most cases, though git log --graph won't draw anything like this at all:

       o--o
      /    \
...--●      *--o--o   <-- master
      \    /
       ●--●   <-- origin/master

This is how I like to lay out my drawings where I show how git merge works.


Optional reading: some further details

The git log algorithm uses a priority queue.

The code begins by seeding the queue with one or more starting point commits (if there are no starting point commits, git log does nothing, but the default is to use the HEAD commit, and HEAD almost always specifies a commit: the exception is when you are on a branch that does not exist, which Git calls an orphan branch or an unborn branch—Git is not consistent about which term it uses). That is:

git log

inserts the HEAD commit (only) into the queue, while:

git log --branches

inserts all branch-tip commits into the queue, and:

git log master origin/master

inserts the commits identified by the names master and origin/master (this may be one single commit, if both names select the same commit, or two separate commits as in your case).

Because the queue is a priority queue, if it contains more than one entry, the "front" entry is the one with the highest priority. This is controlled by various options to git log, such as --date-order, --author-date-order, or --topo-order, but the default is to use the committer timestamp (which is not shown by default: the dates you see here are author dates; but fortunately most commits have the same author and committer date), with the latest timestamp—the most recent commit—having the highest priority.

If the queue has only one entry, of course, that one entry has the only priority, which is therefore the highest, average, and lowest priority all at the same time. So that's a degenerate case—but an important one since very often, there's only one entry in the queue. For instance, that's the case for your git log, which inserts just the HEAD commit, which is also the master commit.

The log code now runs a loop:

  • while the queue is not empty
    • remove the front entry (a commit)
    • decide whether to show this commit
      • if so, show it (there's a special bit of weirdness with --reverse here though)
    • decide which parent(s) if any to insert into the queue

The defaults for the two "decide" steps are: yes, do show it; insert all parents.

Most commits have just one parent, so for the first commit—identified by HEAD and notifiche and master—the loop shows that one commit, "decorated" with the two branch names, and inserts its one parent b63a44f7c1266a53a0e5c91a3395fc9eb75d9bd2.

The queue now has one commit in it, so the loop runs and shows the second commit. There are no names for that commit—no branch identifiers, no tags, etc.—so we just see that commit, without (origin/master) or anything. The parent has one parent too, so that one parent goes into the queue: we're now at beb5d335e0ac35184b6a8e78bf45627e705a2a9b, which is the merge commit.

The queue now has one commit in it, so the loop runs and shows the third commit. This commit has two parents, 1f16a0d and 025f660. They're both inserted into the priority queue.

Now we hit the priority code: the queue has two entries. One of them is at the front. The git log code extracts that one. It's the second parent, but it's the higher priority because it has a later committer timestamp.

The git log code now shows this commit, which is named by one name, origin/master, so now you see the misleading output. That commit has one parent, which goes into the queue, which continues to have two elements in it.

This pattern repeats until the branching implied by going backwards through the merge is itself resolved. Once there's only one commit in the queue, we return to the normal single-threaded walk. (Using --topo-order makes sure that this happens as soon as possible, and --graph implies --topo-order.)

  •  Tags:  
  • git
  • Related