Driving a PITA merge conflict to the release branch

Friday, December 02, 2016 0 Comments

Everything was green and working automatically while merging a feature branch to our latest release branch. In fact, I was about to meet my colleagues in the coffee room for a break, but... Ouch! A big red conflict involving a lot of lines appeared. At first glance, I have no idea what happened to the conflicting file. Cancel the coffee break? Hold on, not yet, not today!

Let me show you the steps I followed to solve a non-trivial merge conflict with Plastic SCM and have the feature branch integrate and checked-in into the release branch. For this purpose, I used some 2nd-class-citizen features in Plastic that definitively I have to promote to 1st class after this experience.

First of all, let's have a look at the conflicting file when merging the feature branch:

Notice I use SemanticMerge as conflict resolution tool. SemanticMerge shows me three conflicts that require user intervention in the conflicting file, named ChangesetCommands.cs:

  • The Execute() method in CsBrowseChangesetCommand class has changed in the feature branch (the blue contributor in the screenshot).
  • Two new members were added in CsBrowseChangesetCommand class in the feature branch too.
  • The CsBrowseChangesetCommand class has disappeared from the ChangesetCommands.cs file in the release branch (the destination contributor, the green one).

The three of them can be summarized in the following statement: The feature branch changed some code in the class, but the class does not exist in the release, destination branch.

It's a change / delete conflict scenario that requires a developer to decide what to do.

But the question is: Was this code really erased from the source tree by a team mate? Hmm, probably not, but I have to confirm this fact... Let's analyze each contributor in the merge:

  1. The source changes in the merge conflicts are crystal clear:
    The feature branch changed the code to implement the required feature, as the following diff shows:
    As you can see, the Semantic Outline in the Diff window shows the exact same symbols involved in the merge: two new fields and Execute() method changed.
  2. The release, destination branch:
    Where did the deleted code go in the release branch?

The Explain Merge feature

The first feature I want to show you is the Explain Merge feature, which is available from the Merge branch view:

Clicking on Explain merge button, a new Branch Explorer view appears filtered with just the branches involved in the merge showing:

In this view, the source changeset in the merge (remember, the blue contributor, corresponding to the feature branch), the base, and the destination changeset (the green contributor, the release branch) are clearly tagged.

With this view, I can easily diff the relevant changesets involved in the merge operation. Because I have doubts with the deleted code between base changeset and the destination changeset in the release branch, I select them and launch a diff between these changesets:

The Analyze refactors feature

This is one of the most amazing features in Plastic: The Diff window has an Analyze refactors button that allows detecting refactored code involving several files without opening any IDE.

The Analyze refactor feature does its magic in the background, so it is still possible to use the Diff window in the meantime. But, once the Analyze refactor calculation is finished (just a matter of few seconds), the Diff window will group related files together by the so-called Refactor groups:

If I navigate to the conflicting file ChangesetCommands.cs, I noticed the file is inside Refactor Group 4. As you can see in the info of this refactor group, the file was moved and the contents changed, and it is grouped together with 20 added files.

If I click on the conflicting file and I look at the bottom panel, the Semantic Outline shows us very helpful info on what happened between diffed changesets:

Look in detail at what the Semantic Outline says: The CsBrowsePlasticDriveCommand class from the conflicting ChangesetCommands.cs file, was moved to a different class file, named CsBrowsePlasticDriveCommand.cs (which is listed in the same refactor group in the diff viewer by Analyze refactors feature).

If I now click on the CsBrowsePlasticDriveCommand.cs (which is grouped in the same refactor group), I will see the original code before changing anything there!

So, I have to conclude the deleted class in the original merge conflict was not erased from the source tree at all, it was moved in a refactor operation!

The Analyze Refactors feature helped me to fully understand what my team mate did in the refactor (Refactor Group 4 from the screenshot above):

  1. Every class in the ChangesetCommand.cs file was moved to a new class file.
  2. The ChangesetCommand.cs file was renamed to CsSwitchToChangesetCommand.cs and now it just contains the CsSwitchToChangesetCommand class.

Now that I fully understand what happened during each contributor's divergent timeline, I can safely make decisions to finish the conflicting merge: I have to apply the changes done by the feature branch not in the original file, but in the new class file CsBrowsePlasticDriveCommand.cs created in the refactor.

To do that, discard the source changes from the feature branch in the SemanticMerge conflicts list:

And apply source changes from the feature branch in the CsBrowsePlasticDriveCommand.cs file:

As you can see in the Semantic Outline, the applied changes in the new class file matches again with the original conflicts and the feature branch diff, which were the following:

  • Two new members in the class.
  • The Execute() method body has changed.

Now I'm able to enjoy a coffee break while I check the changes compile ;)
(Yes they do!)

0 comentarios: