8

I begun a workflow where I aim to do all new features in a development branch and the master branch will only be for production ready code.

After doing the following:

git checkout master
git merge staging

I received a bunch of conflicts looking like this:

CONFLICT (rename/add): Rename app/assets/stylesheets/mobile.css->app/assets/stylesheets/application.css in HEAD. app/...
CONFLICT (modify/delete): app/views/organizers/mobile.html.erb deleted in HEAD and modified in staging. Version stagi...
CONFLICT (modify/delete): app/views/events/mobile.html.erb deleted in HEAD and modified in staging. Version staging of app/v...

When I now have been googling this, all I read is about reviewing each and every file, resolving the conflicts and commit the changes. But I don't see any point in doing all this, as I know the code and it's only an advancement of the same code set.

How can I merge the changes done in staging into master in a simple manner without having to review and resolve each and every change?

Fellow Stranger
  • 32,129
  • 35
  • 168
  • 232

4 Answers4

8

This is a complex problem because it basically means that git cannot figure out intuitively how to combine the two branches. It appears that some files were deleted on master that are then modified in (and therefore used by) staging while another was renamed in master yet added to on staging. The following will mostly be a shot in the dark, and you may still have to manually resolve some (but hopefully fewer) conflicts.

There are a number of different algorithms for merging that git can choose from, so we'll use the one that is simplest, easy to customize, and usually used by default: the recursive strategy. And we'll force its use with the option -s recursive.

The first step to helping git would be to have it spend more time refining the patches, so we'll use the option -Xdiff-algorithm=histogram. This will take the longest but it will force git-merge to produce a better diff output (which will hopefully reduce the conflicts).

The next step is to tell git-merge to spend more time on doing a correct merge. We'll use the use the -Xpatience option to do this.

Since master will depend on the renamed/modified file, while staging will depend on the old file, we can try to trick git into thinking that the file wasn't really renamed with the option -Xrename-threshold=100% (so that the files have to be identical to be considered a rename). Note that you may have some redundant files left over and any changes to files that need to be aware of the other file names won't record old names (just the new ones) so you may want to go back and fix those once you've completed the merge. You could also adjust that 100% to something smaller (the default is 50% and that's currently causing a problem).

Now to include the files that are needed by staging we can use the following option to force them to be added: -Xtheirs. WARNING: this option will silently ignore all merge conflicts and simply use the content of staging by default. Make sure to run a full test suite to check for any inconsistencies. If you do have problems in the test suite after the merge, undo it with git reset --hard HEAD^ and redo the merge without this option. You will likely have a small number of merge conflicts that you'll have to settle manually, but you will know that the source of the errors is from one of those (finitely many) conflicts, which is huge leap forward from guessing in front of a debugger.

Finally, we put it all together to get

git checkout master
git merge staging -s recursive -Xdiff-algorithm=histogram -Xpatience -Xrename-threshold=100% -Xtheirs

Lastly, I suggest you play around with the options. The extra time spent on the merge may make the last two options useless (and even make it worse).

In the future, I suggest you regularly pull any changes in master to your topic branches. This will not only present the conflicts earlier (and thus there are fewer of them at a time) but it will also keep you up to date enough that you may even resolve the conflicts before they occur. The other reason that this is good is because git has a feature called git-rerere that records the conflicts that you resolve manually and then learns how to resolve those same conflicts automatically for you later. So once you resolve it once, you won't have to resolve the same/similar conflict over again.

randomusername
  • 7,927
  • 23
  • 50
  • Thanks for providing the most comprehensive answer. For my situation I finally decided to delete one of the branches instead of merging. – Alex R Dec 30 '14 at 16:49
4

Use git merge option, known as "strategy" -s ours (or theirs). That means git will prefer changes from development branch (or from master). That means if you have one code string changes differently in master and staging, it will take staging changes. Also, I will recommend you to use --no-commit option, so you will be able to check your code once before commit merge (not for every change).

4

I use next strategy. At first, checkout from develop branch to other:

  git checkout -b feature/resolve-conflicts

Next step, you must pull code from master into feature branch:

  git pull origin master

Next resolve conflicts and push feature branch into git:

  git add --all
  git commit -am 'resolve conflicts'
  git push -u origin feature/resolve-conflicts

Then create pull request and compare feature/resolve-conflicts with master branch. On different platforms it makes with different ways. After merge feature branch into master then create new pull request merging develop branch into master branch.

Now, changes in develop must normally merged into master branch with commits.

It works for me, hope i can help.

Manfice
  • 149
  • 7
  • 1
    A caution that you end up with an extra merge in your master branch that is not in your develop branch. This likely isn't a problem in most cases, but does cause some divergence between master and develop that could eventually lead to issues down the line. – majorobot Mar 10 '21 at 20:47
  • Yes, I agree with you. It works in many cases but not at all. In complex projects you should think before doing something. @majorobot actually right. Keep in mind this case. – Manfice Mar 11 '21 at 06:35
1

This is a very common problem and there lies an easy solution. Once you are in a merge conflict, you can do this on a file to file basis by

Keep master's file
git checkout --ours myfile.html

Keep development's file
git checkout --theirs myfile.html

Quite easily you can apply this to the entire folder

git checkout --ours ./
git checkout --theirs ./

This is better than a git merge because you get to see the conflicted files as well. More details are here https://www.kernel.org/pub/software/scm/git/docs/git-checkout.html

Gaurav Ramanan
  • 3,655
  • 2
  • 21
  • 29
  • The checkout to "." fails if some of the files are new/deleted. Really annoying bug IMHO. – Alex R Dec 30 '14 at 16:46
  • @AlexR Maybe you can `git stash` it first? – Gaurav Ramanan Dec 30 '14 at 17:31
  • 2
    What if you have 1000s of files and you are confident the current "Develop" branches state is exactly as you want it. Is there a simple way to just "Copy" the latest development commit into master?> – rollsch Feb 11 '18 at 04:24
  • @rolls I am in the same situation as you are (I know that the changes in the dev branch are the once I want to keep), did you find a way to "copy" the dev into master without the conflicts? – GileBrt Oct 22 '18 at 09:58
  • I think I used a rebase from memory – rollsch Oct 23 '18 at 10:07