We’ve written extensively in previous articles about the non-negotiable importance of learning source control to become a software engineer and the most popular tool to accomplish it: Git. Git’s command line has, for lack of a better way of describing it, evolved into somewhat of a conflicting journey. If you’re looking for a better experience to manage your Git repositories, this guide introduces you to using Git with Visual Studio 2019.
Many developers prefer having the ability to manage repositories inline with the development environment to reduce having to context switch between applications. The downside to this is that you lose the flexibility and customization of the command line. We suggest you start with our extensive guide to Git and follow that up with this guide so that you know how things are working under the hood.
And hey, if things start going haywire, you’ll know how to use the backup method!
Install Git & Learn Git
Git works natively on Windows, Mac, and Linux. When using Windows, you’ll most likely be using the tools included in the Git for Windows initiative (Git BASH, Git GUI, and Git Shell Integration). Even though Git is completely cross platform, Visual Studio 2019 only works on Windows.
Don’t know how to use Git? Take a look at our Getting Started with Git guide so that you’re set up for success and ready to contribute to projects with confidence.
Install Visual Studio 2019
Microsoft sometimes has weird ways of naming their products (Azure DevOps, we’re looking at you). There was a period of time when everything was named with the Visual Studio prefix. That said, don’t confuse Visual Studio 2019 with Visual Studio Code. This guide only uses Visual Studio 2019.
Let’s get started.
Download Visual Studio 2019 Community Edition (free). Read our pricing guide for more detailed comparisons between editions. We suggest the Community Edition because you won’t need any of the features offered by the pricier (and they can be pricey) tiers.
Microsoft recently overhauled the installation process. It’s much, much better than it used to be, so let’s all take a moment of silence to appreciate that.
Anyway, you’re not going to need the thousands of options that are available to you during the initial installation process. This guide only uses the “.NET Core cross-platform development” group and its required dependencies. Feel free to opt in to anything else you think looks interesting but be aware that you will have to download everything. Some of these options can increase the download size by 5+ GB.
Let the installer do its thing. Sit back, relax, and wait for the magic. While it’s downloading, you can check out our article on 5 Essential Visual Studio Extensions. Used properly, extensions have the potential to boost your productivity and development efficiency.
Create New Git Repository
You’ve got Visual Studio, and you’re ready to roll. If you want to know how the Visual Studio Git UI is interacting with Git under the hood, read our introduction to Git to learn the basics of the command line.
Visual Studio tries to be helpful on launch, but sometimes jumps the gun. In this case, we don’t want to create any projects to start. Instead, we want to create a new repository, but that’s hidden behind the tiny link in the bottom right: “Continue without code.” Click it.
To create a new repository:
- Click File –> New –> Repository…
- The Team Explorer opens on the right
- Pick the folder that you want to create the repository in
This is equivalent to running
git init from the command line.
Create Project In New Git Repository
The repository is ready to go, but it’s empty. Confirm that the repository now shows in the “Local Git Repositories” section of the Team Explorer.
Initiate the Project Creation Wizard by clicking File –> New –> Project. Yes, there are keyboard shortcuts to do this, but they’re not very convenient. You can remap them, but really how often are you going to be creating new projects to justify a handy shortcut?
There are definitely a lot of project templates to sift through. If you selected more installation options in the first steps of this guide, you’ll be absolutely swimming in them.
Search for “console” to filter down to just console applications. Select “C# Console App (.NET Core)” to create a new console application based on whatever version of .NET Core SDK was installed with Visual Studio 2019. Note that there are templates for other languages (VB and F#) that aren’t relevant to this tutorial.
Enter your project name, select the folder location in which you initialized the Git repository from previous steps. If you get this wrong, you won’t be able to follow along with the rest of the guide.
Click create, and you’re good to go. Your solution, project file, and template classes will be created and placed in your repository folders.
Commit New Project to Git
Visual Studio has a “Team Explorer” docked sidebar (you can drag it to move it around) which enables you to manage your local Git repository and attached remotes.
Change the Team Explorer section drop down to “Changes” so that you can view changes on your active branch (which is the master branch at the moment).
Even though your project has been added to your local repository folder, you haven’t yet committed those changes. Files that Git recognizes as new are not tracked by default and require you to explicitly add the files to be tracked. If you don’t see any changes listed here, go back to the project creation step and make sure that you created the project in the same folder as the Git repository.
The Changes window in the Team Explorer tab breaks down changes in two ways: “Changes” and “Staged Changes”.
- Changes is a list of pending file changes that you haven’t acted on. That is, you haven’t run
git addon those files to stage them for a commit.
- Staged Changes is a list of files that have been added to Git’s staged tracking list to be committed. In this step, Visual Studio’s repository creation wizard has automatically staged
.gitignorefiles based on built in templates.
.gitignore file was sourced from the GitHub gitignore repository.
+ to stage all changes. In this case, staging everything is fine, but in the real world, you might want to pick and choose exactly which changes to stage. It’s common for developers to make local changes specifically for debugging or testing purposes without the intention of committing those to the repository.
Do yourself and your team a favor by always double checking your changes prior to staging and definitely prior to commitment.
The Team Explorer UI will update with all changes in the Staged Changes section. Confirm that these are as you expect prior to commitment.
If anything looks wrong at this step, you can Unstage by right clicking the file and selecting “Unstage”.
Good commits have good commit messages. Good commit messages aren’t too short and nor too long. That’s vague, but the point is that you should be descriptive enough for those who will come after you but without requiring the reader to struggle to the end.
You have three options here:
- Commit Staged to commit staged changes to the local repository
- Commit Staged and Push to commit staged changes to the local repository and push to a remote repository (like GitHub or Bitbucket)
- Commit Staged and Sync to commit staged changes to the local repository, pull changes from a remote repository, and then push your changes to that remote repository
We only care about the first one for this guide because we have no remote repositories setup.
At this point the project is committed to Git and enshrined in the history forever. Let’s work on incremental changes now.
Commit a Change to Git
Any time you add or change a file to the tracked repository, the Visual Studio Team Explorer will automatically detect and display the changes in the Changes tab.
Add a line to the
Program.cs file and watch as the change displays automatically.
Before you commit stage and commit changes, always check to make sure the changes are what you expected. Long development sessions can leave behind unwanted comments and code that you intended to remove prior to commitment.
Right click the file or folder that changed and click “Compare with Unmodified…” to see what changes are pending to be staged or committed.
The previously chosen option will launch the Visual Studio Diff Viewer, which displays changes between the
HEAD (left, unmodified state of your branch) and the current state (right, pending changes that haven’t yet been committed).
Red lines on the left indicate the previous state. Green lines on the right indicate the current state.
Always confirm that these changes are exactly as you expect.
If everything is as expected, just like before, click the
+ to stage the changes. Enter the commit message (a good one). Click “Commit Staged” to commit to the local repository.
We encourage all developers to commit early and commit often. Reduce your risk and keep your coworkers sane by maintaining small and frequent changes.
Create and Commit to a Local Branch
As we previously wrote about in 5 Essential Things Every Programmer Should Know, branching is critical to your success, your team’s success, and ultimately your project’s success.
Without the ability to create independent branches of code, team members would be conflicting with each other every time a developer made a commit. Since we also advocate for committing early and committing often, such conflicts would be an immediate deal breaker.
Change the Team Explorer section drop down to “Branches” so that you can view all branches in the local repository.
The only branch in a new repository is the default branch known as
master. Until now, all changes have been committed against that branch.
Right click the
master branch, click “New Local Branch From…” to begin creating a branch based on the current state of
The new branch UI will display in the Team Explorer. Name the new branch, confirm that you are branching from
master, and select to checkout the branch so that you don’t have to do that manually after creation. It’s just an option for convenience.
Branch naming is a hot topic and can invoke a lot of opinions. One common naming convention is found in the Gitflow Workflow.
- feature/ prefix indicates that work committed to the branch represents changes for a new feature. Example:
- bugfix/ prefix indicates that work committed to the branch represents changes for a bug fix. Example:
- hotfix/ prefix indicates the branch will have an escalated release path because of critical fixes included on it. Example:
- release/ prefix indicates a standard release branch which includes tested and ready-to-go features an fixes. Example:
Ultimately the naming convention is up to you and your team to decide. Whatever works best for your flow is fine. Just make sure the names make sense.
Confirm the branch was created and that it is now the active branch (indicated by bold lettering) in the Branches tab of Team Explorer.
If you didn’t click to checkout the branch upon creation, you’ll have to double-click the branch now to switch to it.
After confirming that the new branch is the active branch, it’s time to add some changes and commit to that branch.
Just like in the previous sections, add a new line to
Program.cs. You should see changes automatically detected and displayed in the Changes tab.
Again, just like before, use your new skills to confirm your changes, stage those changes, enter a good commit message, and then commit the changes to the new branch.
There is no difference in the process of committing to a separate branch. In fact,
master is considered a branch as well, so the process is exactly the same.
But how do we get our changes back to the
master branch so that other coworkers can use our changes? Surely we don’t want our changes isolated in a separate branch forever.
Merge a Branch Into Master
Branches are nearly useless if kept in isolation forever. Team members would be working on features that never get merged together. The application would then have its code split across a ton of different branches!
Branching in Git works by merging from a source branch into an active branch. For example, if you want to merge changes from a feature branch into the
master branch, you need to checkout the
master branch to make it active and then select the feature branch as the source. You are essentially “pulling” changes from a branch into the active branch.
In the Branches tab of Team Explorer, you can double-click the
master branch or right click then “Checkout” to switch to that branch.
Confirm that you successfully checked out the
master branch. Right click it, then select “Merge From…” to begin the process of merging into the active
The Team Explorer UI will update to allow you to select the source branch (merge from) and confirm the destination branch (master).
Select the feature branch created in the previous sections as the branch to merge from. Check to commit changes after merging. If you don’t check this, you will need to take an extra step to manually commit the merged changes. This is just a convenience option.
Finally, click Merge to complete the operation.
If the merge operation was clean (no conflicts, see below), then you’re good to go. You should still be on the
master branch and see the changes from your feature branch available to you.
Good practice dictates a process of branching early, branching often, committing early, and committing often. All of that combined reduces your risk, keeps your changes isolated from massive conflicts, allows you to test in isolation, and keeps your product delivery cycles unblocked.
Resolve Merge Conflicts
This is the part about source control that everyone hates. But it doesn’t have to be insurmountable. Good branching and commit cadence can reduce the size of conflicts.
When multiple people are working on the same lines of the same files in different branches, at some point there will be a collision. That’s because there are sometimes new features and fixes that may overlap with code that was changed by someone else on a different branch.
So what if you add another
Console.WriteLine statement to
Program.cs on one branch, and your coworker also adds a
Console.WriteLine statement on the same line in
Program.cs on a different branch? There will be a conflict.
When this happens (it’s a matter of when, not if), the Branches tab in Team Explorer will popup a warning indicating that the merge cannot be automatically completed because manual intervention is required to resolve a conflict.
Either Abort the merge (not recommended unless you have no idea how to resolve the issue) or click the Conflicts link to display which files are in conflict.
In the Conflicts list, select a file in conflict to display resolution options.
- Merge button will open the diff editor to manually resolve conflicting lines
- Diff link will show differences between the files for visibility
- Take Source will ignore changes on the active branch and overwrite with changes on the incoming source branch
- Keep Target will ignore changes on the source branch and overwrite with changes on the active branch
Click Merge to begin the manual merge resolution editor.
In the diff editor, you will see three windows.
- Source (left) viewer will show the changes made on the incoming source branch
- Target (right) viewer will show the changes made on the target active branch
- Result (bottom) viewer shows what the merge will look like depending on the resolution decisions
Select the highlighted-in-red conflicting line that you want to resolve. At the top toolbar, click the “Take Left” or “Take Right” icons to indicate which change you want to take.
This step might require you to discuss changes with a coworker if you are merging someone else’s changes into your branch. Depending on the decided upon resolution, you may even end up taking both changes!
Check the bottom window to see what the resulting file will look like after you decided which changes to take.
If everything looks OK, click “Accept Merge” in the upper left to confirm that the file merge conflict has been resolved. If you skip this step, the merge operation on the file will still be marked as pending and in conflict.
If you selected to commit after merge in the previous step, then the merge conflict has been resolved and automatically committed. Otherwise you’ll need to go back and complete the merge operation as appropriate.
View File Commit History
You will inevitably need to check the history of a file to determine who changed what or where something went wrong. It’s extremely common to need to refer to previous commits for the purposes of debugging or reverting.
Right clicking any file in the Solution Explorer shows an option to View History.
In the file history viewer, you’ll see a list of commits, authors, dates, and commit messages. Use this window to give yourself some context about how the file got to its current state.
Right clicking a commit will reveal options:
- Open to view what the file looked like on that commit
- View Commit Details to view what the full context of the commit was beyond just this file
- Compare with Previous… displays a diff between the commit and the immediately preceding commit
- Blame (Annotate) will show a breakdown of the file per line to show who last altered that line
Click to Compare with Previous to open the diff viewer.
At this point, you’ll see the diff viewer popup to reveal differences between two commits. The viewer looks the same as it did previously in the “Compare to Unmodified” steps earlier in this article.
Set Up Remote Repositories
While it’s great that you’re now maintaining your code in Git repositories, the problem is that they’re only local. By taking advantage of remote repository services like GitHub, Bitbucket, and Azure DevOps, you can maintain backups and allow others to contribute.
Connect to GitHub
We are strong advocates of using GitHub to build up a public portfolio of your work to support your job and team search adventures.
To connect your repository to GitHub, change the Team Explorer window to “Sync”. For some reason, Visual Studio will display this tab as “Push”. Click “Publish to GitHub” to initiate the login process using your GitHub credentials. Don’t have any? Don’t worry, GitHub is free!
As stated just now, login or sign up to GitHub.
If the login process is successful, the UI will update to include your account profile and username. Plus, it automatically fills in the repository name based on whatever you named it locally. You don’t have to have the same name, but it helps for clarity to be consistent.
Enter a name and a description then click “Publish”. We don’t suggest making this a private repository as discussed in our guide to building a portfolio on GitHub.
And finally, if the remote repository creation was successful, you should see a success message. You can double confirm by checking the Branches tab in Team Explorer to see that a new
master branch in the
remotes/origin remote repository was created and available now in your local repository.
Connect to Other Remotes
The process is largely the same for non-GitHub repositories. Instead of clicking “Publish to GitHub” in the first step, click “Publish to Git repo” at the bottom.
When prompted, enter the remote URL (usually ends with a
.git extension) to push your changes to. You may be prompted for credentials if pushing to a secured system.
Push & Pull Changes
What good is connecting your repositories if you don’t know how to push your changes and pull your team’s changes? Fortunately, Visual Studio makes that really easy.
Change to the “Sync” tab in Team Explorer to see the incoming and outgoing commits sections.
Incoming commits are those that you are expected to pull in to your local repository to reconcile against other contributor’s changes. Click the “Pull” link to initiate pulling those changes.
Outgoing commits are those that you have committed locally but don’t yet exist on the equivalent remote branch. If you have commits that meet these criteria, you’ll see a list of them in this section. Confirm and click “Push” to push those changes to the remote repository.
Quick Note: if you see an error complaining about not having a matching remote branch when trying to push, that usually means you don’t have any commits in your local repository to push. This is because Git doesn’t create the
master branch until you’ve committed at least one time so there are no branches against which Visual Studio can match and push.
Clone Remote Repository
Sometimes you don’t have a local repository to push and instead want to pull an existing repository down locally to begin contributing to it. That’s when you have to clone.
Unfortunately, Visual Studio doesn’t make finding this option very intuitive. Click the little green plug at the top of the Team Explorer window to switch to the Connections tab. We have no idea why this tab is hidden from the normal tab selector drop down.
Under the Local Git Repositories section, click the Clone link to start the process. Enter the URL of the remote repository and where you’d like the clone to end up on your local disk. By default, the clone process will also clone submodules. Don’t uncheck that unless you explicitly know you don’t need it.
Finally, click the Clone button to download and clone the repository to a matching local repository. This process will create the linked remote automatically for you, and you won’t need to go through the previous process of connecting.