Artifact 281c0ed815ef6939222f211b2cb00134fa864daf32d01047b8b79e205724d0b9:
`zit add DIR` and `zit rm DIR` were hard errors that told the user to name individual files. That made the two most common invocations -- `zit add .` and adding a source subtree -- dead ends, and it was the last place where the CLI asked the user to do work the repository already knows how to do.
What changed:
- `add DIR` now expands to every untracked file beneath the directory. It reuses the ALREADY SHIPPED `extras.sweep` of the checkout root rather than introducing a second walker: the same walk, the same exclusions, the same no-follow symlink posture `addremove` and `clean` use. The sweep is lazy and memoized for the whole invocation, so `zit add a b c` over three directories still performs exactly one enumeration. Because the sweep already subtracts the tracked set and the depth-0 control names, neither can be re-inserted or re-reported by the directory path. - `rm DIR` expands over the TRACKED keys via `ManagedSet.iterate`, never over the disk, so a directory the user has already deleted is still forgettable. Keys are copied into the arena before the first removal, because `iterate()`'s slice and its backing bytes are invalidated by `remove` -- the same discipline `addremove` uses for its remove set. Ascending order is preserved and every expanded key still passes the per-key control-file guard (B-rm-7). - One containment predicate, `pathUnderDir`, serves both verbs, so the boundary case cannot diverge between them: `src2/c` is not under `src`, because containment requires the separator immediately after the directory's bytes. - No new diagnostic class. `zit rm .` with nothing tracked resolves to the existing not-tracked message rather than inventing an empty-expansion error; `zit add .` on a tree holding only control files adds nothing and succeeds. - The `add` and `rm` help text and exit-status wording now describe recursion instead of the removed directory error.
Test evidence: the B-add-5 and B-rm-5 contract tests were rewritten to pin the new behavior (a named symlink is still rejected; a directory now recurses; `.` at the root is never reported as outside the repository), and a new P3-F4 block of end-to-end tests covers nested trees, prefix-boundary siblings, multiple directory arguments sharing one sweep, --dry-run, --hard, and rm of a directory that no longer exists on disk. `zig build test` passes in this worktree after absorbing the sibling packaging commit (3645c03e2d), and the commit is proved against a clean `fossil open` checkout running `zig build` then `zig build test`. by dmestas on 2026-07-18 22:45:16.
C Implement\sadd/rm\sdirectory\srecursion\sthrough\sthe\sshipped\sextras.sweep\swalk\n\n`zit\sadd\sDIR`\sand\s`zit\srm\sDIR`\swere\shard\serrors\sthat\stold\sthe\suser\sto\sname\nindividual\sfiles.\sThat\smade\sthe\stwo\smost\scommon\sinvocations\s--\s`zit\sadd\s.`\sand\nadding\sa\ssource\ssubtree\s--\sdead\sends,\sand\sit\swas\sthe\slast\splace\swhere\sthe\sCLI\nasked\sthe\suser\sto\sdo\swork\sthe\srepository\salready\sknows\show\sto\sdo.\n\nWhat\schanged:\n\n-\s`add\sDIR`\snow\sexpands\sto\severy\suntracked\sfile\sbeneath\sthe\sdirectory.\sIt\n\s\sreuses\sthe\sALREADY\sSHIPPED\s`extras.sweep`\sof\sthe\scheckout\sroot\srather\sthan\n\s\sintroducing\sa\ssecond\swalker:\sthe\ssame\swalk,\sthe\ssame\sexclusions,\sthe\ssame\n\s\sno-follow\ssymlink\sposture\s`addremove`\sand\s`clean`\suse.\sThe\ssweep\sis\slazy\sand\n\s\smemoized\sfor\sthe\swhole\sinvocation,\sso\s`zit\sadd\sa\sb\sc`\sover\sthree\sdirectories\n\s\sstill\sperforms\sexactly\sone\senumeration.\sBecause\sthe\ssweep\salready\ssubtracts\n\s\sthe\stracked\sset\sand\sthe\sdepth-0\scontrol\snames,\sneither\scan\sbe\sre-inserted\sor\n\s\sre-reported\sby\sthe\sdirectory\spath.\n-\s`rm\sDIR`\sexpands\sover\sthe\sTRACKED\skeys\svia\s`ManagedSet.iterate`,\snever\sover\n\s\sthe\sdisk,\sso\sa\sdirectory\sthe\suser\shas\salready\sdeleted\sis\sstill\sforgettable.\n\s\sKeys\sare\scopied\sinto\sthe\sarena\sbefore\sthe\sfirst\sremoval,\sbecause\s`iterate()`'s\n\s\sslice\sand\sits\sbacking\sbytes\sare\sinvalidated\sby\s`remove`\s--\sthe\ssame\sdiscipline\n\s\s`addremove`\suses\sfor\sits\sremove\sset.\sAscending\sorder\sis\spreserved\sand\severy\n\s\sexpanded\skey\sstill\spasses\sthe\sper-key\scontrol-file\sguard\s(B-rm-7).\n-\sOne\scontainment\spredicate,\s`pathUnderDir`,\sserves\sboth\sverbs,\sso\sthe\sboundary\n\s\scase\scannot\sdiverge\sbetween\sthem:\s`src2/c`\sis\snot\sunder\s`src`,\sbecause\n\s\scontainment\srequires\sthe\sseparator\simmediately\safter\sthe\sdirectory's\sbytes.\n-\sNo\snew\sdiagnostic\sclass.\s`zit\srm\s.`\swith\snothing\stracked\sresolves\sto\sthe\n\s\sexisting\snot-tracked\smessage\srather\sthan\sinventing\san\sempty-expansion\serror;\n\s\s`zit\sadd\s.`\son\sa\stree\sholding\sonly\scontrol\sfiles\sadds\snothing\sand\ssucceeds.\n-\sThe\s`add`\sand\s`rm`\shelp\stext\sand\sexit-status\swording\snow\sdescribe\srecursion\n\s\sinstead\sof\sthe\sremoved\sdirectory\serror.\n\nTest\sevidence:\sthe\sB-add-5\sand\sB-rm-5\scontract\stests\swere\srewritten\sto\spin\sthe\nnew\sbehavior\s(a\snamed\ssymlink\sis\sstill\srejected;\sa\sdirectory\snow\srecurses;\s`.`\nat\sthe\sroot\sis\snever\sreported\sas\soutside\sthe\srepository),\sand\sa\snew\sP3-F4\sblock\nof\send-to-end\stests\scovers\snested\strees,\sprefix-boundary\ssiblings,\smultiple\ndirectory\sarguments\ssharing\sone\ssweep,\s--dry-run,\s--hard,\sand\srm\sof\sa\sdirectory\nthat\sno\slonger\sexists\son\sdisk.\s`zig\sbuild\stest`\spasses\sin\sthis\sworktree\safter\nabsorbing\sthe\ssibling\spackaging\scommit\s(3645c03e2d),\sand\sthe\scommit\sis\sproved\nagainst\sa\sclean\s`fossil\sopen`\scheckout\srunning\s`zig\sbuild`\sthen\s`zig\sbuild\stest`.\n D 2026-07-18T22:45:16.616 F .fossil-settings/ignore-glob 3580f973d7097226f91a27c7b61ec13d9d07880d95549f9b90ce86da5d867a23 F LICENSE 702ae7ff835a2a971dae11c5cae039803bf394b77a6d8ec9f18693e449eaaf26 F README.md 62e1f936cc2ea0e33d717539bdffd6126a639843069bd6256359f2b15cb06232 F build.zig 4bc7076a0a8ae829e0860d78027c84f78a3cb40fb2733891a16ac1ebed18715d F build.zig.zon 0eade544d4d7fc5a620348c6f29cc9219ff8c6d6377a05e756ca89860a524781 F docs/rfcs/draft-fossil-artifact-format-00.txt a6d542a5effeb41d0ea1e916058ccdc0a5819787f167ef1ebad7814f5680e7ad F docs/rfcs/draft-fossil-delta-format-00.txt d8025d6d75322e33887c3334a4b9cc729960028b3c994ed01e5a474e4b388fd2 F docs/rfcs/draft-fossil-repo-state-00.txt fd413bcb28001a8a07009ca0c769cfe666394ba79dd4f14a6461f2b592049595 F docs/rfcs/draft-fossil-sync-protocol-00.txt aa55791464a54ef4b651df8780ba5c46e95c723c7997d09a5b727fef658f920a F src/annotate.zig 0a6b6728099e63c84681d1e5228ebb45fafb55d65df1720358247fd6e6532c63 F src/artifact.zig c630afd9fbbcfaccd7b823105b4d05db1dc85b6af03080a6d58635ed064672ca F src/artifact_fuzz.zig 8bb359ddfe790818e109a1dd7ac0c1b5cec1a8fdc556f37e73504cdb7f933e17 F src/artifact_test.zig da999476660f7e6e1f50ed9da5c30f1c83d6b358e2cfcd265c6932959c85bdd1 F src/bisect.zig 7bfc45d3e0e339017826941463cc4e9118892d2898fbcd319e6e6ca85c4d3301 F src/checkout.zig ac48e76f22c8065f70d2a827ae00ffb99887af80950a93c1a3451a9a1ad0f403 F src/cli.zig c6baeb3b06666ba4940575c03fa395c108093fa6442a64f6a4e1e61326e795a2 F src/container.zig cef1028531f2763f94248b761c8779a1884e44bb854b8c45617494e3ead40e61 F src/container_test.zig a013652b1ed181161d3ad8fc9d8f052f7c27b61e129662632ceaf8cf189445ec F src/delta.zig e8f3a6297d47549aea146222732ccf514c39688414f0a74ba396ac91066686f3 F src/delta_fuzz.zig 9e0d8dbb8c82ca0ba1a80974c076e74e4f37072b21a10812efd5539875904719 F src/delta_test.zig b18d97e117dce8fbcc05bff6f6954effdb946bab6d36337efbe5ef64d10646a0 F src/dst.zig 66f7e5b264ded6a4ec82645e87e9780b344ff23360242e263c29415163b25501 F src/e2e.zig e1a176db4c05767e45a8eaafdd6edd685ac62036230506a6e176e495fa05fcc7 F src/extras.zig b8b36b7d6a3d348eeaf733124e671de1dba0bd4fe3cf2acd35268dfb3cf6acfb F src/fuzz_tests.zig 7194e723225d467cf43d60210bf96860e8c248a7ae63413b70c61ea9349d291d F src/logstore.zig a26e7b006f335b0bbb9bb41ac702ea42fab1c5b4ce9b13c7d25a89c84197aed0 F src/logstore_benchmark.zig e245474e1cc4f10617866f718aab3958f13b256c609b3e5fd562f500668388c0 F src/logstore_fuzz.zig 67b7bda806397f92c3b07c94854d9fafca56710090071a1c1c8ec94d137408f8 F src/logstore_test.zig a9baaee2cf009c988832d3632f52a1eeabf36afd52c891315f7673ce28442533 F src/main.zig 664d582c54b8169237936a5ba1e2e129782b9c766c57bd020e7a5077b1610c8a F src/managed.zig 81ccc2d6321fe3dff91f653305ae4e73bea0b3fae7db2ab2f0cf168d9518d601 F src/merge3.zig 22c24bbea4b5fa79879c984b8208e6b0b17b4837977ac36b79821648a4f900bd F src/pack.zig 0ea96cb540b3f43f6ff8ce95a3044ce939879bc6f9bf0eb82b26aab4089009c7 F src/packaging_test.zig 0697e7e19ac575d0b09f957256a90de524192b03128b15210bd37fec767097a4 F src/repocache.zig aae232a6b1b86e27c5242c0ee17ce9f2544299d99cf4936d8c2c9000e3166884 F src/repoimage.zig 7d2dc2f3c145e10e2d9ba6997705fd61a0cc0aedde8e171c253163d1279ccdd5 F src/repository.zig 6d41e75aafeddf6d6f0e0b04d280402354f2e8ee046d3027162733947a199176 F src/repository_test.zig 329c0fdfc9a84f96a94afbc5e7df5f7b2ef81651413e728ebf886e143ff5df5c F src/repostate.zig 72b063c9b779b6154940abc31c26644346d8ff9aca1ff8c7b6d0f47672ab90ec F src/repostate_test.zig 0645afa55d528f74e6cd1c84fc49568fff8c9c55956db30d48c4390f7a142599 F src/repoview.zig ec2958e1e8fd955c761d240ab80c3b3f62e3473991713c25ed5a603df984e403 F src/rfc_vectors/artifact_a1_1_sha1_manifest.txt 82d8fc78b3fbf85efe80cb05e79db40c910bf0551e55b728c6f9c2ca7a42ddb6 F src/rfc_vectors/artifact_a1_2_sha3_technote.txt 832e850f83aab2e2a754aa97591109f7f05fb41e4126c7e71c88a59d6d17e55d F src/rfc_vectors/artifact_a3_rcard_manifest.txt 47e412ef211f095e93275fadf527909e581eef42357d8437fc7e299bfc3e62a7 F src/rfc_vectors/sync_a1_round1_reply.txt 638813029b10f81303b88859279afb17095d7c779c993f4267870909acabbeb9 F src/rfc_vectors/sync_a1_round2_file_cards.txt 4fa397741f7521cc4181d5d6b04dc143187f57602384eaa688552115ecda27c2 F src/rfc_vectors/sync_a1_round2_reply.txt af956808190c79b37a384041690c36b78845b471d6c6c01d1cced6e3ee92e6b1 F src/rfc_vectors/sync_a2_reply.txt aa130a127cde210f793c7842960083c765e0ffc7b673374fabc8f0edfe03d763 F src/root.zig a2a13d28a5e50138557966adf64bbe121a4a70a4de0e2933f395e30924f9140a F src/serve.zig baa76b81f5cc21322e85f3d390b533c828c969f6d10580b5e8aa1f339202d945 F src/sim/faulty_file.zig 8e8a0325d0e987d1dd306588f910af5302e6456561eefae92c73c424fc1a9c94 F src/sim/gen.zig 0e237af570a73bc78e9a717a8ea59867611deb2348287765826ea4ff1942528b F src/sim/net.zig 8fb057012ce0a33f67c928cd696bc2761fea8ba4012b2daa33871b2ba5dfe772 F src/sim/oracle.zig 04b81d9472a0529d9e9d030779304c3d6c930f4db20b800ae3f9cbc5c178969a F src/sim/storage_sweep.zig 5472d632347079fb5c7e7ae300b46c6e8202b91202b1c51b181dd3c7bab8a306 F src/sim/sync_gen.zig 238d46942915b5c0e989dfc738b5927b5228468ea123e6f17b6fb593aa5cbf6e F src/sim/sync_sweep.zig 5458a84be08823d2d0fc9464665f1c3f3f26563185dc9ae24cf73259faa5d40e F src/smoke.zig f2565465a36a6c187d49d116f4e671ed843ea6c1a291fcc9c063af29a9baa3c8 F src/storage.zig 512205eb629d5af8d50ad336f1ef49f53b455abd52ee2f9b56d20893cafad39f F src/storage_benchmark.zig 871f00fef1a34a0b2ee7b9656a217a0cf52949fdd710b642a44dc6bf12d6866e F src/sync.zig b04119b2679887c6705ed4831899ca1911ca20afe0bac865022ac8b7ad879194 F src/sync_benchmark.zig 9a4542401c880750ff6565a2ca9f1d116341d1c5af704ef4a05c2732eeec8849 F src/sync_driver.zig 722ed498ea6b76900661a085d4257e51f6416d43ac1185e8ac15140de265f67e F src/sync_driver_test.zig b50450d2938fc60aa90cd9067fc45141a965b63dc2194f42d5109919e380d7df F src/sync_fuzz.zig 3f419ec3e3c0b4ecf99376e46d111c4fae9826d08f9968e649ffdf84d10f9e61 F src/sync_test.zig b47b05d3defeef61a02c9c9817b66288c48430713737c946734dba151bb17e7e F src/transport.zig 4652b2ab7f43ccd8af5ca6ef7604f8e7cd28353450b1ec90d206b881308f8b5f F src/undo.zig 2e213cbe02118e37b11554620b285deae5dbf5b4d8009ae6dc229b84375b7591 F src/update.zig dbc731b339f2a6b32af95ca0ee6b5a36629d8ef9944b3f7006f4c59d62bfdcfd F src/update_test.zig e80b1902fe29d1e8a6984f82974917ecda9b6460905b0c56b7cbc310ced3b73e F src/warmread_benchmark.zig 9421986d49deff3f720e491fb6d1c7162dc9322f4e67dd14198820c3992dc02a P 3645c03e2dbae1cb98e49711bbe110ba5ef0336a93d12a9f58391e3c2d86b1b4 R bec8a146b1eae562474a566742ae9fb6 U dmestas Z beea1fa74e6a3f6da28f7d9ca475c831