Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this git rebase think there's nothing to do?

Tags:

git

In this example, git doesn't think it needs to rebase, but it clearly does.

Here's a quick script to create a mini test git repository

#!/bin/bash
rm -rf testgit
mkdir testgit
cd testgit
git init
echo -e "a\nb\nc" > file
git add file
git commit -am 'first'

git checkout -b lineA
echo -e "A\nb\nc" > file
git commit -am 'A'

git checkout -b lineB
echo -e "a\nB\nc" > file
git commit -am 'B'

git checkout -b lineC
echo -e "a\nb\nC" > file
git commit -am 'C'

After this runs, here's what file looks like in each branch

master | lineA | lineB | lineC
------------------------------

a        A       a       a
b        b       B       b
c        c       c       C

Now let's merge all the branches into master, after which each letter in file should be capitalised

$ git checkout master
$ git merge lineA

Okay.

$ git checkout lineB
$ git rebase master
Current branch lineB is up to date.

What? No, master changed.

$ git checkout master
$ cat file
A
b
c

$ git checkout lineB
$ cat file
a
B
c

It seems to me that branch lineB needs to rebase to incorporate the change to master made by merging lineA.

Why doesn't git think this needs to be done?

Additionally, if you do this

$ git checkout master
$ git merge lineA
Already up-to-date.
$ git merge lineB
...
 1 file changed, 2 insertions(+), 2 deletions(-)
$ cat file
a
B
c
$ git merge lineC
...
 1 file changed, 2 insertions(+), 2 deletions(-)
$ cat file
a
b
C

Here the merges should conflict, but git is quietly clobbering them. I'm don't know if this is relevant, but it seems weird.

like image 419
spraff Avatar asked Oct 17 '25 03:10

spraff


2 Answers

Result of the First Commands

After the script we have (abbreviating the line branch names...):

master   lA   lB   lC
  ↓      ↓    ↓    ↓
first----A----B----C

These commands:

$ git checkout master
$ git merge lineA

results in a fast-forward merge, which just moves master to point to the same commit as lineA.

Giving:

        master 
         lA   lB   lC
         ↓    ↓    ↓
first----A----B----C

No Work for the Rebase

So this sequence of commands:

$ git checkout lineB
$ git rebase master

is saying move the lineB branch on top of master.

...but it is already on top of master.

master would have to have some commits in its history that lineB does not have for the rebase command to have any work to do.

No Merge Conflict

When Git creates a merge commit it considers the history of the parents that are going into the merge. In this case both branches are pointing to commits on a common chain of commits. Git can see that the A commit came before the B commit which came before the C commit. As such Git considers the later commits to be additions on top of the earlier commits and as such they do not conflict.

If instead you had a history like:

       master
         ↓
      ---A
     /
     |   lB
     |   ↓
first----B

then Git would see them as parallel lines of development. In that case merging lB into master or vice-versa would conflict. This is because B is not an addition on top of A in the history and as such Git needs user input to understand which of the changes to keep.

like image 118
PeterSW Avatar answered Oct 18 '25 22:10

PeterSW


I think your point of confusion is further back. When you executed

git commit -am 'first'
git checkout -b lineA

you did create a new branch called lineA but the branching-off point was the commit you just did ("first"). As Git puts it, first is part of the history of both branches, and at that point in your script it is also the most recent commit in both branches. Git log with some options shows that both branch names point to the first commit, which in this example is commit id 58b7dcf:

git log --graph --oneline --decorate
* 58b7dcf (HEAD, master, lineA) first

Continuing:

echo -e "A\nb\nc" > file
git commit -am 'A'
git log --graph --oneline --decorate
* ed40ed2 (HEAD, lineA) A
* 58b7dcf (master) first

The log command shows that commit A (ed40ed2) is now the most recent commit on lineA, and its parent commit is first, which is still the most recent commit on master. If you were to merge lineA back to master at this time it would be a fast-forward (trivial) merge, because no commits were made to master since lineA branched off from it. Likewise, rebasing lineA onto master would do nothing except report

Current branch lineA is up to date.

because the most recent commit on branch master (the tip of the master branch) is still part of the history of branch lineA. Continuing:

git checkout -b lineB
echo -e "a\nB\nc" > file
git commit -am 'B'
git checkout -b lineC
echo -e "a\nb\nC" > file
git commit -am 'C'
git log --graph --oneline --decorate
* 4033067 (HEAD, lineC) C
* f742aed (lineB) B
* ed40ed2 (lineA) A
* 58b7dcf (master) first

Each new branch is built on the most recent commit of the previous one, with no parallel development going on. Continuing:

git checkout master
git merge lineA
Updating 58b7dcf..ed40ed2
Fast-forward

Notice this is a fast-forward merge, so all it did was move master to ed40ed2:

git log --graph --oneline --decorate
* ed40ed2 (HEAD, master, lineA) A
* 58b7dcf first

Finally:

git checkout lineB
git rebase master
Current branch lineB is up to date.
git log --graph --oneline --decorate
* f742aed (HEAD, lineB) B
* ed40ed2 (master, lineA) A
* 58b7dcf first

You wrote:

What? No, master changed.

Yes, master changed, but not in a way that affected the history of lineB or lineC. master was fast-forward to commit ed40ed2, which was already the parent of lineB. To be precise, it's the parent of f742aed, which is the commit id I got for commit B.

The confusion, for people used to other version control systems, is that Git records a tree-like data structure (technically a DAG) for the commit records, not the branch names. The branches are ephemeral; they're just labels, like movable version tags. The parent-child relationships between the commits is what you see in git log --graph and other visualization tools.

like image 39
gatkin Avatar answered Oct 18 '25 22:10

gatkin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!