Skip to content

Commit

Permalink
Implement support for repositories cloned from a local repository (#438)
Browse files Browse the repository at this point in the history
* Implement support for repositories cloned from a local repository

If the remote of a local repository is a local path retrieve the actual URL from the repository on this path.
The same with submodule URLs.
  • Loading branch information
tmat authored Oct 7, 2019
1 parent 4d6a932 commit b4f237e
Show file tree
Hide file tree
Showing 18 changed files with 432 additions and 262 deletions.
241 changes: 150 additions & 91 deletions src/Microsoft.Build.Tasks.Git.UnitTests/GitOperationsTests.cs

Large diffs are not rendered by default.

78 changes: 27 additions & 51 deletions src/Microsoft.Build.Tasks.Git.UnitTests/GitRepositoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Microsoft.Build.Tasks.Git.UnitTests
public class GitRepositoryTests
{
[Fact]
public void LocateRepository_Worktree()
public void TryFindRepository_Worktree()
{
using var temp = new TempRoot();

Expand All @@ -30,48 +30,32 @@ public void LocateRepository_Worktree()
worktreeGitDir.CreateFile("gitdir").WriteAllText(worktreeGitFile.Path + " \r\n\t\v");

// start under main repository directory:
Assert.True(GitRepository.LocateRepository(
mainWorkingSubDir.Path,
out var locatedGitDirectory,
out var locatedCommonDirectory,
out var locatedWorkingDirectory));
Assert.True(GitRepository.TryFindRepository(mainWorkingSubDir.Path, out var location));

Assert.Equal(mainGitDir.Path, locatedGitDirectory);
Assert.Equal(mainGitDir.Path, locatedCommonDirectory);
Assert.Equal(mainWorkingDir.Path, locatedWorkingDirectory);
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.LocateRepository(
mainGitDir.Path,
out locatedGitDirectory,
out locatedCommonDirectory,
out locatedWorkingDirectory));
Assert.True(GitRepository.TryFindRepository(mainGitDir.Path, out location));

Assert.Equal(mainGitDir.Path, locatedGitDirectory);
Assert.Equal(mainGitDir.Path, locatedCommonDirectory);
Assert.Null(locatedWorkingDirectory);
Assert.Equal(mainGitDir.Path, location.GitDirectory);
Assert.Equal(mainGitDir.Path, location.CommonDirectory);
Assert.Null(location.WorkingDirectory);

// start under worktree directory:
Assert.True(GitRepository.LocateRepository(
worktreeSubDir.Path,
out locatedGitDirectory,
out locatedCommonDirectory,
out locatedWorkingDirectory));
Assert.True(GitRepository.TryFindRepository(worktreeSubDir.Path, out location));

Assert.Equal(worktreeGitDir.Path, locatedGitDirectory);
Assert.Equal(mainGitDir.Path, locatedCommonDirectory);
Assert.Equal(worktreeDir.Path, locatedWorkingDirectory);
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.LocateRepository(
worktreeGitSubDir.Path,
out locatedGitDirectory,
out locatedCommonDirectory,
out locatedWorkingDirectory));

Assert.Equal(worktreeGitDir.Path, locatedGitDirectory);
Assert.Equal(mainGitDir.Path, locatedCommonDirectory);
Assert.Null(locatedWorkingDirectory);
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]
Expand All @@ -93,26 +77,18 @@ public void LocateRepository_Submodule()
submoduleGitDir.CreateDirectory("refs");

// start under submodule working directory:
Assert.True(GitRepository.LocateRepository(
submoduleWorkDir.Path,
out var locatedGitDirectory,
out var locatedCommonDirectory,
out var locatedWorkingDirectory));
Assert.True(GitRepository.TryFindRepository(submoduleWorkDir.Path, out var location));

Assert.Equal(submoduleGitDir.Path, locatedGitDirectory);
Assert.Equal(submoduleGitDir.Path, locatedCommonDirectory);
Assert.Equal(submoduleWorkDir.Path, locatedWorkingDirectory);
Assert.Equal(submoduleGitDir.Path, location.GitDirectory);
Assert.Equal(submoduleGitDir.Path, location.CommonDirectory);
Assert.Equal(submoduleWorkDir.Path, location.WorkingDirectory);

// start under submodule git directory:
Assert.True(GitRepository.LocateRepository(
submoduleGitDir.Path,
out locatedGitDirectory,
out locatedCommonDirectory,
out locatedWorkingDirectory));

Assert.Equal(submoduleGitDir.Path, locatedGitDirectory);
Assert.Equal(submoduleGitDir.Path, locatedCommonDirectory);
Assert.Null(locatedWorkingDirectory);
Assert.True(GitRepository.TryFindRepository(submoduleGitDir.Path, out location));

Assert.Equal(submoduleGitDir.Path, location.GitDirectory);
Assert.Equal(submoduleGitDir.Path, location.CommonDirectory);
Assert.Null(location.WorkingDirectory);
}

[Fact]
Expand Down
119 changes: 68 additions & 51 deletions src/Microsoft.Build.Tasks.Git/GitDataReader/GitRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,27 +85,7 @@ internal GitRepository(
_lazySubmodules = new Lazy<(ImmutableArray<GitSubmodule>, ImmutableArray<string>)>(() => (submodules, submoduleDiagnostics));
_lazyIgnore = new Lazy<GitIgnore>(() => ignore);
_lazyHeadCommitSha = new Lazy<string>(() => headCommitSha);
}

/// <summary>
/// Finds a git repository containing the specified path, if any.
/// </summary>
/// <exception cref="IOException" />
/// <exception cref="InvalidDataException" />
/// <exception cref="NotSupportedException">The repository found requires higher version of git repository format that is currently supported.</exception>
/// <returns>False if no git repository can be found that contains the specified path.</returns>
public static bool TryFindRepository(string path, out GitRepositoryLocation location)
{
if (!LocateRepository(path, out var gitDirectory, out var commonDirectory, out var defaultWorkingDirectory))
{
// unable to find repository
location = default;
return false;
}

location = new GitRepositoryLocation(gitDirectory, commonDirectory, defaultWorkingDirectory);
return true;
}
}

/// <summary>
/// Opens a repository at the specified location.
Expand Down Expand Up @@ -237,7 +217,7 @@ internal string ReadSubmoduleHeadCommitSha(string submoduleWorkingDirectoryFullP

var resolver = new GitReferenceResolver(gitDirectory, commonDirectory);
return resolver.ResolveHeadReference();
}
}

private string GetWorkingDirectory()
=> WorkingDirectory ?? throw new InvalidOperationException(Resources.RepositoryDoesNotHaveWorkingDirectory);
Expand Down Expand Up @@ -368,63 +348,100 @@ private GitIgnore LoadIgnore()
return new GitIgnore(root, workingDirectory, ignoreCase);
}

/// <summary>
/// Returns <see cref="GitRepositoryLocation"/> if the specified <paramref name="repositoryDirectory"/> is
/// a valid repository directory.
/// </summary>
/// <exception cref="IOException" />
/// <exception cref="InvalidDataException" />
internal static bool LocateRepository(string directory, out string gitDirectory, out string commonDirectory, out string workingDirectory)
/// <exception cref="NotSupportedException">The repository found requires higher version of git repository format that is currently supported.</exception>
/// <returns>False if no git repository can be found that contains the specified path.</returns>
public static bool TryGetRepositoryLocation(string directory, out GitRepositoryLocation location)
{
gitDirectory = commonDirectory = workingDirectory = null;
try
{
directory = Path.GetFullPath(directory);
}
catch
{
location = default;
return false;
}

return TryGetRepositoryLocationImpl(directory, out location);
}

/// <summary>
/// Finds a git repository containing the specified path, if any.
/// </summary>
/// <exception cref="IOException" />
/// <exception cref="InvalidDataException" />
/// <exception cref="NotSupportedException">The repository found requires higher version of git repository format that is currently supported.</exception>
/// <returns>False if no git repository can be found that contains the specified path.</returns>
public static bool TryFindRepository(string directory, out GitRepositoryLocation location)
{
try
{
directory = Path.GetFullPath(directory);
}
catch
{
location = default;
return false;
}

while (directory != null)
{
if (TryGetRepositoryLocationImpl(directory, out location))
{
return true;
}

// TODO: https://github.com/dotnet/sourcelink/issues/302
// stop on device boundary
directory = Path.GetDirectoryName(directory);
}

location = default;
return false;
}

var dotGitPath = Path.Combine(directory, GitDirName);
private static bool TryGetRepositoryLocationImpl(string directory, out GitRepositoryLocation location)
{
string commonDirectory;
var dotGitPath = Path.Combine(directory, GitDirName);

if (Directory.Exists(dotGitPath))
if (Directory.Exists(dotGitPath))
{
if (IsGitDirectory(dotGitPath, out commonDirectory))
{
if (IsGitDirectory(dotGitPath, out commonDirectory))
{
gitDirectory = dotGitPath;
workingDirectory = directory;
return true;
}
location = new GitRepositoryLocation(gitDirectory: dotGitPath, commonDirectory, workingDirectory: directory);
return true;
}
else if (File.Exists(dotGitPath))
}
else if (File.Exists(dotGitPath))
{
var link = ReadDotGitFile(dotGitPath);
if (IsGitDirectory(link, out commonDirectory))
{
var link = ReadDotGitFile(dotGitPath);
if (IsGitDirectory(link, out commonDirectory))
{
gitDirectory = link;
workingDirectory = directory;
return true;
}

return false;
location = new GitRepositoryLocation(gitDirectory: link, commonDirectory, workingDirectory: directory);
return true;
}

if (Directory.Exists(directory))
location = default;
return false;
}

if (Directory.Exists(directory))
{
if (IsGitDirectory(directory, out commonDirectory))
{
if (IsGitDirectory(directory, out commonDirectory))
{
gitDirectory = directory;
workingDirectory = null;
return true;
}
location = new GitRepositoryLocation(gitDirectory: directory, commonDirectory, workingDirectory: null);
return true;
}

directory = Path.GetDirectoryName(directory);
}

location = default;
return false;
}

Expand Down
Loading

0 comments on commit b4f237e

Please sign in to comment.