If your Git workflow has settled into a comfortable add, commit, push routine, you are missing the deeper power of the tool. Teams often hit friction when history becomes a tangled mess, a bug is buried in a dozen commits, or a wrong merge seems irreversible. The commands we cover here are not obscure — they are the ones experienced developers reach for when the basics fall short. This guide assumes you already use branches, resolve conflicts, and understand the staging area. We will walk through five commands that each solve a distinct problem: cleaning history, finding bugs, switching context, moving specific changes, and recovering from accidents. Each section includes a composite scenario, the mechanics of how the command works, and the pitfalls that can trip you up.
Interactive Rebase: Taming Your Commit History
You have just spent three days on a feature branch, committing early and often — messages like "wip", "fix typo", "try different approach". Now it is time to merge, and the pull request reviewer will see a noisy, hard-to-review history. This is where git rebase -i shines.
How It Works
Interactive rebase replays a series of commits while giving you a chance to edit, squash, reorder, or drop them. The command opens your default editor with a list of commits in reverse order. You change the action keyword (pick, squash, reword, etc.) for each commit and save. Git then applies the modified sequence.
When to Use It
Use it on a feature branch before merging into main. Squash fixup commits into the original commit, reword messages to follow a convention (e.g., "feat: add login endpoint"), and drop commits that were experimental dead ends. Avoid rebasing commits that others have already pulled — it rewrites history and causes conflicts for collaborators.
Composite Scenario
Imagine you are adding a search feature. Your branch has eight commits: three for the backend endpoint, two for the frontend component, one that adds a test, and two that fix bugs found during code review. With git rebase -i HEAD~8, you can squash the two bugfix commits into the backend commit, reword the frontend commits to be descriptive, and drop a commit that introduced a debug log you no longer need. The resulting history has four clean commits, each representing a logical unit of work.
Common Mistakes
Rebasing a shared branch is the most common pitfall. If you must rewrite history on a branch others have pulled, coordinate with the team and be prepared to force-push. Another mistake is squashing too aggressively — a single massive commit loses the ability to bisect later. Keep each commit focused on one concern.
Bisect: Pinpointing the Bug Introduction
A regression appears in production. You know the feature worked two weeks ago, but now it is broken. Manually checking every commit between then and now is slow and error-prone. git bisect automates a binary search through your commit history to find the exact commit that introduced the bug.
How It Works
You start a bisect session by marking a known good commit and a known bad commit. Git then checks out a commit halfway between them. You test the current state and mark it as good or bad. Git repeats this process, narrowing the range until it identifies the first bad commit. On average, it takes log2(N) steps, where N is the number of commits in the range.
When to Use It
Use bisect when you have a clear test — a failing unit test, a visual regression, or a manual reproduction step. It works best on linear history or when you can test quickly. For branches with many merges, use git bisect --first-parent to follow only the mainline.
Composite Scenario
Your team notices that the login page redirects incorrectly on mobile. You know it worked in version 2.3 but broke by version 2.4. Start bisect with git bisect start v2.3 v2.4. Git checks out a commit in the middle. You run a test script that simulates a mobile login. It fails — you mark it bad. Git moves to an earlier commit. It passes — you mark it good. After a few iterations, Git reports that commit a1b2c3d introduced the bug. You open that commit and see a CSS change that broke the redirect logic.
Common Mistakes
Not having a reliable test is the biggest issue. If you mark a commit incorrectly, bisect will point to the wrong place. Also, bisect does not work well with non-linear history unless you use --first-parent. Another mistake is forgetting to run git bisect reset after finishing, which can leave your repository in a detached HEAD state.
Stash: Saving Work in Progress Safely
You are in the middle of a complex refactor, and an urgent bugfix request comes in. You cannot commit half-done work, but you also cannot lose your changes. git stash temporarily shelves your modifications so you can switch branches and come back later.
How It Works
git stash takes your uncommitted changes (both staged and unstaged), reverts your working directory to the last commit, and stores the diff in a stack. Later, git stash pop applies the most recent stash and removes it from the stack. You can also git stash apply to keep the stash for reuse, or git stash list to see multiple stashes.
When to Use It
Use stash for short interruptions — fixing a quick bug, pulling changes from upstream, or testing a different branch. For longer-lived work, consider creating a temporary branch instead, as stashes can be easy to forget or misapply.
Composite Scenario
You are modifying the payment module in a feature branch. A critical security patch is released on main. You run git stash to save your work, check out main, cherry-pick the patch, push, and then return to your feature branch with git stash pop. The changes reappear exactly as you left them.
Common Mistakes
Popping a stash that conflicts with the current branch can be messy. If the working directory has changes, git stash pop may fail. Always commit or stash your current work before popping. Another mistake is forgetting to stash untracked files — use git stash -u to include them. Also, stashes are not pushed to remotes, so they are local only; do not rely on them as a backup.
Cherry-Pick: Selectively Porting Commits
You need to apply a specific commit from one branch to another without merging the entire branch. Perhaps a bugfix was committed on a development branch, but you need it on the release branch immediately. git cherry-pick takes a single commit (or a range) and applies its diff to the current branch.
How It Works
Cherry-pick computes the diff between the target commit and its parent, then applies that diff to your current HEAD. It creates a new commit with a new hash but the same message and author information (unless you specify -x to append the original commit hash).
When to Use It
Use cherry-pick for hotfixes, backporting features to older release branches, or pulling a single improvement from a long-running feature branch without bringing in half-baked code. Avoid it as a substitute for proper merge or rebase workflows — overusing cherry-pick can lead to duplicate commits and a confusing history.
Composite Scenario
Your release branch (v2.0) is frozen for stability, but a critical null-pointer fix was committed on the develop branch as commit f1e2d3c. You check out the release branch and run git cherry-pick -x f1e2d3c. The fix is applied, and the commit message includes a reference to the original hash, making traceability clear.
Common Mistakes
Cherry-picking a commit that depends on earlier changes that are not in the current branch can cause conflicts or missing context. Always check the parent commits. Another mistake is cherry-picking merge commits — by default, cherry-pick of a merge commit fails unless you specify -m 1 to use one parent. Additionally, cherry-picked commits have new hashes, so tools that rely on commit identity (like some CI systems) may not recognize them as the same change.
Reflog: Your Safety Net for Mistakes
You accidentally reset the wrong branch, deleted a commit you meant to keep, or rebased and lost work. Panic sets in. Then you remember git reflog, which records every movement of HEAD and branch references. It is your undo button for Git operations.
How It Works
The reflog is a local log of when the tip of branches and HEAD were updated. Each entry has an index (e.g., HEAD@{2}) and a description of the action. You can use these indices with git reset, git checkout, or git cherry-pick to recover lost commits. The reflog is kept for 90 days by default for reachable commits and 30 days for unreachable ones.
When to Use It
Use reflog whenever you lose work due to a destructive Git command — git reset --hard, git rebase gone wrong, git branch -D on the wrong branch, or git stash drop by mistake. It is also useful for recovering after a failed interactive rebase.
Composite Scenario
You intended to reset a branch to a previous commit but accidentally ran git reset --hard HEAD~5 on the wrong branch. The five commits seem gone. Run git reflog to see the history of HEAD. You see an entry like HEAD@{3}: reset: moving to HEAD~5. To go back, run git reset --hard HEAD@{3} (the state before the reset). The commits are back.
Common Mistakes
The reflog is local — it does not exist on the remote. If you push a destructive change and then pull on another machine, the reflog on that machine will not help. Another mistake is waiting too long; after 90 days, entries expire and are garbage-collected. Finally, reflog only tracks branch and HEAD movements, not individual file changes. For file-level recovery, use git checkout with a commit hash.
Next Steps and Practical Advice
These five commands form a toolkit for everyday Git challenges. Start by integrating one command into your routine each week. First week, use interactive rebase to clean up a feature branch before merging. Second week, use bisect the next time a regression appears. Third week, practice stashing when context-switching. Fourth week, cherry-pick a hotfix. Fifth week, experiment with reflog to recover from a deliberate mistake.
Beyond these commands, consider exploring aliases to speed up repetitive workflows. For example, an alias like git cleanup = rebase -i can make the habit easier. Also, read the official Git documentation for each command — the man pages are well-written and contain edge cases we did not cover here.
Finally, remember that no command is a silver bullet. Interactive rebase should not be used on shared branches. Bisect requires a reliable test. Stash is for short-term interruptions. Cherry-pick should not become your default merge strategy. And reflog is a safety net, not a backup system — push important branches to the remote regularly. Master these commands, and you will move from a Git user to a Git power user.
Comments (0)
Please sign in to post a comment.
Don't have an account? Create one
No comments yet. Be the first to comment!