diff --git a/reposerver/repository/repository.go b/reposerver/repository/repository.go index 1abccf6bcd2dd..059cf82227cb9 100644 --- a/reposerver/repository/repository.go +++ b/reposerver/repository/repository.go @@ -165,6 +165,7 @@ func (s *Service) Init() error { fullPath := filepath.Join(s.rootDir, file.Name()) closer := s.gitRepoInitializer(fullPath) if repo, err := gogit.PlainOpen(fullPath); err == nil { + s.cleanupStaleFiles(fullPath, []string{"index.lock", "index", "HEAD.lock"}, repo) if remotes, err := repo.Remotes(); err == nil && len(remotes) > 0 && len(remotes[0].Config().URLs) > 0 { s.gitRepoPaths.Add(git.NormalizeGitURL(remotes[0].Config().URLs[0]), fullPath) } @@ -175,6 +176,39 @@ func (s *Service) Init() error { return os.Chmod(s.rootDir, 0o300) } +func (s *Service) cleanupStaleFiles(fullPath string, staleFiles []string, repo *gogit.Repository) { + // for each stale file, if it exists, remove it + for _, staleFile := range staleFiles { + gitFile := filepath.Join(fullPath, ".git", staleFile) + info, err := os.Lstat(gitFile) + if err == nil { + // Only delete if it's a regular file (not a symlink, dir, etc.) + if info.Mode().IsRegular() { + log.Warnf("Stale file %s present in git repository %s, removing it", staleFile, fullPath) + if err = os.Remove(gitFile); err != nil { + log.Errorf("Failed to remove stale file %s: %v", gitFile, err) + } + if staleFile == "index" { + if err == nil { + wt, _ := repo.Worktree() + headRef, _ := repo.Head() + err = wt.Reset(&gogit.ResetOptions{ + Mode: gogit.MixedReset, + Commit: headRef.Hash(), + }) + } + + if err != nil { + log.Errorf("Failed to reset git repo %s: %v", fullPath, err) + } + } + } + } else { + log.Warnf("Stale file %s in git repository %s is not a regular file, skipping removal", staleFile, fullPath) + } + } +} + // ListRefs List a subset of the refs (currently, branches and tags) of a git repo func (s *Service) ListRefs(_ context.Context, q *apiclient.ListRefsRequest) (*apiclient.Refs, error) { gitClient, err := s.newClient(q.Repo) diff --git a/reposerver/repository/repository_test.go b/reposerver/repository/repository_test.go index e96fc3540a95e..0402414a289bf 100644 --- a/reposerver/repository/repository_test.go +++ b/reposerver/repository/repository_test.go @@ -3014,6 +3014,53 @@ func TestInit(t *testing.T) { _, err := os.ReadDir(dir) require.Error(t, err) initGitRepo(t, newGitRepoOptions{path: path.Join(dir, "repo2"), remote: "https://github.com/argo-cd/test-repo2", createPath: true, addEmptyCommit: false}) + + repoPath = path.Join(dir, "repo3") + indexLockFile := path.Join(repoPath, ".git", "index.lock") + indexFile := path.Join(repoPath, ".git", "index") + headLockFile := path.Join(repoPath, ".git", "HEAD.lock") + initGitRepo(t, newGitRepoOptions{path: repoPath, remote: "https://github.com/argo-cd/test-repo3", createPath: true, addEmptyCommit: true}) + require.NoError(t, os.WriteFile(indexLockFile, []byte("test"), 0o644)) + require.NoError(t, os.WriteFile(headLockFile, []byte("test"), 0o644)) + + service = newService(t, ".") + service.rootDir = dir + + _, err = os.Stat(indexLockFile) + require.NoError(t, err) + _, err = os.Stat(indexFile) + require.NoError(t, err) + _, err = os.Stat(headLockFile) + require.NoError(t, err) + + require.NoError(t, service.Init()) + + _, err = os.Stat(indexLockFile) + require.Error(t, err, "index.lock file should be removed after Init()") + require.ErrorContains(t, err, ".git/index.lock: no such file or directory") + _, err = os.Stat(indexFile) + // index file should stay after Init(), since it was recreated after repo reset + require.NoError(t, err) + _, err = os.Stat(headLockFile) + require.Error(t, err, "HEAD.lock file should be removed after Init()") + require.ErrorContains(t, err, ".git/HEAD.lock: no such file or directory") + + repoPath = path.Join(dir, "repo4") + headLockDir := path.Join(repoPath, ".git", "HEAD.lock") + initGitRepo(t, newGitRepoOptions{path: repoPath, remote: "https://github.com/argo-cd/test-repo4", createPath: true, addEmptyCommit: false}) + require.NoError(t, os.Mkdir(headLockDir, 0o755)) + + service = newService(t, ".") + service.rootDir = dir + + _, err = os.Stat(headLockDir) + require.NoError(t, err) + + require.NoError(t, service.Init()) + + _, err = os.Stat(headLockDir) + // headLockDir should stay after Init(), since it is a directory + require.NoError(t, err) } // TestCheckoutRevisionCanGetNonstandardRefs shows that we can fetch a revision that points to a non-standard ref. In