Git tips and tricks I'd teach every developer on day one
Git has a reputation for being scary, and it's earned in one specific way: the commands that rescue you are different from the commands you learned first, and nobody mentions them until you've already panicked. Once you know a dozen of them, mistakes stop feeling permanent.
These are the tricks I'd hand to someone on their first day on a team. Every command here is standard Git, and I've kept the explanations short. Try them in a scratch repository first, because the best time to learn how to undo something is before you need to.
See what you're about to do
A lot of Git trouble comes from committing something you didn't mean to. Three habits fix most of it.
Check the state before anything else:
git status -sb
The -sb flags give a short view with the branch name and whether you're ahead or behind the remote. It's the command I run more than any other.
Stage in pieces, not all at once:
git add -p
This walks through each changed chunk and asks whether to stage it. It's the easiest way to make small, focused commits from a messy working directory, and it forces you to read your own diff. Press y to stage, n to skip, s to split a big chunk into smaller ones.
Look at what's staged before committing:
git diff --staged
If you only learn one review habit, make it this one. You'll catch the debug statement, the stray file, and the accidentally pasted token before they enter history.
Undo things without fear
Git is very good at remembering, which means most mistakes are recoverable if you know where to look.
Throw away uncommitted changes to one file:
git restore path/to/file
Unstage a file you added by mistake, keeping your edits:
git restore --staged path/to/file
The older commands git checkout -- file and git reset HEAD file do the same jobs and are more confusing. The newer restore and switch commands split the overloaded checkout into clearer pieces, so use them if your Git is recent enough.
Fix the last commit's message or add a forgotten file to it:
git commit --amend
Only do that on commits you haven't pushed, or on a branch that only you use. Amending rewrites the commit, and that causes trouble for anyone who already has the old one.
Undo a commit that's already been shared, without rewriting history:
git revert <commit>
This creates a new commit that reverses the old one. It's the polite way to undo something on a shared branch.
Move the branch back a step while keeping your changes in the working directory:
git reset --soft HEAD~1
Use --soft to keep changes staged, the default --mixed to keep them unstaged, and --hard only when you truly want them gone. The word hard means it, so check git status before using it.
The safety net: reflog
This is the trick that saves careers. Git keeps a private log of everywhere your branch tips have been, called the reflog, and it lasts for weeks by default.
git reflog
Say you ran a reset --hard and lost commits, or deleted a branch with unmerged work. The reflog lists recent positions of HEAD, each with a hash. Find the entry from before the mistake and go back to it:
git switch -c rescued-work <hash>
Commits aren't deleted when they become unreachable. They're just hard to find, and the reflog is the map. The first time it works for you, you'll stop being nervous about experimenting.
Find the commit that broke it
Something worked last month and doesn't now, and there are 200 commits in between. Don't read them. Let Git do a binary search:
git bisect start
git bisect bad # the current commit is broken
git bisect good v1.4.0 # a commit or tag you know was fine
Git checks out a commit halfway between. You test it, then tell Git the result with git bisect good or git bisect bad, and it narrows the range. About eight steps cover 200 commits. When it finishes, it names the first bad commit, and you run git bisect reset to go home.
If you can write a command that exits with success or failure, Git will run the whole search for you:
git bisect run ./vendor/bin/pest --filter=InvoiceTest
That's the kind of thing that turns an afternoon of guessing into five minutes.
Two companions are worth knowing. git blame -L 40,60 path/to/file shows who last changed each line in a range, and when. And the pickaxe search finds commits that added or removed a specific string:
git log -S"calculateTotal" --oneline
When someone asks "when did this function disappear?", that's the answer.
Keep history readable
Reviewers appreciate a branch whose commits tell a story. A few commands make that cheap.
Make a fixup commit that targets an earlier one, then fold it in automatically:
git commit --fixup <hash-of-earlier-commit>
git rebase -i --autosquash main
Git marks the fixup commits in the interactive rebase list and places each beside its target. You review the list and save. It's a clean way to apply review feedback without a pile of "fix typo" commits.
Rebase interactively to reorder, squash, or reword commits before you open a pull request:
git rebase -i main
The same warning as for amending applies. Rewrite commits that only you have. Once others have based work on them, leave history alone.
When you do need to push rewritten history to your own branch, prefer the safer form of force:
git push --force-with-lease
A plain --force overwrites whatever is on the remote. --force-with-lease refuses if someone else pushed in the meantime, which protects you from erasing a teammate's work.
Teach Git to remember how you resolved conflicts, so repeated rebases don't make you fix the same one twice:
git config --global rerere.enabled true
Work on two things at once
Everyone has stashed half-finished work to fix an urgent bug and then forgotten what was in the stash. Two better options exist.
The stash is fine for short interruptions. Include untracked files, and give it a name:
git stash push -u -m "wip: invoice export"
git stash list
git stash pop
For longer detours, use a worktree: a second working directory attached to the same repository, on a different branch.
git worktree add ../hotfix-login hotfix/login
You now have ../hotfix-login checked out on its own branch, with no stashing and no switching. When you're done, git worktree remove ../hotfix-login. Worktrees also suit running several coding agents in parallel, each in its own directory, a pattern I mention in a practical Claude Code workflow.
Look around faster
A compact graph of the repository answers most "what's going on here?" questions:
git log --oneline --graph --decorate --all -20
Compare your branch with the main one, showing only what you'd add:
git diff main...HEAD
The three-dot form compares from the point where your branch split off, so you see your changes without noise from whatever landed on main since.
See what a teammate's branch changed, file by file, without checking it out:
git diff --stat main origin/feature/export
Search all of history for a word, restricted to one path:
git log -p -S"retry" -- app/Services
Clean up safely
Remove untracked files and directories, but look first:
git clean -nd # dry run: shows what would be deleted
git clean -fd # actually deletes
The dry run is the whole point. People lose files to git clean -fd because they skipped it. Note that ignored files are left alone unless you add -x, and you'll rarely want that.
Delete local branches that were merged into main:
git branch --merged main
git branch -d old-feature
Prune remote-tracking branches that were deleted on the server:
git fetch --prune
Aliases that earn their keep
Put short names on the commands you type most. In ~/.gitconfig:
[alias]
st = status -sb
co = switch
lg = log --oneline --graph --decorate -20
last = log -1 --stat
unstage = restore --staged
amend = commit --amend --no-edit
Pick your own. The test of a good alias is that you'd miss it after a week without it.
Three disasters and how to get out of them
Knowing the commands is one thing, and recognizing the situation is another. These are the three messes I see most often.
You committed on the wrong branch. Maybe you meant to work on a feature branch and committed straight onto main. Don't panic and don't copy files around. Create a branch at your current position so the commits are safe, then move the original branch back:
git branch feature/export # new branch pointing at your commits
git reset --hard origin/main # put main back where the remote has it
git switch feature/export
Check git status and git log before the reset --hard, since that command discards uncommitted work in your directory. The commits you made still exist on the new branch.
You pushed a secret. Rotate the credential first, because everything else is cleanup. After that, rewriting history with git filter-repo removes it from your repository's past, and you'll need to force-push and ask teammates to re-clone. Forks and caches may still hold a copy, which is why rotation comes first.
A merge went badly and you want out. If you're in the middle of one, git merge --abort returns you to the state before it started. If it finished and you haven't pushed, git reset --hard ORIG_HEAD jumps back to where you were before the merge. If you did push, use git revert -m 1 <merge-commit> to undo it with a new commit.
In each case, the reflog is the backstop. If a command did something you didn't expect, run git reflog before you try anything else, and write down the hash from before the mistake.
Git and AI coding agents
If you work with a coding assistant, Git becomes your safety harness, and a few habits make that work well.
Commit before you let an agent loose, so a clean state exists to return to. Ask it to work on a branch, and keep tasks small enough that each produces a diff you can read in a few minutes. Review with git diff and git add -p, accepting hunks deliberately, and don't approve a whole change because it looks tidy. If an agent goes in a bad direction, git restore . or git reset --hard followed by a better prompt costs you nothing when the last good commit is fresh.
Worktrees pair nicely with agents, because each one gets its own directory and branch, and parallel tasks can't trip over each other. The security side matters as well: keep agents away from credentials, and don't let them force-push to shared branches. The risks are laid out in slopsquatting, leaked secrets, and other AI coding risks.
Habits that matter more than any command
A few practices have more effect than the tricks above.
Commit small and often. Small commits are easy to review, easy to bisect, and easy to revert.
Write messages for the person who'll read them in a year, which is often you. A subject line under about 70 characters in the imperative mood ("Fix rounding in invoice totals"), then a blank line, then a short explanation of why if the reason isn't obvious.
Pull or fetch before you start work, and push before you close the laptop. Work that only exists on your machine isn't backed up.
Never commit secrets. Put them in environment files that are ignored, use a pre-commit scanner, and remember that deleting a leaked key from the latest commit doesn't remove it from history. The steps are in 543,000 valid credentials sat in public GitHub repos.
Review AI-generated changes the way you'd review a colleague's. git add -p and git diff --staged are your best tools for that, and they pair well with a checklist for reviewing AI-generated code.
Where to go from here
Pick two things from this list and use them for a week. git add -p and git reflog are my suggestions, since the first makes you a more careful committer and the second makes you braver. Then add bisect the next time a bug has an unknown start date.
Git rewards a small investment more than almost any other tool you use daily, and none of the commands above are hard. They're just not the ones people show you first.