Git branching strategies
Git branching strategies are structured approaches to managing branches in version control systems. They define how teams create, merge, and maintain branches throughout the software development lifecycle. A well-defined branching strategy helps teams collaborate effectively, maintain code quality, and streamline the release process.
Why Git branching strategies are important
Implementing a Git branching strategy provides several key benefits:
- Improved collaboration: Clear guidelines help team members understand how to contribute without conflicts
- Reduced merge conflicts: Structured branching patterns minimise overlapping changes
- Simplified rollbacks: Isolated changes make it easier to revert problematic updates
- Clear development workflow: Team members know exactly where to commit their changes
The right branching strategy depends on your team size, release frequency, and deployment practices. Below are four popular approaches, each with their own strengths and use cases.
Trunk-based development
Description
Trunk-based development is a branching strategy where developers merge small, frequent updates to a trunk (main) branch rather than working on long-lived feature branches. Developers integrate their changes into a shared trunk, potentially multiple times a day. This shared trunk should always be in a deployable state.
The goal of this strategy is to limit long-lived branches and avoid merge conflicts as all developers work on the same branch. This strategy is often combined with feature flags which can help decouple deployment from release so that changes which are not ready can be wrapped in a feature flag and turned off until they’re ready.
How it’s implemented
Trunk-based development uses:
- trunk (main): The single source of truth, always in a releasable state
- short-lived feature branches (optional): Very short-lived branches (typically less than a day) for work in progress
The typical workflow:
- Pull the latest changes from trunk (typically main)
- Create a very short-lived branch or commit directly to trunk (for small changes)
- Make small, incremental commits
- Run automated tests locally
- Push changes directly to trunk (if trusted), or create a pull request from your feature branch into trunk
- Merge to trunk after automated checks pass
- Use feature flags to hide incomplete features (optional)
- Deploy trunk frequently (multiple times per day)
To implement this strategy, there should be a suite of fast automated tests that run after each commit to make sure the system is working. This implementation of CI helps to eliminate long integration and stabilisation phases by integrating small batches at a time.
Benefits and limitations
| Benefits | Limitations |
|---|---|
| Encourages small, frequent commits. A key principle in Agile development | Requires robust automated testing |
| Reduces merge conflicts significantly | Demands high discipline from developers |
| Faster feedback and integration | Incomplete features need feature flags |
| Simplifies the development workflow | Can be challenging for large distributed teams |
| A true enabler for CI/CD | Requires mature CI/CD infrastructure |
Example flow diagram
gitGraph
commit
commit
branch short-lived-branch-1
checkout short-lived-branch-1
commit
commit
checkout main
merge short-lived-branch-1
commit
branch short-lived-branch-2
checkout short-lived-branch-2
commit
checkout main
merge short-lived-branch-2
commit
GitHub-flow
Description
GitHub-flow is a simplified branching strategy developed by GitHub. It’s designed for teams that deploy frequently and practice continuous delivery.
It includes a singular main branch where all release code lives. Temporary feature branches are created from the main branch (e.g. feature/update-dockerfile) and this is where new development takes place. When changes are ready for review, a pull request (PR) is opened to review and discuss the code. As part of this PR, automated tests / checks can be run and the PR decorated with the results. This feature branch can then be deployed directly to production and merged into the main branch (which the team at GitHub do) or merged into main and then main is deployed to production.
Pull requests are a key feature of this strategy and allow developers to add comments and approve or request changes. Whether the PR is approved or not, the record will still exist for future reference. More information on PRs can be found at About pull requests. The idea behind this strategy is that the main branch is in a constantly deployable state and therefore can support CI / CD processes.
How it’s implemented
GitHub-flow uses:
- main: The single long-lived branch, always deployable
- feature branches: Short-lived branches for all changes (features, fixes, experiments)
The typical workflow:
- Create a branch from main with a descriptive name
- Make commits to the branch
- Open a pull request to start discussion
- Continue making commits based on feedback
- Deploy the feature branch to a pre-production environment for testing (optional)
- Once ready, merge the pull request into main
- Deploy main to production
- Delete the feature branch
Benefits and limitations
| Benefits | Limitations |
|---|---|
| Simple and easy to understand | No separate release preparation phase |
| Encourages continuous deployment | Requires excellent automated testing |
| Fast feedback through pull requests | Main branch must always be deployable |
| Minimal overhead and branch management | No structured way to maintain multiple versions |
| Works well with CI/CD pipelines | Less suitable for scheduled releases |
Flow diagram
gitGraph
commit
branch feature/a
checkout main
commit
branch feature/b
commit
checkout feature/a
commit
checkout main
merge feature/a tag: "PR #1 merged"
commit
checkout feature/b
commit
checkout main
merge feature/b tag: "PR #2 merged"
commit
Git-flow
Description
Git-flow is complex / robust branching strategy which relies on long-lived branches for simultaneously developing new features while supporting current releases. This is considered to be slightly complicated and advanced for much of today’s projects. There are two primary branches, main and develop. The main branch will always reflect the current production-ready state of the software while the develop branch reflects the latest development for the next release. There are also supporting branches such as feature branches which work like they do in GitHub-flow, and release branches which helps prepare the work on develop that will be included in the next release version.
This strategy is well-suited for projects with scheduled release cycles and the need to maintain multiple versions in production.
How it’s implemented
Git-flow uses the following branch types:
- main (or master): Contains production-ready code. Every commit represents a release.
- develop: Integration branch for features. Contains the latest delivered development changes.
- feature branches: Created from develop for new features. Merged back into develop when complete.
- release branches: Created from develop when preparing a new release. Allows for bug fixes and metadata updates. Merged into both main and develop.
- hotfix branches: Created from main for urgent production fixes. Merged into both main and develop.
The typical workflow:
- Create a feature branch from develop
- Develop and test the feature. Testing can happen as part of a pull request targeting develop
- Merge feature branch back into develop
- Create a release branch from develop when ready
- Test and fix bugs in the release branch. Future features can continue to be merged into the develop branch
- Merge release branch into main and tag the release
- Merge release branch back into develop
- For urgent fixes, create hotfix branch from main, fix, and merge into both main and develop
Benefits and limitations
| Benefits | Limitations |
|---|---|
| Clear separation between development and production code | Complex workflow with many branch types |
| Supports parallel development of multiple features | Steep learning curve for new team members |
| Well-suited for scheduled releases | Overhead of maintaining multiple long-lived branches |
| Easy to maintain multiple production versions | Can lead to delayed integration of features |
| Structured approach for hotfixes | May be overkill for simple projects or continuous deployment |
Flow diagram
gitGraph
commit
branch develop
checkout develop
commit
branch feature/a
checkout feature/a
commit
commit
checkout develop
merge feature/a
commit
branch release/1.0
checkout release/1.0
commit
commit
checkout main
merge release/1.0 tag: "v1.0"
checkout develop
merge release/1.0
checkout main
branch hotfix
checkout hotfix
commit
checkout main
merge hotfix
checkout develop
merge hotfix
GitLab-flow
Description
GitLab-flow is a branching strategy that combines elements of GitHub-flow with additional branches for different environments. It provides a middle ground between the simplicity of GitHub-flow and the structure of Git-flow, making it suitable for teams that need environment-specific branches but want to avoid the complexity of Git-flow.
How it’s implemented
GitLab-flow consists of a main branch where all code that is going to be deployed into production exists, and various pre-production branches. It can be implemented in two ways:
Environment-based approach:
- main: Contains production-ready code
- pre-production: Staging/testing environment
- production: Production environment branch (receives merges from main)
Release-based approach:
- main: Development branch
- release branches: Stable release branches (e.g., 2-3-stable, 2-4-stable)
These approaches are discussed in more detail at Introduction to GitLab-flow.
The typical workflow:
- Create a feature branch from main
- Develop and test the feature
- Create a merge request to main
- After review and CI passes, merge to main
- Changes automatically flow to pre-production for testing
- Once validated, changes are merged/deployed to production
- Hotfixes can be applied to release branches and cherry-picked to main
Benefits and limitations
| Benefits | Limitations |
|---|---|
| Clear path from development to production | More complex than GitHub-flow |
| Supports multiple environments naturally | Requires discipline to maintain environment branches |
| Balances simplicity with structure | Can lead to divergence between environment branches |
| Works well with continuous delivery | May require additional tooling for automation |
| Easier to understand than Git-flow | Not ideal for very frequent deployments |
Flow diagram
gitGraph
commit
branch feature/a
checkout main
commit
branch feature/b
commit
checkout feature/a
commit
checkout main
merge feature/a tag: "feature/a merged into main"
commit
checkout feature/b
commit
checkout main
merge feature/b tag: "feature/b merged into main"
commit
branch pre-production
checkout pre-production
commit tag: "main merged into pre-production"
branch production
commit
checkout production
merge pre-production tag: "pre-production merged into production"
Choosing the right strategy
When selecting a branching strategy for your project, consider:
- Team size and distribution: Larger, distributed teams may benefit from more structure
- Release frequency: Frequent releases favor simpler strategies like GitHub-flow or trunk-based development
- Deployment process: Manual deployments may require more structured approaches like Git-flow
- Team experience: Less experienced teams may need more explicit guidelines
- Project complexity: Complex projects with multiple versions may need Git-flow or GitLab-flow
- Testing maturity: Simpler strategies require robust automated testing
Remember, these strategies are guidelines, not strict rules. Many teams adapt and combine elements from different strategies to suit their specific needs and workflows. A good starting point is to choose one of the industry standards above and then modify as necessary.
Common anti-patterns
When devising your Git branching strategy, be aware of the following anti-patterns and avoid them.
Long-lived feature branches
The longer a feature branch exists, the more likely it is to:
- Contain more code and therefore more conflict opportunities
- Become out of sync with main, leading to merge conflicts
- Produce larger, harder-to-review PRs
Feature branches should be short-lived (ideally only a few days). They should be deleted once merged into main. This is in keeping with Agile principles of short, frequent commits.
Managing too many release branches
Every release branch is effectively a codebase you have to maintain. Maintaining numerous long-lived release branches (e.g., supporting 10+ versions simultaneously) creates significant overhead and complexity. Each release branch requires:
- Backporting bug fixes and security patches to multiple branches
- Separate CI/CD pipelines and testing for each version
- Increased cognitive load when deciding where to apply fixes
- Higher risk of inconsistencies between versions
- More complex merge conflicts when backporting changes
To avoid this, keep the number of release branches as low as possible.
Using a complicated branching strategy
A branching strategy should be as simple as possible while meeting your team’s needs. Avoid creating unnecessary branches or rules that add overhead without value. If team members regularly ask “which branch should I use?”, your strategy may be too complex.