fix: recover worktree bootstrap from stale index.lock
Retries transient index.lock conflicts during worktree population Removes unchanged stale locks automatically and continues bootstrap Adds coverage for stale lock recovery
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
cherryPick,
|
||||
createWorktree,
|
||||
getStatus,
|
||||
populateWorktreeWithLockRecovery,
|
||||
removeWorktree,
|
||||
resolvePrimaryWorktreeRoot,
|
||||
resolveWorktreeTopLevel,
|
||||
@@ -321,6 +322,28 @@ describe('worktree root resolution', () => {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('createWorktree', () => {
|
||||
it('recovers from an unchanged stale index lock while populating a worktree', async () => {
|
||||
if (!canRunGit()) return;
|
||||
|
||||
const repo = createTempDir();
|
||||
const worktree = createTempDir();
|
||||
runGit(repo, ['init', '-b', 'main']);
|
||||
runGit(repo, ['config', 'user.email', 'test@example.com']);
|
||||
runGit(repo, ['config', 'user.name', 'Test User']);
|
||||
fs.writeFileSync(path.join(repo, 'README.md'), '# Test\n');
|
||||
runGit(repo, ['add', 'README.md']);
|
||||
runGit(repo, ['commit', '-m', 'Initial commit']);
|
||||
fs.rmSync(worktree, { recursive: true, force: true });
|
||||
runGit(repo, ['worktree', 'add', '--no-checkout', '-b', 'feature/stale-lock', worktree, 'HEAD']);
|
||||
|
||||
const lockPath = runGit(worktree, ['rev-parse', '--git-path', 'index.lock']).trim();
|
||||
fs.writeFileSync(lockPath, 'stale');
|
||||
|
||||
await expect(populateWorktreeWithLockRecovery(worktree)).resolves.toBeUndefined();
|
||||
expect(fs.existsSync(lockPath)).toBe(false);
|
||||
expect(fs.readFileSync(path.join(worktree, 'README.md'), 'utf8')).toBe('# Test\n');
|
||||
});
|
||||
|
||||
it('preflights fast create branch-in-use failures before creating the candidate directory', async () => {
|
||||
if (!canRunGit()) return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user