In previous articles, we’ve tried to make it clear that source control is non-negotiable to becoming a software engineer even if you work alone as an independent developer or contractor. Not only does source control protect you and your team from mistakes, but it also forces you into a habit of standard project organization practices. In learning how to use source control, you will inevitably begin using 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 Code.
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.
If you’d rather use the full Visual Studio experience, we also have a huge guide to using Visual Studio 2019 with Git.
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). Mac and Linux have native command line support.
To get started, go to git-scm.com/downloads to find the best download for your setup.
- If you’re on Windows, use the Windows download link.
- If you’re on Mac, use the Mac download link. This link seems to redirect to Sourceforge.
- If you’re on Linux, use your distribution’s package management commands as seen on the Linux download link.
For example, with Ubuntu, you’d use:
apt install git. You might need to augment this with
sudo if your setup requires root to install software.
The rest of this guide assumes that you’re on Windows, but the process is identical regardless of your operating system since Visual Studio Code is cross platform.
Install Visual Studio Code
Visual Studio Code is another one of those Microsoft products that suffers from weird-naming-convention-syndrome. Since the original “Visual Studio” still exists and is completely different from Visual Studio Code, it’s easy to get the two confused in a conversation. Also, the word “Code” is so vague as to be nearly useless when used in a Google search. Maybe the combination of “Visual Studio” and “Code” makes it unique enough. But we digress.
Despite Microsoft’s strange naming choice, Visual Studio Code has become the most popular source editor according to a recent Stack Overflow survey.
Start by downloading Visual Studio Code for whatever operating system you’re using. It’s cross platform, so if you’re running Mac, Windows, or Linux, you’re in luck. Also, you have the choice to download the Insider edition which gives you early access to beta features.
The installation process is painless compared to Visual Studio 2019. That’s not to say that the latter is painful, but it’s definitely much more involved due to its bundling of .NET Framework features and many other add-ons.
Create New Git Repository
Once you’ve downloaded and installed Visual Studio Code, you’re good to go. Before you really get going, 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.
If you’re used to Visual Studio 2019’s way of organizing projects, you’ll have to make a few adjustments. Visual Studio Code can edit individual files, all files in a folder (“open folder”), or all files in a workspace (“open workspace”). Workspaces are typically reserved for when you need to edit multiple related projects in a single context.
The keep things simple, open the folder of where you want your source repository to live.
In the source tab (looks like a source control branch icon on the left), click the
+ near the top right of the source control view. If you opened a folder, the workspace folder picker will default to that folder. Otherwise, you’ll have to manually select a folder to run
git init in. This will initialize a new repository for you in the selected folder.
You can confirm that the repository was created successfully by seeing the source control viewer update to indicate the repository is “Git”, has no changes, and exposed some icons related to various source control functionality (for example: commit, pull, push, branch).
Commit a Change to Git
Source control repositories aren’t very useful without any changes committed to them. And if you don’t know how to do that, then you’re in pretty bad shape!
Since you’ve created a new repository, you’ll have nothing in it and thus no changes will show up in the changes section.
Go back to the file viewer on the left. Click the icon to add a new file to whatever folder you currently have open. Note that there are many ways to create files, and this is just one of the most convenient ways to show within the same UI.
In this example, we’ve created a
.gitignore file since it’s a good idea to keep a thorough ignore file from the beginning. This prevents your repository from devolving into a cluttered mess.
Assuming your repository was initialized properly, you should see the new file automatically detected in the source control changes window.
Depending on your project, language, and frameworks, you’ll have to decide on the best
.gitignore file for your purposes. We suggest checking out this popular repository on GitHub for some standard ideas. In this example, we’re using the Visual Studio file.
Once you’ve populated your
.gitignore file, you’ll need to stage your changes by clicking the
+ next to the file in the source control changes viewer. This is equivalent to using
git add on the file.
While you are able to use the commit functionality in the UI without first staging, you will receive warnings. It’s best practice to just follow the normal git flow by staging and then committing rather than bypassing the steps. This helps protect you from mistakes by introducing that extra commit gate.
Once your changes are staged, you can enter an optional commit message above the staged changes viewer. Finally, click the check mark icon above the commit message to commit your changes.
Remember that we are currently only working from the master branch at the moment. You can confirm that by looking at the commit message placeholder text. It should indicate something like “Ctrl+Enter to commit on ‘master'”.
Create and Commit to a Local Branch
As mentioned, up until this point, we’ve only committed changes against the master branch. We’ve written previously about the importance of keeping user stories, features, and bug fixes properly isolated using a well understood branching strategy. Trust us, your team will appreciate your amazing branch and merging due diligence when it comes time to fix problems down the road.
Most of the options to interact with Git are hidden under the triple dot menu in the source control viewer. Click that to find the “Checkout to…” option. If you’re familiar with Git terminology around creating branches (
git checkout -b <branch_name>), then this nomenclature will make sense. For new people, it probably isn’t obvious that creating a branch is hidden under this menu.
You can alternatively get here by typing
ctrl+shift+P to open the command palette at the top followed by typing whatever action you want to find like
You have a few options here.
- Create new branch allows you to create a branch based on whatever branch you currently have checked out
- Create new branch from allows you to create a branch based on a specific branch name
- A listing of known local branches that you can checkout
In this case, we are going to create a new branch from master (which we currently have checked out). Go ahead and enter the branch name that you want.
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.
The process of making changes, staging those changes, and ultimately committing them is identical to the previous instructions in this guide. The only difference is that your changes are now being made on a different branch.
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.
Merging branches in Visual Studio Code is a little less obvious than other actions. There doesn’t seem to be an option to merge in the context menus, so you’ll have to use the command palette (
ctrl+shift+p) to find the command by typing “merge” and ultimately selecting “Git: Merge Branch…”.
You can then select which branch you want to merge from. It’s important to note here that you are merging from a selected branch into whatever branch you currently have checked out. So if you’re expecting to merge a feature or bugfix branch into master, you first need to checkout master and then select a branch to merge from.
If the merge was successful, you should see the new files or updates to existing files in the file view. Note that depending on the branches you’re merging together, you might run into merge conflicts (see below).
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 merge conflicts happen (and they will happen), Visual Studio Code makes it pretty easy to resolve them. The conflicting file will show up in the “merge changes” section of the source control viewer. The letter “C” to the right of the file will indicate that it is in conflict.
Open the file to view the contents, and you should see the conflicting lines that Git has added.
<<<<<<< HEADindicates the starting point of your changes.
>>>>>>> [source_branch_name]indicates the end point of your coworker’s changes.
=======is a separator for the conflicting changes.
If the conflict is simple to resolve, you can manually edit the file to remove the markers and only retain the line that you want. Otherwise you have a few options per conflict.
- Accept Current Change to remove all markers and the incoming changes from the source branch
- Accept Incoming Change to remove all markers and the currently checked out branch’s changes in favor of the source branch
- Accept Both Changes to remove all markers and retain all changes
- Compare Changes to open a diff viewer so that you can see changes side-by-side
It’s usually a good idea to compare changes before making a choice so that you get in the habit of double checking (see: bad habits to avoid). Yes, simple changes don’t usually require it, but when things get more complex, you’ll be thanking yourself that you go into a good habit.
After making your decision on all conflicts in all files, you can finish up the merge by saving the files, staging, and committing as normal.
Clone a Remote Repository
The previous steps of this guide have all been against a local repository. But at some point you’re going to want to organize your projects and show your skills off to everyone on GitHub and Bitbucket, right? Of course you do, because you want to build an awesome public portfolio for interviews.
The easiest way to get started with a new repository is to first create it in something like GitHub and then clone it to your local machine.
ctrl+shift+P followed by “clone” to find the “Git: Clone” option.
You’ll then need to enter the URL of the remote repository. Not pictured here is another step where you have to indicate which folder you want to clone the repository into. Note that it must be an empty folder or Git will complain that you are attempting to clone over other files. This is to protect you from wiping out another repository by accident.
Add an Existing Remote Repository
Cloning is great if you don’t have the repository locally yet, but what about all those repositories you’ve been maintaining locally already that you now want to push to GitHub?
No problem, you can attach remote repositories to your existing local repositories.
First, make sure that you have a folder opened in Visual Studio Code that has an initialized Git repo. Then type
ctrl+shift+P followed by “add remote” to find the “Git: Add Remote” option.
Next, you’ll need to name the remote. Since you don’t have any remotes yet, and since the first remote is typically named “origin”, name it that. Note that you can name this whatever you want, but “origin” is the typical standard naming convention.
Finally, you’ll need to enter the remote repository URL to associate with the soon to be created “origin” remote. This is the repository where your pushes and pulls will work against.
Push & Pull Changes
Visual Studio Code has several options to manage code exchange between local and remotes.
- Pull to run
git mergefrom the default remote
- Pull (Rebase) to run
git rebasefrom the default remote
- Pull from… to select a specific remote from which to pull changes
- Push to run
git pushto the default remote
- Push to… to run
git pushto a specifically selected remote
- Sync to pull and then push in a single step
To start the push operation, select “Push to…” under the triple dot menu in the source control viewer.
Now you have the option to select a known remote or add a new remote in real time. Select the origin remote that you created previously.
Any pending commits should then be pushed to the remote. You may receive errors or confirmations to authenticate with the remote if you haven’t already done that or your Git configuration is not setup properly.
To start the pull operation, select “Pull from…” under the triple dot menu in the source control viewer.
Interestingly, the resulting menu to select a remote from which to pull doesn’t have the ability to create a remote in real time. We aren’t sure why.
Regardless, select the remote that you set up earlier in this guide to pull changes from.
We actually found Visual Studio Code’s built-in Git features to be easier to use than Visual Studio 2019’s. That’s probably because Microsoft had the luxury of starting from scratch using a new, minimal user interface rather than a lot of the baggage carried in by years of previous work.
It also seems like Microsoft is making Visual Studio Code to be less opinionated than Visual Studio 2019. This seems to be true with their approach to the source control user interface as well.
For example, with Visual Studio 2019, there are menus and buttons that attempt to drive people into certain workflow tools like GitHub and Azure DevOps. This is presumably because Microsoft also owns those products and wants developers to get hooked into those ecosystems. On the other hand, Visual Studio Code has no bells and whistles and simply allows you to perform a Git command without any advertisements.
In our opinion, if you’re familiar with the Git CLI, there isn’t much to be gained with using the Visual Studio Code Git UI except for the merge conflict resolution features. Having a nice diff viewer to compare complex changes across files and branches is a game changer.
In summary, the Visual Studio Code’s Git integration is definitely useful. If you’re looking for an even more advanced integration to view remotes, view contributors, view branches, see file history, see line history, search commits, and a lot more, check out the GitLens extension.