diff --git a/LibGit2Sharp.Tests/ConflictFixture.cs b/LibGit2Sharp.Tests/ConflictFixture.cs index 23ba4f451..9a98bd1b6 100644 --- a/LibGit2Sharp.Tests/ConflictFixture.cs +++ b/LibGit2Sharp.Tests/ConflictFixture.cs @@ -51,6 +51,8 @@ private static List RenameConflictData [InlineData(false, "ancestor-and-ours.txt", true, true, FileStatus.Removed |FileStatus.Untracked, 2)] [InlineData(true, "ancestor-and-theirs.txt", true, false, FileStatus.Nonexistent, 2)] [InlineData(false, "ancestor-and-theirs.txt", true, true, FileStatus.Untracked, 2)] + [InlineData(true, "ancestor-only.txt", false, false, FileStatus.Nonexistent, 1)] + [InlineData(false, "ancestor-only.txt", false, false, FileStatus.Nonexistent, 1)] [InlineData(true, "conflicts-one.txt", true, false, FileStatus.Removed, 3)] [InlineData(false, "conflicts-one.txt", true, true, FileStatus.Removed | FileStatus.Untracked, 3)] [InlineData(true, "conflicts-two.txt", true, false, FileStatus.Removed, 3)] @@ -61,13 +63,6 @@ private static List RenameConflictData [InlineData(false, "ours-only.txt", true, true, FileStatus.Removed | FileStatus.Untracked, 1)] [InlineData(true, "theirs-only.txt", true, false, FileStatus.Nonexistent, 1)] [InlineData(false, "theirs-only.txt", true, true, FileStatus.Untracked, 1)] - /* Conflicts clearing through Index.Remove() only works when a version of the entry exists in the workdir. - * This is because libgit2's git_iterator_for_index() seem to only care about stage level 0. - * Corrolary: other cases only work out of sheer luck (however, the behaviour is stable, so I guess we - * can rely on it for the moment. - * [InlineData(true, "ancestor-only.txt", false, false, FileStatus.Nonexistent, 0)] - * [InlineData(false, "ancestor-only.txt", false, false, FileStatus.Nonexistent, 0)] - */ public void CanResolveConflictsByRemovingFromTheIndex( bool removeFromWorkdir, string filename, bool existsBeforeRemove, bool existsAfterRemove, FileStatus lastStatus, int removedIndexEntries) { diff --git a/LibGit2Sharp.Tests/RemoveFixture.cs b/LibGit2Sharp.Tests/RemoveFixture.cs index 6dc6507cb..04d12feec 100644 --- a/LibGit2Sharp.Tests/RemoveFixture.cs +++ b/LibGit2Sharp.Tests/RemoveFixture.cs @@ -180,7 +180,7 @@ public void RemovingFileWithBadParamsThrows() Assert.Throws(() => repo.Index.Remove(string.Empty)); Assert.Throws(() => repo.Index.Remove((string)null)); Assert.Throws(() => repo.Index.Remove(new string[] { })); - Assert.Throws(() => repo.Index.Remove(new string[] { null })); + Assert.Throws(() => repo.Index.Remove(new string[] { null })); } } } diff --git a/LibGit2Sharp/Core/Ensure.cs b/LibGit2Sharp/Core/Ensure.cs index 0c3c8ea66..df11d3f60 100644 --- a/LibGit2Sharp/Core/Ensure.cs +++ b/LibGit2Sharp/Core/Ensure.cs @@ -1,4 +1,6 @@ using System; +using System.Linq; +using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; @@ -24,6 +26,21 @@ public static void ArgumentNotNull(object argumentValue, string argumentName) } } + /// + /// Checks an array argument to ensure it isn't null or empty. + /// + /// The argument value to check. + /// The name of the argument. + public static void ArgumentNotNullOrEmptyEnumerable(IEnumerable argumentValue, string argumentName) + { + ArgumentNotNull(argumentValue, argumentName); + + if (argumentValue.Count() == 0) + { + throw new ArgumentException("Enumerable cannot be empty", argumentName); + } + } + /// /// Checks a string argument to ensure it isn't null or empty. /// diff --git a/LibGit2Sharp/Index.cs b/LibGit2Sharp/Index.cs index 1f6501b4e..e953910dc 100644 --- a/LibGit2Sharp/Index.cs +++ b/LibGit2Sharp/Index.cs @@ -345,10 +345,44 @@ public virtual void Remove(string path, bool removeFromWorkingDirectory = true, /// public virtual void Remove(IEnumerable paths, bool removeFromWorkingDirectory = true, ExplicitPathsOptions explicitPathsOptions = null) { - var pathsList = paths.ToList(); - var changes = repo.Diff.Compare(DiffModifiers.IncludeUnmodified | DiffModifiers.IncludeUntracked, pathsList, explicitPathsOptions); + Ensure.ArgumentNotNullOrEmptyEnumerable(paths, "paths"); - var pathsTodelete = pathsList.Where(p => Directory.Exists(Path.Combine(repo.Info.WorkingDirectory, p))).ToList(); + var pathsToDelete = paths.Where(p => Directory.Exists(Path.Combine(repo.Info.WorkingDirectory, p))).ToList(); + var notConflictedPaths = new List(); + + foreach (var path in paths) + { + Ensure.ArgumentNotNullOrEmptyString(path, "path"); + + var conflict = repo.Index.Conflicts[path]; + + if (conflict != null) + { + pathsToDelete.Add(RemoveFromIndex(path)); + } + else + { + notConflictedPaths.Add(path); + } + } + + if (notConflictedPaths.Count > 0) + { + pathsToDelete.AddRange(RemoveStagedItems(notConflictedPaths, removeFromWorkingDirectory, explicitPathsOptions)); + } + + if (removeFromWorkingDirectory) + { + RemoveFilesAndFolders(pathsToDelete); + } + + UpdatePhysicalIndex(); + } + + private IEnumerable RemoveStagedItems(IEnumerable paths, bool removeFromWorkingDirectory = true, ExplicitPathsOptions explicitPathsOptions = null) + { + var removed = new List(); + var changes = repo.Diff.Compare(DiffModifiers.IncludeUnmodified | DiffModifiers.IncludeUntracked, paths, explicitPathsOptions); foreach (var treeEntryChanges in changes) { @@ -358,7 +392,7 @@ public virtual void Remove(IEnumerable paths, bool removeFromWorkingDire { case ChangeKind.Added: case ChangeKind.Deleted: - pathsTodelete.Add(RemoveFromIndex(treeEntryChanges.Path)); + removed.Add(RemoveFromIndex(treeEntryChanges.Path)); break; case ChangeKind.Unmodified: @@ -369,7 +403,7 @@ public virtual void Remove(IEnumerable paths, bool removeFromWorkingDire throw new RemoveFromIndexException(string.Format(CultureInfo.InvariantCulture, "Unable to remove file '{0}', as it has changes staged in the index. You can call the Remove() method with removeFromWorkingDirectory=false if you want to remove it from the index only.", treeEntryChanges.Path)); } - pathsTodelete.Add(RemoveFromIndex(treeEntryChanges.Path)); + removed.Add(RemoveFromIndex(treeEntryChanges.Path)); continue; case ChangeKind.Modified: @@ -383,22 +417,16 @@ public virtual void Remove(IEnumerable paths, bool removeFromWorkingDire throw new RemoveFromIndexException(string.Format(CultureInfo.InvariantCulture, "Unable to remove file '{0}', as it has local modifications. You can call the Remove() method with removeFromWorkingDirectory=false if you want to remove it from the index only.", treeEntryChanges.Path)); } - pathsTodelete.Add(RemoveFromIndex(treeEntryChanges.Path)); + removed.Add(RemoveFromIndex(treeEntryChanges.Path)); continue; - default: throw new RemoveFromIndexException(string.Format(CultureInfo.InvariantCulture, "Unable to remove file '{0}'. Its current status is '{1}'.", treeEntryChanges.Path, treeEntryChanges.Status)); } } - if (removeFromWorkingDirectory) - { - RemoveFilesAndFolders(pathsTodelete); - } - - UpdatePhysicalIndex(); + return removed; } private void RemoveFilesAndFolders(IEnumerable pathsList)