diff --git a/LibGit2Sharp.Tests/ConfigurationFixture.cs b/LibGit2Sharp.Tests/ConfigurationFixture.cs index 78cb71c0b..50b6fdc7a 100644 --- a/LibGit2Sharp.Tests/ConfigurationFixture.cs +++ b/LibGit2Sharp.Tests/ConfigurationFixture.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; +using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -14,35 +16,6 @@ private static void AssertValueInLocalConfigFile(string repoPath, string regex) AssertValueInConfigFile(configFilePath, regex); } - private static string RetrieveGlobalConfigLocation() - { - string[] variables = { "HOME", "USERPROFILE", }; - - foreach (string variable in variables) - { - string potentialLocation = Environment.GetEnvironmentVariable(variable); - if (string.IsNullOrEmpty(potentialLocation)) - { - continue; - } - - string potentialPath = Path.Combine(potentialLocation, ".gitconfig"); - - if (File.Exists(potentialPath)) - { - return potentialPath; - } - } - - throw new InvalidOperationException("Unable to determine the location of '.gitconfig' file."); - } - - private static void AssertValueInGlobalConfigFile(string regex) - { - string configFilePath = RetrieveGlobalConfigLocation(); - AssertValueInConfigFile(configFilePath, regex); - } - [Fact] public void CanUnsetAnEntryFromTheLocalConfiguration() { @@ -256,7 +229,7 @@ public void CanSetBooleanValue() [Fact] public void SettingLocalConfigurationOutsideAReposThrows() { - using (var config = new Configuration(null, null, null)) + using (var config = Configuration.BuildFrom(null, null, null, null)) { Assert.Throws(() => config.Set("unittests.intsetting", 3)); } @@ -395,5 +368,46 @@ public void CanTellIfASpecificStoreContainsAKey() Assert.Null(repo.Config.Get("MCHammer.You-cant-touch-this", ConfigurationLevel.System)); } } + + public static IEnumerable ConfigAccessors + { + get + { + return new List + { + new[] { new Func(p => Path.Combine(p, ".git", "config")) }, + new[] { new Func(p => Path.Combine(p, ".git")) }, + new[] { new Func(p => p) }, + }; + } + } + + [Theory, PropertyData("ConfigAccessors")] + public void CanAccessConfigurationWithoutARepository(Func localConfigurationPathProvider) + { + var path = SandboxStandardTestRepoGitDir(); + + string globalConfigPath = CreateConfigurationWithDummyUser(Constants.Signature); + var options = new RepositoryOptions { GlobalConfigurationLocation = globalConfigPath }; + + using (var repo = new Repository(path, options)) + { + repo.Config.Set("my.key", "local"); + repo.Config.Set("my.key", "mouse", ConfigurationLevel.Global); + } + + using (var config = Configuration.BuildFrom(localConfigurationPathProvider(path), globalConfigPath)) + { + Assert.Equal("local", config.Get("my.key").Value); + Assert.Equal("mouse", config.Get("my.key", ConfigurationLevel.Global).Value); + } + } + + [Fact] + public void PassingANonExistingLocalConfigurationFileToBuildFromthrowss() + { + Assert.Throws(() => Configuration.BuildFrom( + Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()))); + } } } diff --git a/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs b/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs index 017404760..08d7daf0d 100644 --- a/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs +++ b/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs @@ -336,19 +336,19 @@ protected string CreateConfigurationWithDummyUser(Signature signature) protected string CreateConfigurationWithDummyUser(string name, string email) { SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); - Directory.CreateDirectory(scd.DirectoryPath); - string configFilePath = Path.Combine(scd.DirectoryPath, "global-config"); - using (Configuration config = new Configuration(configFilePath)) + string configFilePath = Touch(scd.DirectoryPath, "fake-config"); + + using (Configuration config = Configuration.BuildFrom(configFilePath)) { if (name != null) { - config.Set("user.name", name, ConfigurationLevel.Global); + config.Set("user.name", name); } if (email != null) { - config.Set("user.email", email, ConfigurationLevel.Global); + config.Set("user.email", email); } } diff --git a/LibGit2Sharp/Configuration.cs b/LibGit2Sharp/Configuration.cs index 47b14fb32..b9924c952 100644 --- a/LibGit2Sharp/Configuration.cs +++ b/LibGit2Sharp/Configuration.cs @@ -15,12 +15,11 @@ namespace LibGit2Sharp public class Configuration : IDisposable, IEnumerable> { + private readonly FilePath repoConfigPath; private readonly FilePath globalConfigPath; private readonly FilePath xdgConfigPath; private readonly FilePath systemConfigPath; - private readonly Repository repository; - private ConfigurationSafeHandle configHandle; /// @@ -29,19 +28,22 @@ public class Configuration : IDisposable, protected Configuration() { } - internal Configuration(Repository repository, string globalConfigurationFileLocation, + internal Configuration(Repository repository, string repositoryConfigurationFileLocation, string globalConfigurationFileLocation, string xdgConfigurationFileLocation, string systemConfigurationFileLocation) { - this.repository = repository; + if (repositoryConfigurationFileLocation != null) + { + repoConfigPath = NormalizeConfigPath(repositoryConfigurationFileLocation); + } globalConfigPath = globalConfigurationFileLocation ?? Proxy.git_config_find_global(); xdgConfigPath = xdgConfigurationFileLocation ?? Proxy.git_config_find_xdg(); systemConfigPath = systemConfigurationFileLocation ?? Proxy.git_config_find_system(); - Init(); + Init(repository); } - private void Init() + private void Init(Repository repository) { configHandle = Proxy.git_config_new(); @@ -56,6 +58,10 @@ private void Init() Proxy.git_repository_set_config(repository.Handle, configHandle); } + else if (repoConfigPath != null) + { + Proxy.git_config_add_file_ondisk(configHandle, repoConfigPath, ConfigurationLevel.Local); + } if (globalConfigPath != null) { @@ -73,12 +79,126 @@ private void Init() } } + private FilePath NormalizeConfigPath(FilePath path) + { + if (File.Exists(path.Native)) + { + return path; + } + + if (!Directory.Exists(path.Native)) + { + throw new FileNotFoundException("Cannot find repository configuration file", path.Native); + } + + var configPath = Path.Combine(path.Native, "config"); + + if (File.Exists(configPath)) + { + return configPath; + } + + var gitConfigPath = Path.Combine(path.Native, ".git", "config"); + + if (File.Exists(gitConfigPath)) + { + return gitConfigPath; + } + + throw new FileNotFoundException("Cannot find repository configuration file", path.Native); + } + + /// + /// Access configuration values without a repository. + /// + /// Generally you want to access configuration via an instance of instead. + /// + /// + /// can either contains a path to a file or a directory. In the latter case, + /// this can be the working directory, the .git directory or the directory containing a bare repository. + /// + /// + /// Path to an existing Repository configuration file. + /// An instance of . + public static Configuration BuildFrom( + string repositoryConfigurationFileLocation) + { + return BuildFrom(repositoryConfigurationFileLocation, null, null, null); + } + + /// + /// Access configuration values without a repository. + /// + /// Generally you want to access configuration via an instance of instead. + /// + /// + /// can either contains a path to a file or a directory. In the latter case, + /// this can be the working directory, the .git directory or the directory containing a bare repository. + /// + /// + /// Path to an existing Repository configuration file. + /// Path to a Global configuration file. If null, the default path for a Global configuration file will be probed. + /// An instance of . + public static Configuration BuildFrom( + string repositoryConfigurationFileLocation, + string globalConfigurationFileLocation) + { + return BuildFrom(repositoryConfigurationFileLocation, globalConfigurationFileLocation, null, null); + } + + /// + /// Access configuration values without a repository. + /// + /// Generally you want to access configuration via an instance of instead. + /// + /// + /// can either contains a path to a file or a directory. In the latter case, + /// this can be the working directory, the .git directory or the directory containing a bare repository. + /// + /// + /// Path to an existing Repository configuration file. + /// Path to a Global configuration file. If null, the default path for a Global configuration file will be probed. + /// Path to a XDG configuration file. If null, the default path for a XDG configuration file will be probed. + /// An instance of . + public static Configuration BuildFrom( + string repositoryConfigurationFileLocation, + string globalConfigurationFileLocation, + string xdgConfigurationFileLocation) + { + return BuildFrom(repositoryConfigurationFileLocation, globalConfigurationFileLocation, xdgConfigurationFileLocation, null); + } + + /// + /// Access configuration values without a repository. + /// + /// Generally you want to access configuration via an instance of instead. + /// + /// + /// can either contains a path to a file or a directory. In the latter case, + /// this can be the working directory, the .git directory or the directory containing a bare repository. + /// + /// + /// Path to an existing Repository configuration file. + /// Path to a Global configuration file. If null, the default path for a Global configuration file will be probed. + /// Path to a XDG configuration file. If null, the default path for a XDG configuration file will be probed. + /// Path to a System configuration file. If null, the default path for a System configuration file will be probed. + /// An instance of . + public static Configuration BuildFrom( + string repositoryConfigurationFileLocation, + string globalConfigurationFileLocation, + string xdgConfigurationFileLocation, + string systemConfigurationFileLocation) + { + return new Configuration(null, repositoryConfigurationFileLocation, globalConfigurationFileLocation, xdgConfigurationFileLocation, systemConfigurationFileLocation); + } + /// /// Access configuration values without a repository. Generally you want to access configuration via an instance of instead. /// /// Path to a Global configuration file. If null, the default path for a global configuration file will be probed. + [Obsolete("This method will be removed in the next release. Please use Configuration.BuildFrom(string, string) instead.")] public Configuration(string globalConfigurationFileLocation) - : this(null, globalConfigurationFileLocation, null, null) + : this(null, null, globalConfigurationFileLocation, null, null) { } /// @@ -86,8 +206,9 @@ public Configuration(string globalConfigurationFileLocation) /// /// Path to a Global configuration file. If null, the default path for a global configuration file will be probed. /// Path to a XDG configuration file. If null, the default path for a XDG configuration file will be probed. + [Obsolete("This method will be removed in the next release. Please use Configuration.BuildFrom(string, string, string) instead.")] public Configuration(string globalConfigurationFileLocation, string xdgConfigurationFileLocation) - : this(null, globalConfigurationFileLocation, xdgConfigurationFileLocation, null) + : this(null, null, globalConfigurationFileLocation, xdgConfigurationFileLocation, null) { } /// @@ -96,10 +217,10 @@ public Configuration(string globalConfigurationFileLocation, string xdgConfigura /// Path to a Global configuration file. If null, the default path for a global configuration file will be probed. /// Path to a XDG configuration file. If null, the default path for a XDG configuration file will be probed. /// Path to a System configuration file. If null, the default path for a system configuration file will be probed. + [Obsolete("This method will be removed in the next release. Please use Configuration.BuildFrom(string, string, string, string) instead.")] public Configuration(string globalConfigurationFileLocation, string xdgConfigurationFileLocation, string systemConfigurationFileLocation) - : this(null, globalConfigurationFileLocation, xdgConfigurationFileLocation, systemConfigurationFileLocation) - { - } + : this(null, null, globalConfigurationFileLocation, xdgConfigurationFileLocation, systemConfigurationFileLocation) + { } /// /// Determines which configuration file has been found. diff --git a/LibGit2Sharp/Repository.cs b/LibGit2Sharp/Repository.cs index 79c9077bc..511204087 100644 --- a/LibGit2Sharp/Repository.cs +++ b/LibGit2Sharp/Repository.cs @@ -125,7 +125,7 @@ public Repository(string path, RepositoryOptions options) config = new Lazy( () => - RegisterForCleanup(new Configuration(this, configurationGlobalFilePath, configurationXDGFilePath, + RegisterForCleanup(new Configuration(this, null, configurationGlobalFilePath, configurationXDGFilePath, configurationSystemFilePath))); odb = new Lazy(() => new ObjectDatabase(this)); diff = new Diff(this);