diff --git a/LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs b/LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs index 639d04e96..34e272bf1 100644 --- a/LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs +++ b/LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs @@ -283,7 +283,7 @@ public void CanCompareASubsetofTheTreeAgainstTheIndex() Tree tree = repo.Head.Tip.Tree; TreeChanges changes = repo.Diff.Compare(tree, DiffTargets.Index, - new[] { "deleted_staged_file.txt", "1/branch_file.txt", "I-do/not-exist" }); + new[] { "deleted_staged_file.txt", "1/branch_file.txt" }); Assert.NotNull(changes); @@ -292,6 +292,42 @@ public void CanCompareASubsetofTheTreeAgainstTheIndex() } } + private static void AssertCanCompareASubsetOfTheTreeAgainstTheIndex(TreeChanges changes) + { + Assert.NotNull(changes); + Assert.Equal(1, changes.Count()); + Assert.Equal("deleted_staged_file.txt", changes.Deleted.Single().Path); + } + + [Fact] + public void CanCompareASubsetofTheTreeAgainstTheIndexWithLaxExplicitPathsValidationAndANonExistentPath() + { + using (var repo = new Repository(StandardTestRepoPath)) + { + Tree tree = repo.Head.Tip.Tree; + + TreeChanges changes = repo.Diff.Compare(tree, DiffTargets.Index, + new[] { "deleted_staged_file.txt", "1/branch_file.txt", "I-do/not-exist" }, new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false }); + AssertCanCompareASubsetOfTheTreeAgainstTheIndex(changes); + + changes = repo.Diff.Compare(tree, DiffTargets.Index, + new[] { "deleted_staged_file.txt", "1/branch_file.txt", "I-do/not-exist" }); + AssertCanCompareASubsetOfTheTreeAgainstTheIndex(changes); + } + } + + [Fact] + public void ComparingASubsetofTheTreeAgainstTheIndexWithStrictExplicitPathsValidationAndANonExistentPathThrows() + { + using (var repo = new Repository(StandardTestRepoPath)) + { + Tree tree = repo.Head.Tip.Tree; + + Assert.Throws(() => repo.Diff.Compare(tree, DiffTargets.Index, + new[] { "deleted_staged_file.txt", "1/branch_file.txt", "I-do/not-exist" }, new ExplicitPathsOptions())); + } + } + [Fact] /* * $ git init . diff --git a/LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs b/LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs index 493aeb4e4..f291cd6dd 100644 --- a/LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs +++ b/LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs @@ -87,7 +87,7 @@ public void CanCompareASubsetofTheTreeAgainstOneOfItsAncestor() Tree tree = repo.Head.Tip.Tree; Tree ancestor = repo.Lookup("9fd738e").Tree; - TreeChanges changes = repo.Diff.Compare(ancestor, tree, new[]{ "1", "2/" }); + TreeChanges changes = repo.Diff.Compare(ancestor, tree, new[]{ "1" }); Assert.NotNull(changes); Assert.Equal(1, changes.Count()); @@ -133,6 +133,38 @@ public void CanCompareACommitTreeAgainstATreeWithNoCommonAncestor() } } + [Fact] + public void CanCompareATreeAgainstAnotherTreeWithLaxExplicitPathsValidationAndNonExistentPath() + { + using (var repo = new Repository(StandardTestRepoPath)) + { + Tree commitTree = repo.Head.Tip.Tree; + Tree commitTreeWithDifferentAncestor = repo.Branches["refs/remotes/origin/test"].Tip.Tree; + + TreeChanges changes = repo.Diff.Compare(commitTreeWithDifferentAncestor, commitTree, + new[] { "if-I-exist-this-test-is-really-unlucky.txt" }, new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false }); + Assert.Equal(0, changes.Count()); + + changes = repo.Diff.Compare(commitTreeWithDifferentAncestor, commitTree, + new[] { "if-I-exist-this-test-is-really-unlucky.txt" }); + Assert.Equal(0, changes.Count()); + } + } + + [Fact] + public void ComparingATreeAgainstAnotherTreeWithStrictExplicitPathsValidationThrows() + { + using (var repo = new Repository(StandardTestRepoPath)) + { + Tree commitTree = repo.Head.Tip.Tree; + Tree commitTreeWithDifferentAncestor = repo.Branches["refs/remotes/origin/test"].Tip.Tree; + + Assert.Throws(() => + repo.Diff.Compare(commitTreeWithDifferentAncestor, commitTree, + new[] { "if-I-exist-this-test-is-really-unlucky.txt" }, new ExplicitPathsOptions())); + } + } + /* * $ git diff -M f8d44d7..4be51d6 * diff --git a/my-name-does-not-feel-right.txt b/super-file.txt diff --git a/LibGit2Sharp.Tests/DiffWorkdirToIndexFixture.cs b/LibGit2Sharp.Tests/DiffWorkdirToIndexFixture.cs index 4cc07306b..5232ef2a1 100644 --- a/LibGit2Sharp.Tests/DiffWorkdirToIndexFixture.cs +++ b/LibGit2Sharp.Tests/DiffWorkdirToIndexFixture.cs @@ -1,6 +1,10 @@ +using System; +using System.IO; using System.Linq; +using System.Text; using LibGit2Sharp.Tests.TestHelpers; using Xunit; +using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -36,6 +40,129 @@ public void CanCompareTheWorkDirAgainstTheIndex() } } + [Theory] + [InlineData("new_untracked_file.txt", FileStatus.Untracked)] + [InlineData("really-i-cant-exist.txt", FileStatus.Nonexistent)] + public void CanCompareTheWorkDirAgainstTheIndexWithLaxUnmatchedExplicitPathsValidation(string relativePath, FileStatus currentStatus) + { + using (var repo = new Repository(StandardTestRepoPath)) + { + Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + + TreeChanges changes = repo.Diff.Compare(new[] { relativePath }, false, new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false }); + Assert.Equal(0, changes.Count()); + + changes = repo.Diff.Compare(new[] { relativePath }); + Assert.Equal(0, changes.Count()); + } + } + + [Theory] + [InlineData("new_untracked_file.txt", FileStatus.Untracked)] + [InlineData("really-i-cant-exist.txt", FileStatus.Nonexistent)] + public void ComparingTheWorkDirAgainstTheIndexWithStrictUnmatchedExplicitPathsValidationAndANonExistentPathspecThrows(string relativePath, FileStatus currentStatus) + { + using (var repo = new Repository(StandardTestRepoPath)) + { + Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + + Assert.Throws(() => repo.Diff.Compare(new[] { relativePath }, false, new ExplicitPathsOptions())); + } + } + + [Theory] + [InlineData("new_untracked_file.txt", FileStatus.Untracked)] + [InlineData("where-am-I.txt", FileStatus.Nonexistent)] + public void CallbackForUnmatchedExplicitPathsIsCalledWhenSet(string relativePath, FileStatus currentStatus) + { + var callback = new AssertUnmatchedPathspecsCallbackIsCalled(); + + using (var repo = new Repository(StandardTestRepoPath)) + { + Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + + repo.Diff.Compare(new[] { relativePath }, false, new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false, + OnUnmatchedPath = callback.OnUnmatchedPath }); + + Assert.True(callback.WasCalled); + } + } + + private class AssertUnmatchedPathspecsCallbackIsCalled + { + public bool WasCalled; + + public void OnUnmatchedPath(string unmatchedpath) + { + WasCalled = true; + } + } + + [Fact] + public void ComparingReliesOnProvidedConfigEntriesIfAny() + { + const string file = "1/branch_file.txt"; + + string path = CloneStandardTestRepo(); + using (var repo = new Repository(path)) + { + TreeEntry entry = repo.Head[file]; + Assert.Equal(Mode.ExecutableFile, entry.Mode); + + // Recreate the file in the workdir without the executable bit + string fullpath = Path.Combine(repo.Info.WorkingDirectory, file); + File.Delete(fullpath); + File.WriteAllBytes(fullpath, ((Blob)(entry.Target)).Content); + + // Unset the local core.filemode, if any. + repo.Config.Unset("core.filemode", ConfigurationLevel.Local); + } + + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + + var options = BuildFakeSystemConfigFilemodeOption(scd, true); + + using (var repo = new Repository(path, options)) + { + TreeChanges changes = repo.Diff.Compare(new[] { file }); + + Assert.Equal(1, changes.Count()); + + var change = changes.Modified.Single(); + Assert.Equal(Mode.ExecutableFile, change.OldMode); + Assert.Equal(Mode.NonExecutableFile, change.Mode); + } + + options = BuildFakeSystemConfigFilemodeOption(scd, false); + + using (var repo = new Repository(path, options)) + { + TreeChanges changes = repo.Diff.Compare(new[] { file }); + + Assert.Equal(0, changes.Count()); + } + } + + private RepositoryOptions BuildFakeSystemConfigFilemodeOption( + SelfCleaningDirectory scd, + bool value) + { + Directory.CreateDirectory(scd.DirectoryPath); + + var options = new RepositoryOptions + { + SystemConfigurationLocation = Path.Combine( + scd.RootedDirectoryPath, "fake-system.config") + }; + + StringBuilder sb = new StringBuilder() + .AppendFormat("[core]{0}", Environment.NewLine) + .AppendFormat("filemode = {1}{0}", Environment.NewLine, value); + File.WriteAllText(options.SystemConfigurationLocation, sb.ToString()); + + return options; + } + [Fact] public void CanCompareTheWorkDirAgainstTheIndexWithUntrackedFiles() { diff --git a/LibGit2Sharp.Tests/MetaFixture.cs b/LibGit2Sharp.Tests/MetaFixture.cs index 8cfb5014c..91ca9bbad 100644 --- a/LibGit2Sharp.Tests/MetaFixture.cs +++ b/LibGit2Sharp.Tests/MetaFixture.cs @@ -18,6 +18,7 @@ public class MetaFixture typeof(Repository), typeof(RepositoryOptions), typeof(Signature), + typeof(ExplicitPathsOptions), }; // Related to https://github.com/libgit2/libgit2sharp/pull/251 diff --git a/LibGit2Sharp.Tests/ResetIndexFixture.cs b/LibGit2Sharp.Tests/ResetIndexFixture.cs index 39ac16486..8b3a1e2f2 100644 --- a/LibGit2Sharp.Tests/ResetIndexFixture.cs +++ b/LibGit2Sharp.Tests/ResetIndexFixture.cs @@ -124,16 +124,27 @@ public void CanResetTheIndexToASubsetOfTheContentOfACommitWithCommitishAsArgumen } [Fact] - public void CanResetTheIndexToASubsetOfTheContentOfACommitWithCommitAsArgument() + public void CanResetTheIndexToASubsetOfTheContentOfACommitWithCommitAsArgumentAndLaxUnmatchedExplicitPathsValidation() { string path = CloneStandardTestRepo(); using (var repo = new Repository(path)) { - repo.Reset(repo.Lookup("5b5b025"), new[] { "new.txt" }); + repo.Reset(repo.Lookup("5b5b025"), new[] { "new.txt", "non-existent-path-28.txt" }, + new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false }); Assert.Equal("a8233120f6ad708f843d861ce2b7228ec4e3dec6", repo.Index["README"].Id.Sha); Assert.Equal("fa49b077972391ad58037050f2a75f74e3671e92", repo.Index["new.txt"].Id.Sha); } } + + [Fact] + public void ResettingTheIndexToASubsetOfTheContentOfACommitWithCommitAsArgumentAndStrictUnmatchedPathspecsValidationThrows() + { + using (var repo = new Repository(CloneStandardTestRepo())) + { + Assert.Throws(() => + repo.Reset(repo.Lookup("5b5b025"), new[] { "new.txt", "non-existent-path-28.txt" }, new ExplicitPathsOptions())); + } + } } } diff --git a/LibGit2Sharp.Tests/StageFixture.cs b/LibGit2Sharp.Tests/StageFixture.cs index cef274687..e61d747a8 100644 --- a/LibGit2Sharp.Tests/StageFixture.cs +++ b/LibGit2Sharp.Tests/StageFixture.cs @@ -60,14 +60,46 @@ public void CanStageTheUpdationOfAStagedFile() [Theory] [InlineData("1/I-do-not-exist.txt", FileStatus.Nonexistent)] [InlineData("deleted_staged_file.txt", FileStatus.Removed)] - public void StagingAnUnknownFileThrows(string relativePath, FileStatus status) + public void StagingAnUnknownFileThrowsIfExplicitPath(string relativePath, FileStatus status) { using (var repo = new Repository(StandardTestRepoPath)) { Assert.Null(repo.Index[relativePath]); Assert.Equal(status, repo.Index.RetrieveStatus(relativePath)); - Assert.Throws(() => repo.Index.Stage(relativePath)); + Assert.Throws(() => repo.Index.Stage(relativePath, new ExplicitPathsOptions())); + } + } + + [Theory] + [InlineData("1/I-do-not-exist.txt", FileStatus.Nonexistent)] + [InlineData("deleted_staged_file.txt", FileStatus.Removed)] + public void CanStageAnUnknownFileWithLaxUnmatchedExplicitPathsValidation(string relativePath, FileStatus status) + { + using (var repo = new Repository(StandardTestRepoPath)) + { + Assert.Null(repo.Index[relativePath]); + Assert.Equal(status, repo.Index.RetrieveStatus(relativePath)); + + Assert.DoesNotThrow(() => repo.Index.Stage(relativePath)); + Assert.DoesNotThrow(() => repo.Index.Stage(relativePath, new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false })); + + Assert.Equal(status, repo.Index.RetrieveStatus(relativePath)); + } + } + + [Theory] + [InlineData("1/I-do-not-exist.txt", FileStatus.Nonexistent)] + [InlineData("deleted_staged_file.txt", FileStatus.Removed)] + public void StagingAnUnknownFileWithLaxExplicitPathsValidationDoesntThrow(string relativePath, FileStatus status) + { + using (var repo = new Repository(StandardTestRepoPath)) + { + Assert.Null(repo.Index[relativePath]); + Assert.Equal(status, repo.Index.RetrieveStatus(relativePath)); + + repo.Index.Stage(relativePath); + repo.Index.Stage(relativePath, new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false }); } } @@ -199,7 +231,7 @@ public void StagingANewFileWithAFullPathWhichEscapesOutOfTheWorkingDirThrows() } [Fact] - public void StageFileWithBadParamsThrows() + public void StagingFileWithBadParamsThrows() { using (var repo = new Repository(StandardTestRepoPath)) { @@ -209,5 +241,56 @@ public void StageFileWithBadParamsThrows() Assert.Throws(() => repo.Index.Stage(new string[] { null })); } } + + /* + * $ git status -s + * M 1/branch_file.txt + * M README + * M branch_file.txt + * D deleted_staged_file.txt + * D deleted_unstaged_file.txt + * M modified_staged_file.txt + * M modified_unstaged_file.txt + * M new.txt + * A new_tracked_file.txt + * ?? new_untracked_file.txt + * + * By passing "*" to Stage, the following files will be added/removed/updated from the index: + * - deleted_unstaged_file.txt : removed + * - modified_unstaged_file.txt : updated + * - new_untracked_file.txt : added + */ + [Theory] + [InlineData("*u*", 0)] + [InlineData("*", 0)] + [InlineData("1/*", 0)] + [InlineData("RE*", 0)] + [InlineData("d*", -1)] + [InlineData("*modified_unstaged*", 0)] + [InlineData("new_*file.txt", 1)] + public void CanStageWithPathspec(string relativePath, int expectedIndexCountVariation) + { + using (var repo = new Repository(CloneStandardTestRepo())) + { + int count = repo.Index.Count; + + repo.Index.Stage(relativePath); + + Assert.Equal(count + expectedIndexCountVariation, repo.Index.Count); + } + } + + [Fact] + public void CanStageWithMultiplePathspecs() + { + using (var repo = new Repository(CloneStandardTestRepo())) + { + int count = repo.Index.Count; + + repo.Index.Stage(new string[] { "*", "u*" }); + + Assert.Equal(count, repo.Index.Count); // 1 added file, 1 deleted file, so same count + } + } } } diff --git a/LibGit2Sharp.Tests/UnstageFixture.cs b/LibGit2Sharp.Tests/UnstageFixture.cs index 6f0fc32f3..524d8657f 100644 --- a/LibGit2Sharp.Tests/UnstageFixture.cs +++ b/LibGit2Sharp.Tests/UnstageFixture.cs @@ -65,11 +65,12 @@ public void CanStageAndUnstageAnIgnoredFile() [InlineData("1/branch_file.txt", FileStatus.Unaltered, true, FileStatus.Unaltered, true, 0)] [InlineData("deleted_unstaged_file.txt", FileStatus.Missing, true, FileStatus.Missing, true, 0)] [InlineData("modified_unstaged_file.txt", FileStatus.Modified, true, FileStatus.Modified, true, 0)] - [InlineData("new_untracked_file.txt", FileStatus.Untracked, false, FileStatus.Untracked, false, 0)] [InlineData("modified_staged_file.txt", FileStatus.Staged, true, FileStatus.Modified, true, 0)] [InlineData("new_tracked_file.txt", FileStatus.Added, true, FileStatus.Untracked, false, -1)] - [InlineData("where-am-I.txt", FileStatus.Nonexistent, false, FileStatus.Nonexistent, false, 0)] - public void CanUnStage(string relativePath, FileStatus currentStatus, bool doesCurrentlyExistInTheIndex, FileStatus expectedStatusOnceStaged, bool doesExistInTheIndexOnceStaged, int expectedIndexCountVariation) + [InlineData("deleted_staged_file.txt", FileStatus.Removed, false, FileStatus.Missing, true, 1)] + public void CanUnstage( + string relativePath, FileStatus currentStatus, bool doesCurrentlyExistInTheIndex, + FileStatus expectedStatusOnceStaged, bool doesExistInTheIndexOnceStaged, int expectedIndexCountVariation) { string path = CloneStandardTestRepo(); using (var repo = new Repository(path)) @@ -86,6 +87,33 @@ public void CanUnStage(string relativePath, FileStatus currentStatus, bool doesC } } + [Theory] + [InlineData("new_untracked_file.txt", FileStatus.Untracked)] + [InlineData("where-am-I.txt", FileStatus.Nonexistent)] + public void UnstagingUnknownPathsWithStrictUnmatchedExplicitPathsValidationThrows(string relativePath, FileStatus currentStatus) + { + using (var repo = new Repository(CloneStandardTestRepo())) + { + Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + + Assert.Throws(() => repo.Index.Unstage(relativePath, new ExplicitPathsOptions())); + } + } + + [Theory] + [InlineData("new_untracked_file.txt", FileStatus.Untracked)] + [InlineData("where-am-I.txt", FileStatus.Nonexistent)] + public void CanUnstageUnknownPathsWithLaxUnmatchedExplicitPathsValidation(string relativePath, FileStatus currentStatus) + { + using (var repo = new Repository(CloneStandardTestRepo())) + { + Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + + Assert.DoesNotThrow(() => repo.Index.Unstage(relativePath, new ExplicitPathsOptions() { ShouldFailOnUnmatchedPath = false })); + Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + } + } + [Fact] public void CanUnstageTheRemovalOfAFile() { @@ -125,6 +153,42 @@ public void CanUnstageUntrackedFileAgainstAnOrphanedHead() RepositoryStatus status = repo.Index.RetrieveStatus(); Assert.Equal(0, status.Staged.Count()); Assert.Equal(1, status.Untracked.Count()); + + Assert.Throws(() => repo.Index.Unstage("i-dont-exist", new ExplicitPathsOptions())); + } + } + + [Theory] + [InlineData("new_untracked_file.txt", FileStatus.Untracked)] + [InlineData("where-am-I.txt", FileStatus.Nonexistent)] + public void UnstagingUnknownPathsAgainstAnOrphanedHeadWithStrictUnmatchedExplicitPathsValidationThrows(string relativePath, FileStatus currentStatus) + { + using (var repo = new Repository(CloneStandardTestRepo())) + { + repo.Refs.UpdateTarget("HEAD", "refs/heads/orphaned"); + Assert.True(repo.Info.IsHeadOrphaned); + + Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + + Assert.Throws(() => repo.Index.Unstage(relativePath, new ExplicitPathsOptions())); + } + } + + [Theory] + [InlineData("new_untracked_file.txt", FileStatus.Untracked)] + [InlineData("where-am-I.txt", FileStatus.Nonexistent)] + public void CanUnstageUnknownPathsAgainstAnOrphanedHeadWithLaxUnmatchedExplicitPathsValidation(string relativePath, FileStatus currentStatus) + { + using (var repo = new Repository(CloneStandardTestRepo())) + { + repo.Refs.UpdateTarget("HEAD", "refs/heads/orphaned"); + Assert.True(repo.Info.IsHeadOrphaned); + + Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + + Assert.DoesNotThrow(() => repo.Index.Unstage(relativePath)); + Assert.DoesNotThrow(() => repo.Index.Unstage(relativePath, new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false })); + Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); } } diff --git a/LibGit2Sharp/Core/GitDiff.cs b/LibGit2Sharp/Core/GitDiff.cs index b6b7ae8b2..3eae44c6e 100644 --- a/LibGit2Sharp/Core/GitDiff.cs +++ b/LibGit2Sharp/Core/GitDiff.cs @@ -159,6 +159,12 @@ public void Dispose() } } + internal delegate int diff_notify_cb( + IntPtr diff_so_far, + IntPtr delta_to_add, + IntPtr matched_pathspec, + IntPtr payload); + [StructLayout(LayoutKind.Sequential)] internal class GitDiffOptions : IDisposable { @@ -174,7 +180,7 @@ internal class GitDiffOptions : IDisposable public GitStrArrayIn PathSpec; public Int64 MaxSize; - public IntPtr NotifyCallback; + public diff_notify_cb NotifyCallback; public IntPtr NotifyPayload; public void Dispose() diff --git a/LibGit2Sharp/Diff.cs b/LibGit2Sharp/Diff.cs index 288cd1930..a14071935 100644 --- a/LibGit2Sharp/Diff.cs +++ b/LibGit2Sharp/Diff.cs @@ -1,8 +1,11 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Text; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Compat; using LibGit2Sharp.Core.Handles; +using Environment = System.Environment; namespace LibGit2Sharp { @@ -16,7 +19,7 @@ public class Diff { private readonly Repository repo; - private GitDiffOptions BuildOptions(DiffOptions diffOptions, IEnumerable paths = null) + private static GitDiffOptions BuildOptions(DiffOptions diffOptions, FilePath[] filePaths = null, MatchedPathsAggregator matchedPathsAggregator = null) { var options = new GitDiffOptions(); @@ -30,17 +33,42 @@ private GitDiffOptions BuildOptions(DiffOptions diffOptions, IEnumerable GitDiffOptionFlags.GIT_DIFF_INCLUDE_UNTRACKED_CONTENT; } - if (paths == null) + if (diffOptions.HasFlag(DiffOptions.IncludeIgnored)) + { + options.Flags |= GitDiffOptionFlags.GIT_DIFF_INCLUDE_IGNORED; + } + + if (diffOptions.HasFlag(DiffOptions.IncludeUnmodified)) + { + options.Flags |= GitDiffOptionFlags.GIT_DIFF_INCLUDE_UNMODIFIED; + } + + if (diffOptions.HasFlag(DiffOptions.DisablePathspecMatch)) + { + options.Flags |= GitDiffOptionFlags.GIT_DIFF_DISABLE_PATHSPEC_MATCH; + } + + if (matchedPathsAggregator != null) + { + options.NotifyCallback = matchedPathsAggregator.OnGitDiffNotify; + } + + if (filePaths == null) { return options; } - options.PathSpec = GitStrArrayIn.BuildFrom(ToFilePaths(repo, paths)); + options.PathSpec = GitStrArrayIn.BuildFrom(filePaths); return options; } private static FilePath[] ToFilePaths(Repository repo, IEnumerable paths) { + if (paths == null) + { + return null; + } + var filePaths = new List(); foreach (string path in paths) @@ -78,22 +106,30 @@ internal Diff(Repository repo) /// The you want to compare from. /// The you want to compare to. /// The list of paths (either files or directories) that should be compared. + /// + /// If set, the passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// /// A containing the changes between the and the . - public virtual TreeChanges Compare(Tree oldTree, Tree newTree, IEnumerable paths = null) + public virtual TreeChanges Compare(Tree oldTree, Tree newTree, IEnumerable paths = null, ExplicitPathsOptions explicitPathsOptions = null) { - using(GitDiffOptions options = BuildOptions(DiffOptions.None, paths)) - using (DiffListSafeHandle diff = BuildDiffListFromTrees( - oldTree != null ? oldTree.Id : null, - newTree != null ? newTree.Id : null, - options)) + var comparer = TreeToTree(repo); + ObjectId oldTreeId = oldTree != null ? oldTree.Id : null; + ObjectId newTreeId = newTree != null ? newTree.Id : null; + var diffOptions = DiffOptions.None; + + if (explicitPathsOptions != null) { - return new TreeChanges(diff); + diffOptions |= DiffOptions.DisablePathspecMatch; + + if (explicitPathsOptions.ShouldFailOnUnmatchedPath || + explicitPathsOptions.OnUnmatchedPath != null) + { + diffOptions |= DiffOptions.IncludeUnmodified; + } } - } - private DiffListSafeHandle BuildDiffListFromTrees(ObjectId oldTree, ObjectId newTree, GitDiffOptions options) - { - return Proxy.git_diff_tree_to_tree(repo.Handle, oldTree, newTree, options); + return BuildTreeChangesFromComparer(oldTreeId, newTreeId, comparer, diffOptions, paths, explicitPathsOptions); } /// @@ -128,21 +164,31 @@ private static IDictionaryThe to compare from. /// The targets to compare to. /// The list of paths (either files or directories) that should be compared. + /// + /// If set, the passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// /// A containing the changes between the and the selected target. - public virtual TreeChanges Compare(Tree oldTree, DiffTargets diffTargets, IEnumerable paths = null) + public virtual TreeChanges Compare(Tree oldTree, DiffTargets diffTargets, IEnumerable paths = null, ExplicitPathsOptions explicitPathsOptions = null) { var comparer = handleRetrieverDispatcher[diffTargets](repo); + ObjectId oldTreeId = oldTree != null ? oldTree.Id : null; DiffOptions diffOptions = diffTargets.HasFlag(DiffTargets.WorkingDirectory) ? DiffOptions.IncludeUntracked : DiffOptions.None; - using (GitDiffOptions options = BuildOptions(diffOptions, paths)) - using (DiffListSafeHandle dl = BuildDiffListFromTreeAndComparer( - oldTree != null ? oldTree.Id : null, - comparer, options)) + if (explicitPathsOptions != null) { - return new TreeChanges(dl); + diffOptions |= DiffOptions.DisablePathspecMatch; + + if (explicitPathsOptions.ShouldFailOnUnmatchedPath || + explicitPathsOptions.OnUnmatchedPath != null) + { + diffOptions |= DiffOptions.IncludeUnmodified; + } } + + return BuildTreeChangesFromComparer(oldTreeId, null, comparer, diffOptions, paths, explicitPathsOptions); } /// @@ -150,39 +196,61 @@ public virtual TreeChanges Compare(Tree oldTree, DiffTargets diffTargets, IEnume /// /// The list of paths (either files or directories) that should be compared. /// If true, include untracked files from the working dir as additions. Otherwise ignore them. + /// + /// If set, the passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// /// A containing the changes between the working directory and the index. - public virtual TreeChanges Compare(IEnumerable paths = null, bool includeUntracked = false) + public virtual TreeChanges Compare(IEnumerable paths = null, bool includeUntracked = false, ExplicitPathsOptions explicitPathsOptions = null) + { + return Compare(includeUntracked ? DiffOptions.IncludeUntracked : DiffOptions.None, paths, explicitPathsOptions); + } + + internal virtual TreeChanges Compare(DiffOptions diffOptions, IEnumerable paths = null, + ExplicitPathsOptions explicitPathsOptions = null) { var comparer = WorkdirToIndex(repo); - using (GitDiffOptions options = BuildOptions(includeUntracked ? DiffOptions.IncludeUntracked : DiffOptions.None, paths)) - using (DiffListSafeHandle dl = BuildDiffListFromComparer(null, comparer, options)) + if (explicitPathsOptions != null) { - return new TreeChanges(dl); + diffOptions |= DiffOptions.DisablePathspecMatch; + + if (explicitPathsOptions.ShouldFailOnUnmatchedPath || + explicitPathsOptions.OnUnmatchedPath != null) + { + diffOptions |= DiffOptions.IncludeUnmodified; + } } + + return BuildTreeChangesFromComparer(null, null, comparer, diffOptions, paths, explicitPathsOptions); } - private delegate DiffListSafeHandle TreeComparisonHandleRetriever(ObjectId id, GitDiffOptions options); + private delegate DiffListSafeHandle TreeComparisonHandleRetriever(ObjectId oldTreeId, ObjectId newTreeId, GitDiffOptions options); + + private static TreeComparisonHandleRetriever TreeToTree(Repository repo) + { + return (oh, nh, o) => Proxy.git_diff_tree_to_tree(repo.Handle, oh, nh, o); + } private static TreeComparisonHandleRetriever WorkdirToIndex(Repository repo) { - return (h, o) => Proxy.git_diff_index_to_workdir(repo.Handle, repo.Index.Handle, o); + return (oh, nh, o) => Proxy.git_diff_index_to_workdir(repo.Handle, repo.Index.Handle, o); } private static TreeComparisonHandleRetriever WorkdirToTree(Repository repo) { - return (h, o) => Proxy.git_diff_tree_to_workdir(repo.Handle, h, o); + return (oh, nh, o) => Proxy.git_diff_tree_to_workdir(repo.Handle, oh, o); } private static TreeComparisonHandleRetriever WorkdirAndIndexToTree(Repository repo) { - TreeComparisonHandleRetriever comparisonHandleRetriever = (h, o) => + TreeComparisonHandleRetriever comparisonHandleRetriever = (oh, nh, o) => { DiffListSafeHandle diff = null, diff2 = null; try { - diff = Proxy.git_diff_tree_to_index(repo.Handle, repo.Index.Handle, h, o); + diff = Proxy.git_diff_tree_to_index(repo.Handle, repo.Index.Handle, oh, o); diff2 = Proxy.git_diff_index_to_workdir(repo.Handle, repo.Index.Handle, o); Proxy.git_diff_merge(diff, diff2); } @@ -204,17 +272,57 @@ private static TreeComparisonHandleRetriever WorkdirAndIndexToTree(Repository re private static TreeComparisonHandleRetriever IndexToTree(Repository repo) { - return (h, o) => Proxy.git_diff_tree_to_index(repo.Handle, repo.Index.Handle, h, o); + return (oh, nh, o) => Proxy.git_diff_tree_to_index(repo.Handle, repo.Index.Handle, oh, o); + } + + private TreeChanges BuildTreeChangesFromComparer( + ObjectId oldTreeId, ObjectId newTreeId, TreeComparisonHandleRetriever comparisonHandleRetriever, + DiffOptions diffOptions, IEnumerable paths = null, ExplicitPathsOptions explicitPathsOptions = null) + { + var matchedPaths = new MatchedPathsAggregator(); + var filePaths = ToFilePaths(repo, paths); + + using (GitDiffOptions options = BuildOptions(diffOptions, filePaths, matchedPaths)) + using (DiffListSafeHandle diffList = comparisonHandleRetriever(oldTreeId, newTreeId, options)) + { + if (explicitPathsOptions != null) + { + DispatchUnmatchedPaths(explicitPathsOptions, filePaths, matchedPaths); + } + + return new TreeChanges(diffList); + } } - private static DiffListSafeHandle BuildDiffListFromTreeAndComparer(ObjectId treeId, TreeComparisonHandleRetriever comparisonHandleRetriever, GitDiffOptions options) + private static void DispatchUnmatchedPaths(ExplicitPathsOptions explicitPathsOptions, + IEnumerable filePaths, + IEnumerable matchedPaths) { - return BuildDiffListFromComparer(treeId, comparisonHandleRetriever, options); + List unmatchedPaths = (filePaths != null ? + filePaths.Except(matchedPaths) : Enumerable.Empty()).ToList(); + + if (!unmatchedPaths.Any()) + { + return; + } + + if (explicitPathsOptions.OnUnmatchedPath != null) + { + unmatchedPaths.ForEach(filePath => explicitPathsOptions.OnUnmatchedPath(filePath.Native)); + } + + if (explicitPathsOptions.ShouldFailOnUnmatchedPath) + { + throw new UnmatchedPathException(BuildUnmatchedPathsMessage(unmatchedPaths)); + } } - private static DiffListSafeHandle BuildDiffListFromComparer(ObjectId treeId, TreeComparisonHandleRetriever comparisonHandleRetriever, GitDiffOptions options) + private static string BuildUnmatchedPathsMessage(List unmatchedPaths) { - return comparisonHandleRetriever(treeId, options); + var message = new StringBuilder("There were some unmatched paths:" + Environment.NewLine); + unmatchedPaths.ForEach(filePath => message.AppendFormat("- {0}{1}", filePath.Native, Environment.NewLine)); + + return message.ToString(); } } } diff --git a/LibGit2Sharp/DiffOptions.cs b/LibGit2Sharp/DiffOptions.cs index 133e5a40e..761a52a82 100644 --- a/LibGit2Sharp/DiffOptions.cs +++ b/LibGit2Sharp/DiffOptions.cs @@ -12,12 +12,29 @@ internal enum DiffOptions /// /// No special behavior. /// - None, + None = 0, /// /// Include untracked files among the files to be processed, when /// diffing against the working directory. /// - IncludeUntracked, + IncludeUntracked = (1 << 1), + + /// + /// Include unmodified files among the files to be processed, when + /// diffing against the working directory. + /// + IncludeUnmodified = (1 << 2), + + /// + /// Treats the passed pathspecs as explicit paths (no pathspec match). + /// + DisablePathspecMatch = (1 << 3), + + /// + /// Include ignored files among the files to be processed, when + /// diffing against the working directory. + /// + IncludeIgnored = (1 << 4), } } diff --git a/LibGit2Sharp/ExplicitPathsOptions.cs b/LibGit2Sharp/ExplicitPathsOptions.cs new file mode 100644 index 000000000..02e81c72d --- /dev/null +++ b/LibGit2Sharp/ExplicitPathsOptions.cs @@ -0,0 +1,37 @@ +using LibGit2Sharp.Handlers; + +namespace LibGit2Sharp +{ + /// + /// Allows callers to specify how unmatched paths should be handled + /// by operations such as Reset(), Compare(), Unstage(), ... + /// + /// By passing these options, the passed paths will be treated as + /// explicit paths, and NOT pathspecs containing globs. + /// + /// + public class ExplicitPathsOptions + { + /// + /// Associated paths will be treated as explicit paths. + /// + public ExplicitPathsOptions() + { + ShouldFailOnUnmatchedPath = true; + } + + /// + /// When set to true, the called operation will throw a when an unmatched + /// path is encountered. + /// + /// Set to true by default. + /// + /// + public bool ShouldFailOnUnmatchedPath { get; set; } + + /// + /// Sets a callback that will be called once for each unmatched path. + /// + public UnmatchedPathHandler OnUnmatchedPath { get; set; } + } +} diff --git a/LibGit2Sharp/Handlers.cs b/LibGit2Sharp/Handlers.cs index 96b64b9f2..eb2dc03f5 100644 --- a/LibGit2Sharp/Handlers.cs +++ b/LibGit2Sharp/Handlers.cs @@ -46,4 +46,13 @@ /// Number of completed steps. /// Total number of steps. public delegate void CheckoutProgressHandler(string path, int completedSteps, int totalSteps); + + /// + /// Delegate definition for unmatched path callback. + /// + /// This callback will be called to notify the caller of unmatched path. + /// + /// + /// The unmatched path. + public delegate void UnmatchedPathHandler(string unmatchedPath); } diff --git a/LibGit2Sharp/IRepository.cs b/LibGit2Sharp/IRepository.cs index 12f3f01aa..da0b00b80 100644 --- a/LibGit2Sharp/IRepository.cs +++ b/LibGit2Sharp/IRepository.cs @@ -125,7 +125,11 @@ public interface IRepository : IDisposable /// /// The target commit object. /// The list of paths (either files or directories) that should be considered. - void Reset(Commit commit, IEnumerable paths = null); + /// + /// If set, the passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + void Reset(Commit commit, IEnumerable paths = null, ExplicitPathsOptions explicitPathsOptions = null); /// /// Clean the working tree by removing files that are not under version control. diff --git a/LibGit2Sharp/Index.cs b/LibGit2Sharp/Index.cs index bd1d194bc..1732d45bb 100644 --- a/LibGit2Sharp/Index.cs +++ b/LibGit2Sharp/Index.cs @@ -128,51 +128,51 @@ IEnumerator IEnumerable.GetEnumerator() /// Promotes to the staging area the latest modifications of a file in the working directory (addition, updation or removal). /// /// The path of the file within the working directory. - public virtual void Stage(string path) + /// + /// If set, the passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + public virtual void Stage(string path, ExplicitPathsOptions explicitPathsOptions = null) { Ensure.ArgumentNotNull(path, "path"); - Stage(new[] { path }); + Stage(new[] { path }, explicitPathsOptions); } /// /// Promotes to the staging area the latest modifications of a collection of files in the working directory (addition, updation or removal). /// /// The collection of paths of the files within the working directory. - public virtual void Stage(IEnumerable paths) + /// + /// If set, the passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + public virtual void Stage(IEnumerable paths, ExplicitPathsOptions explicitPathsOptions = null) { - //TODO: Stage() should support following use cases: - // - Recursively staging the content of a directory + Ensure.ArgumentNotNull(paths, "paths"); - IEnumerable> batch = PrepareBatch(paths); + TreeChanges changes = repo.Diff.Compare(DiffOptions.IncludeUntracked | DiffOptions.IncludeIgnored, paths, explicitPathsOptions); - foreach (KeyValuePair kvp in batch) + foreach (var treeEntryChanges in changes) { - if (Directory.Exists(kvp.Key)) - { - throw new NotImplementedException(); - } - - if (!kvp.Value.HasFlag(FileStatus.Nonexistent)) + switch (treeEntryChanges.Status) { - continue; - } + case ChangeKind.Unmodified: + continue; - throw new LibGit2SharpException(string.Format(CultureInfo.InvariantCulture, "Can not stage '{0}'. The file does not exist.", kvp.Key)); - } + case ChangeKind.Deleted: + RemoveFromIndex(treeEntryChanges.Path); + continue; - foreach (KeyValuePair kvp in batch) - { - string relativePath = kvp.Key; - FileStatus fileStatus = kvp.Value; + case ChangeKind.Added: + /* Fall through */ + case ChangeKind.Modified: + AddToIndex(treeEntryChanges.Path); + continue; - if (fileStatus.HasFlag(FileStatus.Missing)) - { - RemoveFromIndex(relativePath); - } - else - { - AddToIndex(relativePath); + default: + throw new InvalidOperationException( + string.Format(CultureInfo.InvariantCulture, "Entry '{0}' bears an unexpected ChangeKind '{1}'", treeEntryChanges.Path, treeEntryChanges.Status)); } } @@ -183,30 +183,38 @@ public virtual void Stage(IEnumerable paths) /// Removes from the staging area all the modifications of a file since the latest commit (addition, updation or removal). /// /// The path of the file within the working directory. - public virtual void Unstage(string path) + /// + /// If set, the passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + public virtual void Unstage(string path, ExplicitPathsOptions explicitPathsOptions = null) { Ensure.ArgumentNotNull(path, "path"); - Unstage(new[] { path }); + Unstage(new[] { path }, explicitPathsOptions); } /// /// Removes from the staging area all the modifications of a collection of file since the latest commit (addition, updation or removal). /// /// The collection of paths of the files within the working directory. - public virtual void Unstage(IEnumerable paths) + /// + /// If set, the passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + public virtual void Unstage(IEnumerable paths, ExplicitPathsOptions explicitPathsOptions = null) { Ensure.ArgumentNotNull(paths, "paths"); if (repo.Info.IsHeadOrphaned) { - TreeChanges changes = repo.Diff.Compare(null, DiffTargets.Index, paths); + TreeChanges changes = repo.Diff.Compare(null, DiffTargets.Index, paths, explicitPathsOptions); Reset(changes); } else { - repo.Reset("HEAD", paths); + repo.Reset("HEAD", paths, explicitPathsOptions); } } @@ -449,6 +457,9 @@ internal void Reset(TreeChanges changes) { switch (treeEntryChanges.Status) { + case ChangeKind.Unmodified: + continue; + case ChangeKind.Added: RemoveFromIndex(treeEntryChanges.Path); continue; diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index 2475cf699..1eba31c0a 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -69,14 +69,17 @@ + + + diff --git a/LibGit2Sharp/MatchedPathsAggregator.cs b/LibGit2Sharp/MatchedPathsAggregator.cs new file mode 100644 index 000000000..4ae2e359f --- /dev/null +++ b/LibGit2Sharp/MatchedPathsAggregator.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + internal class MatchedPathsAggregator : IEnumerable + { + private readonly List matchedPaths = new List(); + + /// + /// The delegate with a signature that matches the native diff git_diff_notify_cb function's signature. + /// + /// The diff list so far, before the delta is inserted. + /// The delta that is being diffed + /// The pathsec that matched the path of the diffed files. + /// Payload object. + internal int OnGitDiffNotify(IntPtr diffListSoFar, IntPtr deltaToAdd, IntPtr matchedPathspec, IntPtr payload) + { + // Convert null strings into empty strings. + var path = (matchedPathspec != IntPtr.Zero) ? FilePathMarshaler.FromNative(matchedPathspec) : FilePath.Empty; + + if (matchedPaths.Contains(path)) + { + return 0; + } + + matchedPaths.Add(path); + return 0; + } + + public IEnumerator GetEnumerator() + { + return matchedPaths.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/LibGit2Sharp/Repository.cs b/LibGit2Sharp/Repository.cs index 1babaeadf..32768a470 100644 --- a/LibGit2Sharp/Repository.cs +++ b/LibGit2Sharp/Repository.cs @@ -641,7 +641,11 @@ public void Reset(ResetOptions resetOptions, Commit commit) /// /// The target commit object. /// The list of paths (either files or directories) that should be considered. - public void Reset(Commit commit, IEnumerable paths = null) + /// + /// If set, the passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + public void Reset(Commit commit, IEnumerable paths = null, ExplicitPathsOptions explicitPathsOptions = null) { if (Info.IsBare) { @@ -650,7 +654,7 @@ public void Reset(Commit commit, IEnumerable paths = null) Ensure.ArgumentNotNull(commit, "commit"); - TreeChanges changes = Diff.Compare(commit.Tree, DiffTargets.Index, paths); + TreeChanges changes = Diff.Compare(commit.Tree, DiffTargets.Index, paths, explicitPathsOptions); Index.Reset(changes); } diff --git a/LibGit2Sharp/RepositoryExtensions.cs b/LibGit2Sharp/RepositoryExtensions.cs index 25c5b703e..88c48087a 100644 --- a/LibGit2Sharp/RepositoryExtensions.cs +++ b/LibGit2Sharp/RepositoryExtensions.cs @@ -136,7 +136,11 @@ public static void Reset(this IRepository repository, ResetOptions resetOptions, /// The being worked with. /// A revparse spec for the target commit object. /// The list of paths (either files or directories) that should be considered. - public static void Reset(this IRepository repository, string committish = "HEAD", IEnumerable paths = null) + /// + /// If set, the passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + public static void Reset(this IRepository repository, string committish = "HEAD", IEnumerable paths = null, ExplicitPathsOptions explicitPathsOptions = null) { if (repository.Info.IsBare) { @@ -147,7 +151,7 @@ public static void Reset(this IRepository repository, string committish = "HEAD" Commit commit = LookUpCommit(repository, committish); - repository.Reset(commit, paths); + repository.Reset(commit, paths, explicitPathsOptions); } private static Commit LookUpCommit(IRepository repository, string committish) diff --git a/LibGit2Sharp/TreeChanges.cs b/LibGit2Sharp/TreeChanges.cs index b1e90daa7..0571d20e0 100644 --- a/LibGit2Sharp/TreeChanges.cs +++ b/LibGit2Sharp/TreeChanges.cs @@ -56,6 +56,11 @@ private int PrintCallBack(GitDiffDelta delta, GitDiffRange range, GitDiffLineOri string formattedoutput = Utf8Marshaler.FromNative(content, (int)contentlen); TreeEntryChanges currentChange = AddFileChange(delta, lineorigin); + if (currentChange == null) + { + return 0; + } + AddLineChange(currentChange, lineorigin); currentChange.AppendToPatch(formattedoutput); @@ -82,6 +87,11 @@ private void AddLineChange(Changes currentChange, GitDiffLineOrigin lineOrigin) private TreeEntryChanges AddFileChange(GitDiffDelta delta, GitDiffLineOrigin lineorigin) { + if (delta.Status == ChangeKind.Unmodified) + { + return null; + } + var newFilePath = FilePathMarshaler.FromNative(delta.NewFile.Path); if (lineorigin != GitDiffLineOrigin.GIT_DIFF_LINE_FILE_HDR) @@ -93,7 +103,7 @@ private TreeEntryChanges AddFileChange(GitDiffDelta delta, GitDiffLineOrigin lin var newOid = delta.NewFile.Oid; var oldOid = delta.OldFile.Oid; - if (delta.Status == ChangeKind.Untracked) + if (delta.Status == ChangeKind.Untracked || delta.Status == ChangeKind.Ignored) { delta.Status = ChangeKind.Added; } diff --git a/LibGit2Sharp/UnmatchedPathException.cs b/LibGit2Sharp/UnmatchedPathException.cs new file mode 100644 index 000000000..38f33c036 --- /dev/null +++ b/LibGit2Sharp/UnmatchedPathException.cs @@ -0,0 +1,54 @@ +using System; +using System.Runtime.Serialization; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// The exception that is thrown when an explicit path or a list of explicit paths could not be matched. + /// + [Serializable] + public class UnmatchedPathException : LibGit2SharpException + { + /// + /// Initializes a new instance of the class. + /// + public UnmatchedPathException() + { + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// A message that describes the error. + public UnmatchedPathException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. If the parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception. + public UnmatchedPathException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class with a serialized data. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected UnmatchedPathException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + internal UnmatchedPathException(string message, GitErrorCode code, GitErrorCategory category) + : base(message, code, category) + { + } + } +}