One Git flow that I use and love

One Git flow that I use and love

Whenever you run a project which based on versions and features

Do you ever face any issues with the Git flow in your projects? I faced always (and still to be honest), and I was struggling a lot with it for a few projects, and a canonical Git flow (which I appreciate a lot) is sometimes not clear enough to just pick it up and apply.

One day, during our work on a mobile app, we formulated a workflow that was an extension of the traditional one (based on it 90%) but became simple enough to adopt right away. While we used it specifically for our app development, this workflow can be easily adapted for any project that is based on agile development (however, I believe not only).

Three Main Components of the Workflow

Our Git workflow consists of three main parts:

  1. Production: This is the sustainable part of the application, represented by the master branch.

  2. In Release: This comprises several branches, each associated with features or bug fixes targeted for the next release. These branches are eventually merged into a 'release branch', which is a comprehensive version of the upcoming release, ready for testing, and then, shipping.

  3. Hotfix Branches: These branches are a bit unique as they fall outside the main process but follow a strict and specific workflow of their own.

Types of Branches

Here's a more detailed look at the different types of branches within the workflow:

  1. Master Branch: This is the branch that's life in production. It represents the most recent, stable version of the application. The main rules to use the Master branch:

    1. Any other branch always begins its journey from Master.

    2. Only Hotfix and release branches could be merged into Master.

  2. Feature Branch: Each feature branch is created from the Master branch. Developers work on this branch to build new features from the sprint. To maintain clarity, we try to establish a one-to-one mapping between Feature branches and User Stories. The main rules:

    1. All Feature branches start their journey from the Master.

    2. All defects that occur while testing the release branch should be committed to the Feature branch.

  3. Bug-fix Branch: These branches function similarly to Feature branches, created for addressing non-urgent bugs. They are typically part of the sprint, just like User Stories. All the rules are the same as with Feature branches.

  4. Release Branch: This is where things get interesting. All tested and production-ready Feature or Bug-fix branches are merged into the Release branch. This branch undergoes a final round of testing before the release. If any fixes are required, they are made in their respective feature branches and then committed back. The key rule here is to avoid direct commits to the Release branch. If a feature needs to be excluded from the release, we can simply reassemble the Release branch without that feature. Rules to keep:

    1. A new Release branch is always made from the Master.

    2. Never commit anything to the release branch - it should be a matter of minutes to destroy the Release branch and assemble a new one.

    3. Release branches are merged into Master when the actual release is performed.

  5. Hotfix Branch: Hotfix branches are created directly from the Master branch and merged back once the fix is implemented. These branches handle urgent fixes and are not typically part of the sprint. The rules once again:

    1. We create a Hotfix branch only from the Master

    2. We can merge it back directly when it is tested to not wait until the next release to add a patch.

One case study

A list of branches might not give a full picture of what’s going on, so let's brainstorm some cases and how our workflow is handle them:

Let's say we are working on Release 1.0.1 while Master is 1.0, we have a list of three features, and one bug. Then in the middle of the Sprint, we have the following picture:

  • master

  • feature/one

  • feature/two

  • feature/three

  • bug/one

We assured the quality of our features independently and start forming a Release branch for integration testing, then we have this picture in Git:

  • master

  • release/1.0.1

    • merged feature/one

    • merged feature/two

    • merged feature/three

    • merged bug/one

However, we faced something unexpected while testing Feature One, and seems like we can’t make it to the end of the Sprint. All we need here is to cut the feature from the release branch, so if we never commit anything directly we can just

  1. Delete our Release branch

  2. Create a fresh one from Master

  3. Merge everything except our problem-causing Feature One.

Then, at the end of the Sprint, we have something like:

  • master

  • feature/one - will end it up in the next Sprint.

  • release/1.0.1 - polished and ready for shipping.

    • merged feature/two

    • merged feature/three

    • merged bug/one

Not a perfect flow

None of Git flows is perfect, and neither is this one. Here are some drawbacks I can pop up before advice to use it in your project tomorrow:

  • Non-linear history - as with any other merge-based Git flow the history here will be a mess. I usually mitigate it via tags in Master if I need to have the ability to travel back in time. We needed it for the mobile app

  • Flow with hotfixes is still a mess - whenever you do a hotfix, don't forget to merge the new, fixed master everywhere. If we have many ongoing features or bugs, we will face endless merges if even one is not done, resulting in conflicts.

Let me know what you think! And share your favorite best practices of using Git, no matter if the concept is so simple I still constantly learn and fail.