diff --git a/Lib/NativeBinaries/amd64/git2.dll b/Lib/NativeBinaries/amd64/git2.dll index 6d668c9ca..9eb2096ae 100644 Binary files a/Lib/NativeBinaries/amd64/git2.dll and b/Lib/NativeBinaries/amd64/git2.dll differ diff --git a/Lib/NativeBinaries/amd64/git2.pdb b/Lib/NativeBinaries/amd64/git2.pdb index 23fac854b..8cdaaf6f1 100644 Binary files a/Lib/NativeBinaries/amd64/git2.pdb and b/Lib/NativeBinaries/amd64/git2.pdb differ diff --git a/Lib/NativeBinaries/x86/git2.dll b/Lib/NativeBinaries/x86/git2.dll index 2a8420816..5c8e4b7c4 100644 Binary files a/Lib/NativeBinaries/x86/git2.dll and b/Lib/NativeBinaries/x86/git2.dll differ diff --git a/Lib/NativeBinaries/x86/git2.pdb b/Lib/NativeBinaries/x86/git2.pdb index 3f9756224..4a72f127e 100644 Binary files a/Lib/NativeBinaries/x86/git2.pdb and b/Lib/NativeBinaries/x86/git2.pdb differ diff --git a/LibGit2Sharp.Tests/CommitFixture.cs b/LibGit2Sharp.Tests/CommitFixture.cs index eafe598d5..216c17227 100644 --- a/LibGit2Sharp.Tests/CommitFixture.cs +++ b/LibGit2Sharp.Tests/CommitFixture.cs @@ -30,7 +30,7 @@ public void CanCorrectlyCountCommitsWhenSwitchingToAnotherBranch() { repo.Reset(ResetOptions.Hard); - repo.Checkout("test"); + repo.Checkout("test", CheckoutOptions.Force, null); Assert.Equal(2, repo.Commits.Count()); Assert.Equal("e90810b8df3e80c413d903f631643c716887138d", repo.Commits.First().Id.Sha); @@ -229,8 +229,8 @@ public void CanEnumerateFromDetachedHead() { repoClone.Reset(ResetOptions.Hard); - string headSha = repoClone.Head.Tip.Sha; - repoClone.Checkout(headSha); + Branch head = repoClone.Head; + repoClone.Checkout(head, CheckoutOptions.Force, null); AssertEnumerationOfCommitsInRepo(repoClone, repo => new Filter { Since = repo.Head }, @@ -479,7 +479,8 @@ public void CanCommitWithSignatureFromConfig() Assert.True(Path.IsPathRooted(dir)); Assert.True(Directory.Exists(dir)); - InconclusiveIf(() => !repo.Config.HasGlobalConfig, "No Git global configuration available"); + InconclusiveIf(() => !repo.Config.HasConfig(ConfigurationLevel.Global), + "No Git global configuration available"); const string relativeFilepath = "new.txt"; string filePath = Path.Combine(repo.Info.WorkingDirectory, relativeFilepath); @@ -496,12 +497,12 @@ public void CanCommitWithSignatureFromConfig() AssertBlobContent(repo.Head[relativeFilepath], "nulltoken\n"); AssertBlobContent(commit[relativeFilepath], "nulltoken\n"); - var name = repo.Config.Get("user.name", null); - var email = repo.Config.Get("user.email", null); - Assert.Equal(commit.Author.Name, name); - Assert.Equal(commit.Author.Email, email); - Assert.Equal(commit.Committer.Name, name); - Assert.Equal(commit.Committer.Email, email); + var name = repo.Config.Get("user.name"); + var email = repo.Config.Get("user.email"); + Assert.Equal(commit.Author.Name, name.Value); + Assert.Equal(commit.Author.Email, email.Value); + Assert.Equal(commit.Committer.Name, name.Value); + Assert.Equal(commit.Committer.Email, email.Value); } } diff --git a/LibGit2Sharp.Tests/ConfigurationFixture.cs b/LibGit2Sharp.Tests/ConfigurationFixture.cs index 604c49416..e7575eaf6 100644 --- a/LibGit2Sharp.Tests/ConfigurationFixture.cs +++ b/LibGit2Sharp.Tests/ConfigurationFixture.cs @@ -50,14 +50,14 @@ public void CanUnsetAnEntryFromTheLocalConfiguration() var path = BuildTemporaryCloneOfTestRepo(StandardTestRepoPath); using (var repo = new Repository(path.RepositoryPath)) { - Assert.False(repo.Config.Get("unittests.boolsetting", false)); + Assert.Null(repo.Config.Get("unittests.boolsetting")); repo.Config.Set("unittests.boolsetting", true); - Assert.True(repo.Config.Get("unittests.boolsetting", false)); + Assert.True(repo.Config.Get("unittests.boolsetting").Value); repo.Config.Unset("unittests.boolsetting"); - Assert.False(repo.Config.Get("unittests.boolsetting", false)); + Assert.Null(repo.Config.Get("unittests.boolsetting")); } } @@ -66,34 +66,18 @@ public void CanUnsetAnEntryFromTheGlobalConfiguration() { SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); - string confs = Path.Combine(scd.DirectoryPath, "confs"); - Directory.CreateDirectory(confs); - - string globalLocation = Path.Combine(confs, "my-global-config"); - string systemLocation = Path.Combine(confs, "my-system-config"); - - StringBuilder sb = new StringBuilder() - .AppendLine("[Wow]") - .AppendFormat("Man-I-am-totally-global = 42{0}", Environment.NewLine); - - File.WriteAllText(globalLocation, sb.ToString()); - - var options = new RepositoryOptions - { - GlobalConfigurationLocation = globalLocation, - SystemConfigurationLocation = systemLocation, - }; + var options = BuildFakeConfigs(scd); using (var repo = new Repository(BareTestRepoPath, options)) { - Assert.True(repo.Config.HasGlobalConfig); - Assert.Equal(42, repo.Config.Get("Wow.Man-I-am-totally-global", 1337)); + Assert.True(repo.Config.HasConfig(ConfigurationLevel.Global)); + Assert.Equal(42, repo.Config.Get("Wow.Man-I-am-totally-global").Value); repo.Config.Unset("Wow.Man-I-am-totally-global"); - Assert.Equal(42, repo.Config.Get("Wow.Man-I-am-totally-global", 1337)); + Assert.Equal(42, repo.Config.Get("Wow.Man-I-am-totally-global").Value); repo.Config.Unset("Wow.Man-I-am-totally-global", ConfigurationLevel.Global); - Assert.Equal(1337, repo.Config.Get("Wow.Man-I-am-totally-global", 1337)); + Assert.Null(repo.Config.Get("Wow.Man-I-am-totally-global")); } } @@ -102,9 +86,10 @@ public void CanGetGlobalStringValue() { using (var repo = new Repository(StandardTestRepoPath)) { - InconclusiveIf(() => !repo.Config.HasGlobalConfig, "No Git global configuration available"); + InconclusiveIf(() => !repo.Config.HasConfig(ConfigurationLevel.Global), + "No Git global configuration available"); - Assert.NotNull(repo.Config.Get("user.name", null)); + Assert.NotNull(repo.Config.Get("user.name")); } } @@ -113,8 +98,10 @@ public void CanGetGlobalStringValueWithoutRepo() { using (var config = new Configuration()) { - InconclusiveIf(() => !config.HasGlobalConfig, "No Git global configuration available"); - Assert.NotNull(config.Get("user.name", null)); + InconclusiveIf(() => !config.HasConfig(ConfigurationLevel.Global), + "No Git global configuration available"); + + Assert.NotNull(config.Get("user.name")); } } @@ -123,8 +110,7 @@ public void CanReadBooleanValue() { using (var repo = new Repository(StandardTestRepoPath)) { - Assert.True(repo.Config.Get("core.ignorecase", false)); - Assert.True(repo.Config.Get("core", "ignorecase", false)); + Assert.True(repo.Config.Get("core.ignorecase").Value); } } @@ -133,8 +119,7 @@ public void CanReadIntValue() { using (var repo = new Repository(StandardTestRepoPath)) { - Assert.Equal(2, repo.Config.Get("unittests.intsetting", 42)); - Assert.Equal(2, repo.Config.Get("unittests", "intsetting", 42)); + Assert.Equal(2, repo.Config.Get("unittests.intsetting").Value); } } @@ -143,8 +128,7 @@ public void CanReadLongValue() { using (var repo = new Repository(StandardTestRepoPath)) { - Assert.Equal(15234, repo.Config.Get("unittests.longsetting", 42)); - Assert.Equal(15234, repo.Config.Get("unittests", "longsetting", 42)); + Assert.Equal(15234, repo.Config.Get("unittests.longsetting").Value); } } @@ -153,8 +137,8 @@ public void CanReadStringValue() { using (var repo = new Repository(StandardTestRepoPath)) { - Assert.Equal("+refs/heads/*:refs/remotes/origin/*", repo.Config.Get("remote.origin.fetch", null)); - Assert.Equal("+refs/heads/*:refs/remotes/origin/*", repo.Config.Get("remote", "origin", "fetch", null)); + Assert.Equal("+refs/heads/*:refs/remotes/origin/*", repo.Config.Get("remote.origin.fetch").Value); + Assert.Equal("+refs/heads/*:refs/remotes/origin/*", repo.Config.Get("remote", "origin", "fetch").Value); } } @@ -163,8 +147,10 @@ public void CanEnumerateGlobalConfig() { using (var repo = new Repository(StandardTestRepoPath)) { - InconclusiveIf(() => !repo.Config.HasGlobalConfig, "No Git global configuration available"); - var entry = repo.Config.FirstOrDefault(e => e.Key == "user.name"); + InconclusiveIf(() => !repo.Config.HasConfig(ConfigurationLevel.Global), + "No Git global configuration available"); + + var entry = repo.Config.FirstOrDefault>(e => e.Key == "user.name"); Assert.NotNull(entry); Assert.NotNull(entry.Value); } @@ -175,7 +161,7 @@ public void CanEnumerateLocalConfig() { using (var repo = new Repository(StandardTestRepoPath)) { - var entry = repo.Config.FirstOrDefault(e => e.Key == "core.ignorecase"); + var entry = repo.Config.FirstOrDefault>(e => e.Key == "core.ignorecase"); Assert.NotNull(entry); Assert.Equal("true", entry.Value); } @@ -198,9 +184,10 @@ public void CanSetGlobalStringValue() { using (var repo = new Repository(StandardTestRepoPath)) { - InconclusiveIf(() => !repo.Config.HasGlobalConfig, "No Git global configuration available"); + InconclusiveIf(() => !repo.Config.HasConfig(ConfigurationLevel.Global), + "No Git global configuration available"); - var existing = repo.Config.Get("user.name", null); + var existing = repo.Config.Get("user.name"); Assert.NotNull(existing); try @@ -211,7 +198,7 @@ public void CanSetGlobalStringValue() } finally { - repo.Config.Set("user.name", existing, ConfigurationLevel.Global); + repo.Config.Set("user.name", existing.Value, ConfigurationLevel.Global); } } } @@ -221,9 +208,10 @@ public void CanSetGlobalStringValueWithoutRepo() { using(var config = new Configuration()) { - InconclusiveIf(() => !config.HasGlobalConfig, "No Git global configuration available"); + InconclusiveIf(() => !config.HasConfig(ConfigurationLevel.Global), + "No Git global configuration available"); - var existing = config.Get("user.name", null); + var existing = config.Get("user.name"); Assert.NotNull(existing); try @@ -234,7 +222,7 @@ public void CanSetGlobalStringValueWithoutRepo() } finally { - config.Set("user.name", existing, ConfigurationLevel.Global); + config.Set("user.name", existing.Value, ConfigurationLevel.Global); } } } @@ -294,14 +282,14 @@ public void CanSetAndReadUnicodeStringValue() AssertValueInLocalConfigFile(path.RepositoryPath, "stringsetting = Juliën$"); - string val = repo.Config.Get("unittests.stringsetting", ""); + string val = repo.Config.Get("unittests.stringsetting").Value; Assert.Equal("Juliën", val); } // Make sure the change is permanent using (var repo = new Repository(path.RepositoryPath)) { - string val = repo.Config.Get("unittests.stringsetting", ""); + string val = repo.Config.Get("unittests.stringsetting").Value; Assert.Equal("Juliën", val); } } @@ -311,24 +299,20 @@ public void ReadingUnsupportedTypeThrows() { using (var repo = new Repository(StandardTestRepoPath)) { - Assert.Throws(() => repo.Config.Get("unittests.setting", 42)); - Assert.Throws(() => repo.Config.Get("unittests.setting", null)); + Assert.Throws(() => repo.Config.Get("unittests.setting")); + Assert.Throws(() => repo.Config.Get("unittests.setting")); } } [Fact] - public void ReadingValueThatDoesntExistReturnsDefault() + public void ReadingValueThatDoesntExistReturnsNull() { using (var repo = new Repository(StandardTestRepoPath)) { - Assert.Null(repo.Config.Get("unittests.ghostsetting", null)); - Assert.Equal(0, repo.Config.Get("unittests.ghostsetting", 0)); - Assert.Equal(0L, repo.Config.Get("unittests.ghostsetting", 0L)); - Assert.False(repo.Config.Get("unittests.ghostsetting", false)); - Assert.Equal("42", repo.Config.Get("unittests.ghostsetting", "42")); - Assert.Equal(42, repo.Config.Get("unittests.ghostsetting", 42)); - Assert.Equal(42L, repo.Config.Get("unittests.ghostsetting", 42L)); - Assert.True(repo.Config.Get("unittests.ghostsetting", true)); + Assert.Null(repo.Config.Get("unittests.ghostsetting")); + Assert.Null(repo.Config.Get("unittests.ghostsetting")); + Assert.Null(repo.Config.Get("unittests.ghostsetting")); + Assert.Null(repo.Config.Get("unittests.ghostsetting")); } } @@ -341,5 +325,86 @@ public void SettingUnsupportedTypeThrows() Assert.Throws(() => repo.Config.Set("unittests.setting", repo.Config)); } } + + [Fact] + public void CanGetAnEntryFromASpecificStore() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + + var options = BuildFakeConfigs(scd); + + var path = BuildTemporaryCloneOfTestRepo(StandardTestRepoPath); + using (var repo = new Repository(path.RepositoryPath, options)) + { + Assert.True(repo.Config.HasConfig(ConfigurationLevel.Local)); + Assert.True(repo.Config.HasConfig(ConfigurationLevel.Global)); + Assert.True(repo.Config.HasConfig(ConfigurationLevel.System)); + + Assert.Null(repo.Config.Get("Woot.global-rocks", ConfigurationLevel.Local)); + + repo.Config.Set("Woot.this-rocks", "local"); + + Assert.Equal("global", repo.Config.Get("Woot.this-rocks", ConfigurationLevel.Global).Value); + Assert.Equal("xdg", repo.Config.Get("Woot.this-rocks", ConfigurationLevel.XDG).Value); + Assert.Equal("system", repo.Config.Get("Woot.this-rocks", ConfigurationLevel.System).Value); + Assert.Equal("local", repo.Config.Get("Woot.this-rocks", ConfigurationLevel.Local).Value); + } + } + + [Fact] + public void CanTellIfASpecificStoreContainsAKey() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + + var options = BuildFakeConfigs(scd); + + using (var repo = new Repository(BareTestRepoPath, options)) + { + Assert.True(repo.Config.HasConfig(ConfigurationLevel.System)); + + Assert.Null(repo.Config.Get("MCHammer.You-cant-touch-this", ConfigurationLevel.System)); + } + } + + private RepositoryOptions BuildFakeConfigs(SelfCleaningDirectory scd) + { + var options = BuildFakeRepositoryOptions(scd); + + StringBuilder sb = new StringBuilder() + .AppendFormat("[Woot]{0}", Environment.NewLine) + .AppendFormat("this-rocks = global{0}", Environment.NewLine) + .AppendFormat("[Wow]{0}", Environment.NewLine) + .AppendFormat("Man-I-am-totally-global = 42{0}", Environment.NewLine); + File.WriteAllText(options.GlobalConfigurationLocation, sb.ToString()); + + sb = new StringBuilder() + .AppendFormat("[Woot]{0}", Environment.NewLine) + .AppendFormat("this-rocks = system{0}", Environment.NewLine); + File.WriteAllText(options.SystemConfigurationLocation, sb.ToString()); + + sb = new StringBuilder() + .AppendFormat("[Woot]{0}", Environment.NewLine) + .AppendFormat("this-rocks = xdg{0}", Environment.NewLine); + File.WriteAllText(options.XDGConfigurationLocation, sb.ToString()); + + return options; + } + + private RepositoryOptions BuildFakeRepositoryOptions(SelfCleaningDirectory scd) + { + string confs = Path.Combine(scd.DirectoryPath, "confs"); + Directory.CreateDirectory(confs); + + string globalLocation = Path.Combine(confs, "my-global-config"); + string xdgLocation = Path.Combine(confs, "my-xdg-config"); + string systemLocation = Path.Combine(confs, "my-system-config"); + + return new RepositoryOptions + { + GlobalConfigurationLocation = globalLocation, + XDGConfigurationLocation = xdgLocation, + SystemConfigurationLocation = systemLocation, + }; + } } } diff --git a/LibGit2Sharp.Tests/RemoteFixture.cs b/LibGit2Sharp.Tests/RemoteFixture.cs index dda9fdb01..2fc2bfade 100644 --- a/LibGit2Sharp.Tests/RemoteFixture.cs +++ b/LibGit2Sharp.Tests/RemoteFixture.cs @@ -165,10 +165,10 @@ public void CreatingANewRemoteAddsADefaultRefSpec() Assert.Equal(name, remote.Name); Assert.Equal(url, remote.Url); - var refSpec = repo.Config.Get("remote", remote.Name, "fetch", null); + var refSpec = repo.Config.Get("remote", remote.Name, "fetch"); Assert.NotNull(refSpec); - Assert.Equal("+refs/heads/*:refs/remotes/upstream/*", refSpec); + Assert.Equal("+refs/heads/*:refs/remotes/upstream/*", refSpec.Value); } } @@ -185,10 +185,10 @@ public void CanAddANewRemoteWithAFetchRefSpec() repo.Remotes.Add(name, url, fetchRefSpec); - var refSpec = repo.Config.Get("remote", name, "fetch", null); + var refSpec = repo.Config.Get("remote", name, "fetch"); Assert.NotNull(refSpec); - Assert.Equal(fetchRefSpec, refSpec); + Assert.Equal(fetchRefSpec, refSpec.Value); } } } diff --git a/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs b/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs index 1093f09bc..bd52741e6 100644 --- a/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs +++ b/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs @@ -146,6 +146,7 @@ private string MeanwhileInAnotherDimensionAnEvilMastermindIsAtWork(string workin public void CanProvideDifferentConfigurationFilesToARepository() { string globalLocation = Path.Combine(newWorkdir, "my-global-config"); + string xdgLocation = Path.Combine(newWorkdir, "my-xdg-config"); string systemLocation = Path.Combine(newWorkdir, "my-system-config"); const string name = "Adam 'aroben' Roben"; @@ -155,20 +156,23 @@ public void CanProvideDifferentConfigurationFilesToARepository() .AppendLine("[user]") .AppendFormat("name = {0}{1}", name, Environment.NewLine) .AppendFormat("email = {0}{1}", email, Environment.NewLine); - File.WriteAllText(globalLocation, sb.ToString()); + File.WriteAllText(systemLocation, string.Empty); + File.WriteAllText(xdgLocation, string.Empty); var options = new RepositoryOptions { GlobalConfigurationLocation = globalLocation, + XDGConfigurationLocation = xdgLocation, SystemConfigurationLocation = systemLocation, }; using (var repo = new Repository(BareTestRepoPath, options)) { - Assert.True(repo.Config.HasGlobalConfig); - Assert.Equal(name, repo.Config.Get("user", "name", null)); - Assert.Equal(email, repo.Config.Get("user", "email", null)); + Assert.True(repo.Config.HasConfig(ConfigurationLevel.Global)); + Assert.Equal(name, repo.Config.Get("user.name").Value); + Assert.Equal(email, repo.Config.Get("user.email").Value); + repo.Config.Set("xdg.setting", "https://twitter.com/libgit2sharp", ConfigurationLevel.XDG); repo.Config.Set("help.link", "https://twitter.com/xpaulbettsx/status/205761932626636800", ConfigurationLevel.System); } diff --git a/LibGit2Sharp.Tests/ResetHeadFixture.cs b/LibGit2Sharp.Tests/ResetHeadFixture.cs index 5dbf383bc..f738a7f17 100644 --- a/LibGit2Sharp.Tests/ResetHeadFixture.cs +++ b/LibGit2Sharp.Tests/ResetHeadFixture.cs @@ -174,7 +174,7 @@ public void HardResetUpdatesTheContentOfTheWorkingDirectory() names.Sort(StringComparer.Ordinal); File.Delete(Path.Combine(repo.Info.WorkingDirectory, "README")); - File.WriteAllText(Path.Combine(repo.Info.WorkingDirectory, "WillBeRemoved.txt"), "content\n"); + File.WriteAllText(Path.Combine(repo.Info.WorkingDirectory, "WillNotBeRemoved.txt"), "content\n"); Assert.True(names.Count > 4); @@ -183,7 +183,7 @@ public void HardResetUpdatesTheContentOfTheWorkingDirectory() names = new DirectoryInfo(repo.Info.WorkingDirectory).GetFileSystemInfos().Select(fsi => fsi.Name).ToList(); names.Sort(StringComparer.Ordinal); - Assert.Equal(new[] { ".git", "README", "branch_file.txt", "new.txt" }, names); + Assert.Equal(new[] { ".git", "README", "WillNotBeRemoved.txt", "branch_file.txt", "new.txt", "new_untracked_file.txt" }, names); } } } diff --git a/LibGit2Sharp/Branch.cs b/LibGit2Sharp/Branch.cs index d5b4c903b..dda928a51 100644 --- a/LibGit2Sharp/Branch.cs +++ b/LibGit2Sharp/Branch.cs @@ -169,15 +169,14 @@ public virtual Remote Remote { get { - string remoteName = repo.Config.Get("branch", Name, "remote", null); - Remote remote = null; + ConfigurationEntry remoteEntry = repo.Config.Get("branch", Name, "remote"); - if (!string.IsNullOrEmpty(remoteName)) + if (remoteEntry == null) { - remote = repo.Remotes[remoteName]; + return null; } - return remote; + return repo.Remotes[remoteEntry.Value]; } } diff --git a/LibGit2Sharp/Configuration.cs b/LibGit2Sharp/Configuration.cs index 0028b2108..7211c97d4 100644 --- a/LibGit2Sharp/Configuration.cs +++ b/LibGit2Sharp/Configuration.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; using System.Runtime.InteropServices; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Handles; @@ -11,16 +12,17 @@ namespace LibGit2Sharp /// /// Provides access to configuration variables for a repository. /// - public class Configuration : IDisposable, IEnumerable + public class Configuration : IDisposable, + IEnumerable, + IEnumerable> { private readonly FilePath globalConfigPath; + private readonly FilePath xdgConfigPath; private readonly FilePath systemConfigPath; private readonly Repository repository; - private ConfigurationSafeHandle systemHandle; - private ConfigurationSafeHandle globalHandle; - private ConfigurationSafeHandle localHandle; + private ConfigurationSafeHandle configHandle; /// /// Needed for mocking purposes. @@ -28,11 +30,13 @@ public class Configuration : IDisposable, IEnumerable protected Configuration() { } - internal Configuration(Repository repository, string globalConfigurationFileLocation, string systemConfigurationFileLocation) + internal Configuration(Repository repository, string globalConfigurationFileLocation, + string xdgConfigurationFileLocation, string systemConfigurationFileLocation) { this.repository = repository; globalConfigPath = globalConfigurationFileLocation ?? Proxy.git_config_find_global(); + xdgConfigPath = xdgConfigurationFileLocation ?? Proxy.git_config_find_xdg(); systemConfigPath = systemConfigurationFileLocation ?? Proxy.git_config_find_system(); Init(); @@ -40,39 +44,33 @@ internal Configuration(Repository repository, string globalConfigurationFileLoca private void Init() { + configHandle = Proxy.git_config_new(); + if (repository != null) { //TODO: push back this logic into libgit2. // As stated by @carlosmn "having a helper function to load the defaults and then allowing you // to modify it before giving it to git_repository_open_ext() would be a good addition, I think." // -- Agreed :) - - localHandle = Proxy.git_config_new(); - string repoConfigLocation = Path.Combine(repository.Info.Path, "config"); - Proxy.git_config_add_file_ondisk(localHandle, repoConfigLocation, 3); - - if (globalConfigPath != null) - { - Proxy.git_config_add_file_ondisk(localHandle, globalConfigPath, 2); - } + Proxy.git_config_add_file_ondisk(configHandle, repoConfigLocation, ConfigurationLevel.Local); - if (systemConfigPath != null) - { - Proxy.git_config_add_file_ondisk(localHandle, systemConfigPath, 1); - } - - Proxy.git_repository_set_config(repository.Handle, localHandle); + Proxy.git_repository_set_config(repository.Handle, configHandle); } if (globalConfigPath != null) { - globalHandle = Proxy.git_config_open_ondisk(globalConfigPath); + Proxy.git_config_add_file_ondisk(configHandle, globalConfigPath, ConfigurationLevel.Global); + } + + if (xdgConfigPath != null) + { + Proxy.git_config_add_file_ondisk(configHandle, xdgConfigPath, ConfigurationLevel.XDG); } if (systemConfigPath != null) { - systemHandle = Proxy.git_config_open_ondisk(systemConfigPath); + Proxy.git_config_add_file_ondisk(configHandle, systemConfigPath, ConfigurationLevel.System); } } @@ -80,39 +78,40 @@ private void Init() /// 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. + /// 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. - public Configuration(string globalConfigurationFileLocation = null, string systemConfigurationFileLocation = null) - : this(null, globalConfigurationFileLocation, systemConfigurationFileLocation) + public Configuration(string globalConfigurationFileLocation = null, string xdgConfigurationFileLocation = null, string systemConfigurationFileLocation = null) + : this(null, globalConfigurationFileLocation, xdgConfigurationFileLocation, systemConfigurationFileLocation) { } - /// - /// Determines if there is a local repository level Git configuration file. - /// - private bool HasLocalConfig - { - get { return localHandle != null; } - } - /// /// Determines if a Git configuration file specific to the current interactive user has been found. /// + [Obsolete("This property will be removed in the next release. Please use HasConfig() instead.")] public virtual bool HasGlobalConfig { - get { return globalConfigPath != null; } + get { return HasConfig(ConfigurationLevel.Global); } } /// /// Determines if a system-wide Git configuration file has been found. /// + [Obsolete("This property will be removed in the next release. Please use HasConfig() instead.")] public virtual bool HasSystemConfig { - get { return systemConfigPath != null; } + get { return HasConfig(ConfigurationLevel.System); } } - internal ConfigurationSafeHandle LocalHandle + /// + /// Determines which configuration file has been found. + /// + public virtual bool HasConfig(ConfigurationLevel level) { - get { return localHandle; } + using (ConfigurationSafeHandle handle = RetrieveConfigurationHandle(level, false)) + { + return handle != null; + } } #region IDisposable Members @@ -136,40 +135,34 @@ public void Dispose() /// The configuration file which should be considered as the target of this operation public virtual void Unset(string key, ConfigurationLevel level = ConfigurationLevel.Local) { - ConfigurationSafeHandle h = RetrieveConfigurationHandle(level); - - bool success = Proxy.git_config_delete(h, key); + Ensure.ArgumentNotNullOrEmptyString(key, "key"); - if (success) + using (ConfigurationSafeHandle h = RetrieveConfigurationHandle(level, true)) { - Save(); + Proxy.git_config_delete(h, key); } } - /// - /// Delete a configuration variable (key and value). - /// - /// The key to delete. - /// The configuration file which should be considered as the target of this operation - [Obsolete("This method will be removed in the next release. Please use Unset() instead.")] - public void Delete(string key, ConfigurationLevel level = ConfigurationLevel.Local) - { - Unset(key, level); - } - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// protected virtual void Dispose(bool disposing) { - localHandle.SafeDispose(); - globalHandle.SafeDispose(); - systemHandle.SafeDispose(); + configHandle.SafeDispose(); } /// /// Get a configuration value for a key. Keys are in the form 'section.name'. /// + /// The same escalation logic than in git.git will be used when looking for the key in the config files: + /// - local: the Git file in the current repository + /// - global: the Git file specific to the current interactive user (usually in `$HOME/.gitconfig`) + /// - XDG: another Git file specific to the current interactive user (usually in `$HOME/.config/git/config`) + /// - system: the system-wide Git file + /// + /// The first occurence of the key will be returned. + /// + /// /// For example in order to get the value for this in a .git\config file: /// /// @@ -180,98 +173,24 @@ protected virtual void Dispose(bool disposing) /// You would call: /// /// - /// bool isBare = repo.Config.Get<bool>("core.bare", false); + /// bool isBare = repo.Config.Get<bool>("core.bare").Value; /// /// /// /// The configuration value type /// The key - /// The default value - /// The configuration value, or defaultValue if not set - public virtual T Get(string key, T defaultValue) + /// The , or null if not set + public ConfigurationEntry Get(string key) { Ensure.ArgumentNotNullOrEmptyString(key, "key"); - if (!configurationTypedRetriever.ContainsKey(typeof(T))) - { - throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Generic Argument of type '{0}' is not supported.", typeof(T).FullName)); - } - - ConfigurationSafeHandle handle = (LocalHandle ?? globalHandle) ?? systemHandle; - if (handle == null) - { - throw new LibGit2SharpException("Could not find a local, global or system level configuration."); - } - - return (T)configurationTypedRetriever[typeof(T)](key, defaultValue, handle); + return Proxy.git_config_get_entry(configHandle, key); } /// /// Get a configuration value for a key. Keys are in the form 'section.name'. /// /// For example in order to get the value for this in a .git\config file: - /// - /// - /// [core] - /// bare = true - /// - /// - /// You would call: - /// - /// - /// bool isBare = repo.Config.Get<bool>("core", "bare", false); - /// - /// - /// - /// The configuration value type - /// The first key part - /// The second key part - /// The default value - /// The configuration value, or defaultValue if not set - public virtual T Get(string firstKeyPart, string secondKeyPart, T defaultValue) - { - Ensure.ArgumentNotNull(firstKeyPart, "firstKeyPart"); - Ensure.ArgumentNotNull(secondKeyPart, "secondKeyPart"); - - return Get(new[] { firstKeyPart, secondKeyPart }, defaultValue); - } - - /// - /// Get a configuration value for the given key parts. - /// - /// For example in order to get the value for this in a .git\config file: - /// - /// - /// [difftool "kdiff3"] - /// path = c:/Program Files/KDiff3/kdiff3.exe - /// - /// - /// You would call: - /// - /// - /// string where = repo.Config.Get<string>("difftool", "kdiff3", "path", null); - /// - /// - /// - /// The configuration value type - /// The first key part - /// The second key part - /// The third key part - /// The default value - /// The configuration value, or defaultValue if not set - public virtual T Get(string firstKeyPart, string secondKeyPart, string thirdKeyPart, T defaultValue) - { - Ensure.ArgumentNotNull(firstKeyPart, "firstKeyPart"); - Ensure.ArgumentNotNull(secondKeyPart, "secondKeyPart"); - Ensure.ArgumentNotNull(thirdKeyPart, "secondKeyPart"); - - return Get(new[] { firstKeyPart, secondKeyPart, thirdKeyPart }, defaultValue); - } - - /// - /// Get a configuration value for the given key parts. - /// - /// For example in order to get the value for this in a .git\config file: /// /// /// [core] @@ -281,25 +200,27 @@ public virtual T Get(string firstKeyPart, string secondKeyPart, string thirdK /// You would call: /// /// - /// bool isBare = repo.Config.Get<bool>(new []{ "core", "bare" }, false); + /// bool isBare = repo.Config.Get<bool>("core.bare").Value; /// /// /// /// The configuration value type - /// The key parts - /// The default value - /// The configuration value, or defaultValue if not set - public virtual T Get(string[] keyParts, T defaultValue) + /// The key + /// The configuration file into which the key should be searched for + /// The , or null if not set + public ConfigurationEntry Get(string key, ConfigurationLevel level) { - Ensure.ArgumentNotNull(keyParts, "keyParts"); + Ensure.ArgumentNotNullOrEmptyString(key, "key"); - return Get(string.Join(".", keyParts), defaultValue); - } + using (ConfigurationSafeHandle handle = RetrieveConfigurationHandle(level, false)) + { + if (handle == null) + { + return null; + } - private void Save() - { - Dispose(true); - Init(); + return Proxy.git_config_get_entry(handle, key); + } } /// @@ -317,90 +238,45 @@ private void Save() /// /// The configuration value type /// The key parts - /// The default value + /// The value /// The configuration file which should be considered as the target of this operation public virtual void Set(string key, T value, ConfigurationLevel level = ConfigurationLevel.Local) { Ensure.ArgumentNotNullOrEmptyString(key, "key"); - ConfigurationSafeHandle h = RetrieveConfigurationHandle(level); - - if (!configurationTypedUpdater.ContainsKey(typeof(T))) + using (ConfigurationSafeHandle h = RetrieveConfigurationHandle(level, true)) { - throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Generic Argument of type '{0}' is not supported.", typeof(T).FullName)); - } + if (!configurationTypedUpdater.ContainsKey(typeof(T))) + { + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Generic Argument of type '{0}' is not supported.", typeof(T).FullName)); + } - configurationTypedUpdater[typeof(T)](key, value, h); - Save(); + configurationTypedUpdater[typeof(T)](key, value, h); + } } - private ConfigurationSafeHandle RetrieveConfigurationHandle(ConfigurationLevel level) + private ConfigurationSafeHandle RetrieveConfigurationHandle(ConfigurationLevel level, bool throwIfStoreHasNotBeenFound) { - if (level == ConfigurationLevel.Local && !HasLocalConfig) - { - throw new LibGit2SharpException("No local configuration file has been found. You must use ConfigurationLevel.Global when accessing configuration outside of repository."); - } - - if (level == ConfigurationLevel.Global && !HasGlobalConfig) + ConfigurationSafeHandle handle = null; + if (configHandle != null) { - throw new LibGit2SharpException("No global configuration file has been found."); + handle = Proxy.git_config_open_level(configHandle, level); } - if (level == ConfigurationLevel.System && !HasSystemConfig) + if (handle == null && throwIfStoreHasNotBeenFound) { - throw new LibGit2SharpException("No system configuration file has been found."); - } - - ConfigurationSafeHandle h; - - switch (level) - { - case ConfigurationLevel.Local: - h = localHandle; - break; - - case ConfigurationLevel.Global: - h = globalHandle; - break; - - case ConfigurationLevel.System: - h = systemHandle; - break; - - default: - throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Configuration level has an unexpected value ('{0}').", level), "level"); + throw new LibGit2SharpException("No matching configuration file has been found."); } - return h; - } - private static Func GetRetriever(Func getter) - { - return (key, defaultValue, handle) => - { - T value = getter(handle, key); - if (Equals(value, default(T))) - { - return defaultValue; - } - - return value; - }; + return handle; } - private readonly IDictionary> configurationTypedRetriever = new Dictionary> - { - { typeof(int), GetRetriever(Proxy.git_config_get_int32) }, - { typeof(long), GetRetriever(Proxy.git_config_get_int64) }, - { typeof(bool), GetRetriever(Proxy.git_config_get_bool) }, - { typeof(string), GetRetriever(Proxy.git_config_get_string) }, - }; - private static Action GetUpdater(Action setter) { return (key, val, handle) => setter(handle, key, (T)val); } - private readonly IDictionary> configurationTypedUpdater = new Dictionary> + private readonly static IDictionary> configurationTypedUpdater = new Dictionary> { { typeof(int), GetUpdater(Proxy.git_config_set_int32) }, { typeof(long), GetUpdater(Proxy.git_config_set_int64) }, @@ -408,22 +284,32 @@ private static Action GetUpdater(Act { typeof(string), GetUpdater(Proxy.git_config_set_string) }, }; + IEnumerator> IEnumerable>.GetEnumerator() + { + return BuildConfigEntries().Cast>().GetEnumerator(); + } + + [Obsolete("This method will be removed in the next release. Please use a different overload instead.")] IEnumerator IEnumerable.GetEnumerator() { - return Proxy.git_config_foreach(LocalHandle, - (entryPtr) => - { - var entry = (GitConfigEntry)Marshal.PtrToStructure(entryPtr, typeof(GitConfigEntry)); - return new ConfigurationEntry(Utf8Marshaler.FromNative(entry.namePtr), - Utf8Marshaler.FromNative(entry.valuePtr), - (ConfigurationLevel) entry.level); - }) - .GetEnumerator(); + return BuildConfigEntries().GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return ((IEnumerable)this).GetEnumerator(); + return ((IEnumerable>)this).GetEnumerator(); + } + + private ICollection BuildConfigEntries() + { + return Proxy.git_config_foreach(configHandle, entryPtr => + { + var entry = (GitConfigEntry)Marshal.PtrToStructure(entryPtr, typeof(GitConfigEntry)); + + return new ConfigurationEntry(Utf8Marshaler.FromNative(entry.namePtr), + Utf8Marshaler.FromNative(entry.valuePtr), + (ConfigurationLevel)entry.level); + }); } } } diff --git a/LibGit2Sharp/ConfigurationEntry.cs b/LibGit2Sharp/ConfigurationEntry.cs index af3a6b83a..4b147d7bb 100644 --- a/LibGit2Sharp/ConfigurationEntry.cs +++ b/LibGit2Sharp/ConfigurationEntry.cs @@ -1,20 +1,22 @@ -namespace LibGit2Sharp +using System; + +namespace LibGit2Sharp { /// - /// An enumerated configuration entry. + /// The full representation of a config option. /// - public class ConfigurationEntry + /// The configuration value type + public class ConfigurationEntry { /// - /// The option name. + /// The fully-qualified option name. /// public string Key { get; private set; } /// /// The option value. /// - public string Value { get; private set; } - + public T Value { get; private set; } /// /// The origin store. @@ -22,16 +24,32 @@ public class ConfigurationEntry public ConfigurationLevel Level { get; private set; } /// - /// Initializes a new instance of the class with a given key and value + /// Initializes a new instance of the class with a given key and value /// /// The option name - /// The option value, as a string + /// The option value /// The origin store - public ConfigurationEntry(string key, string value, ConfigurationLevel level) + public ConfigurationEntry(string key, T value, ConfigurationLevel level) { Key = key; Value = value; Level = level; } } + + /// + /// Enumerated config option + /// + [Obsolete("This class will be removed in the next release. Please use ConfigurationEntry instead.")] + public class ConfigurationEntry : ConfigurationEntry + { + /// + /// Initializes a new instance of the class with a given key and value + /// + /// The option name + /// The option value + /// The origin store + public ConfigurationEntry(string key, string value, ConfigurationLevel level) : base(key, value, level) + { } + } } diff --git a/LibGit2Sharp/ConfigurationExtensions.cs b/LibGit2Sharp/ConfigurationExtensions.cs new file mode 100644 index 000000000..43c3900fe --- /dev/null +++ b/LibGit2Sharp/ConfigurationExtensions.cs @@ -0,0 +1,214 @@ +using System; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// Provides helper overloads to a . + /// + public static class ConfigurationExtensions + { + /// + /// Delete a configuration variable (key and value). + /// + /// The configuration being worked with. + /// The key to delete. + /// The configuration file which should be considered as the target of this operation + [Obsolete("This method will be removed in the next release. Please use Unset() instead.")] + public static void Delete(this Configuration config, string key, + ConfigurationLevel level = ConfigurationLevel.Local) + { + Ensure.ArgumentNotNullOrEmptyString(key, "key"); + + config.Unset(key, level); + } + + /// + /// Get a configuration value for a key. Keys are in the form 'section.name'. + /// + /// For example in order to get the value for this in a .git\config file: + /// + /// + /// [core] + /// bare = true + /// + /// + /// You would call: + /// + /// + /// bool isBare = repo.Config.Get<bool>("core.bare", false); + /// + /// + /// + /// The configuration value type + /// The configuration being worked with. + /// The key + /// The default value + /// The configuration value, or defaultValue if not set + [Obsolete("This method will be removed in the next release. Please use a different overload instead.")] + public static T Get(this Configuration config, string key, T defaultValue) + { + Ensure.ArgumentNotNullOrEmptyString(key, "key"); + + var val = config.Get(key); + + return val == null ? defaultValue : val.Value; + } + + /// + /// Get a configuration value for a key. Keys are in the form 'section.name'. + /// + /// For example in order to get the value for this in a .git\config file: + /// + /// + /// [core] + /// bare = true + /// + /// + /// You would call: + /// + /// + /// bool isBare = repo.Config.Get<bool>("core", "bare", false); + /// + /// + /// + /// The configuration value type + /// The configuration being worked with. + /// The first key part + /// The second key part + /// The default value + /// The configuration value, or defaultValue if not set + [Obsolete("This method will be removed in the next release. Please use a different overload instead.")] + public static T Get(this Configuration config, string firstKeyPart, string secondKeyPart, T defaultValue) + { + Ensure.ArgumentNotNullOrEmptyString(firstKeyPart, "firstKeyPart"); + Ensure.ArgumentNotNullOrEmptyString(secondKeyPart, "secondKeyPart"); + + return config.Get(new[] { firstKeyPart, secondKeyPart }, defaultValue); + } + + /// + /// Get a configuration value for the given key parts. + /// + /// For example in order to get the value for this in a .git\config file: + /// + /// + /// [difftool "kdiff3"] + /// path = c:/Program Files/KDiff3/kdiff3.exe + /// + /// + /// You would call: + /// + /// + /// string where = repo.Config.Get<string>("difftool", "kdiff3", "path", null); + /// + /// + /// + /// The configuration value type + /// The configuration being worked with. + /// The first key part + /// The second key part + /// The third key part + /// The default value + /// The configuration value, or defaultValue if not set + [Obsolete("This method will be removed in the next release. Please use a different overload instead.")] + public static T Get(this Configuration config, string firstKeyPart, string secondKeyPart, string thirdKeyPart, T defaultValue) + { + Ensure.ArgumentNotNullOrEmptyString(firstKeyPart, "firstKeyPart"); + Ensure.ArgumentNotNullOrEmptyString(secondKeyPart, "secondKeyPart"); + Ensure.ArgumentNotNullOrEmptyString(thirdKeyPart, "secondKeyPart"); + + return config.Get(new[] { firstKeyPart, secondKeyPart, thirdKeyPart }, defaultValue); + } + + /// + /// Get a configuration value for the given key parts. + /// + /// For example in order to get the value for this in a .git\config file: + /// + /// + /// [core] + /// bare = true + /// + /// + /// You would call: + /// + /// + /// bool isBare = repo.Config.Get<bool>(new []{ "core", "bare" }, false); + /// + /// + /// + /// The configuration value type + /// The configuration being worked with. + /// The key parts + /// The default value + /// The configuration value, or defaultValue if not set + [Obsolete("This method will be removed in the next release. Please use a different overload instead.")] + public static T Get(this Configuration config, string[] keyParts, T defaultValue) + { + Ensure.ArgumentNotNull(keyParts, "keyParts"); + + return config.Get(string.Join(".", keyParts), defaultValue); + } + + /// + /// Get a configuration value for the given key parts. + /// + /// For example in order to get the value for this in a .git\config file: + /// + /// + /// [core] + /// bare = true + /// + /// + /// You would call: + /// + /// + /// bool isBare = repo.Config.Get<bool>(new []{ "core", "bare" }).Value; + /// + /// + /// + /// The configuration value type + /// The configuration being worked with. + /// The key parts + /// The , or null if not set + public static ConfigurationEntry Get(this Configuration config, string[] keyParts) + { + Ensure.ArgumentNotNull(keyParts, "keyParts"); + + return config.Get(string.Join(".", keyParts)); + } + + /// + /// Get a configuration value for the given key parts. + /// + /// For example in order to get the value for this in a .git\config file: + /// + /// + /// [difftool "kdiff3"] + /// path = c:/Program Files/KDiff3/kdiff3.exe + /// + /// + /// You would call: + /// + /// + /// string where = repo.Config.Get<string>("difftool", "kdiff3", "path").Value; + /// + /// + /// + /// The configuration value type + /// The configuration being worked with. + /// The first key part + /// The second key part + /// The third key part + /// The , or null if not set + public static ConfigurationEntry Get(this Configuration config, string firstKeyPart, string secondKeyPart, string thirdKeyPart) + { + Ensure.ArgumentNotNullOrEmptyString(firstKeyPart, "firstKeyPart"); + Ensure.ArgumentNotNullOrEmptyString(secondKeyPart, "secondKeyPart"); + Ensure.ArgumentNotNullOrEmptyString(thirdKeyPart, "secondKeyPart"); + + return config.Get(new[] { firstKeyPart, secondKeyPart, thirdKeyPart }); + } + } +} diff --git a/LibGit2Sharp/ConfigurationLevel.cs b/LibGit2Sharp/ConfigurationLevel.cs index e4f798a57..b27af6d98 100644 --- a/LibGit2Sharp/ConfigurationLevel.cs +++ b/LibGit2Sharp/ConfigurationLevel.cs @@ -16,7 +16,7 @@ public enum ConfigurationLevel Global = 3, /// - /// The global ~/.config/git/config of the current user + /// The global ~/.config/git/config of the current user. /// XDG = 2, diff --git a/LibGit2Sharp/Core/GitCheckoutOpts.cs b/LibGit2Sharp/Core/GitCheckoutOpts.cs index 39a6f6ce8..7fbec7bed 100644 --- a/LibGit2Sharp/Core/GitCheckoutOpts.cs +++ b/LibGit2Sharp/Core/GitCheckoutOpts.cs @@ -3,11 +3,12 @@ namespace LibGit2Sharp.Core { - internal delegate int skipped_notify_cb( - IntPtr skipped_file, + internal delegate int conflict_cb( + IntPtr conflicting_path, ref GitOid blob_oid, - int file_mode, - IntPtr payload); + uint index_mode, + uint wd_mode, + IntPtr payload); internal delegate void progress_cb( IntPtr strPtr, @@ -23,8 +24,8 @@ internal class GitCheckoutOpts public int DirMode; public int FileMode; public int FileOpenFlags; - public skipped_notify_cb skippedNotifyCb; - public IntPtr NotifyPayload; + public conflict_cb conflictCb; + public IntPtr ConflictPayload; public progress_cb ProgressCb; public IntPtr ProgressPayload; public UnSafeNativeMethods.git_strarray paths; @@ -33,9 +34,51 @@ internal class GitCheckoutOpts [Flags] internal enum CheckoutStrategy { - GIT_CHECKOUT_DEFAULT = (1 << 0), - GIT_CHECKOUT_OVERWRITE_MODIFIED = (1 << 1), - GIT_CHECKOUT_CREATE_MISSING = (1 << 2), - GIT_CHECKOUT_REMOVE_UNTRACKED = (1 << 3), + GIT_CHECKOUT_DEFAULT = 0, /** default is a dry run, no actual updates */ + + /** Allow update of entries where working dir matches HEAD. */ + GIT_CHECKOUT_UPDATE_UNMODIFIED = (1 << 0), + + /** Allow update of entries where working dir does not have file. */ + GIT_CHECKOUT_UPDATE_MISSING = (1 << 1), + + /** Allow safe updates that cannot overwrite uncommited data */ + GIT_CHECKOUT_SAFE = + (GIT_CHECKOUT_UPDATE_UNMODIFIED | GIT_CHECKOUT_UPDATE_MISSING), + + /** Allow update of entries in working dir that are modified from HEAD. */ + GIT_CHECKOUT_UPDATE_MODIFIED = (1 << 2), + + /** Update existing untracked files that are now present in the index. */ + GIT_CHECKOUT_UPDATE_UNTRACKED = (1 << 3), + + /** Allow all updates to force working directory to look like index */ + GIT_CHECKOUT_FORCE = + (GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_MODIFIED | GIT_CHECKOUT_UPDATE_UNTRACKED), + + /** Allow checkout to make updates even if conflicts are found */ + GIT_CHECKOUT_ALLOW_CONFLICTS = (1 << 4), + + /** Remove untracked files not in index (that are not ignored) */ + GIT_CHECKOUT_REMOVE_UNTRACKED = (1 << 5), + + /** Only update existing files, don't create new ones */ + GIT_CHECKOUT_UPDATE_ONLY = (1 << 6), + + /** + * THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED + */ + + /** Allow checkout to skip unmerged files (NOT IMPLEMENTED) */ + GIT_CHECKOUT_SKIP_UNMERGED = (1 << 10), + /** For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED) */ + GIT_CHECKOUT_USE_OURS = (1 << 11), + /** For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED) */ + GIT_CHECKOUT_USE_THEIRS = (1 << 12), + + /** Recursively checkout submodules with same options (NOT IMPLEMENTED) */ + GIT_CHECKOUT_UPDATE_SUBMODULES = (1 << 16), + /** Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED) */ + GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = (1 << 17), } } diff --git a/LibGit2Sharp/Core/GitConfigEntry.cs b/LibGit2Sharp/Core/GitConfigEntry.cs index c6d614eaa..d8bd3751f 100644 --- a/LibGit2Sharp/Core/GitConfigEntry.cs +++ b/LibGit2Sharp/Core/GitConfigEntry.cs @@ -11,4 +11,3 @@ internal struct GitConfigEntry public uint level; } } - diff --git a/LibGit2Sharp/Core/GitDiff.cs b/LibGit2Sharp/Core/GitDiff.cs index a760b8032..3f6c9f456 100644 --- a/LibGit2Sharp/Core/GitDiff.cs +++ b/LibGit2Sharp/Core/GitDiff.cs @@ -6,16 +6,60 @@ namespace LibGit2Sharp.Core [Flags] internal enum GitDiffOptionFlags { + /** Normal diff, the default */ GIT_DIFF_NORMAL = 0, + /** Reverse the sides of the diff */ GIT_DIFF_REVERSE = (1 << 0), + /** Treat all files as text, disabling binary attributes & detection */ GIT_DIFF_FORCE_TEXT = (1 << 1), + /** Ignore all whitespace */ GIT_DIFF_IGNORE_WHITESPACE = (1 << 2), + /** Ignore changes in amount of whitespace */ GIT_DIFF_IGNORE_WHITESPACE_CHANGE = (1 << 3), + /** Ignore whitespace at end of line */ GIT_DIFF_IGNORE_WHITESPACE_EOL = (1 << 4), + /** Exclude submodules from the diff completely */ GIT_DIFF_IGNORE_SUBMODULES = (1 << 5), + /** Use the "patience diff" algorithm (currently unimplemented) */ GIT_DIFF_PATIENCE = (1 << 6), + /** Include ignored files in the diff list */ GIT_DIFF_INCLUDE_IGNORED = (1 << 7), + /** Include untracked files in the diff list */ GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8), + /** Include unmodified files in the diff list */ + GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9), + /** Even with the GIT_DIFF_INCLUDE_UNTRACKED flag, when an untracked + * directory is found, only a single entry for the directory is added + * to the diff list; with this flag, all files under the directory will + * be included, too. + */ + GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10), + /** If the pathspec is set in the diff options, this flags means to + * apply it as an exact match instead of as an fnmatch pattern. + */ + GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11), + /** Use case insensitive filename comparisons */ + GIT_DIFF_DELTAS_ARE_ICASE = (1 << 12), + /** When generating patch text, include the content of untracked files */ + GIT_DIFF_INCLUDE_UNTRACKED_CONTENT = (1 << 13), + /** Disable updating of the `binary` flag in delta records. This is + * useful when iterating over a diff if you don't need hunk and data + * callbacks and want to avoid having to load file completely. + */ + GIT_DIFF_SKIP_BINARY_CHECK = (1 << 14), + /** Normally, a type change between files will be converted into a + * DELETED record for the old and an ADDED record for the new; this + * options enabled the generation of TYPECHANGE delta records. + */ + GIT_DIFF_INCLUDE_TYPECHANGE = (1 << 15), + /** Even with GIT_DIFF_INCLUDE_TYPECHANGE, blob->tree changes still + * generally show as a DELETED blob. This flag tries to correctly + * label blob->tree transitions as TYPECHANGE records with new_file's + * mode set to tree. Note: the tree SHA will not be available. + */ + GIT_DIFF_INCLUDE_TYPECHANGE_TREES = (1 << 16), + /** Ignore file mode changes */ + GIT_DIFF_IGNORE_FILEMODE = (1 << 17), } [StructLayout(LayoutKind.Sequential)] diff --git a/LibGit2Sharp/Core/GitErrorCategory.cs b/LibGit2Sharp/Core/GitErrorCategory.cs index 81431d538..d8d06676f 100644 --- a/LibGit2Sharp/Core/GitErrorCategory.cs +++ b/LibGit2Sharp/Core/GitErrorCategory.cs @@ -18,5 +18,10 @@ internal enum GitErrorCategory Tag, Tree, Indexer, + Ssl, + Submodule, + Thread, + Stash, + Checkout, } } diff --git a/LibGit2Sharp/Core/GitErrorCode.cs b/LibGit2Sharp/Core/GitErrorCode.cs index 230ce17d6..9a6db9829 100644 --- a/LibGit2Sharp/Core/GitErrorCode.cs +++ b/LibGit2Sharp/Core/GitErrorCode.cs @@ -35,6 +35,16 @@ internal enum GitErrorCode /// BareRepo = -8, + /// + /// Operation cannot be performed against an orphaned HEAD. + /// + OrphanedHead = -9, + + /// + /// Operation cannot be performed against a not fully merged index. + /// + UnmergedEntries = -8, + /// /// Skip and passthrough the given ODB backend. /// diff --git a/LibGit2Sharp/Core/GitOdbBackend.cs b/LibGit2Sharp/Core/GitOdbBackend.cs index 65792ed52..19128bc4d 100644 --- a/LibGit2Sharp/Core/GitOdbBackend.cs +++ b/LibGit2Sharp/Core/GitOdbBackend.cs @@ -29,6 +29,7 @@ static GitOdbBackend() public readstream_callback ReadStream; public exists_callback Exists; public foreach_callback Foreach; + public IntPtr Writepack; public free_callback Free; /* The libgit2 structure definition ends here. Subsequent fields are for libgit2sharp bookkeeping. */ diff --git a/LibGit2Sharp/Core/Handles/GitConfigEntryHandle.cs b/LibGit2Sharp/Core/Handles/GitConfigEntryHandle.cs new file mode 100644 index 000000000..c5ac71ded --- /dev/null +++ b/LibGit2Sharp/Core/Handles/GitConfigEntryHandle.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core.Handles +{ + internal class GitConfigEntryHandle : NotOwnedSafeHandleBase + { + public GitConfigEntry MarshalAsGitConfigEntry() + { + return (GitConfigEntry)Marshal.PtrToStructure(handle, typeof(GitConfigEntry)); + } + } +} diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index 2cc767151..58e69fbbe 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -38,7 +38,7 @@ private static void GitInit() { // keep this in a separate method so mono can JIT the .cctor // and adjust the PATH before trying to load the native library - git_threads_init(); + Ensure.Success(git_threads_init()); } private static void ThreadsShutdown(object sender, EventArgs e) @@ -220,29 +220,14 @@ internal static extern int git_commit_create( internal static extern int git_config_find_system(byte[] system_config_path, uint length); [DllImport(libgit2)] - internal static extern void git_config_free(IntPtr cfg); - - [DllImport(libgit2)] - internal static extern int git_config_get_bool( - [MarshalAs(UnmanagedType.Bool)] out bool value, - ConfigurationSafeHandle cfg, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string name); + internal static extern int git_config_find_xdg(byte[] xdg_config_path, uint length); [DllImport(libgit2)] - internal static extern int git_config_get_int32( - out int value, - ConfigurationSafeHandle cfg, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string name); - - [DllImport(libgit2)] - internal static extern int git_config_get_int64( - out long value, - ConfigurationSafeHandle cfg, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string name); + internal static extern void git_config_free(IntPtr cfg); [DllImport(libgit2)] - internal static extern int git_config_get_string( - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] out string value, + internal static extern int git_config_get_entry( + out GitConfigEntryHandle entry, ConfigurationSafeHandle cfg, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string name); @@ -256,11 +241,32 @@ internal static extern int git_config_add_file_ondisk( [DllImport(libgit2)] internal static extern int git_config_new(out ConfigurationSafeHandle cfg); + [DllImport(libgit2)] + internal static extern int git_config_open_level( + out ConfigurationSafeHandle cfg, + ConfigurationSafeHandle parent, + uint level); + [DllImport(libgit2)] internal static extern int git_config_open_ondisk( out ConfigurationSafeHandle cfg, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(FilePathMarshaler))] FilePath path); + [DllImport(libgit2)] + internal static extern int git_config_parse_bool( + [MarshalAs(UnmanagedType.Bool)] out bool value, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string valueToParse); + + [DllImport(libgit2)] + internal static extern int git_config_parse_int32( + [MarshalAs(UnmanagedType.I4)] out int value, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string valueToParse); + + [DllImport(libgit2)] + internal static extern int git_config_parse_int64( + [MarshalAs(UnmanagedType.I8)] out long value, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string valueToParse); + [DllImport(libgit2)] internal static extern int git_config_set_bool( ConfigurationSafeHandle cfg, @@ -300,18 +306,19 @@ internal static extern int git_config_foreach( [DllImport(libgit2)] internal static extern int git_diff_tree_to_tree( + out DiffListSafeHandle diff, RepositorySafeHandle repo, - GitDiffOptions options, GitObjectSafeHandle oldTree, GitObjectSafeHandle newTree, - out DiffListSafeHandle diff); + GitDiffOptions options); [DllImport(libgit2)] internal static extern int git_diff_index_to_tree( + out DiffListSafeHandle diff, RepositorySafeHandle repo, - GitDiffOptions options, GitObjectSafeHandle oldTree, - out DiffListSafeHandle diff); + IndexSafeHandle index, + GitDiffOptions options); [DllImport(libgit2)] internal static extern int git_diff_merge( @@ -320,16 +327,17 @@ internal static extern int git_diff_merge( [DllImport(libgit2)] internal static extern int git_diff_workdir_to_index( + out DiffListSafeHandle diff, RepositorySafeHandle repo, - GitDiffOptions options, - out DiffListSafeHandle diff); + IndexSafeHandle index, + GitDiffOptions options); [DllImport(libgit2)] internal static extern int git_diff_workdir_to_tree( + out DiffListSafeHandle diff, RepositorySafeHandle repo, - GitDiffOptions options, GitObjectSafeHandle oldTree, - out DiffListSafeHandle diff); + GitDiffOptions options); internal delegate int git_diff_file_fn( IntPtr data, @@ -811,10 +819,10 @@ internal static extern int git_tag_delete( internal static extern OidSafeHandle git_tag_target_oid(GitObjectSafeHandle tag); [DllImport(libgit2)] - internal static extern GitObjectType git_tag_type(GitObjectSafeHandle tag); + internal static extern GitObjectType git_tag_target_type(GitObjectSafeHandle tag); [DllImport(libgit2)] - internal static extern void git_threads_init(); + internal static extern int git_threads_init(); [DllImport(libgit2)] internal static extern void git_threads_shutdown(); diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs index 179f0bc22..70196e9ef 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -311,11 +311,11 @@ public static ObjectId git_commit_tree_oid(GitObjectSafeHandle obj) #region git_config_ - public static void git_config_add_file_ondisk(ConfigurationSafeHandle config, FilePath path, uint level) + public static void git_config_add_file_ondisk(ConfigurationSafeHandle config, FilePath path, ConfigurationLevel level) { using (ThreadAffinity()) { - int res = NativeMethods.git_config_add_file_ondisk(config, path, level, true); + int res = NativeMethods.git_config_add_file_ondisk(config, path, (uint)level, true); Ensure.Success(res); } } @@ -346,96 +346,118 @@ public static string git_config_find_system() return ConvertPath(NativeMethods.git_config_find_system); } + public static string git_config_find_xdg() + { + return ConvertPath(NativeMethods.git_config_find_xdg); + } + public static void git_config_free(IntPtr config) { NativeMethods.git_config_free(config); } - public static bool? git_config_get_bool(ConfigurationSafeHandle config, string name) + public static ConfigurationEntry git_config_get_entry(ConfigurationSafeHandle config, string key) { + GitConfigEntryHandle handle; + + if (!configurationParser.ContainsKey(typeof(T))) + { + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Generic Argument of type '{0}' is not supported.", typeof(T).FullName)); + } + using (ThreadAffinity()) { - bool value; - var res = NativeMethods.git_config_get_bool(out value, config, name); + var res = NativeMethods.git_config_get_entry(out handle, config, key); if (res == (int)GitErrorCode.NotFound) { return null; } Ensure.Success(res); - return value; + } + + GitConfigEntry entry = handle.MarshalAsGitConfigEntry(); + + return new ConfigurationEntry(Utf8Marshaler.FromNative(entry.namePtr), + (T)configurationParser[typeof(T)](Utf8Marshaler.FromNative(entry.valuePtr)), + (ConfigurationLevel)entry.level); + } + + public static ConfigurationSafeHandle git_config_new() + { + using (ThreadAffinity()) + { + ConfigurationSafeHandle handle; + int res = NativeMethods.git_config_new(out handle); + Ensure.Success(res); + + return handle; } } - public static int? git_config_get_int32(ConfigurationSafeHandle config, string name) + public static ConfigurationSafeHandle git_config_open_level(ConfigurationSafeHandle parent, ConfigurationLevel level) { using (ThreadAffinity()) { - int value; - var res = NativeMethods.git_config_get_int32(out value, config, name); + ConfigurationSafeHandle handle; + int res = NativeMethods.git_config_open_level(out handle, parent, (uint)level); + if (res == (int)GitErrorCode.NotFound) { return null; } Ensure.Success(res); - return value; + + return handle; } } - public static long? git_config_get_int64(ConfigurationSafeHandle config, string name) + public static ConfigurationSafeHandle git_config_open_ondisk(FilePath path) { using (ThreadAffinity()) { - long value; - var res = NativeMethods.git_config_get_int64(out value, config, name); - if (res == (int)GitErrorCode.NotFound) - { - return null; - } - + ConfigurationSafeHandle handle; + int res = NativeMethods.git_config_open_ondisk(out handle, path); Ensure.Success(res); - return value; + + return handle; } } - public static string git_config_get_string(ConfigurationSafeHandle config, string name) + public static bool git_config_parse_bool(string value) { using (ThreadAffinity()) { - string value; - var res = NativeMethods.git_config_get_string(out value, config, name); - if (res == (int)GitErrorCode.NotFound) - { - return null; - } + bool outVal; + var res = NativeMethods.git_config_parse_bool(out outVal, value); Ensure.Success(res); - return value; + return outVal; } } - public static ConfigurationSafeHandle git_config_new() + public static int git_config_parse_int32(string value) { using (ThreadAffinity()) { - ConfigurationSafeHandle handle; - int res = NativeMethods.git_config_new(out handle); - Ensure.Success(res); + int outVal; + var res = NativeMethods.git_config_parse_int32(out outVal, value); - return handle; + Ensure.Success(res); + return outVal; } } - public static ConfigurationSafeHandle git_config_open_ondisk(FilePath path) + public static long git_config_parse_int64(string value) { using (ThreadAffinity()) { - ConfigurationSafeHandle handle; - int res = NativeMethods.git_config_open_ondisk(out handle, path); - Ensure.Success(res); + long outVal; + var res = NativeMethods.git_config_parse_int64(out outVal, value); - return handle; + Ensure.Success(res); + return outVal; } } @@ -506,14 +528,15 @@ public static void git_diff_blobs( public static DiffListSafeHandle git_diff_index_to_tree( RepositorySafeHandle repo, - GitDiffOptions options, - ObjectId oldTree) + IndexSafeHandle index, + ObjectId oldTree, + GitDiffOptions options) { using (ThreadAffinity()) using (var osw = new ObjectSafeWrapper(oldTree, repo)) { DiffListSafeHandle diff; - int res = NativeMethods.git_diff_index_to_tree(repo, options, osw.ObjectPtr, out diff); + int res = NativeMethods.git_diff_index_to_tree(out diff, repo, osw.ObjectPtr, index, options); Ensure.Success(res); return diff; @@ -545,16 +568,16 @@ public static void git_diff_print_patch(DiffListSafeHandle diff, NativeMethods.g public static DiffListSafeHandle git_diff_tree_to_tree( RepositorySafeHandle repo, - GitDiffOptions options, ObjectId oldTree, - ObjectId newTree) + ObjectId newTree, + GitDiffOptions options) { using (ThreadAffinity()) using (var osw1 = new ObjectSafeWrapper(oldTree, repo)) using (var osw2 = new ObjectSafeWrapper(newTree, repo)) { DiffListSafeHandle diff; - int res = NativeMethods.git_diff_tree_to_tree(repo, options, osw1.ObjectPtr, osw2.ObjectPtr, out diff); + int res = NativeMethods.git_diff_tree_to_tree(out diff, repo, osw1.ObjectPtr, osw2.ObjectPtr, options); Ensure.Success(res); return diff; @@ -563,12 +586,13 @@ public static DiffListSafeHandle git_diff_tree_to_tree( public static DiffListSafeHandle git_diff_workdir_to_index( RepositorySafeHandle repo, + IndexSafeHandle index, GitDiffOptions options) { using (ThreadAffinity()) { DiffListSafeHandle diff; - int res = NativeMethods.git_diff_workdir_to_index(repo, options, out diff); + int res = NativeMethods.git_diff_workdir_to_index(out diff, repo, index, options); Ensure.Success(res); return diff; @@ -577,14 +601,14 @@ public static DiffListSafeHandle git_diff_workdir_to_index( public static DiffListSafeHandle git_diff_workdir_to_tree( RepositorySafeHandle repo, - GitDiffOptions options, - ObjectId oldTree) + ObjectId oldTree, + GitDiffOptions options) { using (ThreadAffinity()) using (var osw = new ObjectSafeWrapper(oldTree, repo)) { DiffListSafeHandle diff; - int res = NativeMethods.git_diff_workdir_to_tree(repo, options, osw.ObjectPtr, out diff); + int res = NativeMethods.git_diff_workdir_to_tree(out diff, repo, osw.ObjectPtr, options); Ensure.Success(res); return diff; @@ -1572,9 +1596,9 @@ public static ObjectId git_tag_target_oid(GitObjectSafeHandle tag) return NativeMethods.git_tag_target_oid(tag).MarshalAsObjectId(); } - public static GitObjectType git_tag_type(GitObjectSafeHandle tag) + public static GitObjectType git_tag_target_type(GitObjectSafeHandle tag) { - return NativeMethods.git_tag_type(tag); + return NativeMethods.git_tag_target_type(tag); } #endregion @@ -1797,6 +1821,14 @@ public void Dispose() Thread.EndThreadAffinity(); } } + + private static readonly IDictionary> configurationParser = new Dictionary> + { + { typeof(int), value => git_config_parse_int32(value) }, + { typeof(long), value => git_config_parse_int64(value) }, + { typeof(bool), value => git_config_parse_bool(value) }, + { typeof(string), value => value }, + }; } } // ReSharper restore InconsistentNaming diff --git a/LibGit2Sharp/Diff.cs b/LibGit2Sharp/Diff.cs index 0e8b4742f..748fc13f2 100644 --- a/LibGit2Sharp/Diff.cs +++ b/LibGit2Sharp/Diff.cs @@ -82,7 +82,7 @@ public virtual TreeChanges Compare(Tree oldTree, Tree newTree, IEnumerable @@ -151,12 +151,12 @@ public virtual TreeChanges Compare(IEnumerable paths = null) private static TreeComparisonHandleRetriever WorkdirToIndex(Repository repo) { - return (h, o) => Proxy.git_diff_workdir_to_index(repo.Handle, o); + return (h, o) => Proxy.git_diff_workdir_to_index(repo.Handle, repo.Index.Handle, o); } private static TreeComparisonHandleRetriever WorkdirToTree(Repository repo) { - return (h, o) => Proxy.git_diff_workdir_to_tree(repo.Handle, o, h); + return (h, o) => Proxy.git_diff_workdir_to_tree(repo.Handle, h, o); } private static TreeComparisonHandleRetriever WorkdirAndIndexToTree(Repository repo) @@ -167,8 +167,8 @@ private static TreeComparisonHandleRetriever WorkdirAndIndexToTree(Repository re try { - diff = Proxy.git_diff_index_to_tree(repo.Handle, o, h); - diff2 = Proxy.git_diff_workdir_to_index(repo.Handle, o); + diff = Proxy.git_diff_index_to_tree(repo.Handle, repo.Index.Handle, h, o); + diff2 = Proxy.git_diff_workdir_to_index(repo.Handle, repo.Index.Handle, o); Proxy.git_diff_merge(diff, diff2); } catch @@ -189,7 +189,7 @@ private static TreeComparisonHandleRetriever WorkdirAndIndexToTree(Repository re private static TreeComparisonHandleRetriever IndexToTree(Repository repo) { - return (h, o) => Proxy.git_diff_index_to_tree(repo.Handle, o, h); + return (h, o) => Proxy.git_diff_index_to_tree(repo.Handle, repo.Index.Handle, h, o); } private static DiffListSafeHandle BuildDiffListFromTreeAndComparer(ObjectId treeId, TreeComparisonHandleRetriever comparisonHandleRetriever, GitDiffOptions options) diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index 9b2483f0a..feeb02e52 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -69,6 +69,7 @@ + @@ -76,6 +77,7 @@ + diff --git a/LibGit2Sharp/Repository.cs b/LibGit2Sharp/Repository.cs index c951a5602..63b817d82 100644 --- a/LibGit2Sharp/Repository.cs +++ b/LibGit2Sharp/Repository.cs @@ -55,6 +55,7 @@ public Repository(string path, RepositoryOptions options = null) Func indexBuilder = () => new Index(this); string configurationGlobalFilePath = null; + string configurationXDGFilePath = null; string configurationSystemFilePath = null; if (options != null) @@ -80,6 +81,7 @@ public Repository(string path, RepositoryOptions options = null) } configurationGlobalFilePath = options.GlobalConfigurationLocation; + configurationXDGFilePath = options.XDGConfigurationLocation; configurationSystemFilePath = options.SystemConfigurationLocation; } @@ -93,7 +95,7 @@ public Repository(string path, RepositoryOptions options = null) branches = new BranchCollection(this); tags = new TagCollection(this); info = new Lazy(() => new RepositoryInformation(this, isBare)); - config = new Lazy(() => RegisterForCleanup(new Configuration(this, configurationGlobalFilePath, configurationSystemFilePath))); + config = new Lazy(() => RegisterForCleanup(new Configuration(this, configurationGlobalFilePath, configurationXDGFilePath, configurationSystemFilePath))); remotes = new Lazy(() => new RemoteCollection(this)); odb = new Lazy(() => new ObjectDatabase(this)); diff = new Diff(this); @@ -420,7 +422,7 @@ public static Repository Clone(string sourceUrl, string workdirPath, { nativeOpts = new GitCheckoutOpts { - checkout_strategy = CheckoutStrategy.GIT_CHECKOUT_CREATE_MISSING, + checkout_strategy = CheckoutStrategy.GIT_CHECKOUT_SAFE, ProgressCb = CheckoutCallbacks.GenerateCheckoutCallbacks(onCheckoutProgress), }; } @@ -532,8 +534,7 @@ private void CheckoutHeadForce(CheckoutProgressHandler onCheckoutProgress) { GitCheckoutOpts options = new GitCheckoutOpts { - checkout_strategy = CheckoutStrategy.GIT_CHECKOUT_CREATE_MISSING | - CheckoutStrategy.GIT_CHECKOUT_OVERWRITE_MODIFIED | + checkout_strategy = CheckoutStrategy.GIT_CHECKOUT_FORCE | CheckoutStrategy.GIT_CHECKOUT_REMOVE_UNTRACKED, ProgressCb = CheckoutCallbacks.GenerateCheckoutCallbacks(onCheckoutProgress) }; diff --git a/LibGit2Sharp/RepositoryExtensions.cs b/LibGit2Sharp/RepositoryExtensions.cs index 32572ae1f..2acc25da0 100644 --- a/LibGit2Sharp/RepositoryExtensions.cs +++ b/LibGit2Sharp/RepositoryExtensions.cs @@ -173,15 +173,15 @@ public static void Fetch(this IRepository repository, string remoteName, private static Signature BuildSignatureFromGlobalConfiguration(IRepository repository, DateTimeOffset now) { - var name = repository.Config.Get("user.name", null); - var email = repository.Config.Get("user.email", null); + var name = repository.Config.Get("user.name"); + var email = repository.Config.Get("user.email"); if ((name == null) || (email == null)) { throw new LibGit2SharpException("Can not find Name and Email settings of the current user in Git configuration."); } - return new Signature(name, email, now); + return new Signature(name.Value, email.Value, now); } } } diff --git a/LibGit2Sharp/RepositoryOptions.cs b/LibGit2Sharp/RepositoryOptions.cs index dfb06e335..81c667d12 100644 --- a/LibGit2Sharp/RepositoryOptions.cs +++ b/LibGit2Sharp/RepositoryOptions.cs @@ -35,6 +35,15 @@ public class RepositoryOptions /// public string GlobalConfigurationLocation { get; set; } + /// + /// Overrides the probed location of the XDG configuration file of a repository. + /// + /// The path has either to lead to an existing valid configuration file, + /// or to a non existent configuration file which will be eventually created. + /// + /// + public string XDGConfigurationLocation { get; set; } + /// /// Overrides the probed location of the System configuration file of a repository. /// diff --git a/LibGit2Sharp/TagAnnotation.cs b/LibGit2Sharp/TagAnnotation.cs index 196b79147..9f1a9f781 100644 --- a/LibGit2Sharp/TagAnnotation.cs +++ b/LibGit2Sharp/TagAnnotation.cs @@ -24,7 +24,7 @@ internal TagAnnotation(Repository repo, ObjectId id) { lazyName = GitObjectLazyGroup.Singleton(repo, id, Proxy.git_tag_name); lazyTarget = GitObjectLazyGroup.Singleton(repo, id, - obj => GitObject.BuildFrom(repo, Proxy.git_tag_target_oid(obj), Proxy.git_tag_type(obj), null)); + obj => GitObject.BuildFrom(repo, Proxy.git_tag_target_oid(obj), Proxy.git_tag_target_type(obj), null)); group = new GitObjectLazyGroup(repo, id); lazyTagger = group.AddLazy(Proxy.git_tag_tagger); diff --git a/LibGit2Sharp/libgit2_hash.txt b/LibGit2Sharp/libgit2_hash.txt index 7153295e3..26d87c25d 100644 --- a/LibGit2Sharp/libgit2_hash.txt +++ b/LibGit2Sharp/libgit2_hash.txt @@ -1 +1 @@ -1e99ce9ac7d7a73f629327d020034e4b2ed1374c +54be4d57ed3cfbfb8c970f04422f840bcb731ddd diff --git a/libgit2 b/libgit2 index 1e99ce9ac..54be4d57e 160000 --- a/libgit2 +++ b/libgit2 @@ -1 +1 @@ -Subproject commit 1e99ce9ac7d7a73f629327d020034e4b2ed1374c +Subproject commit 54be4d57ed3cfbfb8c970f04422f840bcb731ddd