An import only ever adds
Re-importing an archive used to be a thing you did carefully, at night, after a backup. The run went out and fetched every picture again — pictures the archive already held, byte for byte — and each of those downloads landed on top of a file that was already correct.
That is fixed, and the fix is one sentence: a media file's identity is the address it came from.
The archive already knows
Every media entry records its src. A re-import compares what the source offers against what the archive holds, and fetches only what is missing. Over a complete archive that is nothing at all — the second run of the same import downloads not one picture, and says so in the summary.
The source itself is still read, and that is the honest limit of the claim: an export sitting on your disk costs nothing, while a feed or an API is fetched every time, because there is no other way to find out whether anything changed.
Here is a real Ghost import run a second time over an archive that already holds it:
Done. 118 post(s) written, 419 media file(s).
419 of them are already in this archive and come from there, not
from the source.
18 post(s) were scheduled in Ghost -- imported as drafts; schedule
them here when you're ready.
Three seconds. Four hundred and nineteen pictures, not one of them fetched.
Never replaced. Not by a flag, not by anything
A file already in the archive is never overwritten. Not by a re-import, not by REFETCH_MEDIA=1, which only makes the run ask the source about every address again instead of trusting its own records — what lands on disk is still just the files that were missing.
The reasoning is that the bytes an import brings come from somewhere the engine cannot vouch for. A CDN that re-encodes, a platform that starts watermarking, an export that was built wrong: any of those, trusted once, silently replaces originals you cannot get back. The archive wins every disagreement.
When a download no longer matches the copy the archive keeps, the download is discarded and counted. That counter is the one honest signal that the source has drifted away from your archive — and it counts bytes from outside only, so a post that reuses a picture from another post is not mistaken for drift.
A picture that leaves and comes back
Sources edit themselves. A photo drops out of a post in March and reappears in June, and naïvely each of those is a new file: the picture leaves, the name is freed, the return mints a byte-identical duplicate under a fresh number.
Now the post's kept versions remember the address a file was stored under, so a returning picture takes back the name it had. Failing that, the file's own bytes answer — identity by content, checked always rather than only when a name collides. An entry that never carried an address at all is recognised the same way.
The point of all that machinery is one property: a drop-and-return cycle does not grow the post's directory, and a re-import a year from now writes the same tree it wrote today.
And the post describes what is really there
Width and height are re-read from the archive's own copy once the media is in place, so a post cannot claim dimensions over a file that never arrived. A copy that cannot be measured leaves the previous record standing. An entry whose file is missing entirely is left saying exactly what it said — and ./blog.sh check is the tool that finds it, because nothing an import does repairs a damaged file.
Importing should be boring. Run it again on Tuesday and find out that nothing happened: that is the feature.

Comments