Codest’s good practice for building software: GitFlow

This document was written in order to unify the internal company GitFlow rules. This method is not introducing pure GitFlow, as it is a mix of both GitFlow and GitLab Flow, with best company practices worked over many years. It helps to keep a clean and readable history of the repository and better control over changes and project lifecycles. 

Initializing repository

After initializing the repository, create a develop and master branch if it wasn’t included by default. The develop branch should contain a development code that is a mirror of the master branch with new features included. The master contains a stable version of code, representing the production state. Both have infinite lifetimes, and in comparison to other branches in Git Flow, will never be removed. Set up proper protection rules: require to pull request reviews before merging and require status checks to pass before merging. You may also consider allowing only chosen team members to merge changes to the master.

GitFlow

$ git init
$ git commit --allow-empty -m "Initial commit"
$ git checkout -b develop master

NOTE: Sometimes it is important for the project to add more infinite branches, for example, to represent available environments. However, keep the “rule of two” if possible.

Feature branches

When starting to work with a given feature, first ensure you have your develop branch synced. 

$ git checkout develop && git pull --rebase 

Then, checkout to your feature branch. Name it respectively to the given schema: feature-JIRA-TASK-ID or you can also break that rule and name it differently. In this case, ensure that it does not conflict with patterns reserved for release, hotfix, bugfix, development or the master branch. Keeping JIRA task IDs will help you manage your feature branches more effectively.

$ git checkout -b feature-JIRA-TASK-ID develop

This branch should exist as long as the given feature is developed and then merged to the parent branch. To commit to your local branch, please follow this command:

$ git add .
$ git commit -m "JIRA-TASK-ID: Task description"

It is recommended that you add more commits to your local branch, following the “Commit early and often” rule. However, in the end, they must be squashed into a single commit representing a JIRA Task. Often committing will help you to manage your development history. When the feature is ready, it is time to open a Pull Request to develop a branch. First, you may push your local branch if it wasn’t pushed too far:

$ git push origin feature-JIRA-TASK-ID

When the branch is ready, open your pull request on GitHub, by following this article: https://help.github.com/en/articles/creating-a-pull-request

Before opening a pull request, please ensure the following:

– a proper description has been given – usually, you would link your JIRA task, but you may also include some useful information related to the current code

– CircleCI steps were passed successfully

– your team members were assigned – it is good practice to include all your team members as assignees

– the reviewers were selected – the number of reviewers depends on your team

– your code is actually ready for review – take a last look at your code, think again if there is anything left to refactor, test it locally and ensure that it is ready for further steps.

– there are no merge conflicts and a branch is up-to-date with develop – if there are any, resolve them first

$ git checkout develop && git pull --rebase
$ git checkout feature-JIRA-TASK-ID && git rebase develop # use -i flag for squash
$ git push -f origin feature-JIRA-TASK-ID #use this carefully as -f flag overwrites remote repository

– keep only important commits – each commit should be represented by JIRA task, for example, JIRA-TASK-ID: New feature configuration; others should be squashed while rebasing your branch with the develop branch

– you have the proper destination branch selected

– you don’t forget to change the status of your JIRA task

Merging feature branch

It is time to merge your changes to develop branch when:

– the pull request was approved by selected team members

– all tests finished successfully

– there are no merge conflicts and the commit history looks fine

This can be done by either the project manager or feature developer. In order to perform the merge, follow these steps:

$ git checkout develop
$ git merge feature-JIRA-TASK-ID
$ git push origin develop
$ git branch -d feature-JIRA-TASK-ID
$ git push origin :feature-JIRA-TASK-ID

Now, JIRA task status can be changed.

Releases

Release branches should be created by a person responsible for the current release. Usually, releases are created periodically, for example, according to the sprint lifecycle.

Semantic versioning

Giving a release branch a proper name and corresponding tag is not an easy task at the very beginning. It is good practice to start using semantic versioning (https://semver.org/) which allows better control and makes reading the git history easier. The version string is constructed according to MAJOR.MINOR.PATCH schema:

– MAJOR – change representing incompatible API changes

– MINOR – adding new features in a backwards-compatible manner

– PATCH – adding backwards-compatible bug fixes

You may also use special suffixes, such as those representing beta or legacy branches, or create pre-releases. In that case, name it properly, e.g. 1.1.0-beta.4, 1.1.0-beta.5 or 1.1.0-alpha.

Making a release

The release branch should be a child of develop and could contain only commits connected with bugfixes.

The branch name should be based on the release version, with the release- prefix: release-MAJOR.MINOR.PATCH. The release branch should be fully tested both automatically and manually on the staging environment. If bugs occur, this is the last opportunity to push proper fixes and re-run the whole process, as long as it won’t be positively checked and ready for further processing. Then, the release commit should be pushed, with changes of the current release version string in files, such as package.json. It should also have an impact on the CHANGELOG.md file. This will help you track all changes before a proper release and prepare the content for GitHub release when the merge process is completed. The single file update should consist of all release changes grouped into three categories: features, fixes and maintenance.

In the first step however, make sure you have both of your develop and master branches up-to-date.

$ git checkout master && git pull --rebase
$ git checkout develop && git pull --rebase
$ git merge master
$ git checkout -b release-M.M.P
$ git add .
$ git commit -m "Product Name M.M.P release"
$ git push origin release-M.M.P

At this point, one may decide to create another pull request to the master with the release branch. It may be a good idea to run an additional check if all tests pass well on the remote machine.

$ git checkout master
$ git merge release-M.M.P
$ git tag -a M.M.P # addition message may be set via -m flag 
$ git push origin M.M.P
$ git push origin master
$ git branch -d release-M.M.P
$ git push origin :release-M.M.P

Then go to the GitHub releases page and press the “Draft new release” button. In the displayed form, select the current version tag, set the release title similar to the release commit (Product Name M.M.P release) and a proper description, based on the CHANGELOG.md file. For public projects, the description should contain a list of all PR included in the current release.

In case some bugfixes were applied on the release branch, ensure you have your develop branch updated:

$ git checkout develop && git merge master
$ git push origin develop

Hotfixes and Bugfixes

The main difference between a bug and hot fixes is their target branches.

Bugfix

Bug fixes should patch release branches as the only form of updating code before merging it to master. First, create the branch from the current feature branch. Make sure you have it up-to-date locally.

$ git checkout release-M.M.P && git pull --rebase
$ git checkout -b bugfix-M.M.P

Then commit the necessary changes. After the process is finished, create a pull request and target it to the release branch. Follow guidance from the feature branch section. Your final commit title should match the given schema: “Bugfix M.M.P: Problem essence fix”. When the pull request is approved, it is time to merge it to the current release. 

$ git checkout release-M.M.P
$ git merge bugfix-M.M.P
$ git push origin release-M.M.P

Hotfix

In order to perform a hotfix on the master branch, very similar steps to a bugfix must be taken, keeping in mind that the target branch is now the master branch.

$ git checkout master && git pull --rebase
$ git checkout -b hotfix-X.Y.(Z+1) # M.M.P represents current release

Then, follow the usual development steps and when the process is finished, create a pull request with the master branch as a target. Keep in mind that the final commit should match the given schema “Hotfix X.Y.(Z + 1): Problem essence fix”. When every checkpoint has passed successfully, perform a merge to master. These steps differ from the one presented for a bugfix, as we need to bump the current release version.

$ git checkout master && git pull --rebase
$ git merge hotfix-X.Y.(Z+1)
$ git tag -a X.Y.(Z+1) # addition message may be set via -m flag 
$ git push origin X.Y.(Z+1)
$ git push origin master
$ git branch -d hotfix-X.Y.(Z+1)
$ git push origin :hotfix-X.Y.(Z+1)
$ git checkout develop && git merge master
$ git push origin develop

Cheat sheet

Init repository

$ git init
$ git commit --allow-empty -m "Initial commit"
$ git checkout -b develop master
$ git push origin develop
$ git push origin master

Features

$ git checkout develop && git pull --rebase  
$ git checkout -b feature-JIRA-TASK-ID develop

# Start your development and commits
$ git add .
$ git commit -m "IRA-TASK-ID: Task description" #initial commit
$ git push origin feature-JIRA-TASK-ID
# Open pull request

# Rebase and prepare for pull request
$ git checkout develop && git pull --rebase
$ git checkout feature-JIRA-TASK-ID && git rebase develop # use -i flag for squash
$ git push -f origin feature-JIRA-TASK-ID #use this carefully as -f flag overwrites remote repository

# Merge changes and delete branch
$ git checkout develop # branch should be synced with develop branch at this stage
$ git merge feature-JIRA-TASK-ID
$ git push origin develop
$ git branch -d feature-JIRA-TASK-ID
$ git push origin :feature-JIRA-TASK-ID

Releases

$ git checkout master && git pull --rebase
$ git checkout develop && git pull --rebase
$ git merge master
$ git checkout -b release-M.M.P

# Create release commit
$ git add .
$ git commit -m "Product Name M.M.P release" #initial commit
$ git push origin release-M.M.P
# Open pull request

# Merge changes, release and delete branch
$ git checkout master # branch should be synced with master branch at this stage
$ git merge release-M.M.P
$ git tag -a M.M.P # addition message may be set via -m flag 
$ git push origin M.M.P
$ git push origin master
$ git branch -d release-M.M.P
$ git push origin :release-M.M.P

Bugfix

$ git checkout release-M.M.P && git pull --rebase
$ git checkout -b bugfix-M.M.P

$ Commit fixes
$ git add .
$ git commit -m "Bugfix M.M.P: Problem essence fix" #initial commit
$ git push origin bugfix-M.M.P
# Open pull request

# Merge changes and delete branch
$ git checkout release-M.M.P # branch should be synced with current release branch at this stage
$ git merge bugfix-M.M.P
$ git push origin release-M.M.P
$ git branch -d bugfix-M.M.P
$ git push origin :bugfix-M.M.P

Hotfix

$ git checkout master && git pull --rebase
$ git checkout -b hotfix-X.Y.(Z+1) # M.M.P represents current release

$ Commit fixes
$ git add .
$ git commit -m "Hotfix M.M.P: Problem essence fix" #initial commit
$ git push origin bugfix-M.M.P
# Open pull request

# Merge changes, release and delete branch
$ git checkout master # branch should be synced with current master at this stage
$ git merge hotfix-X.Y.(Z+1)
$ git tag -a X.Y.(Z+1) # addition message may be set via -m flag 
$ git push origin X.Y.(Z+1)
$ git push origin master
$ git branch -d hotfix-X.Y.(Z+1)
$ git push origin :hotfix-X.Y.(Z+1)
$ git checkout develop && git merge master
$ git push origin develop

Read more:

– Codest’s good practice for building software: CircleCI

– How to create Google Chrome Extensions using the Netflix subtitles styler?

– React: the most popular JavaScript framework

 

Did you like it? Share this article

Next

Let's start a project

Estimate project