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