CB-576: hasUncommitted tolerates an already-gone worktree
This commit is contained in:
@@ -169,6 +169,14 @@ public final class GitWorktrees implements Worktrees {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasUncommitted(String worktreePath) {
|
public boolean hasUncommitted(String worktreePath) {
|
||||||
|
// A worktree that is already gone holds no work to lose, and it must not break teardown:
|
||||||
|
// git -C <missing-dir> status exits non-zero and would throw where release() is mid-way
|
||||||
|
// through stopping a pane. Mirror remove()'s already-gone tolerance by treating it as clean.
|
||||||
|
Path p = Path.of(worktreePath);
|
||||||
|
if (!Files.exists(p)) {
|
||||||
|
log.debug("worktree {} already gone — nothing can be uncommitted", worktreePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// No --untracked-files=no: the exact shape of the work lost in CB-576 was a new file
|
// No --untracked-files=no: the exact shape of the work lost in CB-576 was a new file
|
||||||
// that was never added, so an untracked-only worktree is still dirty.
|
// that was never added, so an untracked-only worktree is still dirty.
|
||||||
String out = exec("git", "-C", worktreePath, "status", "--porcelain");
|
String out = exec("git", "-C", worktreePath, "status", "--porcelain");
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ public interface Worktrees {
|
|||||||
* modifications, staged files, or untracked files. {@code git status --porcelain} is the
|
* modifications, staged files, or untracked files. {@code git status --porcelain} is the
|
||||||
* test; an empty result means clean. Callers use this to decide whether removing the
|
* test; an empty result means clean. Callers use this to decide whether removing the
|
||||||
* worktree would silently destroy a worker's only copy of its work.
|
* worktree would silently destroy a worker's only copy of its work.
|
||||||
|
*
|
||||||
|
* <p>An already-gone worktree is reported as clean (no throw), matching {@link #remove}'s
|
||||||
|
* idempotent contract: a path that does not exist holds no work to lose, and must not break
|
||||||
|
* a teardown that is mid-way through stopping the pane.
|
||||||
*/
|
*/
|
||||||
boolean hasUncommitted(String worktreePath);
|
boolean hasUncommitted(String worktreePath);
|
||||||
|
|
||||||
|
|||||||
@@ -200,6 +200,21 @@ class GitWorktreesTest {
|
|||||||
"a tracked modification must also count as dirty");
|
"a tracked modification must also count as dirty");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CB-576 review. {@code hasUncommitted} must tolerate a missing worktree exactly like
|
||||||
|
* {@code remove}: an already-gone directory holds no work to lose, and throwing here would
|
||||||
|
* break teardown — SessionManager.release() calls it before stopping the pane, so an
|
||||||
|
* exception would orphan a live pane and skip the release notification (CB-516).
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void hasUncommittedOnAMissingWorktreeReturnsFalseWithoutThrowing(@TempDir Path tmp) {
|
||||||
|
GitWorktrees gitWorktrees = new GitWorktrees(tmp.resolve("wts").toString());
|
||||||
|
String gone = tmp.resolve("wts").resolve("does-not-exist").toString();
|
||||||
|
|
||||||
|
assertFalse(gitWorktrees.hasUncommitted(gone),
|
||||||
|
"a missing worktree is reported clean, not an error");
|
||||||
|
}
|
||||||
|
|
||||||
/** All three protected configs are covered: each one present in a worktree is neutralized and hidden. */
|
/** All three protected configs are covered: each one present in a worktree is neutralized and hidden. */
|
||||||
@Test
|
@Test
|
||||||
void allThreeConfigsAreNeutralizedWhenPresent(@TempDir Path tmp) throws Exception {
|
void allThreeConfigsAreNeutralizedWhenPresent(@TempDir Path tmp) throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user