79

Story: in the middle of a project my colleague created a new branch from master and started doing her heavy re-factoring work. I created my branch from master and started doing new stuff on the page. We are committing regularly, but only I can rebase code to master (because colleagues changes are too heavy and cannot be deployed from master yet). Unfortunately some of our work rely on the same files. So after few days of work when she finally wanted to rebase her changes to master, she had a lot of git conflicts.

my_branch    #---#----#-#-------#----#--#-----#---#----#----#
            /     \              \   \   \              \    \
master     *-------*--------------*---*---*--------------*----*----*
            \                                                     /
her branch   #------#-------#-----------#-----------#------------#

Question 1 is: how to prevent lot of git conflicts when we are working on same files? (or what is the best practice in this situation?)

but this isn't the end of our question, ...to be absolutely correct she tried to do rebase from master to her branch (to have changes I committed), so the commit map should look something like this

my_branch    #---#----#-#-------#----#--#-----#---#----#----#
            /     \              \   \   \              \    \
master     *-------*--------------*---*---*--------------*----*----*
            \                   \            \                    /
her branch   #------#-------#----*------#-----*-----#------------#

And this is what is bothering us. During these rebases she was fixing those conflicts. But git doesn't remember her decision on conflict fix, so when she did another git rebase from master to her-branch she had to fix the same git conflicts again that she was fixing in previous rebases.

Question 2 is: how to tell git to remember git conflict fix after git rebase from master branch, so after next rebase we don't have to fix the same conflicts again?

Rob Bednark
  • 25,981
  • 23
  • 80
  • 125
equivalent8
  • 13,754
  • 8
  • 81
  • 109
  • oh, I forget, my git version: 1.7.4.1 , her should be the same – equivalent8 Aug 30 '11 at 10:17
  • 1
    Exact question I want to ask! – kakyo Mar 15 '13 at 21:35
  • 1
    one more update - past few years I'm doing just `git merge` instead of `git rebase`. Yes, some folks say that by using `merge` you don't have the correct history which is not true. Watch https://www.youtube.com/watch?v=1ffBJ4sVUb4 to understand how everything in git works ( + `rebase` can be actually destructive ) My point is `git merge` is much more productive. I guess than's the reason why github is also using `merge` in they web interface when dealing with pull requests – equivalent8 May 12 '16 at 08:26
  • 1
    @equivalent8 it makes a mess out of your branch history though, I prefer rebasing because it creates a clean git history for the feature-branch I work off. Git merge is great for pull requests because it gives a visual representation of when a pull request was merged into master. – D. Foley Aug 15 '17 at 01:45
  • @D.Foley I fully agree, it's cleaner. For super small projects and projects I work alone I use rebase too. But pragmatically for projects with lot of developers committing frequently it's much more pragmatic to use `merge` otherwise 10% of everyone time will be just resolving rebase conflicts – equivalent8 Aug 15 '17 at 10:04

6 Answers6

85

Fortunately, git has a mechanism for dealing with exactly this problem called git rerere - essentially, if you have git rerere enabled, then each time your resolve a conflict the fact that you resolved that exact conflict in a particular way is remembered. If the same conflict comes up again, the same resolution is automatically used. There are some helpful articles below:

... but essentially you can just do:

git config --global rerere.enabled 1

... and forget about it, while enjoying easier rebasing / merging :)

Gustavo Bezerra
  • 9,984
  • 4
  • 40
  • 48
Mark Longair
  • 446,582
  • 72
  • 411
  • 327
  • 9
    just to add you may want to `git config --global rerere.autoupdate true` as well. Also, if you want others in your team to make use of those recorded resolutions, you can share your .git/rr-cache folder with them. Cheers. – Adam Dymitruk Aug 30 '11 at 18:23
  • 2
    Any downsides to enabling the global config setting? – bryanbraun Aug 13 '13 at 16:42
  • 6
    @bryanbraun: Yes, in that if you record a resolution to a conflict in a particular way, it'll be resolved automatically the next time you see that exact conflict - if you'd resolved it in the wrong way (whatever that might be) one time, you might not even notice it being automatically resolved that way again in the future... – Mark Longair Aug 15 '13 at 08:42
  • Can it be done for a specific branch? I wouldn't like a conflict resolution to be used through all of the project, but it would feel safer if I could choose the branch for that – FabioR Oct 01 '19 at 14:11
  • @FabioR You need to use local configs for per branhc. git config --local rerere.enabled 1 will do the job – Deekshith Anand Feb 22 '22 at 06:08
  • @DeekshithAnand, I don't think `--local` will work per-branch (unless ProjectRoot/.git/config were being tracked by git, which is atypical). `--local` works per-repository/per project, which @FabioR was looking to avoid ( https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration ) – Joshua Goldberg Sep 23 '22 at 15:59
13

Let me share one possible way to resolve rebase conflicts. I call it rebase via merge. It can help if you want to rebase a branch with many commits and many conflicts are expected.

First, let's create a temp branch and force all conflicts to show up with a regular merge

git checkout -b temp
git merge origin/master

Resolve all the conflicts the regular way and finish the merge.

So temp branch now shows how the project should look like with all the conflicts resolved correctly.

Now let's checkout your untouched branch back (let it be alpha).

git checkout alpha

And do a rebase with mechanical conflict auto-resolution in favor of current branch.

git rebase origin/master -X theirs

The project code can be broken or invalid at this moment. That's fine, the last step is to restore the project state from temp branch with a single additional commit

git merge --ff $(git commit-tree temp^{tree} -m "Fix after rebase" -p HEAD)

Basically, this step uses a low-level git command to create a new commit with exact same project state (tree) as in temp branch. And that new commit is being merged immediately.

That's it. We just did a rebase via hidden merge. And temp branch can be deleted.

git branch -D temp

Also, there is a script to do the same thing interactively. It can be found here.

Adam
  • 1,796
  • 18
  • 19
  • 1
    on my console, {} are interpreted, so I have to protect them with single quote like this: `git merge --ff $(git commit-tree 'temp^{tree}' -m "Fix after rebase" -p HEAD)` – aki Aug 25 '22 at 14:17
  • Will this method keep the linear history like git rebase? – jacob galam Dec 11 '22 at 12:32
  • Yes, the history remains linear like a regular rebase. – Adam Dec 12 '22 at 15:45
6

Make sure that you are always rebasing using the --onto switch.

To prevent conflicts, use floating development branches. Each developer will continuously rebase their development branch. This is easy since the developer knows what he just implemented and shouldn't have problem with solving conflicts. Instead of rebasing, just merge the final version (it will already be rebased).

Šimon Tóth
  • 35,456
  • 20
  • 106
  • 151
1

You can squash her branch to prevent Successive conflict resolving. when you squash all commit in her branch after creating from master into one commit, then conflict can be resolve in one step.

If you want to write the new commit message from scratch, this suffices:

git reset --soft HEAD~3 &&
git commit

In this example, we'll squash the last 3 commits.

To prevent this problem in the future, I recommend you to rebase your branch with source branch after each commit.

1

If 2 devs made different changes to the same line of code, conflicts will be unavoidable, as git will not know which like it should keep or discard.

As for rebase from master, this is really not the ideal practice unless you want to deliberately change the commit history (and possibly change the commit history of your colleague).

From the official documentation about rebasing:

Do not rebase commits that exist outside your repository and that people may have based work on.

You should be using git merge instead.

enter image description here

SeF
  • 3,864
  • 2
  • 28
  • 41
0

I do not use rebase anymore for anything other than simple use cases. cherry-pick is almost always easier to use and requires a simpler mental model. The most complicated thing with cherry-pick is sometimes you need to create a new clean branch and apply the cherry-pick commits there.

miguel
  • 16,205
  • 4
  • 53
  • 64