Ronalds Vilciņš

Recover From Common Git Mistakes

Recovering from Git mistakes starts with identifying what changed: the working tree, staging area, a local commit, or shared history. Inspect that state before choosing a command that moves or discards it.

Shell
git status --short
git diff
git diff --staged

Those views answer different questions. The first summarizes state, the second shows unstaged edits, and the third shows what the index is ready to commit.

For work you cannot reproduce, make a separate copy before experimenting. A recovery branch preserves committed work, but it does not automatically preserve uncommitted files.

Recover from Git mistakes by choosing the right layer

A useful starting distinction is:

SituationFirst tool to investigate
A staged change should remain editablegit restore --staged
A local commit seems lostgit reflog and a recovery branch
The latest unshared commit needs another filegit commit --amend
A shared commit needs to be undonegit revert
One file needs an older versiongit restore --source

The restore documentation distinguishes the working tree and index. Undoing a staging decision is a different operation from discarding the file’s contents.

Find a lost commit before moving another branch

Inspect the local reference history:

Shell
git reflog --date=iso

Identify the desired entry, then preserve that commit with a new branch. For example, if inspection shows that HEAD@{1} is the commit you need:

Shell
git branch recovery 'HEAD@{1}'
git log --oneline recovery -5

The selector is an example, not a universal answer. Your reflog entry may have another number, and unrelated operations can change which entry sits at that position.

The reflog manual describes this local history. It is useful for commits Git still knows about; it is not an indefinite backup of every edit or every deleted untracked file.

Creating the branch first gives you a reference to inspect before deciding whether to cherry-pick, reset, or keep working from it.

Add a file to the last unshared commit

When the most recent commit is still local:

Shell
git add path/to/file
git diff --staged
git commit --amend --no-edit

The commit documentation explains that amending creates a replacement commit. Other changes already staged will also be included, which is why checking the staged diff matters.

To change only the message without opening an editor in this example:

Shell
git commit --amend -m "Clarify the change"

For a commit already used by other people, a follow-up commit is often easier to coordinate than rewritten history.

Move an unshared commit off the wrong branch

Assume the latest commit was made on main, belongs on a new feature branch, and the working tree is clean:

Shell
git status --short
git branch feature-name
git reset --hard HEAD~1
git switch feature-name

The branch preserves the current commit before main moves back. The reset manual documents what --hard replaces; do not run this sequence over uncommitted work you intend to keep.

The empty status output is a precondition to check, not something the first command enforces. Also confirm that the commit has not been shared before rewriting the branch.

If the commit is already on shared main, keep that history intact. Starting on main, one example is:

Shell
git branch recovery-shared
git switch -c feature-from-main HEAD~1
git cherry-pick recovery-shared
git switch main
git revert --no-edit recovery-shared

This example assumes exactly one ordinary commit at the tip needs moving. It creates the feature branch before applying the change and then makes a new reversal commit on main.

A merge commit, a longer sequence, or intervening dependent work needs a different plan. Inspect the graph instead of substituting a random parent number.

Undo an older shared commit with a new commit

Inspect the history, identify the unwanted commit, and use its actual object ID. A concrete test case with one later commit can use:

Shell
git log --oneline -5
git revert --no-edit HEAD~1

Here HEAD~1 must be the commit you deliberately selected. It is only an illustrative position in the history.

Revert records a new commit rather than erasing the old one. Later edits can conflict with the reversal; review the result and run the project’s relevant checks before sharing it.

Restore a single file without resetting the branch

After finding the wanted version in the file’s history:

Shell
git log --oneline -- path/to/file
git restore --source=HEAD~1 -- path/to/file
git diff -- path/to/file
git add path/to/file
git commit -m "Restore the previous file version"

Replace HEAD~1 with the commit you inspected when the desired version lives elsewhere. This copies that version into the working tree; it does not move the current branch back to that commit.

Keep a separate copy of the current file first if its uncommitted contents matter. The older git checkout COMMIT -- path/to/file form still exists, but restore makes the intended operation easier to name.

Match a remote branch only after preserving local work

For a deliberate reset to the remote’s main:

Shell
git fetch origin
git switch main
git branch backup-before-reset
git reset --hard origin/main
git clean -d -n

The backup branch preserves the previous committed tip. It does not save uncommitted changes, and the reset has already replaced tracked working-tree content before the clean preview runs.

The final command only previews untracked-file deletion. If that exact list is disposable, the destructive version is:

Shell
git clean -d -f

The clean documentation explains the preview, force flag, and ignored-file behavior. This example does not add -x, so it does not request deletion of ignored files.

I would keep the inspection and preservation steps even when the eventual fix is one command. Recovery is much easier when the target and the work worth keeping are both explicit.

Filed under on

Last updated on

← Back to all articles