32

Here is the deal, I have joined a new company and have been asked to finish off the work on a branch which hasn't been touched for almost a year. In the meanwhile, the master branch has been growing with a steady pace. Ideally I would like to merge all of the changes from the master branch into the feature branch and continue the work from there, but I'm not too sure how to approach this.

How do I perform this merge safely while preserving important changes on both sides of the branch?

6 Answers6

35

At its heart, how to combine two (possibly non-compatible) pieces of code is a development problem, not a version control problem. The Git merge command may help in this process, but it depends on the shape of the problem.

Comparing both versions with the base first makes the most sense. This will give you an idea of the best strategy for taking this forward. Your approach might be different based on the nature and overlap of the changes in each branch.

Imagine the ideal scenario: you would discover that the main branch and the feature branch each only modified mutually exclusive parts of the code, so you could just commit all the changes in and be good to go.

Of course, that will almost certainly not be the case, but the question is: how far removed from this ideal scenario will it be? i.e. how intermingled are the changes?

Also, how mature was the old feature branch? Was it in a good working state, or not (or unknown)? How much of the feature was finished?

If the relevant code in the main branch has changed a lot in the past year, or the feature is not in a very mature state, I might consider creating a new fork of the latest and manually incorporating the old feature in again. This will allow you to take an incremental approach to getting it working.

If you do a messy merge of lots of code and it doesn't work, it will be quite hard to debug. If the main branch has changed a lot over the past year, major design changes may be needed to the feature to get it working. It would not be appropriate to make these changes via "resolve conflicts", since this would require making all the changes at once and hoping it works. This problem would be compounded by the possibility of bugs in the old partially finished branch.

29

In my limited git experience I can say that sometimes it's faster to restart the feature branch over again if the master has gone too far from the detach point.

Merging two branches without knowing the history behind the code (given that you've just joined the project) is really difficult, and I bet that even a developer who followed the project from the beginning would likely make some mistakes in the merge.

This of course makes sense if the feature branch is not huge, but you could simply keep the old feature branch opened, branch again from master and manually re-introduce changes that compose that feature. I know it's the most manual approach, but it allows you to be in complete control in case of missing or moved code.

Pair programming with a senior in this case would be the best case scenario, helping you to get to know the code better.
It could even turn out to be faster too, if you take into account merge conflicts and testing time!

I kinda of assumed that at least trying to do a merge is obviously the best thing to do. If that fails or turns out to be too difficult, then try the cherry picking, if that goes wrong go the manual way.

21

git-imerge is designed exactly for this purpose. It is a git tool which provides a method for incremental merging. By merging incrementally, you only need to deal with the collisions between two versions, never more. Furthermore, a far larger number of merges can be performed automatically as the individual changesets are smaller.

Joe
  • 368
7

Trying to merge the mainline head onto a year stale branch can be an exercise in frustrations and deepening the dent on the desk with your forehead.

The mainline didn't get to where it is in one go over the course of months. It too had development and releases. Trying to bring it all up to date in one monolithic merge can be overwhelming.

Instead, start out by merging from the first feature merge back into mainline after the stale branch split. Get that merge working. Then the next feature merge. And so on. Many of those feature merges will merge in without conflict. It is still important to make sure that the current functionality of stale branch remains compatible with the direction that the mainline has gone.

You may wish to branch from the head of the stale branch for the role of merging in other changes. This is more about making sure that the commits and the history when someone looks back at it is clear and communicates what the role and policy of each branch is. The stale branch was a feature branch. The one you are working from is an accumulation and reconciliation branch.

Much of this will be easier if the old feature or release branches still exist out there and are easily accessible (some places have a policy of cleaning up the names of branches that are older than some date so that the list of branches isn't overwhelming).

The important thing in all of this is to make sure that you test and fix after successfully merging each part of the mainline history into place. Even though something may merge without conflicts, that just means the code didn't conflict. If the way that the stale feature was accessed was deprecated or removed, there may need to be fixes after the successful merge.

As an aside, this works for other version control systems too. I've occasionally had need to merge a specific group of svn commits into a branch (cherry picking) for one feature, fix the branch to work with that feature, and then merge the next group of svn commits rather than just doing a wholesale svn merge.

While one can do a git cherry-pick here, and it does allow bringing in specific commits, this has some disadvantages that may complicate the process. The cherry pick will not show information about the commit you picked from (you can append it to the commit message). This makes actually tracking the commits in the history harder.

Furthermore, it means that you aren't going to effectively replaying the master onto the stale branch - you are going to be picking possibly incomplete features - and those features may be played out of order.

The key reason that one should merge from historical commits to master onto the stale branch is to be able to keep the, lets call it "future history" of the stale branch in a state that you can reason about. You can clearly see the merges from history onto the stale branch and the fixes to reintegrate the functionality. The features are being added in the same order as they were to master. And when you are done, and finally do the merge from the head of master onto the stale branch, you know that everything has been merged and you aren't missing any commits.

1

Step 1. Learn about the code, analyze its architecture, and the changes that have been made on both branches since the latest common ancestor.

Step 2. If the feature appears broadly independent and touches mainly different areas of code, merge, fix conflicts, test, fix etc. The is the happy path, you're pretty much good to go. Otherwise go to Step 3

Step 3. Analyze the areas of conflict, understand the functional impact and reasons in detail. There could easily be conflicts in business requirements that come to light here. Discuss with BAs, other devs as appropriate. Get a feel for the complexity involved with resolving the interference.

Step 4. In light of the above, decide whether to aim to merge/cherry-pick/even cut-paste only those parts that do not conflict and re-writing the conflicting pieces, OR whether to re-write the whole feature from scratch.

0

1. Switch to the branch which is used as a main developer/release branch.

This is the branch which contains the latest changes to the system. Can be master, core, dev, it depends on the company. In your case it is probably master directly.

git checkout master
git pull

Pull to make sure you have the latest version of the main development branch aquired.

2. Checkout and pull the branch which contains the work you are supposed to finish.

You pull to make sure you indeed have the latest contents of the branch. By checking it out directly, without creating it locally first, you ensure not to have the new contents from master (or the main dev branch respectively) in it.

git checkout <name of the obsolete branch>
git pull origin <name of the obsolete branch>

3. Merge the main development branch to the obsolete branch.

Before running the following command, make sure, either by typing git branch or git status that you are on the obsolete branch.

git merge master

The git merge command will try to merge the contents from the specified branch, in this case master, to the branch you are currently at.

Emphasis on will try to. There might be merge conflicts, which will need to be resolved by you and you only.

4. Fix the merge conflicts, commit and push the conflict fix

After fixing the merge conflict in all the files where there is, stage, commit and push the conflict resolution to origin.

git add .
git commit -m "fixed the merge conflict from the past year to update the branch"
git push

You can generally call git add . to stage all the files for commit. When dealing with merge conflicts, you want all the necessary files to be updated.

Additional note

Resolving merge conflict can be a tedious work. Especially if you are new at a company. You might not even have the proper knowledge to resolve all the merge conflicts alone, yet.

Take your time to carefully inspect all the conflicts that have occured and fix them appropriately, before continuing your work.


It can happen so, you start working on an one year old branch, merge the current development state into it and won't have any merge conflicts at all.

This happens when even though the system has changed a lot in the year, nobody has touched the files which were actually altered in the one year old branch.

Andy
  • 10,400