Git for .NET Developers

Stephen Weinrich
Slalom Build
Published in
14 min readOct 4, 2016

by Stephen Weinrich

I’ve been a .NET developer since it was first released by Microsoft in 2002. From the days of Visual SourceSafe and then TFS (Team Foundation Server), source control has been pretty painful. Since the introduction of integrated Git support into the Visual Studio IDE, I’ve heard .NET developers complain about having to adopt a new source control tool. While Git may be “new” to the .NET community, it has been around for over a decade. It is also the preferred distributed source control choice for the open source community, of which Microsoft is now a part of.

Assuming that you have Visual Studio 2013 or newer installed, get GitBash for Windows installed and lets get started!

Creating a GitHub Account and Repository

There are many Git repository providers that we use at Slalom: VSO (Visual Studio Online), GitHub, BitBucket, GitLab etc. For the purposes of this blog post I will be using GitHub.

Go ahead and create a GitHub account here. You can do this for free. Once you have an account you can create a new Repository here:

Options

  • Public vs Private Repository: I selected Public because it’s free. If you want to control access and protect your source code, you can select Private and pay for it.
  • Initialize with a README.md file: I recommend adopting this since it will provide a way for your development team members to get instructions on setting up their local environments once they have downloaded your code from the repository. This file has its own wiki-style language known as Markdown.
  • Add .gitignore VisualStudio: This file is used to ignore certain files from being comitted, such as those in the /bin or /obj folder, these should not be stored in source control since they are constantly being recompiled. BitBucket have kindly provided an out-of-the-box template for Visual Studio projects, but feel free to edit and change this file as needed. You can read more about the .gitignore file here.

Congratulations - you now own your very own repository hosted in GitHub. Mine looks like the image below:

I have highlighted the URL that represents the repository, you will use this next to do something in Git called cloning.

Cloning

Cloning is creating an exact copy of the remote repository on your local machine. This is different to how TFS works because it is a copy of the entire repository history, not merely a copy of the files from the server. It contains the history of the code and branches currently on the server as well as the files thus making it distributed and enabling you to work offline.
Open up Visual Studio. And select Connect under the GitHub tab in the Team Explorer and enter your GitHub credentials to sign in.

Now it is time to clone the repository to your local machine, click on the clone link. Select the repository you would like to clone and the local directory where you would like to store the repository files. Once it has copied successfully it will appear in your Local Git Repositories section of the Team Explorer — Connect window.

Using Windows Explorer, browse to your local repository directory to see the README.md and .gitignore files previously specified. You will also notice a hidden “.git” directory. I’d advise not modifying the files in this folder since they are used by Git to control the repository.

Creating Branches

Branching in Git is different to branching in TFS. In TFS you branch and then check-in / check-out code from a server branch. Using Git you will do everything on a local branch that you create based on a server branch, other developers will do the same. There is a concept of root branches, typically “develop” and “master”, often used to represent code still under development and production respectively. I recommend reading more about GitFlow as a potential branching strategy here.
There is a “local” branch and a “remote” or server branch (called “origin” by default). A branch called “master” under the “remote/origin” folder is on the GitHub server, the other “master” is on your local machine. Next, we will create a “develop” branch on your local machine and then publish it to the server.

Right click the Repository and select “New Local Branch From…” Enter “develop” as the new destination branch name, select “origin/master” as the source branch. I’d recommend not checking “Track remote branch”. This will track changes made to “develop” and will automatically apply them to “master”. This is not ideal since we want to control changes, through code reviews, into the “master” branch. For some reason Visual Studio will tick this box by default which is annoying, coincidentally if you create a branch from local instead of origin the check box will not be there, this is our preference. The problem with creating new branches based on local branches is that they could be stale so you need to remember to “pull” or “sync” before you create a new branch.

So now we have a “develop” branch on our local machine only. We need to publish the branch to the server. Right click “develop” and select “Publish”. You will then see “develop” appear under the “remotes/origin” folder, indicating that it now exists on the server. Whenever you create a new branch locally you will need to publish it before you can push changes to the server.

You can now create branches based on existing root branches. If you navigate to your GitHub repository you will see the new “develop” branch there:

Each development team has their preference for branch naming and branching strategy depending on their project requirements. I recommend GitFlow for most projects, however there are cases where other strategies are better suited. Let’s create another branch that we can do some work in. Let’s call it “feature/12345-create-console-project” and lets branch from “origin/develop” this time, after publishing this branch your branches window should look like this:

Notice that Visual Studio has created a folder called “feature” and put your branch within that folder. This is only conceptual from a UI point of view but very useful for organizing branches by simply using a clever naming strategy. Other possible names we’ve used on our projects could be:

  • bug/12345-some-bug-name
  • story/12345-some-story-name
  • task/12345-some-task-name

Or something like this will keep branches grouped under a feature number:

  • 12345/story/67890-some-story-name
  • 12345/task/67890-some-task-name
  • 12345/bug/67890-some-bug-name

The number used is often a TFS/VSO Work Item Number, or JIRA Issue Number or the ID of whatever system you are using to manage developer tasks.
Do not create branches with your user names in them e.g.

  • stephens-branch
  • user/stephenw
  • stephenweinrich-branch

By naming branches with user names implies that your intention is to reuse this branch for multiple changes required by the system. This is the TFS way of thinking and should not be applied to a Git repository. Each change to the system should warrant the creation of its own branch since it is linked to a feature/story/task/bug. Once the code has been merged, the assumption is that the branch can be deleted and should not be reused.

Something else I’d like to point out is that if you take a look at your directory in Windows Explorer you will see the same files we saw before when we only had a “master” branch. We have subsequently created two new branches, “develop” and “feature/12345-create-console-project”. As a .Net Developer using TFS I am used to seeing multiple copies per branch in my directory. This is not how Git works. The directory will contain the files for the branch that you are currently working on, highlighted in bold in your branch window. In my case it is “feature/12345-create-console-project”. You can switch branches by double clicking on them. Ok enough about branches let’s write some code.

Committing Code

In TFS you check-in code changes or keep them on a shelve-set. In Git, you commit work and it will then compare branches based on yours and other developers commits.
Create a new Console Project called HelloSlalom.proj. Create it into the directory where your repository is stored on our local machine. Put some code in the Program.cs, compile and click on the “Team Explorer — Changes” window.

You can see a list of changed files that will be committed to your local branch.

Notice that other files like /bin /obj are not there. This is because of the .gitignore file we added earlier. To view what Git will ignore select Team Explorer — Settings and then Repository Settings. Click Edit to see a list of all the directories/files that will be ignored when committing code.

To commit changes, from the Team Explorer window, select Changes and enter a commit message. This should be a description of the commit you are currently busy with. You can do as many commits as you like on your branch since they serve as a checkpoint of code on your local machine. Click on the Commit button. There are other options if you click on the down arrow in the drop list but let’s simply Commit the code to our local repository for now. VS will notify you of the success and give you a generated commit hash number to represent your commit. Next we will push/sync our changes to the server. If you are using TFS or VSO (Visual Studio Online) you can put a # and the work item number to associate this commit with a specific work item. This is useful when generating release notes.

Push vs Pull vs Fetch (aka Sync)

Let’s get our Git terminology correct first:

  • Push: Copies all pending commits from my local machine to the server for the branch that I am working on. In our case we only did 1 commit above for the “feature/12345-create-console-project” branch.
  • Pull: Copies and commits from the server branch down to my local branch. In our case we have no commits on our server branch yet.
  • Fetch: Fetches information about any new branches that have been created on the server.
    In our case we have all the branches since we are the only ones working in the repository at the moment, if another developer had created and published a branch, fetch would get the details about that branch and make you aware of it if maybe you needed to use it.
  • Sync: Sync is something Microsoft came up with, the sync keyword does not exist in Git. Basically it performs multiple Git commands namely, fetch, pull and then push, for your convenience. If we wanted to get our changes onto the server branch we could select either push or sync, both would achieve the same objective for us.

Do this now and then go to GitHub and take a look at your feature branch to see the new files on the server:

Pull Requests

Now that we have pushed changes to your feature branch we can merge them into the “develop” root branch so that they can be deployed out to the development environment or shared with the rest of the team. Merging is done via Pull Requests (PR).
Pull Requests are a valuable tool for reviewing changes being introduced into your branches. Teammates can use Pull Requests to review code, make comments, and learn more about the codebase.
To create a PR, browse to your GitHub repository and click on the “New Pull Request” button.

Select “develop” as your base branch, this is the branch we want to merge into. Select “feature/12345-create-console-project” as your compare branch, this is the branch we will be merging from. The PR title and comments can be changed as needed. You can also find extra information towards the bottom of the screenshot about the Pull Request such as the number of commits and file changes. Click on “Create Pull Request”

Before we merge this into “develop”, let’s take a look at the changes. Click on the “Files changed” tab. You can now make comments against lines of code for others to see. You can then make changes, commit the code, push to the server branch, and the PR will update automatically.

When you are happy with the changes, click “Merge pull request” and then “Confirm Merge”. Go take a look at the “develop” branch you will see the changes there

To see these changes in Visual Studio switch to the “develop” branch on the Team Explorer- Branches window by double clicking it, then on the Team Explorer — Changes windows select sync. This will update your local “develop” branch with the changes we have merged into the server (origin) “develop” branch.

Merge Conflicts

Sometimes when you are merging into a branch another developer may have made changes to the same file you were working on. This will create a merge conflict. To avoid this in our example, you may need to merge from “develop” into the “feature/12345-create-console-project” branch. Visual Studio has a great tool for resolving conflicts and I would recommend using it.

Go to the Team Explorer — Branches window and pull down the “feature/12345-create-console-project” branch. I’ve already made some changes to the “develop” branch that will cause a merge conflict.

Right click the branch and select “Merge from…”. I recommend selecting “origin/develop” since this will be the most up to date version of the “develop” branch on the server. Click on the “Merge” button.

We have a conflict! Click on the “Conflicts: 1” link.

The conflict is in the Program.cs file, you have a few options:

  • Merge: VS will attempt to merge but you will need to resolve each conflict manually.
  • Take Source: In our case it would overwrite Program.cs with the version from the “develop” branch.
  • Keep Target: In our case it would keep Program.cs with the version from our current branch, “feature/12345-create-console-project”, the version on “develop” would be overwritten.

Click on the “Merge” button.

The tool shows the Source on the left, “develop” branch, and the Target on the right, “feature/12345-create-console-project” branch. We can see that two developers have both changes the same WriteLine statement. Visual Studio is unable to resolve this, hence the conflict. You need to decide which line of code to keep. The result of your choice is displayed in the bottom window.

I have selected the Target code as it looks the most correct. You can see the merged result in the bottom window. Click on “Accept Merge”, commit it, and push/sync it up to the server. Now when you create the PR you will not have any conflicts.

Using The Console — GitBash

While the VS IDE exposes many common Git commands to you through its interface, these won’t always be sufficient when dealing with edge cases and other source control problems. By using the GitBash command line, you can invoke the same Git commands used by VS with a greater degree of flexibility and control.

GitBash uses a linux bash command line which may take some time getting used to. Below is a list of common GitBash commands. This is only a brief summary of the console commands available to you. Many more commands, and their optional parameters, can be found here, or for an easier read, here.

git clone [repository-url] — Clone a repository from the server to your local machine.

git clone https://github.com/stephenweinrich/HelloSlalom.git

git branch — List the branches on my local machine git branch.

git branch

git checkout -b [new-branch-name] — Create a new branch on your local machine based on the current branch.

git checkout -b feature/12345-create-console-project

git push -u origin [branch-name] — Publish a branch from local to the server git.

push -u origin feature/12345-create-console-project

git checkout — Copy a branch from origin (the server) to my local machine git checkout develop.

git checkout

git pull — Copy the latest changes from origin (the server) to my local machine for the current branch git pull.

git pull

git commit -m “commit message” — Commit changes to local branch git commit.

git commit -m “added new project”

git stash — Temporarily store all changes. You can then switch branches, do some work, switch back, and call git stash apply (below), also useful for abandoning changes and switching to another branch git stash.

git stash

git stash apply — Apply temporarily stored changes to the current branch git stash apply.

git stash apply

git branch -d [branch-name] — Delete a branch from your local machine only.

git branch -d feature/12345-create-console-project

git push origin --delete [branch-name] — Delete a branch from origin (the server).

git push origin --delete feature/12345-create-console-project

git config --global credential.helper wincred — Signal git that you want to store your credentials so that you don’t need to enter them again.

git config --global credential.helper wincred

Conclusion

At Slalom we use Git for small hack-a-thon style projects as well as our larger enterprise projects. Git helps to create a development environment in which we can branch code in a structured way enabling us to release code in a controlled manner. We use Pull Requests to review code and transfer knowledge of the code base across the development team. GitFlow is used on many of our projects to keep us delivering code in an agile way.

Useful Resources

About the Author

Stephen Weinrich is a Solution Principal with Slalom’s Cross-Market delivery center in Chicago. He’s been delivering software and building teams for over 16 years, and is a Microsoft Certified Professional, Agile evangelist, and novice blog writer. Connect with him on LinkedIn.

--

--

Stephen Weinrich
Slalom Build

Stephen Weinrich is a Software Engineering Director for Slalom Build Chicago currently focused on all things Cloud, DevOps and DotNet.