Migrate a Git repository into Git LFS with BFG

WARNING: The following documentation is deprecated. We recommend using git lfs migrate instead of the method documented below.

Using Git LFS can help you to reduce the size of your Git repository and improve its performance.

However, adding the large files that are already in your repository to Git LFS doesn't actually reduce the size of your repository because the files are still referenced by previous commits.

Through the method described on this document, first migrate to Git LFS with a tool such as the open source community-maintained BFG through a mirror repository, then clean up the repository's history, and lastly create LFS tracking rules to prevent new binary files from being added.

This tutorial was inspired by the guide Use BFG to migrate a repository to Git LFS. For more information on Git LFS, see the references below.

WARNING: The method described on this guide rewrites Git history. Make sure to back up your repository before beginning and use it at your own risk.

Prerequisites

Before beginning, make sure:

  • You have enough LFS storage for the files you want to convert. Storage is required for the entire history of all files.
  • All the team members you share the repository with have pushed all changes. Branches based on the repository before applying this method cannot be merged.

To follow this tutorial, you need:

  • The Maintainer role for the existing Git repository you'd like to migrate to LFS with access through the command line.

  • Git and Java Runtime Environment (Java 7 or above) installed locally.

  • BFG installed locally:

    brew install bfg
  • Git LFS installed locally:

    brew install git-lfs

NOTE: This guide was tested on macOS Mojave.

Steps

Consider an example upstream project, git@gitlab.com:gitlab-tests/test-git-lfs-repo-migration.git.

  1. Back up your repository:

    Create a copy of your repository so that you can recover it in case something goes wrong.

  2. Clone --mirror the repository:

    Cloning with the mirror flag creates a bare repository. This ensures you get all the branches within the repository.

    It creates a directory called <repo-name>.git (in our example, test-git-lfs-repo-migration.git), mirroring the upstream project:

    git clone --mirror git@gitlab.com:gitlab-tests/test-git-lfs-repo-migration.git
  3. Convert the Git history with BFG:

    bfg --convert-to-git-lfs "*.{png,mp4,jpg,gif}" --no-blob-protection test-git-lfs-repo-migration.git

    It is scanning all the history, and looking for any files with that extension, and then converting them to an LFS pointer.

  4. Clean up the repository:

    # Change into the mirror repo directory:
    cd test-git-lfs-repo-migration.git
    
    # Clean up the repo:
    git reflog expire --expire=now --all && git gc --prune=now --aggressive

    You can also take a look on how to further clean the repository, but it's not necessary for the purposes of this guide.

  5. Install Git LFS in the mirror repository:

    git lfs install
  6. Unprotect the default branch, so that we can force-push the rewritten repository:

    1. Navigate to your project's Settings > Repository and expand Protected branches.
    2. Scroll down to locate the protected branches and select Unprotect the default branch.
  7. Force-push to GitLab:

    git push --force
  8. Track the files you want with LFS:

    # Change into the /tmp directory
    cd /tmp
    
    # Clone the repo
    git clone git@gitlab.com:gitlab-tests/test-git-lfs-repo-migration.git
    
    # Change into the upstream repo directory:
    cd test-git-lfs-repo-migration
    
    # You may need to reset your local copy with upstream's `main` after force-pushing from the mirror:
    git reset --hard origin/main
    
    # Track the files with LFS:
    git lfs track "*.gif" "*.png" "*.jpg" "*.psd" "*.mp4" "img/"
    
    # Push up changes to .gitattributes
    git add .gitattributes && git commit -m 'Track .gif,.png,.jpg,.psd,.mp4 and img/' && git push

    Now all existing the files you converted, as well as the new ones you add, are properly tracked with LFS.

  9. Re-protect the default branch:

    1. Navigate to your project's Settings > Repository and expand Protected branches.
    2. Select the default branch from the Branch dropdown list, and set up the Allowed to push and merge and Allowed to merge rules.
    3. Select Protect.

References