Developer Tools

Git Workflow Guide: Branching, Merging, and Team Collaboration

Master Git branching strategies, commit best practices, rebasing vs. merging, and the workflows used by professional engineering teams.

8 min read

Developer looking at code on a screen

Git is the foundation of every modern software team's workflow. Yet many developers use only commit, push, and pull — leaving behind the features that make collaborative development smooth and conflict-free. This guide covers the branching strategies, commit practices, and everyday commands used by professional teams.

The core mental model

Git is a directed acyclic graph of snapshots (commits). Each commit points to its parent(s). Branches are simply named pointers to commits — lightweight and cheap to create.

main:    A → B → C → D
feature:         C → E → F

Branching doesn't copy files — it just creates a new pointer. That's why creating a branch takes milliseconds regardless of repository size.

Branching strategies

GitHub Flow (simple, continuous delivery)

Best for teams that deploy frequently:

  1. main is always deployable
  2. Create a feature branch for every change
  3. Open a pull request when ready for review
  4. Merge to main after approval
  5. Deploy immediately
git checkout -b feature/add-user-auth
# ... make changes ...
git push origin feature/add-user-auth
# Open PR → review → merge → deploy

Git Flow (structured releases)

Best for products with versioned releases (apps, libraries):

  • main — production code only
  • develop — integration branch
  • feature/* — new features (branch from develop)
  • release/* — release preparation (branch from develop)
  • hotfix/* — urgent production fixes (branch from main)

More structured but adds overhead. Use GitHub Flow unless you have a genuine need for parallel release management.

Trunk-Based Development

Developers commit directly to main (or short-lived branches < 1 day). Feature flags control what users see. Requires strong CI/CD and test coverage. Used by Google, Facebook, and high-velocity teams.

Writing great commit messages

The commit message is a letter to your future self (and teammates). Follow the Conventional Commits format:

<type>(<scope>): <short summary>

<optional body — what and why, not how>

<optional footer — breaking changes, issue refs>

Types:

  • feat — new feature
  • fix — bug fix
  • docs — documentation only
  • refactor — code change that neither fixes a bug nor adds a feature
  • test — adding or fixing tests
  • chore — build process, dependencies, tooling

Examples:

feat(auth): add JWT refresh token rotation

Implements sliding session windows using refresh token rotation.
Previous tokens are invalidated on use to detect theft.

Closes #142
fix(api): return 404 instead of 500 for missing user

The /users/:id endpoint was throwing an unhandled exception when
the user didn't exist. Now returns a proper 404 with error message.

Rule of thumb: If your commit message is fix stuff or WIP, your team hates you. Make it descriptive.

Merge vs. Rebase

Merge

git checkout main
git merge feature/my-feature

Creates a merge commit that joins two branches. Preserves full history — you can see exactly when branches diverged and merged.

A → B → C → M (merge commit)
         ↑   ↑
         E → F (feature)

Use merge for: integrating long-running branches, preserving context in history.

Rebase

git checkout feature/my-feature
git rebase main

Replays your commits on top of the target branch. Creates a linear history — looks like the feature was built on top of the latest main.

Before: A → B → C (main)
                 ↑
            D → E (feature, based off B)

After:  A → B → C → D' → E' (feature rebased onto C)

Use rebase for: cleaning up local commits before a PR, keeping feature branches up to date.

Golden rule: Never rebase commits that have been pushed to a shared branch. Rebase rewrites history — it breaks other people's local branches.

Interactive rebase: cleaning up history

Before opening a PR, squash and fixup messy work-in-progress commits:

git rebase -i HEAD~4   # interactively edit the last 4 commits

Options in the interactive editor:

  • pick — keep the commit
  • squash — merge into the previous commit, combine messages
  • fixup — merge into the previous commit, discard message
  • reword — keep changes, edit message
  • drop — delete the commit entirely

Handling conflicts

Conflicts happen when two branches modify the same lines. Git marks them:

<<<<<<< HEAD (your branch)
const timeout = 5000;
=======
const timeout = 3000;
>>>>>>> feature/update-timeouts

Resolve by editing the file to the correct state (removing the markers), then:

git add src/config.ts
git merge --continue   # or git rebase --continue

Prevention is better than cure:

  • Pull from main frequently to keep branches short-lived
  • Communicate with teammates when working on the same files
  • Keep PRs small — large PRs have more conflicts and harder reviews

Essential daily commands

# Start a new feature
git checkout -b feature/my-feature

# Stage specific changes (not the whole file)
git add -p

# View unstaged changes
git diff

# View staged changes
git diff --staged

# Amend the last commit (before pushing)
git commit --amend --no-edit

# Temporarily save uncommitted work
git stash
git stash pop

# Find which commit introduced a bug (binary search)
git bisect start
git bisect bad              # current commit is bad
git bisect good v1.2.0      # last known good commit

# Recover a deleted branch or lost commit
git reflog

# See a visual graph of branches
git log --oneline --graph --all

Pull request best practices

A good PR:

  • Does one thing — reviewers can understand the full change
  • Has a clear description — what changed, why, and how to test it
  • Is small — < 400 lines of diff as a rough target
  • Passes CI — never ask for review on a broken build
  • Links to the issueCloses #42 auto-closes the issue on merge

Use our README Generator to scaffold documentation alongside your code changes.

.gitignore essentials

Always add these to .gitignore:

# Environment variables
.env
.env.local
.env.*.local

# Dependencies
node_modules/
vendor/

# Build output
dist/
build/
.next/

# OS files
.DS_Store
Thumbs.db

# Editor files
.vscode/settings.json
.idea/
*.swp

Generate a project-specific .gitignore at gitignore.io — it knows about hundreds of languages and tools.

Git mastery compounds over time. Learn the mental model, adopt good commit habits, and your team's collaboration will become dramatically smoother.

Git Workflow Guide: Branching, Merging, and Team Collaboration — FreeTool24 | FreeTool24