Git workflow for modern projects

After four years of a break for doing some management stuff and other non-development tasks I have decided to get back into coding (or at least to start thinking about getting back to it). First thing was to prepare development environment in general and refresh git flow knowledge in particular.

This article is a mixture of both my old article about Git+Gerrit workflows and Git workflow for Yii 2 contributors article. I assumed minimalist approach to make it possible to use this new workflow for virtually any Git-based project. Thus, I have purged most of, if not all of the things that are specific to Gerrit and Yii2.

Make sure that:

before continuing with this article.

There’s a handy summary checklist at the end of this article.

Use it, if you are overwhelmed with the length of the following contents table or the entire main article.

Initial steps

Fork the repo

Do this only once per each project you want to contribute to.

For obvious reasons, if you want to propose changes to someone else’s project you can’t push theses changes directly to that project’s repository. If dozens of developers would do such thing in the same time, it would make that repository (and the whole project) bloated with source code pieces coming from different sources.

Thus, to avoid this, you have to fork that project’s repository to your own GitHub account.

Clone the repository

Do this only once per each computer you’re working on.

As with every repository (your own or forked) you have to clone it to your local computer:

git clone git@github.com:[USERNAME]/[REPONAME].git

If you have trouble doing above, i.e. you’re getting errors like “Permission Denied (publickey)”, then go through checklist given in introduction to this article to make sure that you have everything in place.

Pull the newest version of the code from remote repository to your local copy of it:

git pull origin master

Add the main repository (i.e. the one where you want to push your changes) as an additional remote repository called “upstream”:

git remote add upstream git://github.com/[USERNAME]/[REPONAME].git

In the next steps it will be clarified, why we need the last one.

Adding new feature or change

You through these steps every time you are working on some new feature, some change or bug fix.

Create an issue for your work

Nearly every project, you contribute to, has an issue tracking system. This step is optional, but consider creating an issue in such system where you will be able to discuss your change, feature or fix, describe your pull request etc.

In GitHub your have all the issues at https://github.com/[USERNAME]/[REPONAME]/issues.

Create a new branch for your feature or fix

The branch is another copy of your code that you can switch together. You can mess all the way around and if at some point you hit the wall you can simply delete a branch without worrying that your main code is changed in anyway. That’s one of the beauties of Git. For more details — see the book.

Working directly on master branch is wrong. If you have doubts, why then either believe me or browsing The Git Book to find the answer.

The key rules here are:

  1. Each separate bug fix or change should go in its own branch.
  2. Each new branch’s name must start with an issue number that corresponds to it.
  3. Each new branch must be based on master branch from the project you are contributing to.

For the last point we use upstream remote, you have created as a one of initial steps:

git checkout [UPSTREAMREMOTENAME]/master
git checkout -b [ISSUENUMBER]-[BRANCHNAME]

Thus, for example:

git checkout upstream/master
git checkout -b 198-user-gender-field-is-invisible-in-user-info-page

Branch name can’t have spaces and letter case is ignored. That’s why we’re playing here with all this dashes and small letters.

You should always use branches, even for doing changes as small as few bytes of code or fixing a typo. And even, if you’re coding for your own purpose, and you’re both committer, reviewer and project manager. If you don’t know why then simply believe me — this will save your ass many times in the future.

You can also browser the famous book’s branching chapter to find all the answers

Code!

Do your magic: code, test, review, fix, code again etc. Make sure everything works as expected.

Update the changelog

This is optional, but many projects has the CHANGELOG file and requires you to update it in each change, whether it is a new feature, some update or a fix for some bug.

In most cases this is as simple as adding one line to CHANGELOG file, which usually is a text file. You can do this manually or append a new line to that file directly from the console:

echo "Bug #198: Make user gender field visible again in user info page (John Doe)" >> CHANGELOG

You can find more info and some example here or in thousand places in the Internet.

Verify and commit all changes

Verify all created or modified files, add them to index and commit all changes:

git status
git add --all
git commit -am "Commit message"

If you’re still not familiar with all the stuff like index, adding files, local repository, committing etc. then review basic git diagram again for some clarity.

Commit your changes with a descriptive commit message (some ideas may be found in this (five years old!) article. Make sure to mention the issue number with #XXX so GitHub will automatically link your commit with the ticket. Here is an example:

git commit -m "Make user gender field visible again in user info page. Fix #198"

On contrary to branch name, commit message can and should have spaces and, of course, shouldn’t have any spelling mistakes.

Pull the latest code from upstream repository

This ensures you have the latest code in your branch before you open your pull request.

git pull upstream master

If there are any merge conflicts, you should fix them now and commit the changes again (i.e. go to step 2.3). This ensures that project’s owner will be able to merge your changes with one click.

Push your code to GitHub

Having resolved any conflicts you’re ready to go:

git push -u origin 198-user-gender-field-is-invisible-in-user-info-page

The -u parameter ensures that your branch will now automatically push and pull from the corresponding GitHub branch. That means that if you type just git push the next time it will know where to push to and you will not have to use the above code again.

This is useful if you want to later add more commits to the same pull request.

Create a pull request

You now need to create a pull request to let project’s owner know that you have some new, cool stuff for his project (i.e. a bug fix or a new feature).

Go to your repository on GitHub (your fork, not to the repository of a project you are contributing to) and click Pull Request, choose your branch on the right and enter some more details in the comment box.

To link the pull request to the issue, again, put #999 anywhere in the pull comment where 999 is the issue number (#198 in our above examples here).

The rule of thumb behind pull requests is the same as behind branches. Each pull request should always fix a single issue. For multiple, unrelated changes, please open multiple pull requests (and use multiple branches).

Wait for the review (or review yourself)

Your change will appear among other pull requests. In GitHub this is always this page:

https://github.com/[USERNAME]/[REPONAME]/pulls

At this point someone will review your change (or you will review it, if you’re using Git for your very own purpose).

If you are asked to make some changes, go to step 2.3). You don’t need to open another pull request if your current one is still open.

If your code is accepted it will be merged into the master branch and become part of the next release of the project you are contributing to. And your name will be carved on the stone wall of fame of that project and blah, blah, blah! :>

Don’t be disheartened or even surprised, if your pull request is rejected (even if it contains no bugs). Different people need different features and each project can’t be everything to everyone. Your code will still be available on GitHub as a reference for people who need it (it can be merged into some fork of the main project’s repository etc.).

This is the very same reason for which you should use branches and pull requests even for simple, two-people projects (or often even for yourself). Thanks to this you keep a clean track of all your changes, you can review everything every time, you can go back to some old, abandoned idea or rewind your project to some point etc.

Clean up your local repository and your fork

After your code was either accepted or declined you can delete branches you’ve worked with for this purpose, both from your local repository and your fork on your account in GitHub:

git checkout master
git branch -D 198-user-gender-field-is-invisible-in-user-info-page
git push origin --delete 198-user-gender-field-is-invisible-in-user-info-page

That would be all.

Quick review (checklist)

Initial steps (once)

  1. Fork the original repository to your account.
  2. Clone your fork: git clone git@github.com:[USERNAME]/[REPONAME].git.
  3. Pull the code from it: git pull origin master.
  4. Add an upstream: git remote add upstream git://github.com/[USERNAME]/[REPONAME].git.

Dealing with a new change (each time)

  1. Create an issue for your work
  2. Create a branch:
    • git checkout [UPSTREAM]/master,
    • git checkout -b [ISSUENUMBER]-[BRANCHNAME].
  3. Code. Do the magic.
  4. Update the changelog: echo "Bug #999: Description of a change (Your Name)" >> CHANGELOG.
  5. Verify and commit:
    • verify: git status
    • add: git add --all
    • commit: git commit -am "Commit message".
  6. Pull from upstream: git pull upstream master.
  7. Resolve merge conflicts, if there are any.
  8. Push your code:
    • first time: git push -u origin [ISSUENUMBER]-[BRANCHNAME],
    • later just: git push.
  9. Create a pull request.

Cleaning up the mess

  1. Wait for the review (or review yourself).
  2. Checkout: git checkout master.
  3. Delete branch in local repository: git branch -D [ISSUENUMBER]-[BRANCHNAME].
  4. Delete branch in remote repository (fork): git push origin --delete [ISSUENUMBER]-[BRANCHNAME].

Make yourself a good copy and read the passionate book again! :>

Leave a Reply