Skip to main content

projectatlas_fs/
worktree.rs

1//! Read-only structural discovery for Git worktrees and common control directories.
2
3use super::{
4    DEFAULT_SCAN_TIMEOUT, FsError, FsResult, GIT_DIRECTORY_POINTER_MAX_BYTES,
5    check_registered_worktree,
6};
7use projectatlas_core::{IndexCancellation, IndexWorkControl, IndexWorkStage};
8use std::collections::BTreeMap;
9use std::fs;
10use std::io::{self, Read};
11use std::path::{Path, PathBuf};
12
13/// Read-only structural classification for one selected path.
14#[derive(Clone, Debug, Eq, PartialEq)]
15pub enum RepositoryStructure {
16    /// No containing structural Git checkout or common directory was found.
17    NonGit {
18        /// Canonical exact non-Git root supplied to discovery.
19        selected_root: PathBuf,
20    },
21    /// A structurally validated Git repository and its worktree inventory.
22    Git(GitRepositoryStructure),
23    /// A Git marker was present, but its structural evidence was unsafe or inconsistent.
24    InvalidGit {
25        /// Canonical containing path whose Git marker was inspected.
26        selected_root: PathBuf,
27        /// Exact typed structural problem.
28        issue: GitStructureIssue,
29    },
30}
31
32/// Structurally validated Git repository evidence.
33#[derive(Clone, Debug, Eq, PartialEq)]
34pub struct GitRepositoryStructure {
35    /// Canonical Git common directory shared by every discovered worktree.
36    pub common_directory: PathBuf,
37    /// How the supplied path selected this repository.
38    pub selection: GitRepositorySelection,
39    /// Deterministic primary-then-registration inventory.
40    pub worktrees: Vec<GitWorktreeEntry>,
41}
42
43/// Selection made from the supplied path.
44#[derive(Clone, Debug, Eq, PartialEq)]
45pub enum GitRepositorySelection {
46    /// The supplied path was inside one exact checked-out worktree.
47    Worktree {
48        /// Canonical selected worktree root.
49        root: PathBuf,
50        /// Structural role of the selected worktree.
51        role: GitWorktreeRole,
52        /// Canonical Git administrative directory for this worktree.
53        administrative_directory: PathBuf,
54    },
55    /// The supplied path addressed the common control directory instead of source.
56    CommonManager {
57        /// Whether the structurally active inventory permits automatic source selection.
58        source_selection: GitManagerSourceSelection,
59    },
60}
61
62/// Bounded manager-side source selection derived only from active structural entries.
63#[derive(Clone, Debug, Eq, PartialEq)]
64pub enum GitManagerSourceSelection {
65    /// No active checked-out source root was structurally discoverable.
66    None,
67    /// Exactly one active source root can be selected without guessing.
68    Unambiguous {
69        /// Canonical active worktree root.
70        root: PathBuf,
71    },
72    /// Multiple active roots exist, so an adapter must require deliberate selection.
73    Ambiguous {
74        /// Number of structurally active roots in the bounded inventory.
75        worktree_count: usize,
76    },
77}
78
79/// Structural role of one checked-out worktree.
80#[derive(Clone, Copy, Debug, Eq, PartialEq)]
81pub enum GitWorktreeRole {
82    /// Primary checkout whose `.git` directory is the common directory.
83    Primary,
84    /// Linked checkout with reciprocal common-directory registration evidence.
85    Linked,
86}
87
88/// One Git worktree administrative entry and its current structural state.
89#[derive(Clone, Debug, Eq, PartialEq)]
90pub struct GitWorktreeEntry {
91    /// Structural role represented by this entry.
92    pub role: GitWorktreeRole,
93    /// Canonical Git administrative directory, stable across a Git-managed move.
94    ///
95    /// This locator is structural evidence, not a durable identity by itself: a
96    /// later recreation may reuse the same administrative path. Persistence stores
97    /// the opaque filesystem identity that distinguishes that lifecycle boundary.
98    pub administrative_directory: PathBuf,
99    /// Current read-only registration state.
100    pub state: GitWorktreeState,
101}
102
103/// Current structural state of one worktree registration.
104#[derive(Clone, Debug, Eq, PartialEq)]
105pub enum GitWorktreeState {
106    /// Reciprocal control paths identify one existing checked-out root.
107    Active {
108        /// Canonical checked-out source root.
109        root: PathBuf,
110        /// Canonical `.git` directory or control file owned by that root.
111        git_control_path: PathBuf,
112    },
113    /// The registration remains, but its recorded checkout control path is absent.
114    Missing {
115        /// Recorded `.git` control path, resolved relative to the administrative entry.
116        git_control_path: PathBuf,
117    },
118    /// The registration exists but cannot be admitted as reciprocal identity evidence.
119    Invalid {
120        /// Exact typed structural problem.
121        issue: GitStructureIssue,
122    },
123}
124
125/// Exact structural problem found while inspecting Git control metadata.
126#[derive(Clone, Debug, Eq, PartialEq)]
127pub struct GitStructureIssue {
128    /// Control path at which the problem was observed.
129    pub path: PathBuf,
130    /// Closed problem classification and any comparison evidence.
131    pub kind: GitStructureIssueKind,
132}
133
134/// Closed classifications for malformed or conflicting Git structural evidence.
135#[derive(Clone, Debug, Eq, PartialEq)]
136pub enum GitStructureIssueKind {
137    /// A control path is a symbolic link or junction and is not followed as identity evidence.
138    SymbolicLink,
139    /// A control path has the wrong filesystem type.
140    UnsupportedPathType,
141    /// Control metadata could not be read for an operating-system reason.
142    FilesystemUnavailable {
143        /// Stable standard-library IO classification.
144        error_kind: io::ErrorKind,
145    },
146    /// A bounded control file exceeded its declared byte limit.
147    PointerTooLarge {
148        /// Maximum admitted bytes.
149        limit_bytes: u64,
150        /// Observed file bytes.
151        observed_bytes: u64,
152    },
153    /// A control file was not valid UTF-8.
154    PointerNotUtf8,
155    /// A control file did not contain exactly one non-empty path record in its expected form.
156    MalformedPointer,
157    /// A selected control pointer addressed a path that does not exist.
158    MissingPointerTarget,
159    /// A candidate common directory did not have the required Git control structure.
160    InvalidCommonDirectory,
161    /// Local Git configuration can relocate or suppress the addressed source root.
162    UnsupportedSourceConfiguration,
163    /// A linked administrative directory was not an immediate registered child of the common directory.
164    RegistrationOutsideCommonDirectory,
165    /// A registration is missing its required `gitdir` pointer.
166    MissingRegistrationPointer,
167    /// Reciprocal worktree control evidence pointed at a different path.
168    ReciprocalControlMismatch {
169        /// Path required by the registration being validated.
170        expected: PathBuf,
171        /// Different path observed in the reciprocal pointer.
172        observed: PathBuf,
173    },
174    /// A worktree resolved to a different common directory.
175    CommonDirectoryMismatch {
176        /// Common directory required by the selected repository.
177        expected: PathBuf,
178        /// Different common directory observed from the worktree.
179        observed: PathBuf,
180    },
181}
182
183impl GitStructureIssue {
184    /// Preserve the operating-system error class when adapting structural discovery.
185    pub(super) fn into_io_error(self) -> (PathBuf, io::Error) {
186        let error_kind = match &self.kind {
187            GitStructureIssueKind::FilesystemUnavailable { error_kind } => *error_kind,
188            _ => io::ErrorKind::InvalidData,
189        };
190        let message = format!("{:?}", self.kind);
191        (self.path, io::Error::new(error_kind, message))
192    }
193}
194
195/// Discover one repository structure without starting Git or mutating the filesystem.
196///
197/// The nearest containing worktree wins for nested paths. If the supplied path is
198/// inside a common control directory, discovery returns manager selection instead
199/// of treating Git metadata as source. A true non-Git path remains that exact path.
200///
201/// # Errors
202///
203/// Returns an error when the selected directory cannot be canonicalized, filesystem
204/// metadata cannot be read, or the bounded registration inventory is exceeded.
205pub fn discover_repository_structure(path: &Path) -> FsResult<RepositoryStructure> {
206    let control = IndexWorkControl::new(IndexCancellation::new(), Some(DEFAULT_SCAN_TIMEOUT));
207    discover_repository_structure_controlled(path, &control)
208}
209
210/// Discover one repository structure under caller-owned cancellation and deadline control.
211///
212/// # Errors
213///
214/// Returns an error when cooperative work stops, filesystem evidence cannot be read,
215/// or the bounded registration inventory is exceeded. Malformed Git evidence is
216/// returned as typed discovery state rather than silently downgraded to non-Git.
217pub fn discover_repository_structure_controlled(
218    path: &Path,
219    control: &IndexWorkControl,
220) -> FsResult<RepositoryStructure> {
221    control.check(IndexWorkStage::RepositoryTraversal)?;
222    if !path.is_dir() {
223        return Err(FsError::RootNotDirectory(path.to_path_buf()));
224    }
225    let selected_root = canonicalize(path, path)?;
226
227    for ancestor in selected_root.ancestors() {
228        control.check(IndexWorkStage::RepositoryTraversal)?;
229        let git_control_path = ancestor.join(".git");
230        match fs::symlink_metadata(&git_control_path) {
231            Ok(_) => {
232                return match inspect_worktree(ancestor) {
233                    Ok(selected) => {
234                        // A source-owned `.git` file already selects its reciprocal checkout exactly.
235                        let selection_kind = if selected.role == GitWorktreeRole::Primary
236                            && paths_equal(&selected.git_control_path, &selected.common_directory)
237                            && (selected.common_directory_bare_setting == Some(true)
238                                || !selected.source_root_selected_exactly)
239                        {
240                            GitRepositorySelectionKind::Manager
241                        } else {
242                            GitRepositorySelectionKind::Worktree
243                        };
244                        build_git_structure(selected, selection_kind, control)
245                            .map(RepositoryStructure::Git)
246                    }
247                    Err(issue) => Ok(RepositoryStructure::InvalidGit {
248                        selected_root: ancestor.to_path_buf(),
249                        issue,
250                    }),
251                };
252            }
253            Err(source) if source.kind() == io::ErrorKind::NotFound => {}
254            Err(source) => {
255                return Err(FsError::RepositoryBoundary {
256                    path: git_control_path,
257                    source,
258                });
259            }
260        }
261
262        if has_git_control_markers(ancestor)? {
263            return match inspect_common_directory(ancestor) {
264                Ok(common_directory) => build_git_structure(
265                    SelectedWorktree {
266                        root: ancestor.to_path_buf(),
267                        git_control_path: common_directory.path.clone(),
268                        administrative_directory: common_directory.path.clone(),
269                        common_directory: common_directory.path,
270                        common_directory_bare_setting: common_directory.bare_setting,
271                        common_directory_source_root_inference_safe: common_directory
272                            .source_root_inference_safe,
273                        source_root_selected_exactly: false,
274                        role: GitWorktreeRole::Primary,
275                    },
276                    GitRepositorySelectionKind::Manager,
277                    control,
278                )
279                .map(RepositoryStructure::Git),
280                Err(issue) => Ok(RepositoryStructure::InvalidGit {
281                    selected_root: ancestor.to_path_buf(),
282                    issue,
283                }),
284            };
285        }
286    }
287
288    Ok(RepositoryStructure::NonGit { selected_root })
289}
290
291/// Internal validated selected-worktree evidence.
292#[derive(Clone, Debug)]
293struct SelectedWorktree {
294    /// Canonical checked-out root, or addressed common directory for manager selection.
295    root: PathBuf,
296    /// Canonical source-owned `.git` path.
297    git_control_path: PathBuf,
298    /// Canonical worktree administrative directory.
299    administrative_directory: PathBuf,
300    /// Canonical repository common directory.
301    common_directory: PathBuf,
302    /// Explicit local `core.bare` setting, when present.
303    common_directory_bare_setting: Option<bool>,
304    /// Whether local config permits inferring source beside the common directory.
305    common_directory_source_root_inference_safe: bool,
306    /// Whether the selected root agrees with the effective local source configuration.
307    source_root_selected_exactly: bool,
308    /// Structural worktree role.
309    role: GitWorktreeRole,
310}
311
312/// Validated common-directory identity and local bare-worktree policy.
313#[derive(Clone, Debug)]
314struct InspectedCommonDirectory {
315    /// Canonical common control directory.
316    path: PathBuf,
317    /// Explicit local `core.bare` setting, when present.
318    bare_setting: Option<bool>,
319    /// Whether local config permits inferring source beside the common directory.
320    source_root_inference_safe: bool,
321}
322
323/// Bounded local Git config facts needed for read-only source selection.
324#[derive(Clone, Debug, Eq, PartialEq)]
325struct GitLocalConfigPolicy {
326    /// Explicit local `core.bare` setting, when present and include-free.
327    bare_setting: Option<bool>,
328    /// False when local worktree settings or unresolved includes may relocate source.
329    source_root_inference_safe: bool,
330    /// Whether Git reads per-worktree values from `config.worktree`.
331    worktree_config_enabled: bool,
332    /// Configured `core.worktree` value, resolved only by an exact pointer owner.
333    worktree_setting: Option<PathBuf>,
334    /// Whether the bounded source-selection subset was parsed without uncertainty.
335    source_selection_policy_complete: bool,
336}
337
338/// Internal selection branch used while constructing the common inventory.
339#[derive(Clone, Copy, Debug, Eq, PartialEq)]
340enum GitRepositorySelectionKind {
341    /// A checked-out source root was selected.
342    Worktree,
343    /// The common manager itself was selected.
344    Manager,
345}
346
347/// Build deterministic repository inventory from one validated selected structure.
348fn build_git_structure(
349    selected: SelectedWorktree,
350    selection_kind: GitRepositorySelectionKind,
351    control: &IndexWorkControl,
352) -> FsResult<GitRepositoryStructure> {
353    let common_directory = canonicalize(&selected.common_directory, &selected.common_directory)?;
354    let primary_root = primary_worktree_root(
355        &common_directory,
356        selected.common_directory_bare_setting,
357        selected.common_directory_source_root_inference_safe,
358    )?;
359    let primary_may_be_unlisted = primary_root.is_none()
360        && (selected.common_directory_bare_setting.is_none()
361            || !selected.common_directory_source_root_inference_safe)
362        && common_directory.file_name().and_then(|name| name.to_str()) == Some(".git");
363    let selected_primary = (selection_kind == GitRepositorySelectionKind::Worktree
364        && selected.role == GitWorktreeRole::Primary)
365        .then_some(selected.clone());
366    let worktrees = worktree_inventory(
367        &common_directory,
368        primary_root.as_deref(),
369        selected_primary.as_ref(),
370        control,
371    )?;
372
373    let selection = match selection_kind {
374        GitRepositorySelectionKind::Worktree => GitRepositorySelection::Worktree {
375            root: selected.root,
376            role: selected.role,
377            administrative_directory: selected.administrative_directory,
378        },
379        GitRepositorySelectionKind::Manager => GitRepositorySelection::CommonManager {
380            source_selection: manager_source_selection(&worktrees, primary_may_be_unlisted),
381        },
382    };
383
384    Ok(GitRepositoryStructure {
385        common_directory,
386        selection,
387        worktrees,
388    })
389}
390
391/// Inspect one exact worktree root from its `.git` control path.
392fn inspect_worktree(root: &Path) -> Result<SelectedWorktree, GitStructureIssue> {
393    let root = canonicalize_issue(root, root)?;
394    let git_control_path = root.join(".git");
395    let metadata = structural_metadata(&git_control_path)?;
396    if metadata.is_dir() {
397        let common_directory = inspect_common_directory(&git_control_path)?;
398        let source_root_selected_exactly = common_directory.bare_setting != Some(true)
399            && (common_directory.source_root_inference_safe
400                || validate_pointer_source_configuration(
401                    &root,
402                    &common_directory.path,
403                    &common_directory.path,
404                    false,
405                )
406                .is_ok());
407        return Ok(SelectedWorktree {
408            root,
409            git_control_path: common_directory.path.clone(),
410            administrative_directory: common_directory.path.clone(),
411            common_directory: common_directory.path,
412            common_directory_bare_setting: common_directory.bare_setting,
413            common_directory_source_root_inference_safe: common_directory
414                .source_root_inference_safe,
415            source_root_selected_exactly,
416            role: GitWorktreeRole::Primary,
417        });
418    }
419    if !metadata.is_file() {
420        return Err(issue(
421            git_control_path,
422            GitStructureIssueKind::UnsupportedPathType,
423        ));
424    }
425
426    let administrative_pointer = read_prefixed_pointer(&git_control_path, "gitdir:")?;
427    let administrative_directory =
428        resolve_existing_directory(&git_control_path, &root, &administrative_pointer)?;
429    let common_pointer_path = administrative_directory.join("commondir");
430    match fs::symlink_metadata(&common_pointer_path) {
431        Ok(_) => {
432            let common_pointer = read_plain_pointer(&common_pointer_path)?;
433            let common_directory_path = resolve_existing_directory(
434                &common_pointer_path,
435                &administrative_directory,
436                &common_pointer,
437            )?;
438            let common_directory = inspect_common_directory(&common_directory_path)?;
439            validate_linked_administrative_directory(
440                &administrative_directory,
441                &common_directory.path,
442            )?;
443            validate_reciprocal_control(
444                &administrative_directory,
445                &git_control_path,
446                &common_directory.path,
447            )?;
448            validate_pointer_source_configuration(
449                &root,
450                &common_directory.path,
451                &administrative_directory,
452                true,
453            )?;
454            Ok(SelectedWorktree {
455                root,
456                git_control_path: canonicalize_issue(&git_control_path, &git_control_path)?,
457                administrative_directory,
458                common_directory: common_directory.path,
459                common_directory_bare_setting: common_directory.bare_setting,
460                common_directory_source_root_inference_safe: common_directory
461                    .source_root_inference_safe,
462                source_root_selected_exactly: true,
463                role: GitWorktreeRole::Linked,
464            })
465        }
466        Err(source) if source.kind() == io::ErrorKind::NotFound => {
467            let common_directory = inspect_common_directory(&administrative_directory)?;
468            validate_pointer_source_configuration(
469                &root,
470                &common_directory.path,
471                &administrative_directory,
472                false,
473            )?;
474            Ok(SelectedWorktree {
475                root,
476                git_control_path: canonicalize_issue(&git_control_path, &git_control_path)?,
477                administrative_directory,
478                common_directory: common_directory.path,
479                common_directory_bare_setting: common_directory.bare_setting,
480                common_directory_source_root_inference_safe: common_directory
481                    .source_root_inference_safe,
482                source_root_selected_exactly: true,
483                role: GitWorktreeRole::Primary,
484            })
485        }
486        Err(_) => Err(issue(
487            common_pointer_path,
488            GitStructureIssueKind::UnsupportedPathType,
489        )),
490    }
491}
492
493/// Validate and canonicalize one Git common control directory.
494fn inspect_common_directory(path: &Path) -> Result<InspectedCommonDirectory, GitStructureIssue> {
495    let common_directory = canonicalize_issue(path, path)?;
496    for (name, directory) in [("HEAD", false), ("objects", true), ("refs", true)] {
497        let marker = common_directory.join(name);
498        let metadata = structural_metadata(&marker)?;
499        if metadata.is_dir() != directory || metadata.is_file() == directory {
500            return Err(issue(
501                common_directory,
502                GitStructureIssueKind::InvalidCommonDirectory,
503            ));
504        }
505    }
506    let config = common_directory.join("config");
507    let mut config_policy = match fs::symlink_metadata(&config) {
508        Ok(_) => local_config_policy(&config)?,
509        Err(source) if source.kind() == io::ErrorKind::NotFound => GitLocalConfigPolicy {
510            bare_setting: None,
511            source_root_inference_safe: true,
512            worktree_config_enabled: false,
513            worktree_setting: None,
514            source_selection_policy_complete: true,
515        },
516        Err(source) => {
517            return Err(issue(
518                config,
519                GitStructureIssueKind::FilesystemUnavailable {
520                    error_kind: source.kind(),
521                },
522            ));
523        }
524    };
525    if config_policy.worktree_config_enabled {
526        let worktree_config = common_directory.join("config.worktree");
527        match fs::symlink_metadata(&worktree_config) {
528            Ok(_) => {
529                let worktree_policy = local_config_policy(&worktree_config)?;
530                config_policy.bare_setting = if worktree_policy.source_root_inference_safe {
531                    worktree_policy.bare_setting.or(config_policy.bare_setting)
532                } else {
533                    None
534                };
535                config_policy.source_root_inference_safe &=
536                    worktree_policy.source_root_inference_safe;
537            }
538            Err(source) if source.kind() == io::ErrorKind::NotFound => {}
539            Err(source) => {
540                return Err(issue(
541                    worktree_config,
542                    GitStructureIssueKind::FilesystemUnavailable {
543                        error_kind: source.kind(),
544                    },
545                ));
546            }
547        }
548    }
549    let registrations = common_directory.join("worktrees");
550    match fs::symlink_metadata(&registrations) {
551        Ok(_) => {
552            let metadata = structural_metadata(&registrations)?;
553            if !metadata.is_dir() {
554                return Err(issue(
555                    registrations,
556                    GitStructureIssueKind::UnsupportedPathType,
557                ));
558            }
559        }
560        Err(source) if source.kind() == io::ErrorKind::NotFound => {}
561        Err(source) => {
562            return Err(issue(
563                registrations,
564                GitStructureIssueKind::FilesystemUnavailable {
565                    error_kind: source.kind(),
566                },
567            ));
568        }
569    }
570    Ok(InspectedCommonDirectory {
571        path: common_directory,
572        bare_setting: config_policy.bare_setting,
573        source_root_inference_safe: config_policy.source_root_inference_safe,
574    })
575}
576
577/// Return whether a path has enough structural markers to be treated as a Git manager candidate.
578fn has_git_control_markers(path: &Path) -> FsResult<bool> {
579    let head = path.join("HEAD");
580    let objects = path.join("objects");
581    let refs = path.join("refs");
582    Ok(path_is_present(&head)? && path_is_present(&objects)? && path_is_present(&refs)?)
583}
584
585/// Read bounded local source-selection policy without following includes or starting Git.
586/// Continued values fail closed because this bounded reader does not interpret full Git syntax.
587fn local_config_policy(path: &Path) -> Result<GitLocalConfigPolicy, GitStructureIssue> {
588    let text = read_bounded_text(path, GIT_DIRECTORY_POINTER_MAX_BYTES)?;
589    let text = text.strip_prefix('\u{feff}').unwrap_or(&text);
590    let mut in_core = false;
591    let mut in_extensions = false;
592    let mut has_include = false;
593    let mut bare_setting = None;
594    let mut source_root_inference_safe = true;
595    let mut worktree_config_enabled = false;
596    let mut worktree_setting = None;
597    let mut source_selection_policy_complete = true;
598    let mut repository_format_version = Some(0_u64);
599    let mut repository_extensions = BTreeMap::new();
600    for raw_line in text.lines() {
601        let mut line = trim_git_config_whitespace(raw_line);
602        if line.is_empty() || line.starts_with('#') || line.starts_with(';') {
603            continue;
604        }
605        if line.starts_with('[') {
606            let Some((section_name, has_subsection, remainder)) = git_config_section(line) else {
607                return Ok(GitLocalConfigPolicy {
608                    bare_setting: None,
609                    source_root_inference_safe: false,
610                    worktree_config_enabled: false,
611                    worktree_setting: None,
612                    source_selection_policy_complete: false,
613                });
614            };
615            has_include |= section_name.eq_ignore_ascii_case("include")
616                || section_name.eq_ignore_ascii_case("includeif");
617            in_core = !has_subsection && section_name.eq_ignore_ascii_case("core");
618            in_extensions = !has_subsection && section_name.eq_ignore_ascii_case("extensions");
619            if remainder.is_empty() || remainder.starts_with(['#', ';']) {
620                continue;
621            }
622            line = remainder;
623        }
624        let (key, raw_value) = line
625            .split_once('=')
626            .map_or((line, "true"), |(key, value)| (key, value));
627        let key = trim_git_config_whitespace(key);
628        let mut key_bytes = key.bytes();
629        if !key_bytes
630            .next()
631            .is_some_and(|byte| byte.is_ascii_alphabetic())
632            || !key_bytes.all(|byte| byte.is_ascii_alphanumeric() || byte == b'-')
633        {
634            return Ok(GitLocalConfigPolicy {
635                bare_setting: None,
636                source_root_inference_safe: false,
637                worktree_config_enabled: false,
638                worktree_setting: None,
639                source_selection_policy_complete: false,
640            });
641        }
642        let Some(value) = git_config_value(raw_value) else {
643            return Ok(GitLocalConfigPolicy {
644                bare_setting: None,
645                source_root_inference_safe: false,
646                worktree_config_enabled: false,
647                worktree_setting: None,
648                source_selection_policy_complete: false,
649            });
650        };
651        if !in_core && !in_extensions {
652            continue;
653        }
654        if in_extensions {
655            repository_extensions.insert(key.to_ascii_lowercase(), value);
656            continue;
657        }
658        if !in_core {
659            continue;
660        }
661        if key.eq_ignore_ascii_case("repositoryformatversion") {
662            repository_format_version = value.parse::<u64>().ok().filter(|version| *version <= 1);
663            continue;
664        }
665        if key.eq_ignore_ascii_case("worktree") {
666            if value.contains('\\') {
667                source_root_inference_safe = false;
668                source_selection_policy_complete = false;
669                continue;
670            }
671            worktree_setting = (!value.is_empty()).then(|| PathBuf::from(value));
672            source_root_inference_safe = false;
673            source_selection_policy_complete &= worktree_setting.is_some();
674            continue;
675        }
676        if !key.eq_ignore_ascii_case("bare") {
677            continue;
678        }
679        if let Some(bare) = parse_git_boolean(&value) {
680            bare_setting = Some(bare);
681        } else {
682            bare_setting = None;
683            source_root_inference_safe = false;
684            source_selection_policy_complete = false;
685        }
686    }
687    let repository_format_version = if let Some(version) = repository_format_version {
688        version
689    } else {
690        source_root_inference_safe = false;
691        source_selection_policy_complete = false;
692        0
693    };
694    let mut has_format_one_extension = false;
695    let mut has_unsupported_repository_extension = false;
696    let mut object_format = None;
697    let mut compatibility_object_format = None;
698    for (key, value) in repository_extensions {
699        match key.as_str() {
700            "worktreeconfig" => {
701                if let Some(enabled) = parse_git_boolean(&value) {
702                    worktree_config_enabled = enabled;
703                } else {
704                    source_root_inference_safe = false;
705                    worktree_config_enabled = false;
706                    source_selection_policy_complete = false;
707                }
708            }
709            "noop" | "partialclone" => {}
710            "preciousobjects" => {
711                if parse_git_boolean(&value).is_none() {
712                    source_root_inference_safe = false;
713                    source_selection_policy_complete = false;
714                }
715            }
716            "compatobjectformat" => {
717                has_format_one_extension = true;
718                has_unsupported_repository_extension |=
719                    !matches!(value.as_str(), "sha1" | "sha256");
720                compatibility_object_format = Some(value);
721            }
722            "noop-v1" => has_format_one_extension = true,
723            "objectformat" => {
724                has_format_one_extension = true;
725                has_unsupported_repository_extension |=
726                    !matches!(value.as_str(), "sha1" | "sha256");
727                object_format = Some(value);
728            }
729            "refstorage" => {
730                has_format_one_extension = true;
731                let format = value
732                    .split_once("://")
733                    .map_or(value.as_str(), |(format, _)| format);
734                has_unsupported_repository_extension |= !matches!(format, "files" | "reftable");
735            }
736            "relativeworktrees" | "submodulepathconfig" => {
737                has_format_one_extension = true;
738                has_unsupported_repository_extension |= parse_git_boolean(&value).is_none();
739            }
740            _ => has_unsupported_repository_extension = true,
741        }
742    }
743    if compatibility_object_format
744        .as_deref()
745        .is_some_and(|compatibility| compatibility == object_format.as_deref().unwrap_or("sha1"))
746    {
747        has_unsupported_repository_extension = true;
748    }
749    if (repository_format_version == 1 && has_unsupported_repository_extension)
750        || (repository_format_version != 1 && has_format_one_extension)
751    {
752        source_root_inference_safe = false;
753        source_selection_policy_complete = false;
754    }
755    source_selection_policy_complete &= !has_include;
756    Ok(GitLocalConfigPolicy {
757        bare_setting: (!has_include).then_some(bare_setting).flatten(),
758        source_root_inference_safe: source_root_inference_safe && !has_include,
759        worktree_config_enabled,
760        worktree_setting,
761        source_selection_policy_complete,
762    })
763}
764
765/// Trim only the space and horizontal tab bytes admitted by Git config syntax.
766fn trim_git_config_whitespace(value: &str) -> &str {
767    value.trim_matches([' ', '\t'])
768}
769
770/// Parse one Git section header and return any assignment that follows it.
771fn git_config_section(line: &str) -> Option<(&str, bool, &str)> {
772    let mut quoted = false;
773    let mut escaped = false;
774    let mut header_end = None;
775    for (index, character) in line.char_indices() {
776        if escaped {
777            escaped = false;
778            continue;
779        }
780        match character {
781            '\\' if quoted => escaped = true,
782            '"' => quoted = !quoted,
783            ']' if !quoted => {
784                header_end = Some(index);
785                break;
786            }
787            _ => {}
788        }
789    }
790    if quoted || escaped {
791        return None;
792    }
793    let header_end = header_end?;
794    let section = trim_git_config_whitespace(&line[1..header_end]);
795    if section.is_empty() || section.contains(['[', ']']) {
796        return None;
797    }
798    let name_end = section
799        .find(|character: char| character.is_ascii_whitespace())
800        .unwrap_or(section.len());
801    let name = &section[..name_end];
802    if !name
803        .bytes()
804        .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'.'))
805    {
806        return None;
807    }
808    let subsection = trim_git_config_whitespace(&section[name_end..]);
809    let remainder = trim_git_config_whitespace(&line[header_end + 1..]);
810    if subsection.is_empty() {
811        return Some((name, false, remainder));
812    }
813    let subsection = subsection.strip_prefix('"')?.strip_suffix('"')?;
814    let mut escaped = false;
815    for character in subsection.chars() {
816        if escaped {
817            escaped = false;
818        } else if character == '\\' {
819            escaped = true;
820        } else if character == '"' {
821            return None;
822        }
823    }
824    (!escaped).then_some((name, true, remainder))
825}
826
827/// Remove unquoted Git comments and concatenate balanced quoted value segments.
828///
829/// Escape sequences remain encoded so pointer-owned source settings can reject
830/// them conservatively instead of resolving a path under different semantics.
831fn git_config_value(raw_value: &str) -> Option<String> {
832    let mut quoted = false;
833    let mut escaped = false;
834    let mut value_end = raw_value.len();
835    for (index, character) in raw_value.char_indices() {
836        if escaped {
837            if !matches!(character, '\\' | '"' | 'n' | 't' | 'b') {
838                return None;
839            }
840            escaped = false;
841            continue;
842        }
843        match character {
844            '\\' => escaped = true,
845            '"' => quoted = !quoted,
846            '#' | ';' if !quoted => {
847                value_end = index;
848                break;
849            }
850            _ => {}
851        }
852    }
853    if quoted || escaped {
854        return None;
855    }
856    let value = trim_git_config_whitespace(&raw_value[..value_end]);
857    let mut normalized = String::with_capacity(value.len());
858    let mut escaped = false;
859    for character in value.chars() {
860        if escaped {
861            normalized.push(character);
862            escaped = false;
863        } else if character == '\\' {
864            normalized.push(character);
865            escaped = true;
866        } else if character != '"' {
867            normalized.push(character);
868        }
869    }
870    Some(normalized)
871}
872
873/// Parse the bounded Git boolean forms that can affect source selection.
874fn parse_git_boolean(value: &str) -> Option<bool> {
875    match value.to_ascii_lowercase().as_str() {
876        "" | "false" | "no" | "off" | "0" => Some(false),
877        "true" | "yes" | "on" | "1" => Some(true),
878        _ => value.parse::<i64>().ok().map(|value| value != 0),
879    }
880}
881
882/// Reject pointer-owned configuration that selects source outside its owner.
883fn validate_pointer_source_configuration(
884    root: &Path,
885    common_directory: &Path,
886    administrative_directory: &Path,
887    common_manager_may_be_bare: bool,
888) -> Result<(), GitStructureIssue> {
889    let common_config = common_directory.join("config");
890    let common_policy = match fs::symlink_metadata(&common_config) {
891        Ok(_) => local_config_policy(&common_config)?,
892        Err(source) if source.kind() == io::ErrorKind::NotFound => return Ok(()),
893        Err(source) => {
894            return Err(issue(
895                common_config,
896                GitStructureIssueKind::FilesystemUnavailable {
897                    error_kind: source.kind(),
898                },
899            ));
900        }
901    };
902    validate_pointer_config_policy(
903        &common_config,
904        administrative_directory,
905        root,
906        &common_policy,
907        common_manager_may_be_bare,
908    )?;
909    if !common_policy.worktree_config_enabled {
910        return Ok(());
911    }
912
913    let worktree_config = administrative_directory.join("config.worktree");
914    let worktree_policy = match fs::symlink_metadata(&worktree_config) {
915        Ok(_) => local_config_policy(&worktree_config)?,
916        Err(source) if source.kind() == io::ErrorKind::NotFound => return Ok(()),
917        Err(source) => {
918            return Err(issue(
919                worktree_config,
920                GitStructureIssueKind::FilesystemUnavailable {
921                    error_kind: source.kind(),
922                },
923            ));
924        }
925    };
926    validate_pointer_config_policy(
927        &worktree_config,
928        administrative_directory,
929        root,
930        &worktree_policy,
931        false,
932    )
933}
934
935/// Require one bounded config policy to preserve an exact pointer owner.
936fn validate_pointer_config_policy(
937    config_path: &Path,
938    administrative_directory: &Path,
939    root: &Path,
940    policy: &GitLocalConfigPolicy,
941    bare_allowed: bool,
942) -> Result<(), GitStructureIssue> {
943    if !policy.source_selection_policy_complete
944        || !bare_allowed && policy.bare_setting == Some(true)
945    {
946        return Err(issue(
947            config_path.to_path_buf(),
948            GitStructureIssueKind::UnsupportedSourceConfiguration,
949        ));
950    }
951    let Some(setting) = &policy.worktree_setting else {
952        return Ok(());
953    };
954    let configured_root =
955        resolve_existing_directory(config_path, administrative_directory, setting)?;
956    if paths_equal(&configured_root, root) {
957        Ok(())
958    } else {
959        Err(issue(
960            config_path.to_path_buf(),
961            GitStructureIssueKind::UnsupportedSourceConfiguration,
962        ))
963    }
964}
965
966/// Build the primary plus registered linked-worktree inventory.
967fn worktree_inventory(
968    common_directory: &Path,
969    primary_root: Option<&Path>,
970    selected_primary: Option<&SelectedWorktree>,
971    control: &IndexWorkControl,
972) -> FsResult<Vec<GitWorktreeEntry>> {
973    let mut worktrees = Vec::new();
974    if let Some(root) = primary_root {
975        worktrees.push(primary_entry(root, common_directory)?);
976    } else if let Some(selected) = selected_primary {
977        worktrees.push(active_entry(selected));
978    }
979
980    let registrations = common_directory.join("worktrees");
981    let entries = match fs::read_dir(&registrations) {
982        Ok(entries) => entries,
983        Err(source) if source.kind() == io::ErrorKind::NotFound => return Ok(worktrees),
984        Err(source) => {
985            return Err(FsError::RepositoryBoundary {
986                path: registrations,
987                source,
988            });
989        }
990    };
991    let mut registration_paths = Vec::new();
992    for (index, entry) in entries.enumerate() {
993        check_registered_worktree(control, index)?;
994        let entry = entry.map_err(|source| FsError::RepositoryBoundary {
995            path: registrations.clone(),
996            source,
997        })?;
998        registration_paths.push(entry.path());
999    }
1000    registration_paths.sort();
1001    for registration in registration_paths {
1002        control.check(IndexWorkStage::RepositoryTraversal)?;
1003        worktrees.push(inspect_registration(&registration, common_directory)?);
1004    }
1005    Ok(worktrees)
1006}
1007
1008/// Build the ordinary primary-checkout entry.
1009fn primary_entry(root: &Path, common_directory: &Path) -> FsResult<GitWorktreeEntry> {
1010    let root = canonicalize(root, root)?;
1011    let git_control_path = canonicalize(&root.join(".git"), &root.join(".git"))?;
1012    Ok(GitWorktreeEntry {
1013        role: GitWorktreeRole::Primary,
1014        administrative_directory: common_directory.to_path_buf(),
1015        state: GitWorktreeState::Active {
1016            root,
1017            git_control_path,
1018        },
1019    })
1020}
1021
1022/// Convert validated selected evidence into one active inventory entry.
1023fn active_entry(selected: &SelectedWorktree) -> GitWorktreeEntry {
1024    GitWorktreeEntry {
1025        role: selected.role,
1026        administrative_directory: selected.administrative_directory.clone(),
1027        state: GitWorktreeState::Active {
1028            root: selected.root.clone(),
1029            git_control_path: selected.git_control_path.clone(),
1030        },
1031    }
1032}
1033
1034/// Inspect one common-directory `worktrees/*` administrative entry.
1035fn inspect_registration(
1036    registration: &Path,
1037    common_directory: &Path,
1038) -> FsResult<GitWorktreeEntry> {
1039    let administrative_directory = match structural_metadata(registration) {
1040        Ok(metadata) if metadata.is_dir() => canonicalize(registration, registration)?,
1041        Ok(_) => {
1042            return Ok(invalid_entry(
1043                registration.to_path_buf(),
1044                issue(
1045                    registration.to_path_buf(),
1046                    GitStructureIssueKind::UnsupportedPathType,
1047                ),
1048            ));
1049        }
1050        Err(issue) => return Ok(invalid_entry(registration.to_path_buf(), issue)),
1051    };
1052    let gitdir_path = administrative_directory.join("gitdir");
1053    let pointer = match fs::symlink_metadata(&gitdir_path) {
1054        Ok(_) => match read_plain_pointer(&gitdir_path) {
1055            Ok(pointer) => pointer,
1056            Err(issue) => return Ok(invalid_entry(administrative_directory, issue)),
1057        },
1058        Err(source) if source.kind() == io::ErrorKind::NotFound => {
1059            return Ok(invalid_entry(
1060                administrative_directory,
1061                issue(
1062                    gitdir_path,
1063                    GitStructureIssueKind::MissingRegistrationPointer,
1064                ),
1065            ));
1066        }
1067        Err(source) => {
1068            return Err(FsError::RepositoryBoundary {
1069                path: gitdir_path,
1070                source,
1071            });
1072        }
1073    };
1074    let git_control_path = resolve_pointer(&administrative_directory, &pointer);
1075    match fs::symlink_metadata(&git_control_path) {
1076        Err(source) if source.kind() == io::ErrorKind::NotFound => {
1077            return Ok(GitWorktreeEntry {
1078                role: GitWorktreeRole::Linked,
1079                administrative_directory,
1080                state: GitWorktreeState::Missing { git_control_path },
1081            });
1082        }
1083        Err(source) => {
1084            return Err(FsError::RepositoryBoundary {
1085                path: git_control_path,
1086                source,
1087            });
1088        }
1089        Ok(_) => {}
1090    }
1091    let git_control_path = match canonicalize_issue(&git_control_path, &git_control_path) {
1092        Ok(path) => path,
1093        Err(issue) => return Ok(invalid_entry(administrative_directory, issue)),
1094    };
1095    if git_control_path.file_name().and_then(|name| name.to_str()) != Some(".git") {
1096        return Ok(invalid_entry(
1097            administrative_directory,
1098            issue(git_control_path, GitStructureIssueKind::UnsupportedPathType),
1099        ));
1100    }
1101    let Some(root) = git_control_path.parent() else {
1102        return Ok(invalid_entry(
1103            administrative_directory,
1104            issue(git_control_path, GitStructureIssueKind::UnsupportedPathType),
1105        ));
1106    };
1107    let selected = match inspect_worktree(root) {
1108        Ok(selected) => selected,
1109        Err(issue) => return Ok(invalid_entry(administrative_directory, issue)),
1110    };
1111    if selected.role != GitWorktreeRole::Linked
1112        || !paths_equal(
1113            &selected.administrative_directory,
1114            &administrative_directory,
1115        )
1116    {
1117        return Ok(invalid_entry(
1118            administrative_directory.clone(),
1119            issue(
1120                git_control_path,
1121                GitStructureIssueKind::ReciprocalControlMismatch {
1122                    expected: administrative_directory,
1123                    observed: selected.administrative_directory,
1124                },
1125            ),
1126        ));
1127    }
1128    if !paths_equal(&selected.common_directory, common_directory) {
1129        return Ok(invalid_entry(
1130            administrative_directory,
1131            issue(
1132                git_control_path,
1133                GitStructureIssueKind::CommonDirectoryMismatch {
1134                    expected: common_directory.to_path_buf(),
1135                    observed: selected.common_directory,
1136                },
1137            ),
1138        ));
1139    }
1140    Ok(active_entry(&selected))
1141}
1142
1143/// Return an invalid linked registration entry.
1144fn invalid_entry(administrative_directory: PathBuf, issue: GitStructureIssue) -> GitWorktreeEntry {
1145    GitWorktreeEntry {
1146        role: GitWorktreeRole::Linked,
1147        administrative_directory,
1148        state: GitWorktreeState::Invalid { issue },
1149    }
1150}
1151
1152/// Validate that one linked administrative directory is an immediate common registration.
1153fn validate_linked_administrative_directory(
1154    administrative_directory: &Path,
1155    common_directory: &Path,
1156) -> Result<(), GitStructureIssue> {
1157    let registrations = common_directory.join("worktrees");
1158    let metadata = structural_metadata(&registrations)?;
1159    if !metadata.is_dir() {
1160        return Err(issue(
1161            registrations,
1162            GitStructureIssueKind::UnsupportedPathType,
1163        ));
1164    }
1165    if administrative_directory
1166        .parent()
1167        .is_some_and(|parent| paths_equal(parent, &registrations))
1168    {
1169        Ok(())
1170    } else {
1171        Err(issue(
1172            administrative_directory.to_path_buf(),
1173            GitStructureIssueKind::RegistrationOutsideCommonDirectory,
1174        ))
1175    }
1176}
1177
1178/// Validate both directions of one linked-worktree registration.
1179fn validate_reciprocal_control(
1180    administrative_directory: &Path,
1181    expected_git_control_path: &Path,
1182    expected_common_directory: &Path,
1183) -> Result<(), GitStructureIssue> {
1184    let gitdir_path = administrative_directory.join("gitdir");
1185    let pointer = read_plain_pointer(&gitdir_path)?;
1186    let observed = resolve_existing_file(&gitdir_path, administrative_directory, &pointer)?;
1187    let expected = canonicalize_issue(expected_git_control_path, expected_git_control_path)?;
1188    if !paths_equal(&expected, &observed) {
1189        return Err(issue(
1190            gitdir_path,
1191            GitStructureIssueKind::ReciprocalControlMismatch { expected, observed },
1192        ));
1193    }
1194
1195    let common_pointer_path = administrative_directory.join("commondir");
1196    let common_pointer = read_plain_pointer(&common_pointer_path)?;
1197    let observed_common = resolve_existing_directory(
1198        &common_pointer_path,
1199        administrative_directory,
1200        &common_pointer,
1201    )?;
1202    let expected_common = canonicalize_issue(expected_common_directory, expected_common_directory)?;
1203    if paths_equal(&expected_common, &observed_common) {
1204        Ok(())
1205    } else {
1206        Err(issue(
1207            common_pointer_path,
1208            GitStructureIssueKind::CommonDirectoryMismatch {
1209                expected: expected_common,
1210                observed: observed_common,
1211            },
1212        ))
1213    }
1214}
1215
1216/// Infer an ordinary primary worktree from a common `.git` directory.
1217fn primary_worktree_root(
1218    common_directory: &Path,
1219    common_directory_bare_setting: Option<bool>,
1220    source_root_inference_safe: bool,
1221) -> FsResult<Option<PathBuf>> {
1222    if !source_root_inference_safe
1223        || common_directory_bare_setting != Some(false)
1224        || common_directory.file_name().and_then(|name| name.to_str()) != Some(".git")
1225    {
1226        return Ok(None);
1227    }
1228    let Some(parent) = common_directory.parent() else {
1229        return Ok(None);
1230    };
1231    let marker = parent.join(".git");
1232    let metadata = match fs::symlink_metadata(&marker) {
1233        Ok(metadata) => metadata,
1234        Err(source) if source.kind() == io::ErrorKind::NotFound => return Ok(None),
1235        Err(source) => {
1236            return Err(FsError::RepositoryBoundary {
1237                path: marker,
1238                source,
1239            });
1240        }
1241    };
1242    if metadata.file_type().is_symlink() || !metadata.is_dir() {
1243        return Ok(None);
1244    }
1245    let marker = canonicalize(&marker, &marker)?;
1246    if paths_equal(&marker, common_directory) {
1247        canonicalize(parent, parent).map(Some)
1248    } else {
1249        Ok(None)
1250    }
1251}
1252
1253/// Derive manager auto-selection without branch, path-name, or recency guesses.
1254fn manager_source_selection(
1255    worktrees: &[GitWorktreeEntry],
1256    primary_may_be_unlisted: bool,
1257) -> GitManagerSourceSelection {
1258    let mut count = 0_usize;
1259    let mut only_root = None;
1260    for entry in worktrees {
1261        if let GitWorktreeState::Active { root, .. } = &entry.state {
1262            count = count.saturating_add(1);
1263            if count == 1 {
1264                only_root = Some(root.clone());
1265            }
1266        }
1267    }
1268    if primary_may_be_unlisted && count == 1 {
1269        return GitManagerSourceSelection::Ambiguous { worktree_count: 2 };
1270    }
1271    match (count, only_root) {
1272        (0, _) => GitManagerSourceSelection::None,
1273        (1, Some(root)) => GitManagerSourceSelection::Unambiguous { root },
1274        (worktree_count, _) => GitManagerSourceSelection::Ambiguous { worktree_count },
1275    }
1276}
1277
1278/// Return an opaque identity for one current Git administrative-directory lifecycle.
1279///
1280/// The value remains stable when Git moves the checked-out worktree because the
1281/// administrative directory itself remains in place. Removing and recreating that
1282/// directory produces a different filesystem identity even when Git reuses its path.
1283///
1284/// # Errors
1285///
1286/// Returns an error when metadata cannot be read, the path is indirect or not a
1287/// directory, or the supported platform cannot provide stable lifecycle evidence.
1288pub fn git_administrative_identity(path: &Path) -> FsResult<String> {
1289    let metadata = fs::symlink_metadata(path).map_err(|source| FsError::RepositoryBoundary {
1290        path: path.to_path_buf(),
1291        source,
1292    })?;
1293    if metadata_is_indirect(&metadata) || !metadata.is_dir() {
1294        return Err(FsError::RepositoryBoundary {
1295            path: path.to_path_buf(),
1296            source: io::Error::new(
1297                io::ErrorKind::InvalidData,
1298                "Git administrative identity requires a direct directory",
1299            ),
1300        });
1301    }
1302
1303    let mut identity = blake3::Hasher::new();
1304    identity.update(b"projectatlas-git-administrative-identity-v1\0");
1305    #[cfg(unix)]
1306    {
1307        use std::os::unix::fs::MetadataExt;
1308
1309        identity.update(b"unix\0");
1310        identity.update(&metadata.dev().to_le_bytes());
1311        identity.update(&metadata.ino().to_le_bytes());
1312        identity.update(&required_creation_nanos(path, metadata.created())?.to_le_bytes());
1313    }
1314    #[cfg(windows)]
1315    {
1316        let windows = windows_file_identity::read(path)?;
1317        identity.update(b"windows\0");
1318        identity.update(&windows.creation_time.to_le_bytes());
1319        identity.update(&windows.volume_serial_number.to_le_bytes());
1320        identity.update(&windows.file_id);
1321    }
1322    #[cfg(not(any(unix, windows)))]
1323    {
1324        identity.update(b"portable\0");
1325        identity.update(&required_creation_nanos(path, metadata.created())?.to_le_bytes());
1326    }
1327    Ok(identity.finalize().to_hex().to_string())
1328}
1329
1330/// Check one exact worktree lifecycle without enumerating sibling registrations.
1331///
1332/// # Errors
1333///
1334/// Returns an error when the selected administrative directory cannot provide
1335/// stable filesystem identity evidence.
1336pub fn git_worktree_lifecycle_matches(
1337    root: &Path,
1338    common_directory: &Path,
1339    administrative_directory: &Path,
1340    administrative_identity: &str,
1341) -> FsResult<bool> {
1342    let Ok(common_directory) = canonicalize_issue(common_directory, common_directory) else {
1343        return Ok(false);
1344    };
1345    let Ok(administrative_directory) =
1346        canonicalize_issue(administrative_directory, administrative_directory)
1347    else {
1348        return Ok(false);
1349    };
1350    let Ok(selected) = inspect_worktree(root) else {
1351        return Ok(false);
1352    };
1353    if !selected.source_root_selected_exactly
1354        || !paths_equal(&selected.common_directory, &common_directory)
1355        || !paths_equal(
1356            &selected.administrative_directory,
1357            &administrative_directory,
1358        )
1359    {
1360        return Ok(false);
1361    }
1362    Ok(git_administrative_identity(&selected.administrative_directory)? == administrative_identity)
1363}
1364
1365/// Require a non-reusable filesystem creation timestamp for lifecycle identity.
1366#[cfg(not(windows))]
1367fn required_creation_nanos(
1368    path: &Path,
1369    created: io::Result<std::time::SystemTime>,
1370) -> FsResult<u128> {
1371    let created = created.map_err(|source| FsError::RepositoryBoundary {
1372        path: path.to_path_buf(),
1373        source,
1374    })?;
1375    created
1376        .duration_since(std::time::UNIX_EPOCH)
1377        .map(|duration| duration.as_nanos())
1378        .map_err(|source| FsError::RepositoryBoundary {
1379            path: path.to_path_buf(),
1380            source: io::Error::new(io::ErrorKind::InvalidData, source),
1381        })
1382}
1383
1384/// Windows directory identity from the retained native handle.
1385#[cfg(windows)]
1386#[expect(
1387    unsafe_code,
1388    reason = "the stable standard library does not expose Windows volume and 128-bit file identity; this bounded native query avoids a release dependency"
1389)]
1390mod windows_file_identity {
1391    use super::{FsError, FsResult, metadata_is_indirect};
1392    use std::ffi::c_void;
1393    use std::fs::OpenOptions;
1394    use std::io;
1395    use std::mem::size_of;
1396    use std::os::windows::fs::{MetadataExt, OpenOptionsExt};
1397    use std::os::windows::io::{AsRawHandle, RawHandle};
1398    use std::path::Path;
1399
1400    /// Permit concurrent readers while the directory identity handle is open.
1401    const FILE_SHARE_READ: u32 = 0x0000_0001;
1402    /// Permit ordinary Git writes while the directory identity handle is open.
1403    const FILE_SHARE_WRITE: u32 = 0x0000_0002;
1404    /// Permit ordinary Git deletion while the directory identity handle is open.
1405    const FILE_SHARE_DELETE: u32 = 0x0000_0004;
1406    /// Admit a directory handle through standard Windows file opening.
1407    const FILE_FLAG_BACKUP_SEMANTICS: u32 = 0x0200_0000;
1408    /// Inspect rather than traverse a replacement reparse point.
1409    const FILE_FLAG_OPEN_REPARSE_POINT: u32 = 0x0020_0000;
1410    /// Native `FileIdInfo` query discriminator.
1411    const FILE_ID_INFO_CLASS: i32 = 18;
1412
1413    /// Native fixed-size `FILE_ID_INFO` output layout.
1414    #[repr(C)]
1415    #[derive(Default)]
1416    struct NativeFileIdInfo {
1417        /// Volume identity owning the file.
1418        volume_serial_number: u64,
1419        /// Filesystem-provided 128-bit file identity.
1420        file_id: [u8; 16],
1421    }
1422
1423    #[link(name = "Kernel32")]
1424    unsafe extern "system" {
1425        fn GetFileInformationByHandleEx(
1426            file: RawHandle,
1427            information_class: i32,
1428            information: *mut c_void,
1429            information_bytes: u32,
1430        ) -> i32;
1431    }
1432
1433    /// Stable fields combined with creation time by the lifecycle hash.
1434    pub(super) struct Identity {
1435        /// Windows creation timestamp from the retained handle.
1436        pub(super) creation_time: u64,
1437        /// Volume identity from `FileIdInfo`.
1438        pub(super) volume_serial_number: u64,
1439        /// Filesystem-provided 128-bit file identity.
1440        pub(super) file_id: [u8; 16],
1441    }
1442
1443    /// Open one direct directory and return its retained-handle identity.
1444    pub(super) fn read(path: &Path) -> FsResult<Identity> {
1445        let directory = OpenOptions::new()
1446            .read(true)
1447            .share_mode(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
1448            .custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT)
1449            .open(path)
1450            .map_err(|source| FsError::RepositoryBoundary {
1451                path: path.to_path_buf(),
1452                source,
1453            })?;
1454        let metadata = directory
1455            .metadata()
1456            .map_err(|source| FsError::RepositoryBoundary {
1457                path: path.to_path_buf(),
1458                source,
1459            })?;
1460        if metadata_is_indirect(&metadata) || !metadata.is_dir() {
1461            return Err(FsError::RepositoryBoundary {
1462                path: path.to_path_buf(),
1463                source: io::Error::new(
1464                    io::ErrorKind::InvalidData,
1465                    "Git administrative identity requires a direct directory handle",
1466                ),
1467            });
1468        }
1469
1470        let mut native = NativeFileIdInfo::default();
1471        let information_bytes =
1472            u32::try_from(size_of::<NativeFileIdInfo>()).map_err(|_source| {
1473                FsError::RepositoryBoundary {
1474                    path: path.to_path_buf(),
1475                    source: io::Error::other("Windows file identity structure exceeds DWORD size"),
1476                }
1477            })?;
1478        // SAFETY: `directory` is a live owned handle and `native` is the exact
1479        // FILE_ID_INFO layout for the fixed-size FileIdInfo query.
1480        let succeeded = unsafe {
1481            GetFileInformationByHandleEx(
1482                directory.as_raw_handle(),
1483                FILE_ID_INFO_CLASS,
1484                (&raw mut native).cast(),
1485                information_bytes,
1486            )
1487        };
1488        if succeeded == 0 {
1489            return Err(FsError::RepositoryBoundary {
1490                path: path.to_path_buf(),
1491                source: io::Error::last_os_error(),
1492            });
1493        }
1494        Ok(Identity {
1495            creation_time: metadata.creation_time(),
1496            volume_serial_number: native.volume_serial_number,
1497            file_id: native.file_id,
1498        })
1499    }
1500}
1501
1502/// Read exactly one `prefix path` pointer record.
1503pub(super) fn read_prefixed_pointer(
1504    path: &Path,
1505    prefix: &str,
1506) -> Result<PathBuf, GitStructureIssue> {
1507    let bytes = read_bounded_bytes(path, GIT_DIRECTORY_POINTER_MAX_BYTES)?;
1508    let value = single_pointer_line_bytes(path, &bytes)?;
1509    let Some(value) = value
1510        .strip_prefix(prefix.as_bytes())
1511        .and_then(|value| value.strip_prefix(b" "))
1512    else {
1513        return Err(issue(
1514            path.to_path_buf(),
1515            if std::str::from_utf8(value).is_err() {
1516                GitStructureIssueKind::PointerNotUtf8
1517            } else {
1518                GitStructureIssueKind::MalformedPointer
1519            },
1520        ));
1521    };
1522    path_value_bytes(path, value)
1523}
1524
1525/// Read exactly one plain path pointer record.
1526pub(super) fn read_plain_pointer(path: &Path) -> Result<PathBuf, GitStructureIssue> {
1527    let bytes = read_bounded_bytes(path, GIT_DIRECTORY_POINTER_MAX_BYTES)?;
1528    path_value_bytes(path, single_pointer_line_bytes(path, &bytes)?)
1529}
1530
1531/// Read one bounded UTF-8 control file without following a symbolic-link leaf.
1532fn read_bounded_text(path: &Path, limit: u64) -> Result<String, GitStructureIssue> {
1533    let bytes = read_bounded_bytes(path, limit)?;
1534    String::from_utf8(bytes)
1535        .map_err(|_source| issue(path.to_path_buf(), GitStructureIssueKind::PointerNotUtf8))
1536}
1537
1538/// Read one bounded control file without following a symbolic-link leaf.
1539fn read_bounded_bytes(path: &Path, limit: u64) -> Result<Vec<u8>, GitStructureIssue> {
1540    let metadata = structural_metadata(path)?;
1541    if !metadata.is_file() {
1542        return Err(issue(
1543            path.to_path_buf(),
1544            GitStructureIssueKind::UnsupportedPathType,
1545        ));
1546    }
1547    if metadata.len() > limit {
1548        return Err(issue(
1549            path.to_path_buf(),
1550            GitStructureIssueKind::PointerTooLarge {
1551                limit_bytes: limit,
1552                observed_bytes: metadata.len(),
1553            },
1554        ));
1555    }
1556    let file = fs::File::open(path).map_err(|source| {
1557        issue(
1558            path.to_path_buf(),
1559            GitStructureIssueKind::FilesystemUnavailable {
1560                error_kind: source.kind(),
1561            },
1562        )
1563    })?;
1564    let mut bytes =
1565        Vec::with_capacity(usize::try_from(metadata.len().min(limit)).unwrap_or(usize::MAX));
1566    file.take(limit.saturating_add(1))
1567        .read_to_end(&mut bytes)
1568        .map_err(|source| {
1569            issue(
1570                path.to_path_buf(),
1571                GitStructureIssueKind::FilesystemUnavailable {
1572                    error_kind: source.kind(),
1573                },
1574            )
1575        })?;
1576    if bytes.len() as u64 > limit {
1577        return Err(issue(
1578            path.to_path_buf(),
1579            GitStructureIssueKind::PointerTooLarge {
1580                limit_bytes: limit,
1581                observed_bytes: bytes.len() as u64,
1582            },
1583        ));
1584    }
1585    Ok(bytes)
1586}
1587
1588/// Return one non-empty native path record and reject ambiguous extra content.
1589fn single_pointer_line_bytes<'a>(
1590    path: &Path,
1591    bytes: &'a [u8],
1592) -> Result<&'a [u8], GitStructureIssue> {
1593    let mut value = bytes;
1594    // Git removes terminal CR/LF bytes; spaces and tabs belong to the native path.
1595    while let Some((b'\r' | b'\n', rest)) = value.split_last() {
1596        value = rest;
1597    }
1598    if value.is_empty() || value.contains(&b'\n') || value.contains(&0) {
1599        return Err(issue(
1600            path.to_path_buf(),
1601            GitStructureIssueKind::MalformedPointer,
1602        ));
1603    }
1604    Ok(value)
1605}
1606
1607/// Convert one non-empty native path record into a platform path.
1608fn path_value_bytes(path: &Path, value: &[u8]) -> Result<PathBuf, GitStructureIssue> {
1609    if value.is_empty() {
1610        return Err(issue(
1611            path.to_path_buf(),
1612            GitStructureIssueKind::MalformedPointer,
1613        ));
1614    }
1615
1616    #[cfg(unix)]
1617    {
1618        use std::ffi::OsString;
1619        use std::os::unix::ffi::OsStringExt;
1620        Ok(PathBuf::from(OsString::from_vec(value.to_vec())))
1621    }
1622    #[cfg(not(unix))]
1623    {
1624        String::from_utf8(value.to_vec())
1625            .map(PathBuf::from)
1626            .map_err(|_source| issue(path.to_path_buf(), GitStructureIssueKind::PointerNotUtf8))
1627    }
1628}
1629
1630/// Resolve one pointer relative to its owning administrative directory.
1631fn resolve_pointer(base: &Path, pointer: &Path) -> PathBuf {
1632    if pointer.is_absolute() {
1633        pointer.to_path_buf()
1634    } else {
1635        base.join(pointer)
1636    }
1637}
1638
1639/// Resolve and validate an existing pointed-to directory.
1640fn resolve_existing_directory(
1641    pointer_path: &Path,
1642    base: &Path,
1643    pointer: &Path,
1644) -> Result<PathBuf, GitStructureIssue> {
1645    let target = resolve_pointer(base, pointer);
1646    let metadata = structural_metadata(&target).map_err(|issue| match issue.kind {
1647        GitStructureIssueKind::MissingPointerTarget => issue,
1648        _ => GitStructureIssue {
1649            path: pointer_path.to_path_buf(),
1650            kind: issue.kind,
1651        },
1652    })?;
1653    if !metadata.is_dir() {
1654        return Err(issue(
1655            pointer_path.to_path_buf(),
1656            GitStructureIssueKind::UnsupportedPathType,
1657        ));
1658    }
1659    canonicalize_issue(&target, pointer_path)
1660}
1661
1662/// Resolve and validate an existing pointed-to file.
1663fn resolve_existing_file(
1664    pointer_path: &Path,
1665    base: &Path,
1666    pointer: &Path,
1667) -> Result<PathBuf, GitStructureIssue> {
1668    let target = resolve_pointer(base, pointer);
1669    let metadata = structural_metadata(&target).map_err(|issue| GitStructureIssue {
1670        path: pointer_path.to_path_buf(),
1671        kind: issue.kind,
1672    })?;
1673    if !metadata.is_file() {
1674        return Err(issue(
1675            pointer_path.to_path_buf(),
1676            GitStructureIssueKind::UnsupportedPathType,
1677        ));
1678    }
1679    canonicalize_issue(&target, pointer_path)
1680}
1681
1682/// Read leaf metadata without accepting a symbolic link as control identity.
1683fn structural_metadata(path: &Path) -> Result<fs::Metadata, GitStructureIssue> {
1684    match fs::symlink_metadata(path) {
1685        Ok(metadata) if metadata_is_indirect(&metadata) => Err(issue(
1686            path.to_path_buf(),
1687            GitStructureIssueKind::SymbolicLink,
1688        )),
1689        Ok(metadata) => Ok(metadata),
1690        Err(source) if source.kind() == io::ErrorKind::NotFound => Err(issue(
1691            path.to_path_buf(),
1692            GitStructureIssueKind::MissingPointerTarget,
1693        )),
1694        Err(source) => Err(issue(
1695            path.to_path_buf(),
1696            GitStructureIssueKind::FilesystemUnavailable {
1697                error_kind: source.kind(),
1698            },
1699        )),
1700    }
1701}
1702
1703/// Return whether metadata represents a symbolic link or Windows reparse point.
1704fn metadata_is_indirect(metadata: &fs::Metadata) -> bool {
1705    if metadata.file_type().is_symlink() {
1706        return true;
1707    }
1708    #[cfg(windows)]
1709    {
1710        use std::os::windows::fs::MetadataExt;
1711
1712        const FILE_ATTRIBUTE_REPARSE_POINT: u32 = 0x0400;
1713        metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT != 0
1714    }
1715    #[cfg(not(windows))]
1716    {
1717        false
1718    }
1719}
1720
1721/// Return whether a path exists while preserving actual metadata IO failures.
1722fn path_is_present(path: &Path) -> FsResult<bool> {
1723    match fs::symlink_metadata(path) {
1724        Ok(_) => Ok(true),
1725        Err(source) if source.kind() == io::ErrorKind::NotFound => Ok(false),
1726        Err(source) => Err(FsError::RepositoryBoundary {
1727            path: path.to_path_buf(),
1728            source,
1729        }),
1730    }
1731}
1732
1733/// Canonicalize one filesystem path into the crate's existing typed IO surface.
1734fn canonicalize(path: &Path, evidence_path: &Path) -> FsResult<PathBuf> {
1735    path.canonicalize()
1736        .map_err(|source| FsError::RepositoryBoundary {
1737            path: evidence_path.to_path_buf(),
1738            source,
1739        })
1740}
1741
1742/// Canonicalize one structural path into typed invalid-Git evidence.
1743fn canonicalize_issue(path: &Path, evidence_path: &Path) -> Result<PathBuf, GitStructureIssue> {
1744    path.canonicalize().map_err(|source| {
1745        issue(
1746            evidence_path.to_path_buf(),
1747            if source.kind() == io::ErrorKind::NotFound {
1748                GitStructureIssueKind::MissingPointerTarget
1749            } else {
1750                GitStructureIssueKind::UnsupportedPathType
1751            },
1752        )
1753    })
1754}
1755
1756/// Compare canonical platform paths, tolerating case-only spelling on Windows.
1757fn paths_equal(left: &Path, right: &Path) -> bool {
1758    #[cfg(windows)]
1759    {
1760        left == right
1761            || left
1762                .to_str()
1763                .zip(right.to_str())
1764                .is_some_and(|(left, right)| left.eq_ignore_ascii_case(right))
1765    }
1766    #[cfg(not(windows))]
1767    {
1768        left == right
1769    }
1770}
1771
1772/// Construct one typed structural issue.
1773fn issue(path: PathBuf, kind: GitStructureIssueKind) -> GitStructureIssue {
1774    GitStructureIssue { path, kind }
1775}
1776
1777#[cfg(test)]
1778mod tests {
1779    use super::*;
1780    use projectatlas_core::{IndexWorkFailure, IndexWorkResource};
1781    use std::error::Error;
1782    use std::ffi::OsStr;
1783    use std::process::Command;
1784
1785    #[test]
1786    fn structural_discovery_covers_real_git_worktree_lifecycle_matrix() -> Result<(), Box<dyn Error>>
1787    {
1788        let temp = tempfile::tempdir()?;
1789        let primary = temp.path().join("primary checkout");
1790        fs::create_dir(&primary)?;
1791        run_git(&primary, ["init"])?;
1792        run_git(&primary, ["config", "user.name", "ProjectAtlas Test"])?;
1793        run_git(
1794            &primary,
1795            ["config", "user.email", "projectatlas@example.invalid"],
1796        )?;
1797        fs::create_dir(primary.join("src"))?;
1798        fs::write(primary.join("src").join("main.rs"), "fn main() {}\n")?;
1799        run_git(&primary, ["add", "."])?;
1800        run_git(&primary, ["commit", "-m", "fixture"])?;
1801
1802        let sha256 = temp.path().join("sha256 checkout");
1803        run_command(
1804            Command::new("git")
1805                .args(["init", "--object-format=sha256"])
1806                .arg(&sha256),
1807        )?;
1808        let sha256_root = Command::new("git")
1809            .current_dir(&sha256)
1810            .args(["rev-parse", "--show-toplevel"])
1811            .output()?;
1812        require(
1813            sha256_root.status.success()
1814                && Path::new(String::from_utf8(sha256_root.stdout)?.trim()).canonicalize()?
1815                    == sha256.canonicalize()?,
1816            "Git fixture did not accept the SHA-256 repository",
1817        )?;
1818        let sha256_structure = require_git(discover_repository_structure(&sha256)?)?;
1819        require_worktree_selection(
1820            &sha256_structure,
1821            &sha256.canonicalize()?,
1822            GitWorktreeRole::Primary,
1823        )?;
1824
1825        let reftable = temp.path().join("reftable checkout");
1826        run_command(
1827            Command::new("git")
1828                .args(["init", "--ref-format=reftable"])
1829                .arg(&reftable),
1830        )?;
1831        let reftable_root = Command::new("git")
1832            .current_dir(&reftable)
1833            .args(["rev-parse", "--show-toplevel"])
1834            .output()?;
1835        require(
1836            reftable_root.status.success()
1837                && Path::new(String::from_utf8(reftable_root.stdout)?.trim()).canonicalize()?
1838                    == reftable.canonicalize()?,
1839            "Git fixture did not accept the reftable repository",
1840        )?;
1841        let reftable_structure = require_git(discover_repository_structure(&reftable)?)?;
1842        require_worktree_selection(
1843            &reftable_structure,
1844            &reftable.canonicalize()?,
1845            GitWorktreeRole::Primary,
1846        )?;
1847        let reftable_manager = require_git(discover_repository_structure(&reftable.join(".git"))?)?;
1848        require(
1849            reftable_manager.common_directory == reftable.join(".git").canonicalize()?,
1850            "reftable manager discovery did not retain its common directory",
1851        )?;
1852
1853        let config_path = primary.join(".git").join("config");
1854        let config = fs::read(&config_path)?;
1855        fs::remove_file(&config_path)?;
1856        let configless = require_git(discover_repository_structure(&primary.join("src"))?)?;
1857        require_worktree_selection(
1858            &configless,
1859            &primary.canonicalize()?,
1860            GitWorktreeRole::Primary,
1861        )?;
1862        let configless_manager =
1863            require_git(discover_repository_structure(&primary.join(".git"))?)?;
1864        require(
1865            configless_manager.selection
1866                == GitRepositorySelection::CommonManager {
1867                    source_selection: GitManagerSourceSelection::None,
1868                },
1869            "configless manager inferred a primary checkout without positive non-bare evidence",
1870        )?;
1871        fs::write(&config_path, &config)?;
1872
1873        let mut bom_config = b"\xef\xbb\xbf".to_vec();
1874        bom_config.extend_from_slice(&config);
1875        fs::write(&config_path, &bom_config)?;
1876        let bom_bare = Command::new("git")
1877            .arg("--git-dir")
1878            .arg(primary.join(".git"))
1879            .args(["config", "--bool", "core.bare"])
1880            .output()?;
1881        require(
1882            bom_bare.status.success() && String::from_utf8(bom_bare.stdout)?.trim() == "false",
1883            "Git fixture did not accept a UTF-8 BOM in local config",
1884        )?;
1885        let bom_checkout = require_git(discover_repository_structure(&primary.join("src"))?)?;
1886        require_worktree_selection(
1887            &bom_checkout,
1888            &primary.canonicalize()?,
1889            GitWorktreeRole::Primary,
1890        )?;
1891        fs::write(&config_path, &config)?;
1892
1893        run_git(&primary, ["config", "core.bare", ""])?;
1894        let effective_bare = Command::new("git")
1895            .arg("--git-dir")
1896            .arg(primary.join(".git"))
1897            .args(["config", "--bool", "core.bare"])
1898            .output()?;
1899        require(
1900            effective_bare.status.success()
1901                && String::from_utf8(effective_bare.stdout)?.trim() == "false",
1902            "Git fixture did not interpret an empty core.bare value as false",
1903        )?;
1904        let empty_bare_manager =
1905            require_git(discover_repository_structure(&primary.join(".git"))?)?;
1906        require(
1907            empty_bare_manager.selection
1908                == GitRepositorySelection::CommonManager {
1909                    source_selection: GitManagerSourceSelection::Unambiguous {
1910                        root: primary.canonicalize()?,
1911                    },
1912                },
1913            "empty core.bare value hid the valid primary checkout",
1914        )?;
1915        fs::write(&config_path, &config)?;
1916
1917        let configured_worktree = temp.path().join("configured external worktree");
1918        fs::create_dir(&configured_worktree)?;
1919        run_command(
1920            Command::new("git")
1921                .current_dir(&primary)
1922                .args(["config", "core.worktree"])
1923                .arg(&configured_worktree),
1924        )?;
1925        let effective_worktree = Command::new("git")
1926            .arg("--git-dir")
1927            .arg(primary.join(".git"))
1928            .args(["rev-parse", "--show-toplevel"])
1929            .output()?;
1930        require(
1931            effective_worktree.status.success()
1932                && paths_equal(
1933                    &PathBuf::from(String::from_utf8(effective_worktree.stdout)?.trim())
1934                        .canonicalize()?,
1935                    &configured_worktree.canonicalize()?,
1936                ),
1937            "Git fixture did not relocate its configured worktree",
1938        )?;
1939        for selected in [&primary, &primary.join(".git")] {
1940            let relocated = require_git(discover_repository_structure(selected)?)?;
1941            require(
1942                relocated.selection
1943                    == GitRepositorySelection::CommonManager {
1944                        source_selection: GitManagerSourceSelection::None,
1945                    },
1946                "core.worktree inferred the common-directory parent as source",
1947            )?;
1948        }
1949        run_git(&primary, ["config", "--unset", "core.worktree"])?;
1950
1951        run_git(&primary, ["config", "core.worktree", ".."])?;
1952        let matching_worktree = Command::new("git")
1953            .arg("-C")
1954            .arg(&primary)
1955            .args(["rev-parse", "--show-toplevel"])
1956            .output()?;
1957        require(
1958            matching_worktree.status.success()
1959                && paths_equal(
1960                    &PathBuf::from(String::from_utf8(matching_worktree.stdout)?.trim())
1961                        .canonicalize()?,
1962                    &primary.canonicalize()?,
1963                ),
1964            "Git fixture did not resolve relative core.worktree back to its checkout",
1965        )?;
1966        let matching = require_git(discover_repository_structure(&primary)?)?;
1967        require_worktree_selection(
1968            &matching,
1969            &primary.canonicalize()?,
1970            GitWorktreeRole::Primary,
1971        )?;
1972        run_git(&primary, ["config", "--unset", "core.worktree"])?;
1973
1974        run_git(&primary, ["config", "extensions.worktreeConfig", "true"])?;
1975        run_command(
1976            Command::new("git")
1977                .current_dir(&primary)
1978                .args(["config", "--worktree", "core.worktree"])
1979                .arg(&configured_worktree),
1980        )?;
1981        let worktree_config_path = primary.join(".git").join("config.worktree");
1982        require(
1983            worktree_config_path.is_file(),
1984            "Git fixture did not create config.worktree",
1985        )?;
1986        let effective_worktree = Command::new("git")
1987            .arg("--git-dir")
1988            .arg(primary.join(".git"))
1989            .args(["rev-parse", "--show-toplevel"])
1990            .output()?;
1991        require(
1992            effective_worktree.status.success()
1993                && Path::new(String::from_utf8(effective_worktree.stdout)?.trim())
1994                    .canonicalize()?
1995                    == configured_worktree.canonicalize()?,
1996            "Git fixture did not honor config.worktree core.worktree",
1997        )?;
1998        for selected in [&primary, &primary.join(".git")] {
1999            let relocated = require_git(discover_repository_structure(selected)?)?;
2000            require(
2001                relocated.selection
2002                    == GitRepositorySelection::CommonManager {
2003                        source_selection: GitManagerSourceSelection::None,
2004                    },
2005                "config.worktree core.worktree inferred the common-directory parent as source",
2006            )?;
2007        }
2008        run_git(
2009            &primary,
2010            ["config", "--worktree", "--unset", "core.worktree"],
2011        )?;
2012        run_git(&primary, ["config", "--worktree", "core.bare", "true"])?;
2013        let effective_bare = Command::new("git")
2014            .arg("--git-dir")
2015            .arg(primary.join(".git"))
2016            .args(["config", "--bool", "core.bare"])
2017            .output()?;
2018        require(
2019            effective_bare.status.success()
2020                && String::from_utf8(effective_bare.stdout)?.trim() == "true",
2021            "Git fixture did not honor config.worktree core.bare",
2022        )?;
2023        for selected in [&primary, &primary.join(".git")] {
2024            let per_worktree_bare = require_git(discover_repository_structure(selected)?)?;
2025            require(
2026                per_worktree_bare.selection
2027                    == GitRepositorySelection::CommonManager {
2028                        source_selection: GitManagerSourceSelection::None,
2029                    },
2030                "config.worktree core.bare invented the common-directory parent as source",
2031            )?;
2032        }
2033        run_git(&primary, ["config", "--worktree", "--unset", "core.bare"])?;
2034        run_git(&primary, ["config", "--unset", "extensions.worktreeConfig"])?;
2035
2036        let submodule_source = temp.path().join("submodule source");
2037        fs::create_dir(&submodule_source)?;
2038        run_git(&submodule_source, ["init"])?;
2039        run_git(
2040            &submodule_source,
2041            ["config", "user.name", "ProjectAtlas Test"],
2042        )?;
2043        run_git(
2044            &submodule_source,
2045            ["config", "user.email", "projectatlas@example.invalid"],
2046        )?;
2047        fs::write(submodule_source.join("lib.rs"), "pub fn submodule() {}\n")?;
2048        run_git(&submodule_source, ["add", "."])?;
2049        run_git(&submodule_source, ["commit", "-m", "submodule fixture"])?;
2050        let submodule = primary.join("vendor").join("submodule");
2051        run_command(
2052            Command::new("git")
2053                .current_dir(&primary)
2054                .args(["-c", "protocol.file.allow=always", "submodule", "add"])
2055                .arg(&submodule_source)
2056                .arg("vendor/submodule"),
2057        )?;
2058        let submodule_structure = require_git(discover_repository_structure(&submodule)?)?;
2059        require_worktree_selection(
2060            &submodule_structure,
2061            &submodule.canonicalize()?,
2062            GitWorktreeRole::Primary,
2063        )?;
2064        let submodule_administrative_directory =
2065            active_entry_for_root(&submodule_structure, &submodule.canonicalize()?)?
2066                .administrative_directory
2067                .clone();
2068        let submodule_config_path = submodule_administrative_directory.join("config");
2069        let submodule_config = fs::read(&submodule_config_path)?;
2070        let quoted_pointer_worktree = submodule.with_file_name("submodule#external;worktree");
2071        fs::create_dir(&quoted_pointer_worktree)?;
2072        run_command(
2073            Command::new("git")
2074                .current_dir(&submodule)
2075                .args(["config", "core.worktree"])
2076                .arg(&quoted_pointer_worktree),
2077        )?;
2078        let effective_pointer_worktree = Command::new("git")
2079            .current_dir(&submodule)
2080            .args(["rev-parse", "--show-toplevel"])
2081            .output()?;
2082        require(
2083            effective_pointer_worktree.status.success()
2084                && paths_equal(
2085                    &PathBuf::from(String::from_utf8(effective_pointer_worktree.stdout)?.trim())
2086                        .canonicalize()?,
2087                    &quoted_pointer_worktree.canonicalize()?,
2088                ),
2089            "Git fixture did not preserve the quoted core.worktree comment marker",
2090        )?;
2091        require_invalid_kind(
2092            discover_repository_structure(&submodule)?,
2093            |kind| matches!(kind, GitStructureIssueKind::UnsupportedSourceConfiguration),
2094            "primary pointer core.worktree was admitted as pointer-owned source",
2095        )?;
2096        fs::write(&submodule_config_path, submodule_config)?;
2097        run_git(&primary, ["add", ".gitmodules", "vendor/submodule"])?;
2098        run_git(&primary, ["commit", "-m", "submodule checkout fixture"])?;
2099
2100        let lookalike = primary.join("src").join("application metadata");
2101        fs::create_dir(&lookalike)?;
2102        fs::write(lookalike.join("HEAD"), "ordinary application data\n")?;
2103        fs::write(lookalike.join("config"), "ordinary application data\n")?;
2104        let lookalike_structure = require_git(discover_repository_structure(&lookalike)?)?;
2105        require_worktree_selection(
2106            &lookalike_structure,
2107            &primary.canonicalize()?,
2108            GitWorktreeRole::Primary,
2109        )?;
2110
2111        let nested_linked = primary.join("arbitrary container").join("linked checkout");
2112        add_worktree(&primary, "nested-linked", &nested_linked)?;
2113        let config = fs::read(&config_path)?;
2114        fs::remove_file(&config_path)?;
2115        let configless_mixed_manager =
2116            require_git(discover_repository_structure(&primary.join(".git"))?)?;
2117        require(
2118            configless_mixed_manager.selection
2119                == GitRepositorySelection::CommonManager {
2120                    source_selection: GitManagerSourceSelection::Ambiguous { worktree_count: 2 },
2121                },
2122            "configless mixed manager routed to its sole inventoried linked checkout",
2123        )?;
2124        fs::write(&config_path, config)?;
2125        let outside_linked = temp
2126            .path()
2127            .join("outside arbitrary 工作树")
2128            .join("linked chëckout");
2129        add_worktree(&primary, "outside-linked", &outside_linked)?;
2130        let nested_cwd = outside_linked.join("deep").join("cwd");
2131        fs::create_dir_all(&nested_cwd)?;
2132
2133        run_git(&primary, ["config", "extensions.worktreeConfig", "true"])?;
2134        run_command(
2135            Command::new("git")
2136                .current_dir(&nested_linked)
2137                .args(["config", "--worktree", "core.worktree"])
2138                .arg(&configured_worktree),
2139        )?;
2140        let linked_effective_root = Command::new("git")
2141            .current_dir(&nested_linked)
2142            .args(["rev-parse", "--show-toplevel"])
2143            .output()?;
2144        require(
2145            linked_effective_root.status.success()
2146                && paths_equal(
2147                    &PathBuf::from(String::from_utf8(linked_effective_root.stdout)?.trim())
2148                        .canonicalize()?,
2149                    &configured_worktree.canonicalize()?,
2150                ),
2151            "Git fixture did not honor linked config.worktree core.worktree",
2152        )?;
2153        require_invalid_kind(
2154            discover_repository_structure(&nested_linked)?,
2155            |kind| matches!(kind, GitStructureIssueKind::UnsupportedSourceConfiguration),
2156            "linked config.worktree core.worktree was admitted as pointer-owned source",
2157        )?;
2158        run_git(
2159            &nested_linked,
2160            ["config", "--worktree", "--unset", "core.worktree"],
2161        )?;
2162        run_git(
2163            &nested_linked,
2164            ["config", "--worktree", "core.bare", "true"],
2165        )?;
2166        require_invalid_kind(
2167            discover_repository_structure(&nested_linked)?,
2168            |kind| matches!(kind, GitStructureIssueKind::UnsupportedSourceConfiguration),
2169            "linked config.worktree core.bare was admitted as checked-out source",
2170        )?;
2171        run_git(
2172            &nested_linked,
2173            ["config", "--worktree", "--unset", "core.bare"],
2174        )?;
2175        run_git(&primary, ["config", "--unset", "extensions.worktreeConfig"])?;
2176
2177        let primary_structure = require_git(discover_repository_structure(&primary.join("src"))?)?;
2178        require_worktree_selection(
2179            &primary_structure,
2180            &primary.canonicalize()?,
2181            GitWorktreeRole::Primary,
2182        )?;
2183        require_active_roots(
2184            &primary_structure,
2185            [&primary, &nested_linked, &outside_linked],
2186        )?;
2187
2188        let linked_structure = require_git(discover_repository_structure(&nested_cwd)?)?;
2189        require_worktree_selection(
2190            &linked_structure,
2191            &outside_linked.canonicalize()?,
2192            GitWorktreeRole::Linked,
2193        )?;
2194        require(
2195            linked_structure.common_directory == primary.join(".git").canonicalize()?,
2196            "linked checkout did not resolve the primary common directory",
2197        )?;
2198
2199        let manager = require_git(discover_repository_structure(&primary.join(".git"))?)?;
2200        require(
2201            manager.selection
2202                == GitRepositorySelection::CommonManager {
2203                    source_selection: GitManagerSourceSelection::Ambiguous { worktree_count: 3 },
2204                },
2205            "multi-worktree manager guessed or omitted its ambiguous selection",
2206        )?;
2207
2208        let copied_root = temp.path().join("copied registration");
2209        fs::create_dir(&copied_root)?;
2210        fs::copy(nested_linked.join(".git"), copied_root.join(".git"))?;
2211        let copied = discover_repository_structure(&copied_root)?;
2212        require(
2213            matches!(
2214                copied,
2215                RepositoryStructure::InvalidGit {
2216                    issue: GitStructureIssue {
2217                        kind: GitStructureIssueKind::ReciprocalControlMismatch { .. },
2218                        ..
2219                    },
2220                    ..
2221                }
2222            ),
2223            "a copied one-way .git control file was admitted as reciprocal identity",
2224        )?;
2225
2226        let before_move = active_entry_for_root(&manager, &outside_linked.canonicalize()?)?
2227            .administrative_directory
2228            .clone();
2229        let relocated = temp
2230            .path()
2231            .join("relocated arbitrary container")
2232            .join("checkout");
2233        fs::create_dir_all(
2234            relocated
2235                .parent()
2236                .ok_or_else(|| io::Error::other("relocated fixture path has no parent"))?,
2237        )?;
2238        move_worktree(&primary, &outside_linked, &relocated)?;
2239        fs::create_dir_all(relocated.join("nested"))?;
2240        let relocated_structure =
2241            require_git(discover_repository_structure(&relocated.join("nested"))?)?;
2242        let relocated_root = relocated.canonicalize()?;
2243        require_worktree_selection(
2244            &relocated_structure,
2245            &relocated_root,
2246            GitWorktreeRole::Linked,
2247        )?;
2248        require(
2249            active_entry_for_root(&relocated_structure, &relocated_root)?.administrative_directory
2250                == before_move,
2251            "Git-managed relocation did not retain its administrative identity evidence",
2252        )?;
2253
2254        fs::remove_dir_all(&relocated)?;
2255        let after_removal = require_git(discover_repository_structure(&primary.join(".git"))?)?;
2256        require(
2257            after_removal.worktrees.iter().any(|entry| {
2258                entry.administrative_directory == before_move
2259                    && matches!(entry.state, GitWorktreeState::Missing { .. })
2260            }),
2261            "externally removed worktree was not retained as a typed missing registration",
2262        )?;
2263
2264        let bare = temp.path().join("bare manager.git");
2265        clone_bare(&primary, &bare)?;
2266        let bare_structure = require_git(discover_repository_structure(&bare)?)?;
2267        require(
2268            bare_structure.selection
2269                == GitRepositorySelection::CommonManager {
2270                    source_selection: GitManagerSourceSelection::None,
2271                },
2272            "bare manager without worktrees exposed a source selection",
2273        )?;
2274        let bare_linked = temp.path().join("bare manager checkout");
2275        add_bare_worktree(&bare, &bare_linked)?;
2276        let bare_with_source = require_git(discover_repository_structure(&bare)?)?;
2277        require(
2278            bare_with_source.selection
2279                == GitRepositorySelection::CommonManager {
2280                    source_selection: GitManagerSourceSelection::Unambiguous {
2281                        root: bare_linked.canonicalize()?,
2282                    },
2283                },
2284            "bare manager did not expose its one unambiguous registered worktree",
2285        )?;
2286
2287        let dot_git_container = temp.path().join("bare dot-git container");
2288        fs::create_dir(&dot_git_container)?;
2289        let bare_dot_git = dot_git_container.join(".git");
2290        clone_bare(&primary, &bare_dot_git)?;
2291        for selected in [&bare_dot_git, &dot_git_container] {
2292            let structure = require_git(discover_repository_structure(selected)?)?;
2293            require(
2294                structure.selection
2295                    == GitRepositorySelection::CommonManager {
2296                        source_selection: GitManagerSourceSelection::None,
2297                    },
2298                "bare repository named .git invented its unrelated parent as source",
2299            )?;
2300        }
2301        let bare_config_path = bare_dot_git.join("config");
2302        let bare_config = fs::read(&bare_config_path)?;
2303        run_command(
2304            Command::new("git")
2305                .arg("--git-dir")
2306                .arg(&bare_dot_git)
2307                .args(["config", "extensions.bare", "false"]),
2308        )?;
2309        let effective_bare = Command::new("git")
2310            .arg("--git-dir")
2311            .arg(&bare_dot_git)
2312            .args(["config", "--bool", "core.bare"])
2313            .output()?;
2314        require(
2315            effective_bare.status.success()
2316                && String::from_utf8(effective_bare.stdout)?.trim() == "true",
2317            "Git fixture let a non-core bare key override core.bare",
2318        )?;
2319        let non_core_bare = require_git(discover_repository_structure(&bare_dot_git)?)?;
2320        require(
2321            non_core_bare.selection
2322                == GitRepositorySelection::CommonManager {
2323                    source_selection: GitManagerSourceSelection::None,
2324                },
2325            "non-core bare key invented the manager parent as source",
2326        )?;
2327        fs::write(&bare_config_path, &bare_config)?;
2328
2329        for supported_config in [
2330            "[core]\n repositoryFormatVersion = 1\n bare = false\n[extensions]\n preciousObjects = true\n",
2331            "[core]\n repositoryFormatVersion = 1\n bare = false\n[extensions]\n partialClone = origin\n",
2332            "[core]\n repositoryFormatVersion = 99\n repositoryFormatVersion = 0\n bare = false\n",
2333        ] {
2334            fs::write(&bare_config_path, supported_config)?;
2335            let accepted_config = Command::new("git")
2336                .arg("--git-dir")
2337                .arg(&bare_dot_git)
2338                .args(["rev-parse", "--is-bare-repository"])
2339                .output()?;
2340            require(
2341                accepted_config.status.success(),
2342                "Git fixture rejected a supported effective repository format",
2343            )?;
2344            let accepted_config = require_git(discover_repository_structure(&bare_dot_git)?)?;
2345            require(
2346                accepted_config.selection
2347                    == GitRepositorySelection::CommonManager {
2348                        source_selection: GitManagerSourceSelection::Unambiguous {
2349                            root: dot_git_container.canonicalize()?,
2350                        },
2351                    },
2352                "supported effective repository format hid the exact manager source",
2353            )?;
2354        }
2355        fs::write(&bare_config_path, &bare_config)?;
2356
2357        for (repository_format_version, unsupported_extension) in [
2358            (1, "madeup = true"),
2359            (1, "objectformat = sha512"),
2360            (0, "objectformat = sha256"),
2361        ] {
2362            fs::write(
2363                &bare_config_path,
2364                format!(
2365                    "[core]\n repositoryFormatVersion = {repository_format_version}\n bare = false\n[extensions]\n {unsupported_extension}\n"
2366                ),
2367            )?;
2368            let rejected_extension = Command::new("git")
2369                .arg("--git-dir")
2370                .arg(&bare_dot_git)
2371                .args(["rev-parse", "--is-bare-repository"])
2372                .output()?;
2373            require(
2374                !rejected_extension.status.success(),
2375                "Git fixture accepted a repository extension outside its supported format",
2376            )?;
2377            let rejected_extension = require_git(discover_repository_structure(&bare_dot_git)?)?;
2378            require(
2379                rejected_extension.selection
2380                    == GitRepositorySelection::CommonManager {
2381                        source_selection: GitManagerSourceSelection::None,
2382                    },
2383                "repository extension outside its supported format invented the manager parent as source",
2384            )?;
2385        }
2386        fs::write(&bare_config_path, &bare_config)?;
2387
2388        run_command(
2389            Command::new("git")
2390                .arg("--git-dir")
2391                .arg(&bare_dot_git)
2392                .args(["config", "extensions.worktreeConfig", ""]),
2393        )?;
2394        fs::write(
2395            bare_dot_git.join("config.worktree"),
2396            "[core]\n bare = false\n",
2397        )?;
2398        let effective_worktree_config = Command::new("git")
2399            .arg("--git-dir")
2400            .arg(&bare_dot_git)
2401            .args(["config", "--bool", "extensions.worktreeConfig"])
2402            .output()?;
2403        require(
2404            effective_worktree_config.status.success()
2405                && String::from_utf8(effective_worktree_config.stdout)?.trim() == "false",
2406            "Git fixture did not interpret an empty extensions.worktreeConfig value as false",
2407        )?;
2408        let empty_worktree_config = require_git(discover_repository_structure(&bare_dot_git)?)?;
2409        require(
2410            empty_worktree_config.selection
2411                == GitRepositorySelection::CommonManager {
2412                    source_selection: GitManagerSourceSelection::None,
2413                },
2414            "empty extensions.worktreeConfig enabled config.worktree and invented a source",
2415        )?;
2416        fs::remove_file(bare_dot_git.join("config.worktree"))?;
2417        fs::write(&bare_config_path, &bare_config)?;
2418
2419        fs::write(
2420            &bare_config_path,
2421            "[core]\n bare = true\n[extensions]\n worktreeConfig = 2\n",
2422        )?;
2423        fs::write(
2424            bare_dot_git.join("config.worktree"),
2425            "[core]\n bare = false\n",
2426        )?;
2427        let numeric_worktree_config = Command::new("git")
2428            .arg("--git-dir")
2429            .arg(&bare_dot_git)
2430            .args(["config", "--bool", "extensions.worktreeConfig"])
2431            .output()?;
2432        require(
2433            numeric_worktree_config.status.success()
2434                && String::from_utf8(numeric_worktree_config.stdout)?.trim() == "true",
2435            "Git fixture did not accept a nonzero decimal worktreeConfig boolean",
2436        )?;
2437        let numeric_worktree_config = require_git(discover_repository_structure(&bare_dot_git)?)?;
2438        require(
2439            numeric_worktree_config.selection
2440                == GitRepositorySelection::CommonManager {
2441                    source_selection: GitManagerSourceSelection::Unambiguous {
2442                        root: dot_git_container.canonicalize()?,
2443                    },
2444                },
2445            "nonzero decimal worktreeConfig hid the exact configured source",
2446        )?;
2447        fs::remove_file(bare_dot_git.join("config.worktree"))?;
2448        fs::write(&bare_config_path, &bare_config)?;
2449
2450        fs::write(
2451            &bare_config_path,
2452            "[core]\n bare = true\n[extensions]\n worktreeConfig = maybe\n",
2453        )?;
2454        fs::write(
2455            bare_dot_git.join("config.worktree"),
2456            "[core]\n bare = false\n",
2457        )?;
2458        let invalid_worktree_config = Command::new("git")
2459            .arg("--git-dir")
2460            .arg(&bare_dot_git)
2461            .args(["config", "--bool", "extensions.worktreeConfig"])
2462            .output()?;
2463        require(
2464            !invalid_worktree_config.status.success(),
2465            "Git fixture accepted an invalid extensions.worktreeConfig boolean",
2466        )?;
2467        let invalid_worktree_config = require_git(discover_repository_structure(&bare_dot_git)?)?;
2468        require(
2469            invalid_worktree_config.selection
2470                == GitRepositorySelection::CommonManager {
2471                    source_selection: GitManagerSourceSelection::None,
2472                },
2473            "invalid extensions.worktreeConfig enabled config.worktree and invented a source",
2474        )?;
2475        fs::remove_file(bare_dot_git.join("config.worktree"))?;
2476        fs::write(&bare_config_path, &bare_config)?;
2477
2478        fs::write(&bare_config_path, "[core] bare = false\n")?;
2479        let inline_section_assignment = Command::new("git")
2480            .arg("--git-dir")
2481            .arg(&bare_dot_git)
2482            .args(["config", "--bool", "core.bare"])
2483            .output()?;
2484        require(
2485            inline_section_assignment.status.success()
2486                && String::from_utf8(inline_section_assignment.stdout)?.trim() == "false",
2487            "Git fixture did not accept an assignment after a section header",
2488        )?;
2489        let inline_section_assignment = require_git(discover_repository_structure(&bare_dot_git)?)?;
2490        require(
2491            inline_section_assignment.selection
2492                == GitRepositorySelection::CommonManager {
2493                    source_selection: GitManagerSourceSelection::Unambiguous {
2494                        root: dot_git_container.canonicalize()?,
2495                    },
2496                },
2497            "inline section assignment hid the exact configured source",
2498        )?;
2499        fs::write(&bare_config_path, &bare_config)?;
2500
2501        for malformed_config in [
2502            "[core\n bare = false\n",
2503            "[core] trailing garbage\n bare = false\n",
2504            "[core]\n bare = true\n bad line\n bare = false\n",
2505        ] {
2506            fs::write(&bare_config_path, malformed_config)?;
2507            let malformed_bare = Command::new("git")
2508                .arg("--git-dir")
2509                .arg(&bare_dot_git)
2510                .args(["config", "--bool", "core.bare"])
2511                .output()?;
2512            require(
2513                !malformed_bare.status.success(),
2514                "Git fixture accepted malformed local config",
2515            )?;
2516            let malformed_bare = require_git(discover_repository_structure(&bare_dot_git)?)?;
2517            require(
2518                malformed_bare.selection
2519                    == GitRepositorySelection::CommonManager {
2520                        source_selection: GitManagerSourceSelection::None,
2521                    },
2522                "malformed local config invented the manager parent as source",
2523            )?;
2524        }
2525        fs::write(&bare_config_path, &bare_config)?;
2526
2527        for invalid_spacing in ['\u{000b}', '\u{000c}', '\r', '\u{00a0}'] {
2528            fs::write(
2529                &bare_config_path,
2530                format!("[other]\n value{invalid_spacing} = accepted\n[core]\n bare = false\n"),
2531            )?;
2532            let malformed_spacing = Command::new("git")
2533                .arg("--git-dir")
2534                .arg(&bare_dot_git)
2535                .args(["config", "--bool", "core.bare"])
2536                .output()?;
2537            require(
2538                !malformed_spacing.status.success(),
2539                "Git fixture accepted invalid variable-name whitespace",
2540            )?;
2541            let malformed_spacing = require_git(discover_repository_structure(&bare_dot_git)?)?;
2542            require(
2543                malformed_spacing.selection
2544                    == GitRepositorySelection::CommonManager {
2545                        source_selection: GitManagerSourceSelection::None,
2546                    },
2547                "invalid variable-name whitespace invented the manager parent as source",
2548            )?;
2549        }
2550        fs::write(&bare_config_path, &bare_config)?;
2551
2552        fs::write(
2553            &bare_config_path,
2554            "[other]\n value = foo\"bar baz\"qux\n[core]\n bare = false\n",
2555        )?;
2556        let mixed_quoted_value = Command::new("git")
2557            .arg("--git-dir")
2558            .arg(&bare_dot_git)
2559            .args(["config", "--get", "other.value"])
2560            .output()?;
2561        require(
2562            mixed_quoted_value.status.success()
2563                && String::from_utf8(mixed_quoted_value.stdout)?.trim_end_matches(['\r', '\n'])
2564                    == "foobar bazqux",
2565            "Git fixture did not concatenate mixed quoted value segments",
2566        )?;
2567        let mixed_quoted_value = require_git(discover_repository_structure(&bare_dot_git)?)?;
2568        require(
2569            mixed_quoted_value.selection
2570                == GitRepositorySelection::CommonManager {
2571                    source_selection: GitManagerSourceSelection::Unambiguous {
2572                        root: dot_git_container.canonicalize()?,
2573                    },
2574                },
2575            "valid mixed quoted value in an unrelated section hid the exact manager source",
2576        )?;
2577        fs::write(&bare_config_path, &bare_config)?;
2578
2579        fs::write(
2580            &bare_config_path,
2581            "[other]\n value = foo\\\\\n[core]\n bare = false\n",
2582        )?;
2583        let escaped_backslash = Command::new("git")
2584            .arg("--git-dir")
2585            .arg(&bare_dot_git)
2586            .args(["config", "--get", "other.value"])
2587            .output()?;
2588        require(
2589            escaped_backslash.status.success()
2590                && String::from_utf8(escaped_backslash.stdout)?.trim_end_matches(['\r', '\n'])
2591                    == "foo\\",
2592            "Git fixture did not decode the escaped terminal backslash",
2593        )?;
2594        let escaped_backslash = require_git(discover_repository_structure(&bare_dot_git)?)?;
2595        require(
2596            escaped_backslash.selection
2597                == GitRepositorySelection::CommonManager {
2598                    source_selection: GitManagerSourceSelection::Unambiguous {
2599                        root: dot_git_container.canonicalize()?,
2600                    },
2601                },
2602            "escaped terminal backslash in an unrelated section hid the exact manager source",
2603        )?;
2604        fs::write(&bare_config_path, &bare_config)?;
2605
2606        for malformed_value in [
2607            "\"unterminated",
2608            r#"foo"unterminated"#,
2609            r"invalid\q",
2610            r#"foo"invalid\q"bar"#,
2611        ] {
2612            fs::write(
2613                &bare_config_path,
2614                format!("[other]\n value = {malformed_value}\n[core]\n bare = false\n"),
2615            )?;
2616            let malformed_unrelated_value = Command::new("git")
2617                .arg("--git-dir")
2618                .arg(&bare_dot_git)
2619                .args(["config", "--bool", "core.bare"])
2620                .output()?;
2621            require(
2622                !malformed_unrelated_value.status.success(),
2623                "Git fixture accepted a malformed value in an unrelated section",
2624            )?;
2625            let malformed_unrelated_value =
2626                require_git(discover_repository_structure(&bare_dot_git)?)?;
2627            require(
2628                malformed_unrelated_value.selection
2629                    == GitRepositorySelection::CommonManager {
2630                        source_selection: GitManagerSourceSelection::None,
2631                    },
2632                "malformed unrelated config value invented the manager parent as source",
2633            )?;
2634        }
2635
2636        fs::write(
2637            &bare_config_path,
2638            "[core]\n bare = false # ignored Windows path C:\\\n",
2639        )?;
2640        let commented_backslash = Command::new("git")
2641            .arg("--git-dir")
2642            .arg(&bare_dot_git)
2643            .args(["config", "--bool", "core.bare"])
2644            .output()?;
2645        require(
2646            commented_backslash.status.success()
2647                && String::from_utf8(commented_backslash.stdout)?.trim() == "false",
2648            "Git fixture treated a trailing backslash in a comment as continuation",
2649        )?;
2650        let commented_backslash = require_git(discover_repository_structure(&bare_dot_git)?)?;
2651        require(
2652            commented_backslash.selection
2653                == GitRepositorySelection::CommonManager {
2654                    source_selection: GitManagerSourceSelection::Unambiguous {
2655                        root: dot_git_container.canonicalize()?,
2656                    },
2657                },
2658            "trailing backslash inside a comment hid the exact manager source",
2659        )?;
2660        fs::write(&bare_config_path, &bare_config)?;
2661
2662        fs::write(
2663            &bare_config_path,
2664            "[core]\n bare = true\n[extensions]\n worktreeConfig = fals\\\ne\n",
2665        )?;
2666        fs::write(
2667            bare_dot_git.join("config.worktree"),
2668            "[core]\n bare = false\n",
2669        )?;
2670        let effective_worktree_config = Command::new("git")
2671            .arg("--git-dir")
2672            .arg(&bare_dot_git)
2673            .args(["config", "--bool", "extensions.worktreeConfig"])
2674            .output()?;
2675        require(
2676            effective_worktree_config.status.success()
2677                && String::from_utf8(effective_worktree_config.stdout)?.trim() == "false",
2678            "Git fixture did not join the continued extensions.worktreeConfig value",
2679        )?;
2680        let continued_worktree_config = require_git(discover_repository_structure(&bare_dot_git)?)?;
2681        require(
2682            continued_worktree_config.selection
2683                == GitRepositorySelection::CommonManager {
2684                    source_selection: GitManagerSourceSelection::None,
2685                },
2686            "continued extensions.worktreeConfig value invented the manager parent as source",
2687        )?;
2688        fs::remove_file(bare_dot_git.join("config.worktree"))?;
2689        fs::write(&bare_config_path, &bare_config)?;
2690
2691        fs::remove_file(&bare_config_path)?;
2692        let configless_bare_manager = require_git(discover_repository_structure(&bare_dot_git)?)?;
2693        require(
2694            configless_bare_manager.selection
2695                == GitRepositorySelection::CommonManager {
2696                    source_selection: GitManagerSourceSelection::None,
2697                },
2698            "configless .git manager inferred a primary checkout without non-bare evidence",
2699        )?;
2700        fs::write(&bare_config_path, bare_config)?;
2701
2702        let included_config = temp.path().join("included bare config");
2703        fs::write(&included_config, "[core]\n bare = true\n")?;
2704        let included_path = included_config
2705            .to_string_lossy()
2706            .replace('\\', "/")
2707            .replace('"', "\\\"");
2708        fs::write(
2709            bare_dot_git.join("config"),
2710            format!("[core]\n bare = false\n[include]\n path = \"{included_path}\"\n"),
2711        )?;
2712        let effective_bare = Command::new("git")
2713            .arg("--git-dir")
2714            .arg(&bare_dot_git)
2715            .args(["config", "--bool", "core.bare"])
2716            .output()?;
2717        require(
2718            effective_bare.status.success()
2719                && String::from_utf8(effective_bare.stdout)?.trim() == "true",
2720            "Git fixture include did not override the local core.bare value",
2721        )?;
2722        let included_bare_manager = require_git(discover_repository_structure(&bare_dot_git)?)?;
2723        require(
2724            included_bare_manager.selection
2725                == GitRepositorySelection::CommonManager {
2726                    source_selection: GitManagerSourceSelection::None,
2727                },
2728            "unresolved config include invented the manager parent as source",
2729        )?;
2730
2731        let non_git = temp.path().join("plain directory");
2732        let non_git_nested = non_git.join("nested").join("cwd");
2733        fs::create_dir_all(non_git.join(".projectatlas"))?;
2734        fs::create_dir_all(&non_git_nested)?;
2735        require(
2736            discover_repository_structure(&non_git_nested)?
2737                == RepositoryStructure::NonGit {
2738                    selected_root: non_git_nested.canonicalize()?,
2739                },
2740            "plain directory did not preserve the exact caller-selected non-Git root",
2741        )?;
2742        Ok(())
2743    }
2744
2745    #[test]
2746    fn native_git_pointer_records_preserve_path_whitespace() -> Result<(), Box<dyn Error>> {
2747        let temp = tempfile::tempdir()?;
2748        let pointer = temp.path().join("pointer");
2749        for value in [" relative path ", "\tleading and trailing tabs\t"] {
2750            for ending in ["", "\n", "\r\n", "\n\n"] {
2751                fs::write(&pointer, format!("gitdir: {value}{ending}"))?;
2752                require(
2753                    read_prefixed_pointer(&pointer, "gitdir:")
2754                        .is_ok_and(|parsed| parsed == Path::new(value)),
2755                    "prefixed Git pointer changed native path whitespace",
2756                )?;
2757                fs::write(&pointer, format!("{value}{ending}"))?;
2758                require(
2759                    read_plain_pointer(&pointer).is_ok_and(|parsed| parsed == Path::new(value)),
2760                    "plain Git pointer changed native path whitespace",
2761                )?;
2762            }
2763        }
2764        for malformed in [b"".as_slice(), b"\r\n", b"first\nsecond", b"path\0suffix"] {
2765            fs::write(&pointer, malformed)?;
2766            require(
2767                read_plain_pointer(&pointer).is_err(),
2768                "malformed plain Git pointer was accepted",
2769            )?;
2770        }
2771        for malformed in [
2772            "gitdir:",
2773            "gitdir: ",
2774            "gitdir:path",
2775            " gitdir: path",
2776            "\ngitdir: path",
2777        ] {
2778            fs::write(&pointer, malformed)?;
2779            require(
2780                read_prefixed_pointer(&pointer, "gitdir:").is_err(),
2781                "malformed prefixed Git pointer was accepted",
2782            )?;
2783        }
2784        Ok(())
2785    }
2786
2787    #[test]
2788    fn structural_discovery_is_git_process_independent_and_rejects_unsafe_control_files()
2789    -> Result<(), Box<dyn Error>> {
2790        let temp = tempfile::tempdir()?;
2791        let handwritten = temp.path().join("handwritten checkout");
2792        write_structural_primary(&handwritten)?;
2793        let structure = require_git(discover_repository_structure(&handwritten)?)?;
2794        require_worktree_selection(
2795            &structure,
2796            &handwritten.canonicalize()?,
2797            GitWorktreeRole::Primary,
2798        )?;
2799
2800        let malformed = temp.path().join("malformed pointer");
2801        fs::create_dir(&malformed)?;
2802        fs::write(malformed.join(".git"), "gitdir: first\ngitdir: second\n")?;
2803        require_invalid_kind(
2804            discover_repository_structure(&malformed)?,
2805            |kind| matches!(kind, GitStructureIssueKind::MalformedPointer),
2806            "ambiguous multi-record .git pointer was not rejected",
2807        )?;
2808
2809        let oversized = temp.path().join("oversized pointer");
2810        fs::create_dir(&oversized)?;
2811        fs::write(
2812            oversized.join(".git"),
2813            vec![b'x'; GIT_DIRECTORY_POINTER_MAX_BYTES as usize + 1],
2814        )?;
2815        require_invalid_kind(
2816            discover_repository_structure(&oversized)?,
2817            |kind| matches!(kind, GitStructureIssueKind::PointerTooLarge { .. }),
2818            "oversized .git pointer was not rejected at the byte bound",
2819        )?;
2820
2821        let non_utf8 = temp.path().join("non utf8 pointer");
2822        fs::create_dir(&non_utf8)?;
2823        fs::write(non_utf8.join(".git"), [0xff, 0xfe])?;
2824        require_invalid_kind(
2825            discover_repository_structure(&non_utf8)?,
2826            |kind| matches!(kind, GitStructureIssueKind::PointerNotUtf8),
2827            "non-UTF-8 .git pointer was not rejected",
2828        )?;
2829
2830        let symlinked = temp.path().join("symlinked control");
2831        fs::create_dir(&symlinked)?;
2832        create_control_symlink(&handwritten.join(".git"), &symlinked.join(".git"))?;
2833        require_invalid_kind(
2834            discover_repository_structure(&symlinked)?,
2835            |kind| matches!(kind, GitStructureIssueKind::SymbolicLink),
2836            "symbolic-link Git control metadata was followed as identity evidence",
2837        )?;
2838
2839        let common_directory = handwritten.join(".git");
2840        let external_registrations = temp.path().join("external registrations");
2841        fs::create_dir(&external_registrations)?;
2842        let registrations = common_directory.join("worktrees");
2843        create_control_symlink(&external_registrations, &registrations)?;
2844        require_invalid_kind(
2845            discover_repository_structure(&handwritten)?,
2846            |kind| matches!(kind, GitStructureIssueKind::SymbolicLink),
2847            "indirect worktree registration container was followed outside the common root",
2848        )?;
2849        remove_control_symlink(&registrations)?;
2850
2851        fs::create_dir(&registrations)?;
2852        let external_registration = temp.path().join("external registration");
2853        fs::create_dir(&external_registration)?;
2854        let indirect_registration = registrations.join("indirect registration");
2855        create_control_symlink(&external_registration, &indirect_registration)?;
2856        let structure = require_git(discover_repository_structure(&handwritten)?)?;
2857        require(
2858            structure.worktrees.iter().any(|entry| {
2859                matches!(
2860                    &entry.state,
2861                    GitWorktreeState::Invalid {
2862                        issue: GitStructureIssue {
2863                            kind: GitStructureIssueKind::SymbolicLink,
2864                            ..
2865                        }
2866                    }
2867                )
2868            }),
2869            "indirect registration entry was not retained as typed invalid evidence",
2870        )?;
2871        remove_control_symlink(&indirect_registration)?;
2872
2873        let cancellation = IndexCancellation::new();
2874        cancellation.cancel();
2875        let canceled = discover_repository_structure_controlled(
2876            &handwritten,
2877            &IndexWorkControl::new(cancellation, None),
2878        );
2879        require(
2880            matches!(
2881                canceled,
2882                Err(FsError::IndexWork(IndexWorkFailure::Cancelled {
2883                    stage: IndexWorkStage::RepositoryTraversal
2884                }))
2885            ),
2886            "pre-canceled structural discovery did not stop before filesystem traversal",
2887        )?;
2888        Ok(())
2889    }
2890
2891    #[test]
2892    fn structural_discovery_enforces_the_registration_count_bound_without_partial_state()
2893    -> Result<(), Box<dyn Error>> {
2894        let temp = tempfile::tempdir()?;
2895        let repo = temp.path().join("bounded checkout");
2896        write_structural_primary(&repo)?;
2897        let registrations = repo.join(".git").join("worktrees");
2898        fs::create_dir(&registrations)?;
2899        for index in 0..=projectatlas_core::MAX_GIT_WORKTREE_REGISTRATIONS {
2900            fs::create_dir(registrations.join(format!("registration-{index:04}")))?;
2901        }
2902
2903        let result = discover_repository_structure(&repo);
2904        require(
2905            matches!(
2906                result,
2907                Err(FsError::IndexWork(
2908                    IndexWorkFailure::ResourceLimitExceeded {
2909                        stage: IndexWorkStage::RepositoryTraversal,
2910                        resource: IndexWorkResource::Entries,
2911                        limit,
2912                        observed,
2913                    }
2914                )) if limit == projectatlas_core::MAX_GIT_WORKTREE_REGISTRATIONS as u64
2915                    && observed == projectatlas_core::MAX_GIT_WORKTREE_REGISTRATIONS as u64 + 1
2916            ),
2917            "registration overflow returned partial or untyped repository state",
2918        )?;
2919        Ok(())
2920    }
2921
2922    #[test]
2923    fn exact_worktree_lifecycle_check_does_not_inventory_sibling_registrations()
2924    -> Result<(), Box<dyn Error>> {
2925        let temp = tempfile::tempdir()?;
2926        let root = temp.path().join("exact checkout");
2927        write_structural_primary(&root)?;
2928        let common = root.join(".git").canonicalize()?;
2929        let identity = git_administrative_identity(&common)?;
2930        let registrations = common.join("worktrees");
2931        fs::create_dir(&registrations)?;
2932        for index in 0..=projectatlas_core::MAX_GIT_WORKTREE_REGISTRATIONS {
2933            fs::create_dir(registrations.join(format!("unrelated-{index:04}")))?;
2934        }
2935
2936        require(
2937            git_worktree_lifecycle_matches(&root, &common, &common, &identity)?,
2938            "exact lifecycle validation enumerated unrelated worktree registrations",
2939        )
2940    }
2941
2942    #[cfg(unix)]
2943    #[test]
2944    fn real_git_worktree_preserves_non_utf8_native_paths() -> Result<(), Box<dyn Error>> {
2945        use std::ffi::OsString;
2946        use std::os::unix::ffi::OsStringExt;
2947
2948        let temp = tempfile::tempdir()?;
2949        let primary = temp
2950            .path()
2951            .join(OsString::from_vec(b"primary-\xff".to_vec()));
2952        fs::create_dir(&primary)?;
2953        run_git(&primary, ["init"])?;
2954        run_git(&primary, ["config", "user.name", "ProjectAtlas Test"])?;
2955        run_git(
2956            &primary,
2957            ["config", "user.email", "projectatlas@example.invalid"],
2958        )?;
2959        fs::write(primary.join("README.md"), "native worktree identity\n")?;
2960        run_git(&primary, ["add", "."])?;
2961        run_git(&primary, ["commit", "-m", "fixture"])?;
2962
2963        let linked = temp
2964            .path()
2965            .join(OsString::from_vec(b"linked-\xfe".to_vec()));
2966        add_worktree(&primary, "native-bytes", &linked)?;
2967
2968        let primary = primary.canonicalize()?;
2969        let linked = linked.canonicalize()?;
2970        let structure = require_git(discover_repository_structure(&linked)?)?;
2971        let entry = structure
2972            .worktrees
2973            .iter()
2974            .find(|entry| {
2975                matches!(
2976                    &entry.state,
2977                    GitWorktreeState::Active { root, .. } if root == &linked
2978                )
2979            })
2980            .ok_or_else(|| io::Error::other("native linked worktree was not discovered"))?;
2981
2982        require(
2983            primary.to_str().is_none()
2984                && linked.to_str().is_none()
2985                && structure.common_directory.to_str().is_none()
2986                && entry.administrative_directory.to_str().is_none(),
2987            "native invalid-byte worktree paths unexpectedly became UTF-8",
2988        )?;
2989        require(
2990            structure.common_directory == primary.join(".git"),
2991            "native linked worktree changed its common directory bytes",
2992        )?;
2993        let administrative_identity = git_administrative_identity(&entry.administrative_directory)?;
2994        require(
2995            git_worktree_lifecycle_matches(
2996                &linked,
2997                &structure.common_directory,
2998                &entry.administrative_directory,
2999                &administrative_identity,
3000            )?,
3001            "native linked worktree lifecycle did not round-trip through Git evidence",
3002        )?;
3003        Ok(())
3004    }
3005
3006    #[test]
3007    fn git_config_values_accept_mixed_quotes_without_weakening_syntax_checks() {
3008        for (raw, expected) in [
3009            (r#" foo"bar baz"qux "#, "foobar bazqux"),
3010            (
3011                r#"pre"quoted #; value"post ; ignored comment"#,
3012                "prequoted #; valuepost",
3013            ),
3014            (r#"escaped\"quote"#, r#"escaped\"quote"#),
3015            (" false # ignored trailing backslash \\", "false"),
3016        ] {
3017            assert_eq!(git_config_value(raw).as_deref(), Some(expected));
3018        }
3019        for malformed in [r#"foo"unterminated"#, r#"foo"invalid\q"bar"#, "trailing\\"] {
3020            assert_eq!(git_config_value(malformed), None);
3021        }
3022    }
3023
3024    #[test]
3025    fn git_repository_extension_policy_covers_defined_keys() -> Result<(), Box<dyn Error>> {
3026        let temp = tempfile::tempdir()?;
3027        let config = temp.path().join("config");
3028        for (repository_format_version, extension, accepted) in [
3029            (1, "compatObjectFormat = sha256", true),
3030            (0, "noop = true", true),
3031            (1, "noop-v1 = true", true),
3032            (1, "objectFormat = sha256", true),
3033            (0, "partialClone = origin", true),
3034            (0, "preciousObjects = true", true),
3035            (1, "refStorage = files", true),
3036            (1, "refStorage = reftable", true),
3037            (1, "relativeWorktrees = true", true),
3038            (1, "submodulePathConfig = true", true),
3039            (0, "worktreeConfig = true", true),
3040            (1, "compatObjectFormat = sha1", false),
3041            (0, "partialClone =", true),
3042            (0, "preciousObjects = invalid", false),
3043            (1, "refStorage = unknown", false),
3044            (1, "relativeWorktrees = invalid", false),
3045            (1, "submodulePathConfig = invalid", false),
3046            (0, "worktreeConfig = invalid", false),
3047        ] {
3048            fs::write(
3049                &config,
3050                format!(
3051                    "[core]\n repositoryFormatVersion = {repository_format_version}\n[extensions]\n {extension}\n"
3052                ),
3053            )?;
3054            let policy = match local_config_policy(&config) {
3055                Ok(policy) => policy,
3056                Err(issue) => {
3057                    return Err(io::Error::other(format!(
3058                        "defined repository extension config was unreadable: {issue:?}"
3059                    ))
3060                    .into());
3061                }
3062            };
3063            require(
3064                policy.source_root_inference_safe == accepted
3065                    && policy.source_selection_policy_complete == accepted,
3066                &format!("repository extension policy disagreed for: {extension}"),
3067            )?;
3068        }
3069        Ok(())
3070    }
3071
3072    #[cfg(unix)]
3073    #[test]
3074    fn unquoted_git_config_escapes_cannot_change_pointer_owner() -> Result<(), Box<dyn Error>> {
3075        let temp = tempfile::tempdir()?;
3076        let pointer_owner = temp.path().join(r"owner\\root");
3077        fs::create_dir(&pointer_owner)?;
3078        write_structural_primary(&pointer_owner)?;
3079        fs::write(
3080            pointer_owner.join(".git").join("config"),
3081            "[core]\n bare = false\n worktree = ../../owner\\\\root\n",
3082        )?;
3083
3084        let effective_setting = Command::new("git")
3085            .arg("--git-dir")
3086            .arg(pointer_owner.join(".git"))
3087            .args(["config", "--get", "core.worktree"])
3088            .output()?;
3089        require(
3090            effective_setting.status.success()
3091                && String::from_utf8(effective_setting.stdout)?.trim_end_matches(['\r', '\n'])
3092                    == r"../../owner\root",
3093            "Git fixture did not decode the unquoted backslash escape",
3094        )?;
3095        let escaped_pointer = require_git(discover_repository_structure(&pointer_owner)?)?;
3096        require(
3097            escaped_pointer.selection
3098                == GitRepositorySelection::CommonManager {
3099                    source_selection: GitManagerSourceSelection::None,
3100                },
3101            "unquoted backslash escape admitted a pointer owner Git did not select",
3102        )?;
3103        Ok(())
3104    }
3105
3106    #[cfg(unix)]
3107    #[test]
3108    fn lifecycle_identity_requires_a_creation_timestamp() {
3109        let result = required_creation_nanos(
3110            Path::new("administrative-directory"),
3111            Err(io::Error::new(
3112                io::ErrorKind::Unsupported,
3113                "creation time unavailable",
3114            )),
3115        );
3116        assert!(matches!(
3117            result,
3118            Err(FsError::RepositoryBoundary { source, .. })
3119                if source.kind() == io::ErrorKind::Unsupported
3120        ));
3121    }
3122
3123    #[cfg(windows)]
3124    #[test]
3125    fn windows_lifecycle_identity_includes_stable_volume_and_file_identity()
3126    -> Result<(), Box<dyn Error>> {
3127        let temp = tempfile::tempdir()?;
3128        let administrative_directory = temp.path().join("administrative directory");
3129        fs::create_dir(&administrative_directory)?;
3130        let first_native = windows_file_identity::read(&administrative_directory)?;
3131        let first = git_administrative_identity(&administrative_directory)?;
3132        let stable_native = windows_file_identity::read(&administrative_directory)?;
3133        let stable = git_administrative_identity(&administrative_directory)?;
3134        require(
3135            first_native.volume_serial_number == stable_native.volume_serial_number
3136                && first_native.file_id == stable_native.file_id
3137                && first == stable,
3138            "Windows directory identity changed within one lifecycle",
3139        )?;
3140
3141        fs::remove_dir(&administrative_directory)?;
3142        fs::create_dir(&administrative_directory)?;
3143        let replacement_native = windows_file_identity::read(&administrative_directory)?;
3144        let replacement = git_administrative_identity(&administrative_directory)?;
3145        require(
3146            first_native.volume_serial_number != replacement_native.volume_serial_number
3147                || first_native.file_id != replacement_native.file_id,
3148            "Windows replacement reused the original volume and file identity",
3149        )?;
3150        require(
3151            first != replacement,
3152            "Windows replacement reused the original lifecycle hash",
3153        )?;
3154        Ok(())
3155    }
3156
3157    /// Run one Git command with fixed UTF-8 arguments.
3158    fn run_git<const N: usize>(repo: &Path, arguments: [&str; N]) -> Result<(), Box<dyn Error>> {
3159        run_command(Command::new("git").current_dir(repo).args(arguments))
3160    }
3161
3162    /// Add one linked worktree at an arbitrary exact path.
3163    fn add_worktree(repo: &Path, branch: &str, path: &Path) -> Result<(), Box<dyn Error>> {
3164        run_command(
3165            Command::new("git")
3166                .current_dir(repo)
3167                .args(["worktree", "add", "-b", branch])
3168                .arg(path),
3169        )
3170    }
3171
3172    /// Move one linked worktree through Git so reciprocal metadata is updated.
3173    fn move_worktree(repo: &Path, from: &Path, to: &Path) -> Result<(), Box<dyn Error>> {
3174        run_command(
3175            Command::new("git")
3176                .current_dir(repo)
3177                .args([OsStr::new("worktree"), OsStr::new("move")])
3178                .arg(from)
3179                .arg(to),
3180        )
3181    }
3182
3183    /// Clone one bare common manager from an existing repository.
3184    fn clone_bare(repo: &Path, bare: &Path) -> Result<(), Box<dyn Error>> {
3185        let parent = repo
3186            .parent()
3187            .ok_or_else(|| io::Error::other("fixture repository has no parent"))?;
3188        run_command(
3189            Command::new("git")
3190                .current_dir(parent)
3191                .args([OsStr::new("clone"), OsStr::new("--bare")])
3192                .arg(repo)
3193                .arg(bare),
3194        )
3195    }
3196
3197    /// Add one detached source worktree to a bare common manager.
3198    fn add_bare_worktree(bare: &Path, path: &Path) -> Result<(), Box<dyn Error>> {
3199        run_command(
3200            Command::new("git")
3201                .arg("--git-dir")
3202                .arg(bare)
3203                .args(["worktree", "add", "--detach"])
3204                .arg(path)
3205                .arg("HEAD"),
3206        )
3207    }
3208
3209    /// Execute a prepared fixture command and preserve its stdout/stderr on failure.
3210    fn run_command(command: &mut Command) -> Result<(), Box<dyn Error>> {
3211        let output = command.output()?;
3212        if output.status.success() {
3213            Ok(())
3214        } else {
3215            Err(io::Error::other(format!(
3216                "fixture Git command failed: {}{}",
3217                String::from_utf8_lossy(&output.stdout),
3218                String::from_utf8_lossy(&output.stderr),
3219            ))
3220            .into())
3221        }
3222    }
3223
3224    /// Write the minimum standard Git control shape used by process-independent tests.
3225    fn write_structural_primary(root: &Path) -> Result<(), Box<dyn Error>> {
3226        let common = root.join(".git");
3227        write_structural_common(&common)
3228    }
3229
3230    /// Write the minimum standard common-control shape without invoking Git.
3231    fn write_structural_common(common: &Path) -> Result<(), Box<dyn Error>> {
3232        fs::create_dir_all(common.join("objects"))?;
3233        fs::create_dir(common.join("refs"))?;
3234        fs::write(common.join("HEAD"), "ref: refs/heads/main\n")?;
3235        fs::write(common.join("config"), "[core]\n bare = false\n")?;
3236        Ok(())
3237    }
3238
3239    /// Create a directory control symlink.
3240    #[cfg(unix)]
3241    fn create_control_symlink(target: &Path, link: &Path) -> Result<(), Box<dyn Error>> {
3242        std::os::unix::fs::symlink(target, link)?;
3243        Ok(())
3244    }
3245
3246    /// Create a directory control symlink or junction.
3247    #[cfg(windows)]
3248    fn create_control_symlink(target: &Path, link: &Path) -> Result<(), Box<dyn Error>> {
3249        match std::os::windows::fs::symlink_dir(target, link) {
3250            Ok(()) => Ok(()),
3251            Err(error)
3252                if error.kind() == io::ErrorKind::PermissionDenied
3253                    || error.raw_os_error() == Some(1314) =>
3254            {
3255                run_command(
3256                    Command::new("cmd.exe")
3257                        .args(["/D", "/C", "mklink", "/J"])
3258                        .arg(link)
3259                        .arg(target),
3260                )?;
3261                Ok(())
3262            }
3263            Err(error) => Err(error.into()),
3264        }
3265    }
3266
3267    /// Remove only the test-owned directory indirection leaf.
3268    #[cfg(unix)]
3269    fn remove_control_symlink(link: &Path) -> Result<(), Box<dyn Error>> {
3270        fs::remove_file(link)?;
3271        Ok(())
3272    }
3273
3274    /// Remove only the test-owned directory indirection leaf.
3275    #[cfg(windows)]
3276    fn remove_control_symlink(link: &Path) -> Result<(), Box<dyn Error>> {
3277        fs::remove_dir(link)?;
3278        Ok(())
3279    }
3280
3281    /// Extract validated Git state from a discovery result.
3282    fn require_git(
3283        structure: RepositoryStructure,
3284    ) -> Result<GitRepositoryStructure, Box<dyn Error>> {
3285        match structure {
3286            RepositoryStructure::Git(structure) => Ok(structure),
3287            other => {
3288                Err(io::Error::other(format!("expected Git structure, found {other:?}")).into())
3289            }
3290        }
3291    }
3292
3293    /// Require the exact selected worktree root and role.
3294    fn require_worktree_selection(
3295        structure: &GitRepositoryStructure,
3296        expected_root: &Path,
3297        expected_role: GitWorktreeRole,
3298    ) -> Result<(), Box<dyn Error>> {
3299        require(
3300            matches!(
3301                &structure.selection,
3302                GitRepositorySelection::Worktree { root, role, .. }
3303                    if paths_equal(root, expected_root) && *role == expected_role
3304            ),
3305            "repository selection did not identify the expected exact worktree",
3306        )
3307    }
3308
3309    /// Require all expected exact roots to appear active.
3310    fn require_active_roots<const N: usize>(
3311        structure: &GitRepositoryStructure,
3312        expected: [&Path; N],
3313    ) -> Result<(), Box<dyn Error>> {
3314        for root in expected {
3315            let root = root.canonicalize()?;
3316            let _ = active_entry_for_root(structure, &root)?;
3317        }
3318        Ok(())
3319    }
3320
3321    /// Return one active entry for an exact root.
3322    fn active_entry_for_root<'a>(
3323        structure: &'a GitRepositoryStructure,
3324        expected_root: &Path,
3325    ) -> Result<&'a GitWorktreeEntry, Box<dyn Error>> {
3326        structure
3327            .worktrees
3328            .iter()
3329            .find(|entry| {
3330                matches!(
3331                    &entry.state,
3332                    GitWorktreeState::Active { root, .. } if paths_equal(root, expected_root)
3333                )
3334            })
3335            .ok_or_else(|| {
3336                io::Error::other(format!(
3337                    "missing active structural worktree {}",
3338                    expected_root.display()
3339                ))
3340                .into()
3341            })
3342    }
3343
3344    /// Require one selected invalid-Git problem classification.
3345    fn require_invalid_kind(
3346        structure: RepositoryStructure,
3347        matches_kind: impl FnOnce(&GitStructureIssueKind) -> bool,
3348        message: &str,
3349    ) -> Result<(), Box<dyn Error>> {
3350        let matches = match structure {
3351            RepositoryStructure::InvalidGit { issue, .. } => matches_kind(&issue.kind),
3352            RepositoryStructure::NonGit { .. } | RepositoryStructure::Git(_) => false,
3353        };
3354        require(matches, message)
3355    }
3356
3357    /// Require one test condition without panicking from a fallible test.
3358    fn require(condition: bool, message: &str) -> Result<(), Box<dyn Error>> {
3359        if condition {
3360            Ok(())
3361        } else {
3362            Err(io::Error::other(message).into())
3363        }
3364    }
3365}