AFST - Analytic File Sync Tool

Sync / Archive without the noise. Only what changed, nothing more.

AFST doesn’t try to be rsync, doesn’t try to be Time Machine, doesn’t try to do everything.
It does one thing: figure out what changed and copy it and does it in a way that’s easy to read, easy to understand, and easy to trust.

1 Like

What’s the advantage of using AFST compared to rsync or similar? I don’t see the point of yet another copying tool.

Fair question.

1) How it decides what changed. Instead of walking the tree file-by-file and running custom comparison logic on each one (which is what most sync tools, rsync included, do at their core), AFST snapshots source and destination once, then hands the comparison off entirely to diff - a tool that’s been optimized for exactly this for decades. Less custom code doing the deciding, more of the heavy lifting done by something already fast and battle-tested. That’s where the speed and simplicity come from.

2) What it does with that result. AFST is additive, not mirrored: By design, nothing on the destination ever gets deleted. Destination grows and keeps history as a side effect of just running the tool normally.

3) Combine the two and you get… a tool that’s fast because it doesn’t reinvent comparison logic, and easy to trust because there’s barely any logic to hide bugs in - it’s a few dozen lines of bash using tools people already know.
Because the code is small enough to actually read and fully understand in a few minutes, you’re not just trusting it blindly -you can see exactly how it works, which makes it just as easy to customize to your own workflow or workload -as it is to trust it.

Long story short: no hashes, no residual files, no databases — the /tmp snapshots are left behind, to give you a history you can read and review.
Beyond that: minimal traffic, minimal footprint on the filesystem.

That said, if none of this maps to a problem you actually have, stick with what you’re using - AFST isn’t meant to replace rsync, just to fill a narrower gap.

What are the point of these snapshots? What do they contain exactly? Can you restore the destination or source directory using them? (Similar to git?)

Looking at the code, it appears that the “snapshots” are text files containing lists of filenames and their modification timestamps. The script does a diff on the “snapshots” created for the source and destination to find files that are in the source but not the destination, or files for which the modification time is different. It then uses cp -p to copy the files that it identified.

I think “snapshot” is a bad word to use for those files. People see that word and think of, e.g., btrfs snapshots, which is bound to lead to confusion.

“Snapshot“ is definitely incorrect here. “File History“ would be a better suited term.

Excuse me for my lengthy answer, I’ll try to answer all your concerns and I’m hoping to get the idea across..

Ok, I feel we’re talking here (someone at least read the code), but obviously not the RTFM part which explicitly explains what it is and what it isn’t.

You’re right about the mechanism -that’s exactly what it does, no magic (it’s open and readable).

On “snapshot” being the wrong word:
I get the concern, but I’d push back a bit. “Snapshot” isn’t owned by btrfs/ZFS — it’s the general term for “a record of state at a point in time,” and that’s precisely what these files are: a snapshot of the filesystem’s metadata state (names + mtimes) at the moment the scan ran. btrfs snapshots are one implementation of that concept at the block layer; AFST’s are another at the metadata layer: same word, different layer, different purpose.
It’s the same reason “backup” isn’t reserved for one specific tool or technique either; there are backups of all kinds, and snapshots are just as broad category.

If someone sees “snapshot” and expects CoW/mountable/instant-rollback semantics, that’s on the docs to prevent, not on them to assume.

As for “File History” as an alternative: the “history” part does exist in the sense that these snapshots are timestamped and traceable over time, so in that broader sense it is a kind of history. The content of any individual snapshot file is a pure state capture, though, but the diff result and the resulting copy list are a different thing, and those are closer to a history of changes.
So if I had to be nitpicking exact about it: these files are snapshots, and together they form a history of changes. Both words are correct, they just describe different layers of the same data.

One more relevant detail:
the script does have cleanup code for snapshots, but it’s intentionally disabled by default, so users can review exactly what was copied and when. In effect, the accumulated snapshots end up functioning as a log of operations - a history of sorts - but that’s a side effect, not the point of the file.
In normal operation, I don’t rely on old snapshots, because I want to stay on top of all the changes in the tree, not just through AFST.

While discussing semantics, allow me one more thing about the name: AFST, as in “Analytic File Sync Tool.”
I didn’t aim for it to sound more than it actually is: the “Analytic” part could just as well have been “Archive” (on count of cumulative non deleting sync) but at the time of creation I wanted to point out the difference in operation.
Since it doesn’t traverse the tree file by file, but instead takes the snapshots and works in a kind of more analytic way using diff, it’s an approach that stands on its own - not only different, but I like to think smart in a way too :slight_smile:

If I’m reading the code right, then indeed nothing gets deleted on the destination side.

But if you modify a file on the destination side, then on the next sync, it will be overwritten by the older file from the source. Because the diff just picks up that the timestamp is different, and doesn’t differentiate between “destination newer than source” vs “source newer than destination”.

Does it preserve ACL’s?

@pg-tips
Good point. You noticed that this is effectively a backup/archive function (one-way implied) rather than a bidirectional sync. That is the original purpose of this tool.

I could do a more elaborate analysis of the diff and add a couple of dozen parameters to establish direction and priorities, but I suspect that would add too much friction for a tool that is intended to be simple and effective. You can easily adjust the code to work the way it fits best for your specific needs, or (of course) simply reverse the source and destination to point the tool in the direction you need!


@anothermindbomb
About ACLs — you are right. Depending on the OS, cp -p by default does not preserve them. My intention is to keep the tool as simple as possible without adding friction with options, but since the script is just a few lines of bash, you can easily edit the copy command in the source code to add whatever flags fit your specific environment. i.e., change cp -p to cp -p --preserve=all.


I think it does on Fedora?

man cp says:

-p     same as --preserve=mode,ownership,timestamps

and confirms that mode includes ACLs:

ATTR_LIST  is a comma-separated list of attributes. Attributes are 'mode' for permissions (including any ACL and xattr permissions), 'ownership' for user and group, 'timestamps' for file timestamps, 'links' for hard links, 'context' for security context, 'xattr' for extended attributes, and 'all' for all attributes.

@pg-tips
I stand corrected… I admit to not consulting the man pages over those specific details, though I am aware of explicit options to keep ACLs. Of course, for my specific use case and general operation, timestamps were the key, and -p does exactly that.

That said… everyone can easily adjust the code to their specific needs.

SIDENOTE / UPDATE:
I actually revisited the code today (the original version of this tool dates back to 2025) and noticed that the status outputs could work much better with a visual interface of zenity and indicate copy progress…

The included afst-gui.sh script, uses zenity to give the tool a lightweight GUI experience. It keeps the core logic just as simple but makes it much more convenient for everyday desktop use!


If instead of using cp -p, you called rsync with the relevant flags, you’d get all the benefit of rsync in addition to your “snapshot” file listings. It’d be way more performant for large files too.

Of course, at that point you might as well just use rsync in the first place…

You nailed it… rsync is powerful and has many uses! (no argument there)
For me, the main joy of AFST is the ability to easily customize the code and tailor its behavior to my exact needs. Plus, the data transfer is fully minimized, so it operates with the absolute bare minimum of traffic.

Do note… I wrote right from the start… AFST doesn’t try to replace rsync or be “better”
-it’s a different approach to backup/archive/synchronization, emphasizing minimalism, customization, and user control; history and auditability come naturally as a result of its design.

I revisited the code and concept, and…
AFST just got a signifficant upgrade/evolution: AFST2

https://github.com/mlohajner/AFST/tree/main/afst2