Git has proven a very versatile and powerful tool at the disposal of software development teams but it’s distributed nature introduces a somewhat steep learning curve for teams that use centralized version control systems. Further complicating matters the git user interface is notoriously complex, offers several different ways of achieving a goal and is geared toward supporting large scale development (e.g. the Linux Kernel). In this article we will discuss the practical aspects of using pull requests (also known as the GitHub Flow) with a few tips for avoiding RSI injuries.
This is how the workflow we use when working with git looks when drawn with pretty colors
The rule of thumb for the team is to open a pull request for every change in the system and do so as soon as possible. The pull request becomes the unit of managing change. The reasoning for the change, decisions and high level description are part of the pull request, the technical details are included in the commit messages.
This method is proving very productive and provides several advantages when dealing with tracking, issue management and continuous integration (a subject for a separate post altogether).
But how do the symmetrical shapes on the diagram translate in the day to day work with git? And what happens after a few of these pull requests have been created and merged?
A typical example
…is adding a code component that includes version information (let’s name it the metadata component)
We start from master (
git checkout master) and make sure we have the latest version (
We then create a new branch with a name that will let us remember what it is about (
git checkout -b metadata_component).
After that, we work, work, work until there is something to show at which point we stage our changes (
git add .) and commit them (
git commit -m "a meaningful commit message").
We have now reached the point where we publish our changes for all to see (
git push --set-upstream origin metadata_component). Now we change to the GitHub/BitBucket/Stash/GitLab etc. web interface and create a pull request between metadata_component and master.
Code review and discussion on the merits of the change take place in the pull request interface offered by the host server and finally the change is merged in master. We delete the branches on the server when merging pull requests, since all commits in the branch are now part of master.
Once a pull request is merged there is some housekeeping to do on our local repository.
First we go back to master (
git checkout master) and pull the merged changes synchronizing our local repository with the server(
git pull). Then we remove the local PR branch (
git branch -d metadata_component) the same way it was removed when merging on the server. And then we remove all tracking information about non-existent remote branches (
git remote prune origin).
At the end of this process the local repository clone will be free of accumulated cruft.
Avoiding tennis elbow
The whole process involves quite a bit of typing. Creating a pull request for every change (yes, even for tiny bug fixes) means the process is repeated several times per day. Some people might be discouraged by the number of keystrokes and resort to “spring cleaning” once in a while.
Luckily git provides us with a very flexible alias system that when applied prevents a lot of RSI inducing activity.
Add the following in the .gitconfig file (~/.gitconfig in *ix systems, c:\users\username\.gitconfig on Windows)
prune = remote prune origin
cleanup = !git branch -d $1 && git remote prune origin && :
publish = push --set-upstream origin
|git remote prune origin||becomes||git prune|
|git branch -d metadata_componentgit remote prune origin||become||git cleanup metadata_component|
|git push –set-upstream origin ||becomes||git publish|
The creation of more elaborate aliases is left as an exercise to the reader. It is certainly possible to reduce the number of keystrokes even further.
There is always an alternative way to perform an action in git. In particular usage of
git add . is discouraged in preference to explicitly choosing which files to stage.