The Git Workflow Nobody Teaches You: Rebase, Bisect, and Worktrees
Most git tutorials stop at branching, merging, and resolving the occasional conflict, which covers maybe the first year of using git day to day but leaves out a set of commands that, once you actually learn them, change how confidently you approach a messy history, a mystery regression, or a large in-progress change. None of these are exotic, they ship with every git install, they are just underexplained.
Interactive rebase: cleaning up history before it becomes permanent
Most people know git rebase as the thing you do to replay your branch on top of an updated main, but interactive rebase, git rebase -i, is a genuinely different, more powerful tool: the ability to rewrite, reorder, squash, and edit a series of commits before they ever become part of shared history.
git rebase -i HEAD~5
This opens an editor listing your last five commits, each with a pick in front of it, and you can change pick to squash to fold a commit into the one before it, reword to change just the commit message without touching the diff, or drop to remove a commit entirely. I use this constantly before opening a pull request, because my actual working history during development is messy by nature, fix typo, actually fix the typo, WIP, forgot a file, and nobody reviewing my PR benefits from seeing that. Squashing it down to three or four commits that each represent one coherent, reviewable idea makes the PR genuinely easier to review and makes git blame meaningfully more useful a year later, because the commit someone lands on when tracing a bug back through history actually explains a real, complete change instead of a fragment of one.
The rule that keeps this safe: never rewrite history on a branch other people have already pulled from and built on top of, because rebasing rewrites commit hashes, and anyone with the old commits will hit a genuinely confusing divergence. This is entirely safe on your own feature branch before it is shared, and that is the only place I use it.
Bisect: finding the exact commit that broke something, automatically
git bisect is the tool most engineers have heard of and almost nobody actually uses, which is a shame because it turns finding a regression's origin from a manual, error-prone binary search you do by hand into an automated process git does for you.
git bisect start
git bisect bad HEAD
git bisect good v2.3.0
Git checks out a commit roughly in the middle of that range, and you tell it whether the bug is present, git bisect good or git bisect bad, and it narrows the range by half each time, exactly the same binary search you would do manually, but tracked correctly and without you having to keep the range in your head. The real power shows up when you automate the good and bad determination with a script.
git bisect start HEAD v2.3.0
git bisect run npm test -- --grep 'checkout flow'
Given a test that reliably fails on the bad commit and passes on the good one, git bisect run will check out each candidate commit, run the test, and automatically narrow the range without you touching anything, landing you directly on the exact commit that introduced the regression, often in under a minute even across a range of hundreds of commits. I found the root cause of that eight-month-old idempotency key bug I have written about elsewhere using exactly this, and it took less time than it would have taken to manually check even five candidate commits by hand.
Worktrees: working on two branches at once without stashing anything
This is the one that changed my daily workflow the most once I actually adopted it. The common situation: you are mid-way through a feature branch with uncommitted changes, and an urgent bug report comes in that needs a fix on main right now. The old way is git stash, switch branches, fix the bug, switch back, git stash pop, and hope nothing conflicts. Worktrees let you have two branches checked out simultaneously in two separate directories, both pointing at the same underlying repository and its full history.
git worktree add ../myproject-hotfix main
This creates a new directory, myproject-hotfix, as a fully functional checkout of main, sharing the same .git history as your primary working directory, but with an entirely separate working tree. You can cd into it, fix the bug, commit, and push, all without touching your in-progress feature branch's uncommitted changes at all, no stash, no risk of a stash conflict when you pop it later, and no context-switching cost of reconstructing what you were in the middle of. When you are done, git worktree remove ../myproject-hotfix cleans it up.
I now keep a permanent second worktree for exactly this purpose, a directory always checked out to main, ready for the moment an urgent fix comes in, while my primary directory stays on whatever feature I am actively building. Once you use worktrees for even one genuinely urgent interruption, going back to stash-based context switching feels like a real regression.
Reflog: the safety net for when you think you have lost something
Every time HEAD moves in your local repository, whether from a commit, a checkout, a reset, or a rebase, git records it in the reflog, which is effectively a local, personal history of everywhere your HEAD has pointed, kept separately from your actual commit history and not shared with anyone else.
git reflog
This is what saves you after a git reset --hard that removed commits you actually needed, or an interactive rebase that went wrong and dropped something important. The commits are almost never actually gone immediately, they are just no longer referenced by any branch, and git will not garbage collect them for a while by default. Find the commit hash you need in the reflog output and git reset --hard
Why none of this gets taught upfront
I think the honest reason is that all four of these tools are genuinely dangerous if used carelessly, rewriting shared history, resetting hard without checking the reflog first, and tutorials reasonably prioritize teaching the safe, boring subset first. But that safe subset is not actually sufficient once you are working on a real team with real history worth preserving cleanly, real regressions that need tracing, and real interruptions that need handling without losing in-progress work. These four commands, learned properly rather than copy-pasted from Stack Overflow under pressure the first time you need them, are the actual difference between fighting git and using it as the genuinely powerful tool it is.
I would add one more habit that pairs naturally with all four of these once they are second nature: writing commit messages as if the reflog and rebase tools did not exist, meaning treat every commit message as a permanent, load-bearing piece of documentation rather than a placeholder you plan to clean up later with an interactive rebase. In practice you will still clean things up before opening a pull request, but going in with the discipline of a good message from the start means the squash and reword steps become genuinely quick, mostly picking the best version of several already-decent messages, rather than trying to reconstruct, days later, what you were actually thinking when you wrote a commit that just says fix stuff. The tools cover for sloppiness after the fact, which is valuable, but they work best paired with reasonably good habits going in, not as a substitute for them.
Written by AIDToolStack Editorial
The AIDToolStack editorial team reviews developer tools, AI products, and MCP servers with hands-on testing and honest assessments. We use the tools we review every day in production.
References & Further Reading
Related Posts
Sponsor Our Newsletter
Reach thousands of developers who are actively evaluating AI tools, MCP servers, and dev infrastructure. Our weekly newsletter goes to engaged technical decision-makers.
All sponsored content is clearly labeled per our editorial policy.