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:
| Area | Purpose | What You See |
|---|---|---|
| Working Directory | Your actual files on disk where you make changes | The latest state of your files after edits |
| Staging Area (Index) | A preparation area where you select changes for the next commit | A snapshot of files that will be included in the next commit |
| Repository (HEAD) | The permanent history of committed changes | The 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 commitmoves 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
| Command | Purpose |
|---|---|
git status | See which files have been modified |
git diff | View 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
| Command | Purpose |
|---|---|
git add <file> | Stage specific file |
git add . | Stage all modified files |
git add -p | Stage changes interactively (chunk by chunk) |
git restore --staged <file> | Unstage a file (keep changes) |
git diff --staged | View changes ready to be committed |
git reset | Unstage all files |
Repository
| Command | Purpose |
|---|---|
git commit -m "message" | Commit staged changes |
git commit -am "message" | Commit all tracked files (skip staging) |
git log --oneline | View commit history |
git show <hash> | View details of a specific commit |
git reset --soft HEAD~1 | Undo last commit, keep changes staged |
git reset --hard HEAD~1 | Undo last commit, discard all changes |
Common Pitfalls & Safety Rules
Understanding these risks helps prevent data loss and repository corruption.
- Avoid force-pushing to shared branches (
main,develop) - if it is unavoidable, use--force-with-leaseand coordinate with the team - Avoid rewriting published history on shared branches - treat pushed commits as immutable in normal workflows
- Always check before destructive operations - run
git statusandgit difffirst - Use
git reflogfor recovery - can restore lost commits
| Area | Risk | Prevention |
|---|---|---|
| Working Directory | Discarding uncommitted work with git checkout -- <file> | Check git diff before discarding |
| Staging Area | Accidentally staging too much with git add . | Use git add -p for interactive staging |
| Repository | git reset --hard discards commits permanently | Create backup branch: git branch backup |
- Use
git revertinstead ofgit resetfor shared history - Use
git reset --softto 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
mainbranch is always deployable. - Create short-lived feature branches from
main. - Merge back to
mainvia Pull Request after CI/CD passes.
Conventional Commits
Standardizing commit messages makes history readable and enables automated changelog generation.
Format: <type>(<scope>): <subject>
| Type | Purpose | Example |
|---|---|---|
feat | New feature | feat(auth): add JWT support |
fix | Bug fix | fix(api): handle null response |
docs | Documentation only | docs: update readme |
refactor | Code change that neither fixes a bug nor adds a feature | refactor: simplify loop |
chore | Maintenance (build, deps, tools) | chore: update docusaurus |
Keep commits atomic. One commit should represent one logical change. This makes git revert and git cherry-pick much safer.
Essential Commands
History & Inspection
| Command | Purpose |
|---|---|
git status -s | Short status summary |
git diff --staged | View changes already added to staging |
git log --oneline --graph --all | Visual representation of all branches |
git blame <file> | Identify who changed each line |
Undo & Recovery
| Command | Effect | Scenario |
|---|---|---|
git commit --amend | Modify the very last commit | Forgot a file or typo in message |
git reset --soft HEAD~1 | Undo commit, keep changes staged | Redo a commit properly |
git reset --hard HEAD~1 | Destructive: Revert to previous state | Discard unwanted work |
git revert <hash> | Create a new commit that undoes a previous one | Safe undo for pushed commits |
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 workgit 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 Worktree | Solution with Worktree |
|---|---|
Need to git stash before switching branches | Each branch has its own directory — no stashing needed |
| Long-running builds block switching | Keep build running in one tree, edit code in another |
Comparing branches requires git diff | Open two editors in two directories and compare directly |
Core Commands
| Command | Purpose |
|---|---|
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 list | List all working trees attached to this repository |
git worktree remove <path> | Remove a working tree (does not delete the branch) |
git worktree prune | Clean 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
Use a consistent prefix for worktree directories, e.g., ../project-hotfix, ../project-review, ../project-release. This keeps your workspace organized.
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 startgit bisect badgit bisect good <stable-hash>
Related Guides
- 📄 Merge, Rebase & Cherry-Pick — Integrating changes and rewriting history.
- 📄 Git Hooks Guide — Automate linting and testing.
- 📄 Submodules Guide — Manage external dependencies.
- 📄 SSH Setup — Secure remote access.