Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions GitBranchStateCache.Tests/Git/GitRunnerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,34 @@ public void BuildEnvironment_SetsTheFlagsThatKeepARunPredictable()
Assert.IsFalse(environment.ContainsKey("PATH"));
}

[TestMethod]
public void BuildEnvironment_WithAWorkingDirectory_StopsDiscoveryAtIt()
{
string mirror = Path.Combine(TempRoot, "github", "studio", "game");

Dictionary<string, string?> environment = GitRunner.BuildEnvironment(
new GitInvocation { WorkingDirectory = mirror, Arguments = ["for-each-ref"], Timeout = TimeSpan.FromSeconds(1) },
new GitBranchStateCacheOptions { MirrorRoot = TempRoot },
new Hashtable { ["GIT_CEILING_DIRECTORIES"] = "/somewhere/inherited" });

// git still finds the repository the directory itself holds, but a directory that is no longer
// one fails instead of being answered from an enclosing repository.
Assert.AreEqual(Path.GetDirectoryName(Path.GetFullPath(mirror)), environment["GIT_CEILING_DIRECTORIES"]);
}

[TestMethod]
public void BuildEnvironment_WithoutAWorkingDirectory_SetsNoCeiling()
{
Dictionary<string, string?> environment = GitRunner.BuildEnvironment(
new GitInvocation { Arguments = ["--version"], Timeout = TimeSpan.FromSeconds(1) },
new GitBranchStateCacheOptions { MirrorRoot = TempRoot },
new Hashtable { ["GIT_CEILING_DIRECTORIES"] = "/somewhere/inherited" });

// Dropped like every other inherited GIT_* variable, and nothing is put in its place.
Assert.IsTrue(environment.TryGetValue("GIT_CEILING_DIRECTORIES", out string? ceiling));
Assert.IsNull(ceiling);
}

[TestMethod]
public void BuildEnvironment_WithACredential_ScopesItToTheUpstream()
{
Expand Down
15 changes: 15 additions & 0 deletions GitBranchStateCache.Tests/Git/RealRepositoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,21 @@
Assert.IsTrue(branches!.Any(branch => branch.Name == "origin/feature/ui" && branch.Tip == _featureTip));
}

[TestMethod]
public async Task RefResolver_ABrokenMirrorInsideAnotherRepository_ReadsNothing()
{
// A mirror directory left holding only .last-used (the reaper racing MarkUsed), under a mirror
// root that sits inside a checkout. Git must not walk up and answer from the checkout.
string broken = Path.Combine(_workTree, "mirrors", "github", "studio", "other", "mirror.git");
Directory.CreateDirectory(broken);
await File.WriteAllTextAsync(Path.Combine(broken, ".last-used"), string.Empty);

Check warning on line 119 in GitBranchStateCache.Tests/Git/RealRepositoryTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_GitBranchStateCache&issues=AaGKPB3RNS0nP5sb5fCL&open=AaGKPB3RNS0nP5sb5fCL&pullRequest=108

IReadOnlyList<BranchRef>? branches = await new RefResolver(Runner, Options.Create(Settings))
.ListAsync(broken, CancellationToken.None);

Assert.IsNull(branches, $"Read {branches?.Count} branches from the enclosing repository.");
}

[TestMethod]
public async Task MirrorQueries_FindsTheForkPointAsTheMergeBase()
{
Expand Down
12 changes: 12 additions & 0 deletions GitBranchStateCache/Git/GitRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,18 @@ public async Task<GitResult> RunAsync(GitInvocation invocation, CancellationToke
// unplanned fetch in production.
environment["GIT_NO_LAZY_FETCH"] = "1";

// Discovery stops at the directory the run was given. Without a ceiling, a mirror directory that
// is no longer a repository (a partial delete, the reaper racing MarkUsed) does not fail: git
// walks up to the first enclosing repository and answers from that, which serves one
// repository's branches to a caller admitted only for another (ktsu-dev/GitBranchStateCache#104).
// A ceiling rather than GIT_DIR, because a clone and its staging directory run before they are
// repositories, and the ceiling still lets git find the repository the directory itself holds.
if (invocation.WorkingDirectory is not null
&& Path.GetDirectoryName(Path.GetFullPath(invocation.WorkingDirectory)) is { Length: > 0 } ceiling)
{
environment["GIT_CEILING_DIRECTORIES"] = ceiling;
}

ApplyConfigEnvironment(environment, invocation);
return environment;
}
Expand Down
Loading