Jazz Source Control – Resolving Conflicts
Build basis: Rational Team Concert 1.0
Authors: Source Control Team
Parallel development is ubiquitous in Jazz. We want users to benefit from SCM’s ability to track and version your changes, whether or not you are ready to share those changes with your team. Accordingly, everyone has their own private repository workspace. Sometimes it will be only a couple of hours before you decide to deliver your changes; sometimes it will be a day, or longer. In addition, as outlined in Multiple Stream Development, it’s easy in Jazz to use streams for parallel development. This document is meant as a guide to how to stay friends with your team members in light of the day to day conflicts that arise.
Table of Content
- Potential Conflicts
- Conflict Gestures
- Conflict Types
- Resolving Conflicts
- Example – Bill and Markus modify the same file
- Example – Bill modifies a file renamed by Markus
- Example – Bill adds a file to a folder deleted by Markus
- Example – Bill and Markus assign different file properties to the same file
- Example – Bill auto-merges a file of unknown type
- Example – Bill and Markus create a file with the same name (‘evil twin’)
- Example – Bill and Markus create conflicting symbolic links
Jazz SCM uses an optimistic locking model. Changes are made to files without needing an explicit checkout or lock. Others can also make changes to the same files in their repository workspaces. As a result, someone will have to resolve the conflicts if and when the changes ever need to be integrated into the same stream or repository workspace. More generally though, conflicts can happen whenever a file is forked, that is two states of the file have the same common ancestor, and both states want to be accepted in a repository workspace. Usually conflicts occur when working with other team members, but you can also create forks working by yourself in Jazz; try suspending a change set, then making other changes to the same file in another change set, and then resuming the change set.
(your repository workspace) S1 -> S3 -> S4
(suspended) S1 -> S2
You initially had version S1 of the file in your workspace, you made a change creating version S2 of the file, then suspended the change. You now have S1->S2 suspended. Since you suspended the change to S2, you have version S1 selected again in your workspace. Now make another change to the file creating version S3. You now have a change S1->S3. Resuming the suspended change causes a conflict because Jazz doesn’t know which version to select: S2 or S3? To resolve the conflict you can merge S2 and S3 and create a new state S4. Of course, you could also choose to resolve the conflict by keeping version S3 (“mine”) or version S2 (“proposed”). When a conflict has been resolved, it is considered merged.
For the remainder of this discussion, to better illustrate conflicts that can occur and how to resolve them, we will be using the JUnit example project. On this project we have two developers, Markus Kent and Bill Cassavelli, who both make changes. Markus is quick off the mark and gets his changes delivered to the stream. When Bill accepts his changes into his workspace, he usually has to resolve the conflicts. Figure 1 shows the example project layout.
Figure 1: Example project
Let’s get back to our example. Bill has modified some files in his workspace. Markus has also modified some files and delivered those change sets to the stream. Bill decides he would like to make a change to the SimpleTest class. He notices the incoming arrow, as shown in Figure 2, meaning he has incoming changes affecting that class.
Figure 2: Incoming change decorations
To better understand what is happening to the file, he selects it and, using the Team > Show Pending Changes command from the context menu, shows it in the Pending Changes view (see Figure 3). All the incoming change sets affecting the file are highlighted. He can double-click on each change to see the actual details of the change. Bill can accept Markus’s changes before proceeding to avoid a conflict.
Figure 3: Browsing the incoming changes
For every change in your workspace that hasn’t been delivered, it is quite possible that someone else could have made a change to the same file in another repository workspace or stream. If there does exist such a fork of the file in another stream or workspace, we call this a potential conflict. For every file you are working on, Jazz doesn’t continually keep track of all its forks, instead it keeps track of the changes you are making and the changes in your current flow target. A potential conflict is a property of your workspace and current flow target. As you change your current flow target, the potential conflicts may also change.
Jazz provides awareness about potential conflicts. Potential conflicts are detected when the Pending Changes view refreshes, as shown in Figure 4. To make it easy to find changes to the same files when you select a file in the Pending Changes view it will highlight the others. You can open the compare editor to see the change for each change set.
Figure 4: Potential conflicts in the Pending Changes view
In addition, as shown in Figure 5, the package explorer shows a marker on the files which are potentially conflicted. The compare editor will not show both the conflicting changes together until there is an actual conflict. This allows you to browse the changes made that cause the potential conflict but you don’t need to resolve anything yet.
You cannot deliver with potential conflicts. You can either remove the change or change set from your repository workspace, or, as we’ll see in the next section, you can accept and resolve the created conflicts. To remove a change you can undo it from the change set if it is not completed (and has not been involved in previous conflicts), or you can discard or suspend the entire change set. Figure 6 shows the Pending Changes view after Bill suspends his change set that is in potential conflict with Markus’s incoming change set. The potential conflict decorations no longer show in the Pending Changes view.
Figure 5: Potential conflicts in the Package Explorer
Figure 6: Clearing potential conflicts by suspending
Conflicts occur when you accept changes into a repository workspace. Whereas potential conflicts are a function of the current flow target and are not persisted, conflicts are a property of the repository workspace and live with it until they are resolved. There are currently two main UI gestures that can add conflicts to your repository workspace:
- Accepting changes
- Conflicts can appear in the Pending Changes view when accepting from the stream you are collaborating with.
- Conflicts can appear after accepting a change set from a work item or another workspace’s or stream’s history.
- Resuming suspended changes can also introduce a conflict.
Once you decide to accept the changes that have highlighted potential conflicts, your repository workspace will contain conflicts that have to be resolved. When accepting changes and you see potential conflicts that are unavoidable (i.e. you want to deliver that change to the flow target) you know you will need to merge. Before the conflict is introduced, there are two ways of approaching the conflict, Proactive Merging and Inverse Merging. This is really a work style preference.
- Proactive Merging: In the first approach, you want to merge the incoming changes on top of the work you have done. Basically you want to see clearly how the other changes will be applied to your work in the compare editor. Nothing special has to be done, you just accept the incoming changes. Not all of them will be applied because they are conflicted. You merge the proposed changes on top of your work to resolve the conflicts.
- Inverse Merging: In the second approach, you want to merge your changes on top of the changes done by others. Basically you catch up with your stream and then re-apply your changes on top. To do this, you suspend your outgoing change set(s), accept the incoming changes, then resume your suspended change sets. Not all of your changes will be applied because they are conflicted. You merge the changes you made previously on top of your new current world. This approach can only be done if this is the first time you are encountering conflicts with your changes. Once you have done some merging and resolved conflicts you can no longer suspend the change set because that would re-introduce the conflicts you previously resolved. Instead, you will want to use the proactive merging approach.
Potential conflicts are less urgent than conflicts. It’s always up to you to decide when you want to deal with potential conflicts. Once you have conflicts, it is also your decision when and how you want to resolve them. All conflict resolution happens in the Pending Changes view. Although we may in the future show conflicts and potential conflicts in other UIs, working with conflicts happens in only one view.
In Jazz, conflicts are broken into two categories: content (the contents of a file is forked) and structural. Content conflicts happen most often. Structural conflicts tend to occur when refactoring is taking place. The structural conflicts that can occur are:
- Two people add the same file/folder (also known as “evil twin” conflict)
- One person adds (or moves) a file/folder into a folder that another person is deleting
- One person moves or modifies a file/folder that another person is deleting
- Two people move/rename the same file/folder to different locations or names.
- One person moves a file/folder to a folder where another person has already added a file/folder with the same name.
Potential conflicts are shown in orange and conflicts in red. Each conflict has a tool-tip describing the conflict and possible actions that can take place. The icon gives an indication of whether it is a structural or content conflict. The lightly grayed conflicts are incidental conflicts. In Figure 7, Bill has modified AllTests.java. Markus has also modified the same file. Bill has accepted Markus’s change into his repository workspace, and now has a conflict on AllTests.java. The tool-tip further explains the conflict and how to resolve it.
Figure 7: Tool-tip explaining how to resolve the conflicted file AllTests.java
Example 1: Bill and Markus modify the same file, then Bill merges the changes with the Conflict Compare Editor Bill has modified AllTests.java, SimpleTest.java and VectorTest.java. Markus also modified the same files. Bill has accepted Markus’s change set into his repository workspace and now has conflicts on AllTests.java, SimpleTest.java and VectorTest.java. The Auto Resolve Conflicts dialog prompts Bill after he accepts Markus’s changes, shown in Figure 8.
Figure 8: Prompt offers to auto-resolve conflicts after accepting conflicting changes
Bill usually simply clicks ‘Auto-Resolve’. This time he clicks ‘Resolve Later’ to ignore the prompt, leaving him with the unresolved conflicts in the Pending Changes view, shown in Figure 9. He knows he can also ‘Auto Resolve’ the conflicts later in the Pending Changes view.
Figure 9: Three content conflicts in Bill’s workspace under the JUnit component
The tool-tip on each conflict suggests using ‘Auto Resolve’. When Bill did the Auto Resolve, Markus’s changes were successfully merged in with Bill’s changes for the two files SimpleTest.java and VectorTest.java. Figure 10 shows that only AllTests.java remains conflicted.
Figure 10: Auto Resolve successfully merging two out of three content conflicts
Bill decides to manually merge the content in AllTests.java with the Conflict Editor, shown in Figure 11.
Figure 11: Conflict Editor is used to manually merge content conflicts
Markus uncommented the MoneyTest test suite and Bill decides to keep that change. The Conflict Editor allows him to merge in Markus’s change into his local file that contains his own change. When he is done, he saves his file (Ctrl-s) and resolves it as merged. The conflicts are cleared from the Pending Changes view and Bill can deliver his changes to the stream, shown in Figure 12.
Figure 12: Conflicts have been resolved and change sets can now be delivered to the stream
Markus and Bill can examine any previous change or merge to any resource with the History View. Figure 13 shows the history of the changes for the resource SimpleTest.java.
Figure 13: Resource SimpleTest.java has been merged by Bill
The merge graph in the first column reveals that Bill and Markus modified the same initial version of the file – their version is directly linked to the initial version. The top node in the graph has two incoming edges i.e. the content of two versions were merged together. Bill’s most recent change was merged with Markus’s change.
The next example illustrates how Auto Resolve takes care of merging your content changes into a resource that has been renamed in the stream.
Example 2: Bill adds content to a file renamed by Markus, then uses Auto Resolve to merge his change into the renamed file Bill added a test method testBoolean() to the JUnit test SimpleTest.java. Markus is refactoring the JUnit tests and renamed SimpleTest.java to ComplicatedTest.java. Figure 14 shows Bill has a potential conflict on SimpleTest.java.
Figure 14: Potential conflict between an outgoing modification and an incoming rename of SimpleTest.java
Bill accepts Markus’s change set. This introduces both a content conflict (Bill added content, Markus renamed the Java class name inside the file) and a structural conflict (Markus renamed the file). In Figure 15, Bill hovers over the conflict icon to get detailed information on how to resolve the conflicts.
Figure 15: Content conflict and move conflict for SimpleTest.java
Bill agrees with Markus’s decision to rename the JUnit test. He wants his change (addition of a new method) to be merged with Markus’s change. He selects ‘Auto Resolve’ as suggested by the tool-tips. The conflicts go away, as shown in Figure 16. Bill’s outgoing change set now refers to the new file name.
Figure 16: Conflicts have been resolved
Bill selects the outgoing change and opens a Change Compare editor. Bill verifies Auto Resolve correctly inserted his new method into Markus’s renamed test file, as shown in Figure 17.
Figure 17: Conflicts have been resolved
In an Agile environment, code gets frequently refactored. Jazz resources can be renamed without losing history. This is possible because Jazz internally associates each resource to a unique ID and remembers that resource’s state, name and parent folder. Auto Resolve can deal with the common structural conflict scenarios described above.
The following examples illustrate more advanced types of conflicts that are less likely to occur. Usually this is when extensive refactoring is going on and there are gaps in understanding who is doing what. In the next case, an entire folder is deleted while someone else has delivered changes affecting resources under the deleted folder.
Example 3: Bill deletes a folder while Markus added a file to the same folder. Bill uses ‘Move’ to relocate Markus’s change. Bill decides it is time to remove the Money code from the project. He deletes the money package and all its content. He then notices Markus has just released a new JUnit test to the folder he just deleted, shown in Figure 18.
Figure 18: Potential conflict between an outgoing folder deletion and an incoming file addition to that folder
Bill examines Markus’s incoming change set. He decides Markus’s new test is necessary but it should really be under the samples package along with the VectorTest.java file. Bill accepts Markus’s change set and examines the structural conflict on Markus’s new test. As usual, Bill examines the conflict tool-tip to assess his options, as shown in Figure 19.
Figure 19: tool-tip for a structural conflict
Bill decides to move Markus’s new test to the folder samples. He does this through the context menu shown in Figure 20.
Figure 20: Move… on a conflicted item
The Move Conflicted Items dialog opens up. Bill can move the conflicted item to any folder in his workspace. He moves ListTest.java to the samples folder as shown in Figure 21.
Figure 21: Move Conflicted Items dialog
Bill clicks OK in the Move Conflicted Items dialog. The conflict goes away and Bill can now see Markus’s new test ListTest.java under the samples package. He edits the Java file so its package declaration points to the new folder. Everything compiles and Bill can deliver his change set. Figure 22 shows that ListTest.java has been moved to the samples directory.
Figure 22: Conflict is resolved and change set can be delivered
Bill picked up the most suitable solution to address this conflict. In a different situation, Bill could have decided Markus’s new test was no longer necessary after he deletes all the Money code. Bill would have used Resolve with Mine on the conflicted resource ListTest.java. ListTest.java would be deleted along with the money folder, thus resolving the conflict.
Or Bill may have realized removing the money folder wasn’t a good idea. Bill would simply discard his outgoing change set that deletes the money package and its content.
Figure 23: resource query.xml has been set with inappropriate file properties
Bill sighs. Why did Markus check-in the resource query.xml with the mime type application/unknown and the line delimiter used on the Mac? The default settings would have been much better, i.e. text/plain and Platform delimiter. Bill sets the defaults back with this editor, hits ‘Finish’ and tries to deliver the resulting change. But Markus has been faster than Bill and he delivered a change set also correcting the file properties for that resource. Bill accepts it and gets a conflict (Figure 24).
Figure 24: Bill and Markus both modified the file properties of the resource query.xml
Figure 25: Conflict Compare editor shows query.xml has conflicting MIME Type and Line delimiter settings
Figure 26: Bill manually merged the file properties MIME Type and Line Delimiter
Note. File properties conflicts are similar to content conflicts. If the same property has been changed by two different developers then it has to be explicitly merged as shown above. Auto resolve can deal with the other cases – e.g. if MIME Type had been modified by Bill only and Line Delimiter by Markus, auto resolve will correctly merge Bill and Markus’s changes.
Figure 26: Bill tries to auto-resolve a conflict on a file with an unknown extension .php
Figure 27: Associating the .php extension to the Text content merger
Bill does not know much about the php file format – Markus is the expert – but he sure knows php is a text format, not binary. So Bill makes the right decision by selecting ‘Text’ Merger. Bill also wants Eclipse to remember this association so he checks the item to save this preference. The auto-resolve is now able to proceed and successfully merges Markus’s code changes with Bill’s copyrights update. Bill can now deliver his copyright changes with the resulting merge (Figure 28).
Figure 28: File with unknown extension type .php has been successfully merged with the Text content merger
Note. Markus uses PHP plugins for Eclipse to get nice PHP script editor and debugging support (e.g. see PHPEclipse). These plugins register the php extension as a text format automatically. Bill could also have manually registered php as a text format through the Eclipse > Windows > Preferences… > General > Content types > Text > Add > php. (Figure 29).
Figure 29: Setting .php extension as Text content type through the Eclipse Preferences
Figure 30: Bill and Markus both created a file readme.txt at the same location
Bill accepts the incoming change set. The Pending Changes view shows the conflict on the resource readme.txt (Figure 31).
Figure 31: Bill must decide what to do with the two readme.txt resources
Bill double clicks on the conflicted item to open up the conflict compare editor. The left pane shows Bill’s readme.txt, right pane is Markus’s readme.txt file (Figure 32).
Figure 32: Conflict compare editor showing two different readme.txt files with a location conflict
Figure 33: Bill appended Markus’s content to his readme.txt file
Bill can now deliver his change to the JUnit stream (Figure 34).
Figure 34: Bill delivers his own readme.txt file to replace Markus’s readme.txt (Merged)
Bill’s readme.txt file replaces Markus’s readme.txt file. Bill’s readme.txt includes the content of Markus’s readme.txt
Note. This is just one of many ways Bill can resolve this type of conflict known as ‘evil twin’.
- Bill could have discarded his change set and added his content to Markus’s readme.txt file.
- Bill could have renamed his readme.txt either before or after accepting Markus’s change.
- Bill could have renamed Markus’s readme.txt with the Move… action on the conflicted item (see Figure 35)
Figure 35: Bill can rename Markus’s readme.txt to resolve the conflict with his own readme.txt
Bill can decide which solution is best to resolve this type of case named ‘evil twin’. In some cases the files need to be renamed. In others it is best to only keep one file but merge their content. Jazz Source Control provides the tooling necessary to support both cases.
Figure 36: Bill and Markus both created a symbolic link called ‘link’
Markus accepts Bill’s changes. The Pending Changes view shows the conflict on the symbolic link (Figure 37).
Figure 37: Markus must resolve the conflict on the symbolic link
Markus double clicks on the conflicted item to open the conflict compare editor. The pane shows Markus’ change on the left and Bill’s change on the right.
Figure 38: Markus must resolve the conflict on the symbolic link
Markus decides to use his symbolic link instead of the link Bill delivered. He selects his change and resolves the conflict as merged. Figure 39 shows the change set with the merges.
Figure 39: Markus has resolved the symbolic link conflict
Note. Symbolic link conflicts are similar to file property conflicts. There is no file content to resolve. The only property to resolve is the path the link points to. The conflict cannot be auto-resolved.
Jazz understands conflicts are inherent to a healthy parallel and team-oriented development process. Conflicts are exposed and resolved through the Pending Changes view. Most common conflicts are content based, when the same resource was modified both in the developer’s local workspace and in the stream. Auto Resolve and the Conflict Editor assist the developer through the process of merging the proposed changes into their local content. Structural conflicts are less common and usually observed during heavy refactoring development phases, for example when two developers move and modify the same resources. Conflict tool-tips detail the options for their resolution. Jazz retains the history of a resource that is moved or renamed even during conflict resolution. This flexibility is an asset for teams following Agile or Extreme Programming methodologies with frequent refactoring, and for teams experiencing rapid project growth.