diff --git a/src/Microsoft.Build.Tasks.Git.UnitTests/GitRepositoryTests.cs b/src/Microsoft.Build.Tasks.Git.UnitTests/GitRepositoryTests.cs index 292bfe33..aa8db1b2 100644 --- a/src/Microsoft.Build.Tasks.Git.UnitTests/GitRepositoryTests.cs +++ b/src/Microsoft.Build.Tasks.Git.UnitTests/GitRepositoryTests.cs @@ -58,6 +58,56 @@ public void TryFindRepository_Worktree() Assert.Null(location.WorkingDirectory); } + [Fact] + public void TryFindRepository_Worktree_Realistic() + { + using var temp = new TempRoot(); + + var mainWorkingDir = temp.CreateDirectory(); + var mainWorkingSubDir = mainWorkingDir.CreateDirectory("A"); + var mainGitDir = mainWorkingDir.CreateDirectory(".git"); + mainGitDir.CreateFile("HEAD"); + + var worktreesDir = mainGitDir.CreateDirectory("worktrees"); + var worktreeGitDir = worktreesDir.CreateDirectory("myworktree"); + var worktreeGitSubDir = worktreeGitDir.CreateDirectory("B"); + var worktreeDir = temp.CreateDirectory(); + var worktreeSubDir = worktreeDir.CreateDirectory("C"); + var worktreeGitFile = worktreeDir.CreateFile(".git").WriteAllText("gitdir: " + worktreeGitDir + " \r\n\t\v"); + + worktreeGitDir.CreateFile("HEAD"); + worktreeGitDir.CreateFile("commondir").WriteAllText("../..\n"); + worktreeGitDir.CreateFile("gitdir").WriteAllText(worktreeGitFile.Path + " \r\n\t\v"); + + // start under main repository directory: + Assert.True(GitRepository.TryFindRepository(mainWorkingSubDir.Path, out var location)); + + Assert.Equal(mainGitDir.Path, location.GitDirectory); + Assert.Equal(mainGitDir.Path, location.CommonDirectory); + Assert.Equal(mainWorkingDir.Path, location.WorkingDirectory); + + // start at main git directory (git config works from this dir, but git status requires work dir): + Assert.True(GitRepository.TryFindRepository(mainGitDir.Path, out location)); + + Assert.Equal(mainGitDir.Path, location.GitDirectory); + Assert.Equal(mainGitDir.Path, location.CommonDirectory); + Assert.Null(location.WorkingDirectory); + + // start under worktree directory: + Assert.True(GitRepository.TryFindRepository(worktreeSubDir.Path, out location)); + + Assert.Equal(worktreeGitDir.Path, location.GitDirectory); + Assert.Equal(mainGitDir.Path, location.CommonDirectory); + Assert.Equal(worktreeDir.Path, location.WorkingDirectory); + + // start under worktree git directory (git config works from this dir, but git status requires work dir): + Assert.True(GitRepository.TryFindRepository(worktreeGitSubDir.Path, out location)); + + Assert.Equal(worktreeGitDir.Path, location.GitDirectory); + Assert.Equal(mainGitDir.Path, location.CommonDirectory); + Assert.Null(location.WorkingDirectory); + } + [Fact] public void LocateRepository_Submodule() { diff --git a/src/Microsoft.Build.Tasks.Git/GitDataReader/GitRepository.cs b/src/Microsoft.Build.Tasks.Git/GitDataReader/GitRepository.cs index a06a5bcc..196c57dc 100644 --- a/src/Microsoft.Build.Tasks.Git/GitDataReader/GitRepository.cs +++ b/src/Microsoft.Build.Tasks.Git/GitDataReader/GitRepository.cs @@ -491,6 +491,8 @@ private static bool IsGitDirectory(string directory, out string commonDirectory) try { commonDirectory = Path.Combine(directory, File.ReadAllText(commonLinkPath).TrimEnd(CharUtils.AsciiWhitespace)); + // Normalize relative paths. For example, git worktrees typically have "../.." in this file. + commonDirectory = Path.GetFullPath(commonDirectory); } catch {