20

I have a git LFS repository checked out. All the binaries are pointers. I pulled the real binaries with:

git lfs pull --include some/binaries

I used the binaries and now I would like to "unpull" the binaries and convert them to the pointers again, so I can reclaim the disk space.

I didn't find any suitable command to do that, and mangling with .git/lfs/objects plus hard resets make me nervous.

Question: How to convert the tracked binaries back to pointers?

Edit:

Paulo Mattos
  • 18,845
  • 10
  • 77
  • 85
Jakub M.
  • 32,471
  • 48
  • 110
  • 179
  • Have you tried modifying `.gitattributes` file? Check this: https://rehansaeed.com/gitattributes-best-practices/#git-large-file-system-lfs – Rodrigo Feb 01 '22 at 14:20

3 Answers3

10

As you mentioned it's not been implemented yet in git LFS. Git LFS users are using scripts to achieve the same functionality. Here's one that works taken from your link.

#!/bin/bash

lfs_files=($(git lfs ls-files -n))
for file in "${lfs_files[@]}"; do        
    git cat-file -e "HEAD:${file}" && git cat-file -p "HEAD:${file}" > "$file"
done
    
rm -rf .git/lfs/objects

It simply creates a list of all files returned by git lfs ls-files and iterates through it to replace the file by its pointer. The last line deletes all git LFS objects from the local repository.

8ctopus
  • 2,617
  • 2
  • 18
  • 25
10

An open LFS issue suggested to run

git read-tree HEAD && GIT_LFS_SKIP_SMUDGE=1 git checkout -f HEAD

Note from the issue:

If you run that in a dirty working tree, you'll blow away your changes, so don't do that.

Running this worked for me even when using LFS inside a submodule.

David Liao
  • 653
  • 8
  • 18
0

It converts files back to pointers. Just like undo git lfs pull. :-)

#!/bin/bash

# ref: https://github.com/git-lfs/git-lfs/issues/1189#issuecomment-348013275
# ref: https://sabicalija.github.io/git-lfs-intro/

if [ $# -eq 0 ]; then
    echo "No input: quit."
    exit 1
fi

cur_size=$(du -sh . | awk -F " " '{print $1}')
for bfile in $@; do
    # to pointer
    # print file size
    f_size=$(du -sh $bfile | awk -F " " '{print $1}')
    mv $bfile $bfile.bak
    cat $bfile.bak | git lfs clean > $bfile
    rm $bfile.bak
    pt_size=$(du -h $bfile | awk -F " " '{print $1}')
    printf "%-30s: " $bfile
    printf "%s" $f_size
    printf " -> %s \n" $pt_size

    # delete cache in .git
    hash_value=$(cat $bfile | grep oid | cut -d ":" -f 2)
    cache_path=$(find . | grep $hash_value)
    if [ -z ${cache_path} ];then
        printf "#WARNING# oid can't find in cache: %s \n" $hash_value
    else
        rm $cache_path
        printf ">> Pruned cache at: %s \n" $cache_path
        git checkout -- $bfile
    fi
done

# report result
after_size=$(du -sh . | awk -F " " '{print $1}')
printf "Finish: Current directory size shrink"
printf "(%s" $cur_size
printf " -> %s).\n" $after_size
exit 0