27

I've been trying to wrap my head around git branching models. I've been looking at http://nvie.com/posts/a-successful-git-branching-model/ for some ideas and coming from Subversion one thing I was really looking forward to was making a change in a single place and merging it to all the branches that needed it. In Subversion, we ended up doing to much copy of code around.

However I still don't get this completely. Here is a standard type of workflow that I have and it will always come up with conflicts.

# create new version branch
git checkout master
git checkout -b v3
vim pom.xml  # change branch version to "3.1-SNAPSHOT"
git commit -a
git checkout master
vim pom.xml  # change master version to "4.0-SNAPSHOT"
git commit -a

So the master is at 4.0-SNAPSHOT and the branch is at 3.1-SNAPSHOT.

Not I want to create a hotfix on the branch and move it to the trunk.

git checkout v3
git checkout -b hotfix
vim file.txt  # make a bugfix change
git commit -a
git checkout v3
git merge hotfix  # this works fine
git checkout master
git merge hotfix  # this has a conflict since both branches have changed the version

I understand why its happening and it makes sense. Is there a better way of doing this?

I read about cherry-pick, which I tested and does work:

git checkout v3
git cherry-pick a518b0b75eaf28868
git checkout master
git cherry-pick a518b0b75eaf28868

However, that doesn't seem like the "correct" way to handle this. Any suggestions?

Chuck M
  • 1,175
  • 3
  • 17
  • 26

4 Answers4

30

If you wanted to get super-technical about it, you could create the hotfix from a common ancestor:

git merge-base v3 master
git checkout -b hotfix <whatever you got from merge-base>
# make your fix
git checkout v3 && git merge --no-ff hotfix
git checkout master && git merge --no-ff hotfix

        v3--------v3 (hotfixed)
       /         /
ancestor----hotfix
       \         \
        master----master (hotfixed)

The --no-ff flag is there to highlight that Git will create a merge commit, keeping the hotfix branch label at the hotfix tip, instead of pulling the label to v3 or master. (You can omit the flag and get the same behavior, since the hotfix branch has one commit that isn't in master or v3. More info in the docs.)

Personally, I think that's overkill. I'd go with gahooa: make the hotfix on the branch that makes sense, then merge or cherry-pick depending on how you want the branches to relate to each other.

ellotheth
  • 4,333
  • 2
  • 17
  • 29
  • 7
    +1 for `merge-base`, `--no-ff` and all the good advice. `cherry-pick` creates new commit: same diff, log message, timestamp, but different hash. If clear history is important, I don't think merge-base is an overkill at all. Cherry picking will work for simple cases, but beware of things like this: http://news.ycombinator.com/item?id=3947950 – Serge Balyuk Feb 15 '13 at 12:50
  • `--no-ff` shouldn't have any effect on `hotfix`. It tells `git merge` to always create a merge commit, rather than just moving the currently checked out reference (`v3` or `master`) to point to the same commit as `hotfix`. In the diagrammed scenario, this shouldn't happen anyway - it can only fast-forward if the commit you're merging is a direct descendant of the commit you currently have checked out. It would only move `v3` or `master` if `ancestor` is actually `v3` or `master`, respectively. – Mike Dimmick Mar 28 '18 at 11:55
14

Really, your answer is dependant on if you want your trees to be based on the same history... For example, 4.0 is based on the latest 3.X + all of the changes in 4.0...

Personally, I don't recommend it once you decide to start a new branch(s) for a new version(s). At a give point of time, the software is taking a different direction, so your branches should also.

This leaves git cherry-pick as your ideal solution. Make the change in whatever branch makes the most sense, and then cherry pick it to the older versions. This is the same as if you had checked out the old branch and manually applied the same change, and made a new commit. It keeps it clean and to the point.

Git merge or rebase are going to try to integrate the branches history together, each in their own way, which I suspect you don't want when backporting bug fixes, etc...

gahooa
  • 131,293
  • 12
  • 98
  • 101
  • I concur (though I think @ellotheth's answer may be more elegant depending on the circumstance). According to git's own [branching philosophy](http://gitster.livejournal.com/42247.html) you really are saying that _this commit_ is the only thing that belongs to all the branches. So cherry-picking is what "says" that. – Alexander Bird May 28 '12 at 03:33
  • Thanks, I think that makes sense. Coming from Subversion, and how painful it was sometimes for us to push fixes to the trunk and a branches (basically doing the fix twice), I had thought in my mind that I could just merge feature branches anywhere I want. When that didn't work as I expected, I was kind of bummed. "cherry-pick" makes alot of sense though. I just need to come up with a process that will be easy for all developers to understand and I think this can be made pretty clear. – Chuck M May 28 '12 at 23:47
1

In the case, you are working on branch "4.0" and have to make a fix on "3.1", you may rebase "4.0" after you commit "3.1":

Make sure you are on the feature branch 4.0:

git checkout 4.0

Save current work so you can check out other branch:

git stash  
git checkout 3.1  

Do editing and commit:

git commit -a -m "bug fix"  
git checkout 4.0  

Get back your changes:

git stash apply  

Change 4.0 so it branches of the current head of "3.1":

git rebase "3.1"
SherylHohman
  • 16,580
  • 17
  • 88
  • 94
Aleš Kotnik
  • 2,654
  • 20
  • 17
  • 5
    I'd like to comment that while this is possible, it removes the ability to rely on tags, pushing, or efficiently sharing 4.0 code. Rebase is great, personally, but not so great when you have already pushed. – gahooa May 25 '12 at 21:17
0

I've been struggling with this question, too, and I think if you're willing to change your versioning strategy a little (i.e., depart from the -SNAPSHOT versions that Maven encourages), this could be solved by using a fixed version (like SNAPSHOT or 0.0.0-SNAPSHOT) on master (or whatever your development branch is). (The SNAPSHOT suffix is important, if you're using Maven, since Maven treats SNAPSHOT-versioned artifacts differently than others.)

In fact, I think you'd want to institute a policy of only ever changing the version number on your production branch (the one from which you build releases) or on branches which are for release purposes only (e.g., changing version numbers) and which you never intend to merge back to the development branch.

I haven't actually used this strategy yet, but was just thinking about it, and I think I'll start trying it.

Mark
  • 1,788
  • 1
  • 22
  • 21