Removing Git LFS From A Project

How to remove Git LFS from your project's history


Git Large File Storage (LFS) is an extension to Git that separates large, frequently changing, binary files and saves them in a separate storage location outside of the normal Git project. LFS replaces these binary files with smaller text files, called pointers, that hold information about the original file and how to download them. These pointers look something like this:

oid sha256:d7cbc07ce9b78a89764c0ac5e9c8e1b9dbdeb42c30d8396cba8f75aace5ba225
size 145930

Git-LFS uses git hooks to transparently replace and create these pointers whenever we make a commit and saves the binary data outside of Git until the next push where it will sync with your LFS server. When everything is working correctly, we should never see these pointer files.

When we say "remove LFS from a project" we really mean removing the hooks from our project, which, if not done correctly, will leave these pointer files all throughout our project's history with no way to download the actual files they pointed to, preventing us from ever building an older release of our project again.

To successfully uninstall LFS, we'll need to rewrite the project's history, replacing all the LFS pointer files with the actual file they pointed to, adding them back into our project's history. In the end, the project will look as if LFS was never installed in the project at all.

Step: 0 - Make A Backup

Before we begin, rewriting history is remarkably dangerous, with many opportunities to silently break something you'll only find out about after it's too late. Make a backup before you start and save it until you're absolutely sure the project's history is correct.

git clone --mirror $URL backup && cd backup
git lfs fetch --all

Replacing $URL with the location of the project, these two commands will:

  1. Create a bare copy of the project and navigate into it.
  2. Download all the files in LFS from the remote LFS server.

This will give you a complete backup of the project, including branches and other refs like tags and notes, as well as download all the files from LFS, ensuring any screw-ups with the next step are recoverable.

Step: 1 - Rewrite History

In the working copy of our project, filter-branch can easily replace all the LFS pointer files for us. However this step will also change all the names of the commits (git objects) in your project, so references to specific commits like Fixed in a4749f3 will break. If you have a lot of these types of commits you might want to use more advanced tools like filter-repo.

git filter-branch -f --prune-empty --tree-filter '
[ -f .gitattributes ] &&  git rm -f .gitattributes
find . -type f | while read FILE; do
   if head -2 "$FILE" | grep -q "^oid sha256:"; then
      POINTER=$(cat "$FILE")
      echo -n "$POINTER" | git lfs smudge > "$FILE"
      git add "$FILE"
done' --tag-name-filter cat -- --all

This uses git filter-branch --tree-filter to go through each commit for every branch, and use find and grep to list every LFS pointer file so we can use git lfs smudge to replace the pointers with the LFS data, before committing the changes and moving to the next commit in the history.

Step: 2 - Remove Hooks & Filters

If this is the only project that uses LFS, you can remove the --local option from the following command to also remove the filters from your global Git config file ~/.gitconfig

git lfs uninstall --local

Once the pointer files have been replaced, we can remove the hooks and filters LFS used to read the pointer files every time we fetched, pushed or committed changes to the project, with this simple built-in LFS command.

Step: 3 - Remove the LFS cache

rm -r .git/lfs

Finally, if this is your working copy of the project, you can save some disk space by removing the LFS cache folder inside the git directory of your project.