diff --git a/src/Microsoft.Build.Tasks.Git.UnitTests/GitRepositoryTests.cs b/src/Microsoft.Build.Tasks.Git.UnitTests/GitRepositoryTests.cs index 442a8dcc..d8c25e4c 100644 --- a/src/Microsoft.Build.Tasks.Git.UnitTests/GitRepositoryTests.cs +++ b/src/Microsoft.Build.Tasks.Git.UnitTests/GitRepositoryTests.cs @@ -295,6 +295,7 @@ public void Submodules_Errors() { "S10: 'sub10' 'http://github.com'", "S11: 'sub11' 'http://github.com'", + "S6: 'sub6' 'http://github.com'", "S9: 'sub9' 'http://github.com'" }, submodules.Select(s => $"{s.Name}: '{s.WorkingDirectoryRelativePath}' '{s.Url}'")); @@ -311,8 +312,6 @@ public void Submodules_Errors() TestUtilities.GetExceptionMessage(() => File.ReadAllText(Path.Combine(workingDir.Path, "sub4", ".git"))), // Could not find a part of the path 'sub5\.git'. TestUtilities.GetExceptionMessage(() => File.ReadAllText(Path.Combine(workingDir.Path, "sub5", ".git"))), - // Access to the path 'sub6\.git' is denied - TestUtilities.GetExceptionMessage(() => File.ReadAllText(Path.Combine(workingDir.Path, "sub6", ".git"))), // The format of the file 'sub7\.git' is invalid. string.Format(Resources.FormatOfFileIsInvalid, Path.Combine(workingDir.Path, "sub7", ".git")), // Path specified in file 'sub8\.git' is invalid. @@ -356,5 +355,25 @@ public void GetSubmoduleHeadCommitSha() var repository = new GitRepository(GitEnvironment.Empty, GitConfig.Empty, gitDir.Path, gitDir.Path, workingDir.Path); Assert.Equal("0000000000000000000000000000000000000000", repository.ReadSubmoduleHeadCommitSha(submoduleWorkingDir.Path)); } + + [Fact] + public void GetOldStyleSubmoduleHeadCommitSha() + { + using var temp = new TempRoot(); + + var gitDir = temp.CreateDirectory(); + var workingDir = temp.CreateDirectory(); + + // this is a unusual but legal case which can occur with older versions of Git or other tools. + // see https://git-scm.com/docs/gitsubmodules#_forms for more details. + var oldStyleSubmoduleWorkingDir = workingDir.CreateDirectory("old-style-submodule"); + var oldStyleSubmoduleGitDir = oldStyleSubmoduleWorkingDir.CreateDirectory(".git"); + var oldStyleSubmoduleRefsHeadDir = oldStyleSubmoduleGitDir.CreateDirectory("refs").CreateDirectory("heads"); + oldStyleSubmoduleRefsHeadDir.CreateFile("branch1").WriteAllText("1111111111111111111111111111111111111111"); + oldStyleSubmoduleGitDir.CreateFile("HEAD").WriteAllText("ref: refs/heads/branch1"); + + var repository = new GitRepository(GitEnvironment.Empty, GitConfig.Empty, gitDir.Path, gitDir.Path, workingDir.Path); + Assert.Equal("1111111111111111111111111111111111111111", repository.ReadSubmoduleHeadCommitSha(oldStyleSubmoduleWorkingDir.Path)); + } } } diff --git a/src/Microsoft.Build.Tasks.Git/GitDataReader/GitRepository.cs b/src/Microsoft.Build.Tasks.Git/GitDataReader/GitRepository.cs index 9f1648f8..532c4feb 100644 --- a/src/Microsoft.Build.Tasks.Git/GitDataReader/GitRepository.cs +++ b/src/Microsoft.Build.Tasks.Git/GitDataReader/GitRepository.cs @@ -209,7 +209,13 @@ public static GitRepository OpenRepository(GitRepositoryLocation location, GitEn /// Null if the HEAD tip reference can't be resolved. internal string? ReadSubmoduleHeadCommitSha(string submoduleWorkingDirectoryFullPath) { - var gitDirectory = ReadDotGitFile(Path.Combine(submoduleWorkingDirectoryFullPath, GitDirName)); + // Submodules don't usually have their own .git directories but this is still legal. + // This can occur with older versions of Git or other tools, or when a user clones one + // repo into another's source tree (but it was not yet registered as a submodule). + // See https://git-scm.com/docs/gitsubmodules#_forms for more details. + var dotGitPath = Path.Combine(submoduleWorkingDirectoryFullPath, GitDirName); + + var gitDirectory = Directory.Exists(dotGitPath) ? dotGitPath : ReadDotGitFile(dotGitPath); if (!IsGitDirectory(gitDirectory, out var commonDirectory)) { return null;