GitReal-World Scenarios

Real-World Git Scenarios

A practical problem-to-solution cookbook for the messy situations every developer eventually hits. Each scenario describes the situation in plain language, explains what is happening, and gives you the exact commands to fix it. Find your problem, copy the commands, understand why they work.

Warning
Several commands here rewrite history or discard work permanently (`reset --hard`, `push --force`, `clean -fd`). If a branch has already been pushed and shared, rewriting its history will break other people's clones. When in doubt, prefer the non-destructive option (revert over reset, --force-with-lease over --force) and make a backup branch first: `git branch backup-before-fix`.
I committed to the wrong branch

You meant to commit on a feature branch but you were on main. The fix: move the commit(s) to the correct branch, then reset the wrong branch back. This works as long as you have not pushed the bad commit.

Move the last commit to a new (or existing) branch

Bash
# You are on main with a commit that belongs on a feature branch.

# Option A: the target branch does not exist yet
git branch feature-x          # create feature-x pointing at the bad commit
git reset --hard HEAD~1       # move main back one commit
git switch feature-x          # your commit is now safely on feature-x

# Option B: the target branch already exists
git switch feature-x
git cherry-pick main          # copy main's tip commit onto feature-x
git switch main
git reset --hard HEAD~1       # remove it from main
I need to undo my last commit but keep the changes

The commit was premature but the code is fine. A soft (or mixed) reset moves the branch pointer back without throwing away your work — the changes return to the staging area or working tree.

Undo a commit, keep the changes

Bash
# Keep changes staged (ready to re-commit)
git reset --soft HEAD~1

# Keep changes in the working tree, unstaged (the default)
git reset --mixed HEAD~1
# same as:
git reset HEAD~1
I committed a secret or password

The single most important step is not a Git command: rotate the credential immediately. Assume it is compromised the moment it is committed. Then remove it from history. How you remove it depends on whether you have pushed.

Remove a secret from history

Bash
# 1. ROTATE THE SECRET FIRST. Treat it as leaked.

# If you have NOT pushed yet and it is only in the last commit:
git rm --cached .env
echo ".env" >> .gitignore
git commit --amend --no-edit

# If it is deeper in history (or already pushed), use git-filter-repo:
pip install git-filter-repo
git filter-repo --path .env --invert-paths
# or replace just the secret string everywhere:
git filter-repo --replace-text <(echo 'mySecret123==>REMOVED')

# Then force-push every branch; all collaborators must re-clone:
git push origin --force --all
Warning
Removing a secret from history does NOT un-leak it. Anyone who already fetched the commit, and any CI logs or forks, still contain it. Rotation is the only real fix; history rewriting just stops further exposure.
I need to combine my last 3 commits into one

You made several "wip" commits and want a single clean commit before opening a PR. Interactive rebase lets you squash them together.

Squash the last 3 commits

Bash
git rebase -i HEAD~3

# An editor opens with:
#   pick a1b2c3 first commit
#   pick d4e5f6 second commit
#   pick 7g8h9i third commit
#
# Change the 2nd and 3rd "pick" to "squash" (or "s"):
#   pick   a1b2c3 first commit
#   squash d4e5f6 second commit
#   squash 7g8h9i third commit
#
# Save and close. A second editor lets you write the combined message.

# Fast alternative if you just want one commit on top of main:
git reset --soft HEAD~3
git commit -m "Single clean commit message"
I accidentally ran git reset --hard and lost work

Do not panic. As long as the lost commits existed at some point, the reflog remembers where HEAD was. The reflog is your time machine for local history.

Recover with the reflog

Bash
git reflog
# You'll see something like:
#   a1b2c3 HEAD@{0}: reset: moving to HEAD~3
#   d4e5f6 HEAD@{1}: commit: the work you thought was gone
#   ...

# Restore the branch to the state before the reset:
git reset --hard HEAD@{1}

# Or, safer, inspect first then create a branch at that point:
git checkout -b recovered d4e5f6
Tip
Reflog entries for reachable commits stick around for ~90 days by default. Even "deleted" commits are recoverable until garbage collection runs, so check the reflog before assuming work is gone.
I need to change a commit message I already pushed

Amending changes the commit's SHA, so the remote and your local copy diverge. You must force-push — use --force-with-lease so you do not clobber commits a teammate pushed in the meantime.

Reword the last (pushed) commit

Bash
git commit --amend -m "Correct commit message"
git push --force-with-lease

# To reword an OLDER commit, use interactive rebase and mark it "reword":
git rebase -i HEAD~4
# change "pick" to "reword" on the target line, save, edit the message
git push --force-with-lease
Warning
Only rewrite shared history when you have coordinated with your team. If others have based work on the old commits, force-pushing will create painful divergence for them.
I want to pull but have local uncommitted changes

Git refuses to pull when uncommitted changes would be overwritten. Stash them, pull, then reapply. The stash is a temporary shelf for your in-progress work.

Stash, pull, reapply

Bash
git stash push -m "wip before pulling"
git pull --rebase
git stash pop      # reapply your changes on top of the freshly pulled code

# If 'pop' produces conflicts, resolve them, then:
git add <resolved-files>
git stash drop     # remove the stash entry once applied cleanly
My merge went wrong, I want to abort

Mid-merge you realise the conflicts are a mess and you want to back out completely, as if you never started. Most in-progress operations have an --abort escape hatch.

Abort an in-progress operation

Bash
git merge --abort        # restore to pre-merge state
git rebase --abort       # bail out of a rebase
git cherry-pick --abort  # bail out of a cherry-pick

# If a merge already completed and you want to undo it:
git reset --hard ORIG_HEAD   # ORIG_HEAD points to where you were before the merge
I need to apply one commit from another branch

A bug fix lives on another branch and you need just that one commit on your current branch without merging everything. That is exactly what cherry-pick is for.

Cherry-pick a single commit

Bash
git switch my-branch
git cherry-pick a1b2c3d              # apply that commit here (new SHA)

# Multiple commits at once:
git cherry-pick a1b2c3d f4e5g6h

# A continuous range (exclusive of the first):
git cherry-pick startHash..endHash

# Apply the changes but don't commit yet (lets you tweak first):
git cherry-pick -n a1b2c3d
I committed as the wrong author or email

Your global Git config had the wrong name or email and it ended up on a commit. Fix the config first, then rewrite the affected commit(s).

Fix the author on commits

Bash
# Fix your config going forward:
git config --global user.name "Correct Name"
git config --global user.email "correct@email.com"

# Fix only the last commit:
git commit --amend --reset-author --no-edit

# Fix the author across many past commits with filter-repo:
git filter-repo --commit-callback '
  if commit.author_email == b"wrong@email.com":
      commit.author_email = b"correct@email.com"
      commit.author_name  = b"Correct Name"
'
I need to find which commit introduced a bug

The bug exists now but worked in some older version. git bisect does a binary search through history — you mark commits good or bad and it zeroes in on the culprit in a logarithmic number of steps.

Binary-search for the bad commit

Bash
git bisect start
git bisect bad                 # current commit has the bug
git bisect good v1.0.0         # this older tag was fine

# Git checks out a midpoint commit. Test it, then:
git bisect good                # ...if the midpoint is fine
git bisect bad                 # ...if the midpoint has the bug
# Repeat until Git prints "<sha> is the first bad commit".

git bisect reset               # return to where you started

# Automate it with a test command (exit 0 = good, non-zero = bad):
git bisect start HEAD v1.0.0
git bisect run npm test
I want to temporarily switch tasks

An urgent fix lands while you are mid-feature. You can shelve your work with stash, or — better when you need both checked out at once — use a second working tree with worktree.

Stash vs worktree

Bash
# Quick context switch on the same checkout:
git stash push -u -m "wip feature"   # -u also stashes untracked files
git switch hotfix
# ...do the hotfix...
git switch feature
git stash pop

# Keep BOTH active at the same time in separate folders:
git worktree add ../hotfix-tree hotfix
# now ../hotfix-tree is a full checkout of the hotfix branch
git worktree remove ../hotfix-tree   # clean up when done
I deleted a branch and need it back

You ran git branch -D and immediately regretted it. The branch pointer is gone but the commits still exist and the reflog knows the SHA they pointed to.

Restore a deleted branch

Bash
# Find the tip commit of the deleted branch:
git reflog
# look for the last "checkout: moving from <branch>" or commit on it

# Recreate the branch at that commit:
git branch recovered-branch a1b2c3d

# If nothing useful in reflog, search dangling commits:
git fsck --no-reflogs --lost-found
# then: git branch recovered-branch <dangling-sha>
I need to split a large commit into smaller ones

One giant commit mixes unrelated changes and reviewers hate it. Use interactive rebase to stop at that commit, undo it, and re-commit in logical pieces.

Split one commit into several

Bash
git rebase -i HEAD~3
# Mark the oversized commit with "edit" instead of "pick", save.

# Git pauses ON that commit. Undo it but keep the changes:
git reset HEAD~1

# Now stage and commit in coherent chunks (use -p to pick hunks):
git add -p src/feature-a.js
git commit -m "feat: add feature A"
git add src/feature-b.js
git commit -m "feat: add feature B"

# When the working tree is clean, resume the rebase:
git rebase --continue
The escape-hatch mindset
Almost every Git mistake is recoverable because Git rarely deletes data immediately — it just moves pointers. Before any risky operation, two habits save you: make a throwaway backup branch (`git branch backup`), and remember that `git reflog` records every position HEAD has had. If you can find the SHA, you can get the work back.
Tip
Build muscle memory for the recovery commands now, while things are calm. The worst time to learn `git reflog` is during a production incident with a panicking team watching over your shoulder.
Quick scenario index
  • Wrong branch? → create branch at HEAD, then reset the wrong branch back

  • Undo commit, keep code? → git reset --soft HEAD~1

  • Leaked secret? → rotate it, then git filter-repo to scrub history

  • Combine commits? → git rebase -i and squash

  • Lost work after reset --hard? → git reflog, then reset to HEAD@{n}

  • Bad pushed message? → git commit --amend, then push --force-with-lease

  • Pull with dirty tree? → git stash, pull, git stash pop

  • Merge gone wrong? → git merge --abort (or reset --hard ORIG_HEAD)

  • One commit from elsewhere? → git cherry-pick <sha>

  • Wrong author? → fix config, then git commit --amend --reset-author

  • Find the bug commit? → git bisect run <test>

  • Switch tasks? → git stash or git worktree add

  • Deleted branch? → git reflog, then git branch <name> <sha>

  • Commit too big? → git rebase -i, mark edit, reset, re-commit in pieces