4

My colleague and I are having an vigorous discussion about merge strategies and I'm hoping we can get some information to help resolve it.

The tl:dr is: Should we use merge or rebase when pulling changes from a remote branch so that we are not constantly redoing the conflict resolution.

  • svn: our master golden repository
  • trunk: git remote branch tracking svn. Used to stage the git change before dcommitting to subversion.
  • feature-branch: git remote branch where the work of 2+ colleagues on one feature is combined
  • colleague1: First colleague's local branch
  • colleague2: Second colleague's local branch.

We are using the Sebastien Varette's excellent workflow from Is git-svn dcommit after merging in git dangerous?.

The problem we are getting is that every time colleague1 rebases from svn (by rebasing to trunk and then rebasing to colleague1 before pushing to feature-branch when done) then commits on feature-branch which conflict against each other have to be re-done. The same conflict resolution has to be done time and time again - every time the svn rebase workflow is done.

Note that we are using git rebase mirror un-3451 for this rebase.

My argument is that we should use git merge mirror un-3451. My understanding is that this would yield a commit which is marked as the merge of two previous commits. Therefore, git would know not to use the previous commits but using the merge commit instead.

My colleague, on the other hand, argues that using merge will make the eventual merge with svn much harder and much prefers using rebase. He finds it easy - sadly he's not the one resolving all the conflicts. He argues that using a squash commit would yield the same effect.

My argument is that the merge has the same effect as a squash but includes marking the commits that were included.

State 1

svn-trunk:      A
                \
git-trunk:       A'
                 \ 
remote-feature:   A'-----C
                  \     /
c1-feature:        A'--C
                   \
c2-feature:         A'--D

State 2

C conflicts with D on File 1(F1). F1 has the conflict resolved so D becomes D'.

git pull --rebase remote feature
git add F1
git rebase --continue
git push

Commit graph:

svn-trunk:      A-----B
                \     \(svn rebase)
git-trunk:       A'----B'   
                 \ 
remote-feature:   A'-----C---------D'
                  \     / \       /
c1-feature:        A'--C   \     / (git push)
                   \        \   /
c2-feature:         A'-------C--D'

State 3

Now, colleague1 wants to pull in the changes from svn-trunk:

git checkout trunk
git svn rebase

and then rebase them onto c2-feature

git checkout feature-branch
git rebase trunk

The rebase puts B1 onto c2-feature and then C and then grabs D' and forces the same conflict resolution to become D''

Commit graph:

svn-trunk:      A--------B
                \         \(svn rebase)
git-trunk:       A'--------B'   
                 \         \
remote-feature:   A'-----C--\-----D'
                  \     / \--\--\  \       
c1-feature:        A'--C      \  \  \    
                   \           \  \  \  
c2-feature:         A'----------B'-C--D''

Each time the git checkout trunk; git svn rebase; git checkout feature-branch; git rebase trunk; git push remote cycle happens all the conflict resolutions have to be done again and again.

Questions

  1. if you rebase from svn, push to the remote branch, have colleague2 push some commits to the remote branch and, then later on, colleague1 does a pull merge from remote branch will the resulting merge commit mention the svn commits (and therefore cause a problem on subsequent rebases).
  2. Which is better in the scenario above: rebase or merge?
  3. Would the rerere functionality be of more use?

Many thanks!

Community
  • 1
  • 1
Sarge
  • 2,367
  • 2
  • 23
  • 36
  • Could you plot a commit graph to illustrate your process in greater detail? Personally I would be inclined to rebase to keep the history linear on all the remote branches – prusswan Dec 09 '11 at 19:35
  • As requested, @prusswan, I've added the commit graphs. Many thanks for any insights. – Sarge Dec 13 '11 at 18:09
  • I don't have time now to produce a full-length answer, but my first impression is that the rebase against trunk should have been performed on top of D' (which is a 'safe' commit point to rebase on), which is the whole point of using rebase in such scenarios. – prusswan Dec 14 '11 at 03:48
  • @prusswan but is that not the behaviour of `git svn rebase`? That it will find the closest node shared between c2-feature and svn-trunk (A), then roll C and D back, apply B and then apply C and D. – Sarge Dec 14 '11 at 18:12

2 Answers2

0

I'm colleague2.

My position is that its the frequent rebases from SVN to our feature branch that are causing the pain. Each time we rebase the change set is replayed on top of the SVN changes. The actual conflicts are in our replayed change set.

Ideally, we'd squash our change set into a single commit so that the conflicts that occurred in the change set would be resolved, and not have to be resolved again after another rebase.

Alternately, we could wait to rebase from SVN until the feature branch is complete and ready to be committed.

I'm not convinced that switching from rebasing when we pull to merging will improve our process at all. The pain point is the frequent rebases from SVN and I don't believe that merging between the feature branch and our local branches will improve the SVN rebase.

0

First, I will omit the identical nodes and reorganize the nodes such that each branch has its head as the right-most node, to represent the tree better. (correct me if the tree is wrong). My comments are in bold.

State 1

svn-trunk:     A
                \
git-trunk:       A'
                  \ 
c1/remote-feature: \----C
                    \     
                     \
                      \
c2-feature:            --D

Then D was rebased as D' onto C. This is the easy part (to understand). It could have been cherrypick (or "plain" D) if there was no conflict.

State 2

C conflicts with D on File 1(F1). F1 has the conflict resolved so D becomes D'.

svn-trunk:     A-----B
                \     \(svn rebase)
git-trunk:       A'----B'   
                  \ 
                   \
c1-feature:         C   
                     \ 
c2/remote feature:    D'

Now we get to the hard part.

State 3

Now, colleague1 wants to pull in the changes from svn-trunk:

git checkout trunk
git svn rebase

and then rebase them onto c2-feature

git checkout feature-branch
git rebase trunk

The rebase puts B1 onto c2-feature and then C and then grabs D' and forces the same conflict resolution to become D''

Commit graph:

svn-trunk:     A
                \         
git-trunk:       A'----B'
                  \     \ 
                   \     \
c1-feature:         C     \       
                     \     \
remote feature:       D'    \
                             \
c2-feature:                   C----D''

I have some difficulty understanding the transition from State 2 to State 3 since your comments don't seem to match up with the state of the graph (which looks more of an attempt to rebase c2/remote feature onto trunk (at B'), with c2-feature as the result. I believe what you really wanted is this:

Desired State 3

svn-trunk:     A-----B
                \     \(svn rebase)
git-trunk:       A'----B'   
                        \ 
                         \
c1-feature:               C   
                           \ 
c2/remote feature:          D'

which means you were unable to rebase A'-C-D' onto A'-B' without further need for conflict resolution regarding D', which is strange because this should not happen (I have attempted to reproduce this on a git-svn repo and it works as intended). Strictly speaking it isn't even a git-svn problem since you are merely rebasing one git branch against another. It really looks like you are making an attempt to rebase properly, so I can only presume some information is missing, i.e. what I described isn't what that really happened.

prusswan
  • 6,853
  • 4
  • 40
  • 61