Skip to content
EgyKode
Guided labgit

Git Recovery & History Surgery

Destroy work four different ways and get it back, then remove a secret from history and understand why that is not the fix.

Time
45 min
Level
Intermediate
Objectives
4 objectives
Cost
Free

Before you start

You will need

  • git 2.30+

You will be able to

  • Recover commits after a hard reset, a deleted branch or a bad rebase
  • Choose between revert, reset and restore deliberately
  • Remove a file from history, and know what that does not achieve

CostFree

— a local repository is enough.

Success criteria

0 of 4

The scenario#

You reset the wrong branch. A colleague force-pushed over your work. There is an API key in a commit from three weeks ago.

All three are recoverable, and knowing that changes how confidently you work.

Set up a repository to break#

Terminal
mkdir /tmp/git-recovery && cd /tmp/git-recovery && git init
for i in 1 2 3 4 5; do echo "line $i" >> file.txt; git add -A; git commit -qm "commit $i"; done
git log --oneline

1. reset --hard, undone#

Terminal
git reset --hard HEAD~3
git log --oneline          # two commits left
git reflog                 # every position HEAD has held
git reset --hard HEAD@{1}  # back

git reflog is the repository's own undo history. A commit stays reachable for about 90 days even when no branch points at it. Almost nothing done locally is truly destructive.

2. A deleted branch#

Terminal
git switch -c feature && echo work >> file.txt && git commit -qam "feature work"
git switch master
git branch -D feature          # "permanently" deleted
git reflog | grep feature      # the commit hash is still here
git switch -c feature-restored <hash>

3. An abandoned rebase#

Terminal
git rebase -i HEAD~3    # not available non-interactively; abort instead
git rebase --abort      # puts everything back exactly as it was

--abort is always available mid-rebase, and it is complete. Trying a rebase costs nothing.

4. revert, reset or restore#

The three are not interchangeable, and picking the wrong one on a shared branch is how you create the next problem:

CommandDoesUse when
git restore <file>Discards uncommitted changes to a fileYou edited something by mistake
git reset --hard <ref>Moves the branch, discards commitsThe work is local and unpushed
git revert <commit>Adds a commit undoing anotherThe commit is already pushed

Never reset a branch other people have pulled. revert is the shared-branch answer: history stays intact and everyone's clone still agrees.

Terminal
git revert --no-edit HEAD
git log --oneline -2        # the original and its revert, both present

5. A secret in history#

Terminal
echo "AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG" > .env
git add .env && git commit -qm "add config"
echo "more work" >> file.txt && git commit -qam "unrelated"
 
git log --oneline -- .env          # still there
git rm --cached .env -q && echo ".env" >> .gitignore
git commit -qm "stop tracking .env"
 
git log -p --all -S 'wJalrXUtnFEMI' | head -5   # the value is STILL in history

That last command is the point. Removing the file going forward does nothing about the commits that already contain it, and every clone has them.

Terminal
# git-filter-repo is the maintained tool; BFG is the alternative
pip install git-filter-repo
git filter-repo --path .env --invert-paths --force
git log --all --oneline -S 'wJalrXUtnFEMI'      # gone

The order that actually matters:

  1. Rotate the credential. Assume it is compromised the moment it was pushed. This is the only step that protects anything.
  2. Then rewrite history.
  3. Then force-push and tell everyone with a clone to re-clone.

Doing 2 without 1 is theatre. The key was in a public repository, in CI logs, in forks, and quite possibly in a scraper's database within minutes.

When it goes wrong#

reflog does not show the commit

Reflog is per-clone and local. If the work was only ever on another machine or a deleted remote branch, it is not here.

filter-repo refuses to run

It requires a fresh clone by default. Use --force only when you understand it is rewriting this working copy.

Colleagues' branches break after a rewrite

Expected — every commit hash changed. They must re-clone or rebase onto the new history.

The secret still appears on GitHub after rewriting

GitHub keeps unreferenced commits accessible for a while and caches PR views. Contact support to purge, and rotate regardless.

The concept behind it

Ready to try it without help?Do the challenge

Next up

Lab 7 of 58 on the project path

Professional Collaboration on GitHubProtect a branch, require review, and make the pipeline the thing that decides whether code can merge.45 minIntermediate

Previous: Git Branching & Collaboration