How to compare two folders on a Mac, step by step
The word covers at least three separate jobs, and the fastest way to waste an afternoon is to run a check that answers a different one. Confirming that a copy finished intact is not the same job as working out which of two working copies has the newer edits, and neither is the same as producing a list of what changed inside a set of documents. Each needs a different check, each costs a different amount of time, and one of the most commonly recommended commands will report two folders as matching when their contents are not. Getting the order right removes most of the difficulty.
Step one: decide what identical means
Write down the question before opening a terminal. There are three, and they lead to different places.
| Question | What the check must look at | Rough cost |
|---|---|---|
| Did the copy complete intact? | Every byte of every file | Reads both sides in full |
| Which side has newer work? | Names, sizes, modification times | Reads directory entries only |
| What changed inside these documents? | The contents, presented line by line | Slow, and needs a viewer |
The first is a verification job. Nothing except a full read of both sides answers it honestly, and the time is proportional to the total size. The second is a triage job that finishes in seconds on a tree of a hundred thousand files, because nothing is opened. The third is an editing job that belongs in a visual tool rather than a listing.
Mixing them is where people get burned. Using timestamps to verify a backup means trusting a number that any tool can set. Using checksums to find out which laptop has the newer draft means waiting an hour for information that was available immediately. The question decides the check, not the other way round.
Step two: make sure both sides are really there
A comparison can only report on what it was able to read, and on a Mac there are several ways for a folder to be less present than it looks.
The first is iCloud Drive. With Desktop and Documents in iCloud, items can appear complete in Finder while their contents are still in the cloud.
In some cases, items may be stored in iCloud Drive but may not yet be downloaded to your Mac. Source: support.apple.com
A checksum pass over such a folder will trigger downloads for every item it touches, which turns a two minute check into a long transfer and may fail partway on a metered connection. Either download the material deliberately first, or compare a folder that is genuinely local.
The second is permissions. A subdirectory the current user cannot read is frequently reported as containing nothing rather than as an error, so a comparison sails past it and declares success. The third is a network volume that has dropped its connection, which produces an empty listing rather than a failure in some tools.
The cheap insurance against all three is to count first. Run a file count on each side and compare the two numbers before running anything expensive:
find /path/to/A -type f | wc -l
find /path/to/B -type f | wc -l
If the counts are wildly different, something structural is wrong and no comparison of contents will be meaningful yet. If they are close, the difference itself is useful information about what to expect.
Step three: the names pass
Start with the cheapest check, because it often answers the question on its own. The goal here is a list of what exists on one side and not the other, with nothing opened.
diff <(cd /path/to/A && find . -type f | sort) \
<(cd /path/to/B && find . -type f | sort)
Both sides are reduced to a sorted list of relative paths and the two lists are compared as text. Lines prefixed with a left angle bracket exist only in the first folder, lines with a right angle bracket only in the second. On a large tree this finishes in seconds because no file is opened.
The version most guides recommend, diff -rq A B, does more than this. It reports one line per difference in two fixed forms, Only in A: filename for something present on one side and Files A/x and B/x differ for a content difference, which means it opens and reads files to decide. That makes it the right tool when the tree is small and the wrong one when it is a photo library. Add -x '.DS_Store' to suppress the view settings file that Finder writes into every folder it displays.
Read the output before doing anything with it. The two categories, present on one side and different in content, usually have different causes and different fixes, and treating them as one list is how the wrong side gets overwritten.
The names pass also tends to expose structural problems that a content check would only reveal slowly. A folder that was copied one level too deep shows up as every path being present on one side and absent on the other, which is instantly recognisable in a list of paths and looks like total corruption in a content comparison. An interrupted copy shows up as a contiguous block of missing paths in alphabetical order, which is a strong hint that the transfer stopped rather than that files were deleted. Neither pattern is visible if the first thing run is an expensive byte level comparison, which is the main argument for doing the cheap pass first even when the expensive one is going to be run anyway.
Step four: the contents pass, and the trap inside it
The recommendation found in most articles is a dry run of a sync:
rsync -avn --delete /path/to/A/ /path/to/B/
This is genuinely useful and it does not modify anything, but it has a limitation that is rarely stated. By default the decision about whether a file needs transferring is made from size and modification time, not from the contents. The manual describes the alternative as using full file checksums rather than the usual file size and modification time quick check.
The consequence is easy to demonstrate. Two files of the same length, edited in place within the same second, are reported as matching by the default run and as differing once checksums are switched on. Any workflow where a file is modified without changing its length, which covers fixed width records, some database and index files, and anything whose timestamp has been deliberately preserved, will produce a clean report from a check that never looked.
For a verification job, add the checksum flag and accept the cost:
rsync -rn --checksum --delete /path/to/A/ /path/to/B/
It is worth being clear about why timestamps are weak evidence rather than merely imprecise. A modification time is a value stored alongside the file, and plenty of ordinary operations set it to something other than the moment the contents last changed. Restoring from a backup, copying with a flag that preserves attributes, and unpacking an archive all write a timestamp that came from somewhere else. Synchronisation software sets timestamps deliberately so that its own future runs behave predictably. None of that is wrong, and all of it means that two files agreeing on size and time is a statement about their metadata rather than about their contents.
There is a second approach worth knowing, because it produces something the dry run does not: a record.
cd /path/to/A && find . -type f -exec shasum {} + | sort > ~/a-manifest.txt
cd /path/to/B && find . -type f -exec shasum {} + | sort > ~/b-manifest.txt
diff ~/a-manifest.txt ~/b-manifest.txt
Each side becomes a text file of hashes and paths, and the comparison is a diff of two text files. The manifests can be kept, mailed, compared against a third copy next month, or checked after a drive has been in storage for a year. For anything that has to be demonstrated to someone else later, a stored manifest is worth more than a transcript of a command that has already scrolled away.
Step five: act on the result without overwriting the wrong side
The comparison is the safe part. Everything after it can destroy material, so the order matters.
Decide the direction first, in words, before typing anything: which folder is the source of truth for this particular job. Then choose what should happen to items that exist only in the destination. Keeping them and deleting them are both reasonable, and the choice is what the --delete flag controls. That flag is the one that turns a sync into a loss when the direction was wrong.
Two mechanical details cause more accidents than anything else. A trailing slash on the source path in rsync means the contents of the folder, and no trailing slash means the folder itself, so one missing character produces a nested copy instead of a merge. And every run that will modify something should be run once with the dry run flag, read, and only then repeated without it. The dry run and the real run should differ by exactly one character, so that nothing else can change between the version that was inspected and the version that executed.
For a one way copy where the destination should end up matching the source exactly, the pair of commands is the same line twice, once with the dry run flag and once without. For a merge where both sides hold work, there is no safe single command, and the differences have to be resolved individually.
That last case deserves more care than it usually gets, because it is the common one. Two folders that have both been worked in are not a synchronisation problem at all. They are a set of individual decisions, and the only automation that helps is the part that narrows the list down to the items that actually need a human. Sorting the differences by type first makes the list shorter than it looks. Files present on one side only are usually a straightforward decision about whether the item is wanted. Files that differ are the ones that need opening. If the number in the second group is small, resolving them one by one is faster and safer than any command. If it is large, the honest answer is often that the two folders should not be merged, and that one of them should be kept as an archive under a dated name while work continues in the other.
Step six: when this is not a one off
Comparing two folders once is a task. Comparing them regularly, opening the differences, running commands against them and moving items between the two sides, is a loop that runs across a folder view, a terminal, and increasingly a chat window where the command is worked out before it is run.
The state does not travel between those windows. A set of files identified by a comparison exists as text in a terminal, and turning it into a selection in a folder view means finding each one by hand. That is the point at which the tool matters more than the technique. A look at how different file managers arrange this is more useful than a feature comparison, because what changes the loop is whether the panes are in one window. The capability list is worth reading with the same question in mind.
What to change first
Stop using a default rsync dry run to verify copies, because it compares sizes and timestamps rather than contents; add the checksum flag whenever the answer has to be trustworthy. Keep the manifest files rather than the terminal output, so the same check can be repeated later against a third copy. If the comparison is one step in a longer loop of inspecting and fixing, the thing to change is the window count, which is what Atriens is built around.
Frequently asked questions
Does an rsync dry run prove that two folders are identical?
Not by itself. By default it decides what to transfer from file size and modification time, so a file edited without changing its length, or with its timestamp preserved, is reported as matching. Add the checksum flag when the answer has to be trustworthy, and accept that both sides will then be read in full.
What is the fastest way to see which files exist in only one of two folders?
Compare sorted lists of relative paths rather than the folders themselves. Produce a list from each side with find . -type f | sort, then diff the two lists. Nothing is opened, so it finishes in seconds even on very large trees, and the output is a plain list of paths.
Why does a comparison say a folder is empty when it clearly is not?
Usually permissions. A directory the current user cannot read is reported by several tools as having no contents rather than as an error. A dropped network volume and an iCloud folder whose items have not been downloaded produce similar results. Counting files on both sides first catches all three.
Is it better to compare folders in Finder or in Terminal?
Finder has no folder comparison feature, so the choice is really between the built in commands and a third party application with a comparison view. Commands are exact and repeatable and produce a record. A graphical tool is worth the switch when the result is a few hundred differences that each need a human decision.