As an experienced developer, you often need to diagnose bugs, reverse changes, prototype features, or release hotfixes without disrupting your team‘s main development.
Creating branches from specific commits unlocks precise control over your Git repository‘s history.
In this comprehensive 3500+ word guide, you‘ll learn:
- Common scenarios for commit-based branching
- Underlying commit anatomy with SHA-1 hashes
- Steps to create branches from detached HEAD states
- Advanced integration workflows for these branches
- Expert tips for managing branches from old commits
By the end, you‘ll have mastered branching from commits for smarter Git-based collaboration.
Why Create Branches From Commits?
Before explaining how to branch from commits, let‘s explore common why scenarios:
Hotfix Releases
Imagine an application crash-causing bug needs an urgent patch. But main is mid-feature development with unstable code.
Here, you‘d branch from the last known good commit before the regressing change. Build, test and release the hotfix on this branch isolated from disruptive new main commits.
Reverting Commits
Did a pull request unintentionally break features? Rather than a risky revert on main, checkout an older commit then apply and test the revert on a branch there. This analyzes the fix safely away from upstream.
Developing Alternatives
When prototyping a major rewrite, branching from further back lets you compare solutions across separate Git histories. You can also evolve branches in isolation without impacting customer-facing main.
Branching from arbitrary commits enables "what-if" scenarios like these without altering published commit histories. The flexibility empowers developers to inspect, iterate, and backtrack through their repo‘s evolution.
Next let‘s break down commit anatomy to see how branches pin to commits.
Demystifying Git Commits and SHAs
Fundamentally, a Git repository stores a Directed Acyclic Graph (DAG) of commits. The nodes in this DAG are the commits, linked by parent references.
In Git‘s eyes, a commit object contains:
- Content – A snapshot of all tracked files/folders
- Metadata – Author, committer, parent commits, message etc
- Address – SHA-1 hash acting as a unique content-based identifier
For example, a commit object may internally look like:
commit a7cdf21b534fee240d5b0874b657cbf48c36dfo2
Author: Mary Developer <mary@dev.com>
Date: July 21 2022
Add login page
<tree of tracked file contents>
<references to parent commits>
The key field is the SHA-1 hash derived from the content snapshot. This hash uniquely identifies the exact state of a commit as a 160-bit hexadecimal string:
a7cdf21b534fee240d5b0874b657cbf48c36dfo2
Per the Git objects documentation:
"The SHA-1 hash‘s key property is that it varies drastically with even the slightest change to the content of the file. This allows Git to detect any potential corruption or tampering with old data."
So even if all fields stayed the same, altering a single byte of the source code would produce a totally different SHA.
In this way, SHAs let developers reference precise, immutable commits by their cryptographic fingerprints when branching.
Now we‘re equipped to utilize SHAs for pinpoint branching!
Step-by-Step: Creating Branches From Commits
Suppose you want to patch a newly deployed version 1.2 from last Tuesday. Here‘s how to branch from those commits:
1. Identify the Base SHA
First figure out the target commit. The git log prints recent history:
commit fde9ca22cc5ea4786526eea3a2eb6ae5c3e447f0
Date: Tue Jul 19 11:32:28 2022 -0400
v1.2 Release
commit 9325c72c8a3b3aa968c95eb4328cded40d39bb2f
Date: Thu Jul 21 10:32:28 2022 -0400
Add login form
commit d89b23d10283a213ae9369f9e800f3d61c286d5e
Date: Mon Jul 18 09:43:01 2022 -0400
Initial commit
Let‘s branch from v1.2‘s fde9ca22cc SHA.
2. Check Out the Commit
Now check out this commit directly by SHA:
git checkout fde9ca22cc
This switches the working directory and files to match fde9ca22cc:
Note: HEAD is now at fde9ca22 v1.2 Release
Git "detaches" HEAD to pin to the specific commit instead of a named branch. This lets you effectively go back in time to any state.
3. Spawn the New Branch
With HEAD detached at target commit fde9ca22cc, branch from here:
git checkout -b hotfix/1.2.1
Switched to a new branch ‘hotfix/1.2.1‘ based on the older detached HEAD:
--- hotfix/1.2.1 (HEAD)
|
| * ---- (main)
|
| * ---- v1.2 Release (fde9ca22)
Now you have an independent branch rooted at the publishes v1.2 commits.
4. Develop the Fixes
With files matching v1.2, code your hotfix changes on the hotfix/1.2.1 branch in isolation:
fix crashing bug
test solution
bump version to 1.2.1
This keeps disruptive development separated on main while you patch.
5. Merge Back to Main
Once the hotfix passes your tests, merge it into the public main branch:
git checkout main
git merge hotfix/1.2.1 --no-ff
This integrates the bugfix while preserving hotfix branch history.
Then continue evolving main allowing parallel streams of development!
Now that you‘ve seen branch creation hands-on, let‘s explore workflows integrating these branches.
Comparing Branch Merge vs Rebase Workflows
As specialized branches diverge further from main, integrating them gets trickier. You essentially have two options:
Merging Branch Commits
Merging concatenates commits between branches:
C---D Merge main into feature
/
A---B----E---F feature
The feature branch HEAD F becomes a new merge commit with main changes as second parent.
Tradeoffs:
- Preserves complete feature branch history
- Can create convoluted mergers from old branches
Rebasing Branch Commits
Rebasing replays branch commits onto updated main:
C‘--D‘ Rebase feature onto main
/
A---B-------E‘--F‘ feature
Here, feature changes get re-committed as E‘ onwards, branching directly off latest main.
Tradeoffs:
- Cleaner topology with linear commit history
- Rewrites public commit SHAs – don‘t rebase shared branches!
So whether to merge vs rebase depends on branch purpose and visibility.
Next let‘s explore best practices managing these specialized branches.
Expert Tips for Managing Commit-Based Branches
While isolated branches enable localized development, they require some maintenance to pay off in the long run:
Label Branches Meaningfully
Give branches contextual names derived from base commits like:
bugfix/login-form-#932fc5a
feature/blue-theme-testing-v1.3
This better identifies divergent branches at a glance.
Rebase Over Merging Old Branches
As branches age, their histories get increasingly out of date from main. Rather than a convoluted merge, rebase hotfix/bug branches to selectively replay commits atop main or develop:
git checkout bugfix/report-gen
git rebase main
# Resolve merge conflicts
# Test refreshed changes on updated main
Keeping specialized branches rebased results in a cleaner commit topology.
Reviewpoc Commits Before Rebasing
However, avoid rebasing branches where commits are shared publicly across teams. Rebased SHAs get entirely new hashes, breaking external references to old commits.
Instead carefully merge vetted topic branches preserving their commit history.
Integrate Early, Integrate Often
Don‘t let branches drift on their own tangent long term. Keep changes flowing with frequent integration. Merge/rebase every few days or when a feature segment completes.
Draw new branches from current tip of main to minimize divergence too.
Share Common History
Avoid baking months of commits on a branch before reconciling with main. Other developers race ahead committing fixes and features best folded into your tasks early.
Cursorily rebase just before issuing your pull request. Then reviewers can clearly trace vertical history through your recent related commits.
Delete Stale Branches
Once you land significant branch work via successful pull requests, delete the remote feature branch reference.
Keep your branch namespace lean containing only active works-in-progress. Remember, merged commits remain intact under main while their branch reference gets removed.
Recap: Best Practices Branching from Commits
Let‘s recap key learnings for proficiently managing branches from commits:
Why branch from commits?
- Safely walk back changes, develop alternatives and release hotfixes
- Empowers nonlinear development unique to distributed Git histories
Core concepts:
- Commits are immutable content-addressed objects with SHA-1 hashes
- Branches are movable labels pointing to commits
git checkout <SHA>detaches HEAD to directly reference any commit
Key workflows:
- Checkout target commit → New branch → Develop → Test → Merge/rebase
- Merge to aggregate commit histories across branches
- Rebase to replay commit sequences atop updated main branch
Managing commit-based branches:
- Meaningfully label branches by commit SHA and purpose
- Prefer rebasing over merging aged branches
- Delete old branch labels after pull requests land
- Keep specialized branches rebased/merged rather than drifting far behind main
Internalizing these commits and branching fundamentals will enable you to utilize Git histories with far greater flexibility.
Whether you must urgently respond to bugs, experimentally develop or safely walk back changes, branching from arbitrary commits is an indispensable tool.
Key Takeaways
And that wraps up our deep dive on branching from commits!
We covered specialized scenarios driving commit-based branches, Git‘s object model internals, SHA-1 semantics, isolated development workflows and integrating old branches.
The key ideas to internalize are:
- Why branch from old commits – hotfix, revert, prototype, compare
- Commit anatomy – content snapshot, authoring metadata, SHA-1 hash
- Branching process – finding SHA, detaching HEAD, new branch
- Merge vs Rebase tradeoffs with older branches
- Best practices naming, integrating and managing commit branches
Now discover the superpowers unlocking nonlinear development through Git‘s DAG of branches and commits!


