How to hash a folder on Mac
Drop a folder into Rocket Hash and you do not get one digest — you get one for every file underneath it. That is not a limitation to work around; it is the only answer that means anything. Here is why a folder has no single fingerprint, what to do with the list you get instead, and how to boil it down to one value when somebody insists on one.
Somebody has asked you for “the hash of the folder”, or you have dragged a folder in expecting one line of hexadecimal and got forty-seven rows instead. The instinct is that something went wrong. Nothing went wrong. A folder does not have a hash the way a file has a hash, and the list you are looking at is the closest true thing to one.
This page is about that distinction and how to work with it: what actually happens when a whole tree is hashed, which files come along that you did not know were in there, how to turn the result into something you can re-check in six months, and how to produce a single value for a whole folder in the one case where you really do need one.
If you only have one file, hashing a single file is the shorter read. If you have a pile of loose files rather than a folder and you mostly want to know how to get through them, hashing many files at once covers the queue and what the progress counter is telling you.
A hash function takes a sequence of bytes. A file is a sequence of bytes; a folder is a list of names pointing at other things. The list of per-file digests is the result — and it is more useful than a single value would be, because it tells you which file changed.
Why a folder has no single digest
Imagine you wanted to invent one. You would have to flatten the folder into a single stream of bytes first, and the moment you try, you are making decisions that nobody has agreed on:
- In what order? A folder has no inherent sequence. Alphabetical by name, by creation date, by position on disk — each gives a different stream and therefore a different digest.
- Do the names count? If you include the file names, renaming a file changes the folder's digest. If you exclude them, two folders holding the same files under different names look identical.
- What about everything that is not data? Permissions, owners, modification dates, symlinks, Finder tags, empty subfolders. Include them and the digest changes when you copy the folder to a different disk. Exclude them and you are not really fingerprinting the folder.
Every tool that advertises a folder hash has quietly answered those three questions its own way, which is why two of them rarely agree with each other, and why the checksums on a download page are always attached to a file rather than to a directory. The file is the unit on which everybody does agree. Hash the files, keep the list, and you have something another person's tooling can check.
The list is also strictly more informative. A single folder-level value can only ever tell you something in here is different. Forty-seven per-file digests tell you that config.yml is the one that moved, which is the question you actually had.
Hash every file in a folder
-
Drop the folder in
Select Files in the sidebar and drag the folder itself out of the Finder into the window — you do not have to open it and select its contents. The add (+) button in the toolbar takes a folder too. Every file underneath is queued, at every depth, and streamed through in turn.
-
Check the count and the size
The left-hand side of the status bar summarizes what it found — the number of files and the total bytes. Read it before you read any digest. If you expected a few dozen files and it found nine hundred, you have dropped in a folder containing an app bundle, a Photos library or a Git checkout, and that is worth knowing now rather than in ten minutes.
-
Settle on one algorithm
Set the Algorithm control before you export. A manifest has no column that records which function produced it, which is why the algorithm conventionally lives in the file's name —
SHA256SUMS, notsums.txt— and why the person who checks it six months from now is relying on that name being honest. Choose as though you will be reading the list in five years: CRC32 is 8 characters and not an identity, MD5 is the line an auditor stops at, and SHA-256 costs you nothing extra because the disk sets the pace rather than the arithmetic. -
Export the list
The export button in the toolbar turns the list into a text file in the
shasumformat — the same two-column shape Linux distributions publish, so anything that already reads one of those lists reads yours with nothing to convert. That text file is the folder's fingerprint. Keep it, commit it, attach it to a release, or drop it in the backup beside the data it describes. What a SHA256SUMS file contains reads one line by line. -
Make one value out of the list, if you must
There is a respectable trick for the case where something genuinely needs a single string: hash the manifest. Add the exported file back in as an ordinary file and its digest now stands for every byte of every file the manifest covers, because changing any of them changes a line in it. The catch is that you are now fingerprinting a text file, so the line order and the paths written in it are part of the answer — sort the manifest before you hash it if you intend to reproduce the value later, and do not expect it to match on a machine where the folder lives somewhere else.
What is actually in there
A folder on a Mac contains more than the Finder shows you, and this is the single biggest source of surprise when a count comes back higher than expected.
| What | Where it came from |
|---|---|
.DS_Store | The Finder, the first time anybody opened the folder in a window |
._filename | Extended attributes written as separate files when a folder is copied to a FAT or exFAT disk |
.localized, .gitignore, .env | Ordinary files whose names begin with a dot, so the Finder hides them |
| Hundreds of files inside one “file” | An .app, .rtfd, .photoslibrary or .fcpbundle — all of them folders that the Finder presents as single items |
The Finder's own count will rarely agree with the file count either, because Get Info counts enclosed folders as items and a hashing run has nothing to hash in a folder. Two numbers that differ by exactly the number of subfolders are not a bug.
.DS_Store deserves its own warning, because it is the reason folder-level fingerprints rot. It is written when somebody opens the folder, rewritten when they resize the window, and it is not copied consistently between machines. Every real file in the folder can be byte-for-byte identical on two Macs while the folder's contents differ — which is exactly the fragility that per-file digests do not have.
A ZIP records modification times and the order its entries were added, so zipping identical folders twice produces two different archives and two different digests. You would also be reading every byte anyway, plus compressing them. Hashing a ZIP explains which of the two checks inside an archive you are really running.
Comparing two folders, or the same folder later
This is what the list is for, and it is where per-file digests pay for themselves. Manifest the folder now, manifest it again after the copy, the sync, the transfer or the year in storage, and compare the two. Three outcomes, each meaning something different:
- The path appears in both with the same digest. That file is intact — not similar, identical.
- The path appears in both with different digests. That file changed, and you know which one to look at.
- The path appears in only one. Something was added, removed, renamed or never copied — the commonest real-world failure, and the one a single folder digest would tell you nothing useful about.
Checking files against a manifest covers lining the two up properly, including the awkward case where the folder has moved and every path in the old list is wrong while every digest in it is still right.
Getting through a big tree
Forty-seven files are done while you are still looking at the window. Nine hundred are not, and a bundle or a deep build directory can put you in the second case without warning. Two numbers in the status bar are how you follow a long walk: the summary on the left is what was found, a file count and a byte total, and the figure on the right is how many of those have been hashed so far. When the two agree, the walk is over and nothing was left out.
The app also reports live throughput and an estimate of the time remaining. On a deep tree of small files that rate reads far below anything the drive could manage on one long sequential read, which is the shape of the tree rather than a fault. Reading the speed and the time remaining separates the ordinary slow cases from the ones worth looking into.
You do not have to sit and watch it. A run can be paused mid-file and resumed from the exact byte it stopped at, so taking the Mac back for twenty minutes costs twenty minutes rather than the hour already spent — which matters most on the tree with one 60 GB disk image buried in it. Stopping ends the run gracefully instead, leaving every finished row intact and exportable. Pausing and resuming goes through what survives each of the two.
Prune the list before you export
This is the step that gets skipped, and it decides whether the manifest is still true in a year. The rows you have collected include the hidden files from the table further up: a .DS_Store for every folder somebody has opened in a window, ._name files from a trip across an exFAT disk, whatever a build left behind. They are real files and they were really hashed. They are also the ones most likely to change for reasons that have nothing to do with your data, and each of them in the list is a line that will later report a difference you do not care about.
The remove (⊗) button at the end of a row takes that row out, and the reset button empties the whole list for when the selection was wrong from the start rather than wrong in four places. Prune first and export second, because what comes out is the rows that are in front of you. Exporting a manifest covers where that file should live afterwards.
Troubleshooting
Permission denied halfway through a folder
Dropping a folder in grants access to that folder, but a tree can contain items the sandbox treats separately — another user's home directory, a mounted volume that appears inside it, or a system location that nothing is allowed to read. Grant access to the enclosing folder rather than the individual files, which is the route permission denied when hashing a folder lays out, switch by switch.
The two folders look identical but some digests differ
Check the file sizes of the rows that disagree. A difference of a byte or two in a text file is a line-ending change, picked up from an editor or a transfer through Windows. A difference of a few kilobytes across many files suggests they were re-exported rather than copied. Identical sizes with different digests is the interesting case, and the one worth investigating properly.
I dropped in an app and got hundreds of rows
Because an application is a folder. Safari.app is a directory with a Contents folder inside it and several hundred files below that, and the Finder is being polite by showing it as one icon. There is no single digest to be had. If what you want is to confirm an app is the one the developer shipped, hash the installer it arrived in — see hashing a DMG, which is one file and one digest.
It is taking much longer than the total size suggests
A tree is a per-file workload, so the byte total predicts very little. Depth costs again on top of that: every directory on the way down has to be read before anything inside it can be queued, which makes a deep tree of small files the slowest shape there is. Hashing multiple files at once has that arithmetic and what to do about it.
The folder is on a disk that keeps changing
A folder being written to while it is being walked produces a manifest that describes no single moment in time: early files as they were ten minutes ago, late files as they are now. Stop whatever writes to it, or copy the folder somewhere quiet and hash the copy. Active mail stores, photo libraries and anything with a database inside it belong in this category.
Frequently asked questions
What is the hash of a folder?
Strictly, there is not one. Hash functions consume a sequence of bytes, and a folder is a list of names rather than a sequence of bytes, so any single “folder hash” is really a hash of one particular way of flattening the folder — and different tools flatten differently. What you get instead is one digest per file, which is both standard and more useful, because it identifies which file changed.
Does hashing a folder include subfolders?
Yes — every file underneath it, at any depth. The folders themselves contribute nothing, because a folder has no contents of its own to read. An empty subfolder therefore leaves no trace in the result at all, which is worth remembering if empty directories matter to whatever you are checking.
Why are there more files in my folder than I expected?
Almost always hidden files. .DS_Store appears in any folder the Finder has opened, ._name files appear when a folder has been copied to a FAT or exFAT disk, and anything beginning with a dot is hidden from the Finder by default. Bundles inflate a count dramatically too: one .app icon can be several hundred files.
How do I compare two folders using checksums?
Produce a manifest for each — one line per file, same algorithm for both — then compare the two lists line by line. Matching lines are files that are byte-for-byte identical; a path in both with different digests is a changed file; a path in only one is something added, removed or renamed. See checking files against a manifest.
How do I get the checksum of a Mac app?
You cannot get one for the .app itself, because it is a folder containing hundreds of files. Hash the DMG or ZIP the app was distributed in, which is a single file and the thing a developer would have published a checksum for. For an app already installed, macOS code signing rather than a checksum is the mechanism that answers “is this unmodified”.
Is it faster to zip a folder and hash the ZIP?
No, and the result is worse. You still read every byte, plus you spend time compressing them, and a ZIP embeds modification times and entry order — so archiving the same unchanged folder twice gives you two different digests. A manifest of per-file digests reproduces reliably; an archive's digest does not.
Will the same folder give the same digests on another Mac?
Every real file will, because a file's digest depends only on its bytes. The folder as a whole may not look the same, because .DS_Store files, extended attributes and empty directories travel inconsistently between machines and disk formats. This is the practical argument for per-file digests over any single folder-level value.