Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for adding and clearing multi-valued configuration #1720

Merged
merged 3 commits into from
Oct 9, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

language: csharp
dist: xenial
dotnet: 2.1.506
dotnet: 2.1.802
mono: none
osx_image: xcode8.3

Expand Down
113 changes: 113 additions & 0 deletions LibGit2Sharp.Tests/ConfigurationFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,119 @@ public void CanUnsetAnEntryFromTheGlobalConfiguration()
}
}

[Fact]
public void CanAddAndReadMultivarFromTheLocalConfiguration()
{
string path = SandboxStandardTestRepo();
using (var repo = new Repository(path))
{
Assert.Empty(repo.Config
.OfType<ConfigurationEntry<string>>()
.Where(x => x.Key == "unittests.plugin"));

repo.Config.Add("unittests.plugin", "value1", ConfigurationLevel.Local);
repo.Config.Add("unittests.plugin", "value2", ConfigurationLevel.Local);

Assert.Equal(new[] { "value1", "value2" }, repo.Config
.OfType<ConfigurationEntry<string>>()
.Where(x => x.Key == "unittests.plugin" && x.Level == ConfigurationLevel.Local)
.Select(x => x.Value)
.ToArray());
}
}

[Fact]
public void CanAddAndReadMultivarFromTheGlobalConfiguration()
{
string path = SandboxBareTestRepo();
using (var repo = new Repository(path))
{
Assert.True(repo.Config.HasConfig(ConfigurationLevel.Global));
Assert.Empty(repo.Config
.OfType<ConfigurationEntry<string>>()
.Where(x => x.Key == "unittests.plugin"));

repo.Config.Add("unittests.plugin", "value1", ConfigurationLevel.Global);
repo.Config.Add("unittests.plugin", "value2", ConfigurationLevel.Global);

Assert.Equal(new[] { "value1", "value2" }, repo.Config
.OfType<ConfigurationEntry<string>>()
.Where(x => x.Key == "unittests.plugin")
.Select(x => x.Value)
.ToArray());
}
}

[Fact]
public void CanUnsetAllFromTheGlobalConfiguration()
{
string path = SandboxBareTestRepo();
using (var repo = new Repository(path))
{
Assert.True(repo.Config.HasConfig(ConfigurationLevel.Global));
Assert.Empty(repo.Config
.OfType<ConfigurationEntry<string>>()
.Where(x => x.Key == "unittests.plugin")
.Select(x => x.Value)
.ToArray());

repo.Config.Add("unittests.plugin", "value1", ConfigurationLevel.Global);
repo.Config.Add("unittests.plugin", "value2", ConfigurationLevel.Global);

Assert.Equal(2, repo.Config
.OfType<ConfigurationEntry<string>>()
.Where(x => x.Key == "unittests.plugin" && x.Level == ConfigurationLevel.Global)
.Select(x => x.Value)
.Count());

repo.Config.UnsetAll("unittests.plugin");

Assert.Equal(2, repo.Config
.OfType<ConfigurationEntry<string>>()
.Where(x => x.Key == "unittests.plugin" && x.Level == ConfigurationLevel.Global)
.Select(x => x.Value)
.Count());

repo.Config.UnsetAll("unittests.plugin", ConfigurationLevel.Global);

Assert.Empty(repo.Config
.OfType<ConfigurationEntry<string>>()
.Where(x => x.Key == "unittests.plugin")
.Select(x => x.Value)
.ToArray());
}
}

[Fact]
public void CanUnsetAllFromTheLocalConfiguration()
{
string path = SandboxStandardTestRepo();
using (var repo = new Repository(path))
{
Assert.True(repo.Config.HasConfig(ConfigurationLevel.Global));
Assert.Empty(repo.Config
.OfType<ConfigurationEntry<string>>()
.Where(x => x.Key == "unittests.plugin")
.Select(x => x.Value)
.ToArray());

repo.Config.Add("unittests.plugin", "value1");
repo.Config.Add("unittests.plugin", "value2");

Assert.Equal(2, repo.Config
.OfType<ConfigurationEntry<string>>()
.Where(x => x.Key == "unittests.plugin" && x.Level == ConfigurationLevel.Local)
.Select(x => x.Value)
.Count());

repo.Config.UnsetAll("unittests.plugin");

Assert.Empty(repo.Config
.OfType<ConfigurationEntry<string>>()
.Where(x => x.Key == "unittests.plugin"));
}
}

[Fact]
public void CanReadBooleanValue()
{
Expand Down
75 changes: 69 additions & 6 deletions LibGit2Sharp/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,33 +233,47 @@ public void Dispose()
/// Unset a configuration variable (key and value) in the local configuration.
/// </summary>
/// <param name="key">The key to unset.</param>
public virtual void Unset(string key)
public virtual bool Unset(string key)
{
Unset(key, ConfigurationLevel.Local);
return Unset(key, ConfigurationLevel.Local);
}

/// <summary>
/// Unset a configuration variable (key and value).
/// </summary>
/// <param name="key">The key to unset.</param>
/// <param name="level">The configuration file which should be considered as the target of this operation</param>
public virtual void Unset(string key, ConfigurationLevel level)
public virtual bool Unset(string key, ConfigurationLevel level)
{
Ensure.ArgumentNotNullOrEmptyString(key, "key");

using (ConfigurationHandle h = RetrieveConfigurationHandle(level, true, configHandle))
{
Proxy.git_config_delete(h, key);
return Proxy.git_config_delete(h, key);
}
}

internal void UnsetMultivar(string key, ConfigurationLevel level)
/// <summary>
/// Unset a configuration values in a multivar variable (key and value) in the local configuration.
kzu marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <param name="key">The key to unset.</param>
public virtual bool UnsetAll(string key)
{
return UnsetAll(key, ConfigurationLevel.Local);
}

/// <summary>
/// Unset all configuration values in a multivar variable (key and value).
/// </summary>
/// <param name="key">The key to unset.</param>
/// <param name="level">The configuration file which should be considered as the target of this operation</param>
public virtual bool UnsetAll(string key, ConfigurationLevel level)
{
Ensure.ArgumentNotNullOrEmptyString(key, "key");

using (ConfigurationHandle h = RetrieveConfigurationHandle(level, true, configHandle))
{
Proxy.git_config_delete_multivar(h, key);
return Proxy.git_config_delete_multivar(h, key);
}
}

Expand Down Expand Up @@ -634,6 +648,55 @@ public virtual void Set<T>(string key, T value, ConfigurationLevel level)
}
}

/// <summary>
/// Adds a configuration value for a multivalue key in the local configuration. Keys are in the form 'section.name'.
/// <para>
/// For example in order to add the value for this in a .git\config file:
///
/// [test]
/// plugin = first
///
/// You would call:
///
/// repo.Config.Add("test.plugin", "first");
/// </para>
/// </summary>
/// <typeparam name="T">The configuration value type</typeparam>
/// <param name="key">The key parts</param>
/// <param name="value">The value</param>
public virtual void Add(string key, string value)
{
Add(key, value, ConfigurationLevel.Local);
}

/// <summary>
/// Adds a configuration value for a multivalue key. Keys are in the form 'section.name'.
/// <para>
/// For example in order to add the value for this in a .git\config file:
///
/// [test]
/// plugin = first
///
/// You would call:
///
/// repo.Config.Add("test.plugin", "first");
/// </para>
/// </summary>
/// <typeparam name="T">The configuration value type</typeparam>
/// <param name="key">The key parts</param>
/// <param name="value">The value</param>
/// <param name="level">The configuration file which should be considered as the target of this operation</param>
public virtual void Add(string key, string value, ConfigurationLevel level)
{
Ensure.ArgumentNotNull(value, "value");
Ensure.ArgumentNotNullOrEmptyString(key, "key");

using (ConfigurationHandle h = RetrieveConfigurationHandle(level, true, configHandle))
{
Proxy.git_config_add_string(h, key, value);
}
}

/// <summary>
/// Find configuration entries matching <paramref name="regexp"/>.
/// </summary>
Expand Down
7 changes: 7 additions & 0 deletions LibGit2Sharp/Core/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,13 @@ private sealed class NativeShutdownObject : CriticalFinalizerObject
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string regexp);

[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
internal static extern unsafe int git_config_set_multivar(
git_config* cfg,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string regexp,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string value);

[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
internal static extern int git_config_find_global(GitBuf global_config_path);

Expand Down
8 changes: 8 additions & 0 deletions LibGit2Sharp/Core/Proxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,14 @@ public static unsafe void git_config_set_string(ConfigurationHandle config, stri
Ensure.ZeroResult(res);
}

static readonly string non_existing_regex = Guid.NewGuid().ToString();

public static unsafe void git_config_add_string(ConfigurationHandle config, string name, string value)
{
int res = NativeMethods.git_config_set_multivar(config, name, non_existing_regex, value);
Ensure.ZeroResult(res);
}

public static unsafe ICollection<TResult> git_config_foreach<TResult>(
ConfigurationHandle config,
Func<IntPtr, TResult> resultSelector)
Expand Down
4 changes: 2 additions & 2 deletions LibGit2Sharp/RemoteUpdater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ private IEnumerable<string> GetFetchRefSpecs()

private void SetFetchRefSpecs(IEnumerable<string> value)
{
repo.Config.UnsetMultivar(string.Format("remote.{0}.fetch", remoteName), ConfigurationLevel.Local);
repo.Config.UnsetAll(string.Format("remote.{0}.fetch", remoteName), ConfigurationLevel.Local);

foreach (var url in value)
{
Expand All @@ -74,7 +74,7 @@ private IEnumerable<string> GetPushRefSpecs()

private void SetPushRefSpecs(IEnumerable<string> value)
{
repo.Config.UnsetMultivar(string.Format("remote.{0}.push", remoteName), ConfigurationLevel.Local);
repo.Config.UnsetAll(string.Format("remote.{0}.push", remoteName), ConfigurationLevel.Local);

foreach (var url in value)
{
Expand Down