This adds the possibility to: * retrieve untracked directories from the status * instruct the CleanCommand to clean those directories. * retrieve ignored paths from the status * instruct the CleanCommand to leave those ignored paths alone Bug: 338717 Change-Id: Ibed0459005a5e306c010b9932f5b5fd107fb5448 Signed-off-by: Chris Aniszczyk <zx@twitter.com>tags/v2.2.0.201212191850-r
@@ -71,8 +71,18 @@ public class CleanCommandTest extends RepositoryTestCase { | |||
writeTrashFile("File2.txt", "Delete Me"); | |||
writeTrashFile("File3.txt", "Delete Me"); | |||
// create files in sub-directories. | |||
writeTrashFile("sub-noclean/File1.txt", "Hello world"); | |||
writeTrashFile("sub-noclean/File2.txt", "Delete Me"); | |||
writeTrashFile("sub-clean/File4.txt", "Delete Me"); | |||
writeTrashFile("sub-noclean/Ignored.txt", "Ignored"); | |||
writeTrashFile(".gitignore", "/ignored-dir\n/sub-noclean/Ignored.txt"); | |||
writeTrashFile("ignored-dir/Ignored2.txt", "Ignored"); | |||
// add and commit first file | |||
git.add().addFilepattern("File1.txt").call(); | |||
git.add().addFilepattern("sub-noclean/File1.txt").call(); | |||
git.add().addFilepattern(".gitignore").call(); | |||
git.commit().setMessage("Initial commit").call(); | |||
} | |||
@@ -90,9 +100,34 @@ public class CleanCommandTest extends RepositoryTestCase { | |||
status = git.status().call(); | |||
files = status.getUntracked(); | |||
assertEquals(0, files.size()); | |||
assertTrue(files.size() == 1); // one remains (directories not cleaned) | |||
assertTrue(cleanedFiles.contains("File2.txt")); | |||
assertTrue(cleanedFiles.contains("File3.txt")); | |||
assertTrue(!cleanedFiles.contains("sub-noclean/File1.txt")); | |||
assertTrue(cleanedFiles.contains("sub-noclean/File2.txt")); | |||
assertTrue(!cleanedFiles.contains("sub-clean/File4.txt")); | |||
} | |||
@Test | |||
public void testCleanDirs() throws NoWorkTreeException, GitAPIException { | |||
// create status | |||
StatusCommand command = git.status(); | |||
Status status = command.call(); | |||
Set<String> files = status.getUntracked(); | |||
assertTrue(files.size() > 0); | |||
// run clean | |||
Set<String> cleanedFiles = git.clean().setCleanDirectories(true).call(); | |||
status = git.status().call(); | |||
files = status.getUntracked(); | |||
assertTrue(files.size() == 0); | |||
assertTrue(cleanedFiles.contains("File2.txt")); | |||
assertTrue(cleanedFiles.contains("File3.txt")); | |||
assertTrue(!cleanedFiles.contains("sub-noclean/File1.txt")); | |||
assertTrue(cleanedFiles.contains("sub-noclean/File2.txt")); | |||
assertTrue(cleanedFiles.contains("sub-clean/")); | |||
} | |||
@Test | |||
@@ -111,7 +146,7 @@ public class CleanCommandTest extends RepositoryTestCase { | |||
status = git.status().call(); | |||
files = status.getUntracked(); | |||
assertEquals(1, files.size()); | |||
assertTrue(files.size() == 3); | |||
assertTrue(cleanedFiles.contains("File3.txt")); | |||
assertFalse(cleanedFiles.contains("File2.txt")); | |||
} | |||
@@ -131,9 +166,65 @@ public class CleanCommandTest extends RepositoryTestCase { | |||
status = git.status().call(); | |||
files = status.getUntracked(); | |||
assertEquals(2, files.size()); | |||
assertEquals(4, files.size()); | |||
assertTrue(cleanedFiles.contains("File2.txt")); | |||
assertTrue(cleanedFiles.contains("File3.txt")); | |||
assertTrue(!cleanedFiles.contains("sub-noclean/File1.txt")); | |||
assertTrue(cleanedFiles.contains("sub-noclean/File2.txt")); | |||
} | |||
@Test | |||
public void testCleanDirsWithDryRun() throws NoWorkTreeException, | |||
GitAPIException { | |||
// create status | |||
StatusCommand command = git.status(); | |||
Status status = command.call(); | |||
Set<String> files = status.getUntracked(); | |||
assertTrue(files.size() > 0); | |||
// run clean | |||
Set<String> cleanedFiles = git.clean().setDryRun(true) | |||
.setCleanDirectories(true).call(); | |||
status = git.status().call(); | |||
files = status.getUntracked(); | |||
assertTrue(files.size() == 4); | |||
assertTrue(cleanedFiles.contains("File2.txt")); | |||
assertTrue(cleanedFiles.contains("File3.txt")); | |||
assertTrue(!cleanedFiles.contains("sub-noclean/File1.txt")); | |||
assertTrue(cleanedFiles.contains("sub-noclean/File2.txt")); | |||
assertTrue(cleanedFiles.contains("sub-clean/")); | |||
} | |||
@Test | |||
public void testCleanWithDryRunAndNoIgnore() throws NoWorkTreeException, | |||
GitAPIException { | |||
// run clean | |||
Set<String> cleanedFiles = git.clean().setDryRun(true).setIgnore(false) | |||
.call(); | |||
Status status = git.status().call(); | |||
Set<String> files = status.getIgnoredNotInIndex(); | |||
assertTrue(files.size() == 2); | |||
assertTrue(cleanedFiles.contains("sub-noclean/Ignored.txt")); | |||
assertTrue(!cleanedFiles.contains("ignored-dir/")); | |||
} | |||
@Test | |||
public void testCleanDirsWithDryRunAndNoIgnore() | |||
throws NoWorkTreeException, GitAPIException { | |||
// run clean | |||
Set<String> cleanedFiles = git.clean().setDryRun(true).setIgnore(false) | |||
.setCleanDirectories(true).call(); | |||
Status status = git.status().call(); | |||
Set<String> files = status.getIgnoredNotInIndex(); | |||
assertTrue(files.size() == 2); | |||
assertTrue(cleanedFiles.contains("sub-noclean/Ignored.txt")); | |||
assertTrue(cleanedFiles.contains("ignored-dir/")); | |||
} | |||
} |
@@ -44,6 +44,7 @@ package org.eclipse.jgit.api; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertTrue; | |||
import java.io.File; | |||
import java.io.IOException; | |||
@@ -125,6 +126,10 @@ public class StatusCommandTest extends RepositoryTestCase { | |||
assertEquals(set("a"), stat.getUntracked()); | |||
git.commit().setMessage("t").call(); | |||
writeTrashFile("sub/a", "sub-file"); | |||
stat = git.status().call(); | |||
assertEquals(1, stat.getUntrackedFolders().size()); | |||
assertTrue(stat.getUntrackedFolders().contains("sub")); | |||
} | |||
public static Set<String> set(String... elements) { |
@@ -68,6 +68,10 @@ public class CleanCommand extends GitCommand<Set<String>> { | |||
private boolean dryRun; | |||
private boolean directories; | |||
private boolean ignore = true; | |||
/** | |||
* @param repo | |||
*/ | |||
@@ -90,19 +94,82 @@ public class CleanCommand extends GitCommand<Set<String>> { | |||
try { | |||
StatusCommand command = new StatusCommand(repo); | |||
Status status = command.call(); | |||
for (String file : status.getUntracked()) { | |||
Set<String> untrackedAndIgnoredFiles = new TreeSet<String>( | |||
status.getUntracked()); | |||
Set<String> untrackedAndIgnoredDirs = new TreeSet<String>( | |||
status.getUntrackedFolders()); | |||
for (String p : status.getIgnoredNotInIndex()) { | |||
File f = new File(repo.getWorkTree(), p); | |||
if (f.isFile()) { | |||
untrackedAndIgnoredFiles.add(p); | |||
} else if (f.isDirectory()) { | |||
untrackedAndIgnoredDirs.add(p); | |||
} | |||
} | |||
Set<String> filtered = filterFolders(untrackedAndIgnoredFiles, | |||
untrackedAndIgnoredDirs); | |||
Set<String> notIgnoredFiles = filterIgnorePaths(filtered, | |||
status.getIgnoredNotInIndex(), true); | |||
Set<String> notIgnoredDirs = filterIgnorePaths( | |||
untrackedAndIgnoredDirs, | |||
status.getIgnoredNotInIndex(), false); | |||
for (String file : notIgnoredFiles) | |||
if (paths.isEmpty() || paths.contains(file)) { | |||
if (!dryRun) | |||
FileUtils.delete(new File(repo.getWorkTree(), file)); | |||
files.add(file); | |||
} | |||
} | |||
if (directories) | |||
for (String dir : notIgnoredDirs) | |||
if (paths.isEmpty() || paths.contains(dir)) { | |||
if (!dryRun) | |||
FileUtils.delete(new File(repo.getWorkTree(), dir), | |||
FileUtils.RECURSIVE); | |||
files.add(dir + "/"); | |||
} | |||
} catch (IOException e) { | |||
throw new JGitInternalException(e.getMessage(), e); | |||
} | |||
return files; | |||
} | |||
private Set<String> filterIgnorePaths(Set<String> inputPaths, | |||
Set<String> ignoredNotInIndex, boolean exact) { | |||
if (ignore) { | |||
Set<String> filtered = new TreeSet<String>(inputPaths); | |||
for (String path : inputPaths) | |||
for (String ignored : ignoredNotInIndex) | |||
if ((exact && path.equals(ignored)) | |||
|| (!exact && path.startsWith(ignored))) { | |||
filtered.remove(path); | |||
break; | |||
} | |||
return filtered; | |||
} | |||
return inputPaths; | |||
} | |||
private Set<String> filterFolders(Set<String> untracked, | |||
Set<String> untrackedFolders) { | |||
Set<String> filtered = new TreeSet<String>(untracked); | |||
for (String file : untracked) | |||
for (String folder : untrackedFolders) | |||
if (file.startsWith(folder)) { | |||
filtered.remove(file); | |||
break; | |||
} | |||
return filtered; | |||
} | |||
/** | |||
* If paths are set, only these paths are affected by the cleaning. | |||
* | |||
@@ -126,4 +193,29 @@ public class CleanCommand extends GitCommand<Set<String>> { | |||
this.dryRun = dryRun; | |||
return this; | |||
} | |||
/** | |||
* If dirs is set, in addition to files, also clean directories. | |||
* | |||
* @param dirs | |||
* whether to clean directories too, or only files. | |||
* @return {@code this} | |||
*/ | |||
public CleanCommand setCleanDirectories(boolean dirs) { | |||
directories = dirs; | |||
return this; | |||
} | |||
/** | |||
* If ignore is set, don't report/clean files/directories that are ignored | |||
* by a .gitignore. otherwise do handle them. | |||
* | |||
* @param ignore | |||
* whether to respect .gitignore or not. | |||
* @return {@code this} | |||
*/ | |||
public CleanCommand setIgnore(boolean ignore) { | |||
this.ignore = ignore; | |||
return this; | |||
} | |||
} |
@@ -137,6 +137,13 @@ public class Status { | |||
return Collections.unmodifiableSet(diff.getUntracked()); | |||
} | |||
/** | |||
* @return set of directories that are not ignored, and not in the index. | |||
*/ | |||
public Set<String> getUntrackedFolders() { | |||
return Collections.unmodifiableSet(diff.getUntrackedFolders()); | |||
} | |||
/** | |||
* @return list of files that are in conflict. (e.g what you get if you | |||
* modify file that was modified by someone else in the meantime) | |||
@@ -144,4 +151,11 @@ public class Status { | |||
public Set<String> getConflicting() { | |||
return Collections.unmodifiableSet(diff.getConflicting()); | |||
} | |||
/** | |||
* @return set of files and folders that are ignored and not in the index. | |||
*/ | |||
public Set<String> getIgnoredNotInIndex() { | |||
return Collections.unmodifiableSet(diff.getIgnoredNotInIndex()); | |||
} | |||
} |