Skip to content

Commit 8897550

Browse files
committed
fix(storage): bound legacy worktree probing
1 parent f6c57ed commit 8897550

1 file changed

Lines changed: 103 additions & 15 deletions

File tree

src/storage.rs

Lines changed: 103 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -448,25 +448,25 @@ pub(crate) fn matching_legacy_profile_layouts(
448448
profile_root: &Path,
449449
excluded_project_id: Option<&str>,
450450
) -> Result<(Vec<StoreLayout>, bool)> {
451-
matching_legacy_profile_layouts_with_git_resolver(
451+
matching_legacy_profile_layouts_with_git_identity_resolver(
452452
project_root,
453453
profile_root,
454454
excluded_project_id,
455455
crate::worktree::is_detached_linked_worktree,
456-
crate::worktree::git_common_dir,
456+
crate::worktree::git_repo_identity_outcome,
457457
)
458458
}
459459

460-
fn matching_legacy_profile_layouts_with_git_resolver<D, G>(
460+
fn matching_legacy_profile_layouts_with_git_identity_resolver<D, G>(
461461
project_root: &Path,
462462
profile_root: &Path,
463463
excluded_project_id: Option<&str>,
464464
mut is_detached_linked_worktree: D,
465-
mut git_common_dir: G,
465+
mut git_identity: G,
466466
) -> Result<(Vec<StoreLayout>, bool)>
467467
where
468468
D: FnMut(&Path) -> bool,
469-
G: FnMut(&Path) -> Option<PathBuf>,
469+
G: FnMut(&Path) -> crate::worktree::GitRepoIdentityOutcome,
470470
{
471471
let projects_root = profile_root.join("projects");
472472
let Ok(entries) = fs::read_dir(&projects_root) else {
@@ -507,7 +507,13 @@ where
507507
selected_manifest_matches_exact_root && exact_manifests.is_empty();
508508
let matching_manifests = if exact_manifests.is_empty() {
509509
let project_git_common_dir = (!is_detached_linked_worktree(project_root))
510-
.then(|| git_common_dir(project_root))
510+
.then(|| match git_identity(project_root) {
511+
crate::worktree::GitRepoIdentityOutcome::Resolved(identity) => {
512+
Some(identity.common_dir)
513+
}
514+
crate::worktree::GitRepoIdentityOutcome::Unknown
515+
| crate::worktree::GitRepoIdentityOutcome::NotFound => None,
516+
})
511517
.flatten();
512518
let mut legacy_git_common_dirs = HashMap::<PathBuf, Option<PathBuf>>::new();
513519
non_exact_manifests
@@ -520,7 +526,13 @@ where
520526
manifest
521527
.project_root
522528
.is_dir()
523-
.then(|| git_common_dir(&manifest.project_root))
529+
.then(|| match git_identity(&manifest.project_root) {
530+
crate::worktree::GitRepoIdentityOutcome::Resolved(identity) => {
531+
Some(identity.common_dir)
532+
}
533+
crate::worktree::GitRepoIdentityOutcome::Unknown
534+
| crate::worktree::GitRepoIdentityOutcome::NotFound => None,
535+
})
524536
.flatten()
525537
})
526538
.as_deref()
@@ -1314,6 +1326,67 @@ mod tests {
13141326
use std::cell::RefCell;
13151327
use std::sync::{Arc, Barrier};
13161328

1329+
#[test]
1330+
fn legacy_shared_git_probe_skips_timeout_and_unresolvable_roots() {
1331+
let dir = tempfile::tempdir().unwrap();
1332+
let project_root = dir.path().join("repo");
1333+
let legacy_root = dir.path().join("legacy");
1334+
let profile_root = dir.path().join("profile");
1335+
let data_root = profile_root.join("projects").join("proj_legacy");
1336+
for root in [&project_root, &legacy_root, &data_root] {
1337+
fs::create_dir_all(root).unwrap();
1338+
}
1339+
write_store_manifest_to_path(
1340+
&data_root.join(STORE_MANIFEST_FILENAME),
1341+
&StoreManifest {
1342+
schema_version: STORE_MANIFEST_SCHEMA_VERSION,
1343+
project_id: Some("proj_legacy".to_string()),
1344+
store_kind: StoreKind::CodeProject,
1345+
storage_mode: StorageMode::ProfileSharded,
1346+
project_root: legacy_root.clone(),
1347+
data_root,
1348+
graph_db_relpath: "tracedecay.db".into(),
1349+
sessions_db_relpath: "sessions.db".into(),
1350+
branch_meta_relpath: "branch-meta.json".into(),
1351+
},
1352+
)
1353+
.unwrap();
1354+
1355+
let (layouts, _) = matching_legacy_profile_layouts_with_git_identity_resolver(
1356+
&project_root,
1357+
&profile_root,
1358+
None,
1359+
|_| false,
1360+
|_| crate::worktree::GitRepoIdentityOutcome::Unknown,
1361+
)
1362+
.unwrap();
1363+
assert!(layouts.is_empty(), "a timed-out current root cannot match");
1364+
1365+
let (layouts, _) = matching_legacy_profile_layouts_with_git_identity_resolver(
1366+
&project_root,
1367+
&profile_root,
1368+
None,
1369+
|_| false,
1370+
|root| {
1371+
if root == project_root {
1372+
crate::worktree::GitRepoIdentityOutcome::Resolved(
1373+
crate::worktree::GitRepoIdentity {
1374+
worktree_root: project_root.clone(),
1375+
common_dir: dir.path().join("shared.git"),
1376+
},
1377+
)
1378+
} else {
1379+
crate::worktree::GitRepoIdentityOutcome::NotFound
1380+
}
1381+
},
1382+
)
1383+
.unwrap();
1384+
assert!(
1385+
layouts.is_empty(),
1386+
"an unresolvable legacy root cannot be selected"
1387+
);
1388+
}
1389+
13171390
#[test]
13181391
fn exact_root_manifest_overrides_shared_git_discovery() {
13191392
fn write_manifest(profile_root: &Path, project_id: &str, project_root: &Path) {
@@ -1347,14 +1420,19 @@ mod tests {
13471420

13481421
let resolver_calls = RefCell::new(Vec::new());
13491422
let (layouts, selected_is_sole_exact_root) =
1350-
matching_legacy_profile_layouts_with_git_resolver(
1423+
matching_legacy_profile_layouts_with_git_identity_resolver(
13511424
&project_root,
13521425
&profile_root,
13531426
None,
13541427
|_| false,
13551428
|root| {
13561429
resolver_calls.borrow_mut().push(root.to_path_buf());
1357-
Some(dir.path().join("shared.git"))
1430+
crate::worktree::GitRepoIdentityOutcome::Resolved(
1431+
crate::worktree::GitRepoIdentity {
1432+
worktree_root: root.to_path_buf(),
1433+
common_dir: dir.path().join("shared.git"),
1434+
},
1435+
)
13581436
},
13591437
)
13601438
.unwrap();
@@ -1371,14 +1449,19 @@ mod tests {
13711449

13721450
resolver_calls.borrow_mut().clear();
13731451
let (layouts, selected_is_sole_exact_root) =
1374-
matching_legacy_profile_layouts_with_git_resolver(
1452+
matching_legacy_profile_layouts_with_git_identity_resolver(
13751453
&project_root,
13761454
&profile_root,
13771455
Some("proj_exact"),
13781456
|_| false,
13791457
|root| {
13801458
resolver_calls.borrow_mut().push(root.to_path_buf());
1381-
Some(dir.path().join("shared.git"))
1459+
crate::worktree::GitRepoIdentityOutcome::Resolved(
1460+
crate::worktree::GitRepoIdentity {
1461+
worktree_root: root.to_path_buf(),
1462+
common_dir: dir.path().join("shared.git"),
1463+
},
1464+
)
13821465
},
13831466
)
13841467
.unwrap();
@@ -1422,12 +1505,12 @@ mod tests {
14221505
)
14231506
.unwrap();
14241507

1425-
let error = matching_legacy_profile_layouts_with_git_resolver(
1508+
let error = matching_legacy_profile_layouts_with_git_identity_resolver(
14261509
&project_root,
14271510
&profile_root,
14281511
None,
14291512
|_| false,
1430-
|_| None,
1513+
|_| crate::worktree::GitRepoIdentityOutcome::NotFound,
14311514
)
14321515
.expect_err("missing project_id must fail closed");
14331516
assert!(error.to_string().contains("project_id is missing"));
@@ -1468,14 +1551,19 @@ mod tests {
14681551

14691552
let resolver_calls = RefCell::new(Vec::new());
14701553
let (layouts, selected_is_sole_exact_root) =
1471-
matching_legacy_profile_layouts_with_git_resolver(
1554+
matching_legacy_profile_layouts_with_git_identity_resolver(
14721555
&worktree_root,
14731556
&profile_root,
14741557
Some("proj_selected"),
14751558
|_| false,
14761559
|root| {
14771560
resolver_calls.borrow_mut().push(root.to_path_buf());
1478-
Some(dir.path().join("shared.git"))
1561+
crate::worktree::GitRepoIdentityOutcome::Resolved(
1562+
crate::worktree::GitRepoIdentity {
1563+
worktree_root: root.to_path_buf(),
1564+
common_dir: dir.path().join("shared.git"),
1565+
},
1566+
)
14791567
},
14801568
)
14811569
.unwrap();

0 commit comments

Comments
 (0)