views:

501

answers:

1

Coming from mercurial, I'm using branches to organize features. Naturally I want to see this work-flow in my history as well.

But I started my new project with git and finished a feature. After merging I realized that git used fast-forward and forgot about my branch. So to think into the future: I'm the only one working on this project. If I use the default approach of git (fast-forward merging) my history would result in one giant master branch.

I don't want this and can't see any good reason making this default. Maybe there are reasons, but what's so striking about it, that it has to be the default action?

+15  A: 

Fast-forward merging make sense for short-live branches, but on a more complex history, it is not always adapted:

alt text
(From nvie.com, Vincent Driessen, post "A successful Git branching model")

Incorporating a finished feature on develop

Finished features may be merged into the develop branch definitely add them to the upcoming release:

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff myfeature
Updating ea1b82a..05e9557
(Summary of changes)
$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).
$ git push origin develop

The --no-ff flag causes the merge to always create a new commit object, even if the merge could be performed with a fast-forward. This avoids losing information about the historical existence of a feature branch and groups together all commits that together added the feature.


The fast-forward is the default because:

  • short-lived branches are very easy to create and used in Git
  • they often isolate many many commit that can be reorganized freely within that branch
  • those commits are actually part of the main branch: once reorganized, the main branch is fast-forwarded to include them.

But if you anticipate an iterative workflow on one topic/feature branch (i.e: I merge, then I go back to this feature branch and add some more commits), then is useful to include only the merge in the main branch, rather than all the intermediate commits of the feature branch.

In this case, you can end up setting this kind of config file:

[branch "master"]
# This is the list of cmdline options that should be added to git-merge 
# when I merge commits into the master branch.

# First off, the option --no-commit instructs git not to commit the merge
# by default. This allows me to do some final adjustment to the commit log
# message before it gets commited. I often use this to add extra info to
# the merge message or rewrite my local branch names in the commit message
# to branch names sensible to the casual reader of the git log.

# Option --no-ff instructs git to always record a merge commit, even if
# the branch being merged into can be fast-forwarded. This is often the
# case when you create a short-lived topic branch which tracks master, do
# some changes on the topic branch and then merge the changes into the
# master which remained unchanged while you were doing your work on the
# topic branch. In this case the master branch can be fast-forwarded (that
# is the tip of the master branch can be updated to point to the tip of
# the topic branch) and this is what git does by default. With --no-ff
# option set git creates a real merge commit which records the fact that
# another branch was merged. I find this easier to understand and read in
# the log.

mergeoptions = --no-commit --no-ff

The OP adds in the comments:

I see some sense in fast-forward for short living branches, but making it the default action means, that git assumes you most often have such short living branches. Reasonable?

Jefromi answers:

I think the lifetime of branches varies greatly from user to user. Among experienced users, though, there's probably a tendency to have far more short-lived branches.

To me, a short-lived branch is one that I create in order to make a certain operation easier (rebasing, likely, or quick patching and testing), and then immediately delete once I'm done.
That means it likely should be absorbed into the topic branch it forked from, and the topic branch will be merged as one branch. No one needs to know what I did internally in order to create the series of commits implementing that given feature.

More generally, I add:

it really depends on your development workflow:

  • if it is linear, one branch makes sense.
  • If you need to isolate features and work on them for a long period of time and repeatedly merge them, several branches make sense.

See "When should you branch?"

Actually, when you consider the Mercurial branch model, it is at its core one branch per repository (even though you can create anonymous heads, bookmarks and even named branches)
See "Git and Mercurial - Compare and Contrast".

Mercurial, by default, uses anonymous lightweight codelines, which in its terminology are called "heads".
Git uses lightweight named branches, with injective mapping to map names of branches in remote repository to names of remote-tracking branches.
Git "forces" you to name branches (well, with exception of single unnamed branch, situation called detached HEAD), but I think this works better with branch-heavy workflows such as topic branch workflow, meaning multiple branches in single repository paradigm.

VonC
Wow that was fast. ;)I know about the --no-ff option, but only find out about fast-forward after I screwed up my feature.I see some sense in fast-forward for short living branches, but making it the default action means, that git assumes you most often have such short living branches. Reasonable?
Florian Pilz
@Florian: I believe this is a reasonable view of the process. I have added an example of a config which will setup the way you want to manage merge to master.
VonC
Thanks, the config file should help to avoid such gotchas. :) Only applied this configuration locally with "git config branch.master.mergeoptions '--no-ff'"
Florian Pilz
@Florian: Just to add a second voice, I think the lifetime of branches varies greatly from user to user. Among experienced users, though, there's probably a tendency to have far more short-lived branches (quick daily operations) than long-lived ones (only merged when finished), so I think it's a reasonable default.
Jefromi
@Jefromi: Still - if I have only a single master branch after, say, 500 commits, doesn't that look somehow unprofessional? Nobody knows that there was a feature-branch structure during development. You only see the outcome. Or are branches in git more heavy weight like production and development branch which may stay on it's own for a month or more?
Florian Pilz
@Florian: it really depends on your development workflow (http://stackoverflow.com/questions/216212#216228): if it is linear, one branch makes sense. If you need to isolate features and work on them for a long period of time and repeatedly merge them, several branches make sense. See "When should you branch": http://stackoverflow.com/questions/2100829/when-should-you-branch/2107672#2107672
VonC
@Florian: I think we have different notions of "short-lived". To me, a short-lived branch is one that I create in order to make a certain operation easier (rebasing, likely, or quick patching and testing), and then immediately delete once I'm done. That means it likely should be absorbed into the topic branch it forked from, and the topic branch will be merged as one branch. No one needs to know what I did internally in order to create the series of commits implementing that given feature.
Jefromi
Big THANKS to both of you. I will dig deeper into the philosophy of git and will read through the links you (VonC) added to your post. :)
Florian Pilz