using System.Linq;
using System.IO;
using System.Collections.Generic;
using LibGit2Sharp;
using LibGit2Sharp.Core;
namespace LibGit2Sharp
{
public static partial class Commands
{
///
/// Removes a file from the staging area, and optionally removes it from the working directory as well.
///
/// If the file has already been deleted from the working directory, this method will only deal
/// with promoting the removal to the staging area.
///
///
/// The default behavior is to remove the file from the working directory as well.
///
///
/// The being worked with.
/// The path of the file within the working directory.
public static void Remove(IRepository repository, string path)
{
Remove(repository, path, true, null);
}
///
/// Removes a file from the staging area, and optionally removes it from the working directory as well.
///
/// If the file has already been deleted from the working directory, this method will only deal
/// with promoting the removal to the staging area.
///
///
/// The default behavior is to remove the file from the working directory as well.
///
///
/// The being worked with.
/// The path of the file within the working directory.
/// True to remove the file from the working directory, False otherwise.
public static void Remove(IRepository repository, string path, bool removeFromWorkingDirectory)
{
Remove(repository, path, removeFromWorkingDirectory, null);
}
///
/// Removes a file from the staging area, and optionally removes it from the working directory as well.
///
/// If the file has already been deleted from the working directory, this method will only deal
/// with promoting the removal to the staging area.
///
///
/// The default behavior is to remove the file from the working directory as well.
///
///
/// When not passing a , the passed path will be treated as
/// a pathspec. You can for example use it to pass the relative path to a folder inside the working directory,
/// so that all files beneath this folders, and the folder itself, will be removed.
///
///
/// The repository in which to operate
/// The path of the file within the working directory.
/// True to remove the file from the working directory, False otherwise.
///
/// The passed will be treated as an explicit path.
/// Use these options to determine how unmatched explicit paths should be handled.
///
public static void Remove(IRepository repository, string path, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions)
{
Ensure.ArgumentNotNull(repository, "repository");
Ensure.ArgumentNotNull(path, "path");
Remove(repository, new[] { path }, removeFromWorkingDirectory, explicitPathsOptions);
}
///
/// Removes a collection of fileS from the staging, and optionally removes them from the working directory as well.
///
/// If a file has already been deleted from the working directory, this method will only deal
/// with promoting the removal to the staging area.
///
///
/// The default behavior is to remove the files from the working directory as well.
///
///
/// The being worked with.
/// The collection of paths of the files within the working directory.
public static void Remove(IRepository repository, IEnumerable paths)
{
Remove(repository, paths, true, null);
}
///
/// Removes a collection of fileS from the staging, and optionally removes them from the working directory as well.
///
/// If a file has already been deleted from the working directory, this method will only deal
/// with promoting the removal to the staging area.
///
///
/// The default behavior is to remove the files from the working directory as well.
///
///
/// When not passing a , the passed paths will be treated as
/// a pathspec. You can for example use it to pass the relative paths to folders inside the working directory,
/// so that all files beneath these folders, and the folders themselves, will be removed.
///
///
/// The repository in which to operate
/// The collection of paths of the files within the working directory.
/// True to remove the files from the working directory, False otherwise.
///
/// The passed will be treated as explicit paths.
/// Use these options to determine how unmatched explicit paths should be handled.
///
public static void Remove(IRepository repository, IEnumerable paths, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions)
{
Ensure.ArgumentNotNull(repository, "repository");
Ensure.ArgumentNotNullOrEmptyEnumerable(paths, "paths");
var pathsToDelete = paths.Where(p => Directory.Exists(Path.Combine(repository.Info.WorkingDirectory, p))).ToList();
var notConflictedPaths = new List();
var index = repository.Index;
foreach (var path in paths)
{
Ensure.ArgumentNotNullOrEmptyString(path, "path");
var conflict = index.Conflicts[path];
if (conflict != null)
{
index.Remove(path);
pathsToDelete.Add(path);
}
else
{
notConflictedPaths.Add(path);
}
}
// Make sure status will see the changes from before this
index.Write();
if (notConflictedPaths.Count > 0)
{
pathsToDelete.AddRange(RemoveStagedItems(repository, notConflictedPaths, removeFromWorkingDirectory, explicitPathsOptions));
}
if (removeFromWorkingDirectory)
{
RemoveFilesAndFolders(repository, pathsToDelete);
}
index.Write();
}
private static void RemoveFilesAndFolders(IRepository repository, IEnumerable pathsList)
{
string wd = repository.Info.WorkingDirectory;
foreach (string path in pathsList)
{
string fileName = Path.Combine(wd, path);
if (Directory.Exists(fileName))
{
Directory.Delete(fileName, true);
continue;
}
if (!File.Exists(fileName))
{
continue;
}
File.Delete(fileName);
}
}
private static IEnumerable RemoveStagedItems(IRepository repository, IEnumerable paths, bool removeFromWorkingDirectory = true, ExplicitPathsOptions explicitPathsOptions = null)
{
var removed = new List();
using (var changes = repository.Diff.Compare(DiffModifiers.IncludeUnmodified | DiffModifiers.IncludeUntracked, paths, explicitPathsOptions))
{
var index = repository.Index;
foreach (var treeEntryChanges in changes)
{
var status = repository.RetrieveStatus(treeEntryChanges.Path);
switch (treeEntryChanges.Status)
{
case ChangeKind.Added:
case ChangeKind.Deleted:
removed.Add(treeEntryChanges.Path);
index.Remove(treeEntryChanges.Path);
break;
case ChangeKind.Unmodified:
if (removeFromWorkingDirectory && (
status.HasFlag(FileStatus.ModifiedInIndex) ||
status.HasFlag(FileStatus.NewInIndex)))
{
throw new RemoveFromIndexException("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);
}
removed.Add(treeEntryChanges.Path);
index.Remove(treeEntryChanges.Path);
continue;
case ChangeKind.Modified:
if (status.HasFlag(FileStatus.ModifiedInWorkdir) && status.HasFlag(FileStatus.ModifiedInIndex))
{
throw new RemoveFromIndexException("Unable to remove file '{0}', as it has staged content different from both the working directory and the HEAD.",
treeEntryChanges.Path);
}
if (removeFromWorkingDirectory)
{
throw new RemoveFromIndexException("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);
}
removed.Add(treeEntryChanges.Path);
index.Remove(treeEntryChanges.Path);
continue;
default:
throw new RemoveFromIndexException("Unable to remove file '{0}'. Its current status is '{1}'.",
treeEntryChanges.Path,
treeEntryChanges.Status);
}
}
index.Write();
return removed;
}
}
}
}