Skip to content

Instantly share code, notes, and snippets.

@nedtwigg
Created April 25, 2019 05:18
Show Gist options
  • Save nedtwigg/a5ce9dadc160a8ec2ac48deb55b4e4c6 to your computer and use it in GitHub Desktop.
Save nedtwigg/a5ce9dadc160a8ec2ac48deb55b4e4c6 to your computer and use it in GitHub Desktop.
A rewrite of CleanCommand
import java.io.File;
import java.io.IOException;
import java.util.Objects;
import java.util.TreeSet;
import java.util.function.Consumer;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.GitCommand;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.errors.NoWorkTreeException;
import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.submodule.SubmoduleWalk.IgnoreSubmoduleMode;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
import com.diffplug.common.base.Consumers;
/**
* Remove untracked files from the working tree
*
* @see <a
* href="http://www.kernel.org/pub/software/scm/git/docs/git-clean.html"
* >Git documentation about Clean</a>
*/
public class CleanCommand extends GitCommand<TreeSet<String>> {
private boolean applyToSubmodules;
private boolean dryRun;
private boolean directories;
private boolean ignoreOnly;
private Consumer<String> perFile = Consumers.doNothing();
/**
* Constructor for CleanCommand
*
* @param repo
* the {@link org.eclipse.jgit.lib.Repository}
*/
public CleanCommand(Repository repo) {
super(repo);
}
/**
* {@inheritDoc}
* <p>
* Executes the {@code clean} command with all the options and parameters
* collected by the setter methods of this class. Each instance of this
* class should only be used for one invocation of the command (means: one
* call to {@link #call()})
*/
@Override
public TreeSet<String> call() throws NoWorkTreeException, GitAPIException {
TreeSet<String> allModified = new TreeSet<>();
try (Git git = new Git(repo)) {
Status status = git.status()
.setIgnoreSubmodules(applyToSubmodules ? IgnoreSubmoduleMode.NONE : IgnoreSubmoduleMode.ALL)
.call();
// find the
TreeSet<String> filesToDelete = new TreeSet<>();
TreeSet<String> foldersToDelete = directories ? new TreeSet<>() : null;
if (ignoreOnly) {
FS fs = getRepository().getFS();
for (String p : status.getIgnoredNotInIndex()) {
File f = new File(repo.getWorkTree(), p);
if (fs.isFile(f) || fs.isSymLink(f)) {
filesToDelete.add(p);
} else if (directories && fs.isDirectory(f)) {
foldersToDelete.add(p);
}
}
} else {
filesToDelete.addAll(status.getUntracked());
if (directories) {
foldersToDelete.addAll(status.getUntrackedFolders());
}
}
for (String path : filesToDelete) {
allModified.add(path);
perFile.accept(path);
if (!dryRun) {
FileUtils.delete(new File(repo.getWorkTree(), path), FileUtils.SKIP_MISSING);
}
}
if (directories) {
for (String path : foldersToDelete) {
String pathWithSlash = path + "/";
perFile.accept(pathWithSlash);
allModified.add(pathWithSlash);
if (!dryRun) {
FileUtils.delete(new File(repo.getWorkTree(), path), FileUtils.RECURSIVE | FileUtils.SKIP_MISSING);
}
}
}
} catch (IOException e) {
throw new JGitInternalException(e.getMessage(), e);
} finally {
if (!dryRun && !allModified.isEmpty()) {
repo.fireEvent(new WorkingTreeModifiedEvent(null, allModified));
}
}
return allModified;
}
/**
* If dryRun is set, the paths in question will not actually be deleted.
* Defaults to false.
*
* @param dryRun
* whether to do a dry run or not
* @return {@code this}
*/
public CleanCommand setDryRun(boolean dryRun) {
this.dryRun = dryRun;
return this;
}
/**
* If dirs is set, in addition to files, also clean directories.
* Defaults to false.
*
* @param dirs
* whether to clean directories too, or only files.
* @return {@code this}
*/
public CleanCommand setCleanDirectories(boolean dirs) {
directories = dirs;
return this;
}
/**
* If ignoreOnly is set, only report ignored files and folders.
* Otherwise, skip any files which are covered by .gitignore.
* Defaults to false.
*
* @param ignore
* whether to apply only to .gitignore or not.
* @return {@code this}
*/
public CleanCommand setIgnoreOnly(boolean ignoreOnly) {
this.ignoreOnly = ignoreOnly;
return this;
}
/**
* Determines whether to apply command to the working copy
* of submodules. Defaults to false.
*
* @param applyToSubmodules
* whether to clean submodules or not
* @return {@code this}
*/
public CleanCommand setApplyToSubmodules(boolean applyToSubmodules) {
this.applyToSubmodules = applyToSubmodules;
return this;
}
/** Called before each file is removed. */
public CleanCommand beforeRemove(Consumer<String> perFile) {
this.perFile = Objects.requireNonNull(perFile);
return this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment