Search code examples
gitgit-mergemerge-conflict-resolution

Big git merge - split resolution across commits


Say I have a core-master branch and a core-client1 branch, that have split a good while ago (>2months).

core-master is used by our main product, and core-client1 was needed for a white-label version for a client. We couldn't merge until now cause of release schedules.

Now that it's possible, I'm merging the core-client1 changes back to core-master.

I would like to "break up" the conflict resolutions into separate commits so they could be reviewed/PR'd separately, and reverted separately if needed.

But since I have conflicts remaining - git won't let me commit some of the files.

I realize I could mark all as resolved and start going back to them - but then I would have to keep track of which files saved myself, and the history would be such that commited conflicts exist in the repo for many of the commit states.

I've searched around for "git partial merge commit" "git split merge" but have only found info about picking certain commits from core-master, which is irrelevant in my case.

Is there a proper way to do this?


Solution

  • I will try to describe a strategy I adopted in a similar situation :

    For a time (a few month), we had two versions living side by side :

    *--x--*--*--*--*--*--* <- master
        \
         A--B--C--D--E--F <- side-version  # the actual nr of commits was more like 100
    

    Merging side-version in master in one go was not feasible.

    What I ended up doing was to merge bit by bit :

    # merging bit by bit :
    *--x--*--*--*--*--*--* <- master
        \                 \
         \                 *-----*-----*--* <- fusion
          \               /     /     /  /
           --------------A--B--C--D--E--F <- side-version
    

    For ease of use : I didn't commit straight on master, I created a fusion branch and committed the merging there.

    On the history of side-version, I listed some key commits (in our case : I used the commits corresponding to released versions), and started merging those versions, one at a time.

    Now, since both versions were live, in some cases, the bugs fixed on side-version had also been fixed on master, so sometimes, "merging" resulted in discarding the content of side-version and keeping the version on master.

    Having isolated points mainly allowed me to :

    • solve conflicts on a reduced set of files, and with a scoped changelog, so it was easier to validate the behavior at each step ("the behavior on master, plus a handful of features" rather than "both versions combined")
    • merge the unit tests, and validate them on each new merge commit I created
    • in some cases, start the server in the current merged state, and check some features on my dev machine

    The choice of "key commits" really depends on your setup :

    • you may have only a handful of commits on side-version, which all describe a state where your product is runnable and testable, in which case merging commits one by one is perfectly ok,
    • you may have some way to clearly identify "this is where this specific feature was added", "this is where this bug was fixed", and use these,
    • you may choose to arbitrarily split the history of side-version in ten chunks ...

    Whatever suits your needs.


    The choice of the merge points can also be adapted :

    • in my case, it wasn't a stopper to have an absurd commit stating "the version of master in august combined with side-version in may"
    • if you have an incentive to aim at more realistic merges, you can also identify "key commits" on master :
    # an alternative strategy :
    *--x---P--Q--R--S--T--U--V <- master
        \      \     \        \
         \   *--*--*--*--*--*--* <- fusion
          \ /     /     /  /
           A--B--C--D--E--F <- side-version
    

    The merging did take some time (several days), during which the master branch evolved. When I reached my first satisfying fusion state, I actually had to merge in some extra features and fixes released on master.

    In our case, it was acceptable to have a history of merged commits. If you have the need however: once you have reached a satisfying state for fusion, you may choose to rewrite its history, to split commits in some more meaningful chunks.