Home > Mobile >  Git log and graph show different results after git rebase with merge commits
Git log and graph show different results after git rebase with merge commits

Time:04-19

Setup

I have a scenario, as shown by the git log graph below.

$ git log --oneline --graph
*   1c3ae5de (HEAD -> feature) Merge feature-2 into feature
|\
| * 9051a8b8 (feature-2) Commit D
|/
*   940c88e0 Merge feature-1 into feature
|\
| * 60dca27d (feature-1) Commit C
|/
* 8781f253 (main) Commit B
* 8704354e Initial commit

As you can hopefully see, there was a main branch which branched into the feature branch. On the feature branch, two separate branches feature-1 and feature-2 were branched off, work performed, and merged back into the feature branch with a merge commit.

The git log of this scenario is shown below as well.

$ git log --oneline
1c3ae5de (HEAD -> feature) Merge feature-2 into feature
9051a8b8 (feature-2) Commit D
940c88e0 Merge feature-1 into feature
60dca27d (feature-1) Commit C
8781f253 (main) Commit B
8704354e Initial commit

Issue

Now, someone commits a new commit on main and suddenly the log graph looks like the following1.

* 8a16a716 (HEAD -> main) Commit E
| *   1c3ae5de (feature) Merge feature-2 into feature
| |\
| | * 9051a8b8 (feature-2) Commit D
| |/
| * 940c88e0 Merge feature-1 into feature
|/|
| * 60dca27d (feature-1) Commit C
|/
* 8781f253 Commit B
* 8704354e Initial commit

What I would like to do is rebase the feature branch off of main, and preserve merge commits. This is achieved by executing git rebase --rebase-merges main while on the feature branch. However, after performing this action, the output of git log --oneline and git log --oneline --graph do not agree in terms of the order of the commits.

$ git log --oneline --graph
*   24e18500 (HEAD -> feature) Merge feature-2 into feature
|\
| * 33da98a4 Commit D
|/
*   a1ce7cd3 Merge feature-1 into feature
|\
| * bd23945b Commit C
|/
* 8a16a716 (main) Commit E
* 8781f253 Commit B
* 8704354e Initial commit
$ git log --oneline
24e18500 (HEAD -> feature) Merge feature-2 into feature
a1ce7cd3 Merge feature-1 into feature
33da98a4 Commit D
bd23945b Commit C
8a16a716 (main) Commit E
8781f253 Commit B
8704354e Initial commit

Question

Why does the output of git log --online show all the merge commits at the top of the list after a git rebase --rebase-merges? And why does that order disagree with the output in graph form?

1 As an ancillary question, I don't understand why the graph shows a / from the main line branch to the 940c88e0 commit. If anyone could answer that as well, I'd be much appreciative.

CodePudding user response:

The root of the problem is that git log must sort commits in some cases.

Remember that git log is displaying one commit at a time. Meanwhile, history, in a Git repository, consists of the commits themselves and their connections. For instance, in the initial graph output:

*   1c3ae5de (HEAD -> feature) Merge feature-2 into feature
|\
| * 9051a8b8 (feature-2) Commit D
|/
*   940c88e0 Merge feature-1 into feature
|\
| * 60dca27d (feature-1) Commit C
|/
* 8781f253 (main) Commit B
* 8704354e Initial commit

commit 1c3ae5de has two parent commits 940c88e0 (first parent) and 9051a8b8 (second parent) respectively. Commit 9051a8b8 has just one parent, 940c88e0. This pattern repeats a bit and when we get to 8781f253 (at the tip of main), that commit has one parent 8704354e and commit 8704354e has no parents.

When we have no merge commits at all—just a simple linear chain of commits—we have a nice simple picture:

... <-F <-G <-H   <--latest

The branch name, here latest, helps Git find commit H quickly. Commit H provides, via the commit object, a snapshot (an archive of all files) and metadata, and the metadata for commit H list the author and date and log message and so on, but also list the raw hash ID of earlier commit G. This allows Git to use H to find G.

Commit G is of course a commit, and as such has both snapshot and metadata. The metadata for G lists earlier commit F's raw hash ID, which allows Git to use G to find F. Commit F, being a commit, has snapshot and metadata, and onwards (or backwards) we (or Git) go(es). Eventually we reach the very first commit, which—being first—has an empty list of previous commits and therefore Git gets to stop going backwards.

For git log to do its job—show (some) commits—we tell Git log which commit(s) we wish it to start with, via command line arguments:

git log main

or:

git log feature

for instance, or we let it default to starting with HEAD, or we use --all or --branches or something to select more than one starting point, or we can even give git log multiple raw hash IDs (though of course we probably have to run git log first to get them

  • Related