Skip to content

Worktree 0.18.0

Choose a tag to compare

@github-actions github-actions released this 13 Oct 03:30
· 16 commits to main since this release
  • discard-cache (and finish --discard-cache, sweep) treats every ignored entry Git reports
    (status --ignored=matching, file or directory) as disposable, unless the repository's keep
    file keeps it or it is or holds a repository. An ignored entry no structural rule recognises
    is discarded as the new kind ignored-output instead of retained. This reverses "anything
    unrecognised is retained": a record written into an ignored directory (target/backlog-input/,
    for instance) is deleted unless the keep file keeps it.
  • The keep file is .worktreekeep at the tree root. Its lines are Git exclude patterns matched
    by Git's own exclude engine (git ls-files --others --ignored --exclude-from), so negation,
    directory-only patterns and anchoring mean what they mean in .gitignore; a path is kept when
    a pattern matches it or one of its ancestors, and a directory a pattern names is kept even
    when it holds no file (a parent Git lists only because everything in it matches is not). Git
    cannot re-include a path below an excluded
    directory, so !dir/file after dir/ keeps dir/file too; write dir/* to let a negation
    take effect. An absent keep file keeps nothing. A symbolic link, a directory or an unreadable
    file there refuses the discard as keep-file-invalid and deletes nothing. The keep file itself
    is never deleted, even when Git ignores it. Kept files are reported one by one in
    retained_ignored.
  • A repository is retained whole: an entry named .git, a directory holding one directly, or a
    Git directory. So is a mount point (read from /proc/self/mountinfo on Linux, so a bind mount
    from the same filesystem counts; elsewhere only another device does), an entry on another
    device, and one that cannot be read: a directory whose names cannot be listed or a regular file
    this user cannot open. A directory holding any of these, or a kept path, at any depth is never
    deleted whole, whatever kind it is: it falls to its direct entries, each classified by the same
    rule. 0.17.1 retained a profile, node_modules,
    virtual environment, tool cache or Flutter output directory holding a repository whole; each
    now falls to its parts.
  • A Cargo target holding anything but Cargo's layout falls to its parts: profiles stay
    cargo-profile, tmp/ stays cargo-target-tmp, and its CACHEDIR.TAG, .rustc_info.json
    and everything else are discarded as ignored-output.
  • A symbolic link is deleted itself and never followed, and a deletion is refused as
    cache-state-changed when a directory above the entry is no longer a real directory. An
    ignored path that is not UTF-8 refuses the discard as cache-path-not-utf8 before anything is
    deleted, since the report could not name it.
  • The keep file, read again and scoped to the entry, the mount table and the walk for
    repositories are repeated immediately before each deletion.
  • archive (and so finish --archive and a sweep's archive) reads the keep file too: a kept path
    is never build layout, so it stays in dirty.patch even below a recognised cargo target or
    Flutter output directory, and finish --discard-cache --archive archives every kept record.
    A keep file that is not a readable regular file keeps every path there, so archive leaves
    nothing out; it does not refuse. Removal through a worktree.archive/3 archive deletes a
    left-out file only while it is not kept and the keep file's bytes are those it was split with.
  • ignored-output is a new kind value; the CLI JSON protocol stays 4 and no field changes.
    archive never modifies the tree; sweep never removes a tree.
  • prune-archives --strip-build-output also strips ignored output from existing archives: a
    section that purely adds an untracked file the keep file does not keep, whose path Git's
    exclude engine ignores under both HEAD's .gitignore files (read from Git) and the tree's as
    archived (HEAD's with the patch's .gitignore additions, modifications and deletions applied).
    A path the tree's own .gitignore change un-ignored stays; where those changes cannot be
    applied, nothing at or below the touched file's directory is stripped. A non-layout file below
    an ignored target/, node_modules/ or build/ is stripped now; 0.17.1 kept it.
  • Neither info/exclude nor any core.excludesFile is read: either may have changed since the
    archive was written, so a path only they ignore stays. Each rule set is asked in one
    git check-ignore --no-index --stdin -z over a scratch work tree in the system's temporary
    directory, removed on every path out; a dry-run writes nothing below the archive root.
  • The strip never strips a path the keep file keeps, layout or ignored output. The keep file is
    read from HEAD's tree, or from the patch when it purely adds .worktreekeep (that section
    stays). A patch that changes a tracked keep file, or a keep file in HEAD or the patch that is
    not a regular file (a symbolic link, a gitlink) or holds a NUL byte, keeps every path.
    Repository content (a .git component, or a Git directory the patch shows) stays whatever the
    rules say.
  • Stripped ignored output is recorded as a build_output target of origin strip, per outermost
    directory both rule sets ignore that HEAD does not hold and that holds no path whose section
    stays, or per file. The archive format stays worktree.archive/3 and the CLI JSON protocol
    stays 4.