|
| 1 | +--- |
| 2 | +title: "Git LFS" |
| 3 | +description: "Git recipes for tracking large files with Git Large File Storage — setup, tracking, common operations, hosting limits, and gotchas." |
| 4 | +section: "playbook/lfs" |
| 5 | +order: 95 |
| 6 | +--- |
| 7 | + |
| 8 | +## Git Large File Storage |
| 9 | + |
| 10 | +Git stores every version of every file as a blob. For large binary |
| 11 | +files — images, videos, datasets, compiled assets — this causes the |
| 12 | +repository to grow quickly. Cloning becomes slow, and every developer |
| 13 | +downloads the full history of every large file whether they need it |
| 14 | +or not. |
| 15 | + |
| 16 | +**Git LFS** (Large File Storage) solves this by replacing large files |
| 17 | +with small **pointer files** in the repository. The actual file content |
| 18 | +is stored on a separate LFS server. Git downloads large files only |
| 19 | +when you check out a commit that needs them. |
| 20 | + |
| 21 | +### How it works |
| 22 | + |
| 23 | +```text |
| 24 | +Without LFS: |
| 25 | + .git/objects/ contains every version of every large file |
| 26 | + repo size grows with each commit that touches a large file |
| 27 | +
|
| 28 | +With LFS: |
| 29 | + .git/objects/ contains small pointer files (~130 bytes each) |
| 30 | + large file content lives on the LFS server |
| 31 | + Git downloads only the versions you check out |
| 32 | +``` |
| 33 | + |
| 34 | +A pointer file looks like this: |
| 35 | + |
| 36 | +```text |
| 37 | +version https://git-lfs.github.com/spec/v1 |
| 38 | +oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393 |
| 39 | +size 12345678 |
| 40 | +``` |
| 41 | + |
| 42 | +Git LFS intercepts `push`, `pull`, and `checkout` to upload and |
| 43 | +download the real content transparently. |
| 44 | + |
| 45 | +### Setup |
| 46 | + |
| 47 | +Install Git LFS (once per machine): |
| 48 | + |
| 49 | +```text |
| 50 | +$ git lfs install |
| 51 | +Updated git hooks. |
| 52 | +Git LFS initialized. |
| 53 | +``` |
| 54 | + |
| 55 | +This adds LFS hooks to your global Git configuration. The hooks |
| 56 | +intercept Git operations to handle large files automatically. |
| 57 | + |
| 58 | +### Track files by pattern |
| 59 | + |
| 60 | +Tell LFS which files to manage using glob patterns: |
| 61 | + |
| 62 | +```text |
| 63 | +$ git lfs track "*.psd" |
| 64 | +$ git lfs track "*.zip" |
| 65 | +$ git lfs track "assets/videos/**" |
| 66 | +``` |
| 67 | + |
| 68 | +Each `track` command adds a line to `.gitattributes`: |
| 69 | + |
| 70 | +```text |
| 71 | +$ cat .gitattributes |
| 72 | +*.psd filter=lfs diff=lfs merge=lfs -text |
| 73 | +*.zip filter=lfs diff=lfs merge=lfs -text |
| 74 | +assets/videos/** filter=lfs diff=lfs merge=lfs -text |
| 75 | +``` |
| 76 | + |
| 77 | +Commit `.gitattributes` so other developers get the same LFS rules: |
| 78 | + |
| 79 | +```text |
| 80 | +$ git add .gitattributes |
| 81 | +$ git commit -m "Track large files with LFS" |
| 82 | +``` |
| 83 | + |
| 84 | +### Common operations |
| 85 | + |
| 86 | +#### Push |
| 87 | + |
| 88 | +LFS uploads tracked files to the LFS server automatically during |
| 89 | +`git push`: |
| 90 | + |
| 91 | +```text |
| 92 | +$ git push origin main |
| 93 | +Uploading LFS objects: 100% (3/3), 45 MB | 2.1 MB/s, done. |
| 94 | +``` |
| 95 | + |
| 96 | +#### Pull and fetch |
| 97 | + |
| 98 | +`git pull` downloads LFS files for the current checkout. To download |
| 99 | +LFS content without merging: |
| 100 | + |
| 101 | +```text |
| 102 | +$ git lfs fetch # download LFS objects for current ref |
| 103 | +$ git lfs fetch --all # download LFS objects for all refs |
| 104 | +$ git lfs pull # fetch + checkout (update working tree) |
| 105 | +``` |
| 106 | + |
| 107 | +#### Check tracked files |
| 108 | + |
| 109 | +```text |
| 110 | +$ git lfs ls-files # list all LFS-tracked files in the repo |
| 111 | +$ git lfs status # show pending LFS uploads |
| 112 | +``` |
| 113 | + |
| 114 | +### Migrate existing files to LFS |
| 115 | + |
| 116 | +If large files are already in the repository history, use `migrate` |
| 117 | +to rewrite history and move them to LFS: |
| 118 | + |
| 119 | +```text |
| 120 | +# Migrate all .zip files in the entire history |
| 121 | +$ git lfs migrate import --include="*.zip" --everything |
| 122 | +
|
| 123 | +# Migrate only in the current branch |
| 124 | +$ git lfs migrate import --include="*.zip" |
| 125 | +``` |
| 126 | + |
| 127 | +This rewrites commits, so coordinate with your team and force-push |
| 128 | +afterward. |
| 129 | + |
| 130 | +### Hosting support |
| 131 | + |
| 132 | +| Host | Free LFS storage | Free bandwidth/month | |
| 133 | +|------|-------------------|---------------------| |
| 134 | +| GitHub | 1 GB | 1 GB | |
| 135 | +| GitLab | 5 GB (project) | Unlimited (self-managed) | |
| 136 | +| Bitbucket | 1 GB | 5 GB | |
| 137 | + |
| 138 | +All three hosts offer paid plans for additional storage and bandwidth. |
| 139 | +Self-hosted GitLab and Bitbucket have configurable limits. |
| 140 | + |
| 141 | +### Gotchas |
| 142 | + |
| 143 | +- **LFS requires server support.** The remote must have an LFS |
| 144 | + endpoint. All major hosts support it, but a plain bare repository |
| 145 | + on a file server does not. |
| 146 | +- **Cloning without LFS installed gives you pointer files.** If a |
| 147 | + developer clones without Git LFS installed, they get the small |
| 148 | + pointer text instead of the actual files. Run `git lfs pull` after |
| 149 | + installing LFS to fix this. |
| 150 | +- **CI pipelines need LFS.** If your CI runner does not have Git LFS, |
| 151 | + builds that depend on tracked files will fail. Most CI platforms |
| 152 | + support LFS — check that `git lfs install` runs before checkout. |
| 153 | +- **Forks do not share LFS storage.** On GitHub, each fork has its |
| 154 | + own LFS quota. Large LFS repositories can be expensive to fork. |
| 155 | +- **Bandwidth counts on download.** Every `git clone`, `git fetch`, |
| 156 | + or `git lfs pull` that downloads LFS objects counts against the |
| 157 | + bandwidth quota. |
| 158 | +- **You cannot un-track a file retroactively.** Removing a pattern |
| 159 | + from `.gitattributes` only affects future commits. Existing LFS |
| 160 | + pointers in history stay as pointers. |
0 commit comments