Skip to main content

Git Flow Branching Strategy

This document defines the branching strategy for NeuraFlow, following the Git Flow model by Vincent Driessen.

Reference: A successful Git branching model


Overview

Git Flow uses two permanent branches and three types of supporting branches to manage development, releases, and hotfixes.


Branch Structure

Permanent Branches

BranchPurposeProtection
mainProduction-ready code. Each commit represents a release.Protected. No direct commits.
developIntegration branch containing latest development changes.Protected. Only merge from feature/release/hotfix.

Supporting Branches

TypeBranch FromMerge ToNamingLifecycle
Featuredevelopdevelopfeature/*Temporary. Delete after merge.
Releasedevelopmain AND developrelease/*Temporary. Delete after merge.
Hotfixmainmain AND develophotfix/*Temporary. Delete after merge.

Core Concept: No Fast-Forward Merges

Always use --no-ff flag to preserve branch history and enable clean reverts.

Fast-Forward Merge (Default Behavior - Avoid)

Result: main → A → B → C → D (Linear - no branch information preserved)

Problem: Cannot identify which commits (C, D) belonged to the feature branch.

No Fast-Forward Merge (Use --no-ff - Preferred)

Result: main → A → B → M with feature branch B → C → D → M preserved

Advantage: Merge commit (M) preserves branch structure. Easy to identify feature commits (C, D).

Legend: 🔵 Main commits | 🔴 Feature commits | 🟢 Merge commit

Benefits

  • Preserves context - Branch history remains visible
  • Atomic reverts - Revert entire feature: git revert -m 1 <merge-commit>
  • Clear audit trail - Track what was merged and when
  • Groups changes - Related commits stay together

Workflows

Feature Development

# Create feature
git checkout develop
git pull origin develop
git checkout -b feature/my-feature

# Develop and commit
git add .
git commit -m "feat: add feature description"

# Merge to develop
git checkout develop
git merge --no-ff feature/my-feature
git branch -d feature/my-feature
git push origin develop

Keeping Feature Branch Up to Date

When develop receives critical changes (e.g., bug fixes, shared API changes, or dependency updates) that your feature branch depends on, rebase your feature branch onto the latest develop:

Before rebase — feature branch is behind develop:

Legend: 🔵 Develop commits | 🔴 Feature commits

After git rebase origin/develop — feature commits replayed on top of latest develop:

Legend: 🔵 Develop commits | 🟢 Rebased feature commits

Commits C', E', and H' are new commits with the same changes as C, E, and H, but replayed on top of G.

# Fetch latest changes
git fetch origin

# Rebase feature branch onto latest develop
git checkout feature/my-feature
git rebase origin/develop

# Resolve conflicts if any, then continue
git rebase --continue

# Force push if the branch was already pushed to remote
git push --force-with-lease origin feature/my-feature

Note: Use --force-with-lease instead of --force to safely push rebased branches. It will abort if the remote has changes you haven't seen, preventing accidental overwrites.

Warning: Do not rebase branches that multiple developers are actively working on. In that case, use git merge develop into the feature branch instead to avoid rewriting shared history.


Release Process

# Create release branch
git checkout develop
git checkout -b release/1.2.0

# Prepare release (bump version, fix bugs, update docs)
git commit -m "chore: bump version to 1.2.0"

# Merge to main
git checkout main
git merge --no-ff release/1.2.0
git tag -a v1.2.0 -m "Release version 1.2.0"
git push origin main --tags

# Merge to develop
git checkout develop
git merge --no-ff release/1.2.0
git push origin develop

# Clean up
git branch -d release/1.2.0

⚠️ Critical: Merge to Both Branches Individually

Incorrect Approach:

# ❌ WRONG - Do not merge main into develop
git checkout main
git merge --no-ff release/1.2.0

git checkout develop
git merge --no-ff main # Never do this

Correct Approach:

# ✅ CORRECT - Merge release branch to both targets
git checkout main
git merge --no-ff release/1.2.0

git checkout develop
git merge --no-ff release/1.2.0 # Merge release, not main

Rationale:

  1. develop may contain new features committed during release preparation
  2. Merging release to both branches applies only release changes
  3. Merging main → develop could introduce unintended changes
  4. Clearer conflict resolution with specific release changes
  5. Explicit intent improves history readability

Hotfix Process

# Create hotfix
git checkout main
git checkout -b hotfix/1.2.1

# Fix the issue
git commit -m "fix: critical bug description"

# Merge to main
git checkout main
git merge --no-ff hotfix/1.2.1
git tag -a v1.2.1 -m "Hotfix version 1.2.1"
git push origin main --tags

# Merge to develop
git checkout develop
git merge --no-ff hotfix/1.2.1
git push origin develop

# Clean up
git branch -d hotfix/1.2.1

Best Practices

  1. Always use --no-ff - Preserve branch history for all merges
  2. Protect main branches - Require pull requests for main and develop
  3. Tag all releases - Use semantic versioning (e.g., v1.2.3)
  4. Short-lived branches - Merge features frequently to avoid conflicts
  5. Never commit directly - Use branches for all changes
  6. Test before merging - Validate on release branches before production

Commit Message Convention

Follow Conventional Commits specification:

TypeDescriptionExample
featNew featurefeat: add user authentication
fixBug fixfix: resolve login timeout
docsDocumentationdocs: update API guide
styleCode formattingstyle: format with prettier
refactorCode restructuringrefactor: extract validation logic
testTeststest: add unit tests for auth
choreMaintenancechore: update dependencies

Releasing New Versions

Client (Frontend)

Update version in package.json and merge to main:

cd client
npm version patch # For bug fixes (2.2.0 → 2.2.1)
npm version minor # For new features (2.2.0 → 2.3.0)
npm version major # For breaking changes (2.2.0 → 3.0.0)

git add package.json
git commit -m "chore: bump client version to v2.3.0"
git push origin develop

# Create PR to main, merge when ready
# GitHub Actions automatically tags image as :v2.3.0 and :latest

Server (Backend)

Create git tag and merge to main:

git tag -a v1.5.0 -m "Release version 1.5.0"
git push origin v1.5.0

# Create PR to main, merge when ready
# GitHub Actions automatically tags image as :v1.5.0 and :latest

Docker Image Tags

BranchTags GeneratedEnvironment
develop:developStaging/Testing
main:v1.5.0, :latestProduction

Deployment Examples:

# Staging (auto-updates)
docker pull ghcr.io/brain-station-23/neuraflow/server:develop

# Production (specific version)
docker pull ghcr.io/brain-station-23/neuraflow/server:v1.5.0

# Production (latest)
docker pull ghcr.io/brain-station-23/neuraflow/server:latest

When to Use Git Flow

Best suited for:

  • Projects with scheduled releases
  • Software requiring multiple production versions
  • Teams coordinating parallel feature development

Consider alternatives for:

  • Continuous deployment (use GitHub Flow)
  • Small teams with simple workflows
  • Projects requiring rapid iteration