1

I have a git branch, dev, that is exactly where I want my other git branch, master to be. master has some hotfixes on it that are further in the future than the common ancestor of both so this command chain creates complicated and silly merge conflicts:

git checkout master
git merge dev # Complicated merge conflicts. Boo hiss.

What I would like to do is make a new commit on master that has the state of dev HEAD on it. From then on things can proceed in a more sane manner

Example:

dev:    A ---> B ---> C ----> D
master: A ---> Y ---> Z ----> *

A is my common ancestor. Y and Z are conflicting hotfix changes, so the three-way merge between D, A, and Z is a mess.

In the end I want this result:

  • HEAD of master contains the exact same code as the HEAD on dev
  • History is not rewritten, since both branches have been pushed to remotes
  • A nice commit message that explains this mergeish commit
SimplGy
  • 20,079
  • 15
  • 107
  • 144
  • If resolving conflicts is so painful, you might just need a better merge tool. What are you currently using, and what operating system are you on? –  Sep 02 '13 at 22:33
  • I use Kaleidoscope on OSX. I'm comfortable with it as a mergetool, but this is just not a merge situation. For example, the `Gruntfile` that specifies the build was changed in both, but I don't want it to try to merge changes--I know I want the dev version. I want dev for everything, not just to win in a conflict. – SimplGy Sep 02 '13 at 22:35
  • I think there's a merge strategy that you can pass as an option to Git for that kind of situation then, look around on Stack Overflow or Google it, I'm sure it's been asked and answered before. –  Sep 02 '13 at 22:36
  • The accepted answer here http://stackoverflow.com/a/6070417/2348315 looks on the right track for your needs. – PeterSW Sep 02 '13 at 22:41
  • @SimpleAsCouldBe I've actually updated my answer with something that looks like it could work for your situation. –  Sep 02 '13 at 22:43

3 Answers3

2

You could try using the ours merge strategy:

git checkout dev
git merge -s ours master
git checkout master
git merge dev

The merge to master will create a merge commit, but the result of the commit will ignore all changes made in master. Then you simply fast-forward master to match dev.

From the documentation for merge strategies:

ours

This resolves any number of heads, but the resulting tree of the merge is always that of the current branch head, effectively ignoring all changes from all other branches. It is meant to be used to supersede old development history of side branches. Note that this is different from the -Xours option to the recursive merge strategy.

SimplGy
  • 20,079
  • 15
  • 107
  • 144
  • Yes, it will change the history, since `dev` won't be a fast-forward from `master`. If the `dev` branch is private, another option is to `rebase` it against master and resolve the conflicts in smaller chunks...is that an option for you? –  Sep 02 '13 at 22:28
  • Tested. The `-s ours` merge strategy works great for this use case. @PeterSW's answer also works, but this one keeps the history of the dev branch as well as showing a commit for the merge, so which method you pick depends on what you're looking for. – SimplGy Sep 02 '13 at 23:03
1

To get a new commit on master that gives an exact match to what is on dev you can do the following:

git checkout dev                         # Get the working directory we want.
git symbolic-ref HEAD refs/heads/master  # Move HEAD to master without changing 
                                         # index or working directory.
git commit                               # Create the new commit on master.

This creates a completely new commit separate from the dev branch.

My inspiration comes from here: https://stackoverflow.com/a/6070417/2348315

If you follow this with a merge commit between dev and master would need to resolve these separate histories. Creating a merge commit immediately after this operation should be conflict free but it will have to be a merge commit rather than a fast-forward merge.

This approach would make sense if you were cherry-picking changes across and had got into a conflict situation that you want to resolve in the way you describe. If your workflow though is to merge changes across then @Cupcake 's merge -s ours approach seems a better fit.

Community
  • 1
  • 1
PeterSW
  • 4,921
  • 1
  • 24
  • 35
  • Excellent. This creates a new commit on master with the contents of devs, and has one single commit to show for it. – SimplGy Sep 02 '13 at 23:01
  • Hmm. There is a downside to this approach. It works great at first, it's surgical and precise. Later though, if I want to continue on my dev branch for a few more commits and merge them into master when it's ready for prime time, ALL of the old dev commits show up again--the master branch still thinks that dev is ahead of it. This means it will try to merge in B, C, and D from dev, as well as any future development when you do the merge. – SimplGy Sep 02 '13 at 23:31
  • Update: When you merge, the commits show up in history, but try to revert the files, so it's not breaking anything. – SimplGy Sep 02 '13 at 23:33
  • @SimpleAsCouldBe So if I'm understanding right what you saw would be a result of it creating a new independent commit separate from dev's history, then later creating a merge commit brings dev's history in? – PeterSW Sep 04 '13 at 16:53
  • Trying my best to explain without really understanding git :) Above I should have said "but *does not* try to revert the files". I can describe what I saw: symbolic-ref method creates a new commit on master that has all of dev's contents. A few commits on dev later, I want to do a normal merge of dev into master (usually this is a fast-forward in our workflow). At this point it shows a LOT of history--all the history from dev that was skipped in the sym-ref commit. But it doesn't try to change the files so it's ok, I think. Just hard to grok. – SimplGy Sep 04 '13 at 17:58
  • Aha, ok that matches my understanding and what I thought I saw when I tried it out :) I suppose @Cupcake 's answer better matches you're workflow then. – PeterSW Sep 04 '13 at 21:57
0
  1. fork marster into a new branch hotfix
  2. reset master to the ancestor you want (A)
  3. merge D (== head/dev) into master

for details on reset the great book gitpro : http://git-scm.com/2011/07/11/reset.html

dzada
  • 5,344
  • 5
  • 29
  • 37
  • [According to the original poster](http://stackoverflow.com/questions/18581475/git-merge-by-creating-a-new-checkin-based-on-another-branch/18581519?noredirect=1#comment27342771_18581519), `master` is a public branch that could be shared with others, so resetting `master` is not an option. –  Sep 02 '13 at 22:35
  • It is if you explain to others that they have to reclone for instance. in that case that ma be the only way. It is also possible if the local master is not pushed, further than A – dzada Sep 02 '13 at 22:37