Skip to content

Commit ae22b20

Browse files
committed
fix: recover when there is unreleased lock file
If a previous git invocation crashes, it is possible that an orphaned lock file (e.g. shallow.lock) is left on the filesystem. This previously caused git-sync to crash loop because the lock file is never deleted. This change adds a check in sanityCheckRepo for the existence of a git lock file. If the git lock file exists at this stage, then initRepo will re-initialize the repository.
1 parent 9782ee2 commit ae22b20

File tree

2 files changed

+64
-0
lines changed

2 files changed

+64
-0
lines changed

main.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,6 +1227,20 @@ func (git *repoSync) removeStaleWorktrees() (int, error) {
12271227
return count, nil
12281228
}
12291229

1230+
func hasGitLockFile(gitRoot absPath) (string, error) {
1231+
gitLockFiles := []string{"shallow.lock"}
1232+
for _, lockFile := range gitLockFiles {
1233+
lockFilePath := gitRoot.Join(".git", lockFile).String()
1234+
_, err := os.Stat(lockFilePath)
1235+
if err == nil {
1236+
return lockFilePath, nil
1237+
} else if !errors.Is(err, os.ErrNotExist) {
1238+
return lockFilePath, err
1239+
}
1240+
}
1241+
return "", nil
1242+
}
1243+
12301244
// sanityCheckRepo tries to make sure that the repo dir is a valid git repository.
12311245
func (git *repoSync) sanityCheckRepo(ctx context.Context) bool {
12321246
git.log.V(3).Info("sanity-checking git repo", "repo", git.root)
@@ -1258,6 +1272,16 @@ func (git *repoSync) sanityCheckRepo(ctx context.Context) bool {
12581272
return false
12591273
}
12601274

1275+
// Check if the repository contains an unreleased lock file. This can happen if
1276+
// a previous git invocation crashed.
1277+
if lockFile, err := hasGitLockFile(git.root); err != nil {
1278+
git.log.Error(err, "error calling stat on file", "path", lockFile)
1279+
return false
1280+
} else if len(lockFile) > 0 {
1281+
git.log.Error(nil, "repo contains lock file", "path", lockFile)
1282+
return false
1283+
}
1284+
12611285
return true
12621286
}
12631287

main_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,3 +424,43 @@ func TestTouch(t *testing.T) {
424424
t.Errorf("touch(newfile) mtime %v is not after %v", newfileInfo.ModTime(), stamp)
425425
}
426426
}
427+
428+
func TestHasGitLockFile(t *testing.T) {
429+
testCases := map[string]struct {
430+
inputFilePath []string
431+
expectLockFile bool
432+
}{
433+
"missing .git directory": {
434+
expectLockFile: false,
435+
},
436+
"has git directory but no lock files": {
437+
inputFilePath: []string{".git", "HEAD"},
438+
expectLockFile: false,
439+
},
440+
"shallow.lock file": {
441+
inputFilePath: []string{".git", "shallow.lock"},
442+
expectLockFile: true,
443+
},
444+
}
445+
446+
for name, tc := range testCases {
447+
t.Run(name, func(t *testing.T) {
448+
root := absPath(t.TempDir())
449+
450+
if len(tc.inputFilePath) > 0 {
451+
if err := touch(root.Join(tc.inputFilePath...)); err != nil {
452+
t.Fatal(err)
453+
}
454+
}
455+
456+
lockFile, err := hasGitLockFile(root)
457+
if err != nil {
458+
t.Fatal(err)
459+
}
460+
hasLock := len(lockFile) > 0
461+
if hasLock != tc.expectLockFile {
462+
t.Fatalf("expected hasGitLockFile to return %v, but got %v", tc.expectLockFile, hasLock)
463+
}
464+
})
465+
}
466+
}

0 commit comments

Comments
 (0)