Skip to main content

Git Workflow & Branching Strategy

This guide covers professional Git workflows, branching strategies, conventional commits, and essential commands for daily development.

Basic Workflow

A standard Git workflow involves moving changes through three main areas: the Working Directory, the Staging Area (Index), and the Repository (HEAD).

Understanding Git's Three Areas

Git manages files in three distinct areas, each serving a specific purpose:

AreaPurposeWhat You See
Working DirectoryYour actual files on disk where you make changesThe latest state of your files after edits
Staging Area (Index)A preparation area where you select changes for the next commitA snapshot of files that will be included in the next commit
Repository (HEAD)The permanent history of committed changesThe committed project history with metadata (author, date, message)

Key Differences:

  • Working Directory vs Staging Area: The working directory contains your latest edits, while the staging area contains only the changes you've explicitly marked with git add. You can edit a file ten times but only stage the final version.

  • Staging Area vs Repository: The staging area holds changes for the next commit (transient), while the repository stores the entire commit history (permanent). Only git commit moves changes from staging to the repository.

  • HEAD: A pointer to the currently checked-out commit, typically the latest commit on your current branch.

Commands for Each Area

Working Directory

CommandPurpose
git statusSee which files have been modified
git diffView changes not yet staged
git checkout -- <file>Discard changes in working directory (restore to last committed version)
git restore <file>Modern alternative to git checkout -- <file>

Staging Area

CommandPurpose
git add <file>Stage specific file
git add .Stage all modified files
git add -pStage changes interactively (chunk by chunk)
git restore --staged <file>Unstage a file (keep changes)
git diff --stagedView changes ready to be committed
git resetUnstage all files

Repository

CommandPurpose
git commit -m "message"Commit staged changes
git commit -am "message"Commit all tracked files (skip staging)
git log --onelineView commit history
git show <hash>View details of a specific commit
git reset --soft HEAD~1Undo last commit, keep changes staged
git reset --hard HEAD~1Undo last commit, discard all changes

Common Pitfalls & Safety Rules

Understanding these risks helps prevent data loss and repository corruption.

Critical Rules
  1. Avoid force-pushing to shared branches (main, develop) - if it is unavoidable, use --force-with-lease and coordinate with the team
  2. Avoid rewriting published history on shared branches - treat pushed commits as immutable in normal workflows
  3. Always check before destructive operations - run git status and git diff first
  4. Use git reflog for recovery - can restore lost commits
AreaRiskPrevention
Working DirectoryDiscarding uncommitted work with git checkout -- <file>Check git diff before discarding
Staging AreaAccidentally staging too much with git add .Use git add -p for interactive staging
Repositorygit reset --hard discards commits permanentlyCreate backup branch: git branch backup
Safe Workflow
  • Use git revert instead of git reset for shared history
  • Use git reset --soft to undo commits without losing changes
  • Commit or stash before switching branches

Branching Strategies

Choosing the right branching strategy depends on your team size and release frequency.

Git Flow

Best for: Scheduled releases and versioned products.

  • main: Production-ready code only.
  • develop: Integration branch for features.
  • feature/: Temporary branches for new features.
  • release/: Preparation for a new production release.
  • hotfix/: Quick fixes for production bugs.

GitHub Flow

Best for: Continuous Deployment (CD) and web applications.

  • The main branch is always deployable.
  • Create short-lived feature branches from main.
  • Merge back to main via Pull Request after CI/CD passes.

Conventional Commits

Standardizing commit messages makes history readable and enables automated changelog generation.

Format: <type>(<scope>): <subject>

TypePurposeExample
featNew featurefeat(auth): add JWT support
fixBug fixfix(api): handle null response
docsDocumentation onlydocs: update readme
refactorCode change that neither fixes a bug nor adds a featurerefactor: simplify loop
choreMaintenance (build, deps, tools)chore: update docusaurus
Atomicity

Keep commits atomic. One commit should represent one logical change. This makes git revert and git cherry-pick much safer.


Essential Commands

History & Inspection

CommandPurpose
git status -sShort status summary
git diff --stagedView changes already added to staging
git log --oneline --graph --allVisual representation of all branches
git blame <file>Identify who changed each line

Undo & Recovery

CommandEffectScenario
git commit --amendModify the very last commitForgot a file or typo in message
git reset --soft HEAD~1Undo commit, keep changes stagedRedo a commit properly
git reset --hard HEAD~1Destructive: Revert to previous stateDiscard unwanted work
git revert <hash>Create a new commit that undoes a previous oneSafe undo for pushed commits
Force Push

Never use git push --force on shared branches like main or develop. Use git push --force-with-lease if you must update a remote branch after a rebase.


Advanced Tools

Git Stash

Temporarily save uncommitted changes to switch branches.

  • git stash: Save work
  • git stash pop: Restore work

Git Worktree

Manage multiple working trees attached to the same repository, so you can check out different branches side-by-side without stashing or switching.

Why Use Worktree?

Problem without WorktreeSolution with Worktree
Need to git stash before switching branchesEach branch has its own directory — no stashing needed
Long-running builds block switchingKeep build running in one tree, edit code in another
Comparing branches requires git diffOpen two editors in two directories and compare directly

Core Commands

CommandPurpose
git worktree add <path> <branch>Create a new working tree at <path> and check out <branch>
git worktree add -b <new-branch> <path> <start-point>Create a new branch and working tree simultaneously
git worktree listList all working trees attached to this repository
git worktree remove <path>Remove a working tree (does not delete the branch)
git worktree pruneClean up stale worktree entries (e.g., after manually deleting a directory)

Example Workflow

# You are on `main`, working on a new feature.
# A hotfix is needed for production branch `v2.0`.

# 1. Create a worktree for the hotfix (no need to stash anything)
git worktree add ../hotfix-dir v2.0

# 2. Fix the bug in the separate directory
cd ../hotfix-dir
# ... edit, commit, push ...
git push origin v2.0

# 3. Return to your original work
cd ../project
# Your feature branch is exactly where you left it.

# 4. Clean up the hotfix worktree
git worktree remove ../hotfix-dir
Naming Convention

Use a consistent prefix for worktree directories, e.g., ../project-hotfix, ../project-review, ../project-release. This keeps your workspace organized.

caution

Each branch can only be checked out in one worktree at a time. Attempting to check out the same branch in two trees will fail.

Git Bisect

Binary search to find the commit that introduced a bug.

  • git bisect start
  • git bisect bad
  • git bisect good <stable-hash>