Created
January 20, 2026 10:07
-
-
Save nirgeier/abf1778ddca662deb5b0fc386f639cfa to your computer and use it in GitHub Desktop.
LFS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| set -e | |
| # ------------------------------------------------------------------------- | |
| # Git LFS (Large File Storage) .gitattributes | |
| # ------------------------------------------------------------------------- | |
| # --- Images --- | |
| *.jpg filter=lfs diff=lfs merge=lfs -text | |
| *.jpeg filter=lfs diff=lfs merge=lfs -text | |
| *.png filter=lfs diff=lfs merge=lfs -text | |
| *.gif filter=lfs diff=lfs merge=lfs -text | |
| *.psd filter=lfs diff=lfs merge=lfs -text | |
| *.ai filter=lfs diff=lfs merge=lfs -text | |
| *.webp filter=lfs diff=lfs merge=lfs -text | |
| *.svgz filter=lfs diff=lfs merge=lfs -text | |
| *.tiff filter=lfs diff=lfs merge=lfs -text | |
| *.tif filter=lfs diff=lfs merge=lfs -text | |
| *.bmp filter=lfs diff=lfs merge=lfs -text | |
| *.ico filter=lfs diff=lfs merge=lfs -text | |
| *.jif filter=lfs diff=lfs merge=lfs -text | |
| *.jp2 filter=lfs diff=lfs merge=lfs -text | |
| *.jpx filter=lfs diff=lfs merge=lfs -text | |
| *.j2k filter=lfs diff=lfs merge=lfs -text | |
| *.j2c filter=lfs diff=lfs merge=lfs -text | |
| *.fpx filter=lfs diff=lfs merge=lfs -text | |
| *.pcd filter=lfs diff=lfs merge=lfs -text | |
| *.xcf filter=lfs diff=lfs merge=lfs -text | |
| *.tga filter=lfs diff=lfs merge=lfs -text | |
| *.dds filter=lfs diff=lfs merge=lfs -text | |
| *.exr filter=lfs diff=lfs merge=lfs -text | |
| *.hdr filter=lfs diff=lfs merge=lfs -text | |
| *.raw filter=lfs diff=lfs merge=lfs -text | |
| *.cr2 filter=lfs diff=lfs merge=lfs -text | |
| *.nef filter=lfs diff=lfs merge=lfs -text | |
| *.orf filter=lfs diff=lfs merge=lfs -text | |
| *.sr2 filter=lfs diff=lfs merge=lfs -text | |
| # --- Audio --- | |
| *.mp3 filter=lfs diff=lfs merge=lfs -text | |
| *.wav filter=lfs diff=lfs merge=lfs -text | |
| *.ogg filter=lfs diff=lfs merge=lfs -text | |
| *.m4a filter=lfs diff=lfs merge=lfs -text | |
| *.wma filter=lfs diff=lfs merge=lfs -text | |
| *.aac filter=lfs diff=lfs merge=lfs -text | |
| *.flac filter=lfs diff=lfs merge=lfs -text | |
| *.aiff filter=lfs diff=lfs merge=lfs -text | |
| *.ra filter=lfs diff=lfs merge=lfs -text | |
| *.mid filter=lfs diff=lfs merge=lfs -text | |
| *.midi filter=lfs diff=lfs merge=lfs -text | |
| *.aif filter=lfs diff=lfs merge=lfs -text | |
| *.ape filter=lfs diff=lfs merge=lfs -text | |
| *.opus filter=lfs diff=lfs merge=lfs -text | |
| # --- Video --- | |
| *.mp4 filter=lfs diff=lfs merge=lfs -text | |
| *.mov filter=lfs diff=lfs merge=lfs -text | |
| *.avi filter=lfs diff=lfs merge=lfs -text | |
| *.wmv filter=lfs diff=lfs merge=lfs -text | |
| *.flv filter=lfs diff=lfs merge=lfs -text | |
| *.webm filter=lfs diff=lfs merge=lfs -text | |
| *.mkv filter=lfs diff=lfs merge=lfs -text | |
| *.m4v filter=lfs diff=lfs merge=lfs -text | |
| *.mpg filter=lfs diff=lfs merge=lfs -text | |
| *.mpeg filter=lfs diff=lfs merge=lfs -text | |
| *.3gp filter=lfs diff=lfs merge=lfs -text | |
| *.3g2 filter=lfs diff=lfs merge=lfs -text | |
| *.asf filter=lfs diff=lfs merge=lfs -text | |
| *.rm filter=lfs diff=lfs merge=lfs -text | |
| *.swf filter=lfs diff=lfs merge=lfs -text | |
| *.vob filter=lfs diff=lfs merge=lfs -text | |
| *.ts filter=lfs diff=lfs merge=lfs -text | |
| *.m2ts filter=lfs diff=lfs merge=lfs -text | |
| # --- 3D Models & Assets --- | |
| *.fbx filter=lfs diff=lfs merge=lfs -text | |
| *.obj filter=lfs diff=lfs merge=lfs -text | |
| *.blend filter=lfs diff=lfs merge=lfs -text | |
| *.blend1 filter=lfs diff=lfs merge=lfs -text | |
| *.max filter=lfs diff=lfs merge=lfs -text | |
| *.maya filter=lfs diff=lfs merge=lfs -text | |
| *.3ds filter=lfs diff=lfs merge=lfs -text | |
| *.dae filter=lfs diff=lfs merge=lfs -text | |
| *.stl filter=lfs diff=lfs merge=lfs -text | |
| *.ply filter=lfs diff=lfs merge=lfs -text | |
| *.glb filter=lfs diff=lfs merge=lfs -text | |
| *.gltf filter=lfs diff=lfs merge=lfs -text | |
| *.ma filter=lfs diff=lfs merge=lfs -text | |
| *.mb filter=lfs diff=lfs merge=lfs -text | |
| *.dwg filter=lfs diff=lfs merge=lfs -text | |
| *.dxf filter=lfs diff=lfs merge=lfs -text | |
| *.c4d filter=lfs diff=lfs merge=lfs -text | |
| *.lwo filter=lfs diff=lfs merge=lfs -text | |
| *.lws filter=lfs diff=lfs merge=lfs -text | |
| *.lxo filter=lfs diff=lfs merge=lfs -text | |
| *.abc filter=lfs diff=lfs merge=lfs -text | |
| *.3dm filter=lfs diff=lfs merge=lfs -text | |
| *.spp filter=lfs diff=lfs merge=lfs -text | |
| # --- Archives & Compressed --- | |
| *.zip filter=lfs diff=lfs merge=lfs -text | |
| *.tar filter=lfs diff=lfs merge=lfs -text | |
| *.gz filter=lfs diff=lfs merge=lfs -text | |
| *.7z filter=lfs diff=lfs merge=lfs -text | |
| *.rar filter=lfs diff=lfs merge=lfs -text | |
| *.iso filter=lfs diff=lfs merge=lfs -text | |
| *.dmg filter=lfs diff=lfs merge=lfs -text | |
| *.deb filter=lfs diff=lfs merge=lfs -text | |
| *.rpm filter=lfs diff=lfs merge=lfs -text | |
| *.apk filter=lfs diff=lfs merge=lfs -text | |
| *.jar filter=lfs diff=lfs merge=lfs -text | |
| *.war filter=lfs diff=lfs merge=lfs -text | |
| *.ear filter=lfs diff=lfs merge=lfs -text | |
| *.bz2 filter=lfs diff=lfs merge=lfs -text | |
| *.xz filter=lfs diff=lfs merge=lfs -text | |
| *.lz filter=lfs diff=lfs merge=lfs -text | |
| *.tgz filter=lfs diff=lfs merge=lfs -text | |
| *.tbz2 filter=lfs diff=lfs merge=lfs -text | |
| *.sit filter=lfs diff=lfs merge=lfs -text | |
| *.sitx filter=lfs diff=lfs merge=lfs -text | |
| *.z filter=lfs diff=lfs merge=lfs -text | |
| # --- Documents --- | |
| *.pdf filter=lfs diff=lfs merge=lfs -text | |
| *.doc filter=lfs diff=lfs merge=lfs -text | |
| *.ppt filter=lfs diff=lfs merge=lfs -text | |
| *.docx filter=lfs diff=lfs merge=lfs -text | |
| *.pptx filter=lfs diff=lfs merge=lfs -text | |
| *.xlsx filter=lfs diff=lfs merge=lfs -text | |
| *.odt filter=lfs diff=lfs merge=lfs -text | |
| *.ods filter=lfs diff=lfs merge=lfs -text | |
| *.odp filter=lfs diff=lfs merge=lfs -text | |
| *.epub filter=lfs diff=lfs merge=lfs -text | |
| *.chm filter=lfs diff=lfs merge=lfs -text | |
| *.key filter=lfs diff=lfs merge=lfs -text | |
| *.numbers filter=lfs diff=lfs merge=lfs -text | |
| *.pages filter=lfs diff=lfs merge=lfs -text | |
| # --- Executables, Libraries & Binaries --- | |
| *.exe filter=lfs diff=lfs merge=lfs -text | |
| *.dll filter=lfs diff=lfs merge=lfs -text | |
| *.so filter=lfs diff=lfs merge=lfs -text | |
| *.dylib filter=lfs diff=lfs merge=lfs -text | |
| *.bin filter=lfs diff=lfs merge=lfs -text | |
| *.dat filter=lfs diff=lfs merge=lfs -text | |
| *.db filter=lfs diff=lfs merge=lfs -text | |
| *.sqlite filter=lfs diff=lfs merge=lfs -text | |
| *.msi filter=lfs diff=lfs merge=lfs -text | |
| *.pdb filter=lfs diff=lfs merge=lfs -text | |
| *.a filter=lfs diff=lfs merge=lfs -text | |
| *.lib filter=lfs diff=lfs merge=lfs -text | |
| *.o filter=lfs diff=lfs merge=lfs -text | |
| *.obj filter=lfs diff=lfs merge=lfs -text | |
| *.pyc filter=lfs diff=lfs merge=lfs -text | |
| # --- Design & Fonts --- | |
| *.otf filter=lfs diff=lfs merge=lfs -text | |
| *.ttf filter=lfs diff=lfs merge=lfs -text | |
| *.woff filter=lfs diff=lfs merge=lfs -text | |
| *.woff2 filter=lfs diff=lfs merge=lfs -text | |
| *.eot filter=lfs diff=lfs merge=lfs -text | |
| *.sketch filter=lfs diff=lfs merge=lfs -text | |
| *.xd filter=lfs diff=lfs merge=lfs -text | |
| *.fig filter=lfs diff=lfs merge=lfs -text | |
| *.psb filter=lfs diff=lfs merge=lfs -text | |
| *.ind filter=lfs diff=lfs merge=lfs -text | |
| *.indd filter=lfs diff=lfs merge=lfs -text | |
| *.indt filter=lfs diff=lfs merge=lfs -text | |
| # https://gemini.google.com/share/f40e23468348 | |
| # change the directory to the first argument | |
| cd $1 || { | |
| echo "Error: Could not change to directory $1" | |
| exit 1 | |
| } | |
| # Define file outputs | |
| LFS_RAW_LIST="lfs_debug_raw.txt" | |
| LFS_PATHS_LIST="lfs_files_paths.txt" | |
| LFS_NAMES_LIST="lfs_files_names.txt" | |
| echo "========================================================" | |
| echo " LFS Cleanup Script using BFG" | |
| echo "========================================================" | |
| # Check if git lfs is available | |
| if ! git lfs version &>/dev/null; then | |
| echo "Error: git-lfs is not installed or not in PATH." | |
| exit 1 | |
| fi | |
| echo "[1/4] Preparing branches and scanning for LFS files..." | |
| # Fetch all remotes | |
| git fetch --all | |
| # Loop over all remote branches to ensure we have local copies for BFG to update | |
| # BFG Repo-Cleaner automatically updates all local branches (refs/heads/*). | |
| # By creating local tracking branches for all remote branches, we ensure BFG cleans ALL of them. | |
| echo "Checking out all remote tracking branches..." | |
| git for-each-ref --format='%(refname:short)' refs/remotes/origin/ | while read -r remote; do | |
| # Skip HEAD | |
| if [[ "$remote" == *"HEAD" ]]; then | |
| continue | |
| fi | |
| # Remove 'origin/' to get the branch name | |
| branch=${remote#origin/} | |
| # Create local branch if it doesn't exist | |
| if ! git show-ref --verify --quiet refs/heads/"$branch"; then | |
| echo "Creating local branch: $branch matching $remote" | |
| git branch --track "$branch" "$remote" | |
| else | |
| echo "Verified local branch: $branch" | |
| fi | |
| done | |
| # Scan all refs for LFS files | |
| echo "Scanning ALL refs for LFS files..." | |
| git lfs ls-files --all >"$LFS_RAW_LIST" | |
| if [ ! -s "$LFS_RAW_LIST" ]; then | |
| echo "No LFS files found in the repository history." | |
| exit 0 | |
| fi | |
| # Extract file paths (strip OID and status char) | |
| # Format is: <oid> < * | - > <path> | |
| cat "$LFS_RAW_LIST" | sed 's/^[^ ]* . //' | sort | uniq >"$LFS_PATHS_LIST" | |
| # Extract just filenames (BFG --delete-files works on filenames, not path globs) | |
| cat "$LFS_PATHS_LIST" | awk -F/ '{print $NF}' | sort | uniq >"$LFS_NAMES_LIST" | |
| COUNT=$(wc -l <"$LFS_NAMES_LIST") | |
| echo "Found $COUNT unique LFS filenames." | |
| echo "Full paths saved to: $LFS_PATHS_LIST" | |
| echo "Filenames saved to: $LFS_NAMES_LIST" | |
| echo "--------------------------------------------------------" | |
| cat "$LFS_NAMES_LIST" | |
| echo "--------------------------------------------------------" | |
| # Check for BFG | |
| echo "[2/4] Checking for BFG Repo-Cleaner..." | |
| if command -v bfg &>/dev/null; then | |
| BFG_CMD="bfg" | |
| elif [ -f bfg.jar ]; then | |
| BFG_CMD="java -jar bfg.jar" | |
| elif [ -n "$BFG_PATH_JAR" ]; then | |
| BFG_CMD="java -jar $BFG_PATH_JAR" | |
| else | |
| echo "Error: 'bfg' command not found and bfg.jar not in current directory." | |
| echo "Please download bfg.jar from https://rtyley.github.io/bfg-repo-cleaner/ and place it here," | |
| echo "or install 'bfg' via package manager (e.g., 'brew install bfg')." | |
| exit 1 | |
| fi | |
| echo "Using: $BFG_CMD" | |
| # Construct glob string | |
| # Join lines with commas | |
| FILES_GLOB=$(paste -sd "," "$LFS_NAMES_LIST") | |
| # If multiple files, ensure it is treated as a list by BFG wrapping in {} | |
| if [ "$COUNT" -gt 1 ]; then | |
| FILES_GLOB="{$FILES_GLOB}" | |
| fi | |
| echo "[3/4] Running BFG to remove LFS files..." | |
| echo "Target glob: $FILES_GLOB" | |
| # Run BFG | |
| # --delete-files removes the file from history | |
| # --no-blob-protection ensures even recent files are removed | |
| $BFG_CMD --delete-files "$FILES_GLOB" --no-blob-protection | |
| echo "[4/4] Finalizing cleanup (git gc)..." | |
| # Expire reflog and prune to physically remove objects | |
| git reflog expire --expire=now --all && git gc --prune=now --aggressive | |
| echo "========================================================" | |
| echo "Cleanup Complete!" | |
| echo "NOTE: BFG has rewritten your commit history." | |
| echo "Changes are currently local." | |
| echo "To apply changes remotely, run: git push --force --all" | |
| echo "========================================================" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment