How I Freed 50GB of Disk Space Using a Git Trick Youโve Never Heard Of
Are your local ~/Downloads, ~/Desktop, or ~/Documents folders slowly drowning under a mountain of screenshots, screen recordings, zoom logs, and random design mockups?
If you are like most developers, designers, or technical writers, you probably have gigabytes of screen recordings and screenshots cluttering your local disk. You need to keep them for future reference, but they are eating away at your SSD, slowing down your system, and making backups a nightmare.
You could buy iCloud or Google Drive storage. Or you could use this insane Git workflow that stores your files permanently on GitHub, keeps them fully browsable on the web, but consumes exactly 0 bytes of your local storage.
In this deep-dive article, we will show you how to set up a permanent, automated media vault on GitHub using Git Sparse-Checkout and Git LFS (Large File Storage)โand provide a complete automation script and "Skill" file so your AI coding assistants can run it for you!
The Problem: The Standard Backup Trap
Usually, backing up files to GitHub means:
- Pushing the files.
- Keeping them locally.
If you delete them locally and commit the deletion, they disappear from GitHub's main directory browser. If you don't commit the deletion, your local working tree is dirty forever, and git status screams at you with hundreds of red lines:
Changes not staged for commit:
deleted: Screenshot 2026-04-12 at 5.32.00 PM.png
deleted: Screen Recording 2026-04-13 at 10.00.04 PM.mov
...
Is there a way to keep them on GitHub but free up local space while keeping your git status clean?
Yes. It's called Git Sparse-Checkout.
๐ The Secret Weapon: Git Sparse-Checkout
Sparse-checkout is a native Git feature designed for giant monorepos. It allows you to tell Git: "Keep these files committed in the remote repository history, but do not physically download or extract them to my local machine."
Combine this with Git LFS for large video files, and you have a free, infinite, automated cloud storage locker.
๐ ๏ธ Step-by-Step Implementation Guide
Let's walk through how to build this system from scratch.
Step 1: Initialize Git and Setup LFS (Large File Storage)
If you have large recordings (over 100MB), standard GitHub commits will reject them due to GitHub's file size limits. We'll set up Git LFS first.
Open your terminal in your media folder (e.g. ~/Documents) and run:
# 1. Install Git LFS (on macOS)
brew install git-lfs
# 2. Initialize LFS in your folder
git lfs install
# 3. Track heavy video formats with LFS
git lfs track "*.mov"
git lfs track "*.mp4"
# 4. Commit LFS configurations
git add .gitattributes
git commit -m "setup: track video formats via LFS"
Step 2: Configure Sparse-Checkout (Exclusion Patterns)
Now, we set up the exclusion rules. We want to tell Git to exclude the folders where we store our images and videos locally, but keep the index, readme, and scripts checked out.
# 1. Enable sparse-checkout in non-cone (custom pattern) mode
git sparse-checkout init --no-cone
# 2. Set the exclusion rules in Git's sparse config
echo '/*
!/images/
!/videos/' > .git/info/sparse-checkout
# 3. Apply the rules
git sparse-checkout reapply
What does this pattern mean?
-
/*checks out all files at the root level (likeREADME.md,INDEX.md,backup.sh). -
!/images/and!/videos/tells Git never to check out or keep theimagesorvideosdirectories on your local drive.
Any existing files matching these directories will be deleted instantly from your local disk, but remain safe on GitHub!
๐ค Step 3: Automate It (The One-Click Script)
To make this effortless, here is a bash script (backup.sh) that automates the entire flow:
- Scans the root directory for new screenshots or videos.
- Moves them automatically into folders organized by date and type (
images/YYYY-MM/andvideos/YYYY-MM/). - Commits and pushes them to GitHub.
- Grabs the full 40-character commit SHA (important to avoid raw usercontent 404s).
- Rebuilds a markdown catalog (
INDEX.md) listing download URLs. - Reapplies the sparse-checkout rules to wipe the local files and reclaim your disk space!
Here is the script backup.sh:
#!/usr/bin/env bash
# backup.sh - Automatically organize, backup new screenshots/videos to subfolders, update INDEX.md, push to GitHub, and clean up local space.
set -e
REPO_URL="https://raw.githubusercontent.com/YOUR_USERNAME/YOUR_REPO"
INDEX_FILE="INDEX.md"
BRANCH="main"
echo "๐ Scanning for new media files to back up..."
# Find new untracked/modified media files at root or inside subdirs
NEW_FILES=()
while IFS= read -r -d '' file; do
relative="${file#./}"
if [[ "$relative" =~ \.(png|jpg|jpeg|mov|mp4)$ ]]; then
NEW_FILES+=("$relative")
fi
done < <(git status --porcelain -z | grep -z -E '^\?\?|^ M' | cut -z -c4- | sort -z)
TOTAL=${#NEW_FILES[@]}
if [ "$TOTAL" -eq 0 ]; then
echo "โ
No new media files found to back up."
exit 0
fi
echo "๐ Found $TOTAL new files to process."
# Move files to date-based folders if they are at the root level
MOVED_FILES=()
for f in "${NEW_FILES[@]}"; do
if [[ ! "$f" == *"/"* ]]; then
# Parse date from name or default to current year-month
if [[ "$f" =~ [0-9]{4}-[0-9]{2}-[0-9]{2} ]]; then
DATE_STR="${BASH_REMATCH[0]}"
YM="${DATE_STR:0:7}" # YYYY-MM
else
YM=$(date '+%Y-%m') # Fallback to current year-month
fi
if [[ "$f" =~ \.(png|jpg|jpeg)$ ]]; then
dest_dir="images/$YM"
else
dest_dir="videos/$YM"
fi
mkdir -p "$dest_dir"
dest_path="$dest_dir/$f"
echo "๐ Moving root file: $f -> $dest_path"
mv "$f" "$dest_path"
git add "$dest_path"
MOVED_FILES+=("$dest_path")
else
git add "$f"
MOVED_FILES+=("$f")
fi
done
# Commit and Push the files first to get the commit SHA
echo "๐พ Committing new organized files..."
git commit -m "backup: add $TOTAL new files organized by date"
echo "๐ Pushing files to GitHub..."
git push origin "$BRANCH"
# Get the full 40-character commit SHA
COMMIT_SHA=$(git rev-parse HEAD)
echo "โ
Pushed successfully! Commit: $COMMIT_SHA"
# Rebuild INDEX.md using Python to keep it clean and ordered
echo "๐ Rebuilding INDEX.md..."
python3 -c "
import os
import urllib.parse
from datetime import datetime
repo_url_base = '${REPO_URL}/${COMMIT_SHA}'
index_file_path = '${INDEX_FILE}'
def get_human_size(path):
try:
size = os.path.getsize(path)
if size >= 1073741824:
return f'{size / 1073741824:.1f} GB'
elif size >= 1048576:
return f'{size / 1048576:.1f} MB'
elif size >= 1024:
return f'{size / 1024:.1f} KB'
else:
return f'{size} B'
except Exception:
return 'Unknown'
# Collect images
images = []
for root, dirs, files in os.walk('images'):
for f in files:
if f.endswith('.png'):
relative_path = os.path.join(root, f)
images.append(relative_path)
images.sort()
# Collect videos
videos = []
for root, dirs, files in os.walk('videos'):
for f in files:
if f.endswith('.mov') or f.endswith('.mp4'):
relative_path = os.path.join(root, f)
videos.append(relative_path)
videos.sort()
# Read the first commit SHA for other files
first_sha = os.popen('git log --oneline | tail -1 | cut -d\" \" -f1').read().strip()
with open(index_file_path, 'w') as f_obj:
f_obj.write('# ๐ Documents Index\n\n')
f_obj.write('> Auto-generated index of all files backed up to this repository.\n')
f_obj.write('> Use the Raw URL to download any file, even after local deletion.\n')
f_obj.write(f'> Generated on: {datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")}\n\n')
f_obj.write('## ๐ผ๏ธ Images (PNG Screenshots)\n\n')
f_obj.write('| # | File | Size | Commit | Raw URL |\n')
f_obj.write('|---|------|------|--------|---------|\n')
for idx, path in enumerate(images, 1):
size = get_human_size(path)
encoded = urllib.parse.quote(path, safe='/')
f_obj.write(f'| {idx} | `{path}` | {size} | `{COMMIT_SHA[:7]}` | [Download]({repo_url_base}/{encoded}) |\n')
f_obj.write('\n## ๐ฅ Videos (Screen Recordings)\n\n')
f_obj.write('| # | File | Size | Commit | Raw URL |\n')
f_obj.write('|---|------|------|--------|---------|\n')
for idx, path in enumerate(videos, 1):
size = get_human_size(path)
encoded = urllib.parse.quote(path, safe='/')
f_obj.write(f'| {idx} | `{path}` | {size} | `{COMMIT_SHA[:7]}` | [Download]({repo_url_base}/{encoded}) |\n')
f_obj.write('\n## ๐ Other Files\n\n')
f_obj.write('| # | File | Size | Commit | Raw URL |\n')
f_obj.write('|---|------|------|--------|---------|\n')
f_obj.write(f'| 1 | `hls-video.excalidraw` | {get_human_size(\"hls-video.excalidraw\")} | `{first_sha}` | [Download]({repo_url_base}/hls-video.excalidraw) |\n')
"
# Commit and Push INDEX.md
git add "$INDEX_FILE"
git commit -m "index: update INDEX.md with new backup file mappings"
git push origin "$BRANCH"
# Reapply sparse checkout to reclaim local disk space
echo "๐งน Reclaiming local disk space (running sparse-checkout)..."
git sparse-checkout reapply
echo "๐ Backup complete! Local space is cleared, and files are safe on GitHub."
Make it executable:
chmod +x backup.sh
๐ค Step 4: The AI Assistant "Skill" File
If you are using AI agents (like Claude, Gemini, or Copilot) to help you develop, they can automate this backup system completely. By checking in a Skill Markdown file to your repository, future agents will immediately understand the repository rules and can safely execute the backups for you!
Here is the exact backup.skill.md file you should add to the repository:
# Skill: Organized Documents Backup and Space Reclaiming
This skill provides instructions for AI coding agents to back up new screenshots, screen recordings, and media files from the local computer to the GitHub repository using Git LFS and Git Sparse-Checkout, keeping files organized by date and type.
## ๐ Repository Directory Structure
Files are organized in folders automatically by date and type:
* `/images/YYYY-MM/` โ Screenshot PNGs grouped by month
* `/videos/YYYY-MM/` โ Screen Recording MOVs and MP4s grouped by month
---
## ๐ ๏ธ How to Back Up New Files (Agent Flow)
If the user drops new files into the folder, you should run the automated script.
### Option A: Automated (Recommended)
Run the backup script directly:
bash
./backup.sh
This script will:
1. Detect any untracked or modified images/videos.
2. Automatically parse dates from filenames (e.g. `2026-04-12`) and move them to their respective `images/YYYY-MM/` or `videos/YYYY-MM/` subdirectory.
3. Stage, commit, and push them to GitHub.
4. Fetch the **full 40-character commit SHA** (required for raw GitHub usercontent links to prevent 404s).
5. Rebuild `INDEX.md` using python to catalog all files with their paths, sizes, and direct raw download links.
6. Commit and push the updated `INDEX.md`.
7. Run `git sparse-checkout reapply` to clean up the local disk.
### Option B: Manual Execution
If the script needs debugging or you want to run it manually:
1. **Move files into directories**:
bash
mkdir -p images/YYYY-MM
git mv "Screenshot YYYY-MM-DD ...png" images/YYYY-MM/
2. **Stage and commit the new files**:
bash
git commit -m "backup: add new files"
git push origin main
3. **Retrieve the full commit SHA**:
bash
COMMIT_SHA=$(git rev-parse HEAD)
4. **Format entry for INDEX.md**:
Add rows to `INDEX.md` under the appropriate table header:
markdown
| # | File | Size | Commit | Raw URL |
|---|------|------|--------|---------|
| [Index] | images/YYYY-MM/[Filename] | [Size MB] MB | [Commit SHA] | Download |
5. **Push INDEX.md**:
bash
git add INDEX.md
git commit -m "index: update entries"
git push origin main
6. **Reapply sparse checkout to delete local files**:
bash
git sparse-checkout reapply
---
## ๐ฅ How to Restore a File for the User
If the user requests to restore one or more files to their computer:
1. Locate the file in `INDEX.md` to get its path.
2. Tell Git to stop skipping the file:
bash
git sparse-checkout add "images/YYYY-MM/filename.png"
*Note: This will immediately download the file from GitHub to the local directory.*
3. To exclude it again after use:
bash
git sparse-checkout reapply
shell
๐ก How This Helps You: 5 Real-World Benefits
1. Zero Disk Usage
The main benefit. Your local project folders stay incredibly clean and compact, preserving your SSD life and leaving space for active coding projects.
2. Fully Browsable on GitHub
Unlike standard backup archives (like .zip or .tar), your files aren't locked away in a compressed format. You can click through /images/2026-06/ on GitHub's website and browse them instantly.
3. Bulletproof, Unbreakable Links
By pointing the download URLs in INDEX.md to the full commit SHA (/Documents/a2b2ed5a43.../images/file.png) instead of /main/, these links will work forever. Even if you delete or modify the files on the main branch later to clean up remote storage, the historical commit raw URLs remain functional.
4. Zero Maintenance for Future Assistant Agents
If you use coding agents, you can check in the backup.skill.md file. Future agents entering your workspace will automatically understand how to restore files or perform backups without you having to guide them.
5. Restore Files Instantly
Need a file back? You don't have to download it from a web browser. Just tell Git to stop skipping it:
git sparse-checkout add "images/2026-04/screenshot.png"
Git will instantly fetch it from GitHub and drop it back into your local folder. Once you are done, run:
git sparse-checkout reapply
And it disappears from your disk again!
โก Conclusion
Stop letting screenshots and screen recordings choke your machine's performance. Git was built to track changes, but with Sparse-Checkout, it becomes a premium, automated, zero-local-footprint cloud drive.
Have you ever used sparse checkout? Let me know in the comments how you manage local file clutter!












