Job support6 min read

    Git went wrong: how to get your work back

    How to recover from bad resets, the wrong branch, force pushes, detached HEAD and committed secrets, and what in Git is genuinely unrecoverable.

    The moment a Git command does something unexpected is genuinely unpleasant. Work appears to be gone, the branch looks wrong, and the instinct is to start trying commands until it looks right again — which is how a recoverable situation becomes an unrecoverable one.

    Almost everything in Git is recoverable. Committed work is very hard to destroy permanently. The exceptions are narrow and worth knowing precisely, because they are the only situations where speed matters.

    Before anything: stop and take a copy

    The single most useful habit when Git goes wrong:

    cd ..
    cp -r your-project your-project-backup
    

    Copy the entire folder, including the hidden .git directory. Thirty seconds, and now every subsequent experiment is safe. Whatever state you are in is preserved, and you can try things without the fear that makes people freeze.

    Do this before reading the rest of this article if you are in trouble right now.

    The thing that saves you: reflog

    Git keeps a log of everywhere your branch pointer has been, including states no longer reachable from any branch. This is what makes "lost" commits recoverable.

    git reflog
    

    You get something like:

    a1b2c3d HEAD@{0}: reset: moving to HEAD~3
    e4f5g6h HEAD@{1}: commit: add invoice validation
    i7j8k9l HEAD@{2}: commit: wire up the parser
    

    Every line is a state your repository was in. To go back to one:

    git reset --hard HEAD@{1}
    

    Or, more cautiously, look first without changing anything:

    git checkout HEAD@{1}
    

    That puts you in a detached state where you can inspect and copy what you need, then return with git switch -.

    Reflog entries expire, typically after ninety days for reachable commits and thirty for unreachable ones. In practice you have plenty of time, but it is not forever.

    The common disasters, and their fixes

    "I committed to the wrong branch"

    Nothing is lost. Move the commit.

    git log --oneline -1          # note the commit hash
    git reset --hard HEAD~1       # remove it from this branch
    git switch correct-branch
    git cherry-pick <hash>
    

    If you have several commits to move, cherry-pick them oldest first, or use a range.

    "I reset --hard and lost my commits"

    This is the classic panic, and it is fully recoverable. Find the state in reflog and reset back to it:

    git reflog
    git reset --hard HEAD@{1}
    

    The important caveat: reset --hard also discards uncommitted changes, and those are not in the reflog. Uncommitted work is the genuinely fragile category — see the section below.

    "I need to undo a commit that is already pushed"

    Do not rewrite history that other people have pulled. Add a commit that reverses it:

    git revert <hash>
    

    This creates a new commit undoing those changes. History stays intact, everyone's clone stays consistent, and the record shows what happened — which is usually what you want on a shared branch anyway.

    "I force-pushed over someone else's work"

    Recoverable, if you act reasonably quickly.

    The other person still has the old commits in their local clone and their reflog. Ask them not to pull. They can find the old state in their reflog and push it back.

    If you have the old hash yourself — check your own reflog, or your terminal scrollback, or the CI logs which often record commit hashes — you can restore it directly.

    This is why --force-with-lease exists. It refuses the push if the remote has moved since you last fetched, which turns this disaster into an error message. Make it your habit; there is no downside.

    "I have a merge conflict and I have made it worse"

    Abandon the attempt and start again:

    git merge --abort      # or: git rebase --abort
    

    This returns you to the state before the merge started. Conflicts are much easier from a clean start than from a half-resolved mess.

    If you have already committed the bad resolution, reset back to before the merge — find it in reflog — and redo it.

    "I am in a detached HEAD state"

    Usually harmless and easily reversed. You checked out a commit rather than a branch.

    If you changed nothing:

    git switch -
    

    If you made commits and want to keep them, give them a branch before leaving:

    git switch -c rescue-branch
    

    Now they are on a named branch and safe.

    "I committed a secret"

    This is the one where speed genuinely matters, and the priority order surprises people.

    First, rotate the credential. Immediately, before touching Git. If it was pushed anywhere others can reach, assume it is compromised — automated scanners find keys in public repositories within minutes, and even a private repository has been seen by everyone with access.

    Then clean the history. Removing it from the latest commit is not enough; it is still in the history. You need a history rewrite across every affected commit, using a purpose-built tool, followed by a force push and everyone re-cloning.

    But rewriting history does not un-leak a secret that was already visible. Rotation is the fix. History cleaning is hygiene.

    The genuinely fragile category: uncommitted work

    Everything above works because the work was committed. Git protects committed work well. It does not protect what you never committed.

    These destroy uncommitted changes with no reflog entry and no recovery:

    • git checkout -- <file> and git restore <file> — discards your edits to that file
    • git reset --hard — discards all uncommitted changes
    • git clean -fd — deletes untracked files entirely
    • git stash drop and git stash clear — though a dropped stash is briefly recoverable via its hash, if you can find it

    The defence is a habit rather than a command: commit early and often on your own branch. Commits are cheap, private until you push, and you can tidy them later with an interactive rebase. A messy series of commits that preserves your work beats a clean history that lost it.

    If you are about to run something destructive and are not certain, stash instead of discarding:

    git stash push -m "before trying the risky thing"
    

    Stashes are recoverable. Discards are not.

    One partial rescue: if you edited files in an editor that keeps local history — several do — your changes may survive there even when Git has discarded them. Worth checking before giving up.

    Reading your way out instead of guessing

    Two commands that tell you where you actually are, which is usually the real problem:

    git status              # working tree and staging area
    git log --oneline --graph --all --decorate -20
    

    That second one draws the branch structure, showing where every branch and HEAD points. Most confusion is a mental model that has drifted from reality, and seeing the actual shape resolves it faster than any single fix.

    Also useful when you suspect you have lost something not in any branch:

    git fsck --lost-found
    

    This finds dangling commits — work that exists in the object store but nothing points to.

    The habits that prevent most of this

    • Commit frequently on your own branch. The best protection there is.
    • git status before anything destructive. Two seconds.
    • --force-with-lease, never plain --force.
    • Pull before you push, so you are rebasing or merging deliberately rather than under pressure.
    • A .gitignore that covers .env and credential files, so secrets never get staged in the first place.
    • Read what a command does before running it, particularly when copied from a search result. A worrying amount of Git advice online is reset --hard applied to problems that did not need it.

    Why this feels harder than it is

    Git is not difficult because the commands are complex. It is difficult because the mental model — a graph of commits, with branches as movable labels pointing into it — is rarely taught, and most people learn commands as spells instead.

    Once the graph is the thing you picture, the commands become obvious: reset moves a label, revert adds a commit, cherry-pick copies one, rebase replays them elsewhere. The panic mostly comes from operating a system whose state you cannot visualise.

    That is a very common gap, and an hour with someone drawing it out on your actual repository tends to fix it permanently. It is one of the more frequent things people bring to technical support — not a bug, just the tool that everyone is assumed to have absorbed and nobody was taught.

    Common questions

    Yes, in almost all cases. Git keeps a log of everywhere your branch pointer has been, including states no longer reachable from any branch. Run git reflog, find the entry from before the reset, and reset back to it. The exception is uncommitted changes, which are not recorded in the reflog and are genuinely lost.

    Rotate the credential immediately, before touching Git at all. If it reached anywhere others can access, assume it is compromised — automated scanners find keys in public repositories within minutes. Cleaning the history afterwards is good hygiene, but it does not un-leak something that was already visible.

    Uncommitted work. Commands like git checkout on a file, git reset --hard, and git clean -fd discard changes that were never committed, and nothing records them. That is why committing early and often on your own branch is the single best protection; commits are cheap, private until pushed, and can be tidied later.

    Want this looked at properly?

    Bring the actual blocker from the actual project. We will work through it with you so you understand the fix, not just the patch.

    Ask on WhatsApp

    Related services

    Keep reading

    All articles
    Job support8 min read

    Your first week in an unfamiliar codebase

    The instinct is to start reading files. On any real codebase you will read for three days and retain nothing. Do this instead.

    Read