Version Control and Release Management with Git
This chapter covers the following topics:
Version Control and Git: This section briefly discusses version control for managing code or file changes and how Git is a widely used version control system (VCS). It also discusses Git basics and advanced features for managing code changes from multiple concurrent developers.
Git Workflow: This section discusses Git basic workflow agreement, how to manage access to the code, and who contributes and who is trusted to manage the workflow. Basics of branching and forking are also discussed.
Git Branching Strategy: This section discusses a strategy for managing the code development and stabilization teams and processes. This is a strategy that all developers must understand and agree upon.
This chapter maps to the first part of the Developing Applications Using Cisco Core Platforms and APIs v1.0 (350-901) Exam Blueprint Section 1.0, “Infrastructure and Automation,” specifically subsections 1.10 and 1.11.
This chapter describes version control, version control systems (VCSs) in general, and then goes on to describe Git as a version control system. The concept of version control is simple and used in every aspect of our lives: documents, applications, operating systems, webpages, standards, frameworks—you get the idea. The code you build for automating and orchestrating your network is no different. We’re confident that you’re not building the code by yourself, and we’re confident you’re not building it in one sitting. You’re collaborating with others and most probably in an agile development process where various releases and sprints are in order. This chapter teaches you how to keep track of the development process.
The “Do I Know This Already?” quiz allows you to assess whether you should read this entire chapter thoroughly or jump to the “Exam Preparation Tasks” section. If you are in doubt about your answers to these questions or your own assessment of your knowledge of the topics, read the entire chapter. Table 4-1 lists the major headings in this chapter and their corresponding “Do I Know This Already?” quiz questions. You can find the answers in Appendix A, “Answers to the ‘Do I Know This Already?’ Quizzes.”
Table 4-1 “Do I Know This Already?” Section-to-Question Mapping
Foundation Topics Section | Questions |
Version Control and Git | 1 |
Git Workflow | 2, 3 |
Git Branching Strategy | 4, 5 |
1. What is version control used to track?
Code changes
Configurations
Releases
All of these answers are correct.
2. What is the difference between Git and GitHub?
There is no difference.
GitHub is used for hosting Git projects.
GitHub has a hub, and Git does not.
GitLab, GitHub, and Git are different names for the same thing.
3. The main benefit of a VCS is your ability to do what?
Manage code for multiple development projects.
Streamline the development cycle or process.
Track and save all changes.
All of these answers are correct.
4. Which scenario is more likely to cause a merge conflict?
Two developers open the same file.
A file gets deleted by one developer while it’s being modified by another developer.
Two merges are performed back-to-back.
All these scenarios are easily handled by Git.
5. Which statements are correct about branching? (Choose two.)
Branching is important only if you plan to merge changes from two independent projects.
A branching strategy needs to define types of branches and all rules governing usage by the developers.
Every developer must understand the strategy and agree to follow it.
Branching strategies must be agreed upon only by development team leads, not all developers.
When we discussed maintainability and modifiability in the preceding chapter, we started with a statement that is super fit to use here: “Change is constant.” Therefore, a system or methodology for tracking changes to a code, who made them, and for what purpose becomes a very important component of a project. Version control, or simply versioning, can be at all levels of the development process: a module, function, feature, system, or application.
A number of popular systems are in use today, and most of the time the one you use will be the one that your company supports and that your development team is currently using. Git is such a system; it has gained a lot of popularity recently for its ease of use (at least the latest versions) and simplicity, and because it’s open source. In the next few sections, we describe Git and look at some of its basic and advanced features.
Git differentiates itself in being fast and efficient, and also by having the following interesting features:
A typical VCS stores deltas, or file changes, over a period of time (delta-based version control), whereas Git stores snapshots of files and the filesystem.
Almost all operations are local to your system or computer. All related history is stored locally and can be accessed regardless of whether you’re online or offline.
Data in Git is verified for integrity through a checksum algorithm. A checksum is produced before storing a file, and the checksum is used to refer to the file or data.
For more information about Git and how it works, we highly recommend you refer to the free book written by Scott Chacon and Ben Straub and contributed to by the Git developer community: Pro Git, Version 2.1.240. It was used as a reference for this book. You can refer to the book and to online documentation for installing and learning Git.
When multiple people need to collaborate and contribute to the same Git repository for a project, there needs to be a working agreement on how that work will be done. A team must agree on an operating model for coordinating their contributions to a shared, source-of-truth code repository.
The Git Workflow that a team selects depends on the following:
Shared repo access policies
Will all contributors have read-write access to the shared repository?
Or will some contributors have read-only access to the shared repository?
Untrusted contributors
Are all contributors known up front and trusted with repository read-write access?
Or will some contributors be untrusted or from the public?
Novice versus intermediate Git users
Will users follow a more complex workflow that is used by the vast majority of open-source projects?
Or will they follow a simpler workflow that novice users of Git may easily understand?
The following sections detail the two primary workflows that teams use: the Branch and Pull Workflow and the Fork and Pull Workflow.
The Branch and Pull Workflow is the simpler of the two options for Git workflows. It works best with the following properties:
Shared repo access policies: All contributors have read-write access to the shared repository.
Trusted contributors: All contributors are known up front and trusted with repository read-write access.
Novice versus intermediate Git users: The team requires a simpler workflow that novice users of Git may easily understand.
The Branch and Pull Workflow has the following pros and cons.
This model requires only simple knowledge of Git, only incrementally different from working solo on a Git repository because only a single Git repo is involved.
It does not require a contributor to be aware of the concept of forking.
Because there are no forks, the user does not need to understand how to manage working with distributed Git repos (that is, synchronizing the same source code among many repos).
This model diverges from the way that the vast majority of open-source software works (most open-source software uses the Fork and Pull Workflow).
All code contributors must have read-write access to the shared, source-of-truth repository.
This workflow is less safe because all team members have read-write access and are pushing to the same source of truth, allowing the possibility of overwriting each other’s branches, especially shared branches such as the main or master.
This model cannot work for untrusted contributors who do not have write access to the shared repository.
Next, let’s look at a sample setup, followed by the actual Branch and Pull example.
Assume that you have configured your github.com user profile with an SSH key.
This allows you to execute remote Git operations such as clone and push using SSH-key authentication.
Otherwise, Git defaults to using password authentication, which requires you to frequently type in a username and password.
Assume that bluecodemonks is your team name.
Thus, your team has already created a GitHub organization named bluecodemonks.
Assume that go-hello-world will be a team collaboration project.
Thus, your team has already created a shared, source-of-truth repository named go-hello-world.
Assume that you have already been added as a read-write contributor to the project. Figure 4-1 shows the go-hello-world project.
Step 1. Clone the project repository to your local computer:
$ mkdir ~/Dev $ cd ~/Dev $ git clone [email protected]:bluecodemonks/go-hello- world.git Cloning into 'go-hello-world'... [snipped]
Step 2. You now have a local copy of the project repository on your filesystem, as seen in Figure 4-2, where the local clone of go-hello-world was created.
Step 3. Look around the newly cloned repository:
$ cd ~/Dev/go-hello-world $ tree . ├── LICENSE ├── README.md ├── go.mod └── main.go 0 directories, 4 files
Step 4. Notice that a Git remote repository configuration has already been set up because you cloned from a remote repository.
This repository is named origin (by default).
It refers to the original URI from which this repo was cloned.
Your team’s shared, source-of-truth remote repo is now labeled origin:
$ git remote -v origin [email protected]:bluecodemonks/go-hello-world.git (fetch) origin [email protected]:bluecodemonks/go-hello-world.git (push)
Step 5. Look at what branch you are currently on and what branches are available.
You are on the main branch, denoted by the asterisk (*) character.
Your local main branch is at the same commit as the remote origin/main branch. It is set to track the origin/main remote upstream by default (see Figure 4-3). Thus, if you execute commands such as git push without fully qualifying where to push, it assumes the target is the default origin/main remote upstream. To push to a specific git remote and specific branch on that remote, you may fully qualify the target git push <remote>/<branch>.
$ git branch -avv * main ad0bbc5 [origin/main] Added install and run instructions remotes/origin/HEAD -> origin/main remotes/origin/main ad0bbc5 Added install and run instructions
Step 6. Now it’s time to make a contribution. First, create a local branch to store your local changes. Because you are currently on the main branch, your new branch will be created from main. Example 4-1 shows the CLI commands, and Figure 4-4 illustrates the logical results.
# Check what branch we are on $ git branch * main # Create a new feature-branch from "main" and switch to it $ git checkout -b update-readme Switched to a new branch 'update-readme' # View your branch status $ git branch -avv main ad0bbc5 [origin/main] Added install and run instructions * update-readme ad0bbc5 Added install and run instructions remotes/origin/HEAD -> origin/main remotes/origin/main ad0bbc5 Added install and run instructions
Step 7. Now, make some edits to the README.md file. You can enhance the README.md with uninstall instructions:
$ vi README.md Uninstall ``` $ rm $GOPATH/bin/go-hello-world ```
Step 8. Assess your changes before you stage them.
This step is not required but is extremely good practice to evaluate your changes before you stage them. The steps are described in Example 4-2.
$ git status On branch update-readme Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: README.md no changes added to commit (use "git add" and/or "git commit -a") $ git diff diff --git a/README.md b/README.md index f272b4d..8e86322 100644 --- a/README.md +++ b/README.md @@ -11,4 +11,10 @@ Run ``` $ go-hello-world Hello World -``` No newline at end of file +``` + +Uninstall + +``` +$ rm $GOPATH/bin/go-hello-world +```
Step 9. Stage your changes:
$ git add README.md
Step 10. Assess your staged changes to double-check your edits.
This step is not required but is extremely good practice to evaluate your changes before you commit them. The steps to review changes before committing are described in Example 4-3.
$ git diff --cached diff --git a/README.md b/README.md index f272b4d..8e86322 100644 --- a/README.md +++ b/README.md @@ -11,4 +11,10 @@ Run ``` $ go-hello-world Hello World -``` No newline at end of file +``` + +Uninstall + +``` +$ rm $GOPATH/bin/go-hello-world +```
Step 11. Commit the changes to the local branch:
$ git commit -m "Update README.md with uninstall instructions" [update-readme bb63877] Update README.md with uninstall instructions 1 file changed, 7 insertions(+), 1 deletion(-)
Step 12. Push a copy of your local branch update-readme to the shared source-of-truth repo. The git push command is illustrated in Example 4-4 and logically represented in Figure 4-5.
The source-of-truth repo is named origin in your git remotes:
$ git remote -v origin [email protected]:bluecodemonks/go-hello-world.git (fetch) origin [email protected]:bluecodemonks/go-hello-world.git (push)
Notice that you are currently on the update-readme local branch and that the origin git remote has no such branch:
$ git branch -avv main ad0bbc5 [origin/main] Added install and run instructions * update-readme 8144024 Update README.md with uninstall instructions remotes/origin/HEAD -> origin/main remotes/origin/main ad0bbc5 Added install and run instructions
Thus, you must push the current branch to the origin remote, specifying a fully qualified branch name in the format <remote> <branch>.
$ git push origin update-readme Enumerating objects: 5, done. Counting objects: 100% (5/5), done. Delta compression using up to 12 threads Compressing objects: 100% (1/1), done. Writing objects: 100% (3/3), 367 bytes | 367.00 KiB/s, done. Total 3 (delta 2), reused 2 (delta 2), pack-reused 0 remote: Resolving deltas: 100% (2/2), completed with 2 local objects. Remote: remote: Create a pull request for 'update-readme' on GitHub by visiting: remote: https://github.com/bluecodemonks/go-hello-world/pull/new/update-readme remote: To github.com:bluecodemonks/go-hello-world.git * [new branch] update-readme -> update-readme
Now, verify that the remote origin has a new branch named updated-readme:
$ git branch -avv main ad0bbc5 [origin/main] Added install and run instructions * update-readme bb63877 Update README.md with uninstall instructions remotes/origin/HEAD -> origin/main remotes/origin/main ad0bbc5 Added install and run instructions remotes/origin/update-readme bb63877 Update README.md with uninstall instructions
Step 13. Initiate a pull request (PR) through the repo on the GitHub UI by clicking the Compare & Pull Request button shown in Figure 4-6.
You are taken to the Open a Pull Request page (as shown in Figure 4-7).
Notice that you are asking to merge the update-readme branch into the main branch.
This is your opportunity to provide a name for the pull request, as well as add additional comments if necessary.
Step 14. Scroll down and inspect the proposed code changes. In Figure 4-8, notice the changes in the lower window, shown as “showing 1 changed file with 7 additions and 1 deletion.”
If the code changes are not what you expected, you may make additional commits in your local update-readme branch and then push the additional commits to the origin/update-readme branch, which will then automatically update the pull request.
Step 15. Click the Create Pull Request button. Figure 4-9 clearly illustrates the pull request, and Figure 4-10 shows that the pull request is complete and ready for review.
Step 16. Get your PR code reviewed by a friend or colleague:
The code reviewer should signal that the pull request is in an acceptable/mergeable state by providing an approval signal or comment (shown as thumbs up in Figure 4-11).
If the code reviewer requests changes, the reviewer can comment on the code, and you may fix the identified issues by making additional commits in your local update-readme branch and then pushing the additional commits to the origin/update-readme branch, which then automatically updates the PR.
Step 17. Now that your pull request has been code reviewed and approved, you may merge your PR by clicking the Merge Pull Request button. Figure 4-12 shows the approval.
Subsequently, confirm the merge, as shown in Figure 4-13.
Step 18. The PR will be fully merged after the Confirm button is clicked. For the purposes of maintaining this sample repository, do not merge this PR; instead, leave it in the open state for people to inspect.
Step 19. Because you pushed your branch update-readme to a shared repository, common courtesy is to delete your branch from the shared repository.
Note that this step is unnecessary in the Fork and Pull Workflow.
The Fork and Pull Workflow is the more complex of the two options for Git workflows, but it works in all use cases. In addition, it is the most popular Git workflow in the open-source and commercial worlds because it enables contributions from untrusted contributors without read-write access to the shared, source-of-truth repo.
It works when
Shared repo access policies: Contributors have a minimal read-only access to the shared repository.
Untrusted contributors: Unknown, untrusted, or public contributors.
Novice versus intermediate Git users: The team is capable of following a more complex workflow, especially if they are already familiar with the open-source model.
This Fork and Pull Workflow has the following pros and cons.
This model matches the workflow of the vast majority of open-source software.
All code contributors follow the same workflow:
Whether a contributor is a complete stranger
Whether a contributor has read-only or read-write access to the shared, source-of-truth repository
This workflow is safer because team members with write access by default push changes to their own forks before PR (instead of to a shared repository).
This model can work for untrusted contributors who do not have write access to the shared repository.
This model requires a higher competence of Git, beyond working in a solo repository.
It requires a contributor to be aware of the concept of forking.
Let’s look at a sample setup, followed immediately by the actual Fork and Pull Workflow.
Step 1. Assume that you have configured your github.com user profile with an SSH key.
This allows you to execute remote Git operations such as clone and push using SSH-key authentication.
Otherwise, Git defaults to using password authentication, which requires you to frequently type in a username and password.
Step 2. Assume that bluecodemonks is a GitHub organization that has a code repository that you would like to contribute to.
Assume also that the organization already exists, regardless of whether or not you are a member.
Step 3. Assume that you would like to contribute to the go-hello-world repository.
The community has agreed to use this repository as the shared, source of truth for the go-hello-world project.
Step 4. Assume that you have read-only access to this project, which has been enabled publicly readable by the project maintainers so that anyone on the Internet may view the source code. Figure 4-14 shows the bluecodemonks organization’s project called go-hello-world.
This organization might not know you, but you are still able to contribute!
Step 1. From the shared, source-of-truth repo, fork the project repository to your personal GitHub organization (shown in Figure 4-15).
The shared repository is located at https://github.com/bluecodemonks/go-hello-world.
After clicking the Fork button, you might be prompted to specify the target location for the fork. If this is the case, choose your personal GitHub organization: https://github.com/<your-gitub-id>.
This places the fork at https://github.com/<your-github-id>/go-hello-world.
Step 2. Go to your forked repository in your personal GitHub organization:
It should be found at https://github.com/<your-github-id>/go-hello-world.
For the purposes of illustrating the rest of this example, as shown in Figure 4-16, the repo has been forked to https://github.com/dcwangmit01/go-hello-world.
Step 3. Your fork is now tracking the shared, source-of-truth team repo.
A fork is essentially a clone of the original GitHub repo but is stored in a different GitHub organization than the original rather than your local disk. GitHub tracks the parent of the fork, to set defaults such as default targets for pull request initiation. Notice the difference between the original and the clone (fork) in the logical representation shown in Figure 4-17.
Step 4. Clone the project repository to your local computer:
$ mkdir ~/Dev $ cd ~/Dev $ git clone [email protected]:dcwangmit01/go-hello-world.git Cloning into 'go-hello-world'... [snipped]
Step 5. You now have a local copy of the project repository on your filesystem. The local copy is shown in Figure 4-18.
Step 6. Look around the newly cloned repository:
$ cd ~/Dev/go-hello-world $ tree . ├── LICENSE ├── README.md ├── go.mod └── main.go 0 directories, 4 files
Step 7. Notice that a Git remote repository configuration has already been set up because you cloned from a remote repository.
This repository is named origin (by default).
It refers to the original URI from which this repo was cloned, which is the personal repository fork, instead of the shared, source-of-truth bluecodemonks.
$ git remote -v origin [email protected]:dcwangmit01/go-hello-world.git (fetch) origin [email protected]:dcwangmit01/go-hello-world.git (push)
Step 8. Add an additional Git remote repository configuration for the original shared, source-of-truth repository.
Explicitly name the remote repository upstream, as in the original repo that has been forked.
$ git remote add upstream [email protected]:bluecodemonks/ go-hello-world.git $ git remote -v origin [email protected]:dcwangmit01/go-hello-world.git (fetch) origin [email protected]:dcwangmit01/go-hello-world.git (push) upstream [email protected]:bluecodemonks/go-hello-world.git (fetch) upstream [email protected]:bluecodemonks/go-hello-world.git (push)
Note that you may name these Git remotes however you choose. An alternate configuration may be to name the original shared, source-of-truth forked repository origin and then name any other remote to be the name of the GitHub organization. This helps if you need to access the personal forks of people you are collaborating with. Your configuration might look like this:
$ git remote -v origin [email protected]:bluecodemonks/go-hello-world.git (fetch) origin [email protected]:bluecodemonks/go-hello-world.git (push) jack [email protected]:jack/go-hello-world.git (fetch) jack [email protected]:jack/go-hello-world.git (push) jill [email protected]:jill/go-hello-world.git (fetch) jill [email protected]:jill/go-hello-world.git (push) you [email protected]:you/go-hello-world.git (fetch) you [email protected]:you/go-hello-world.git (push)
The rest of this example assumes the more common naming convention that the original, shared source-of-truth repository is named upstream.
Step 9. Look at what branch you are currently on and what branches are available.
You are on the main branch, denoted by the asterisk (*) character.
Your local main branch is at the same commit as the remote origin/main branch. It is set to track the origin/main remote upstream by default. Thus, if you execute commands such as git push without fully qualifying where to push, it assumes the target is the default origin/main remote upstream. To push to a specific Git remote and specific branch on that remote, you may fully qualify the target git push <remote>/<branch>. Note the logical representation in Figure 4-19.
$ git branch -avv * main ad0bbc5 [origin/main] Added install and run instructions remotes/origin/HEAD -> origin/main remotes/origin/main ad0bbc5 Added install and run instructions remotes/origin/update-readme bb63877 Update README.md with uninstall instructions
Step 10. Now it’s time to make a contribution. First, create a local branch to store your local changes. Because you are currently on the main branch, the new branch is created from main. The steps are illustrated in Example 4-5 and logically represented in Figure 4-20.
# Check what branch we are on $ git branch * main # Create a new feature-branch from "main" and switch to it $ git checkout -b update-readme-2 Switched to a new branch 'update-readme-2' # View your branch status $ git branch -avv main ad0bbc5 [origin/main] Added install and run instructions * update-readme-2 ad0bbc5 Added install and run instructions remotes/origin/HEAD -> origin/main remotes/origin/main ad0bbc5 Added install and run instructions remotes/origin/update-readme bb63877 Update README.md with uninstall instructions
Step 11. Now make some edits to the README.md file. You’re going to enhance the README.md with details:
$ vi README.md This is an example repo.
Step 12. Assess your changes before you stage them.
The step represented in Example 4-6 is not required but is extremely good practice to evaluate your changes before you stage them.
$ git status On branch update-readme-2 Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: README.md no changes added to commit (use "git add" and/or "git commit -a") $ git diff diff --git a/README.md b/README.md index f272b4d..567f23b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # go-hello-world +This is an example repo. + Install ```
Step 13. Stage your changes:
$ git add README.md
Step 14. Assess your staged changes to double-check your edits.
This step, illustrated in Example 4-7, is not required but is extremely good practice to evaluate your changes before you commit them.
$ git diff --cached
diff --git a/README.md b/README.md index f272b4d..567f23b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # go-hello-world +This is an example repo. + Install ```
Step 15. Commit the changes to the local branch:
$ git commit -m "Update README.md with details" [update-readme-2 de7b443] Update README.md with details 1 file changed, 2 insertions(+)
Step 16. Push a copy of your local branch update-readme-2 to your personal fork repo (instead of the shared source-of-truth repo).
The personal fork repo is named origin in your Git remotes:
$ git remote -v origin [email protected]:dcwangmit01/go-hello-world.git (fetch) origin [email protected]:dcwangmit01/go-hello-world.git (push) upstream [email protected]:bluecodemonks/go-hello-world.git (fetch) upstream [email protected]:bluecodemonks/go-hello-world.git (push)
Notice that you are currently on the update-readme-2 local branch and that the origin Git remote has no such branch:
$ git branch -avv main ad0bbc5 [origin/main] Added install and run instructions * update-readme-2 de7b443 Update README.md with details remotes/origin/HEAD -> origin/main remotes/origin/main ad0bbc5 Added install and run instructions remotes/origin/update-readme bb63877 Update README.md with uninstall instructions
Thus, you must push the current branch to the origin remote, specifying a fully qualified branch name in the format <remote> <branch>.
The configuration steps are shown in Example 4-8 and illustrated in Figure 4-21.
$ git push origin update-readme-2 Enumerating objects: 5, done. Counting objects: 100% (5/5), done. Delta compression using up to 12 threads Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 380 bytes | 380.00 KiB/s, done. Total 3 (delta 1), reused 0 (delta 0), pack-reused 0 remote: Resolving deltas: 100% (1/1), completed with 1 local object. Remote: remote: Create a pull request for ‘update-readme-2’ on GitHub by visiting: remote: https://github.com/dcwangmit01/go-hello-world/pull/new/update-readme-2 remote: To github.com:dcwangmit01/go-hello-world.git * [new branch] update-readme-2 -> update-readme-2
Now verify that the remote origin has a new branch named updated-readme:
$ git branch -avv main ad0bbc5 [origin/main] Added install and run instructions * update-readme-2 de7b443 Update README.md with details remotes/origin/HEAD -> origin/main remotes/origin/main ad0bbc5 Added install and run instructions remotes/origin/update-readme bb63877 Update README.md with uninstall instructions remotes/origin/update-readme-2 de7b443 Update README.md with details
Step 17. Now initiate a pull request from your personal fork to the shared, source-of-truth repo on the GitHub UI by clicking the Compare & Pull Request button. You may initiate this pull request from either your personal fork or the shared, source-of-truth repo. In the example, shown here in Figure 4-22, the latter is chosen.
Step 18. You are taken to the Open a Pull Request page.
Notice that you are asking to merge the update-readme-2 branch into the main branch.
This is your opportunity to provide a name for the pull request, as well as add additional comments if necessary.
Figure 4-23 shows the Open a Pull Request page and the text box for adding your comments.
Step 19. Scroll down and inspect the proposed code changes. Figure 4-24, which is a continuation of Figure 4-23, shows the proposed code changes for review before submitting.
If the code changes are not what you expected, you may make additional commits in your local update-readme branch and then push the additional commits to the origin/update-readme branch, which then automatically updates the PR.
Step 20. Click the Create Pull Request button. Figure 4-25 shows the Create pull request step, and it is immediately followed by Figure 4-26 showing the pull request complete.
Step 21. Get your PR code reviewed by a friend or colleague.
The code reviewer should signal that the pull request is in an acceptable/mergeable state by providing an approval signal or comment. See Figure 4-27 for more details.
If the code reviewer requests changes, the reviewer can comment on the code, and you may fix the identified issues by making additional commits in your local update-readme branch and then pushing the additional commits to the origin/update-readme branch, which then automatically updates the PR.
Step 22. Now that your pull request has been code reviewed and approved, you may merge your PR by clicking the Merge Pull Request button. Figure 4-28 shows that the approval has been obtained (for example, 1 approval), and now it is ready for Merge.
Subsequently, confirm the merge, as shown in Figure 4-29.
Step 23. Now the pull request will be fully merged after the Confirm button is clicked. For the purposes of maintaining this sample repository, do not merge this PR; instead, leave it in the open state for people to inspect.
Branching is easy to understand and is a powerful tool when dealing with larger projects involving multiple developers. However, all powerful tools can be useless if the team does not agree on a common workflow and a branching strategy. The following sections discuss the most common branching strategies and how to use them.
A branching strategy is a team working agreement that enables the management of
Developing code: Destabilizes code because development introduces code changes
Stabilizing code: Stabilizes code through reducing the rate of code changes
The need for code development for new features versus stabilizing code for release is a constant push and pull. Thus, a team needs a working agreement on managing this process.
When you’re deciding on a branching strategy, the most important question that influences the outcome is, Do you trust your automated tests?
If your automated tests are comprehensive enough where a pass result on its own is trusted evidence that the code is deployable to production, your team is a shining example of modern software development.
If you do a web search on Git branching strategies, you’ll find a large collection of documented strategies. The most popular ones are as follows:
GitHub Flow: https://guides.github.com/introduction/flow/
Git Flow: https://nvie.com/posts/a-successful-git-branching-model/
GitLab Flow: https://docs.gitlab.com/ee/topics/gitlab_flow.html
GitHub Flow is the recommended branch strategy for all software projects that are disciplined and adhere to comprehensive automated test coverage. New software projects should always start with this modern-day approach.
If you answered “yes” to the question “Do you trust your automated tests?” it enables a lot of good things:
You are able to keep a stable main branch because tests can block the PR merge.
You are able to release to production several times a day.
You can tag versions of your release directly from the main branch.
You have no need for release branches to stabilize your product.
You have no need for code freezes to stabilize your product.
You have no dependencies on manual quality assurance (QA) to stabilize your product.
Here’s a visualization of the GitHub Flow model. Notice that a single, linear main branch exists with only feature branches and without the existence of release branches. This example (shown in Figure 4-30) is the simplest of the Git branching strategies; it keeps a single main branch stable.
If you’re not able to trust your automated tests because they are not comprehensive, then GitHub Flow does not work. Instead, use a different model called Git Flow.
Most of the open-source world uses GitHub Flow.
Git Flow is the recommended branch strategy for software projects that are not disciplined with comprehensive automated test coverage. Typically, legacy software development projects that rely on days, weeks, or months of manual QA testing should follow the Git Flow branch strategy. Git Flow uses release branches to allow development and quality assurance teams to stabilize code for a release, while allowing code development to proceed on the main branch.
Git Flow is not recommended as a first choice for branch strategy, unless your project falls into this legacy category. In fact, the original author of Git Flow now recommends GitHub Flow. You can read the original article at https://nvie.com/posts/a-successful-git-branching-model/.
If you answered “no” to the question “Do you trust your automated tests?” your project may have a lot of legacy qualities:
You are unable to keep a stable main branch because your automated tests are not comprehensive enough to block PR merges based on test results.
You are unable to release to production several times a day.
You cannot tag versions of your release directly from the main branch because main is not kept stable.
You must use release branches to stabilize your product, and you tag your releases off the release branches.
You need code freezes to stabilize your product.
You have dependencies on manual QA to stabilize your product.
Figure 4-31 shows a visualization of the Git Flow model.
The Git Flow branching strategy allows teams to stabilize code in release branches that are code-frozen for bug fixes only. In addition, development may continue to move forward and allow destabilized code to continue to be merged into the develop branch. The main branch would only receive merges from stabilized release branches.
GitLab Flow is a Git branching strategy that is centered around a main branch as the base branch and enables maintaining a separate branch per environment (develop, staging, product). As changes occur in the environment branches, they may be cherry-picked back into the main branch.
This branching strategy can increase complexity and its procedural impact should be carefully reviewed if needed for your use cases. Read more at https://docs.gitlab.com/ee/topics/gitlab_flow.html.
Shared, source-of-truth GitHub repositories should be configured for team access and code reviews. These settings are available via the repo settings page. In this example, the settings page, which is found at https://github.com/bluecodemonks/go-hello-world/settings, is accessible only by repository owners.
Merge button settings are found under Options > Merge Button. To keep a clean Git commit history, we recommend that you rebase merging and thus disable the other options (see Figure 4-32).
Rebase merging enables a team to keep a clean, linear Git history.
In GitHub, a branch protection rule may be used to disallow contributors to push directly to a shared branch without certain prerequisites, such as code reviews. Configuration is done at the page shown in Figure 4-33.
From the main page shown in the figure, an administrator may add a branch protection rule. A common practice is to add a branch protection rule for the main branch.
The main shared branch is often a branch that needs protection, affecting important practices such as
Disabling the ability to directly push to a branch without a code review
Requiring one or more code review approvals before enabling PR merges
Enforcing a linear commit history
The settings are shown in Figure 4-34.
In this chapter we provided you with frequently used commands, techniques, and workflows for version control. As your projects grow in size and the number of developers and testers, your version control system also grows in complexity. Depending on your organization, we highly recommend you take a deeper look at Git through practice and use the Pro Git book mentioned at the beginning of this chapter.
As mentioned in the section “How to Use This Book” in the Introduction, you have a couple of choices for exam preparation: the exercises here, Chapter 17, “Final Preparation,” and the exam simulation questions on the companion website.
Review the most important topics in this chapter, noted with the Key Topic icon in the outer margin of the page. Table 4-2 lists a reference of these key topics and the page numbers on which each is found.
There are no memory tables or lists in this chapter.
Define the following key terms from this chapter and check your answers in the glossary:
URL | QR Code |
ProGit: Everything You Need to Know About Git | |
3.133.114.221