3

When I work with GIT branches my "mental model" of any given branch is that it is a path which breaks out from the branch which I base that branch on. Let's assume I do the following :

git checkout develop
git checkout -b feature/1
touch README.md
git add --all
git commit -m "Added README.md"
git push -u origin feature/1
git checkout develop
git merge feature/1
git push

Now, I would expect a git GUI to visualise this by drawing a develop branch as a separate path. Then, from the develop branch path I would expect to see a feature/1 branch path break out from the develop branch path. I would then expect to see the two paths go alongside each other, until i merge feature/1 into develop. At this point I would expect to see the feature/1 branch path merge back into the develop branch path.

enter image description here

However, when I only work at a single branch at any given time (in addition to the develop and master branch which are always there) - this is not what happens. If I begin work on a new branch, finish the branch and merge it back into develop, it all seems to take place on the same "path". However, when I work on two branches at the same time it seems that one of the branches breaks out on its own path. However, the first branch is just visualised as a continuation of the develop branch.

enter image description here

I find it confusing that only one of the branches are visually represented with its own path, while the other is just represented as a direct continuation of the develop branch. Am I getting this wrong - or thinking about branches wrong? I just want to get a clear and consistent view of when I broke a new branch out of develop and when i merged it back in again. In my opinion, having a separate path for every branch created would make this easier to get.

enter image description here

Is there a GIT client out there which does this? Or do I need to look at this differently?

sbrattla
  • 5,274
  • 3
  • 39
  • 63

3 Answers3

3

Your "problem" is, that you are not aware of fast-forward merges. If there is nothing to merge, meaning you didn't change anything on develop between the point where you branched off and merge back, you do a fast-forward merge by default that just makes develop point to the same commit as feature/1. If you want to have the history with different paths preserved, you can use the --no-ff option of merge which will create a merge commit even if there is nothing to merge actually. Then any Git client will show you the image you expect. If you don't do it, no Git client can show it to you as that is not how the history looks like.

Vampire
  • 35,631
  • 4
  • 76
  • 102
2

What you're seeing here is the difference between fast-forward merges and true merges.

In Git, merging a branch into another doesn't always result in a merge commit; if no new commits were made on the destination branch since the branching point, Git will simply move the source branch forward so that it points to the same commit as the destination branch.

Consider this example:

A - B - C (master)
     \        
      E - F (feature)

Here, the branching point of the feature branch is commit B. Since then, commit C was made on the master branch. If you were to merge feature into master, Git would notice that the branching point is no longer the latest commit in master, hence it would have to tie the two lines of history together (what you call "paths") by creating a merge commit, that is a commit that has more than one parent:

A - B - C - M (master)
     \     /   
      E - F (feature)

However, if no new commits happened on master since the branching point, Git would simply move the master branch forward to point to the same commit as feature. This is called a fast-forward merge:

fast-forward
A fast-forward is a special type of merge where you have a revision and you are "merging" another branch's changes that happen to be a descendant of what you have. In such a case, you do not make a new merge commit but instead just update to his revision.

So, given this scenario:

A - B (master)
     \        
      E - F (feature)

Merging feature into master would simply result in:

A - B - E - F (master, feature)

If you want to force Git to create a merge commit even if it would do a fast-forward merge, you can add the --no-ff option of git-merge. So doing this:

git merge feature --no-ff

Would result in:

A - B - M (master)
     \   \   
      E - F (feature)
Enrico Campidoglio
  • 56,676
  • 12
  • 126
  • 154
1

Note: Git 2.33 (Q3 2021) clarifies the description of "fast-forward" in the glossary:

See commit e22f2da (19 May 2021) by Reuven Y (robi-y).
(Merged by Junio C Hamano -- gitster -- in commit 7f06d94, 10 Jun 2021)

docs: improve fast-forward in glossary content

Signed-off-by: Reuven Yagel

The text was somewhat confusing between the revision itself and the author.

glossary-content now includes in its man page:

fast-forward:

A fast-forward is a special type of merge, where you have a revision and you are "merging" another branch's changes that happen to be a descendant of what you have.

In such a case, you do not make a new merge commit, but instead just update your branch to point at the same revision as the branch you are merging.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250