This tutorial is dedicated to a topic that is very important for development teams, Git in Azure DevOps. It will cover various aspects of Git like
- how to use git,
- when to use,
- where to use and
- what are some advantages of using git.
Version Control is a software which helps development team members track the changes they make in code while developing a project or a product.
In the absence of a version control system, team members may have to keep multiple copies of the code. In which case, there is always a fear of changing the wrong copy, overwriting changes done by others and losing valuable time in the process.
There are two types of version control systems, Centralized version control and Distributed version control system (DVCS). Examples of centralized version control are Team Foundation Version Control, SVN, CVS etc. Git is a distributed version control system. There are other less popular (comparatively) distributed version control systems like Mercurial.
Software development involves working in a team and sharing code amongst team members. Git helps in tracking any change made by any team member, which can be for small or large projects. It is an open source version control system developed by Linus Torvalds in 2005 while developing the Linux Kernel. As is convention with distributed version control systems, Git keeps clone of the complete repository on every team member’s machine. This repository has full history, including every version of every file in every branch. When a team member commits the code to repository, it does not require any network access. It happens in the local repository so it is much faster. Over the years, Git has become an unofficial standard of version control.
There are a few popular implementations of Git namely Atlassian Bitbucket, GitHub and Azure DevOps Git.
When compared to a DVCS, Centralized Version Control system has a single repository at one place which provides access to all team members. This is the reason why it is more suitable for large code bases. But Microsoft overcame this problem when they implemented Git Virtual File system (GVFS) and successfully transferred the whole Windows code base of 300GB to Git. You can find more information about how Microsoft achieved it by using this link.
Advantages of working with Distributed Version Control System (DVCS)
- Allows users to work without connection to network – which makes commits much faster
- Light weight branches – branches can be created in local repository, and only pushed if required.
- Can get full local history – which reduces time for finding bugs
There is a small hitch here, when team members want to switch over from centralized to DVCS, there is a huge learning curve. It is often said that it is easy to start working with DVCS if the team is not using any kind of version control system, rather than changing from centralized to distributed. But once the concepts are known, working becomes easier.
Let us find out all the basic features by doing a walkthrough of working with Git.
Create a new organization for Azure DevOps using the link. You will get the following screen where you can select the option for Start Free
We can even use GitHub to store our code, but in this article, I will use Azure DevOps Git. Both GitHub and Azure DevOps Git are DVCS offering by Microsoft. Once the organization is created, create a Team Project by using Git as version control and Scrum / Agile process.
Git Basic Features with Azure DevOps
Code is stored in repositories (repo) in Git. Every Team Project in Azure DevOps can have one or multiple repos. Go to the Project Settings blade from the bottom left side for Team Project and select Repositories.
Once the Team Project is created, a remote repo automatically gets created and can be seen in following diagram (same name as project name).
This is the place where all files related to your project will be stored. This is a remote repository where all team members will subsequently put their respective code. When the team is already working on a project, the code will be available in this place. Some teams follow the strategy of creating less Team Projects and multiple repos where separation of code can be done. Furthe,r security majors can be added as to who will have access to which repos.
Whenever any team member wants to add code to the repo, the first thing the member needs to do is to clone it on his/her machine. I am going to show this using Visual Studio 2019. You can download Visual Studio 2019 Community edition, which is free, from this link.
We can use the option to clone in any tool from the browser by selecting Repo blade for Team Project. You can observe the various tools with which you can clone the repo. The moment you clone a repo, it automatically creates a remote connection called origin pointing back to cloned repo on Azure DevOps.
Start Visual Studio 2019 – select the option of “Clone or check out code” – select Azure DevOps from browse a repository – select the Team Project and click on Connect – Clone
With Clone, the whole repository with its complete history gets cloned on the local machine. In this case, it will clone an empty repository as we do not have any code yet. If we browse to the folder where the repo is cloned, we will find a .git folder created with some other subfolders in it (you will have to select the check box for hidden item to view these). This folder stores all the information about your project from version control perspective. This consists of commits, remote repo address, history of commits. The Head file points to current branch we are working on. Once the cloned folder is created, we can start writing actual code.
This operation is used for saving your changes to the local repo.
There are total 3 commit operations – Commit All, Commit All and Push, along with Commit All and Sync.
The first one will save changes to local repo. With Commit All and Push, the changes will be saved locally as well as will be pushed to remote server-side repo. We will study Push operation later in the article. With Commit All and Sync, the changes from local repo will be pushed to remote repo, as well as the changes made by other team members on server will be brought to local repository. In both these options, we may have to take care of conflicts if any. Visual Studio 2019 will provide us conflict resolution. This feature is called as Diff & Merge Tool.
I used New Solution option from Team Explorer to add an ASP.NET Web application as the code. Once the application is added, we need to specify the git ignore file so that unnecessary files like .obj and other files from NuGet packages will not be committed. We can add this file by selecting Team Explorer – Home – Settings – Repository Settings – Add the file
Before adding this file, if we go to Team Explorer – Changes, we see more than 300 files to be committed as can be seen below.
After adding the .gitignore file, these changes reduce dramatically (76 to be precise). You can open the ignore file to find what all extensions will be ignored. Observe there is Staged Changes added just for a single ignore file. If we provide the comment for the commit, only 1 file will be committed.
Staged Changes: These changes are tracked by git in version control. Once we mark these changes as staged, they will become part of commit, when the commit operation is executed. The files which we do not want to be added to the repo can be kept un-staged.
Right click on un-staged changes to put them in stage, provide a comment for the commit, and click on Commit Staged which is equivalent to Commit All. Each commit automatically gets Commit ID. Every commit needs to have a comment which should be as informative as possible.
We see 77 changes as 1 file was for gitgnore.
The history of the whole repo can be obtained with the branch. Currently we have just one branch which gets automatically created called master. Every time a new commit is made, the pointer for master branch will move forward. The local history can be seen in diagram below:
Observe there is single default master branch.
Push, Pull and Fetch
Push will be used to push the local repo to remote server whereas Pull will do the reverse. Now that we have committed to the local repo, we need to Push the code to remote branch. When we select Team Explorer – Sync, we can see the Outgoing Commits.
Push the code to remote repo.
Fetch, Merge, Sync
If any other team member has already committed some changes in the code on a remote repo, we can first view it and compare. If you feel this code can be merged into our local repo, you can do so.
To simulate this, let us do some changes on remote repo by editing a file in the browser. We can see the new change in Repos – Commits. Now use Team Explorer – Sync – Fetch command. This will get the remote commits and show them under fetch. These changes are shown to you but not yet reflected locally. We can select the changes to display all the files with remote and local view in Diff tool (which is Visual Studio in our case).
In Team Explorer – Branches you will observe one more master under remotes/origin. As the name suggests, this is the master from remote repository. We can decide when we want to merge the code from remote to local. Once we merge it, the code from local and remote repo will be one and the same.
Pull is a combination of Fetch with Merge, the remote repo code will be pulled and also merged with the local repo.
Branches, Tags and Policies
Tags and Branches
Both branches and tags are pointers to commits.
A tag is similar to a branch which does not change. Once a tag is created, the commit to which tag is kept frozen. With tags we can add annotations to the code. Tags are used to differentiate specific commit in the history of repository. They can be used to keep stable release intact. A tag can be created from an existing branch. This is a little similar to label to centralized version control.
A branch is very lightweight in Git as it is just a pointer to one of the commits. As discussed earlier, the default branch is named master. Every time a new commit is done, the pointer moves ahead automatically. Branches in Git can be created in local repo so the remote repo can remain uncluttered.
A developer works on a local branch as It is not recommended to work on master and directly update it in remote. It is always a good strategy to get your code reviewed before merging it to remote repo. So, the developer creates a branch, works on it, pushes it to remote repository, creates Pull Request (PR) so the code can be reviewed. Once the PR is reviewed and accepted, it is merged to master.
Once PR is done and the branch on remote repo is merged with master, the branch can be deleted to keep remote repo with as few branches as possible. This approach of deleting unnecessary branches from remote, keeps the remote repo healthy. The developer needs to decide whether to delete the branch from local repo or not.
As already said, a branch can be created on a local as well as remote repo. Obviously, the branch from local will remain local till we push it to remote.
Let us create a local branch, add some code into it. We can directly start working on the branch by using “Checkout branch” option. Now the code change will be committed to the new branch.
After changing the code in branch, we can push the new branch to remote repo and create a Pull Request for it. If we want to send all local commits to the remote repo in one go, we can squash the commits and then push the branch.
You can provide a combined commit message for all changes before a push. A branch can even be created while fixing a bug.
When a local branch is pushed to server-side repo, it is a best practice to get the changes reviewed by other developers before the code gets merged into the master branch. This is done by creating a Pull Request (PR). Create a PR and provide reviewers to review the code. The title for PR will be automatically taken as the commit comment, which can be changed. Provide an appropriate description for the PR and create it. It is a good idea to link PR with work items which will enable traceability.
Practices of good Pull Request
- Ask the right reviewers assigned to review
- Provide feedback which is self-explanatory
- Provide feedback which can be acted upon
When a PR is created and assigned to a reviewer, the reviewer can take a look at the code and provide comments. Information about all the files changed, as well as updates done along with all the commits for the branch, can be seen. The reviewer can provide feedback if the code needs to be changed at any place along with comments.
You can view changes either inline or side by side using the diff tool. Finally, the PR can be completed with the option of deleting the branch after merging. If the reviewer provides comments for suggestions, then the comments should be precise (especially if the code change suggested cannot be incorporated, we need to provide reason for it).
Some branches are critical in nature which need to be restricted. Adding policies will ensure that developers will not be able to directly push the code to such protected branches. Master is one such example which should be kept protected.
Branching policies will ensure the following:
- Master branch can be kept separate
- Server-side build can only be triggered once the code to be built is reviewed
- Provide minimum number of reviewers or a selective group of reviewers
- Ensure that the code needs to be linked to work items for better traceability
To add a branch policy, select Repos – Branches – Select a branch – click on ellipses button to get context menu for Branch Policies
Require a minimum number of reviewers
Getting code reviewed is a good development practice. A team can be decided upon for reviewing the code and ensure that the PR cannot be completed.
Check for linked work items and comment resolution
When we want to ensure that the PR must be linked to work item, we can select the specified policy. The other policy is for comment resolution
Build Validation Policy
Have server-side build to enable this policy. This will ensure that the code in PR can be successfully built. This is similar to gated check-in available with TFVC (Team Foundation Version Control).
Automatically include code reviewers
The reviewers will get automatically added to Pull Requests along with specifying activity feed messages.
Once Required is selected, PR cannot be completed till every reviewer approves the code changes.
We can choose the branch policy which will be suitable for our development and implementation.
Forking functionality creates a server copy of repository. This feature provides us with doing any experiments without changing original repo. Normally this feature is implemented when developers are contributing for public project (number of contributors are large in number). In such cases, the team member who is contributing to the project may not have permission to directly change the repo so he/she forks it. Once the code is available in the fork, a Pull Request can be created. The decision whether to merge this code to original can then be taken by administrator of the repo. It is not required to have branching policies in this case.
In this article I have given an overview of features of Git with Azure DevOps. We explored what is Git, how and when to use it and some advantages of using Git.
This article was technically reviewed by Subodh Sohoni.
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!