Advanced Git Strategies
Git is far more than just commit, push, and pull. When working in complex software engineering teams, advanced Git commands become essential for recovering lost work, managing complex branching strategies, and collaborating safely without breaking the main codebase. In this tutorial, we will explore some of the most powerful and underutilized features of Git, including Reflog, Rebase, Cherry-Pick, and Bisect.
1. Git Reflog: The Ultimate Safety Net
Have you ever accidentally deleted a branch, performed a hard reset, or messed up a rebase and lost your commits? git reflog is your ultimate safety net. While standard Git history (git log) only shows commits reachable from your current branch, the reflog (reference log) records every time your HEAD or branch references move, even if those commits are no longer part of the official history.
git reflog
When you run the command above, you will see output that looks like this:
e1b2c3d HEAD@{0}: commit: Add advanced features
f4g5h6i HEAD@{1}: reset: moving to HEAD~1
a1b2c3d HEAD@{2}: checkout: moving from feature to main
If you realize you shouldn't have done that reset at HEAD@{1}, you can simply reset your branch back to the state it was in before the mistake by referencing the SHA or the HEAD pointer:
git reset --hard e1b2c3d
This brings your lost commit back to life. Keep in mind that reflog entries expire after a certain period (usually 90 days), so don't wait too long to recover lost work!
2. Git Rebase: Keeping History Clean
Merging branches is the standard way to integrate changes, but it can lead to a messy, non-linear history filled with "Merge branch X into Y" commits. Rebasing rewrites your branch's history so that it appears as if you created your commits sequentially on top of the latest changes from the target branch.
To rebase your current feature branch onto main, run:
git checkout feature-branch
git rebase main
During a rebase, Git temporarily sets aside your commits, updates your branch to match `main`, and then reapplies your commits one by one. If there are conflicts, Git will pause and ask you to resolve them. Once resolved, use:
git add .
git rebase --continue
Warning: Never rebase commits that have already been pushed to a public repository shared with other developers. Rebasing rewrites history, and changing shared history will cause massive synchronization headaches for your team.
3. Git Interactive Rebase: Squashing Commits
Interactive rebasing allows you to modify commits before they are finalized. You can reorder, edit, or squash multiple small commits into a single, cohesive commit. This is excellent for cleaning up a messy local history before opening a Pull Request.
git rebase -i HEAD~4
This opens an editor with your last 4 commits. You can change the word pick to squash (or s) for the commits you want to merge into the one above them. When you save and close the editor, Git will prompt you to write a new commit message for the combined commit.
4. Git Cherry-Pick: Selectively Applying Commits
Sometimes you make a commit on the wrong branch, or you need to apply a specific hotfix from a feature branch directly into `main` without merging the entire feature. Cherry-picking allows you to grab a specific commit by its SHA hash and apply it to your current branch.
git checkout main
git cherry-pick 9f8e7d6
This creates a new commit on `main` that introduces the exact same changes as commit `9f8e7d6`. Use this sparingly, as it duplicates commits (which can complicate future merges), but it is invaluable for surgical hotfixes.
5. Git Bisect: Finding the Bug
When a bug is introduced into the codebase but you have no idea which commit caused it, finding the culprit manually can take hours. git bisect uses a binary search algorithm to quickly isolate the exact commit that introduced the bug.
To start a bisect session:
git bisect start
git bisect bad # Mark current commit as broken
git bisect good 1a2b3c4 # Mark a known-good older commit
Git will automatically check out a commit halfway between the good and bad commits. You test your application to see if the bug exists:
- If the bug is present, type
git bisect bad. - If the bug is NOT present, type
git bisect good.
Git will continue halving the search space until it pinpoints the exact commit that broke the code. Once you're done, run git bisect reset to return to your original state.
Written by A.M. Rinas