Collaborative Development Model With GIT Using Pull Requests
03 Jan 2017 | Development
GIT is an open source distributed version control system that has been around for some time. Initially designed and developed by Linus Torvalds for Linux kernel development in 2005, GIT is widely used by IT teams nowadays.
Most of the teams I worked with as a Drupal Lead Developer or Web Architect use GIT the way we did 10 years ago with SVN (I’m not judging). When working the old way, with at least 2 main branches (Master for production and Dev for development purposes) and allowing all the developers to work directly in the dev branch to implement new features, you miss all the great features of GIT. More importantly, this is really problematic because you can never really push the code base to production without risking deploying an incomplete feature or breaking the build.
This post is an introduction to another way of contribution, the Pull Request model, which is frequently used in open source projects. It can also be applied to small teams to help them do code reviews and improve their code quality.
Working with pull request
When working with Pull Requests (PR), you have two solutions : the Fork model and the Shared Repository model. Both of them work fine, the first one is ideal for open source projects and the second one for a small team. In a shared repository model, team members are allowed to contribute to a single shared repository and to create branches for delivering new features. The process is equal to the fork mode. A PR needs to be created by a developer to push new code to the main development branch. Pull Requests are useful because they initiate code reviews and discussions about the code implementation in a dedicated branch that does not interfere with the development branch.
Nevertheless, there is an issue when using this approach. If you ask developers to push new features to dedicated branches, this means that at some point, someone will have to merge all these branches to the main branch. This role can become a bottleneck if he is not fast enough or waits too long for approving the awaiting code.
Also, when working with Pull Requests, you need to enforce a “no committing to master directly” rule, otherwise there’s no benefit if a team member can bypass the process.
The Shared Repository Model
Here’s a simple workflow that demonstrates how to work with Pull Requests. First, you need to clone the repository you want to work on using the GIT Clone command. For the rest of the article, I’m assuming you work on the master branch but you may need to clone the repo from another branch.
$ git clone firstname.lastname@example.org:my-organization/my-repo.git
Work on your story
Now you fetched the project locally and before doing anything else, you have to create a new branch from your development one in order to work with the most recent version of the code. As seen in the introduction, it’s a prerequisite that when working with Pull Requests, you keep your feature code isolated from other features. Trash or stash files that are not related to the story you are working on to avoid pushing code which would not be related to what you are developing.
Step 1. Create a new branch
(master) $ git checkout -b feature/my-awesome-feature
Step 2. Work on it, make changes, run tests and commit to your local repository.
(feature/my-awesome-feature) $ git add [files] (feature/my-awesome-feature) $ git commit
Note 1: When working with branches for developing stories, I’ve taken the habit of prefixing them “features/” or “PR/”. This is also very useful if you have a Continuous Integration tool to build environments based on GIT branches.
Note 2: When working in a team with GIT and a ticketing system, it’s a good habit to add the ticket number of the story you are working on in the commit message.
Step 3. Push the code to the server repository.
(feature/my-awesome-feature) $ git push -u origin feature/my-awesome-feature
The -u option adds an upstream tracking reference to your local branch, meaning that you will be able to push any following commits using GIT Push, and without having to specify again the remote and branch names.
Iterate this process until you have something working, testable and that fits the needs of your user story. It’s good practice to often commit small chunks of code instead of all the code at once. You can merge them later into the master branch whenever you want or squash them when rebasing. In the event of an issue, you will have the opportunity to revert the code to an older version.
Squashing your commits before pushing
Personally, I like to squash all my commits concerning only one User Story into a single commit before creating a Pull Request. Squashing helps to reduce the amount of commit messages and keeps a clean history. This is especially useful when you want to do code reviews, as you have a better idea of what has been implemented at the end, and you are not obliged to go from commit to commit. Furthermore, it ensures the consistency of the code, removing the risk of using an intermediary commit that would be incomplete or unstable.
So, after you have committed all your work to your local branch (Step 2) and before pushing to the server (Step 3), you have to rebase your work into a single commit.
Step 1. Rebasing all your commits into a single one.
(my-awesome-feature) $ git rebase -i master
GIT will compare the work done in the feature branch to the master. Of course, this only works if you haven’t merged the code into your master yet. After filling in the precedent command, your favorite Git editor will edit a file with the following content.
pick 409270d commit 1 pick 8ba5720 commit 2 pick c851ca2 commit 3
Each line represents a commit on the local branch displayed in chronological order, with the latest at the bottom. To transform all these commits into a single one, change the file to this:
pick 409270d commit 1 squash 8ba5720 commit 2 squash c851ca2 commit 3
This means, you pick the first commit and squash the following onto it. Save the file and your editor will open once more to edit a commit message for the squashed commit.
Step 2. Push the result to the server
(my-awesome-feature) $ git push --force
You will need to add the –force option to force GIT pushing the update to the source repository.
Note: do not squash commits that you’ve already shared with others or it will cause them a lot of trouble.
Submit your Pull Request
A Pull request is not a GIT command, it is a feature provided by GIT tools such as Github, Gitlab or Bitbucket that you do on their web interface.
Whatever tool you use that allows Pull Request contributions, the process is the same. Once you are satisfied with your code, you need to go to the web interface of your GIT repository manager to create the Pull Request. Before submitting it, I strongly encourage you to add a meaningful comment and review all the committed files.
GitLab call this a “merge request”, however it’s called a “pull request” for Github. Both mean the same thing ; pulling changes from a branch and merging them to another branch.
If you want to create a Pull Request, here is some documentation:
Reviewing Pull Request
Now that developers push their works in dedicated branches and create a Pull Request per feature, someone has to review and integrate them into the development branch. As said in the introduction, analyzing Pull Requests is a good opportunity for doing code review, ensuring the code is clean, follows coding standards and is shipped with tests.
Go to the web interface of your GIT tool and find the Pull Request administration page to see the list of contributions awaiting for validation and review them one by one, starting with the oldest one first.
Look at new and deleted files, review all changes and add comments directly through the website to help developers. If you are Ok with the code, you can approve it. Otherwise reassign it to the developer.
If the pull request cannot be merged online due to conflicts, you will need to perform the merge locally. Update your development branch and checkout the Pull Request branch.
(master) $ git fetch (master) $ git checkout feature/my-awesome-feature
Finally you can merge the code.
(master) $ git merge feature/my-awesome-feature
Once the code has been merged, you can simply close the pull request and (optionally) delete the feature branch.
(master) $ git branch -D feature/masquerading (master) $ git push origin :feature/masquerading
This contribution workflow is really not complicated to implement if you are already using GIT. However, the difficulties are on the human side. The team needs to understand and apply the process and the Lead developer has to do his code reviews otherwise he will stop the delivery process. If you succeed in using Pull Request contributions, the development team will increase the code base quality and the application will be better.