Removing Git LFS From A Project

How to remove Git LFS from your project's history

published: 

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. Git 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:

version https://git-lfs.github.com/spec/v1
oid sha256:d7cbc07ce9b78a89764c0ac5e9c8e1b9dbdeb42c30d8396cba8f75aace5ba225
size 145930

When we remove Git LFS from a project, we also remove the separate storage location holding all these files, leaving the pointers scattered throughout the project's history, pointing to files that no longer exist, and deleting any chance of allowing new people to clone or build older versions of the project.

To successfully uninstall Git LFS, we need to rewrite the project's history, replacing all the LFS pointer files with the actual file they pointed to. In the end the project will look as if Git 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 Git LFS, ensuring any screw-ups with the next step are recoverable.

Step: 1 - Rewrite History

For simple projects, filter-branch can easily replace all the LFS pointer files in our project. 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 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"
   fi
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 Git 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 Git 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 Git 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 Git LFS cache folder inside the working directory of your project.