How to un-commit files in git
There’s a few situations that can arise when you’d want to reverse committing a file to your repo:
- Accidentally committing secrets
- Accidentally committing data or artifacts and running into a Git LFS warning when pushing
- Accidentally committing a Jupyter notebook with absurdly large outputs which result in slow diffs
However you’ve landed in this position, it’s luckily not too hard to get out of! But it requires going a bit beyond the basics of Git and understanding a bit more about how Git works.
Git’s tracking architecture optimizes for tracking changes between snapshots. When I learned about Git initially, I incorrectly assumed it was just like Dropbox or other versioned file storage systems, and it took a while to un-learn this.
Each commit is a snapshot of the repository’s state (referenced by a random SHA such as
d85e8fe...
), and Git internally optimizes storage by storing differences (deltas) between these
states (represented as the added/removed lines across each file in the codebase). These diffs can be
thought of as patches which can be applied to move from one commit to another, enabling rapid
traversal of codebase versions and branches by following the tree of commits!
I made changes to my codebase, and I just want to go back to the HEAD
commit
Save your uncommitted changes with git stash
, so that you can apply them later with git stash pop
. This reverts the codebase to the last commit, and most people usually learn this early on
their Git journey.
But if I’ve already git add
-ed some changes (staged my changes), this command will unstage all
changes without deleting anything in my codebase:
1git reset HEAD
But if you’d prefer to delete changes made beyond the last commit:
1git reset --hard HEAD
I committed something I shouldn’t have a few commits ago, help!
Lets assume we’re working with a codebase with an output of git log --oneline -n 5
:
d85e8fe (HEAD -> main) add even more code
14bfedd edit file I shouldn't have added
f18083a add code
b425f55 add file I shouldn't have added
974d26b (origin/main, origin/HEAD) add some code
I’ve made 5 commits before pushing them to Github, two of them (b425f55
and 14bfedd
) touch a
file I shouldn’t have added. I can list the commits that touch the file I want to remove using
1git log --oneline --follow -- <file_path>
Which gives me the output
14bfedd edit file I shouldn't have added
b425f55 add file I shouldn't have added
Lets edit the commits starting from the earliest commit in that result:
1git rebase -i b425f55^
This starts an interactive rebase (starting from the parent of the first bad commit), which opens up a text editor to the following:
pick d85e8fe add even more code
pick 14bfedd edit file I shouldn't have added
pick f18083a add code
pick b425f55 add file I shouldn't have added
# Rebase 974d26b..d85e8fe onto 974d26b (4 commands)
#
# Commands:
# ...
We’ll change pick
to edit
for the commits we’ve selected, then save and quit.
Our first stop is the first bad commit b425f55
. At this stop we’ll
- Remove the bad file (and make the edits we need to this snapshot)
- Update the commit message with
git commit --amend
- Continue the rebase with
git rebase --continue
Our next stop will be the next bad commit 14bfedd
. We’ll repeat the steps above
There may be some conflicts that need to be resolved. This will take a bit of time, but it’ll work out if you look through them slowly and methodically.
After the rebase is done, all the commits are back in order, and we can safely git push
out code
now! (If we’ve already pushed commits, we’ll need to do a git push --force
to edit the history
on Github, which is a destructive action).
Prevention is the best cure
The awkwardness of Git is a great incentive to do things the right way. Operations with Git that go outside the developers knowledge can be hazardous in some cases. I think it’s worth learning a little bit about what’s happening under the hood, but avoiding these situations is also a smart thing to do. There are a few pre-commit hooks that might be worth checking out:
- Strip notebook output before committing:
nbstripout
- Detect secrets and prevent commits:
detect-secrets
- Commit large files with Git LFS
- Above all else, make sure you prevent git from tracking specific files and dirs with a good
.gitignore
file ;)