Skip to content
Open
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
32 changes: 32 additions & 0 deletions src/Core.Tests/GitConfigurationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -656,5 +656,37 @@ public void GitConfiguration_TypedQuery_CanonicalizesValues()
// Value should be canonicalized path, not raw "~/example"
Assert.NotEqual("~/example", value);
}

[Fact]
public void GitConfiguration_ScopedConfig_ProcessesIncludes()
{
const string configKey = "test.value";
const string configTrigger = "remote.*.url:http://localhost/**";
const string configValue = "foo123";
string repoPath = CreateRepository(out string workDirPath);

ExecGit(repoPath, workDirPath, $"config --file {workDirPath}/git.config.glob {configKey} {configValue}").AssertSuccess();
ExecGit(repoPath, workDirPath, $"config --global includeif.hasconfig:{configTrigger}.path {workDirPath}/git.config.glob").AssertSuccess();

ExecGit(repoPath, workDirPath, $"config --file {workDirPath}/git.config.loc {configKey} {configValue}").AssertSuccess();
ExecGit(repoPath, workDirPath, $"config --local include.path {workDirPath}/git.config.loc").AssertSuccess();

ExecGit(repoPath, workDirPath, $"config --local remote.test.url http://localhost/my/repo").AssertSuccess();

string gitPath = GetGitPath();
var trace = new NullTrace();
var trace2 = new NullTrace2();
var processManager = new TestProcessManager();

var git = new GitProcess(trace, trace2, processManager, gitPath, repoPath);
IGitConfiguration config = git.GetConfiguration();

// value of included file set in scopes
foreach (var scope in new GitConfigurationLevel[] { GitConfigurationLevel.Global, GitConfigurationLevel.Local })
{
Assert.True(config.TryGet(scope, GitConfigurationType.Raw, configKey, out string value));
Assert.Equal(configValue, value);
}
}
}
}
52 changes: 38 additions & 14 deletions src/Core/GitConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -326,11 +326,13 @@ public class GitProcessConfiguration : IGitConfiguration
private static readonly GitVersion ConfigListTypeMinVersion = new GitVersion(2, 54, 0);
private static readonly GitVersion ConfigListTypeMinVfsBase = new GitVersion(2, 53, 0);
private static readonly GitVersion ConfigListTypeMinVfsSuffix = new GitVersion(0, 1);
private static readonly GitVersion ConfigListRawMinVersion = new GitVersion(2, 26, 0);

private readonly ITrace _trace;
private readonly GitProcess _git;
private readonly Dictionary<GitConfigurationType, ConfigCache> _cache;
private readonly bool _useCache;
private readonly bool _useConfigTypeCache;
private readonly bool _useConfigRawCache;

internal GitProcessConfiguration(ITrace trace, GitProcess git) : this(trace, git, useCache: true)
{
Expand All @@ -349,11 +351,23 @@ internal GitProcessConfiguration(ITrace trace, GitProcess git, bool useCache)
if (useCache && !SupportsConfigListType(git))
{
trace.WriteLine($"Git version {git.Version.OriginalString} does not support 'git config list --type'; config cache disabled");
useCache = false;
_useConfigTypeCache = false;
}
else
{
_useConfigTypeCache = useCache;
}

_useCache = useCache;
_cache = useCache ? new Dictionary<GitConfigurationType, ConfigCache>() : null;
if (useCache && !SupportsConfigListRaw(git))
{
trace.WriteLine($"Git version {git.Version.OriginalString} does not support 'git config list --type'; config cache disabled");
_useConfigRawCache = false;
}
else
{
_useConfigRawCache = useCache;
}
_cache = _useConfigTypeCache || _useConfigRawCache ? new Dictionary<GitConfigurationType, ConfigCache>() : null;
}

private static bool SupportsConfigListType(GitProcess git)
Expand Down Expand Up @@ -381,10 +395,20 @@ private static bool SupportsConfigListType(GitProcess git)
return false;
}

private static bool SupportsConfigListRaw(GitProcess git)
{
return git.Version >= ConfigListRawMinVersion;
}

private bool UseConfigCache(GitConfigurationType type)
{
return type != GitConfigurationType.Raw ? _useConfigTypeCache : _useConfigRawCache;
}

private void EnsureCacheLoaded(GitConfigurationType type)
{
ConfigCache cache;
if (!_useCache || (_cache.TryGetValue(type, out cache) && cache.IsLoaded))
if (!UseConfigCache(type) || (_cache.TryGetValue(type, out cache) && cache.IsLoaded))
{
return;
}
Expand All @@ -400,7 +424,7 @@ private void EnsureCacheLoaded(GitConfigurationType type)
switch (type)
{
case GitConfigurationType.Raw:
typeArg = "--no-type";
typeArg = "";
break;

case GitConfigurationType.Path:
Expand Down Expand Up @@ -437,7 +461,7 @@ private void EnsureCacheLoaded(GitConfigurationType type)

private void InvalidateCache()
{
if (_useCache)
if (_useConfigTypeCache ||_useConfigRawCache)
{
foreach (ConfigCache cache in _cache.Values)
{
Expand All @@ -448,7 +472,7 @@ private void InvalidateCache()

public void Enumerate(GitConfigurationLevel level, GitConfigurationEnumerationCallback cb)
{
if (_useCache)
if (_useConfigRawCache)
{
EnsureCacheLoaded(GitConfigurationType.Raw);

Expand All @@ -463,7 +487,7 @@ public void Enumerate(GitConfigurationLevel level, GitConfigurationEnumerationCa

// Fall back to original implementation
string levelArg = GetLevelFilterArg(level);
using (ChildProcess git = _git.CreateProcess($"config --null {levelArg} --list"))
using (ChildProcess git = _git.CreateProcess($"config --null {levelArg} --includes --list"))
{
git.Start(Trace2ProcessClass.Git);
// To avoid deadlocks, always read the output stream first and then wait
Expand Down Expand Up @@ -530,7 +554,7 @@ public void Enumerate(GitConfigurationLevel level, GitConfigurationEnumerationCa

public bool TryGet(GitConfigurationLevel level, GitConfigurationType type, string name, out string value)
{
if (_useCache)
if (UseConfigCache(type))
{
EnsureCacheLoaded(type);

Expand All @@ -545,7 +569,7 @@ public bool TryGet(GitConfigurationLevel level, GitConfigurationType type, strin
// Fall back to individual git config command if cache not available
string levelArg = GetLevelFilterArg(level);
string typeArg = GetCanonicalizeTypeArg(type);
using (ChildProcess git = _git.CreateProcess($"config --null {levelArg} {typeArg} {QuoteCmdArg(name)}"))
using (ChildProcess git = _git.CreateProcess($"config --null {levelArg} --includes {typeArg} {QuoteCmdArg(name)}"))
{
git.Start(Trace2ProcessClass.Git);
// To avoid deadlocks, always read the output stream first and then wait
Expand Down Expand Up @@ -647,7 +671,7 @@ public void Unset(GitConfigurationLevel level, string name)

public IEnumerable<string> GetAll(GitConfigurationLevel level, GitConfigurationType type, string name)
{
if (_useCache)
if (UseConfigCache(type))
{
EnsureCacheLoaded(type);

Expand All @@ -667,7 +691,7 @@ public IEnumerable<string> GetAll(GitConfigurationLevel level, GitConfigurationT
string levelArg = GetLevelFilterArg(level);
string typeArg = GetCanonicalizeTypeArg(type);

var gitArgs = $"config --null {levelArg} {typeArg} --get-all {QuoteCmdArg(name)}";
var gitArgs = $"config --null {levelArg} --includes {typeArg} --get-all {QuoteCmdArg(name)}";

using (ChildProcess git = _git.CreateProcess(gitArgs))
{
Expand Down Expand Up @@ -705,7 +729,7 @@ public IEnumerable<string> GetRegex(GitConfigurationLevel level, GitConfiguratio
string levelArg = GetLevelFilterArg(level);
string typeArg = GetCanonicalizeTypeArg(type);

var gitArgs = $"config --null {levelArg} {typeArg} --get-regex {QuoteCmdArg(nameRegex)}";
var gitArgs = $"config --null {levelArg} --includes {typeArg} --get-regex {QuoteCmdArg(nameRegex)}";
if (valueRegex != null)
{
gitArgs += $" {QuoteCmdArg(valueRegex)}";
Expand Down
Loading