1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//! Conservative tree checkout with literal blob bytes and native POSIX modes.
//!
//! [`Repository::checkout_tree`](crate::Repository::checkout_tree) takes an explicit clean
//! baseline and target, with default [`Limits`] and no cancellation;
//! [`Repository::checkout_tree_with`](crate::Repository::checkout_tree_with) also takes limits and
//! a cancellation flag. [`Report`] records completed operations and
//! index publication; [`Failure`] preserves that report and cleanup failures when a later phase
//! stops. Inspect the actual worktree before recovery because mutations are not rolled back.
//!
//! The selected tree replaces a clean baseline and index without changing HEAD or references.
//! Pass the tree represented by the current index as the baseline, or `None` for an empty index
//! (including a no-checkout clone). A nonempty baseline with a missing index is rejected. An unborn
//! HEAD needs no special treatment because HEAD is neither read nor switched. Staged changes
//! relative to the supplied baseline, conflicts and dirty tracked paths are refused, even on
//! unchanged paths.
//!
//! Callers must exclude other worktree writers and renames of the root/ancestors throughout the
//! call, and protect repository metadata, objects and mount topology from replacement. The index
//! lock excludes cooperating index writers. Descriptor-relative no-follow traversal, exact names,
//! content verification and repeated identity checks detect observed changes, but POSIX has no
//! conditional rename/unlink by inode: arbitrary writers racing the final check are not excluded.
//! Hardlink/mount attacks and ABA replacements are outside this contract. No batch atomicity,
//! automatic rollback, crash durability, branch switching or force mode is provided.
//!
//! Linux/macOS only, on filesystems with native hard links, symlinks and POSIX modes. macOS paths
//! and names in enumerated directories must be ASCII. Both platforms conservatively reject ASCII
//! case aliases, metadata names, unsafe components, gitlinks, nested repositories and index
//! extensions other than `TREE`, `FSMN`, `UNTR`, `EOIE` and `IEOT`. Intent-to-add and skip-worktree
//! entries require caller policy and are refused. Path components are limited to 255 bytes. Windows
//! returns unsupported. Raw checkout preserves blob bytes without attributes, filters, EOL
//! conversion, ignores or config overrides. Symlink blobs must be nonempty, NUL-free and at most
//! 1024 bytes. Mode changes replace files, with permissions 0644/0755; ownership, ACLs and other
//! metadata are not preserved.
//!
//! Preparation also projects the nested-repository marker policy across the planned operations,
//! including retained siblings and directories. A non-root directory containing `HEAD`, `objects`
//! and `refs` (including ASCII case aliases) is refused even when these are ordinary files. This
//! deliberately excludes some valid Git trees so checkout cannot trigger its own live guard after
//! starting to mutate the worktree. The live guards remain active at every mutation boundary.
//!
//! Preparation holds the index lock, verifies trees/blobs, clean tracked content, names and
//! obstructions, then constructs the replacement index before worktree mutation. Mutation removes
//! changed tracked leaves, removes only known empty directories needed for directory-to-file
//! transitions, creates missing directories and installs leaves. Each operation rechecks its path
//! and ancestors. Publication rechecks all target content and the index before renaming the index
//! lock. Preparation and publication enumerate each directory's names once; mutation instead
//! creates every directory and leaf exclusively, so a name that appeared after preparation,
//! including a filesystem case or normalization alias, is refused rather than replaced. Leaves are
//! written under their final names, as Git writes them, so a concurrent reader can observe a file
//! whose content is still being written. An install is reported only once its content is
//! complete; a failed write removes the partial leaf while it is still the file checkout created.
//! Derived `TREE`, `FSMN`, `UNTR`, `EOIE` and `IEOT` caches are discarded, including on
//! unchanged checkouts. Git can rebuild them; girt does not consult fsmonitor or untracked caches.
//! New index entries have zero cached stat words and cleared assume-valid flags; Git may
//! need `git update-index --refresh` before stat-only plumbing such as `git diff-files`.
//! Unrelated content and empty directories are retained. Failures report completed operations
//! in execution order, the active stage and owned artifacts whose cleanup failed. Earlier
//! operations remain applied; inspect the report and actual files before recovery, rather than
//! blindly retrying.
//!
//! # Example
//!
//! Materialize a tree into a new working tree whose index is still absent, as after a no-checkout
//! clone. HEAD and references are unchanged:
//!
//! ```
//! use girt::{EntryMode, InitKind, ObjectFormat, Repository, Tree, TreeEntry};
//!
//! # if cfg!(not(any(target_os = "linux", target_os = "macos"))) {
//! # return Ok(());
//! # }
//! let directory = tempfile::tempdir()?;
//! let repository = Repository::init(
//! ObjectFormat::Sha1,
//! directory.path().join("w"),
//! InitKind::Worktree,
//! )?;
//! let loose = repository.loose_objects();
//! let id = loose.write_blob(b"#!/bin/sh\necho hello\n")?;
//! let entry = TreeEntry {
//! mode: EntryMode::Executable,
//! name: b"hello.sh".to_vec(),
//! id,
//! };
//! let tree = loose.write_tree(&Tree::new(ObjectFormat::Sha1, vec![entry])?)?;
//!
//! // `None` baseline: the current index is empty or absent.
//! repository.checkout_tree(None, Some(tree))?;
//!
//! let file = repository
//! .worktree()
//! .expect("ordinary repository")
//! .join("hello.sh");
//! assert_eq!(std::fs::read(&file)?, b"#!/bin/sh\necho hello\n");
//! #[cfg(unix)]
//! {
//! use std::os::unix::fs::PermissionsExt;
//! assert_ne!(std::fs::metadata(&file)?.permissions().mode() & 0o111, 0);
//! }
//! let index = repository
//! .read_index(Default::default())?
//! .expect("checkout published an index");
//! assert_eq!(index.entries()[0].path, b"hello.sh");
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
pub use *;