fix: accept case-aliased worktree repository paths (#3837)
This commit is contained in:
@@ -218,7 +218,16 @@ func validateLocalRepositoryWorkspace(ctx context.Context, workspacePath, reposi
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if workspaceCommonDir != repositoryCommonDir {
|
||||
workspaceCommonInfo, err := os.Stat(workspaceCommonDir)
|
||||
if err != nil {
|
||||
return worktree.ErrReuseWorktreeUnavailable
|
||||
}
|
||||
repositoryCommonInfo, err := os.Stat(repositoryCommonDir)
|
||||
if err != nil {
|
||||
return worktree.ErrReuseWorktreeUnavailable
|
||||
}
|
||||
if !workspaceCommonInfo.IsDir() || !repositoryCommonInfo.IsDir() ||
|
||||
!os.SameFile(workspaceCommonInfo, repositoryCommonInfo) {
|
||||
return worktree.ErrReuseWorktreeUnavailable
|
||||
}
|
||||
return nil
|
||||
@@ -243,9 +252,9 @@ func localGitTopLevel(ctx context.Context, path string) (string, error) {
|
||||
}
|
||||
|
||||
// localGitCommonDir returns the canonical Git directory shared by a checkout
|
||||
// and all of its linked worktrees. Comparing this value, instead of the
|
||||
// worktree top-level paths, proves that a linked worktree belongs to the
|
||||
// selected repository while still rejecting an unrelated Git checkout.
|
||||
// and all of its linked worktrees. Its filesystem identity, rather than its
|
||||
// path spelling, proves that a linked worktree belongs to the selected
|
||||
// repository while still rejecting an unrelated Git checkout.
|
||||
func localGitCommonDir(ctx context.Context, path string) (string, error) {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil || !info.IsDir() {
|
||||
|
||||
@@ -78,6 +78,27 @@ func TestLocalPreparer_ReuseRequiredValidatesRepositoryIdentity(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// @covers AC-TASKS-ADDITIONAL-SESSION-WORKSPACE-REUSE-002.1
|
||||
func TestValidateLocalRepositoryWorkspaceAcceptsCaseAliasedCommonDirectory(t *testing.T) {
|
||||
parent := t.TempDir()
|
||||
repositoryPath := filepath.Join(parent, "CaseIdentity")
|
||||
if err := os.Mkdir(repositoryPath, 0o755); err != nil {
|
||||
t.Fatalf("create mixed-case repository directory: %v", err)
|
||||
}
|
||||
initGitRepoAt(t, repositoryPath)
|
||||
caseAlias := filepath.Join(parent, "caseidentity")
|
||||
if _, err := os.Stat(caseAlias); err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
t.Skip("filesystem is case-sensitive")
|
||||
}
|
||||
t.Fatalf("inspect case-aliased repository path: %v", err)
|
||||
}
|
||||
|
||||
if err := validateLocalRepositoryWorkspace(context.Background(), repositoryPath, caseAlias); err != nil {
|
||||
t.Fatalf("validateLocalRepositoryWorkspace() rejected the same repository through a case alias: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocalPreparer_RejectsRepoBackedNonGitWorkspace(t *testing.T) {
|
||||
workspacePath := t.TempDir()
|
||||
preparer := NewLocalPreparer(newTestLocalLogger())
|
||||
@@ -116,6 +137,11 @@ func TestLocalPreparer_AcceptsMatchingRepoBackedGitWorkspace(t *testing.T) {
|
||||
func initGitRepo(t *testing.T) string {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
return initGitRepoAt(t, dir)
|
||||
}
|
||||
|
||||
func initGitRepoAt(t *testing.T, dir string) string {
|
||||
t.Helper()
|
||||
// Start from a clean env: filter all GIT_* vars that may leak from parent
|
||||
// processes (e.g. pre-commit hooks set GIT_DIR, GIT_WORK_TREE, GIT_INDEX_FILE).
|
||||
gitEnv := filterLocalTestGitEnv(os.Environ())
|
||||
|
||||
Reference in New Issue
Block a user