DEV Community

Rizwan Saleem
Rizwan Saleem

Posted on

A Practical Git Lunch-and-Learn: A Branch-First, Rebase-Driven Collaboration Workflow

A Practical Git Lunch-and-Learn: A Branch-First, Rebase-Driven Collaboration Workflow

A Practical Git Lunch-and-Learn: A Branch-First, Rebase-Driven Collaboration Workflow

If you’ve ever wrestled with merge conflicts at 4 a.m., you’re not alone. The truth is, most teams don’t need every feature Git offers; they need a workflow that reduces friction, preserves a clean history, and makes collaboration predictable. This guide presents a branch-first, rebase-driven workflow that works well for small-to-medium teams. It emphasizes clear branch semantics, lightweight rebasing for feature work, and a disciplined release cadence. You’ll find actionable steps, concrete commands, and a complete checklist you can adapt to your project.

Table of contents

  • Why a branch-first workflow
  • Core principles you’ll adopt
  • Setting up repository conventions
  • Branch model and naming
  • Daily workflow: from kickoff to review
  • Handling conflicts with rebase
  • Integrating with CI/CD
  • Release tagging and changelogs
  • Common pitfalls and how to dodge them
  • Example run-through: a feature in three commits
  • Tooling you might add

Why a branch-first workflow

  • Isolates work: Each feature or bug fix lives on its own branch, reducing risk to main.
  • Lightweight history: Rebasing keeps a linear, readable history without the noise of too many merges.
  • Predictable reviews: PRs or merge requests become focused on a single intent with a clear narrative.
  • Safer rollbacks: You can revert a single branch’s changes without impacting others, provided you release via well-defined tags.

Core principles you’ll adopt

  • Branch per feature or task: Each item on the to-do list gets a dedicated branch.
  • Rebase over merge for feature commits: Rebase your feature branch onto the latest main before merging.
  • Fast, small commits: Commit frequently with meaningful messages; avoid back-to-back “work in progress” commits.
  • Continuous integration on short-lived branches: CI runs on pull requests to catch issues early.
  • Immutable main in production cadence: Main (or master) reflects production-ready state only after a verified release.

Setting up repository conventions

  • Require branch names that reflect the task type and a short description. Examples:
    • feature/login-improvements
    • fix/memory-leak-accounts
    • chore/docs-update
  • Ensure protected main branch:
    • Require pull requests for changes
    • Run CI on PRs
    • Require at least one approved review
    • Enforce linear history if your team prefers it (see rebasing section)
  • Commit message guidelines:
    • Use imperative mood: “Add,” “Fix,” “Refactor”
    • Include a brief context: (e.g., “Add authentication retry on 503”)
    • Optional footer: “Closes #123” or “Relates to #456”

Branch model and naming

  • Main branch: main (or master)
  • Feature branches: feature/-
  • Bugfix branches: fix/-
  • Chore/docs: chore/
  • Release branches (optional for larger teams): release/v1.2.x
  • Hotfix branches (emergency fixes on production): hotfix/

Daily workflow: from kickoff to review
1) Kickoff

  • Create a new branch from main:
    • git checkout main
    • git pull rebase
    • git checkout -b feature/user-auth-refresh
  • Start with a clear objective in your branch’s first commit.

2) Work and commit

  • Work in small, focused steps. Each commit should:
    • Address one logical change
    • Have a meaningful message
    • Include tests or update tests when possible
  • Example commits:
    • Add JWT refresh flow to auth service
    • Update unit tests for token expiry edge case
    • Update API docs for new endpoint
  • Regularly push to the remote feature branch to back up progress:
    • git push -u origin feature/user-auth-refresh

3) Rebase regularly to keep history clean

  • Before opening a PR, rebase your branch onto the latest main:
    • git fetch origin
    • git rebase origin/main
  • If you encounter conflicts:
    • Resolve them in your editor
    • Continue with: git add and git rebase continue
  • After rebasing, force-push your updated branch:
    • git push force-with-lease

4) Review and refine

  • Open a pull request against main.
  • Address review comments with small, discrete commits (rebasing again is optional for small tweaks).
  • If multiple people work on the same area, consider squashing minor fixups during the review to keep the mainline clean.

5) Merge strategy

  • Use “squash and merge” or “rebase and merge” depending on your policy:
    • Squash keeps a single, cohesive feature commit on main.
    • Rebase and merge preserves individual commits but requires a clean history.
  • If your policy is strictly linear history, use rebase and merge with a final rebase of the PR branch onto main before merging.

6) Release preparation

  • After merging, update changelog and tags:
    • git fetch tags
    • Create a release branch if your process uses one, or tag the main directly:
    • git tag -a v1.3.0 -m "Release notes for v1.3.0"
    • git push origin v1.3.0

Handling conflicts with rebase

  • Rebase strategy helps keep a linear history but makes conflicts more frequent on long-lived branches.
  • Best practices:
    • Rebase often during active work to minimize big merges later.
    • When conflicts arise, pause, resolve, then continue with git rebase continue.
    • If the branch has diverged too much, consider a fresh rebase onto main: git fetch origin; git rebase onto origin/main origin/feature/user-auth-refresh
  • If you’re uncomfortable with rewriting public history, reserve rebasing for private branches or coordinate with your team.

Integrating with CI/CD

  • Ensure CI runs for every PR:
    • Lint, unit tests, and integration tests as appropriate.
  • Optional: run a separate pipeline for releases:
    • Build artifacts
    • Run end-to-end tests in a staging environment
    • Deploy to a preview environment for reviewer validation
  • Use status checks to block merges until CI passes.

Release tagging and changelogs

  • Maintain a lightweight CHANGELOG.md:
    • Keep a section per release with bullet points of user-visible changes.
  • Tagging recommendations:
    • Use semantic versioning if you can: MAJOR.MINOR.PATCH
    • Example: v2.1.0 with a tag message listing highlights
  • Automation ideas:
    • Create a script to extract merged PR titles since last tag to populate changelog automatically.

Common pitfalls and how to dodge them

  • Pitfall: Long-lived feature branches that diverge from main
    • Solution: Rebase weekly or more often; communicate when you plan to rebase.
  • Pitfall: Large, unfocused commits
    • Solution: Break work into small commits; use interactive rebase to squash or reorder.
  • Pitfall: Inconsistent PR reviews
    • Solution: Enforce a small, focused scope per PR; require at least one reviewer.
  • Pitfall: Skipping tests on feature branches
    • Solution: Run the full test suite in CI; consider lightweight tests for speed during development.

Example run-through: a feature in three commits

  • Feature: Add “remember me” to login
  • Branch: feature/remember-me
  • Commits: 1) feat(auth): add remember-me flag to login API and DB schema 2) test(auth): cover remember-me flow and token expiration 3) feat(ui): expose remember-me checkbox on login form and update UI tests
  • Workflow:
    • Create branch from main, begin work, and commit as shown.
    • Rebase onto latest main before opening PR.
    • Open PR, pass CI, get one reviewer, address comments with a fourth small commit, then merge with squash.
    • Tag release if this feature is part of a new version.

Tooling you might add

  • Git clients and helpers
    • CLI: Git, Git LFS for large binaries
    • Diff tools: Meld, Beyond Compare, or VS Code’s built-in diff
    • Git aliases to speed up rebases and cleanups
  • PR workflow
    • GitHub/GitLab/Bitbucket PRs with required reviews and status checks
    • Templates for PRs to ensure consistent context and acceptance criteria
  • Quality gates
    • Pre-commit hooks for lint and unit tests
    • CI that runs on PRs and on push to main
  • Release automation
    • A small script that aggregates merged PR titles since the last tag into a changelog and creates a new tag

Sample commands you’ll use

  • Start a feature branch from main:
    • git checkout main
    • git pull rebase
    • git checkout -b feature/example
  • Rebase onto latest main before PR:
    • git fetch origin
    • git rebase origin/main
    • Resolve conflicts, then:
    • git add .
    • git rebase continue
    • git push force-with-lease origin feature/example
  • Merge option choices:
    • Squash merge via UI, or
    • git checkout main; git pull rebase; git merge no-ff origin/feature/example
    • git push origin main
  • Tagging a release:
    • git tag -a v1.3.0 -m "Release v1.3.0: remember-me feature and UI refinements"
    • git push origin v1.3.0

Illustrative quick-start checklist

  • [ ] Branch per task established
  • [ ] Feature branch rebased onto latest main before PR
  • [ ] CI configured to run on PRs
  • [ ] PR reviews completed and approved
  • [ ] Changes merged with chosen strategy (squash or rebase)
  • [ ] Release notes and tag created for the release

If you want, I can tailor this workflow to your stack (e.g., GitLab CI, GitHub Actions, or Bitbucket Pipelines), your programming language, and your team size. Would you like a version tailored to a Node.js project with GitHub Actions and a formal release cadence, or a Python project with GitLab CI and a weekly release train?

-

Rizwan Saleem | https://rizwansaleem.co

Top comments (0)