Git branching strategies are more than just ways to organize code; they’re the foundational blueprints for how your Continuous Integration and Continuous Deployment (CI/CD) pipelines will function, dictating speed, stability, and collaboration.

Imagine a simple feature being developed. In a basic Gitflow-like structure, a developer might create a feature/add-user-profile branch off develop.

# Start on the main development branch
git checkout develop

# Create a new branch for the feature
git checkout -b feature/add-user-profile

They commit their work here.

# Make changes...
git add .
git commit -m "feat: Implement user profile basic structure"

When the feature is ready for integration, it’s merged back into develop.

# Switch back to develop
git checkout develop

# Merge the feature branch
git merge --no-ff feature/add-user-profile

This merge event, especially with --no-ff (no fast-forward), creates a distinct commit on develop, clearly demarcating the integration point for the feature. This is often the trigger for your CI pipeline. The CI server watches the develop branch. Upon detecting a new commit (like the merge commit), it pulls the latest code, runs linters, compiles, and executes unit tests. If all tests pass, the CI server might automatically deploy this develop branch to a staging environment.

Now, consider a release. A release/v1.0.0 branch is cut from develop. This branch is for final testing and bug fixes specifically for this release.

# Create a release branch from develop
git checkout develop
git checkout -b release/v1.0.0

Any critical bugs found here are fixed directly on the release branch and then merged back into both develop and main (or master).

# Fix a bug on the release branch
git checkout release/v1.0.0
# Make fixes...
git commit -m "fix: Correct login bug in release candidate"

# Merge the fix back to develop
git checkout develop
git merge --no-ff release/v1.0.0

# Merge the fix to main for the actual release
git checkout main
git merge --no-ff release/v1.0.0

This dual merge ensures that bug fixes are present in ongoing development (develop) and in the deployed artifact (main). The merge into main is often the trigger for your CD pipeline, deploying the stable v1.0.0 to production.

Hotfixes follow a similar pattern but originate from main. A hotfix/critical-auth-bug branch is created from main.

# Identify a critical bug in production
git checkout main

# Create a hotfix branch
git checkout -b hotfix/critical-auth-bug

The fix is applied, committed, and then merged back into both main and develop.

# Apply the hotfix
git checkout hotfix/critical-auth-bug
# Make fixes...
git commit -m "fix: Address critical authentication vulnerability"

# Merge hotfix into main
git checkout main
git merge --no-ff hotfix/critical-auth-bug

# Merge hotfix into develop
git checkout develop
git merge --no-ff hotfix/critical-auth-bug

This ensures the critical fix is immediately in production and also incorporated into future development.

The most surprising truth about these workflows is how little they force automation. A robust Git workflow doesn’t inherently do CI/CD; it enables it by providing predictable integration points. A CI/CD pipeline is an observer and actor on these branches. It’s the Git structure that makes it possible to say, "When develop changes, build and test." Or, "When main changes, deploy to production."

The core idea is that each main branch (develop, main) represents a distinct state: develop is the bleeding edge of integration, main is the stable, production-ready code. Feature branches isolate experimentation, release branches stabilize for deployment, and hotfix branches address immediate production issues. Your CI/CD pipeline then hooks into the merge events into these specific branches. For example, a GitHub Actions workflow might have a on: push: branches: [ develop ] trigger, or a GitLab CI job with only: [develop].

The specific configuration for triggering pipelines depends heavily on your CI/CD tool. For example, in Jenkins, you might configure a "Poll SCM" trigger on the develop branch, or, more efficiently, use a webhook from your Git provider (GitHub, GitLab, Bitbucket) to notify Jenkins of a push event. The webhook payload would contain information about the branch, and Jenkins would then execute the appropriate build job.

git log --graph --oneline --decorate --all

This command visualizes the entire branch history, showing where merges occurred and how branches diverged and reconverged. It’s indispensable for understanding the state of your repository and verifying that your merges, especially those intended to be non-fast-forward (--no-ff), have created the expected history for your CI/CD triggers.

The next logical step after mastering these branching strategies is understanding how to manage merge conflicts gracefully within a CI/CD context.

Want structured learning?

Take the full Git course →