Documentation

Every command, every option, and when to reach for each one. Install with npm install -g exifregistry, run bare exifreg for the interactive guided mode, or use the commands below directly.

Conventions that hold everywhere

  • Previews first. Every file operation prints its full plan and only executes with --apply.
  • Backups by default. Every metadata edit keeps the untouched original (suffix _original); revert with undo. Opt out per command with --no-backup.
  • Nothing is overwritten. Name collisions get numeric suffixes, always.
  • Pairs stay together. RAW+JPEG twins and .xmp/.aae sidecars move, rename and back up as one unit.
  • Pixels are never touched. Metadata edits rewrite metadata segments only; frame and resize write new files.
  • 100% local. No network calls, ever. Even GPS-to-city and GPS-to-timezone lookups use bundled databases.

Pattern placeholders (organize, rename, ingest, backup --by)

PlaceholderMeaning
{year} {month} {day}capture date parts (from DateTimeOriginal, with sensible fallbacks)
{date}2026-07-05
{time} {hour} {minute} {second}capture time parts
{camera}camera model, e.g. Sony A7 IV
{lens}lens model
{type}Photos, RAW or Videos
{name} {ext}original file name / extension
{city} {region} {country}from GPS, resolved offline
{counter} {counter:4}sequence number in shooting order, optionally padded

Inspect & analyze

exifreg show

exifreg show <files...> [options]

Prints a curated metadata report: camera, lens, shutter count, serial number, full exposure block, dates, GPS. Use it any time you need to know what a file really says about itself: checking gear info before selling a camera, confirming a fix worked, or reading the shutter count of a body you are about to buy.

OptionWhat it doesDefault
-v, --verbosealso list every remaining tag after the key fields
-a, --allflat alphabetical dump of every tag (raw values)
--jsonmachine-readable output for scripts
-r, --recursiverecurse into subfolders
-e, --export [file.md]also save the report as Markdown<name>.metadata.md
$ exifreg show DSC02481.ARW # key fields for one file
$ exifreg show trip/ -r -v # whole folder, every tag
$ exifreg show photo.jpg -e report.md # archive the report as Markdown

exifreg diff

exifreg diff <fileA> <fileB> [options]

Compares the metadata of two files side by side and shows only what differs. Typical case: an exported JPEG next to its RAW, to see exactly what the editor changed, kept or stripped. Filesystem identity tags (name, folder, access dates) are excluded so the table stays meaningful.

OptionWhat it doesDefault
-a, --allalso list the tags whose values are identical
$ exifreg diff original.ARW exported.jpg # what did the export change?

exifreg stats

exifreg stats <paths...> [options]

Aggregates a library's metadata into terminal bar charts: which cameras and lenses you actually use, favorite focal lengths and apertures, ISO habits, shots per month and per hour of day. Great before buying or selling gear, or just to understand your own shooting.

OptionWhat it doesDefault
-r, --recursiverecurse into subfolders
--jsonmachine-readable output
-e, --export [file.md]export the report to Markdownlibrary-stats.md
$ exifreg stats ~/Photos -r # the whole library at a glance
$ exifreg stats 2026/ -e year-review.md # a year in review, exported

exifreg find

exifreg find <paths...> -w "<condition>" [options]

Queries files by metadata and prints matching paths, one per line, ready to pipe into any other command. Conditions look like ISO>3200, Model=Sony A7 IV or LensModel~35mm. Repeat -w to AND conditions. Numbers compare numerically; EXIF dates compare chronologically; ~ means contains.

OptionWhat it doesDefault
-w, --where <condition>required; repeat to combine with AND
-r, --recursiverecurse into subfolders

Operators: = != > >= < <= and ~ (contains). Keys and string values are case-insensitive. Exit code is 1 when nothing matches, so it behaves well in scripts.

$ exifreg find . -w "ISO>3200" # the noisy ones
$ exifreg find . -w "Model~sony" -w "DateTimeOriginal>=2026:07" # Sony shots since July
$ exifreg find . -w "GPSLatitude>0" | xargs exifreg gps --remove # strip GPS wherever it exists

Edit metadata

exifreg gps

exifreg gps <files...> [options]

Sets or removes the GPS position. Paste coordinates straight from Google or Apple Maps with --coords, or pass --lat/--lon. Works on videos too (QuickTime GPSCoordinates). Use --remove before sharing photos of your home or a client's address.

OptionWhat it doesDefault
-c, --coords "<lat, lon>"coordinates as copied from a maps app
--lat <deg> / --lon <deg>decimal degrees, as an alternative to --coords
--alt <meters>altitude
--removedelete all GPS data instead
--no-backupskip the automatic _original backup
$ exifreg gps beach.jpg --coords "-23.5505, -46.6333" # set a location
$ exifreg gps home-session/*.jpg --remove # privacy before delivery

exifreg date

exifreg date <files...> [options]

Fixes capture and modification dates. The killer option is --shift: when the camera clock was wrong or on the wrong timezone, shift every date by the same amount without touching anything else. Accepts friendly date forms like 2026-07-05 14:30 or 05/07/2026.

OptionWhat it doesDefault
-t, --taken <datetime>capture date (DateTimeOriginal + CreateDate)
-m, --modified <datetime>metadata modification date
--all <datetime>set taken, created and modified at once
-s, --shift "<amount>"shift all dates: "+2h", "-30m", "+1d 2h30m"
--sync-filealso set the file's own modified date to the capture date
--no-backupskip the automatic backup
$ exifreg date *.ARW --shift "+2h" # clock was two hours behind
$ exifreg date scan.jpg --taken "1994-12-25" # date a scanned print

exifreg timezone

exifreg timezone <files...> [options]

Writes the UTC offset tags (OffsetTimeOriginal and friends) that most cameras leave empty. With --from-gps each photo's offset is derived from its own coordinates, offline, with daylight saving handled at the capture date. Editors and catalogs then stop guessing what time your photos were really taken.

OptionWhat it doesDefault
--offset "±HH:MM"explicit offset, e.g. "-03:00"
--from-gpsderive each photo's offset from its GPS position
--no-backupskip the automatic backup
$ exifreg timezone *.jpg --offset "-03:00" # shot in Brazil
$ exifreg timezone trip/ --from-gps # multi-country trip, each photo correct

exifreg copy

exifreg copy <source> <targets...> [--no-backup]

Copies all metadata from one file onto others. The classic case: an export or a panorama stitch came out clean of EXIF, and you want the original's camera info, dates and GPS back on it.

OptionWhat it doesDefault
--no-backupskip the automatic backup
$ exifreg copy original.ARW stitched-pano.jpg # restore lost EXIF

exifreg copydates

exifreg copydates <source> <targets...> [options]

Copies only the dates from one file onto others, and lets you choose which: capture, modified, or all. Handy when a scan or an export lost its dates but a sibling file still has them.

OptionWhat it doesDefault
-t, --takencopy the capture date (DateTimeOriginal + CreateDate)
-m, --modifiedcopy the modification date
--allcopy all dates (default)
--no-backupskip the automatic backup
$ exifreg copydates raw.ARW scan.jpg --taken # just the capture date
$ exifreg copydates raw.ARW scan.jpg # all dates at once

exifreg sign

exifreg sign <files...> [options]

Stamps your authorship (Artist and Copyright) into files in bulk. Save a preset once with --save-preset and from then on a plain exifreg sign folder/ is enough. {year} in the copyright text becomes the current year automatically.

OptionWhat it doesDefault
--artist <name>artist / creator name
--copyright "<text>"copyright notice; "{year}" expands
--save-presetstore artist/copyright as the default preset
--no-backupskip the automatic backup
$ exifreg sign *.jpg --artist "Davi Arndt" --copyright "© {year} Davi Arndt" --save-preset # first time
$ exifreg sign wedding/ # every time after that

exifreg strip

exifreg strip <files...> [options]

Removes ALL metadata: camera, dates, GPS, serial numbers, everything. For privacy-safe sharing when the picture should speak and the data should not. Asks for confirmation unless you pass -y.

OptionWhat it doesDefault
-y, --yesskip the confirmation prompt
--no-backupskip the automatic backup
$ exifreg strip listing-photos/*.jpg -y # publish clean files

exifreg undo

exifreg undo <files...>

Every metadata edit keeps the untouched original next to the file with an _original suffix. undo puts it back. Accepts the edited file, the _original itself, or a whole folder to restore every backup inside.

$ exifreg undo photo.jpg # revert the last edit of one file
$ exifreg undo wedding/ # revert everything in a folder

Organize files

exifreg organize

exifreg organize <paths...> --to <dir> [options]

Moves (or copies) photos into folders derived from their metadata: by date, by camera, even by city using offline GPS geocoding. RAW+JPEG pairs and sidecar files always travel together. Dry-run by default; nothing is ever overwritten; every executed batch can be undone.

OptionWhat it doesDefault
--to <dir>destination rootcurrent folder
--by "<pattern>"folder pattern, see placeholders below{year}/{date}
--copycopy instead of move
-r, --recursiverecurse into subfolders
--applyexecute (default is a preview)
--undorevert the last executed batch under --to
$ exifreg organize card/ --to ~/Photos --by "{year}/{date}" --apply # the classic import layout
$ exifreg organize . --by "{camera}/{date}" # two-shooter wedding, split by body
$ exifreg organize . --by "{country}/{city}" # travel archive, fully offline

exifreg rename

exifreg rename <files...> -p "<pattern>" [options]

Renames files in place from their metadata, keeping pairs and sidecars in matching names. {counter} numbers files in shooting order, which fixes the classic two-cameras-same-counter mess.

OptionWhat it doesDefault
-p, --pattern "<pattern>"filename pattern{date}_{time}_{name}
-r, --recursiverecurse into subfolders
--applyexecute (default is a preview)
--undo <folder>revert the last rename in that folder
$ exifreg rename . -p "wedding_{counter:3}" --apply # wedding_001.ARW, _002...

exifreg ingest

exifreg ingest <card> --to <dir> [options]

Imports a memory card: copies into organized folders and, with --verify, checks every copy against a SHA-256 checksum before you ever consider formatting the card. Copies by default; the card keeps its files unless you pass --move.

OptionWhat it doesDefault
--to <dir>destination root (required)
--by "<pattern>"folder pattern under --to{year}/{date}
--verifychecksum-verify every copy
--movemove instead of copy
--applyexecute (default is a preview)
--undorevert the last ingest under --to
$ exifreg ingest /Volumes/EOS_R6 --to ~/Photos --verify --apply # the safe import

exifreg split

exifreg split <dir> [--apply] [--undo]

Sorts a mixed folder into Photos/, RAW/ and Videos/ subfolders. For that downloads folder where everything landed together.

OptionWhat it doesDefault
--applyexecute (default is a preview)
--undorevert the last split in this folder
$ exifreg split ~/Downloads/mixed --apply # three tidy piles

exifreg dupes

exifreg dupes <paths...> [options]

Finds byte-identical duplicates (size first, then SHA-256, so false positives are impossible) and reports how much space they waste. Deletion is opt-in, keeps the first of each group, and asks for confirmation. This is the one operation without an undo, so it is triple-gated.

OptionWhat it doesDefault
-r, --recursiverecurse into subfolders
--deleteplan deletion of the duplicates
--applywith --delete: actually delete (confirmed interactively)
$ exifreg dupes ~/Photos -r # report only, always safe
$ exifreg dupes ~/Photos -r --delete --apply # reclaim the space

Archive

exifreg backup

exifreg backup <sources...> --to <dir> [options]

Verified, append-only backups with a SHA-256 manifest. Deletions never propagate, changed files are versioned under _versions/ instead of overwritten, every copy is atomic and checksum-verified, and --verify re-checks every byte to catch silent corruption. Read the full story on the backup page.

OptionWhat it doesDefault
--to <dir>backup root (required)
--by "<pattern>"organized layout instead of mirroring folders
--paranoidre-hash every file instead of trusting size and date
--verifyre-hash the whole backup against its manifest
--statusfiles, size, versions, capture span
--applyexecute (default is a preview)
$ exifreg backup ~/Photos --to /Volumes/Backup --apply # the habit worth having
$ exifreg backup --verify --to /Volumes/Backup # monthly integrity check

exifreg restore

exifreg restore <backup-root> [options]

Restores from a backup: everything, or a slice by capture date thanks to the manifest. Files that already exist with identical content are skipped; files that differ are conflicts and are never overwritten; a backup copy that fails its own checksum is refused rather than restored.

OptionWhat it doesDefault
--to <dir>restore into this folder instead of the original locations
--taken <date>only files captured then: "2026", "2026-07", "2026-07-05"
--applyexecute (default is a preview)
$ exifreg restore /Volumes/Backup --apply # put missing originals back
$ exifreg restore /Volumes/Backup --taken 2026-07 --to ./recovered --apply # one month, elsewhere

Prepare & publish

exifreg frame

exifreg frame <files...> [options]

Re-renders photos inside an aesthetic colored frame with their exposure caption in Space Mono: the "shot on" portfolio look. RAW files work through their embedded previews. The output keeps the original photo's EXIF.

OptionWhat it doesDefault
-c, --color <name|#hex>frame color; list all 21 with --colorswhite
--ratio <W:H|original>"1:1", "4:5", "9:16", "3:2"...original
--caption <pos>bottom, top or nonebottom
--camerainclude the camera model in the caption
--margin <pct>margin around the photo6
--size <px|full>"full" keeps the photo at native resolution3000
-q, --quality <1-100>JPEG quality of the render95
-o, --out <dir>output foldernext to each photo
--colorslist the palette with names and hex codes
$ exifreg frame photo.ARW -c off-white --ratio 4:5 # Instagram-ready
$ exifreg frame photo.jpg -c sage --caption none --size full # just the frame, print-grade

exifreg resize

exifreg resize <files...> [options]

Resizes and converts into NEW files; originals are never touched and the EXIF is carried over. The standout option is --max-size: say "1mb" and a binary search finds the highest quality that fits, so you stop guessing quality numbers.

OptionWhat it doesDefault
-s, --max-size <size>target file size: "1mb", "500kb"
--long <px>resize the long edge
--width / --height <px>fit within a box
--percent <n>scale by percentage
-f, --format <fmt>jpeg, png, webp, avif, tiffkeep
-q, --quality <1-100>explicit quality85
-o, --out <dir>output folder
--suffix <text>inserted before the extensionresized
$ exifreg resize photo.jpg --max-size 1mb # client asked for under 1 MB
$ exifreg resize *.jpg --long 2048 -f webp -o web/ # web-ready batch

exifreg contact

exifreg contact <paths...> [options]

Renders a contact sheet: a thumbnail grid with filename (and optional EXIF) labels, as a single JPEG or a high-resolution PDF. The classic deliverable for a client to pick selects from. The file is written next to the photos, not the terminal's folder. RAW files contribute their embedded previews.

OptionWhat it doesDefault
-c, --columns <n>thumbnails per row4
--cell <px>width of each cell320
-f, --format <fmt>jpeg or pdf (inferred from --out)jpeg
--color <name|#hex>sheet background coloroff-white
--no-exiffilename labels only, no exposure line
-o, --out <file>output file<folder>-contact next to the photos
-r, --recursiverecurse into subfolders
$ exifreg contact selects/ -c 5 # sheet saved into selects/
$ exifreg contact selects/ -f pdf --color charcoal # dark PDF for print
$ exifreg contact selects/ --no-exif # just the frame numbers

Utility

exifreg config

exifreg config [key] [value] [options]

Saves defaults so you stop repeating flags: your name and copyright for sign, a default backup drive, a favorite frame color. Stored in ~/.config/exifregistry/config.json. Run it with no arguments to see current settings.

OptionWhat it doesDefault
--keyslist every settable key
--pathprint the config file location

Keys: sign.artist, sign.copyright, backup.to, frame.color. An empty value unsets a key.

$ exifreg config sign.artist "Davi Arndt" # set your name once
$ exifreg config backup.to "/Volumes/Backup" # default backup drive
$ exifreg config # show everything you have set

exifreg history

exifreg history [options]

Shows the operations that changed your files, newest first: what ran, when, and how many files it touched. A quiet paper trail for a tool that edits irreplaceable work.

OptionWhat it doesDefault
-n, --limit <n>how many entries to show20
--clearerase the log
--pathprint the log file location
$ exifreg history # the last 20 operations
$ exifreg history -n 100 # look further back

exifreg completion

exifreg completion <shell>

Prints a shell completion script so pressing Tab suggests commands. Supports bash, zsh and fish.

$ exifreg completion zsh > "${fpath[1]}/_exifreg" # install for zsh
$ exifreg completion fish > ~/.config/fish/completions/exifreg.fish # install for fish

exifreg update

exifreg update

Checks npm for a newer version and tells you how to upgrade. This is the only command that uses the network, and only when you run it: everything else is 100% local.

$ exifreg update # am I on the latest?

exifreg doctor

exifreg doctor

Checks that the installation is healthy: version, the bundled ExifTool responding, and how many config settings you have. Run it once after installing, or when something feels off.

$ exifreg doctor # all green in one second

Something missing or wrong? Open an issue. Docs generated from the same reference the tool ships with.