Version control was built for text. Images break several of its assumptions, and understanding how tells you what to do instead.

Why Git Struggles With Images

  • No meaningful diff. Git can't store "what changed" in a JPEG; each version is an entirely new blob.
  • History is permanent. Every version of every image stays in the repository forever, even after deletion. A 5 MB image replaced twenty times is 100 MB of history, in every clone, forever.
  • Merges can't be resolved. Two people editing the same PSD produces a conflict no tool can merge; someone's work is discarded.
  • Clone times suffer. Repositories heavy with binaries become slow to clone and check out, which affects everyone including people who never touch images.

When Git Is Fine

  • Small, stable, text-adjacent assets: SVG icons (which do diff meaningfully, being XML), small PNGs, favicons.
  • Assets that change rarely.
  • Anything the build genuinely needs, since it must be versioned with the code that references it.

A handful of optimized SVGs and small PNGs in a repository is completely normal and fine.

When to Use Git LFS

Git Large File Storage replaces large files in the repository with small pointers, storing the actual content elsewhere.

Use it for: large raster assets that must live with the code, video, design files that need to be versioned alongside a project.

Be aware: LFS requires setup on every clone, has bandwidth and storage quotas on hosted services, and migrating an existing repository to LFS rewrites history. Decide before the repository grows, not after.

What to Do Instead

For most teams, the better answer is to keep large binaries out of the code repository:

  • Design sources (PSD, Sketch, Figma, AI) belong in a design tool or asset manager with built-in version history, not in Git. Design tools already version well, with visual history and comments.
  • Photography and raw media belong in a DAM or a structured shared drive.
  • Only exported, optimized, build-required assets go into the repository.
  • Large media can live on a CDN or object store, referenced by URL, with the reference versioned in code.

Conventions That Substitute for Diffing

Since you can't diff an image, make its identity explicit:

  • Content-hashed filenames for build output: logo.a1b2c3.svg. Changing the file changes the name, which makes caching safe and version confusion impossible.
  • Semantic names for source assets: logo-primary-dark.svg, not logo-final-2.svg.
  • A changelog for assets — a short text file noting what changed and when. Text diffs even when images don't.
  • Never overwrite silently. If a change is significant, it deserves a new name or a note.

Practical Rules

  1. Decide the policy before the repository grows. Cleaning binaries out of history later requires rewriting it, which disrupts everyone.
  2. Add a size check to CI that fails on unexpectedly large files — the single most effective guard.
  3. Gitignore design sources and exports you don't need in the build.
  4. Optimize before committing, since the committed version is permanent.
  5. Document where each asset type lives so nobody has to guess whether an image belongs in Git, the DAM or the CDN.

The overall aim: the repository holds what the build needs, the asset manager holds what the design process needs, and each does the job it's good at.