Photo by Yosuke Ota on Unsplash

On this page

This chapter follows Working with remotes.

Mistakes are normal: wrong files in a commit, work half-done when a hotfix arrives, a bad merge on a shared branch. Git separates moving refs, changing the index and working tree, and adding new commits that undo old ones. Pick the tool that matches whether history is already public.

Stash

When you must switch branches but are not ready to commit:

Terminal window
git stash push -u -m "WIP login form"
git stash list
git stash apply
git stash pop
git stash clear

-u includes untracked files. apply keeps the stash entry; pop applies and removes it. Stash is a stack of WIP snapshots, not a substitute for small commits on a feature branch.

Amend the latest commit

Forgot a file in the last commit?

Terminal window
git add forgotten.ts
git commit --amend

Git replaces the tip commit with a new one (new hash). Safe only if you have not pushed that commit, or your team accepts rewriting the remote branch.

Reset: move the branch and optional trees

git reset moves the current branch to a commit, then optionally updates index and working tree:

Mode Branch Index Working tree
--soft Moves Unchanged Unchanged
--mixed (default) Moves Matches commit Unchanged
--hard Moves Matches commit Matches commit
Terminal window
git reset --soft HEAD~1
git reset HEAD file.txt
git reset --hard origin/main

git reset HEAD file.txt unstages without moving the branch (same idea as git restore --staged). --hard discards local changes—use deliberately.

Diagram 1
flowchart TB
  subgraph before [Before reset --hard to older commit]
    B1[Branch at C3]
    WT1[Working tree has edits]
  end
  subgraph after [After reset --hard]
    B2[Branch at C1]
    WT2[Working tree matches C1]
  end
  before --> after

Restore without moving the branch

Terminal window
git restore --staged file.txt
git restore file.txt
git restore --source=HEAD~2 --staged --worktree path/

restore copies from a commit into the index and/or working tree while HEAD stays put.

Revert: new commit that undoes an old one

On shared branches, avoid rewriting history. git revert creates a commit that applies the inverse patch:

Terminal window
git revert abc1234
git revert -m 1 merge_commit_sha

Use -m 1 when reverting a merge commit (choose the mainline parent). Prefer revert over interactive rebase once others may have pulled the bad commit.

Undo a merge in progress or just finished

Terminal window
git merge --abort
git reset --merge ORIG_HEAD

ORIG_HEAD often points at HEAD before the last merge. reset --merge tries to preserve uncommitted changes outside conflict paths.

Cherry-pick

Copy one or more commits onto the current branch:

Terminal window
git log other-branch --oneline
git cherry-pick ec24ds24
git cherry-pick a1b2c3d d4e5f6a

Original commits stay on their branch; new commits with new hashes appear on HEAD. Typical uses: port a bugfix to release/2.x, salvage commits from an abandoned branch.

Diagram 2
gitGraph
  commit id: "base"
  branch fix
  checkout fix
  commit id: "fixA"
  checkout main
  commit id: "main1"
  checkout main
  commit id: "fixA_on_main"

Resolve conflicts like a merge, then git cherry-pick --continue.

Rewriting large history locally

Accidentally committed a huge binary across many commits? git filter-repo (preferred over deprecated filter-branch) removes paths from all history:

Terminal window
git filter-repo --path huge.bin --invert-paths

Rewrites every commit touching that path. Treat like force-push: coordinate with anyone who cloned the repo.

Next: Merging versus rebasing.

Search articles

Type a word to search published articles.

Open the search page