© Johan Abildskov 2020
J. AbildskovPractical Githttps://doi.org/10.1007/978-1-4842-6270-2_5

5. Collaboration in Git

Johan Abildskov1 
(1)
Tilst, Denmark
 

As with many other things, software development is not fun until we do it together with other people. Unfortunately, most software developers are not introduced to Git in a healthy setting. Either they are experiencing Git for the setting in a classroom where the professor has understood that Git is important and that someone should be teaching it to the students, but it is simply a footnote in a much larger curriculum. Or they are introduced to workflows and collaboration in Git in some organization that are more concerned with doing things according to the described process than in a meaningful way. This chapter will hopefully get you back on track and enable you to select a Git workflow and work efficiently with colleagues.

In this chapter, we will first cover the foundations of working with a remote repository. So far, we have only concerned ourselves with local repositories. Fear not, if you have grasped branches, remotes will be a small extension of these concepts. After that, we will compare the most common workflows and discuss pros and cons of each.

Working with Remotes

Git is said to be a distributed version control system, and Git implements this distribution through the concept of remotes. Commonly, we work with a single remote in our repository, and by default, it has the name origin. Most development on software projects start with a clone of an existing project. This instantiates a local copy of the original repository on your computer and saves a reference to the original repository as the remote origin.

In client/server-based version control systems, all commands and actions go through the server. This means that we can do things like lock files, so only one user at a time can modify it. In Git, it is not so. We work asynchronously and then at the user’s leisure synchronize our work. Most commonly, this is done through a common repository manager such as GitHub.

Most of the tasks in collaboration flow around how we manage branches, but other than that, we work with clone, fetch, push, and pull. With these four commands, 98% of your day-to-day collaboration work will be covered.

Note

Collaboration typically takes place on a managed server or cloud solution such as GitHub, GitLab, or Bitbucket. For the purpose of creating self-contained exercises, we are not using a repository manager. We are modeling the workflows using local repositories. The last exercise in this chapter should you choose to complete it requires an account on GitHub and will show off a repository manager.

Cloning

There are two scenarios for starting work on a project. First, it can be a new project. We covered that scenario a long time ago, using git init. Second and perhaps more common, we are going to contribute to an existing code base, open source or proprietary. When starting on an existing code base, the first thing that we do is to clone the repository in order to get a local instance on our machine. We do this with the command git clone <url> <path>, for example, git clone https://github.com/randomsort/practical-git/ git-exercises. This will initialize a local repository on disk, download the entire repository from the remote, check out the default branch into the workspace, and create a pointer to the remote repository named origin. The default branch in most cases is the master branch. If we omit the path parameter, Git will use the repository name instead. In the preceding example, it would be in a folder called practical-git if we omit the path.

CLONING A REPOSITORY

In this exercise, we will clone a public repository from GitHub and look at what we get on our disk. This exercise can be done from anywhere, so is not dependent on the exercise source bundled with this book.

First, we clone the repository https://github.com/randomsort/practical-git-students.

Git tells us a lot about what is going on during the clone, but it is basically uninteresting facts on performance. Consider it a geeky progress bar.
$ git clone https://github.com/randomsort/practical-git-students git-exercises
Cloning into 'git-exercises'...
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 7 (delta 1), reused 2 (delta 0), pack-reused 0
Unpacking objects: 100% (7/7), done.
$ cd git-exercises/
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean
$ ls
README.md  the-practical-git.md
Navigate to the repository and use git status and ls to let us know what we downloaded. We can see that there are a few files, that we have a clean workspace, and that the master branch is up to date with the remote. This is as expected as we have done no work in the repository.
$ git log --oneline -n 5
f18e7bc (HEAD -> master, origin/master, origin/HEAD) Merge pull request #1 from the-practical-git/master
1135048 Add the Practical Git Bio
ce866b9 Initial commit
We use git log to see the history. This will likely look different for you, as more pull requests enter the repository on a frequent basis. Here, we can both see the local branches and those from the remote. The remote branches are prefixed with origin/.
$ git remote show origin
We use the command git remote show origin to see some details about our remote.
* remote origin
  Fetch URL: https://github.com/randomsort/practical-git-students
  Push  URL: https://github.com/randomsort/practical-git-students
  HEAD branch: master
  Remote branch:
    master tracked
This section shows the basic information about the repository’s remote. Usually, fetch and push point to the same repository, but if you have a highly distributed setup, there is the possibility of having different read and write servers.
  Local branch configured for 'git pull':
    master merges with remote master
  Local ref configured for 'git push':
    master pushes to master (up to date)
$ git branch
* master
$ git branch --remote
  origin/HEAD -> origin/master
  origin/master

This exercise showed you how you could clone a repository and see the origin.

Synchronizing with Remote

Now that our local repository is established, we are set up to do some work. The common workflow in Git is to do some work locally and then synchronize that work with the remote. Delivering our work to the remote is called pushing. When there is work available on the remote that we do not have locally, we can get that work using pull or fetch. There are a few types of differences that can occur between a local remote. They can either be about objects or references. For the purpose of this chapter, commits are the only type of objects we concern ourselves with. When we synchronize objects, it is always an additive operation. We always deliver more objects or download more objects. We can never delete or modify objects either locally or remotely. This makes object operations safe, as we never lose an object, bar garbage collection. Secondly, we can need to synchronize references, that is, branches and tags. They can either disagree on what they point to or whether they should exist at all.

These divergences are reconciled using branching methods and the methods for interacting with the remote: push, fetch, and pull. Pull is a shorthand for a fetch and a merge. Push is the least interesting of the commands. We send the references and objects that we have to the remote – and if the remote is unable to do a fast-forward merge on the references, then it will reject the change.

When we fetch, we get all the objects from the remote that we are missing. Then, we get the references from the remote. They are name spaced such that references from the remote named origin are prefixed with origin/; thus, the master branch on the origin is called origin/master when we look at it from our local repository. Thus, the flow for getting the changes from the master on origin is as follows:
  • Fetch: To get objects and references from remote

  • Merge: To get the changes from the remote onto our local master branch

This can be seen in Figure 5-2.
../images/495602_1_En_5_Chapter/495602_1_En_5_Fig1_HTML.jpg
Figure 5-1

Git repository managed centrally, but cloned locally. Alice and Bob can work asynchronously and either coordinate work at the origin or between each other

../images/495602_1_En_5_Chapter/495602_1_En_5_Fig2_HTML.jpg
Figure 5-2

(a) Repository before fetching. (b) Repository after fetching. (c) Repository after merging

../images/495602_1_En_5_Chapter/495602_1_En_5_Fig3_HTML.jpg
Figure 5-3

(a) Scenario prepush. (b) Scenario after push. Note that before the push, C and D are not available on the remote

../images/495602_1_En_5_Chapter/495602_1_En_5_Fig4_HTML.jpg
Figure 5-4

(a) Before we push, this is our world view; (b) as there has been done work on master on the remote (commit E), push will be rejected. After fetch, this is how it looks (c). We reconcile the differences with a merge, and the result can be pushed which leads to (d) status after push

../images/495602_1_En_5_Chapter/495602_1_En_5_Fig5_HTML.jpg
Figure 5-5

A pull request from a fork to the original repository. Commonly, the owner of the fork has no access rights to the original repository

../images/495602_1_En_5_Chapter/495602_1_En_5_Fig6_HTML.jpg
Figure 5-6

Fork button in the repository

../images/495602_1_En_5_Chapter/495602_1_En_5_Fig7_HTML.jpg
Figure 5-7

Forking the repository to your account

../images/495602_1_En_5_Chapter/495602_1_En_5_Fig8_HTML.jpg
Figure 5-8

Commit in your repository – click Pull request

../images/495602_1_En_5_Chapter/495602_1_En_5_Fig9_HTML.jpg
Figure 5-9

Seeing the changeset. Create pull request

../images/495602_1_En_5_Chapter/495602_1_En_5_Fig10_HTML.jpg
Figure 5-10

Open a pull request

../images/495602_1_En_5_Chapter/495602_1_En_5_Fig11_HTML.jpg
Figure 5-11

Pull requests tab

../images/495602_1_En_5_Chapter/495602_1_En_5_Fig12_HTML.jpg
Figure 5-12

Interact with pull request contributor

When we push and get our changes rejected, we go through a fetch/merge loop and will then be able to deliver our changes.

Note

We can see the origin/ namespace as our cache of how the remote repository looks. This is not automatically synchronized by Git, so we need to do the fetch to update our cache. Thus, when we run Git status, the output is based on our cache, rather than what is on the remote, and this might yield to unexpected results.

We will cover how this plays out in the next exercise based on the simplified workflow. Now that we have investigated the moving parts of working with a remote, we can see the different ways of working and how we can work within them.

Simplified Workflow

You might have come across the term simplified workflow, master based or centralized workflow. This workflow is known by many names and is default workflow unless you configured your repository manager differently. The defining characteristic of this workflow is that all collaboration happens directly on the master. This means that while you may have local branches to isolate your work, when you are done, you push to master. This workflow is how I work with my toy projects, note repositories, and similar things. The good thing is there is little overhead and almost no process. This makes it an efficient workflow that is easy to understand, that is, if we stay on the happy path. The bad thing is that we can have race conditions with our colleagues, and we have no workflow tools that help keep the quality of the source on our master branch high.

There are basically two scenarios that we need to cover in a master-based workflow. First, there is the happy scenario where no work has been done in master while we have been working locally. This case is boring, as this works, and becomes a fast-forward merge on the remote. This scenario can be seen in Figure 5-3.

Then, there is the race condition scenario where a colleague has delivered work to the master branch while we were working locally. This is a much interesting scenario, as it takes some measure of effort to resolve. The technical detail is that repository managers only allow you to push fast-forward merges. Any other types of merges must be reconciled locally. This means that the scenario with competing deliveries looks like this:
  • Clone or fetch from origin.

  • Do work locally and commit.

  • Push, and be rejected from remote.

  • Fetch newest changes and merge them into your local master branch.

  • Push master to origin, as it is now a fast-forward merge.

Note

It is entirely possible that this can happen repeatedly thus locking out developers from delivering their changes. This either means that the repository spans too many architectural boundaries or that the workflow you are using is not scaling with your organization. In any case, this is unlikely to happen for normal usage, so if you end up here, step back and reflect on repository architecture.

The preceding workflow can be seen in Figure 5-4. First is the scenario where local changes will be rejected, then the reconciliation, and the fast-forward merge on the remote.

In the following, we will do an exercise that simulates interacting with a remote repository on the master branch. As this is a more complex exercise than the previous exercises, I am going to run through the master-workflow kata in this exercise. The kata is available in the git-kata repository.

MASTER-BASED WORKFLOW
In this exercise, we are going to go through the entire master-workflow kata and experience both the happy path and the path with a race condition.
$ git clone https://github.com/praqma-training/gitkatas
Cloning into 'gitkatas'...
remote: Enumerating objects: 111, done.
remote: Counting objects: 100% (111/111), done.
remote: Compressing objects: 100% (99/99), done.
remote: Total 1961 (delta 26), reused 35 (delta 10), pack-reused 1850
Receiving objects: 100% (1961/1961), 528.24 KiB | 1.56 MiB/s, done.
Resolving deltas: 100% (825/825), done.
$ cd gitkatas/
$ cd master-based-workflow/
$ source setup.sh
--- Truncated Output ---
Now, we have fetched the kata and run the proper exercise script, so we are ready to move through the exercise as described in the README.
$ ls
fake-remote-repository/  fitzgerald-pushes-before-we-do.sh*
First, we clone the fake remote repository and make a commit in our local repository. Then, we can investigate the relationship between our local and the remote.
$ git clone fake-remote-repository/ local-repo
Cloning into 'local-repo'...
done.
$ cd local-repo/
$ echo "line of text" >> README.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
        modified:   README.md
no changes added to commit (use "git add" and/or "git commit -a")
We note that since we have not created any commits, we are still up to date with the remote master, also designated origin/master.
$ git add .
$ git commit -m "Added content to the README"
[master 9eea570] Added content to the README
 1 file changed, 1 insertion(+)
$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)
nothing to commit, working tree clean
As we have created a single commit, and no work has been done on the remote, we are up to date.
$ git push
Counting objects: 3, done.
Writing objects: 100% (3/3), 279 bytes | 279.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To C:/Users/rando/repos/randomsort/gitkatas/master-based-workflow/exercise/fake-remote-repository/
   054c055..9eea570  master -> master
We can now deliver a change to the remote and move on to the nonhappy path scenario.
$ echo "Another line of text" >> README.md
$ git add README.md
$ git commit -m "Update README"
[master d144b48] Update README
 1 file changed, 1 insertion(+)
Now, after we have updated the README and made another commit, we run a script to simulate our colleague delivering work.
$ ../fitzgerald-pushes-before-we-do.sh
 --- Output truncated ---
$ git push
To C:/Users/rando/repos/randomsort/gitkatas/master-based-workflow/exercise/fake-remote-repository/
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'C:/Users/rando/repos/randomsort/gitkatas/master-based-workflow/exercise/fake-remote-repository/'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Now when we try to push, we get rejected by the remote. If we read the error output, we can see that our push is rejected because the remote contains work that we do not. However, when we run git status, we are told we are up to date.
$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)
nothing to commit, working tree clean
This is because we have a local cache of the remote state and that is not updated when we push, but rather during the fetch.
$ git fetch
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From C:/Users/rando/repos/randomsort/gitkatas/master-based-workflow/exercise/fake-remote-repository
   9eea570..96a3f9c  master     -> origin/master
$ git status
On branch master
Your branch and 'origin/master' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)
nothing to commit, working tree clean
After the fetch, status tells us that we have diverged from origin/master. This is the scenario shown in Figure 5-4(b).
$ git log --all --graph --decorate --oneline
* 96a3f9c (origin/master, origin/HEAD) Fitz made this
| * d144b48 (HEAD -> master) Update README
|/
* 9eea570 Added content to the README
* 054c055 Add README.md
$ git merge origin/master -m "merge"
Merge made by the 'recursive' strategy.
 fitz-was-here.md | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 fitz-was-here.md
$ git merge origin/master
$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)
nothing to commit, working tree clean
After we have merged, we are in the state as shown in Figure 5-4(c). We are two commits ahead, the commit we had locally and the merge commit.
$ git log --all --oneline --decorate --graph
*   a73deeb (HEAD -> master) Merge remote-tracking branch 'origin/master'
|
| * 96a3f9c (origin/master, origin/HEAD) Fitz made this
* | d144b48 Update README
|/
* 9eea570 Added content to the README
* 054c055 Add README.md
We can now push as we have established the conditions for a fast-forward merge from origin/master to master .
$ git push
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (5/5), 582 bytes | 582.00 KiB/s, done.
Total 5 (delta 0), reused 0 (delta 0)
To C:/Users/rando/repos/randomsort/gitkatas/master-based-workflow/exercise/fake-remote-repository/
   96a3f9c..a73deeb  master -> master
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean

As we have seen in this exercise, nothing tremendously exciting is happening, and it is simple to reconcile the race condition in this simple scenario. If you encounter many merge conflicts, this is a sign that you should investigate a different way of working.

The master-based workflow is not bad for simple projects, and the low amount of overhead and process is attractive for many. If you are just starting out, this is a good workflow to get your bearings. If you continuously pay attention to whether the lack of process is hurting your productivity, you should be good.

Fork-Based Workflows

Fork-based workflows are commonly used in open source software, where the trust model is a bit different than inside an organization. While open source means that everyone can contribute, it does not mean that all changes will get into the projects. The fork-based workflow helps enable this way of working.

In fork-based workflows, we have multiple remote repositories. One of which is the original and contains the ultimate truth of the project. Let us say that I want to contribute to a major open source project such as Kubernetes. I can’t simply clone the repository and push back any changes I would like. First, there is the issue of the quality of my delivery, what if I am horribly incompetent and my work should be kept out? Second, there is also the vision of the product. If there are no clear vision and guidelines to what features the project is interested in supporting, it will become an unmaintainable and unusable mess over time. Thus, even if my work is good, the project might not be interested in integrating it. And lastly, the two previous points were even assuming that my intentions were benign. If we do not have guard rails or some sort of access control, all open source projects would be instantly compromised by bad third parties. There have been situations where evil actors have injected vulnerabilities in high-profile open source projects, thus compromising all those that depended on that code.

The solution to this is that we create a so-called fork of the original project, on our own namespace. This gives us full access to our fork. We can then make our changes and submit those back to the original project using a mechanic commonly called pull requests. This can be seen in Figure 5-5.

Note

It is called pull requests because you make a second remote available and request the maintainers to pull your changes into their repository.

FORK-BASED WORKFLOW

This exercise is a bit different in that it will require a GitHub account, and it will be based more on screenshots than on command-line interface.

If, however, you complete this exercise, you will have contributed to a public repository on GitHub.

This exercise assumes that you have a GitHub account and that you are logged in.

First, we are going to locate the repository that we are going to contribute to and create a fork of that.

To do so, open https://github.com/randomsort/practical-git-students in your browser and locate the fork button as seen in Figure 5-6.

Clicking the fork button will fork the repository to your own account and take you to this page in Figure 5-7, where it will be your own username rather than mine showing up.

We can note that it is explicit from where we have forked the repository.

Now that we have our own fork, or working copy, we can clone this link, either through the clone button or the command line.

I will clone through the command line. I will not cover how to set up credentials or anything here.
$ git clone https://github.com/the-practical-git/practical-git-students
Cloning into 'practical-git-students'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
I will now go into the folder and create a file with my bio in it.
$ cd practical-git-students/
$ touch the-practical-git.md
$ vim the-practical-git.md
$ git add .
$ git commit -m "Add the Practical Git Bio"
[master 1135048] Add the Practical Git Bio
 1 file changed, 11 insertions(+)
 create mode 100644 the-practical-git.md
$ git push
Username for 'https://github.com': the-practical-git
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 493 bytes | 493.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/the-practical-git/practical-git-students
   ce866b9..1135048  master -> master

Note that if you are following along with the exercise, you should choose your own name or username as the filename, and you should use your own username for authenticating for GitHub. Depending on how your local Git installation is configured you may be prompted for credentials, or it might just work.

Now, we can go back to our fork and see that the changes we made showed up in the GitHub interface. For me, I go to https://github.com/the-practical-git/practical-git-students, but you will have to substitute with your own username.

We can see in Figure 5-8 that we now have a commit that is not present in the original repository. This is what we would like to contribute back! So we click the Pull request link to the right.

This takes us to Figure 5-9 where we can see the changeset and what branches we are using. In this case, we will contribute back to the master branch in the original repository, what is on our master branch in our fork. So we click the Create pull request button.

This takes us to Figure 5-10 where we can add a bit more information to the pull request. Commonly, we will describe the changeset, or the reason for the change. This is our communication toward the maintainers of the repository. In this case, our changeset is trivial, so we only add a brief description before clicking Create pull request.

In many scenarios, there will be a bit back and forth between the contributors and the maintainers to make sure that the pull request adheres to their coding guidelines, has the documentation and tests they need, and so on. In this case, I will accept your pull request, if you keep the language clean and kind, and do not cover political or religious issue. I would love for you to say Hi, though!

Now that you have created the pull request, your work is done, unless there are any requests by the maintainers for reworks. From the maintainer side, we can now go and find the pull request in the Pull requests tab, as seen in Figure 5-11.

We click the pull request to see what is being contributed, and here we can comment and interact with the contributor (Figure 5-12).

As the maintainer, I can click the Merge pull request and accept your changes. If you have been following along with this exercise, I am looking forward to merging in your commits!

Note that while this exercise was performed in GitHub, all the big repository managers support fork-based workflows.

This has been an exercise into fork-based workflows, commonly used in open source setups. I know some open source projects who have their source in Git have different email-based systems, but that is so arcane and not used a lot that we will not go into details on that. In the next section, we will present the more commonly used workflow inside of organizations.

Pull Request–Based Workflows

While we could argue that the fork-based workflow described earlier is also based on pull requests, the workflow we go through in this section is commonly known as pull request–based workflow. It is a simpler version of the fork-based workflow, starting from the fact that inside an organization we have a different trust model. Everyone is allowed to contribute directly to the repository, even though not everyone necessarily has access rights to merge to the master branch.

The way this works is by using branches as the abstraction rather than forks. This causes much less overhead in terms of keeping repositories, local and remote, up to date. The workflow in a pull request–based workflow is as such:
  • Clone or fetch the repository.

  • Create a feature branch.

  • Do work locally and commit to your feature branch.

  • Push your feature branch to the remote.

  • Go to the remotes web interface and create a pull request from your feature branch to your master branch.

  • Those that have access rights merge or request changes.

The pull request–based workflow is simple, understandable, and does not have a lot of overhead. However, pull requests lend themselves to a few antipatterns that we will cover here. First, depending on your way of working, pull requests might be a manual gate, requiring reviews and manual approval. This can lead to handoffs and delayed feedback loops; this reduces productivity and morale and leads to lower-quality software.

Second, pull requests tend to be created late in the development process, when we are ready to deliver. To great effect, they can be created at the start of the process as a work in progress branch. This will create traceability and add the ability for early feedback on work, and collaboration, which increases productivity.

Third , when many pull requests target the same master branch, this can also lead to issues in synchronizing and maintaining the pull request, while those that are in front in the queue get processed. This can also lead to broken builds on the master due to tests being run on another state that ends up being merged.

Again, if this is something that you encounter, you have outgrown this way of working, or your repository architecture.

Git Flow

I have had a long inner discussion on whether to cover the Git Flow or not. It is a workflow that I have seen many organizations adapt, and none succeed with. It is described by nvie in his blog post at https://nvie.com/posts/a-successful-git-branching-model/ as “a successful git branching model.” While I am certain that some organizations have had good fortune with this workflow, but Git, the tools that surround it, and our ways of working have outgrown it. Thus, Git Flow is, for most uses, an antipattern. The problems that we try to solve by introducing the abstractions and the “develop” branch often end up with doing the exact opposite. We end up with long merge queues, complex workflows, and integration hell in multiple directions. So, I do really recommend against it.

The scenario I can imagine where Git Flow is useful is if you have a completely dysfunctional way of working and you need a temporary transition flow in order to get to a sane place. This can help with the organizational resistance, tooling, and upskilling as required.

Git Katas

To support this chapter’s learning goals, I recommend that you go through your master-workflow kata of the previous exercise. After that, if you have not already covered the exercise doing a pull request on GitHub, I suggest you backtrack to that and make the pull request now. I look forward to saying Hi and hearing from you!

Summary

In this chapter, we covered a few basic Git workflows and showed how you can collaborate using Git. Hopefully, you now feel more confident that you can be a valuable contributor in a software organization. It is important to me that you take charge of your workflow and do not let the workflow dictate how you work, but rather let the way you work dictate your workflow. If there is a mismatch between the desired way of working and the implemented Git workflow, you will live in pain and frustration.

I recommend that you consider the following questions on a routine basis:
  • Is my workflow introducing manual gates or handoffs?

  • Is my workflow making it easy to deliver changes?

  • Do I feel confident in our workflow?

  • Does the workflow introduce unnecessary bureaucracy?

  • What are the common mistakes that our developers make? Can we do something to minimize either the impact or frequency of these?

If we keep asking these questions and accept that our workflow is not a dead static thing, but rather something that lives and evolves together with our software, we will end up in a good place.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.212.99