GitFeature Branch Workflow

Feature Branch Workflow

The feature branch workflow is the most widely-used Git collaboration model in professional teams. Its core idea is simple: every piece of work — a feature, a bug fix, a refactor, an experiment — gets its own dedicated branch. No one commits directly to main. Code lands on main only after it has been reviewed, tested, and deliberately merged. This single constraint produces enormous improvements in code quality, team coordination, and release confidence.

Core idea

Feature branches isolate work in progress

Text
main:         A ── B ─────────────── M1 ─────────── M2
                              │              ↑              ↑
                              │          (merge PR#1)  (merge PR#2)
feature/auth: └── C ── D ── E             |
                                           (reviewed, CI passed)

feature/search:         └── F ── G ── H ──────────────────┘

main is always in a releasable state.
Work-in-progress lives safely on its own branch.
Branch naming conventions

Consistent branch names make it easy to understand the purpose of a branch at a glance, filter branches, and automate CI/CD pipelines.

Pattern

Example

Used for

feature/<description>

feature/user-authentication

New features

fix/<description>

fix/login-redirect-bug

Bug fixes

hotfix/<description>

hotfix/payment-null-crash

Critical production fixes

chore/<description>

chore/update-dependencies

Maintenance, upgrades

refactor/<description>

refactor/database-layer

Code restructuring, no new features

docs/<description>

docs/api-endpoints

Documentation updates

test/<description>

test/add-auth-coverage

Adding or improving tests

release/<version>

release/2.1.0

Release preparation (Git Flow style)

Use lowercase and hyphens in branch names
Branch names should be lowercase, use hyphens (not underscores or spaces), and be descriptive but not excessively long. Ticket/issue numbers are useful: `fix/PROJ-123-login-crash`.
Full lifecycle: from idea to merged code
  1. Update main to the latest state.

  2. Create a new branch from main for the feature.

  3. Write code, commit in small logical increments.

  4. Push the branch to the remote regularly.

  5. When ready, open a Pull Request from your branch to main.

  6. Team reviews the PR — leave comments, request changes, or approve.

  7. CI/CD runs automated tests on the branch.

  8. Once approved and tests pass, merge the PR into main.

  9. Delete the feature branch (local and remote).

  10. Pull the updated main locally.

Real command walkthrough — feature/user-search

Bash
# 1. Start from a fresh main
git switch main
git pull origin main
# Already up to date.

# 2. Create the feature branch
git switch -c feature/user-search
# Switched to a new branch 'feature/user-search'

# 3. Write code and commit incrementally
# (first commit)
git add src/components/SearchBar.tsx
git commit -m "feat: add SearchBar component skeleton"

# (second commit)
git add src/hooks/useSearch.ts
git commit -m "feat: implement useSearch hook with debouncing"

# (third commit)
git add src/pages/SearchResults.tsx tests/search.test.ts
git commit -m "feat: add SearchResults page with unit tests"

# 4. Push the branch to remote
git push -u origin feature/user-search
# Branch 'feature/user-search' set up to track remote branch.

# 5. Open a PR on GitHub/GitLab
# (done in the web UI — or via CLI)
gh pr create --title "Add user search feature"   --body "Implements full-text user search with debouncing and pagination."

# 6. (After review and approval)

# 7. Merge the PR (via web UI, or:)
git switch main
git merge --no-ff feature/user-search
git push origin main

# 8. Delete the branch
git branch -d feature/user-search
git push origin --delete feature/user-search

# 9. Update local main
git pull origin main
Benefits over centralized workflow

Concern

Centralized

Feature Branch

Code review

None — code goes directly to main

Mandatory — PRs require approval before merge

Main branch stability

Fragile — any commit can break it

Protected — only reviewed, passing code merges

Parallel work

Risky — easy to interfere

Safe — each feature is isolated

Incomplete features

Visible in main immediately

Hidden on their own branch until ready

Deployment

Any commit might go to prod

Only merged, reviewed code goes to prod

CI/CD

Runs on main after commit

Runs on feature branch before merge — catches issues early

Blame and bisect

Fine

Better — logical feature-per-merge grouping

Keeping feature branches up to date with main

If your feature branch lives for more than a day or two, main will have moved forward. You need to bring those changes into your branch to avoid a painful merge conflict at PR time.

Option 1: Merge main into your feature branch

Bash
# Bring main's latest commits into your feature branch via merge
git switch feature/user-search
git fetch origin
git merge origin/main

# This creates a "merge commit" — your history shows a fork and re-join
# Good for: seeing when you synced
# Bad for: noisy history with many merge commits

Option 2: Rebase your branch onto main (cleaner history)

Bash
git switch feature/user-search
git fetch origin
git rebase origin/main

# If conflicts occur during rebase:
# Fix the conflict in each file, then:
git add <resolved-file>
git rebase --continue

# Result: your commits are replayed on top of the latest main
# History looks like you started the branch today, not 2 weeks ago.

# After rebase, force-push is required (history was rewritten):
git push --force-with-lease origin feature/user-search
Force-push only on your own feature branches
Only force-push to branches where you are the sole developer. Force-pushing a branch that a colleague is also working on will overwrite their commits. `--force-with-lease` is safer than `--force` because it refuses to push if someone else has pushed to the branch since your last fetch.
Handling long-lived feature branches
  • Aim for feature branches shorter than 1–2 days. Long branches are harder to review and produce more conflicts.

  • Break large features into smaller sub-tasks, each on its own short-lived branch.

  • Use feature flags to merge incomplete code to main early, keeping the flag off in production until ready.

  • Schedule periodic rebases (or merges from main) so your branch does not drift too far.

  • Set a PR "work in progress" (WIP/Draft) status to signal that the branch is not ready for review yet.

Common errors

Error: forgot to branch from main

Bash
# You realize you have been committing directly to main
git log --oneline
# e5f6a7b (HEAD -> main, origin/main) my feature work  ← oops

# Fix: move those commits to a new branch, then reset main
git branch feature/my-work       # create branch pointing to current HEAD
git reset --hard origin/main     # reset main to remote state
git switch feature/my-work       # switch to the new branch
# Your commits are now on feature/my-work, not main

Error: branch has diverged from remote after rebase

Bash
git push origin feature/user-search
# ! [rejected] feature/user-search -> feature/user-search (non-fast-forward)

# This is expected after a rebase — use force-with-lease:
git push --force-with-lease origin feature/user-search
# (safe: only force-pushes if no one else has pushed to the branch)
PRs are platform features, not Git features
Pull Requests / Merge Requests are provided by hosting platforms (GitHub, GitLab, Bitbucket). Git itself has no PR concept. The feature branch workflow works with any hosting platform that supports code review — or even just `git merge` on the command line after a face-to-face review.