Expand description
Worktree commands: add, remove, clean, convert, trash, snapshots and the placement ladder. What: Worktree commands: add, remove, clean, convert, trash, snapshots and the placement ladder. Why: Worktree lifecycle code shares plans and journals.
In TS you’d write (pseudocode):
// worktree/index.ts: one `export * from './file'` per module below.Modules§
- add_
timing - The record of where one wrapped
git worktree addspent its time, step by step, appended beside the commit timing record. What: The record of where one wrappedgit worktree addspent its time, step by step, appended to a size-capped file in the common Git directory beside the commit timing record. Why: An add that took thirteen seconds could not be split afterwards without astracethat distorted it. Each step is a stretch of the command (a walk of the source, a Git run, the snapshot call, a durable write), timed on the monotonic clock; a step inside another is taken out of the outer one, so the steps are disjoint and what none covers is reported asotherMs. The record goes to the file only, in one append when the command ends, with no flush to disk, and it never changes what the add does. - checkout_
workers - Whether the checkout of a new worktree may be given Git’s parallel checkout, and the option that does it.
What:
Whether the checkout of a new worktree may be given Git’s parallel checkout, and the option that does it.
Why:
Git’s
checkout.workersdefaults to one worker, and a checkout of twenty thousand files measured several times faster withcheckout.workers=0(one worker per logical CPU). The owner decided on 2026-11-03 “Pass only if unset”: a value the person set anywhere Git reads (a configuration file of any scope,GIT_CONFIG_*,GIT_CONFIG_PARAMETERS, a-cor--config-envthey typed) is theirs and stands alone; only where nothing sets the key does the wrapper put-c checkout.workers=0before the subcommand of its own checkout run. The files are read through the one configuration read the wrapper already makes, so no extra Git start is added. - inner_
git_ words - Where the words of a Git run the wrapper or the service started on its own go, now that none carries
--quiet. What: Where the words of a Git run the wrapper or the service started on its own go, now that none of those runs carries--quietor-q: one journal entry, from the service for a run it made and from the wrapper for a run it made while carrying out a person’s command. Why: The owner’s rule is “No the wrapper should never append –quiet. Letting native git emit whatever it wants to is expected”, and for the wrapper’s and the service’s own runs the answer was “Drop it everywhere (Recommended)”. Every such run is captured (run_gitpipes both streams intoGitOutput), so Git’s words would be discarded with the capture; this keeps them where a failure of the same run is already recorded (journalctl). A hook’s standard output and the service’s protocol replies are never the destination, and no other option or redirect hides the words in the meantime. - shell_
quote - Quoting of bytes printed inside a shell command for a person to run.
What:
Quoting of bytes that are printed inside a shell command for a person to copy and run.
Why:
A path or a kernel argument can hold spaces, quotes,
;,$(...)or bytes that are not UTF-8. Printed bare, a pasted command runs whatever follows the separator or substitution, or passes the wrong arguments. A value of safe characters is printed as it is; any other value is wrapped in single quotes (each single quote inside closed, escaped and opened again); a value with a control character or a byte that is not UTF-8 is spelled in the ANSI-C form$'...'of bash, zsh and ksh with those bytes written as\xHHescapes, so the printed text holds no control character and no line break. The same rules aspackage/wg/cli-quicker/src/shell-quote.ts. - worktree_
add_ binding - The binding of a
git worktree addto the directories Git made for it, its record, and the check that the binding holds. What: The binding of onegit worktree addto the objects real Git made for it (the destination directory and the administrative directory that registers it), the record that carries the binding, and the check that the binding still holds. Why: The wrapper completes an add in place, through the bound objects, and never deletes or unregisters a worktree: a path can be changed by another process between any check and any act, so no pathname or identity recorded earlier proves the right to remove something later. The binding is what a fast rung and the fallback checkout verify before they write into the worktree, and what the report of an interrupted add names. It is captured right after Git returned, and an add whose pair cannot be proven is left exactly as Git made it. - worktree_
add_ plan - The plan for one
git worktree addthat the cheap-worktree ladder serves: its destination and insertion point. What: The plan for onegit worktree addthe cheap-worktree ladder may serve: where the new worktree goes and where in the argument list a rung inserts--no-checkout, or nothing when the invocation uses a form the ladder does not know. Why: The ladder runs real Git once with the person’s arguments, so it only needs the destination path and a promise that every other word means what the table says. An option the table does not list, a bundled short flag, a global option that changes how Git reads configuration, or a destination whose parent does not exist makes the whole invocation a plain Git run, which is always correct. - worktree_
attributes - What:
Git’s attribute-file lexer and pattern matcher over raw bytes, as far as a refusal needs them: which lines of an
attributes file set an attribute that can change a file’s bytes on the way into the index, and whether the pattern of
such a line may apply to a path.
Why:
Paths and patterns are byte strings to Git, and
attr.csplits a line on the four blank bytes space, tab, carriage return and line feed only, ends a line’s text at a NUL, and skips a UTF-8 byte order mark on the first line of a worktree file. Any other reading of the same bytes (Unicode whitespace, lossy decoding, Unicode case folding) can miss a line Git applies, and a miss accepts a file whose bytes Git would convert. Every spelling the matcher does not model answers that the line may apply, so the error is always a refusal. - worktree_
clean - Whether a worktree is clean enough to copy from or to remove without asking Git, proven in process.
What:
Whether a worktree is clean enough to be copied from, or removed without asking Git: no operation in progress, an
index equal to the tree of
HEAD, every tracked file unchanged by its recorded size, times and inode, and no untracked file that Git does not ignore. Why: A snapshot or a clone carries the bytes of the source’s files, so any change the source has not committed would reach the new worktree, and a removal by the subvolume ioctl cannot be undone. The tracked-file checks read the index in process (no Git process) with the same comparison Git’s fast path makes, and refuse every shape they cannot prove: a split or sparse index, a skip-worktree or assume-valid entry, a submodule, an entry as new as the index file (Git re-hashes those, so their stat fields prove nothing). The one question the index cannot answer, whether an untracked file is ignored, goes to onegit ls-filesrun. Every refusal returns its reason in words for the log. - worktree_
clean_ content - What:
The decision, by content and without a Git process, whether a tracked file that its index entry’s stat data cannot
vouch for (a racily clean entry, a stale stat) still equals the committed blob: hash the file as a blob and compare it
with the entry’s object name, refusing where a content conversion could make the two differ.
Why:
Git settles those entries by hashing the file; stat data alone cannot tell a modified file from a copied or cloned one
that kept the index’s old inode and times. The hash of the raw bytes is conclusive only when Git’s own conversion on the
way into the index (
filter,ident,working-tree-encoding, or a line-ending conversion that strips a carriage return) leaves the bytes alone, so a path such an attribute applies to, and a file with a carriage return, are refused with that reason instead of guessed. The file is read through a handle opened below the worktree root without following a link, its type and mode are judged on that handle, and its state is compared before and after the read, so the bytes that were hashed are the bytes of the one file that was judged. - worktree_
clone - The clone rung: tracked files cloned into the new worktree, the source’s index installed with fresh stat fields.
What:
Rung 2 of the cheap-worktree ladder: each tracked file of the clean source is cloned (
FICLONE) into the new worktree with the source’s mode and times, and the source’s index is installed with fresh stat fields. Why: A clone shares every data block with the source on btrfs, on XFS with reflink and inside an overlay, which the snapshot rung cannot reach (overlayfs and XFS have reflink but no subvolumes). It takes no less time than Git’s checkout on btrfs (doc/planning/cli-git-btrfs-worktree.md, option B), so it saves disk, not time, and it is the accelerated rung where a snapshot is impossible. The wrapper’s own index writer isrewrite_index_stat_with: with each file’s size and modification time kept, only the inode, change time and device of every entry change. The first failure ends the rung with its reason, the entries this run created stay (a name cannot be checked and unlinked in one step), and Git’s own checkout completes the worktree and overwrites the tracked ones. Every source file is opened below a held handle of the source root without following a link, and its descriptor’s metadata, not a path, is compared with the frozen index entry before and after the clone; every destination entry is created below a held handle of the destination. A directory swapped for a link while the run goes on is an error, never a detour. - worktree_
completion - Bringing added paths’ worktree copies to their landed bytes.
What:
Bringing each added path’s worktree copy to the landed bytes after a commit landed,
with
descriptor-bound checks that never overwrite a concurrent edit.
Why:
An added path’s committed bytes differ from its worktree copy (a policy corrected it);
the
copy is replaced only while it still holds the original bytes,
through a same-directory
temporary file.
A copy holding neither is kept and reported,
since the commit already
landed (
src/policy-engine/commit-transaction-added-paths.ts,-worktree-check.ts,-worktree-replace.ts). - worktree_
convert - Converting a linked worktree that is an ordinary directory into a btrfs subvolume at the same path, resumably.
What:
Convert one registered linked worktree that is an ordinary directory into a btrfs subvolume at the same path,
resumably: quiesce it, copy it with cloned data into
<path>.cli-git-converting, prove the copy equals the original, swap the two with one atomic exchange, refresh the index stat fields, and remove the old tree only after the swap is verified. Why: A snapshot of the main checkout shows a nested worktree that is a subvolume as an empty directory, and one that is an ordinary directory as a full copy that must then be removed (0.5 s each). Converting each ordinary worktree once makes every later snapshot skip it for nothing. The registration is untouched: the path and the bytes of the.gitfile do not change, so the administrative directory’s back-pointer stays valid. A journal in the administrative directory records the phase, so a rerun completes the conversion in place; nothing is deleted that the journal does not prove this command created. - worktree_
convert_ arguments - The grammar of
git cli-git convert-worktree-to-subvolume. What: The grammar ofgit cli-git convert-worktree-to-subvolume. Why: The grammar is parsed on every platform by the management namespace; only the run needs Linux and btrfs. - worktree_
convert_ command git cli-git convert-worktree-to-subvolume: the worktrees to cover and the report. What:git cli-git convert-worktree-to-subvolume: parse the options, find the worktrees to convert, run the conversion on each, and turn the results into lines and an exit status. Why: A conversion takes seconds to minutes per worktree and is bound by the filesystem’s metadata work, not by any one worktree’s state, so the worktrees are converted side by side within a budget the host allows (parallel_budget.rs); progress lines go to standard error as they happen for the first unfinished worktree, and every worktree’s lines come in input order (parallel_runner.rs). Exit status 0 means every worktree is converted, already a subvolume, previewed, or skipped under--all; 1 means a named worktree was refused before anything changed; 2 means a step failed after the journal began, with the way forward. One worktree’s failure never stops another: each has its own journal and claim.- worktree_
convert_ compare - The read-only comparison of the old tree of a conversion with the installed one. What: A read-only comparison of the old tree of a conversion with the installed one: the first entry of the old tree that the installed tree does not hold in the same form, or that this command does not delete. Why: The old tree is deleted only after the installed copy is known to hold everything in it. The comparison changes neither tree and copies nothing: a difference keeps the old tree and says which entry differs, and whoever knows which version is wanted settles it. An entry on another device (a nested subvolume or a mount point) is reported the same way, because deleting it is not this command’s to do.
- worktree_
convert_ journal - The journal of a conversion and the retirement of the kernel lock file older releases left.
What:
The journal of one conversion of a worktree into a subvolume.
Why:
The journal is the only proof that the converting subvolume and the index lock are this command’s, and it names the
identity of the old root and of the subvolume it created, which is what every later phase checks before it reuses,
exchanges, destroys or deletes anything. The journal is written through exclusively created files and never replaces
a file it did not make. One command at a time owns a conversion through a numbered claim file
(
single_owner_claim.rs) taken before the journal is read, so no kernel lock is held. - worktree_
convert_ remove - The removal of the old tree of a conversion beneath a pinned descriptor. What: Remove the old tree of a conversion, but only the directory the journal recorded, pinned by a descriptor from the moment its identity is checked until its last entry is gone. Why: A device and inode read from a path say nothing about what the path names a moment later: another process can rename the recorded directory away and put an unrelated one at the name before a recursive delete resolves it again. The directory is opened below its parent without following a link, its identity is read from that descriptor, its entries are removed through the descriptor’s own path, and the last step removes the name only while it still names the same object and only if that object is empty (a directory removal cannot take a tree).
- worktree_
dirty_ restore - The restore of a snapshot of a main checkout with uncommitted changes, and its proof.
What:
The restore of a snapshot of a main checkout that had uncommitted changes: make the copy’s index and files equal the
tree of the new worktree’s
HEADwithgit read-tree --reset -uandgit clean -f -d, then prove it in the stage. Why:git worktree addfrom a checkout with changes makes a worktree that holds the committed files and nothing of the changes. The snapshot carries the changes, so the copy is reset the way Git’s ownreset --hardwould reset it, from the index the snapshot carries: that index still records the files as they were before they were edited, so the reset rewrites exactly the edited, deleted and staged paths and leaves the rest. Untracked files that no ignore rule covers go, ignored files stay. Both Git runs are the ladder’s own steps on the new worktree (its Git directory and its stage), started as real Git children by the lifecycle’s runner, so no policy of the wrapper that guards a person’sresetorcleanapplies to them. The proof runs before the stage is swapped in; a proof that fails ends the rung and Git’s own checkout makes the worktree. The main checkout is read only to be snapshotted and listed, and is never written. - worktree_
identity - Repository and worktree identity as reported by real Git.
What:
Ask real Git which repository and worktree an invocation selects.
Why:
Configuration lives at the worktree top level,
and worktree policies need to
know main versus linked.
Git applies
-C,--git-dir,--work-treeand its environment itself, so the wrapper never re-implements that selection. - worktree_
ladder - The cheap-worktree ladder around one
git worktree add: what runs before real Git and after it. What: The cheap-worktree ladder around onegit worktree add: what runs before real Git and after it, on Linux; on other systems the ladder does nothing and Git’s own checkout stands. Why:doc/planning/cli-git-btrfs-worktree.mddesigns the ladder and measures it: a snapshot of a clean subvolume worktree, then clones of its tracked files, then Git’s own checkout. The lifecycle of the copy (worktree_copy.rs) calls this one module, so the rungs stay out of its settlement and recovery code. - worktree_
ladder_ finish - The ladder’s steps after real Git returned: the snapshot rung and the fall back to Git’s checkout.
What:
What the ladder does after real Git returned: bind the add to the directories Git made, the snapshot rung’s steps (read
the new
HEAD, snapshot, apply the tree difference, swap into place, print Git’s checkout line), the clone rung’s steps, and the fall back to Git’s ownreset --hardin the bound worktree when a step cannot finish. Why: Every step before the swap leaves the destination exactly as Git’s--no-checkoutrun made it, so a failure there completes the worktree the way Git would have. The ladder never deletes or unregisters a worktree: when the fallback fails, or the bound objects are no longer what they were, the worktree and its registration stay exactly as they are and one loud report names what is incomplete and the command that removes it. The only deletions are of objects this process made and still checks: the stage it took and never exchanged. The line that Git’s own checkout would have printed is printed here, so the person cannot tell the rungs apart except by the time they take. No hook runs on any rung: every real-Git start of the wrapper switches hooks off (hooks_off.rs). - worktree_
ladder_ run - The ladder’s choice of rung before real Git runs.
What:
The ladder around one
git worktree addon Linux: the choice of rung before real Git runs, and, after it returned, the snapshot rung’s steps with a fall back to Git’s own checkout whenever one cannot finish. Why:doc/planning/cli-git-btrfs-worktree.mddesigns it. With a snapshot source Git runs with--no-checkout(branch validation, administrative files, configuration copy and every message stay Git’s), the wrapper snapshots the source into a stage name beside the destination, makes it the worktree, applies the difference between the source’s tree and the newHEADwithgit read-tree -m -u, and swaps it into place. Any failure before the swap destroys the stage and completes the worktree the way Git does, withgit reset --hard; the person only ever sees Git’s result. - worktree_
ladder_ target - What both filling rungs read from Git’s
--no-checkoutresult, and the one step that applies the tree difference. What: What both rungs that fill a worktree themselves need to know about the target of agit worktree addonce real Git ran without a checkout: the administrative directory, Git’s pointer, the commit and treeHEADnames, whether the tree differs from the source’s, the line Git’s checkout would print, and the step that applies the difference. Why: The snapshot and clone rungs differ in how the source’s files reach the destination and agree on everything else, so the shared reading of Git’s result and the singlegit read-tree -m -ustep live in one place. The first version keeps Git for the two-tree update because it owns the conversion filters (doc/planning/cli-git-btrfs-worktree.md). - worktree_
removal_ rescue - What becomes of the unshown push notices and unfinished pushes of a linked worktree that Git removes.
What:
What happens to the unshown push-failure notices and the unfinished backup pushes of a linked worktree when Git removes
that worktree: before
git worktree removeorgit worktree pruneruns, the wrapper copies those records into a snapshot under the common Git directory, and a later wrapper command settles each snapshot, turning it into notices that any worktree shows when the worktree is gone and discarding it when the removal did not happen. Why: A worktree keeps its records in its own Git directory, which Git deletes with the worktree. Without this a failed push nobody had seen yet, or a push the service had not run yet, would vanish with no word, and the person would never learn that a branch is not on the remote. The wrapper hands the process over to Git for the removal, so it cannot look afterwards; the snapshot is taken for every linked worktree that has records and settled later. A Git directory that is gone proves the removal. A Git directory that is still there proves a refusal only once the command that took the snapshot has ended (the process keeps its identity when it becomes Git, so the snapshot names it) and the worktree’s checkout is intact; while that command lives, or a checkout is missing from a Git directory that is still registered, the removal may be under way or cut short, and the snapshot is kept. A snapshot that cannot be fully promoted, or fully taken, is kept as far as it goes and reported on standard error, because a line in the diagnostic log is not a word to the person. - worktree_
remove - The removal of a clean subvolume worktree by one ioctl, and the plan of the
git worktree removeit serves. What: The removal of a clean linked worktree that is a btrfs subvolume, by one subvolume destroy call and the deletion of its administrative directory, in place of Git’s file-by-file removal. Why: Git unlinks every file of the worktree (0.7 s without ignored state, 3.6 s with a large ignored tree in the measurements ofdoc/planning/cli-git-btrfs-worktree.md), and the subvolume call takes milliseconds. The call cannot be undone, so the wrapper takes it only when the same conditions hold that make Git remove without--force: the worktree is registered in the repository the command runs in, under the name Git would pick, is not locked, is clean (worktree_clean.rs), and holds no submodule. The mount must also allow the call to an unprivileged user (user_subvol_rm_allowed). Every other case, and every call that the kernel refuses, is left to Git, which then words the refusal itself. - worktree_
remove_ plan - The plan of a
git worktree removethat names one directory and no option. What: The plan for onegit worktree removethat the wrapper may serve with a subvolume destroy: the one directory it names. Why: Only the plain form (one directory, no option, a non-empty name) is served;--forceand every other option change what Git checks, and a name Git resolves by its last path component is not a directory here, so those stay with Git. - worktree_
remove_ registry - What:
Which registration of the invoking repository a worktree directory belongs to, and whether Git would pick that same
worktree for the name the person typed.
Why:
Git removes only a worktree that its own list for the repository it runs in contains, found first by the last
components of every registered path and only then by the resolved path (
find_worktreein Git’sworktree.c). The.gitfile of a worktree and thegitdirfile of an administrative directory that name each other prove only that the pair is consistent, not that the repository the command runs in registered it, and a directory that exists under the typed name proves nothing about the worktree Git would remove for that name. Anything but a registration of the invoking repository that Git would also pick is left to Git, which words its own answer. - worktree_
slow_ route - The plain sentence that says why an add was not made as a snapshot, and where it goes.
What:
The plain sentence that tells a person why a
git worktree addwas not made as a snapshot, and the places it goes: one line on standard error unless--quietwas given, one warning entry in the journal, and the debug log that already holds the detail. Why: A snapshot of the main checkout takes seconds and Git’s checkout with the ignored-state copy takes minutes. The choice between them was made silently, so two slow adds could not be explained afterwards. The sentence names the cause in the person’s words (a change, a running Git command, a stale index, a missing subvolume) and what makes the next add quick, without the names of the ladder’s steps. A filesystem that is not btrfs gets no sentence: nothing the person does there would make the add a snapshot, so the line would only repeat itself. - worktree_
snapshot - The snapshot rung’s mechanics: snapshot to a stage name, make it a worktree, swap it into place.
What:
The mechanics of rung 1 of the cheap-worktree ladder: a snapshot of the source subvolume taken at a stage name beside
the destination, made into a worktree there (its own
.gitpointer, no stubs of nested worktrees, the source’s index with refreshed stat fields), and swapped with the destination in one atomic step. Why: The snapshot shares every data block with the source and carries its ignored state at no cost (doc/planning/cli-git-btrfs-worktree.md). It is made at a name Git never hands out, so the destination is complete or untouched at every instant:RENAME_EXCHANGEputs the finished subvolume at the destination and the empty directory real Git made at the stage name, which is then removed. A source that changed while the snapshot was taken is caught by comparing the snapshot’s own files with the index entries copied from the source. - worktree_
snapshot_ source - The snapshot source: whether the creating worktree can be snapshotted for a new worktree, and what that needs. What: Whether the creating worktree can be the source of a snapshot for a new worktree, decided before real Git runs, and everything the snapshot needs to know about it. Why: A snapshot copies the whole subvolume, so the source must be a subvolume root on the same filesystem as the destination, clean, and free of every feature whose result would differ from Git’s checkout: a sparse checkout, a worktree-specific configuration, a submodule, and a nested linked worktree that is an ordinary directory (a snapshot carries its files, and removing them one by one costs more than the checkout saves). Each refusal returns its reason in words; the caller logs it and falls to the next rung without a word to the person.
- worktree_
subvolume - The empty subvolume made at the destination of a
git worktree addbefore Git runs, and its cleanup. What: The empty btrfs subvolume the wrapper makes at the destination of agit worktree add, before real Git runs, and the cleanup of it when Git leaves it empty. Why: A worktree that is a subvolume is a source of a later snapshot with no migration, and it is removed in one call. Git accepts an existing empty directory as the destination and fills it, and an empty subvolume is an empty directory (doc/planning/cli-git-btrfs-worktree.md, “Creating new worktrees as subvolumes”). Git removes the destination again only when it created it and failed late; a failure before that point (a branch that exists, a name Git rejects) leaves the directory, so the wrapper removes the empty subvolume itself. A destination that is not on btrfs, or a subvolume the kernel refuses, leaves Git to make its ordinary directory without a word to the person, with one diagnostic line for the log. - worktree_
trash - Setting aside what a snapshot of the main checkout leaves out, and having the commit service empty it.
What:
The leave-out step of a snapshot of the main checkout: set aside the nested worktrees that are ordinary directories,
the other checkouts, the caches and the run records that the main-checkout rule leaves out, into a trash directory inside
the new subvolume, and write down what was made and moved; and, once the worktree is in place, hand the trash to the
commit service as a work item that empties it (
worktree_trash_dispose.rs). Why: A snapshot carries everything, so the leave-out is a removal. Unlinking about 495 thousand entries takes 14 s, which the person should not wait for; renaming 480 directories into one trash directory takes 1.6 s, 1.2 s of which is the walk. The rename keeps the new worktree final at once. The.gitpointer of a nested worktree that was copied names another worktree’s administrative directory, so it is unlinked before anything else and before the add returns. Nothing the main checkout tracks is ever moved, and every move and removal names one plain entry below a held directory handle. - worktree_
trash_ dispose - Disposal of a snapshot’s trash: the work item, the foreground fallback and the service’s handler. What: The disposal of the trash a snapshot’s leave-out made: hand it to the commit service as a work item, or empty it here when the service cannot be reached, and the service’s handler. Both delete only the directory the leave-out recorded and only the entries it recorded, found by walking down from the worktree’s handle without following a link. Why: A name of forty hexadecimal digits proves nothing about who made a directory, and a path string is resolved again by every call that takes it. The manifest says which objects the leave-out made and moved; the disposal opens each directory on the way by one plain name below the handle of the one above, compares identities, and leaves anything else alone with a reason. A record stays until the trash is gone or the service has taken it, so a failure is never a silent success.
- worktree_
trash_ entries - The removal of the entries a snapshot’s leave-out moved into its trash, side by side.
What:
The removal of the entries a snapshot’s leave-out moved into its trash, side by side.
Why:
The entries are separate directories that share only the held trash handle, each opened by its own name and removed only
when it is the recorded object, so they are independent items: a reason for one entry never stops the others. They run
within the budget the host allows (
parallel_budget.rs), which inside the commit service’s control group is the two threads and the memory that unit is given, and the reasons come back in entry order (parallel_runner.rs). - worktree_
trash_ manifest - What a snapshot’s leave-out made and moved, with identities, as the text a work item carries. What: What a snapshot’s leave-out made and moved, with identities: the trash directory it created and each directory it renamed into it, as the text a work item carries and the handler reads back. Why: A name of forty hexadecimal digits proves nothing about who made a directory. Only what the leave-out itself created or moved may be deleted, so the leave-out writes down the identity of every object it put in the trash and the disposal deletes an object only when it is still the one written down.
- worktree_
trash_ remove - Removal of a directory tree beneath a held parent handle, never following a link.
What:
Remove a directory tree beneath a held parent directory: every directory is opened by one plain name below the handle of
its parent without following a link, and every entry is unlinked from the handle of the directory that holds it.
Why:
A path string is resolved again by every call that takes it, so a directory replaced by a link between a check and the
removal redirects the removal. Here no call resolves a path: a link, a mount point or a directory on another device is
an error that names it, and the identity of the top directory is compared with the recorded one on the object that is
opened, so a replacement is never emptied. A directory the owner cannot write to is given the owner’s permission bits
through its descriptor on the way down, as
subvolume_remove.rsdoes for the old tree of a conversion.