Establishing a git workflow with VSTS and Visual Studio - git merge
In this post I will look at git merge in Visual Studio, in particular merging down from master into a branch and at what happens to each commit. This post will not make a case for merge over alternatives such as rebase, though the findings will contribute to the workflow that will be decided upon.
All posts in this series on establishing a git workflow for VSTS and Visual Studio are:
- Establishing a git workflow with VSTS and Visual Studio
- Configuring a VSTS repo from the Github sample repo
- Git merge (this post)
- Git rebase
Recap on setup
If you have just landed on this post then a quick word on set up. This post builds on the git_workflow_sample_setup available on Github. For this post the sample has been cloned and pushed to VSTS. The previous posts describe the sample and steps to clone, reconfigure and push to VSTS from Visual Studio.
What is the expected end state?
Given the current commit history for master and merge_branch as:
Relying on Visual Studio’s integrated git tooling, the end result expectation for source (shown below) is:
- the Hello World program cleanly merged
- the project xml to be cleanly merged with the new HelloWorld and Translator class files
And how did we get there?
An incorrect view I've held for a long time is around commits being time based, leading to an expectation in terms of commit history on the merge_branch as:
This came about through my use of git log, which by default presents commit history in reverse chronological order. I will later show how this is not the case.
Let us merge
The intended action is a merge down from master into the merge_branch working branch.
First, verify the merge_branch is the checked out or active branch by navigating to the Home view, select Branches and then double-click the branch or alternatively right-click on the branch to select Checkout as shown below.
To merge down from master, right-click on the branch and select Merge From. Then in the inline popup, select master as the Merge from branch and click Merge. The inline popup changes to a merge conflicts warning, stating there is one conflict.
Before addressing the merge conflict it is worth taking a moment to look at the current changes (shown below) by navigating to the Home view and selecting Changes.
Here we can see the auto-merged changes, relating to the Hello World class created on master, that are now staged changes and pending commit, and the pending changes due to merge conflict that must be resolved manually. Double-clicking on the staged HellowWorld.csproj opens a diff showing the merge, as shown below.
The dreaded merge conflict
Visual Studio does make working through conflicts reasonably straight-forward. By clicking the Conflicts link next to the warning triangle the a list of conflicts is presented and each can be worked through by clicking to select. When selected an inline popup with some merge options is presented as shown below. Before looking at merge it is worth looking at the two other merge options available.
Edited on merge_branch
The Diff for this option shows only the source changes made on the branch. If we Take Target for this option we lose all changes made on master. Bearing in mind for the purposes of this investigation master is synced with the remote repo, this is not the best option as published commits are being reverted. Code is being lost.
Edited on master
The Diff for this option shows only the source changes made on master. If we Take Source for this option we lose all changes made on the branch. Not as bad as the previous option however, code can still be lost.
Clicking Merge opens a code merge window providing the ability to selectively merge code.
The result of selecting source changes from master first and then merge_branch is:
The result of selecting source changes from merge_branch first and then master is:
Neither are correct. Fortunately, to get the desired result the code in the result pane can be edited to arrive at the required merge result as shown
To complete the merge, click Accept Merge at the top of the merge code window, then Commit Merge in the Team Explorer Resolve Conflicts view, and finally entering a commit message before clicking Commit Staged as shown.
The end state
The expected end state for the code conflict merge has been arrived at however, the tooling only gets us so far and manual intervention inevitable. This problem is only around conflicting sections of code, for example the tooling successfully auto merges the xml project file.
And how did we get here?
Looking at the commit history using the Visual Studio's branch history and branch commit history illustration below, it is clear the commit history is not in chronological order. The merge down action can be summarised as:
- Leaving all commits on the target branch unchanged, write each commit unchanged from the source to the target branch
- Perform a diff of the last commit from the source and the original last commit from the target branches
If there is a change and if the change can be auto-merged then stage the change however, if there is a conflict then raise a merge conflict action. The change is not yet saved to the working directory.
- Address each conflict manually and accept the merge. I suspect at this point the accepted change is staged
- Once all conflicts have been resolved then manually commit the staged changes
Note the new merge commit has two parents; last commit from the source and the original last commit from the target branches.
The git merge flow presented by Visual Studio's Team Explorer views is easy to follow and intuitive however, insofar as resolving conflicts, it is possible to break code. Breaking code in one's development branch is less of a problem than breaking code committed to the remote repo, and so one needs to be careful with the Edited on master option and similarly carful when using the code merge tooling. The goal of establishing a git workflow sets out to limit the impact of these factors, which will be compared with rebasing and commit squashing in subsequent posts.
The end state for commit history on the merge_branch is acceptable for the branch however, it is not the ideal end state when pushing to master, and this will be further explored in a subsequent post on pull requests.