Published by Marco on 24. May 2013 08:59:33
------------------------------------------------------------------------

This article originally appeared on "earthli News"
 and has been cross-posted
here.

--------------------------------------------------------------------------------


I was recently asked a question about merge conflicts in source-control systems.

"[...] there keep being issues of files being over written, changes backed out
etc. from people coding in the same file from different teams."

My response was as follows:

"tl;dr: The way to prevent this is to keep people who have no idea what they're
doing from merging files."

[Extended version]

Let's talk about bad merges happening accidentally. Any source-control worth its
salt will support at least some form of automatic merging.

An automatic merge is generally not a problem because the system will not
automatically merge when there are conflicts (i.e. simultaneous edits of the
same lines, or edits that are "close" to one another in the base file).

An automatic merge can, however, introduce semantic issues.

For example if both sides declared a method with the same name, but in different
places in the same file, an automatic merge will include both copies but the
resulting file won't compile (because the same method was declared twice).

Or, another example is as follows:


[Base file]


public void A(B b)
{
  var a = new A();

  b.Do(a);
  b.Do(a);
  b.Do(a);
}



[Team One version]


public void A(B b)
{
  var a = new A();

  b.Do(a);
  b.Do(a);
  b.Do(a);
  a.Do();
}



[Team Two version]


public void A(B b)
{
  var a = null;

  b.Do(a);
  b.Do(a);
  b.Do(a);
}



[Automatic merge]


public void A(B b)
{
  var a = null;

  b.Do(a);
  b.Do(a);
  b.Do(a);
  a.Do();
}



The automatically merged result will compile, but it will crash at run-time.
Some tools (like ReSharper) will display a warning when the merged file is
opened, showing that a method is being called on a provably null variable.
However, if the file is never opened or the warning ignored or overlooked, the
program will crash when run.

In my experience, though, this kind of automatic-merge "error" doesn't happen
very often. Code-organization techniques like putting each type in its own file
and keeping methods bodies relatively compact go a long way toward preventing
such conflicts. They help to drastically reduce the likelihood that two
developers will be working in the same area in a file.

With these relatively rare automatic-merge errors taken care of, let's move on
to errors introduced deliberately through maliciousness or stupidity. This kind
of error is also very rare, in my experience, but I work with very good people.

"Let's say we have two teams:
Team One - branch one
> Works on file 1
Team Two - branch two
> Works on file 1
Team One promotes file 1 into the Master B branch, there are some conflicts that
they are working out but the file is promoted."

I originally answered that I wasn't sure what it meant to "promote" a file while
still working on it. How can a file be commited or checked in without having
resolved all of the conflicts?

As it turns out, it can't. As documented in "TFS Server 2012 and Promoting
changes"
,
promotion simply means telling TFS to pick up local changes and add them to the
list of "Pending Changes". This is part of a new TFS2012 feature called "Local
Workspaces". A promoted change corresponds to having added a file to a change
list in Perforce or having staged a file in Git.

The net effect, though, is that the change is purely local. That is has been
promoted has nothing to do with merging or committing to the shared repository.
Other users cannot see your promoted changes. When you pull down new changes
from the server, conflicts with local "promoted" changes will be indicated as
usual, even if TFS has already indicated conflicts between a previous change and
another promoted, uncommitted version of the same file. Any other behavior else
would be madness. [1]

"Team Two checks in their file 1 into the Master B branch. They back out the
changes that Team One made without telling anyone anything."

There's your problem. This should never happen unless Team Two has truly
determined that their changes have replaced all of the work that Team One did or
otherwise made it obsolete. If people don't know how to deal with merges, then
they should not be merging.

Just as Stevie Wonder's not allowed behind the wheel of a car, neither should
some developers be allowed to deal with merge conflicts. In my opinion, though,
any developer who can't deal with merges in code that he or she is working on
should be moved another team or, possibly, job. You have to know your own code
and you have to know your tools. [2]

"Team One figures out the conflicts in their branch and re-promotes file one
(and other files) to Master B branch. The source control system remembers that
file 1 was backed out by Team Two so it doesn't promote file 1 but doesn't let
the user know."

This sounds insane. When a file is promoted -- i.e. added to the pending changes
-- it is assumed that the current version is added to the pending changes, akin
to staging a file in Git. When further changes are made to the file locally, the
source-control system should indicate that it has changed since having been
promoted (i.e. staged).

When you re-promote the file (re-stage it), TFS should treat that as the most
recent version in your workspace. When you pull down the changes from Team 2,
you will have all-new conflicts to resolve because your newly promoted file will
still be in conflict with the changes they made to "file 1" -- namely that they
threw away all of the changes that you'd made previously.

And, I'm not sure how it works in TFS, but in Git, you can't "back out" a commit
without leaving a trail:

  * Either there is a merge commit where you can see that Team Two chose to
    "accept their version" rather than "merge" or "accept other version"
  * Or, there is a "revert" commit that "undoes" the changes from a previous
    commit

Either way, your local changes will cause a conflict because they will have
altered the same file in the same place as either the "merge" or "revert" commit
and -- this is important -- will have done so after that other commit.

To recap, let me summarize what this sounds like:

  * T1: I want to check in file1
  * TFS: You have conflicts
  * T1: Promote file1 so that TFS knows about (other users can't see it yet
    because it hasn't been committed)
  * TFS: Okie dokie
  * T2: I want to check in file1
  * TFS: You have conflicts
  * T2: F&#$ that. Use my version. Oh, and, f&#$ T1.
  * TFS: I hear and obey. T2/file1 it is.
  * T1: OK, I resolved conflicts; here's the final version of file1
  * TFS: Thanks! *tosses T1/file1 out the window*

I don't believe that this is really possible -- even with TFS -- but, if this is
a possibility with your source-control, then you have two problems:

   1. You have team members who don't know how to merge
   2. Your source control is helping them torpedo development

There is probably a setting in your source-control system that disallows
simultaneous editing for files. This is a pretty huge restriction, but if your
developers either can't or won't play nice, you probably have no choice.

--------------------------------------------------------------------------------


[1] This is not to rule out such behavior 100%, especially in a source-control
    system with which I am largely unfamiliar. It only serves to indicate the
    degree to which I would be unwilling to work with any system that exhibits
    this kind of behavior.


[1] Different companies can have different grace periods for learning these two
    things, of course. I suppose that grace period can be interminably long,
    but...