Git worktrees share the Git object database, but large checked-out files can still occupy separate disk blocks. On macOS/APFS, identical files can share their data blocks while remaining independently editable files.
In one dependency-heavy iOS repository, a post-checkout sharing pass shared about 761–766 MB of logical file contents per worktree, across roughly 217–249 files. An initial real-worktree experiment measured a 760.14 MB decrease in APFS private data. These are workload-specific results, not a promise for every repository. All MB figures here are decimal.
- Let Git create and check out the worktree normally. Finish dependency and project generation.
- Enumerate tracked regular files in the destination. This helper considers files at least 64 KiB and conservatively excludes Swift files, a policy inherited from its original repository.
- Compare each file with the same relative path in the existing checkout using SHA-256. Different branches are fine: different contents are left alone.
- Use native
clonefile(2)to clone matching source data into a temporary directory beside the destination. - Preserve and verify the destination's metadata, and verify the staged file's contents both before and after copying metadata.
- Check that the destination has not changed, then atomically replace it with the clone.
APFS allocates private blocks when either file is edited. Deleting the source does not invalidate the clone. Each file has its own inode. Hard links do not provide this independent-edit behavior.
Use a single agent to adapt this technique to the repository you are working in. Read its worktree creation scripts and repository instructions first. Keep the normal Git registration, checkout, dependency setup, and cleanup flow. Add one synchronous sharing pass after setup and before handing the worktree to a user, editor, or build. An ordinary implementation task is sufficient; no persistent goal loop or multi-agent orchestration is needed.
The attached Python helper is reference code, not a requirement to replace your tooling. Preserve its guarded per-file operation and adapt only the integration and repository policy. In particular:
- Start with tracked regular files at least 64 KiB. The
.swiftexclusion belongs to the original repository, not APFS; choose exclusions appropriate to your repository. Never traverse.git, ignored caches, or symlinked paths. - Compare actual bytes, including materialized Git LFS contents. Do not assume matching branch names, object IDs, sizes, or clean Git status prove identical on-disk contents.
- Keep sharing optional with an explicit opt-out. Unsupported platforms, filesystems, and metadata cases should keep ordinary copies and report why sharing was skipped.
- Put reports somewhere ignored or outside the checkout. Report cloned logical bytes separately from measured allocation savings. Preserve the complete before/after Git status, including pre-existing changes.
- Keep the destination exclusively owned throughout the pass. Do not run this automatically against worktrees already in use. A last-moment stat check cannot make replacement safe against concurrent writers.
- Implement the verification checklist below before enabling sharing by default. Follow the repository's normal approval process for that behavior change.
1. Native cloning with an explicit failure path. clonefile(source, staged, 0) creates an independent file backed by shared data blocks. The staged path must not exist and must be on the same filesystem as the source. In Python, the helper calls libc with ctypes and captures errno. A hard link is not a substitute. /bin/cp -c can fall back to a full copy, so command success alone does not prove sharing.
2. Verified publication. The important ordering is:
reject unsupported paths and metadata
record destination identity and hash
require source hash == destination hash
clone source into destination-local temporary staging
require staged hash == original destination hash
copy destination xattrs, mode, timestamps, and flags to staged clone
verify preserved destination metadata
require staged hash == original destination hash AGAIN
require destination identity unchanged
atomically replace destination with staged clone
If any pre-publication step fails, keep the destination and remove staging. Preserve cancellation cleanup. Do not delete the original first, and do not copy file data back over the staged clone when restoring metadata. The helper uses copyfile(..., COPYFILE_XATTR) for metadata-only xattr copying.
3. Sharing-aware measurement. The helper's sharing() function uses getattrlist(2) with extended file attributes: ATTR_CMNEXT_PRIVATESIZE, ATTR_CMNEXT_CLONEID, and ATTR_CMNEXT_CLONE_REFCNT. Verify attribute masks and packed layouts against the installed SDK before adapting this low-level code. Matching nonzero clone IDs establish fully shared data streams; ordinary copies provide a useful negative control. logical_bytes.cloned alone does not establish the number of bytes reclaimed.
- Identical files acquire matching nonzero clone IDs but retain different inodes; different contents remain untouched.
- Editing either clone leaves the other unchanged, and deleting the source leaves the destination readable.
- Destination metadata survives. A staged-content change during metadata copying is rejected by the final hash.
- Symlinks, ACLs, hardlinked destinations, unsupported filesystems, and cross-volume pairs are skipped safely.
- Inject clone failure and ENOSPC; interrupt during staging with SIGTERM. Originals must survive and staging must be cleaned.
- A repeat pass recognizes already-shared files. Git status and LFS hashes remain correct; branch switching and normal worktree cleanup still work.
- Run the normal build or another meaningful repository integration check after a real worktree creation. Distinguish fresh results from the historical measurements below.
Requires macOS, an APFS volume shared by both checkouts, Git, and Python 3.11+ (hashlib.file_digest). The original integration was tested with Python 3.13.11; support across macOS versions has not been established.
Save worktree_file_sharing.py outside the destination, then run after your normal worktree creation/setup command:
python3 worktree_file_sharing.py \
--source /path/to/existing-checkout \
--repo /path/to/new-worktreeThis command applies changes. Use it only while your setup process exclusively owns the new worktree: no editors, builds, Git operations, or other writers there until it finishes. Keep the source quiescent too. The stat checks are safeguards, not concurrency locks or protection against hostile filesystem races.
The helper prints a JSON summary and writes build/worktree-sharing.json inside the destination. Arrange for that report path to be ignored in your repository; otherwise the report itself will appear as an untracked file. The reported logical_bytes.cloned is the payload cloned, not a measurement of newly reclaimed physical storage. Repeating the pass recognizes already-shared files.
The script is a standalone extraction of an existing helper, retaining its conservative behavior. It skips symlinks, hardlinked destinations, different owners, ACLs, cross-volume pairs, and differing contents. Unsupported clone/attribute operations retain the ordinary files and report errors. Review the JSON: exit success can include skipped files. It does not copy Git metadata, ignored build caches, or an entire checkout.
SIGINT/SIGTERM unwind staging. SIGKILL can leave .worktree-sharing-* staging directories. This pass reduces retained space; checkout still initially needs the ordinary disk allocation. Later writes need free space for private blocks.
Identical bytes can have different extended attributes. In the original experiment, requiring identical xattrs shared only 203.8 MB. Copying the destination's xattrs onto the staged clone unlocked another 555.9 MB while preserving destination metadata.
The helper uses metadata-only copyfile(3) for xattrs and verifies mode, owner/group, modification time, flags, and xattrs before replacement. It does not promise identical inode numbers, creation times, or ctimes. ACL cases are deliberately skipped. A final content hash matters because some metadata can affect file-data representation.
Do not add up du or st_blocks and call that physical usage: shared blocks can be counted more than once. Use APFS clone IDs to verify sharing and private-size attributes or a controlled volume free-space experiment to quantify allocation. Snapshots and delayed reclamation affect when space becomes free.
An isolated APFS-volume experiment used the same 815.51 MB corpus for each strategy:
| Strategy | Additional volume allocation |
|---|---|
| Explicit byte-by-byte copy | 846.54 MB |
Per-file clones via /bin/cp -cR |
3.73 MB |
| Bulk native clone | 3.74 MB |
These are historical corpus measurements, not complete-worktree benchmarks. The integrated verification pass added about 12–20 seconds of setup time. Bulk cloning did not materially improve disk savings, so the integration kept normal Git checkout and used per-file replacement. Current local clonefile(2) documentation also discourages directory cloning.
The original integration verified independent edits, source deletion, branch switching, Git LFS hashes, unsupported-filesystem/cross-volume fallback, cancellation, and an app build. Real disk exhaustion was not tested (ENOSPC was injected), and a second Mac/oldest supported OS remains unverified. The accompanying tests cover the core file operation, not that full integration.
python3 -m unittest -v test_worktree_file_sharingRun on an APFS Mac. Tests create temporary files, use actual native clones, and inject failures to check that originals survive. They do not alter an existing checkout.
Consult the manuals shipped with your Mac: man 2 clonefile, man 3 copyfile, man 2 getattrlist, and man 1 cp. The helper's APFS attribute constants/layout come from the macOS SDK's sys/attr.h; xattr copying uses COPYFILE_XATTR from copyfile.h.
The reusable technique is: normal checkout, content comparison, staged native clone, destination metadata preservation, verification, atomic replacement.