This post will show how to merge using Git. This is the third post in a series.
- Git for Team Foundation Developers
- Git for Team Foundation Developers – Branches
- Git for Team Foundation Developers – Merging (this post)
The series focuses on introducing Git for developers who are familiar with Team Foundation Version Control.
One of the absolute coolest parts of Git is the flexibility it has with merging. The types of scenarios it enables are exactly the scenarios that you face daily. You make lots of check-ins, some of them you need to make (“Going on vacation… sorry if this breaks the build”), others you are happy to make (“Version 3 release… time for vacation”). At the same time, other members of your team are also making check-ins. Git makes it easy to merge your frequent check-ins (“going to lunch” type check-ins) with the release branch for your team, and it makes it easy for you to merge your changes with the rest of the team.
My previous post talked about branches in Git. This is the thing I now most about Git, and also one of the things that completely tripped me up originally because I didn’t understand all the names that Git used. When I create a team project in Visual Studio online, I clone the repository to a local repository. I get the default branch, master. I then create another branch, v2, and publish it to Visual Studio Online. Here is what my branches look like right now:
What you don’t see in this picture is that there are actually 4 branches: 2 local, 2 remote. VSOnline is the “origin” of the code, and each user has their own local repository.
The reason I bring this up in a discussion of merging is because you will see the term “ORIGIN” occasionally. You can choose to create a branch from an existing branch in your local repository or the remote repository.
Our team decided to use the v2 branch for builds. I first switch to the v2 branch (which switches in my local repository), and then I sync changes by clicking the Sync button, pulling changes from Visual Studio Online and pushing any outgoing commits.
Now that I know the local v2 and origin v2 repositories are synchronized, I am going to create a branch, kirke_v2, to do my work in. The rest of my team may have branches specific to them, such as Simon_v2, Donovan_v2, and Paul_v2. They are free to make whatever changes they want to their local branch, even publish that branch to the server, knowing that those changes are not part of the branch that our team is using for builds, the v2 branch.
Think about how powerful this is. Rather than do all of your work against the central repository and possibly break the build, you can fetch the latest source from a particular branch on the server and work locally. When you are ready to push your changes to the server, you do that explicitly. I like that model.
Now that I’ve created the kirke_v2 branch, notice that it is local only and is not on the server. We can tell that because it is an “unpublished branch”.
Finally, I decide to publish that branch to Visual Studio Online.
There are now 6 total branches (3 local and 3 remote), and any changes that I make continue to work only against the local repository until I decide to push the changes to the remote repository.
Let’s cause a few changes. Here is my project as I start:
I am going to add a few classes. Here’s my base class.
I then implement a ConsoleOutputWriter and a DebugOutputWriter.
Each time I write some code, I commit. You probably wouldn’t do this in your daily work, but I am doing it here to highlight how commits are tracked.
Each time I added code, I made sure to commit. You can see the history for the kirke_v2 branch in Visual Studio. Go to Changes / Actions / View History.
The history for the branch is now shown. Notice those two markers on the right. Those show the pointers to the last commit in master that the branch kirke_v2 knows about, and the last commit in kirke_v2.
Let me explain that last part a little. The pointer to master points to ffffb5c7, “Modified to use an interface”. We’ll see in a second that the “v2” branch actually has new commits in it made by a new intern in our group. When we branched from “v2” to create “kirke_v2”, Git copied all of the commits to the kirke_v2 branch. Think of it as a complete copy of all the commits from the source branch, because that’s what it is.
Let’s go over to Git Bash. We will run a program called “gitk”.
gitk opens, and we can see all of our checkins represented as a Directed Acyclic Graph (DAG). I use this fancy word not to show off my incredibly vocabulary and sound as geeky as possible, but to point out that this term is used frequently in the Git documentation. A directed acyclic graph is where you go from node to node and can never revisit a node. For instance:
- The DAG shows all of the commits, with the most recent at top
- Each commit is by a user at a specific time
- Each commit has history that can be used to show the different between the current commit and the previous commit.
GitK is a fairly useful tool.
Notice the changes that it is showing in section 3, highlighting new code in the Main method (using a green font).
While I’ve been busy working on my fancy new class structure for writing output, a new intern in our group took the initiative to go make changes in Program.cs and commit them to the v2 branch, the same branch I’m about to merge my awesome new code into.
Houston, we have a conflict. Of course, I won’t know this yet because that change commit is not in my kirke_v2 branch, but I’ll discover it when I attempt to merge. That edit is smack-dab in the middle of the edits we made, and Git will let us know there’s a conflict that has to be resolved. To see this in action, let’s try to merge our changes from the kirke_v2 branch to the v2 branch.
I want to first make sure that I have the latest source for v2. I first switch to the v2 branch.
Next, I use the Sync button to pull commits from the remote repository.
Now the v2 branch is synchronized with my local repository.
Go to Team Explorer / Branches to view the branches, and then choose Merge. The dialog will change to ask you to pick a source and destination.
Click the Merge button, and we start yelling at the intern.
Visual Studio is doing something incredibly nice for us here. If you’re not familiar with Unix tools like vim, then editing this stuff using Git Bash is going to be very difficult. Thankfully, the Visual Studio team provided a UI for this. Click the “Resolve the conflicts” link. The dialog changes to:
OK, that’s not very helpful. Click the program.cs file in the Conflicts section.
That’s a little better. Click that big “Merge” button.
Ah, there we go! A visual diff tool that allows us to pick the changes from the conflicting commits. Next to each conflict (highlighted in red in the tool) is a checkbox so that you can visually select which change stays. The bottom pane shows the results.
Take that, intern.
Here’s the confusing part… what next? The file has an asterisk next to its name, and its not a preview file it’s a file actually opened for editing. Do I save it, or do I click that big “Merge” button on the right of the screen?
Tucked away, somewhat hidden, is a button that says “Accept Merge”.
Click that, and now the Team Explorer pane changes to include a button that says “Commit Merge”.
Click the Commit Merge button. Remember how we started this out trying to commit our changes? Visual Studio says, “you’ve resolved all the problems, but we still haven’t done a commit.”
Finally, we want to push our changes to Visual Studio Online so that the rest of the team can use them. We can then go to Changes and see the unsynced commits section that shows all of the commits in our local repository. We go to the outgoing commits section and click Push to push them into the remote repository.
The results show that we were successful pushing to “origin/v2”.
Now hopefully you see why I started this post with the explanation of “origin”.
Let’s go to Visual Studio Online again. A nice shortcut to get there is to use the link in Visual Studio.
We are taken to the portal, and we switch to the Code section. Click the History tab, and we can see all of our commits, including the merge, are in the commit history.
Visualizing the Branches
Like I said in my previous posts, it helps to understand what Git is doing under the covers because the Visual Studio tools are nice enough to hide some of the gory details from us. Let’s go back to the Git command line tools. We used Git Bash to open a visual tool called gitk. We should already have gitk open from our previous demo, go to the gitk File menu and choose “Start git gui”.
Once Git Gui starts, we can choose “Visualize All Branch History”.
That command will then launch gitk again, and now we can see the Directed Acyclic Graph (DAG) for our commits. The picture below is straight out of the GitK tool, I didn’t draw this myself, so this can be a hugely valuable tool for understanding what’s going on in your commit history.
Here is what it looked like after I pushed to the remote repository.
A little explanation of what you’re looking at is in order. You’re not looking at a time line, but rather a visualization of the commits. In a previous post I commented that changes have parents, and that is what this graph is showing.
The initial vertex in the graph is our initial check-in at the bottom where I was working in the master branch. I made a few changes, and then at the 5th commit I pushed the changes to “remotes/origin/master”. What’s interesting here is that the same line also shows a push to “remotes/origin/kirke_v2”. This graph doesn’t show when the branches were created, it just shows when something happened on them. When I created the kirke_v2 branch and published it at the beginning of this post, the changes it had were the same changes as in v2 and in master. At some point, the intern added a commit to the v2 branch (the nodes do not reflect time relative to each branch, only precedence on the current branch). We finally see our kirke_v2 branch is merged with the main v2 branch and pushed to “remotes/origin/v2”.
Note: I’m impressed that I made it this far without really explaining the DAG and the various types in it. Most texts you read on Git dive into tag, branch, commit, tree, and blob by now. My goal is to make you familiar and productive, not to mire you in theory.