My previous post discussed the basic get and checkin operations that are key to the operation of source control. It also indicated that these operations are not in practice sufficient to build a usable change control system. This post will discuss why and go through the mechanisms source control systems use to address these problems.

Take an example system which contains the class TestItem.cs, defined as:
public class TestItem
{
public int FirstValue
{ get; set; }
public int SecondValue
{ get; set; }

public int CalculateResult()
{
return FirstValue * SecondValue;
}
}
Developer A modifies the code to be:
public class TestItem
{
public int FirstValue
{ get; set; }
public int SecondValue
{ get; set; }

public int CalculateResult(int scale)
{
return FirstValue * SecondValue * scale;
}
}
Simultaneously, Developer B modifies the code to be:
public class TestItem
{
    public int FirstValue
    { get; set; }
    public int SecondValue
    { get; set; }

    public TestItem(int firstValue, int secondValue)
    {
        FirstValue = firstValue;
        SecondValue = secondValue;
    }

    public int CalculateResult()
    {
        return FirstValue * SecondValue;
    }
}
If the repository simply accepts whatever changes are submitted the result of these two incompatible changes will likely be a broken build. The last change performed will become the latest version. This means that the earlier change will be overwritten. Each of the changes above would require alterations in other code and it is possible that this would be in different areas for each change. The result is a partial implementation of each change. Some invocations of CalculateResult would specify a parameter, others would not, as would some uses of the object constructor. This would not compile.

Hence source control provides mechanisms to prevent or to detect and resolve these incompatible changes. This is through the use of a checkout model.

The conceptually simplest solution is to implement exclusive checkout. In this system a developer may not edit any file under source control by default. In order to edit a file they must request a checkout from the respository. This is a token for the file managed by the source control system. In exclusive checkout only one user may check out a file at a time. This eliminates the possibility of simultaneous edits to the file.

The alternative is a merge on checkin model. In this model multiple developers may edit a file simultaneously. Some system will require them to check the file out first but where this is required multiple checkouts may be performed. The first developer to submit their changes has them accepted immediately. For further checkins a merge step is required.

The merge step requires the developer doing the checkin to resolve conflicts between the latest version in the repository and their own local copy. This is generally performed with a tool that can perform a three way merge and in most systems prevents the changes from being checked into the repository. Once the merge has been performed the developer may check their changes in again. Assuming there are no further conflicts (which would require further merges) the changes are then accepted.

There are proponents of each model. However it is important to note that proponents of exclusive checkout are wrong and that for anything but single developer environments exclusive checkout is limiting and has a negative impact on productivity.

When multiple developers are working on a single codebase there are inevitably key files that are edited more frequently than others. Demanding that only a single developer may edit one of these files at a time puts chokepoints on development. Developers must wait until a file becomes free, requiring overhead to monitor the state of the files they need to work with. At times they may be unable to proceed because they cannot access the requisite files. This may lead to the use of poor coding practices in order to work around their inability to make the changes they need in the appropriate places.

Merging is also an overhead, but in comparison it is significantly less so. In practice many edits to a single file are non-conflicting and may be resolved quickly with automated tools. Conflicting changes will still need manual effort to be resolved. However exclusive checkout does not eliminate conflicting changes, it merely hides it by requiring a developer to account for previous changes when they are making their own. Decent tooling support means that resolving conflicting changes is a managable overhead.

It is also possible for a developer to initiate a merge without performing a checkin. Generally this occurs when they get the latest version from the repository. This is useful in ensuring that their changes remain in sync with the source of truth, reducing the merge overhead when they are ready to check in.

Unfortunately merge on checkin does not resolve all issues with multiple developers. My next post will discuss these issues and practices teams may adopt to resolve them.