Preface
We, at Promact, use the Git workflow that best suits our projects, and we attempt to explain it in detail here. You should understand the different types of Git workflows that the industry uses before deciding on the best one.
Branch management
We use 3 main branches to manage source code:
- Dev: Deployed to the development environment
- Staging: Deployed to the Staging/QA environment
- Master: Deployed to the production environment
Feature branches
All features/bugs are developed in feature/issue branches which are based on the development branch. When a developer starts working on the feature ‘User registration’, the person creates a new branch called feature-user-reg from the dev branch and initiates the work there. Meanwhile, other developers can also start working on the ‘Dashboard’ feature using the feature-dash branch.
After completing the feature implementation, the developer creates a pull request (i.e., source: feature-dash & target: dev) in whichever Git provider the team uses, for example, Github, Team Foundation Server, Bitbucket, Gitlab, etc.
The team members can review the pull request for suggestions/enhancements and later an authorized person can merge that pull request to the development branch.
Integrating upstream changes for conflict resolution
Before sending the pull request in some situations, the developer needs to get the latest changes from the upstream branch (i.e., dev in current scenario) otherwise while sending the pull request it may show an error for ‘Conflicting Changes’.
For instance, if you are a developer, your pull request could show such an error because there may be conflicting changes in the upstream branch after you created your branch i.e., developer of feature-dash and feature-user-reg both may have changed the same index.html file, and feature-dash branch might have merged with the dev branch before, so there will obviously be conflicts while trying to merge the feature-user-reg branch to dev.
In such a case, you need to align the branch with the latest changes from the upstream branch before sending the pull request.
There are two ways to do this:
Merge
To fetch the latest changes from the dev branch, you can merge the dev branch to your feature branch by using git merge dev command. Before running this command, your current branch must be your feature branch. If it is not the case, use the git checkout feature-x command first. Git Merge performs a three-way merge between two branches (dev and feature-user-reg).
Rebase
Another way to fetch the latest changes from the development branch is to do a rebase operation using git rebase dev command. It takes all the commits from your feature branch and replays them one by one onto the development branch, so it provides a cleaner history compared to the merge commit in the above case.
Note that all the commits of the feature branch c3, c4 and c5 are replayed over dev branches commits. Here, rebase operation takes changes for each commit and replays them over the head of the branch on which you are rebasing. So, the commit content will be the same, but Git will change the commit ids
Force push
If you have already pushed your feature branch to the remote repository, you may have to force push a rebased branch. The reason behind this is that Git Rebase changes commit history and assigns new commit ids. So while pushing your changes to remote, Git thinks that there are two different sets of commits (incoming and outgoing) even if they are the same changes. In such a case, you need to force push your rebased branch after rebasing.
In the above image c3, c4 ,and c5 are already pushed to the remote branch, now after rebasing in local, Git generates new commit SHA ids for same commits c3′, c4′, and c5′, so while pushing this branch, Git thinks that remote and local branches changes are different and it tries to merge them together causing two sets of same commits, which is confusing. So, in this case, we need to force push our local rebased branch so that remote c3, c4 and c5 commits are overwritten by c3′, c4′ and c5′.
In most cases, force pushing is necessary after rebasing our local feature branch to the dev branch because of the above reasons. You need to make sure to use force push if using command line pull and push (git pull is a combination of git fetch and git merge command). So if you try to pull your changes before pushing your feature branch, it will automatically merge remote commits that are different from local rebased commits (remember c3′, c4′ and c5′), which is not proper. The same thing is applicable while using Visual studio sync operation, which first pulls and then pushes the changes.
Merging to staging and master
Now that feature branch is merged with dev using the pull request feature provided by your source control (TFS, Github, etc.), you can test your feature in the development environment. If it is good to go, then you can create another pull request from development to staging branch.
After staging branch pull request is accepted, the QA team can test feature in the staging environment and provide their confirmation. If everything is proper, the same process can be done for merging staging to the master branch.
Hotfix Branches
Till now, we have discussed the regular day-to-day git branching and merging process based on feature branches. But life’s always not so easy as at times you could detect a critical issue on the production environment that you need to fix quickly.
For this, you can create a new branch from dev (i.e., fix-branch) and do required changes there, and send a pull request from fix-branch -> dev -> staging -> master. But doing so is not feasible because of two reasons.
One is that there may be a code of new functionalities being developed in the dev branch which you don’t want yet in production. The second reason is that this process requires a lot of time to go through pull requests in every branch.
So, in a scenario like this, you need to apply the concept of the hot-fix branch. Hot-fix branches are directly being checked out from the master branch and merged to the master branch directly. Remember that hotfix branches are also required to be merged to development branch as well as staging branch, otherwise you will get merge conflicts when you try to merge your staging branch to master.