SemanticMerge and C#

Overview

As an experienced developer you have undoubtedly encountered issues with merging your code, especially when attempting a refactor, because traditional tools are text-based and they simply do not understand the code like SemanticMerge does. Built on the premise of code semantics, SemanticMerge not only sees your code at the text level but it can actually determine what is going on within it by focusing on the structure rather than the actual location of your code. By understanding the code at this level, SemanticMerge is able to deliver a visual representation of your method at the pre-merge and post-merge states that makes this tool very powerful.

Programming Language Support

The SemanticMerge tool has been designed from the ground up to support many programming languages now and in the future. In the current release it supports C#, C, Java and VB.NET and is planned to be released for C++, Objective-C and JavaScript in the future. Additional programming language support will be determined by your feedback so please take a moment to cast your vote by clicking on this link here.

How to Change Methods using XMerge

The best way to get a real understanding of the power of the SemanticMerge tool is with an example. Let's start with a basic refactoring of a Windows Phone C# application that has the following ViewModel class structure:

  1. public class MainViewModel : INotifyPropertyChanged  
  2. {  
  3.     private string message;  
  4.   
  5.     public MainViewModel()  
  6.     {  
  7.         Message = "hello world";  
  8.     }  
  9.   
  10.     public string Message  
  11.     {  
  12.         get { return this.message; }  
  13.         set  
  14.         {  
  15.             this.message = value;  
  16.             RaisePC("Message");  
  17.         }  
  18.     }  
  19.   
  20.     public event PropertyChangedEventHandler PropertyChanged;  
  21.   
  22.     private void RaisePC(string propertyName)  
  23.     {  
  24.         if (PropertyChanged != null)  
  25.             PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));  
  26.     }  
  27. }  
Assuming that both developer A and developer B are working on this code at the same time and they each make the following changes (where Developer A refactors the code to change the method RaisePC by moving it to the start of the class):
  1. private void RaisePC(string propertyName)  
  2. {  
  3.     var tmpHandler = PropertyChanged;  
  4.     if (tmpHandler != null)  
  5.         tmpHandler(thisnew PropertyChangedEventArgs(propertyName));  
  6. }  
Developer B refactors the code to change the method with the following result:
  1. private void RaisePropertyChanged([CallerMemberName]string propertyName = "")  
  2. {  
  3.     var eventHandler = PropertyChanged;  
  4.     if (null != eventHandler)  
  5.     {  
  6.         eventHandler(thisnew PropertyChangedEventArgs(propertyName));  
  7.     }  
  8. }  
As you can see in this example, both developers made changes to the method but developer A actually moved the method to a different location inside the class that will definitely create a conflict that must be resolved. By using the SemanticMerge tool this conflict can be easily managed and resolved, let's have a look at how you would accomplish this:

SemanticMerge

As you can see, SemanticMerge detected the conflict and proposed a solution to resolve it by providing information about the merge needed in the RaisePC method. By clicking the green merge button under the changes, SemanticMerge will launch the XMerge tool that is included with SemanticMerge. Here is the new XMerge view that will open up:

XMerge

Referring to the new XMerge view, there are four code windows that are displayed as follows:
  • The top left window displays the source code (theirs)
  • The top right window displays the destination code (yours)
  • The top middle window displays the base code
  • The bottom window displays the result of the merge

For each conflicting change you want to resolve, you would select the source, destination or base code as your starting point. This flexibility enables you to select the solution that is best suited to resolving all of the file conflicts.

Referring to the example code, the first conflict concerns the method name and parameters that were changed in the destination file. Based on this information, we would select the Green destination code changes and then deselect the source and base code changes by clicking on the Blue and Yellow headers:

method name and parameters

As you can see, the method body has been changed in both the source and destination files. Based on this information you can now proceed to the second review by selecting the source code change since it is a better version of the change. Just as we did with the first conflict, you would deselect the base and destination code changes by clicking on the Yellow and Green headers and keep only the source header selected:

source header

At this point you are nearly done with making changes to this method. The remaining conflict concerns an extra curly bracket that was left behind at the end of the code. This conflict is easily resolved by simply deselecting the destination header so that it is not included in the final result:

final result

With all of the conflicts resolved, you can now click on save & exit to return to the main SemanticMerge screen.

Using Explain Move

In the example, the second conflict that was detected by SemanticMerge involved moving and renaming the method in the source and destination code, this is called a divergent move. If you are interested in digging deeper to understand exactly what was done, you can click on the explain move button that will show you a summary of the change(s):

Explain Move

Using Explain Move

After reviewing the change summary you can now return to the SemanticMerge main screen and choose to either keep the source changes or the destination changes:

destination changes

In this example, we will choose to keep the destination changes because this will preserve the move that developer B made and it will also keep the previous merge of the method. SemanticMerge not only identifies when the text is moved but actually understands the meaning of the source code and identifies that the same code is in place. This is the real power of SemanticMerge.

Using Visual Merge

If you want to have a closer look and determine exactly what the tool did to your code then you can use the Visual Merge button on the left side of the screen to see a graphical representation of the operations SemanticMerge did to resolve all the conflicts:

Semantic Merge

Referring to the Visual Merge screen, you will see that the top part of the screen displays a graphic with the source, destination and base files with arrows connecting the changes made to the code in each one of the files. By selecting any two of the changes, we can click on the diff button in the top left hand corner of the screen to display the resulting change collision code in the lower part of the screen.

Visual Merge makes it very easy to understand what changes were made as well as where and how they related to the base file.

How to Skip Format Changes Using the –merge-decl Parameter

One of the benefits of SemanticMerges understanding of the underlying code structure of your files is that SemanticMerge can detect and skip format changes that don't introduce new code and only reformats the existing ones.

Using the following method as an example, let's take a closer at how the –merge-dcl parameter will enable SemanticMerge to resolve a conflict automatically:

  1. public async Task<Movie> GetMovieInformation(string movieName, int year)  
  2. {  
  3.     Movie resultMovie = new Movie();  
  4.     HttpClient client = new HttpClient();  
  5.     string url = string.Format("http://www.omdbapi.com/?t={0}&y={1}&plot=full&r=json", movieName, year);  
  6.   
  7.     var result = await client.GetAsync(url);  
  8.     if (result != null)  
  9.     {  
  10.         string json = await result.Content.ReadAsStringAsync();  
  11.         resultMovie = JsonConvert.DeserializeObject<Movie>(json);  
  12.     }  
  13.   
  14.     return resultMovie;  
  15. }  
This is a fairly simple code snippet that queries a REST service for movie information. Developer A introduces some changes (in Red) when working with it:
  1. public async Task<Movie> GetMovieInformation(string movieName,  
  2.                                              int year)  
  3. {  
  4.     Movie resultMovie = new Movie();  
  5.     HttpClient client = new HttpClient();  
  6.     string url = string.Format("http://www.omdbapi.com/?t={0}&y={1}&plot=full&r=json", movieName, year);  
  7.   
  8.     var result = await client.GetAsync(url);  
  9.     if (result != null)  
  10.     {  
  11.         string json = await result.Content.ReadAsStringAsync();  
  12.         if (!string.IsNullOrEmpty(json))  
  13.         {  
  14.             resultMovie = JsonConvert.DeserializeObject<Movie>(json);  
  15.         }  
  16.     }  
  17.   
  18.     return resultMovie;  
  19. }  
Developer B also introduces some changes (in Green) to the GetMovieInformation method:
  1. public async Task<Movie> GetMovieInformation(string movieName, int year, string plot)  
  2. {  
  3.     Movie resultMovie = new Movie();  
  4.     HttpClient client = new HttpClient();  
  5.     string url = string.Format("http://www.omdbapi.com/?t={0}&y={1}&plot={2}&r=json", movieName, year, plot);  
  6.   
  7.     var result = await client.GetAsync(url);  
  8.     if (result != null)  
  9.     {  
  10.         string json = await result.Content.ReadAsStringAsync();  
  11.         resultMovie = JsonConvert.DeserializeObject<Movie>(json);  
  12.     }  
  13.   
  14.     return resultMovie;  
  15. }  
In this example, both developers made non-conflicting changes. One added a parameter to the string.Format and the other added a validation to the returned JSON. Simple changes but the result is that there is a conflict change in the method declaration. So when developer A changed the format of the declaration, developer B added a new parameter. For an experienced developer this is an easy conflict to resolve but for a Traditional merge tool, resolving this conflict is a real challenge. Unlike SemanticMerge, which can use –merge-dcl to automatically resolve the conflict, Traditional merge tools cannot decide what to do automatically:

non conflicting changes

So when the declaration changes made by developer A were merely aesthetic changes and developer B introduced a new parameter, SemanticMerge automatically discarded developer A changes and re-executed the text merge.

Conclusion

In this article we provided a sneak peek at the power behind SemanticMerge that is designed to help you focus on development and avoid the time consuming tasks associated with merging your code. If you are looking for a way to improve upon the traditional merging and diff tools then SemanticMerge may be just the tool to make life easier for you and your team.

If you like what you see and you want to try SemanticMerge please have a look at the website where you can download a free trial and give it a test drive with your applications.

 


Similar Articles
DevsDNA
Development, training and mentoring company focused on Mobile and Augmented reality