File Organization
Purpose
Bring order to a directory without losing anything. Every file-organization task has one hard requirement that overrides all others: nothing is destroyed, and every action is reversible.
When to Use
- Organizing a directory of accumulated files.
- Finding and removing duplicates.
- Reclaiming disk space.
- Applying a consistent naming convention.
- Sorting downloads, documents, or media.
Capabilities
- Classification by type, content, and date.
- True duplicate detection by content hash.
- Consistent renaming.
- Space analysis.
- Safe, reversible operations.
Inputs
- The directory, and how large it actually is.
- The organizing principle: by type, by date, by project.
- What must not be touched.
Outputs
- A plan, shown before anything is executed.
- The reorganization, performed reversibly.
- A report of what changed.
Workflow
- Survey first — Count, size, types, and the largest items. Never act on a directory you have not looked at.
- Propose a plan and show it — What will move where, what will be renamed, what will be deleted. This is shown, and confirmed, before anything happens.
- Detect duplicates by content, not by name — Hash the files. Two files with the same name are frequently different; two files with different names are frequently identical.
- Move, do not delete — Duplicates and junk go to a quarantine directory, not to oblivion. Delete only after the user has confirmed, and preferably never.
- Preserve the metadata — Modification times carry information. Do not destroy them by copying carelessly.
- Report what happened — With a way to undo it.
Best Practices
- Never delete. Move to a quarantine directory and let the user delete it themselves once they are satisfied. A cleanup that destroys one needed file has failed regardless of how much space it reclaimed.
- Hash before declaring a duplicate. Filename matching produces false positives (two different
invoice.pdf) and false negatives (report.pdf and report (1).pdf that are identical).
- Hash cheaply: compare sizes first, then hash only the files whose sizes match. Hashing a 200 GB directory in full is slow and almost entirely wasted.
- Dry-run everything. Show the plan, and require confirmation for anything destructive.
- Preserve the original name somewhere — in a manifest, if not in the filename. Renaming without a record is irreversible in practice.
- Watch for hard links and symlinks. "Deleting a duplicate" that is a hard link frees nothing and may break something.
Examples
Duplicate detection that is correct and fast:
import hashlib
from collections import defaultdict
from pathlib import Path
def find_duplicates(root: Path) -> dict[str, list[Path]]:
"""Two passes. Size first — cheap. Hash only the size collisions."""
by_size: dict[int, list[Path]] = defaultdict(list)
for path in root.rglob("*"):
if path.is_file() and not path.is_symlink():
by_size[path.stat().st_size].append(path)
# Only files with an identical size can be identical. Everything else is
# excluded without reading a byte.
candidates = [paths for paths in by_size.values() if len(paths) > 1]
by_hash: dict[str, list[Path]] = defaultdict(list)
for group in candidates:
for path in group:
by_hash[_hash(path)].append(path)
return {h: paths for h, paths in by_hash.items() if len(paths) > 1}
def _hash(path: Path, chunk: int = 1 << 20) -> str:
h = hashlib.blake2b(digest_size=16)
with path.open("rb") as f:
while data := f.read(chunk):
h.update(data)
return h.hexdigest()
A plan shown before anything is done:
Survey of ~/Downloads:
2,847 files, 41.2 GB
Duplicates (identical content) : 312 files, 8.4 GB reclaimable
Installers (.dmg, .pkg, .exe) : 89 files, 14.1 GB — all older than 6 months
Screenshots : 1,204 files, 2.1 GB
Documents (pdf, docx) : 418 files
Archives (.zip, .tar.gz) : 203 files, 9.8 GB
PLAN — nothing is deleted. Everything is moved, and a manifest records the
original location of every file.
1. Duplicates -> ~/Downloads/_quarantine/duplicates/
The most recently modified copy of each set stays where it is.
312 files, 8.4 GB.
2. Installers older than 6 months -> ~/Downloads/_quarantine/installers/
89 files, 14.1 GB.
3. Screenshots -> ~/Downloads/Screenshots/YYYY-MM/
Organized by capture date (from EXIF where available, mtime otherwise).
4. Documents -> ~/Downloads/Documents/
5. Archives -> left in place (they may be needed; too risky to move blind).
Manifest written to ~/Downloads/_quarantine/manifest.json.
To undo everything: python restore.py manifest.json
Proceed? [y/N]
Notes
- The size-then-hash approach is not an optimization detail; on a large directory it is the difference between a task that finishes in seconds and one that reads every byte on the disk.
- The quarantine directory plus a manifest is what makes a cleanup safe. The user gets the space back the moment they empty it, and until then nothing is lost.
- BLAKE2 is faster than SHA-256 and entirely adequate for duplicate detection, where you are not defending against an adversary constructing collisions.
1---2name: file-organization3description: Use when organizing, deduplicating, or cleaning up files. Covers safe classification and renaming, finding true duplicates, reclaiming space, and never destroying data during a cleanup.4---56# File Organization78## Purpose910Bring order to a directory without losing anything. Every file-organization task has one hard requirement that overrides all others: nothing is destroyed, and every action is reversible.1112## When to Use1314- Organizing a directory of accumulated files.15- Finding and removing duplicates.16- Reclaiming disk space.17- Applying a consistent naming convention.18- Sorting downloads, documents, or media.1920## Capabilities2122- Classification by type, content, and date.23- True duplicate detection by content hash.24- Consistent renaming.25- Space analysis.26- Safe, reversible operations.2728## Inputs2930- The directory, and how large it actually is.31- The organizing principle: by type, by date, by project.32- What must not be touched.3334## Outputs3536- A plan, shown before anything is executed.37- The reorganization, performed reversibly.38- A report of what changed.3940## Workflow41421. **Survey first** — Count, size, types, and the largest items. Never act on a directory you have not looked at.432. **Propose a plan and show it** — What will move where, what will be renamed, what will be deleted. This is shown, and confirmed, before anything happens.443. **Detect duplicates by content, not by name** — Hash the files. Two files with the same name are frequently different; two files with different names are frequently identical.454. **Move, do not delete** — Duplicates and junk go to a quarantine directory, not to oblivion. Delete only after the user has confirmed, and preferably never.465. **Preserve the metadata** — Modification times carry information. Do not destroy them by copying carelessly.476. **Report what happened** — With a way to undo it.4849## Best Practices5051- Never delete. Move to a quarantine directory and let the user delete it themselves once they are satisfied. A cleanup that destroys one needed file has failed regardless of how much space it reclaimed.52- Hash before declaring a duplicate. Filename matching produces false positives (two different `invoice.pdf`) and false negatives (`report.pdf` and `report (1).pdf` that are identical).53- Hash cheaply: compare sizes first, then hash only the files whose sizes match. Hashing a 200 GB directory in full is slow and almost entirely wasted.54- Dry-run everything. Show the plan, and require confirmation for anything destructive.55- Preserve the original name somewhere — in a manifest, if not in the filename. Renaming without a record is irreversible in practice.56- Watch for hard links and symlinks. "Deleting a duplicate" that is a hard link frees nothing and may break something.5758## Examples5960**Duplicate detection that is correct and fast:**6162```python63import hashlib64from collections import defaultdict65from pathlib import Path6667def find_duplicates(root: Path) -> dict[str, list[Path]]:68 """Two passes. Size first — cheap. Hash only the size collisions."""69 by_size: dict[int, list[Path]] = defaultdict(list)7071 for path in root.rglob("*"):72 if path.is_file() and not path.is_symlink():73 by_size[path.stat().st_size].append(path)7475 # Only files with an identical size can be identical. Everything else is76 # excluded without reading a byte.77 candidates = [paths for paths in by_size.values() if len(paths) > 1]7879 by_hash: dict[str, list[Path]] = defaultdict(list)80 for group in candidates:81 for path in group:82 by_hash[_hash(path)].append(path)8384 return {h: paths for h, paths in by_hash.items() if len(paths) > 1}858687def _hash(path: Path, chunk: int = 1 << 20) -> str:88 h = hashlib.blake2b(digest_size=16)89 with path.open("rb") as f:90 while data := f.read(chunk):91 h.update(data)92 return h.hexdigest()93```9495**A plan shown before anything is done:**9697```text98Survey of ~/Downloads:99 2,847 files, 41.2 GB100101 Duplicates (identical content) : 312 files, 8.4 GB reclaimable102 Installers (.dmg, .pkg, .exe) : 89 files, 14.1 GB — all older than 6 months103 Screenshots : 1,204 files, 2.1 GB104 Documents (pdf, docx) : 418 files105 Archives (.zip, .tar.gz) : 203 files, 9.8 GB106107PLAN — nothing is deleted. Everything is moved, and a manifest records the108original location of every file.109110 1. Duplicates -> ~/Downloads/_quarantine/duplicates/111 The most recently modified copy of each set stays where it is.112 312 files, 8.4 GB.113114 2. Installers older than 6 months -> ~/Downloads/_quarantine/installers/115 89 files, 14.1 GB.116117 3. Screenshots -> ~/Downloads/Screenshots/YYYY-MM/118 Organized by capture date (from EXIF where available, mtime otherwise).119120 4. Documents -> ~/Downloads/Documents/121 5. Archives -> left in place (they may be needed; too risky to move blind).122123 Manifest written to ~/Downloads/_quarantine/manifest.json.124 To undo everything: python restore.py manifest.json125126Proceed? [y/N]127```128129## Notes130131- The size-then-hash approach is not an optimization detail; on a large directory it is the difference between a task that finishes in seconds and one that reads every byte on the disk.132- The quarantine directory plus a manifest is what makes a cleanup safe. The user gets the space back the moment they empty it, and until then nothing is lost.133- BLAKE2 is faster than SHA-256 and entirely adequate for duplicate detection, where you are not defending against an adversary constructing collisions.