maciej łebkowski Maciej Łebkowski

Git workflow, branch naming and pull requests

in Professional


Git is a very powerful tool and there’s always a lot of ways do achieve same goal. We chose this kind of workflow but of course YMMV. This is the basic workflow for git at Docplanner. It’s loosely based on gitflow, but hasn’t got that much in common.

We used Github from the start and it’s our main and only fronted. It’s also the origin for our repo. We have tried Bitbucket and Gitlab but with no luck. Every project, public or private, goes to Github.

As for the clients — it’s mainly PhpStorm and some command line for more advanced tasks (or for more proficient users). Some tool for displaying the history in a graphical way is often useful (gitk or gitx are good choices).

We’re focusing our development on branches and using pull requests to merge larger pieces of code into master. From there, a continuous deployment is set up to publish the application to staging and production environments.

Branch structure

In addition, you can combine them together:

We decided to use hyphen-notation for the names.

Usage of forks

Since Github allows us unlimited forks of private repos, and they do not count to the private repo limit (they are basically free), we use private forks as the RFC branches. Small features, implemented by one or two people, are pushed there not to pollute the main repo.

Merging back to main branch

Whether it’s the feature branch or the master, all large features need to be merged back using a pull request.

  1. Clean up your code. Remove comments, reformat code to comply with the codings standard and do every other thing you would otherwise notice during the code review
  2. If you can — rebase the branch. Rebase can do two things:
    • rebase -i main-branch --onto $(git merge-base main-branch) — squash multiple commits into one, without moving the branch (this way you won’t have to resolve conflicts after every squashed commit)
    • rebase main-branch — and move your branch to the latest commit on the main branch. This results in a cleaner history and and up to date code (without conflicts).
  3. Choose your branch name, then push and create a pull request. Make sure to define the correct branch to merge to (Github always defaults to master), and give it a nice description. It’s always good to point out what has changed, what was the reason for those changes and is there something tricky in particular about this diff. Use "@" to mention the devs you want specifically to review your code.

Remember that you should never rebase a public history (pushed commits). We make an exception for that for rfc/ branches. Just make sure that everyone know not to fetch them and base their work of them.

Fixing code after review

So your teammates are now beating the living shit out of your code and you comply and fix your shitty feature. And depending on your skills it may take one, two, or more rounds like this. After every set of comments, fix them in a commit and push to the same branch. The pull request will refresh automatically.

After the nightmare is done, you have three options:

A poor man’s rebase

Sometimes you’re stuck on a feature branch with lots of commits and lots of merges from master. I don’t know a way to rebase those merge commits, but there’s a trick to squash it all to one commit:

git reset --soft main-branch
git commit -am 'my new feature rebased'
git push origin HEAD:refs/heads/feature/bite-my-shiny-metal-ass

This way all your files stay the same, but the HEAD is moved and git sees all your changes made on your branch as uncommitted.

Skipping the pull request

If you ever want to skip the pull request, make sure not to make a fast-forward merge. This is done by git merge --no-ff …. If you skip this flag, the history could become fucked up.

* [03d2ab0] (master) (HEAD)
* [4163f27] merge master into feature/hal-9000 [parents: 32a0828, 798b456]
| \
|  *  [798b456] this commit was on master before   
*  |  [32a0828] implement AI in feature/hal-9000
|  *  [92b8700] some other stuff

Doing git log --first-parent will show commits from the feature branch instead of the master branch. This is because of the "merge master…" on your feature branch just before reintegrating your feature. Instead, with --no--ff:

* [7006fcf] (master) (HEAD)
* [9344222] merge feature/hal-9000 into master [parents: 798b456, 4163f27]
| \
|  * [4163f27] merge master into feature/hal-9000 [parents: 32a0828, 798b456]
*  | [798b456] this commit was on master before   
|  * [32a0828] implement AI in feature/hal-9000
*  | [92b8700] some other stuff

And everything is nice and clear (notice the order of parents in merge commit in HEAD~).

That’s it

One last rule — you should never merge your own branches. Make someone else do it. This way there are at least two people responsible for that code being on master (the committer/author and the developer that merged it in).

Delete your branches after use. You’re done.

Creative Commons License

This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License . This means that you may use it for commercial purposes, adapt upon it, but you need to release it under the same license. In any case you must give credit to the original author of the work (Maciej Łebkowski), including a URI to the work.