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