Git ghost commits

A commit was reverted at work. The commit was present in the repository history, but there was no trace of it in the GitHub file history log.

A file history log (for a.txt, as an example) looked like this:

$ git log --oneline -- a.txt 
be9d26c (test-branch) 3 - test-branch
689ae1c 1

However, we knew there were more commits on that file between 689ae1c and be9d26c, but we could not find them on GitHub or using git log.

Finding the commit

At the time, the repository history looked like that (logs taken from the repository created below):

$ git log --oneline
0f0ad15 (HEAD -> main) Merge branch 'test-branch'
be9d26c (test-branch) 3 - test-branch
076dc52 Revert "2"
d779068 2
689ae1c 1

I expected the history to also include commits d779068 and 076dc52 in the file history, between commits 689ae1c and be9d26c.

Using git log --full-history --oneline -- a.txt (still using a.txt as the file name) did not list the commits, but using the --full-history option worked:

$ git log --full-history --oneline -- a.txt
0f0ad15 (HEAD -> main) Merge branch 'test-branch'
be9d26c (test-branch) 3 - test-branch
076dc52 Revert "2"
d779068 2
689ae1c 1

Reproducing the behavior

To try to understand the behavior, I tried to reproduce it by creating a fresh repository:

$ git init repo-test # Create an empty repository
$ cd repo-test
$ echo "1" > a.txt # Create a.txt with the content 1
$ git add a.txt
$ git commit -m "1" # Commit the file
$ echo "2" > a.txt # Update a.txt with the content 2
$ git add a.txt
$ git commit -m "2" # Commit the updated file
$ git revert HEAD # Revert previous commit

At this point, the file has all the commits, even without using --full-history:

$ git log --oneline -- a.txt      
c935f73 (HEAD -> titi) Revert "2"
d779068 2
689ae1c 1

However, in my case, the history also had a merge commit from another branch, let’s try it:

$ git checkout 689ae1c -b test-branch # Create a branch from the first commit
$ echo "3" >> a.txt # Append a.txt with 3
$ echo "3" > b.txt # Create a.txt with the content 3
$ git add a.txt b.txt
$ git commit -m "2" # Commit the updated files
$ git switch main # Going back to the main branch
$ git merge test-branch --no-ff # Create a merge commit from test-branch to main

The full history is available on Github.

And then let’s view the file history for a.txt:

git log --oneline -- a.txt
be9d26c (test-branch) 3 - test-branch
689ae1c 1

Ha! Commits d779068 and c935f73 are not listed! See on Github.

They appear as expected with --full-history:

$ git log --full-history --oneline -- a.txt
0f0ad15 (HEAD -> main) Merge branch 'test-branch'
be9d26c (test-branch) 3 - test-branch
076dc52 Revert "2"
d779068 2
689ae1c 1

The repository is available here.

Conclusion

It seems that these “ghost commits” (I do not have a better term for commits that exist but are not displayed) are triggered when:

  • commits on a single file cancel each other out
  • a merge commit is added after a revert, and the merge commit has an ancestor that predates the revert
  • the merge commit must include a branch that also modifies the same file. Otherwise, all the commits will be displayed

Git can be tricky… I guess it just hides commits that result in no changes after a merge commit (?). I still do not fully understand this behavior…