aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java35
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target34
-rw-r--r--org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.target34
-rw-r--r--org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java26
-rw-r--r--org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java118
-rw-r--r--org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties3
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java7
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java125
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GarbageCollectCommandTest.java11
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java28
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java34
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java368
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java87
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherTest.java12
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GCTest.java741
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java162
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBranchPrunedTest.java119
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java119
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDirCacheSavesObjectsTest.java85
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java104
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java180
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java120
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java146
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTagTest.java78
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java113
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0004_PackReaderTest.java22
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java25
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryResolveTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0002_TreeTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/test/resources/SampleDataRepositoryTestCase.java (renamed from org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/SampleDataRepositoryTestCase.java)13
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateParserBadlyFormattedTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateParserTest.java39
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java20
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java138
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java89
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RmCommand.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java16
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/Status.java44
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/StatusCommand.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java68
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/InvalidPathException.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java61
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java18
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java29
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyOneSided.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java21
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java92
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java15
-rw-r--r--pom.xml2
89 files changed, 2740 insertions, 1040 deletions
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java
index c6fe4e4d91..1079d98439 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java
@@ -46,8 +46,10 @@
package org.eclipse.jgit.junit;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.reflect.Method;
@@ -124,13 +126,44 @@ public abstract class JGitTestUtil {
// loaded previously
return new File("tst", fileName);
}
+ if ("jar".equals(url.getProtocol())) {
+ try {
+ File tmp = File.createTempFile("tmp_", "_" + fileName);
+ copyTestResource(fileName, tmp);
+ return tmp;
+ } catch (IOException err) {
+ throw new RuntimeException("Cannot create temporary file", err);
+ }
+ }
try {
return new File(url.toURI());
- } catch(URISyntaxException e) {
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException(e.getMessage() + " " + url);
+ } catch (URISyntaxException e) {
return new File(url.getPath());
}
}
+ public static void copyTestResource(String name, File dest)
+ throws IOException {
+ URL url = cl().getResource(CLASSPATH_TO_RESOURCES + name);
+ if (url == null)
+ throw new FileNotFoundException(name);
+ InputStream in = url.openStream();
+ try {
+ FileOutputStream out = new FileOutputStream(dest);
+ try {
+ byte[] buf = new byte[4096];
+ for (int n; (n = in.read(buf)) > 0;)
+ out.write(buf, 0, n);
+ } finally {
+ out.close();
+ }
+ } finally {
+ in.close();
+ }
+ }
+
private static ClassLoader cl() {
return JGitTestUtil.class.getClassLoader();
}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target
index 269b90be66..bb8a34c8bc 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target
@@ -11,23 +11,23 @@
<target name="jgit.target" sequenceNumber="55">
<locations>
<location includeAllPlatforms="false" includeConfigurePhase="true" includeMode="slicer" includeSource="true" type="InstallableUnit">
- <repository location="http://download.eclipse.org/jetty/updates/jetty-bundles-7.x/7.6.11.v20130520/"/>
- <unit id="org.eclipse.jetty.client" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.client.source" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.continuation" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.continuation.source" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.http" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.http.source" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.io" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.io.source" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.security" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.security.source" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.server" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.server.source" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.servlet" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.servlet.source" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.util" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.util.source" version="7.6.11.v20130520"/>
+ <repository location="http://download.eclipse.org/jetty/updates/jetty-bundles-7.x/7.6.14.v20131031/"/>
+ <unit id="org.eclipse.jetty.client" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.client.source" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.continuation" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.http" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.http.source" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.io" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.io.source" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.security" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.security.source" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.server" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.server.source" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.servlet" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.util" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.util.source" version="7.6.14.v20131031"/>
</location>
<location includeAllPlatforms="false" includeConfigurePhase="true" includeMode="slicer" includeSource="true" type="InstallableUnit">
<repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20130827064939/repository/"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.target
index a8d5d05eb7..f361e42307 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.target
@@ -11,23 +11,23 @@
<target name="jgit.target" sequenceNumber="55">
<locations>
<location includeAllPlatforms="false" includeConfigurePhase="true" includeMode="slicer" includeSource="true" type="InstallableUnit">
- <repository location="http://download.eclipse.org/jetty/updates/jetty-bundles-7.x/7.6.11.v20130520/"/>
- <unit id="org.eclipse.jetty.client" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.client.source" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.continuation" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.continuation.source" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.http" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.http.source" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.io" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.io.source" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.security" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.security.source" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.server" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.server.source" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.servlet" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.servlet.source" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.util" version="7.6.11.v20130520"/>
- <unit id="org.eclipse.jetty.util.source" version="7.6.11.v20130520"/>
+ <repository location="http://download.eclipse.org/jetty/updates/jetty-bundles-7.x/7.6.14.v20131031/"/>
+ <unit id="org.eclipse.jetty.client" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.client.source" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.continuation" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.http" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.http.source" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.io" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.io.source" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.security" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.security.source" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.server" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.server.source" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.servlet" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.util" version="7.6.14.v20131031"/>
+ <unit id="org.eclipse.jetty.util.source" version="7.6.14.v20131031"/>
</location>
<location includeAllPlatforms="false" includeConfigurePhase="true" includeMode="slicer" includeSource="true" type="InstallableUnit">
<repository location="http://download.eclipse.org/tools/orbit/downloads/drops/S20131024145017/repository/"/>
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
index 48cb5c54ca..a6ea48c0ea 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
@@ -42,8 +42,11 @@
*/
package org.eclipse.jgit.pgm;
+import java.io.File;
+
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
+import org.eclipse.jgit.util.FileUtils;
import org.junit.Assert;
import org.junit.Test;
@@ -107,6 +110,29 @@ public class CheckoutTest extends CLIRepositoryTestCase {
assertEquals("", execute("git checkout HEAD"));
}
+ @Test
+ public void testCheckoutExistingBranchWithConflict() throws Exception {
+ Git git = new Git(db);
+ writeTrashFile("a", "Hello world a");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("commit file a").call();
+ git.branchCreate().setName("branch_1").call();
+ git.rm().addFilepattern("a").call();
+ FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
+ writeTrashFile("a/b", "Hello world b");
+ git.add().addFilepattern("a/b").call();
+ git.commit().setMessage("commit folder a").call();
+ git.rm().addFilepattern("a").call();
+ writeTrashFile("a", "New Hello world a");
+ git.add().addFilepattern(".").call();
+
+ String[] execute = execute("git checkout branch_1");
+ Assert.assertEquals(
+ "error: Your local changes to the following files would be overwritten by checkout:",
+ execute[0]);
+ Assert.assertEquals("\ta", execute[1]);
+ }
+
static private void assertEquals(String expected, String[] actual) {
// if there is more than one line, ignore last one if empty
Assert.assertEquals(
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java
index 73ae598a8c..acc2be6b43 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java
@@ -213,4 +213,122 @@ public class StatusTest extends CLIRepositoryTestCase {
"" //
}, execute("git status")); //
}
+
+ @Test
+ public void testStatusPorcelain() throws Exception {
+ Git git = new Git(db);
+ // Write all files
+ writeTrashFile("tracked", "tracked");
+ writeTrashFile("stagedNew", "stagedNew");
+ writeTrashFile("stagedModified", "stagedModified");
+ writeTrashFile("stagedDeleted", "stagedDeleted");
+ writeTrashFile("trackedModified", "trackedModified");
+ writeTrashFile("trackedDeleted", "trackedDeleted");
+ writeTrashFile("untracked", "untracked");
+ // Test untracked
+ assertArrayOfLinesEquals(new String[] { // git status output
+ "?? stagedDeleted", //
+ "?? stagedModified", //
+ "?? stagedNew", //
+ "?? tracked", //
+ "?? trackedDeleted", //
+ "?? trackedModified", //
+ "?? untracked", //
+ "" //
+ }, execute("git status --porcelain")); //
+ // Add to index
+ git.add().addFilepattern("tracked").call();
+ git.add().addFilepattern("stagedModified").call();
+ git.add().addFilepattern("stagedDeleted").call();
+ git.add().addFilepattern("trackedModified").call();
+ git.add().addFilepattern("trackedDeleted").call();
+ // Test staged count
+ assertArrayOfLinesEquals(new String[] { // git status output
+ "A stagedDeleted", //
+ "A stagedModified", //
+ "A tracked", //
+ "A trackedDeleted", //
+ "A trackedModified", //
+ "?? stagedNew", //
+ "?? untracked", //
+ "" //
+ }, execute("git status --porcelain")); //
+ // Commit
+ git.commit().setMessage("initial commit").call();
+ assertArrayOfLinesEquals(new String[] { // git status output
+ "?? stagedNew", //
+ "?? untracked", //
+ "" //
+ }, execute("git status --porcelain")); //
+ // Make some changes and stage them
+ writeTrashFile("stagedModified", "stagedModified modified");
+ deleteTrashFile("stagedDeleted");
+ writeTrashFile("trackedModified", "trackedModified modified");
+ deleteTrashFile("trackedDeleted");
+ git.add().addFilepattern("stagedModified").call();
+ git.rm().addFilepattern("stagedDeleted").call();
+ git.add().addFilepattern("stagedNew").call();
+ // Test staged/not-staged status
+ assertArrayOfLinesEquals(new String[] { // git status output
+ "D stagedDeleted", //
+ "M stagedModified", //
+ "A stagedNew", //
+ " D trackedDeleted", //
+ " M trackedModified", //
+ "?? untracked", //
+ "" //
+ }, execute("git status --porcelain")); //
+ // Create unmerged file
+ writeTrashFile("unmerged", "unmerged");
+ git.add().addFilepattern("unmerged").call();
+ // Commit pending changes
+ git.add().addFilepattern("trackedModified").call();
+ git.rm().addFilepattern("trackedDeleted").call();
+ git.commit().setMessage("commit before branching").call();
+ assertArrayOfLinesEquals(new String[] { // git status output
+ "?? untracked", //
+ "" //
+ }, execute("git status --porcelain")); //
+ // Checkout new branch
+ git.checkout().setCreateBranch(true).setName("test").call();
+ // Test branch status
+ assertArrayOfLinesEquals(new String[] { // git status output
+ "?? untracked", //
+ "" //
+ }, execute("git status --porcelain")); //
+ // Commit change and checkout master again
+ writeTrashFile("unmerged", "changed in test branch");
+ git.add().addFilepattern("unmerged").call();
+ RevCommit testBranch = git.commit()
+ .setMessage("changed unmerged in test branch").call();
+ assertArrayOfLinesEquals(new String[] { // git status output
+ "?? untracked", //
+ "" //
+ }, execute("git status --porcelain")); //
+ git.checkout().setName("master").call();
+ // Change the same file and commit
+ writeTrashFile("unmerged", "changed in master branch");
+ git.add().addFilepattern("unmerged").call();
+ git.commit().setMessage("changed unmerged in master branch").call();
+ assertArrayOfLinesEquals(new String[] { // git status output
+ "?? untracked", //
+ "" //
+ }, execute("git status --porcelain")); //
+ // Merge test branch into master
+ git.merge().include(testBranch.getId()).call();
+ // Test unmerged status
+ assertArrayOfLinesEquals(new String[] { // git status output
+ "UU unmerged", //
+ "?? untracked", //
+ "" //
+ }, execute("git status --porcelain")); //
+ // Test detached head
+ String commitId = db.getRef(Constants.MASTER).getObjectId().name();
+ git.checkout().setName(commitId).call();
+ assertArrayOfLinesEquals(new String[] { // git status output
+ "UU unmerged", //
+ "?? untracked", //
+ "" //
+ }, execute("git status --porcelain")); //
+ }
}
diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
index 1212d93652..d23f378993 100644
--- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
+++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
@@ -36,6 +36,8 @@ cantFindGitDirectory=error: can't find git directory
cantWrite=Can't write {0}
changesNotStagedForCommit=Changes not staged for commit:
changesToBeCommitted=Changes to be committed:
+checkoutConflict=error: Your local changes to the following files would be overwritten by checkout:
+checkoutConflictPathLine=\t{0}
commitLabel=commit
configFileNotFound=configuration file {0} not found
conflictingUsageOf_git_dir_andArguments=conflicting usage of --git-dir and arguments
@@ -280,6 +282,7 @@ usage_inputOutputFile=Input/output file
usage_listBothRemoteTrackingAndLocalBranches=list both remote-tracking and local branches
usage_listCreateOrDeleteBranches=List, create, or delete branches
usage_logAllPretty=format:%H %ct %P' output=log --all '--pretty=format:%H %ct %P' output
+usage_machineReadableOutput=machine-readable output
usage_manageReflogInformation=Manage reflog information
usage_mergeFf=When the merge resolves as a fast-forward, only update the branch pointer, without creating a merge commit.
usage_mergeNoFf=Create a merge commit even when the merge resolves as a fast-forward.
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
index 8115039729..2f35ecbabd 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2010, 2012 Chris Aniszczyk <caniszczyk@gmail.com>
+ * Copyright (C) 2013, Obeo
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -47,6 +48,7 @@ import java.text.MessageFormat;
import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.CheckoutConflictException;
import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.lib.Constants;
@@ -107,6 +109,11 @@ class Checkout extends TextBuiltin {
} catch (RefAlreadyExistsException e) {
throw die(MessageFormat.format(CLIText.get().branchAlreadyExists,
name));
+ } catch (CheckoutConflictException e) {
+ outw.println(CLIText.get().checkoutConflict);
+ for (String path : e.getConflictingPaths())
+ outw.println(MessageFormat.format(
+ CLIText.get().checkoutConflictPathLine, path));
}
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java
index 0214ed00e0..2ae950bdc5 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java
@@ -50,6 +50,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.TreeSet;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.StatusCommand;
@@ -71,26 +72,134 @@ class Status extends TextBuiltin {
protected final String statusFileListFormatUnmerged = CLIText.get().statusFileListFormatUnmerged;
+ @Option(name = "--porcelain", usage = "usage_machineReadableOutput")
+ protected boolean porcelain;
+
@Option(name = "--", metaVar = "metaVar_path", multiValued = true)
protected List<String> filterPaths;
@Override
protected void run() throws Exception {
+ StatusCommand statusCommand = new Git(db).status();
+ if (filterPaths != null && filterPaths.size() > 0)
+ for (String path : filterPaths)
+ statusCommand.addPath(path);
+ org.eclipse.jgit.api.Status status = statusCommand.call();
+ printStatus(status);
+ }
+
+ private void printStatus(org.eclipse.jgit.api.Status status)
+ throws IOException {
+ if (porcelain)
+ printPorcelainStatus(status);
+ else
+ printLongStatus(status);
+ }
+
+ private void printPorcelainStatus(org.eclipse.jgit.api.Status status)
+ throws IOException {
+
+ Collection<String> added = status.getAdded();
+ Collection<String> changed = status.getChanged();
+ Collection<String> removed = status.getRemoved();
+ Collection<String> modified = status.getModified();
+ Collection<String> missing = status.getMissing();
+ Map<String, StageState> conflicting = status.getConflictingStageState();
+
+ // build a sorted list of all paths except untracked and ignored
+ TreeSet<String> sorted = new TreeSet<String>();
+ sorted.addAll(added);
+ sorted.addAll(changed);
+ sorted.addAll(removed);
+ sorted.addAll(modified);
+ sorted.addAll(missing);
+ sorted.addAll(conflicting.keySet());
+
+ // list each path
+ for (String path : sorted) {
+ char x = ' ';
+ char y = ' ';
+
+ if (added.contains(path))
+ x = 'A';
+ else if (changed.contains(path))
+ x = 'M';
+ else if (removed.contains(path))
+ x = 'D';
+
+ if (modified.contains(path))
+ y = 'M';
+ else if (missing.contains(path))
+ y = 'D';
+
+ if (conflicting.containsKey(path)) {
+ StageState stageState = conflicting.get(path);
+
+ switch (stageState) {
+ case BOTH_DELETED:
+ x = 'D';
+ y = 'D';
+ break;
+ case ADDED_BY_US:
+ x = 'A';
+ y = 'U';
+ break;
+ case DELETED_BY_THEM:
+ x = 'U';
+ y = 'D';
+ break;
+ case ADDED_BY_THEM:
+ x = 'U';
+ y = 'A';
+ break;
+ case DELETED_BY_US:
+ x = 'D';
+ y = 'U';
+ break;
+ case BOTH_ADDED:
+ x = 'A';
+ y = 'A';
+ break;
+ case BOTH_MODIFIED:
+ x = 'U';
+ y = 'U';
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown StageState: " //$NON-NLS-1$
+ + stageState);
+ }
+ }
+
+ printPorcelainLine(x, y, path);
+ }
+
+ // untracked are always at the end of the list
+ TreeSet<String> untracked = new TreeSet<String>(status.getUntracked());
+ for (String path : untracked)
+ printPorcelainLine('?', '?', path);
+ }
+
+ private void printPorcelainLine(char x, char y, String path)
+ throws IOException {
+ StringBuilder lineBuilder = new StringBuilder();
+ lineBuilder.append(x).append(y).append(' ').append(path);
+ outw.println(lineBuilder.toString());
+ }
+
+ private void printLongStatus(org.eclipse.jgit.api.Status status)
+ throws IOException {
// Print current branch name
final Ref head = db.getRef(Constants.HEAD);
- boolean firstHeader = true;
if (head != null && head.isSymbolic()) {
String branch = Repository.shortenRefName(head.getLeaf().getName());
- outw.println(CLIText.formatLine(
- MessageFormat.format(CLIText.get().onBranch, branch)));
+ outw.println(CLIText.formatLine(MessageFormat.format(
+ CLIText.get().onBranch, branch)));
} else
outw.println(CLIText.formatLine(CLIText.get().notOnAnyBranch));
+
// List changes
- StatusCommand statusCommand = new Git(db).status();
- if (filterPaths != null && filterPaths.size() > 0)
- for (String path : filterPaths)
- statusCommand.addPath(path);
- org.eclipse.jgit.api.Status status = statusCommand.call();
+ boolean firstHeader = true;
+
Collection<String> added = status.getAdded();
Collection<String> changed = status.getChanged();
Collection<String> removed = status.getRemoved();
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
index de8675e0fb..a51313ae1e 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2010, 2013 Sasa Zivkov <sasa.zivkov@sap.com>
+ * Copyright (C) 2013, Obeo
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -106,6 +107,8 @@ public class CLIText extends TranslationBundle {
/***/ public String cantWrite;
/***/ public String changesNotStagedForCommit;
/***/ public String changesToBeCommitted;
+ /***/ public String checkoutConflict;
+ /***/ public String checkoutConflictPathLine;
/***/ public String commitLabel;
/***/ public String conflictingUsageOf_git_dir_andArguments;
/***/ public String couldNotCreateBranch;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GarbageCollectCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GarbageCollectCommandTest.java
index 47ebb6b2a3..098f2b3b5d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GarbageCollectCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GarbageCollectCommandTest.java
@@ -49,6 +49,7 @@ import java.util.Properties;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.util.GitDateParser;
+import org.eclipse.jgit.util.SystemReader;
import org.junit.Before;
import org.junit.Test;
@@ -68,7 +69,8 @@ public class GarbageCollectCommandTest extends RepositoryTestCase {
@Test
public void testGConeCommit() throws Exception {
- Date expire = GitDateParser.parse("now", null);
+ Date expire = GitDateParser.parse("now", null, SystemReader
+ .getInstance().getLocale());
Properties res = git.gc().setExpire(expire).call();
assertTrue(res.size() == 7);
}
@@ -83,8 +85,11 @@ public class GarbageCollectCommandTest extends RepositoryTestCase {
writeTrashFile("b.txt", "a couple of words for gc to pack more 2");
writeTrashFile("c.txt", "a couple of words for gc to pack more 3");
git.commit().setAll(true).setMessage("commit3").call();
- Properties res = git.gc().setExpire(GitDateParser.parse("now", null))
- .call();
+ Properties res = git
+ .gc()
+ .setExpire(
+ GitDateParser.parse("now", null, SystemReader
+ .getInstance().getLocale())).call();
assertTrue(res.size() == 7);
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java
index 8d7758cb7a..64bb8bfa4d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java
@@ -45,6 +45,7 @@ package org.eclipse.jgit.api;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import java.io.File;
import java.io.IOException;
import org.eclipse.jgit.api.ListBranchCommand.ListMode;
@@ -53,6 +54,7 @@ import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.FileUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -123,4 +125,30 @@ public class GitConstructionTest extends RepositoryTestCase {
// should not get here
}
}
+
+ @Test
+ /**
+ * Tests that a repository with packfiles can be deleted after calling
+ * Git.close(). On Windows the first try to delete the worktree will fail
+ * (because file handles on packfiles are still open) but the second
+ * attempt after a close will succeed.
+ *
+ * @throws IOException
+ * @throws JGitInternalException
+ * @throws GitAPIException
+ */
+ public void testClose() throws IOException, JGitInternalException,
+ GitAPIException {
+ File workTree = db.getWorkTree();
+ Git git = Git.wrap(db);
+ git.gc().setExpire(null).call();
+ git.checkout().setName(git.getRepository().resolve("HEAD^").getName())
+ .call();
+ try {
+ FileUtils.delete(workTree, FileUtils.RECURSIVE);
+ } catch (IOException e) {
+ git.close();
+ FileUtils.delete(workTree, FileUtils.RECURSIVE);
+ }
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
index 29146dc585..1eeb9f7c0d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
@@ -56,8 +56,11 @@ import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
import org.eclipse.jgit.api.MergeResult.MergeStatus;
import org.eclipse.jgit.api.errors.InvalidMergeHeadsException;
import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
@@ -1532,6 +1535,37 @@ public class MergeCommandTest extends RepositoryTestCase {
assertEquals(MergeStatus.ABORTED, result.getMergeStatus());
}
+
+ @Test
+ public void testRecursiveMergeWithConflict() throws Exception {
+ TestRepository<Repository> db_t = new TestRepository<Repository>(db);
+ BranchBuilder master = db_t.branch("master");
+ RevCommit m0 = master.commit().add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9\n")
+ .message("m0").create();
+ RevCommit m1 = master.commit()
+ .add("f", "1-master\n2\n3\n4\n5\n6\n7\n8\n9\n").message("m1")
+ .create();
+ db_t.getRevWalk().parseCommit(m1);
+
+ BranchBuilder side = db_t.branch("side");
+ RevCommit s1 = side.commit().parent(m0)
+ .add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9-side\n").message("s1")
+ .create();
+ RevCommit s2 = side.commit().parent(m1)
+ .add("f", "1-master\n2\n3\n4\n5\n6\n7-res(side)\n8\n9-side\n")
+ .message("s2(merge)").create();
+ master.commit().parent(s1)
+ .add("f", "1-master\n2\n3\n4\n5\n6\n7-conflict\n8\n9-side\n")
+ .message("m2(merge)").create();
+
+ Git git = Git.wrap(db);
+ git.checkout().setName("master").call();
+
+ MergeResult result = git.merge().setStrategy(MergeStrategy.RECURSIVE)
+ .include("side", s2).call();
+ assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
+ }
+
private static void setExecutable(Git git, String path, boolean executable) {
FS.DETECTED.setExecute(
new File(git.getRepository().getWorkTree(), path), executable);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
index 241d099d19..a61b44eda8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
@@ -68,9 +68,14 @@ import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.api.errors.UnmergedPathsException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
+import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.dircache.DirCacheCheckout;
+import org.eclipse.jgit.errors.AmbiguousObjectException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
@@ -80,9 +85,10 @@ import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.ReflogEntry;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.merge.MergeStrategy;
-import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
@@ -1200,9 +1206,9 @@ public class RebaseCommandTest extends RepositoryTestCase {
// rebase
RebaseResult result = git.rebase().setUpstream("refs/heads/master")
.call();
- assertEquals(Status.CONFLICTS, result.getStatus());
- assertEquals(1, result.getConflicts().size());
- assertEquals("file2", result.getConflicts().get(0));
+ assertEquals(Status.UNCOMMITTED_CHANGES, result.getStatus());
+ assertEquals(1, result.getUncommittedChanges().size());
+ assertEquals("file2", result.getUncommittedChanges().get(0));
}
@Test
@@ -1233,9 +1239,9 @@ public class RebaseCommandTest extends RepositoryTestCase {
RebaseResult result = git.rebase().setUpstream("refs/heads/master")
.call();
- assertEquals(Status.CONFLICTS, result.getStatus());
- assertEquals(1, result.getConflicts().size());
- assertEquals("file2", result.getConflicts().get(0));
+ assertEquals(Status.UNCOMMITTED_CHANGES, result.getStatus());
+ assertEquals(1, result.getUncommittedChanges().size());
+ assertEquals("file2", result.getUncommittedChanges().get(0));
checkFile(uncommittedFile, "uncommitted file2");
assertEquals(RepositoryState.SAFE, git.getRepository().getRepositoryState());
@@ -1268,9 +1274,9 @@ public class RebaseCommandTest extends RepositoryTestCase {
// rebase
RebaseResult result = git.rebase().setUpstream("refs/heads/master")
.call();
- assertEquals(Status.CONFLICTS, result.getStatus());
- assertEquals(1, result.getConflicts().size());
- assertEquals(FILE1, result.getConflicts().get(0));
+ assertEquals(Status.UNCOMMITTED_CHANGES, result.getStatus());
+ assertEquals(1, result.getUncommittedChanges().size());
+ assertEquals(FILE1, result.getUncommittedChanges().get(0));
}
@Test
@@ -1302,9 +1308,9 @@ public class RebaseCommandTest extends RepositoryTestCase {
// rebase
RebaseResult result = git.rebase().setUpstream("refs/heads/master")
.call();
- assertEquals(Status.CONFLICTS, result.getStatus());
- assertEquals(1, result.getConflicts().size());
- assertEquals(FILE1, result.getConflicts().get(0));
+ assertEquals(Status.UNCOMMITTED_CHANGES, result.getStatus());
+ assertEquals(1, result.getUncommittedChanges().size());
+ assertEquals(FILE1, result.getUncommittedChanges().get(0));
}
@Test
@@ -1333,7 +1339,8 @@ public class RebaseCommandTest extends RepositoryTestCase {
writeTrashFile("file0", "unstaged modified file0");
// rebase
- assertEquals(Status.OK, git.rebase().setUpstream("refs/heads/master")
+ assertEquals(Status.UNCOMMITTED_CHANGES,
+ git.rebase().setUpstream("refs/heads/master")
.call().getStatus());
}
@@ -1371,12 +1378,8 @@ public class RebaseCommandTest extends RepositoryTestCase {
// rebase
RebaseResult result = git.rebase().setUpstream("refs/heads/master")
.call();
- assertEquals(Status.FAILED, result.getStatus());
- // staged file0 causes DIRTY_INDEX
- assertEquals(1, result.getFailingPaths().size());
- assertEquals(MergeFailureReason.DIRTY_INDEX, result.getFailingPaths()
- .get("file0"));
- assertEquals("unstaged modified file0", read(file0));
+ assertEquals(Status.UNCOMMITTED_CHANGES, result.getStatus());
+ assertEquals(1, result.getUncommittedChanges().size());
// index shall be unchanged
assertEquals(indexState, indexState(CONTENT));
assertEquals(RepositoryState.SAFE, db.getRepositoryState());
@@ -1412,7 +1415,8 @@ public class RebaseCommandTest extends RepositoryTestCase {
writeTrashFile("file0", "unstaged modified file0");
// rebase
- assertEquals(Status.OK, git.rebase().setUpstream("refs/heads/master")
+ assertEquals(Status.UNCOMMITTED_CHANGES,
+ git.rebase().setUpstream("refs/heads/master")
.call().getStatus());
}
@@ -1453,11 +1457,9 @@ public class RebaseCommandTest extends RepositoryTestCase {
// rebase
RebaseResult result = git.rebase().setUpstream("refs/heads/master")
.call();
- assertEquals(Status.FAILED, result.getStatus());
+ assertEquals(Status.UNCOMMITTED_CHANGES, result.getStatus());
// staged file0 causes DIRTY_INDEX
- assertEquals(1, result.getFailingPaths().size());
- assertEquals(MergeFailureReason.DIRTY_INDEX, result.getFailingPaths()
- .get("file0"));
+ assertEquals(1, result.getUncommittedChanges().size());
assertEquals("unstaged modified file0", read(file0));
// index shall be unchanged
assertEquals(indexState, indexState(CONTENT));
@@ -1465,6 +1467,82 @@ public class RebaseCommandTest extends RepositoryTestCase {
}
@Test
+ public void testFastForwardRebaseWithModification() throws Exception {
+ // create file0 + file1, add and commit
+ writeTrashFile("file0", "file0");
+ writeTrashFile(FILE1, "file1");
+ git.add().addFilepattern("file0").addFilepattern(FILE1).call();
+ RevCommit commit = git.commit().setMessage("commit1").call();
+
+ // create topic branch
+ createBranch(commit, "refs/heads/topic");
+
+ // still on master / modify file1, add and commit
+ writeTrashFile(FILE1, "modified file1");
+ git.add().addFilepattern(FILE1).call();
+ git.commit().setMessage("commit2").call();
+
+ // checkout topic branch / modify file0 and add to index
+ checkoutBranch("refs/heads/topic");
+ writeTrashFile("file0", "modified file0 in index");
+ git.add().addFilepattern("file0").addFilepattern(FILE1).call();
+ // modify once more
+ writeTrashFile("file0", "modified file0");
+
+ // rebase
+ RebaseResult result = git.rebase().setUpstream("refs/heads/master")
+ .call();
+ assertEquals(Status.FAST_FORWARD, result.getStatus());
+ checkFile(new File(db.getWorkTree(), "file0"), "modified file0");
+ checkFile(new File(db.getWorkTree(), FILE1), "modified file1");
+ assertEquals("[file0, mode:100644, content:modified file0 in index]"
+ + "[file1, mode:100644, content:modified file1]",
+ indexState(CONTENT));
+ assertEquals(RepositoryState.SAFE, db.getRepositoryState());
+ }
+
+ @Test
+ public void testRebaseWithModificationShouldNotDeleteData()
+ throws Exception {
+ // create file0 + file1, add and commit
+ writeTrashFile("file0", "file0");
+ writeTrashFile(FILE1, "file1");
+ git.add().addFilepattern("file0").addFilepattern(FILE1).call();
+ RevCommit commit = git.commit().setMessage("commit1").call();
+
+ // create topic branch
+ createBranch(commit, "refs/heads/topic");
+
+ // still on master / modify file1, add and commit
+ writeTrashFile(FILE1, "modified file1");
+ git.add().addFilepattern(FILE1).call();
+ git.commit().setMessage("commit2").call();
+
+ // checkout topic branch / modify file1, add and commit
+ checkoutBranch("refs/heads/topic");
+ writeTrashFile(FILE1, "modified file1 on topic");
+ git.add().addFilepattern(FILE1).call();
+ git.commit().setMessage("commit3").call();
+
+ writeTrashFile("file0", "modified file0");
+
+ RebaseResult result = git.rebase().setUpstream("refs/heads/master")
+ .call();
+ // the following condition was true before commit 83b6ab233:
+ // jgit started the rebase and deleted the change on abort
+ // This test should verify that content was deleted
+ if (result.getStatus() == Status.STOPPED)
+ git.rebase().setOperation(Operation.ABORT).call();
+
+ checkFile(new File(db.getWorkTree(), "file0"), "modified file0");
+ checkFile(new File(db.getWorkTree(), FILE1),
+ "modified file1 on topic");
+ assertEquals("[file0, mode:100644, content:file0]"
+ + "[file1, mode:100644, content:modified file1 on topic]",
+ indexState(CONTENT));
+ }
+
+ @Test
public void testRebaseWithUncommittedDelete() throws Exception {
// create file0 + file1, add and commit
File file0 = writeTrashFile("file0", "file0");
@@ -1496,6 +1574,136 @@ public class RebaseCommandTest extends RepositoryTestCase {
assertEquals(RepositoryState.SAFE, db.getRepositoryState());
}
+ @Test
+ public void testRebaseWithAutoStash()
+ throws Exception {
+ // create file0, add and commit
+ db.getConfig().setBoolean(ConfigConstants.CONFIG_REBASE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_AUTOSTASH, true);
+ writeTrashFile("file0", "file0");
+ git.add().addFilepattern("file0").call();
+ git.commit().setMessage("commit0").call();
+ // create file1, add and commit
+ writeTrashFile(FILE1, "file1");
+ git.add().addFilepattern(FILE1).call();
+ RevCommit commit = git.commit().setMessage("commit1").call();
+
+ // create topic branch and checkout / create file2, add and commit
+ createBranch(commit, "refs/heads/topic");
+ checkoutBranch("refs/heads/topic");
+ writeTrashFile("file2", "file2");
+ git.add().addFilepattern("file2").call();
+ git.commit().setMessage("commit2").call();
+
+ // checkout master branch / modify file1, add and commit
+ checkoutBranch("refs/heads/master");
+ writeTrashFile(FILE1, "modified file1");
+ git.add().addFilepattern(FILE1).call();
+ git.commit().setMessage("commit3").call();
+
+ // checkout topic branch / modify file0
+ checkoutBranch("refs/heads/topic");
+ writeTrashFile("file0", "unstaged modified file0");
+
+ // rebase
+ assertEquals(Status.OK,
+ git.rebase().setUpstream("refs/heads/master").call()
+ .getStatus());
+ checkFile(new File(db.getWorkTree(), "file0"),
+ "unstaged modified file0");
+ checkFile(new File(db.getWorkTree(), FILE1), "modified file1");
+ checkFile(new File(db.getWorkTree(), "file2"), "file2");
+ assertEquals("[file0, mode:100644, content:file0]"
+ + "[file1, mode:100644, content:modified file1]"
+ + "[file2, mode:100644, content:file2]",
+ indexState(CONTENT));
+ assertEquals(RepositoryState.SAFE, db.getRepositoryState());
+ }
+
+ @Test
+ public void testRebaseWithAutoStashConflictOnApply() throws Exception {
+ // create file0, add and commit
+ db.getConfig().setBoolean(ConfigConstants.CONFIG_REBASE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_AUTOSTASH, true);
+ writeTrashFile("file0", "file0");
+ git.add().addFilepattern("file0").call();
+ git.commit().setMessage("commit0").call();
+ // create file1, add and commit
+ writeTrashFile(FILE1, "file1");
+ git.add().addFilepattern(FILE1).call();
+ RevCommit commit = git.commit().setMessage("commit1").call();
+
+ // create topic branch and checkout / create file2, add and commit
+ createBranch(commit, "refs/heads/topic");
+ checkoutBranch("refs/heads/topic");
+ writeTrashFile("file2", "file2");
+ git.add().addFilepattern("file2").call();
+ git.commit().setMessage("commit2").call();
+
+ // checkout master branch / modify file1, add and commit
+ checkoutBranch("refs/heads/master");
+ writeTrashFile(FILE1, "modified file1");
+ git.add().addFilepattern(FILE1).call();
+ git.commit().setMessage("commit3").call();
+
+ // checkout topic branch / modify file0
+ checkoutBranch("refs/heads/topic");
+ writeTrashFile("file1", "unstaged modified file1");
+
+ // rebase
+ assertEquals(Status.STASH_APPLY_CONFLICTS,
+ git.rebase().setUpstream("refs/heads/master").call()
+ .getStatus());
+ checkFile(new File(db.getWorkTree(), "file0"), "file0");
+ checkFile(
+ new File(db.getWorkTree(), FILE1),
+ "<<<<<<< HEAD\nmodified file1\n=======\nunstaged modified file1\n>>>>>>> stash\n");
+ checkFile(new File(db.getWorkTree(), "file2"), "file2");
+ assertEquals(
+ "[file0, mode:100644, content:file0]"
+ + "[file1, mode:100644, stage:1, content:file1]"
+ + "[file1, mode:100644, stage:2, content:modified file1]"
+ + "[file1, mode:100644, stage:3, content:unstaged modified file1]"
+ + "[file2, mode:100644, content:file2]",
+ indexState(CONTENT));
+ assertEquals(RepositoryState.SAFE, db.getRepositoryState());
+
+ List<DiffEntry> diffs = getStashedDiff();
+ assertEquals(1, diffs.size());
+ assertEquals(DiffEntry.ChangeType.MODIFY, diffs.get(0).getChangeType());
+ assertEquals("file1", diffs.get(0).getOldPath());
+ }
+
+ private List<DiffEntry> getStashedDiff() throws AmbiguousObjectException,
+ IncorrectObjectTypeException, IOException, MissingObjectException {
+ ObjectId stashId = db.resolve("stash@{0}");
+ RevWalk revWalk = new RevWalk(db);
+ RevCommit stashCommit = revWalk.parseCommit(stashId);
+ List<DiffEntry> diffs = diffWorkingAgainstHead(stashCommit, revWalk);
+ return diffs;
+ }
+
+ private TreeWalk createTreeWalk() {
+ TreeWalk walk = new TreeWalk(db);
+ walk.setRecursive(true);
+ walk.setFilter(TreeFilter.ANY_DIFF);
+ return walk;
+ }
+
+ private List<DiffEntry> diffWorkingAgainstHead(final RevCommit commit,
+ RevWalk revWalk)
+ throws IOException {
+ TreeWalk walk = createTreeWalk();
+ RevCommit parentCommit = revWalk.parseCommit(commit.getParent(0));
+ try {
+ walk.addTree(parentCommit.getTree());
+ walk.addTree(commit.getTree());
+ return DiffEntry.scan(walk);
+ } finally {
+ walk.release();
+ }
+ }
+
private int countPicks() throws IOException {
int count = 0;
File todoFile = getTodoFile();
@@ -1595,9 +1803,9 @@ public class RebaseCommandTest extends RepositoryTestCase {
// and attempt to rebase
RebaseResult rebaseResult = git.rebase()
.setUpstream("refs/heads/master").call();
- assertEquals(Status.CONFLICTS, rebaseResult.getStatus());
- assertEquals(1, rebaseResult.getConflicts().size());
- assertEquals(FILE1, rebaseResult.getConflicts().get(0));
+ assertEquals(Status.UNCOMMITTED_CHANGES, rebaseResult.getStatus());
+ assertEquals(1, rebaseResult.getUncommittedChanges().size());
+ assertEquals(FILE1, rebaseResult.getUncommittedChanges().get(0));
checkFile(theFile, "dirty the file");
@@ -1642,6 +1850,19 @@ public class RebaseCommandTest extends RepositoryTestCase {
}
@Test
+ public void testRebaseShouldBeAbleToHandleLinesWithoutCommitMessageInRebaseTodoFile()
+ throws IOException {
+ String todo = "pick 1111111 \n" + "pick 2222222 Commit 2\n"
+ + "# Comment line at end\n";
+ write(getTodoFile(), todo);
+
+ List<RebaseTodoLine> steps = db.readRebaseTodo(GIT_REBASE_TODO, false);
+ assertEquals(2, steps.size());
+ assertEquals("1111111", steps.get(0).getCommit().name());
+ assertEquals("2222222", steps.get(1).getCommit().name());
+ }
+
+ @Test
public void testRebaseShouldNotFailIfUserAddCommentLinesInPrepareSteps()
throws Exception {
commitFile(FILE1, FILE1, "master");
@@ -2237,6 +2458,44 @@ public class RebaseCommandTest extends RepositoryTestCase {
head1Commit.getFullMessage());
}
+ @Test
+ public void testRebaseInteractiveFixupWithBlankLines() throws Exception {
+ // create file1 on master
+ writeTrashFile(FILE1, FILE1);
+ git.add().addFilepattern(FILE1).call();
+ git.commit().setMessage("Add file1\nnew line").call();
+ assertTrue(new File(db.getWorkTree(), FILE1).exists());
+
+ // create file2 on master
+ writeTrashFile("file2", "file2");
+ git.add().addFilepattern("file2").call();
+ git.commit().setMessage("Add file2").call();
+ assertTrue(new File(db.getWorkTree(), "file2").exists());
+
+ // update FILE1 on master
+ writeTrashFile(FILE1, "blah");
+ git.add().addFilepattern(FILE1).call();
+ git.commit().setMessage("updated file1 on master\n\nsome text").call();
+
+ git.rebase().setUpstream("HEAD~2")
+ .runInteractively(new InteractiveHandler() {
+
+ public void prepareSteps(List<RebaseTodoLine> steps) {
+ steps.get(1).setAction(Action.FIXUP);
+ }
+
+ public String modifyCommitMessage(String commit) {
+ fail("No callback to modify commit message expected for single fixup");
+ return commit;
+ }
+ }).call();
+
+ RevWalk walk = new RevWalk(db);
+ ObjectId headId = db.resolve(Constants.HEAD);
+ RevCommit headCommit = walk.parseCommit(headId);
+ assertEquals("Add file2",
+ headCommit.getFullMessage());
+ }
@Test(expected = InvalidRebaseStepException.class)
public void testRebaseInteractiveFixupFirstCommitShouldFail()
@@ -2534,6 +2793,59 @@ public class RebaseCommandTest extends RepositoryTestCase {
}
+ @Test
+ public void testInteractiveRebaseWithModificationShouldNotDeleteDataOnAbort()
+ throws Exception {
+ // create file0 + file1, add and commit
+ writeTrashFile("file0", "file0");
+ writeTrashFile(FILE1, "file1");
+ git.add().addFilepattern("file0").addFilepattern(FILE1).call();
+ git.commit().setMessage("commit1").call();
+
+ // modify file1, add and commit
+ writeTrashFile(FILE1, "modified file1");
+ git.add().addFilepattern(FILE1).call();
+ git.commit().setMessage("commit2").call();
+
+ // modify file1, add and commit
+ writeTrashFile(FILE1, "modified file1 a second time");
+ git.add().addFilepattern(FILE1).call();
+ git.commit().setMessage("commit3").call();
+
+ // modify file0, but do not commit
+ writeTrashFile("file0", "modified file0 in index");
+ git.add().addFilepattern("file0").addFilepattern(FILE1).call();
+ // do not commit
+ writeTrashFile("file0", "modified file0");
+
+ // start rebase
+ RebaseResult result = git.rebase().setUpstream("HEAD~2")
+ .runInteractively(new InteractiveHandler() {
+
+ public void prepareSteps(List<RebaseTodoLine> steps) {
+ steps.get(0).setAction(Action.EDIT);
+ steps.get(1).setAction(Action.PICK);
+ }
+
+ public String modifyCommitMessage(String commit) {
+ return commit;
+ }
+ }).call();
+ // the following condition was true before commit 83b6ab233:
+ // jgit started the rebase and deleted the change on abort
+ // This test should verify that content was deleted
+ if (result.getStatus() == Status.EDIT)
+ git.rebase().setOperation(Operation.ABORT).call();
+
+ checkFile(new File(db.getWorkTree(), "file0"), "modified file0");
+ checkFile(new File(db.getWorkTree(), "file1"),
+ "modified file1 a second time");
+ assertEquals("[file0, mode:100644, content:modified file0 in index]"
+ + "[file1, mode:100644, content:modified file1 a second time]",
+ indexState(CONTENT));
+
+ }
+
private File getTodoFile() {
File todoFile = new File(db.getDirectory(), GIT_REBASE_TODO);
return todoFile;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java
index 16e80f1bfa..2834100389 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java
@@ -409,6 +409,93 @@ public class StashApplyCommandTest extends RepositoryTestCase {
}
@Test
+ public void stashedApplyOnOtherBranch() throws Exception {
+ writeTrashFile(PATH, "content\nmore content\n");
+ git.add().addFilepattern(PATH).call();
+ git.commit().setMessage("more content").call();
+ String path2 = "file2.txt";
+ File file2 = writeTrashFile(path2, "content\nmore content\n");
+ git.add().addFilepattern(PATH).call();
+ git.add().addFilepattern(path2).call();
+ git.commit().setMessage("even content").call();
+
+ String otherBranch = "otherBranch";
+ git.branchCreate().setName(otherBranch).call();
+
+ writeTrashFile(PATH, "master content");
+ git.add().addFilepattern(PATH).call();
+ git.commit().setMessage("even content").call();
+
+ git.checkout().setName(otherBranch).call();
+
+ writeTrashFile(PATH, "otherBranch content");
+ git.add().addFilepattern(PATH).call();
+ git.commit().setMessage("even more content").call();
+
+ writeTrashFile(path2, "content\nstashed change\nmore content\n");
+
+ RevCommit stashed = git.stashCreate().call();
+
+ assertNotNull(stashed);
+ assertEquals("content\nmore content\n", read(file2));
+ assertEquals("otherBranch content",
+ read(committedFile));
+ assertTrue(git.status().call().isClean());
+
+ git.checkout().setName("master").call();
+ git.stashApply().call();
+ assertEquals("content\nstashed change\nmore content\n", read(file2));
+ assertEquals("master content",
+ read(committedFile));
+ }
+
+ @Test
+ public void stashedApplyOnOtherBranchWithStagedChange() throws Exception {
+ writeTrashFile(PATH, "content\nmore content\n");
+ git.add().addFilepattern(PATH).call();
+ git.commit().setMessage("more content").call();
+ String path2 = "file2.txt";
+ File file2 = writeTrashFile(path2, "content\nmore content\n");
+ git.add().addFilepattern(PATH).call();
+ git.add().addFilepattern(path2).call();
+ git.commit().setMessage("even content").call();
+
+ String otherBranch = "otherBranch";
+ git.branchCreate().setName(otherBranch).call();
+
+ writeTrashFile(PATH, "master content");
+ git.add().addFilepattern(PATH).call();
+ git.commit().setMessage("even content").call();
+
+ git.checkout().setName(otherBranch).call();
+
+ writeTrashFile(PATH, "otherBranch content");
+ git.add().addFilepattern(PATH).call();
+ git.commit().setMessage("even more content").call();
+
+ writeTrashFile(path2,
+ "content\nstashed change in index\nmore content\n");
+ git.add().addFilepattern(path2).call();
+ writeTrashFile(path2, "content\nstashed change\nmore content\n");
+
+ RevCommit stashed = git.stashCreate().call();
+
+ assertNotNull(stashed);
+ assertEquals("content\nmore content\n", read(file2));
+ assertEquals("otherBranch content", read(committedFile));
+ assertTrue(git.status().call().isClean());
+
+ git.checkout().setName("master").call();
+ git.stashApply().call();
+ assertEquals("content\nstashed change\nmore content\n", read(file2));
+ assertEquals(
+ "[file.txt, mode:100644, content:master content]"
+ + "[file2.txt, mode:100644, content:content\nstashed change in index\nmore content\n]",
+ indexState(CONTENT));
+ assertEquals("master content", read(committedFile));
+ }
+
+ @Test
public void workingDirectoryContentMerge() throws Exception {
writeTrashFile(PATH, "content\nmore content\n");
git.add().addFilepattern(PATH).call();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherTest.java
index d911efc1d1..aa98696b24 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherTest.java
@@ -340,6 +340,18 @@ public class IgnoreMatcherTest {
assertEquals(r.getPattern(), "/patter?");
}
+ @Test
+ public void testResetState() {
+ String pattern = "/build/*";
+ String target = "/build";
+ // Don't use the assert methods of this class, as we want to test
+ // whether the state in IgnoreRule is reset properly
+ IgnoreRule r = new IgnoreRule(pattern);
+ // Result should be the same for the same inputs
+ assertFalse(r.isMatch(target, true));
+ assertFalse(r.isMatch(target, true));
+ }
+
/**
* Check for a match. If target ends with "/", match will assume that the
* target is meant to be a directory.
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GCTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GCTest.java
deleted file mode 100644
index 9d2a03b097..0000000000
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GCTest.java
+++ /dev/null
@@ -1,741 +0,0 @@
-/*
- * Copyright (C) 2012, Christian Halstrick <christian.halstrick@sap.com>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package org.eclipse.jgit.internal.storage.file;
-
-import static java.lang.Integer.valueOf;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.concurrent.BrokenBarrierException;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CyclicBarrier;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.internal.storage.file.GC.RepoStatistics;
-import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
-import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
-import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.eclipse.jgit.junit.TestRepository;
-import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
-import org.eclipse.jgit.junit.TestRepository.CommitBuilder;
-import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.EmptyProgressMonitor;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref.Storage;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.RefUpdate.Result;
-import org.eclipse.jgit.merge.MergeStrategy;
-import org.eclipse.jgit.merge.Merger;
-import org.eclipse.jgit.revwalk.RevBlob;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevTag;
-import org.eclipse.jgit.revwalk.RevTree;
-import org.eclipse.jgit.util.FileUtils;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class GCTest extends LocalDiskRepositoryTestCase {
- private TestRepository<FileRepository> tr;
-
- private FileRepository repo;
-
- private GC gc;
-
- private RepoStatistics stats;
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
- repo = createWorkRepository();
- tr = new TestRepository<FileRepository>((repo));
- gc = new GC(repo);
- }
-
- @After
- public void tearDown() throws Exception {
- super.tearDown();
- }
-
- // GC.packRefs tests
-
- @Test
- public void packRefs_looseRefPacked() throws Exception {
- RevBlob a = tr.blob("a");
- tr.lightweightTag("t", a);
-
- gc.packRefs();
- assertSame(repo.getRef("t").getStorage(), Storage.PACKED);
- }
-
- @Test
- public void concurrentPackRefs_onlyOneWritesPackedRefs() throws Exception {
- RevBlob a = tr.blob("a");
- tr.lightweightTag("t", a);
-
- final CyclicBarrier syncPoint = new CyclicBarrier(2);
-
- Callable<Integer> packRefs = new Callable<Integer>() {
-
- /** @return 0 for success, 1 in case of error when writing pack */
- public Integer call() throws Exception {
- syncPoint.await();
- try {
- gc.packRefs();
- return valueOf(0);
- } catch (IOException e) {
- return valueOf(1);
- }
- }
- };
- ExecutorService pool = Executors.newFixedThreadPool(2);
- try {
- Future<Integer> p1 = pool.submit(packRefs);
- Future<Integer> p2 = pool.submit(packRefs);
- assertEquals(1, p1.get().intValue() + p2.get().intValue());
- } finally {
- pool.shutdown();
- pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
- }
- }
-
- @Test
- public void packRefsWhileRefLocked_refNotPackedNoError()
- throws Exception {
- RevBlob a = tr.blob("a");
- tr.lightweightTag("t1", a);
- tr.lightweightTag("t2", a);
- LockFile refLock = new LockFile(new File(repo.getDirectory(),
- "refs/tags/t1"), repo.getFS());
- try {
- refLock.lock();
- gc.packRefs();
- } finally {
- refLock.unlock();
- }
-
- assertSame(repo.getRef("refs/tags/t1").getStorage(), Storage.LOOSE);
- assertSame(repo.getRef("refs/tags/t2").getStorage(), Storage.PACKED);
- }
-
- @Test
- public void packRefsWhileRefUpdated_refUpdateSucceeds()
- throws Exception {
- RevBlob a = tr.blob("a");
- tr.lightweightTag("t", a);
- final RevBlob b = tr.blob("b");
-
- final CyclicBarrier refUpdateLockedRef = new CyclicBarrier(2);
- final CyclicBarrier packRefsDone = new CyclicBarrier(2);
- ExecutorService pool = Executors.newFixedThreadPool(2);
- try {
- Future<Result> result = pool.submit(new Callable<Result>() {
-
- public Result call() throws Exception {
- RefUpdate update = new RefDirectoryUpdate(
- (RefDirectory) repo.getRefDatabase(),
- repo.getRef("refs/tags/t")) {
- @Override
- public boolean isForceUpdate() {
- try {
- refUpdateLockedRef.await();
- packRefsDone.await();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- } catch (BrokenBarrierException e) {
- Thread.currentThread().interrupt();
- }
- return super.isForceUpdate();
- }
- };
- update.setForceUpdate(true);
- update.setNewObjectId(b);
- return update.update();
- }
- });
-
- pool.submit(new Callable<Void>() {
- public Void call() throws Exception {
- refUpdateLockedRef.await();
- gc.packRefs();
- packRefsDone.await();
- return null;
- }
- });
-
- assertSame(result.get(), Result.FORCED);
-
- } finally {
- pool.shutdownNow();
- pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
- }
-
- assertEquals(repo.getRef("refs/tags/t").getObjectId(), b);
- }
-
- // GC.repack tests
-
- @Test
- public void repackEmptyRepo_noPackCreated() throws IOException {
- gc.repack();
- assertEquals(0, repo.getObjectDatabase().getPacks().size());
- }
-
- @Test
- public void concurrentRepack() throws Exception {
- final CyclicBarrier syncPoint = new CyclicBarrier(2);
-
- class DoRepack extends EmptyProgressMonitor implements
- Callable<Integer> {
-
- public void beginTask(String title, int totalWork) {
- if (title.equals(JGitText.get().writingObjects)) {
- try {
- syncPoint.await();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- } catch (BrokenBarrierException ignored) {
- //
- }
- }
- }
-
- /** @return 0 for success, 1 in case of error when writing pack */
- public Integer call() throws Exception {
- try {
- gc.setProgressMonitor(this);
- gc.repack();
- return valueOf(0);
- } catch (IOException e) {
- // leave the syncPoint in broken state so any awaiting
- // threads and any threads that call await in the future get
- // the BrokenBarrierException
- Thread.currentThread().interrupt();
- try {
- syncPoint.await();
- } catch (InterruptedException ignored) {
- //
- }
- return valueOf(1);
- }
- }
- }
-
- RevBlob a = tr.blob("a");
- tr.lightweightTag("t", a);
-
- ExecutorService pool = Executors.newFixedThreadPool(2);
- try {
- DoRepack repack1 = new DoRepack();
- DoRepack repack2 = new DoRepack();
- Future<Integer> result1 = pool.submit(repack1);
- Future<Integer> result2 = pool.submit(repack2);
- assertEquals(0, result1.get().intValue() + result2.get().intValue());
- } finally {
- pool.shutdown();
- pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
- }
- }
-
- // GC.prune tests
-
- @Test
- public void nonReferencedNonExpiredObject_notPruned() throws Exception {
- RevBlob a = tr.blob("a");
- gc.setExpire(new Date(lastModified(a)));
- gc.prune(Collections.<ObjectId> emptySet());
- assertTrue(repo.hasObject(a));
- }
-
- @Test
- public void nonReferencedExpiredObject_pruned() throws Exception {
- RevBlob a = tr.blob("a");
- gc.setExpireAgeMillis(0);
- fsTick();
- gc.prune(Collections.<ObjectId> emptySet());
- assertFalse(repo.hasObject(a));
- }
-
- @Test
- public void nonReferencedExpiredObjectTree_pruned() throws Exception {
- RevBlob a = tr.blob("a");
- RevTree t = tr.tree(tr.file("a", a));
- gc.setExpireAgeMillis(0);
- fsTick();
- gc.prune(Collections.<ObjectId> emptySet());
- assertFalse(repo.hasObject(t));
- assertFalse(repo.hasObject(a));
- }
-
- @Test
- public void nonReferencedObjects_onlyExpiredPruned() throws Exception {
- RevBlob a = tr.blob("a");
- gc.setExpire(new Date(lastModified(a) + 1));
-
- fsTick();
- RevBlob b = tr.blob("b");
-
- gc.prune(Collections.<ObjectId> emptySet());
- assertFalse(repo.hasObject(a));
- assertTrue(repo.hasObject(b));
- }
-
- @Test
- public void lightweightTag_objectNotPruned() throws Exception {
- RevBlob a = tr.blob("a");
- tr.lightweightTag("t", a);
- gc.setExpireAgeMillis(0);
- fsTick();
- gc.prune(Collections.<ObjectId> emptySet());
- assertTrue(repo.hasObject(a));
- }
-
- @Test
- public void annotatedTag_objectNotPruned() throws Exception {
- RevBlob a = tr.blob("a");
- RevTag t = tr.tag("t", a); // this doesn't create the refs/tags/t ref
- tr.lightweightTag("t", t);
-
- gc.setExpireAgeMillis(0);
- fsTick();
- gc.prune(Collections.<ObjectId> emptySet());
- assertTrue(repo.hasObject(t));
- assertTrue(repo.hasObject(a));
- }
-
- @Test
- public void branch_historyNotPruned() throws Exception {
- RevCommit tip = commitChain(10);
- tr.branch("b").update(tip);
- gc.setExpireAgeMillis(0);
- fsTick();
- gc.prune(Collections.<ObjectId> emptySet());
- do {
- assertTrue(repo.hasObject(tip));
- tr.parseBody(tip);
- RevTree t = tip.getTree();
- assertTrue(repo.hasObject(t));
- assertTrue(repo.hasObject(tr.get(t, "a")));
- tip = tip.getParentCount() > 0 ? tip.getParent(0) : null;
- } while (tip != null);
- }
-
- @Test
- public void deleteBranch_historyPruned() throws Exception {
- RevCommit tip = commitChain(10);
- tr.branch("b").update(tip);
- RefUpdate update = repo.updateRef("refs/heads/b");
- update.setForceUpdate(true);
- update.delete();
- gc.setExpireAgeMillis(0);
- fsTick();
- gc.prune(Collections.<ObjectId> emptySet());
- assertTrue(gc.getStatistics().numberOfLooseObjects == 0);
- }
-
- @Test
- public void deleteMergedBranch_historyNotPruned() throws Exception {
- RevCommit parent = tr.commit().create();
- RevCommit b1Tip = tr.branch("b1").commit().parent(parent).add("x", "x")
- .create();
- RevCommit b2Tip = tr.branch("b2").commit().parent(parent).add("y", "y")
- .create();
-
- // merge b1Tip and b2Tip and update refs/heads/b1 to the merge commit
- Merger merger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(repo);
- merger.merge(b1Tip, b2Tip);
- CommitBuilder cb = tr.commit();
- cb.parent(b1Tip).parent(b2Tip);
- cb.setTopLevelTree(merger.getResultTreeId());
- RevCommit mergeCommit = cb.create();
- RefUpdate u = repo.updateRef("refs/heads/b1");
- u.setNewObjectId(mergeCommit);
- u.update();
-
- RefUpdate update = repo.updateRef("refs/heads/b2");
- update.setForceUpdate(true);
- update.delete();
-
- gc.setExpireAgeMillis(0);
- fsTick();
- gc.prune(Collections.<ObjectId> emptySet());
- assertTrue(repo.hasObject(b2Tip));
- }
-
- @Test
- public void testPackAllObjectsInOnePack() throws Exception {
- tr.branch("refs/heads/master").commit().add("A", "A").add("B", "B")
- .create();
- stats = gc.getStatistics();
- assertEquals(4, stats.numberOfLooseObjects);
- assertEquals(0, stats.numberOfPackedObjects);
- gc.gc();
- stats = gc.getStatistics();
- assertEquals(0, stats.numberOfLooseObjects);
- assertEquals(4, stats.numberOfPackedObjects);
- assertEquals(1, stats.numberOfPackFiles);
-
- // Do the gc again and check that it hasn't changed anything
- gc.gc();
- stats = gc.getStatistics();
- assertEquals(0, stats.numberOfLooseObjects);
- assertEquals(4, stats.numberOfPackedObjects);
- assertEquals(1, stats.numberOfPackFiles);
- }
-
- @Test
- public void testPackRepoWithCorruptReflog() throws Exception {
- // create a reflog entry "0000... 0000... foobar" by doing an initial
- // refupdate for HEAD which points to a non-existing ref. The
- // All-Projects repo of gerrit instances had such entries
- RefUpdate ru = repo.updateRef(Constants.HEAD);
- ru.link("refs/to/garbage");
- tr.branch("refs/heads/master").commit().add("A", "A").add("B", "B")
- .create();
- // make sure HEAD exists
- Git.wrap(repo).checkout().setName("refs/heads/master").call();
- gc.gc();
- }
-
- @Test
- public void testKeepFiles() throws Exception {
- BranchBuilder bb = tr.branch("refs/heads/master");
- bb.commit().add("A", "A").add("B", "B").create();
- stats = gc.getStatistics();
- assertEquals(4, stats.numberOfLooseObjects);
- assertEquals(0, stats.numberOfPackedObjects);
- assertEquals(0, stats.numberOfPackFiles);
- gc.gc();
- stats = gc.getStatistics();
- assertEquals(0, stats.numberOfLooseObjects);
- assertEquals(4, stats.numberOfPackedObjects);
- assertEquals(1, stats.numberOfPackFiles);
-
- Iterator<PackFile> packIt = repo.getObjectDatabase().getPacks()
- .iterator();
- PackFile singlePack = packIt.next();
- assertFalse(packIt.hasNext());
- File keepFile = new File(singlePack.getPackFile().getPath() + ".keep");
- assertFalse(keepFile.exists());
- assertTrue(keepFile.createNewFile());
- bb.commit().add("A", "A2").add("B", "B2").create();
- stats = gc.getStatistics();
- assertEquals(4, stats.numberOfLooseObjects);
- assertEquals(4, stats.numberOfPackedObjects);
- assertEquals(1, stats.numberOfPackFiles);
- gc.gc();
- stats = gc.getStatistics();
- assertEquals(0, stats.numberOfLooseObjects);
- assertEquals(8, stats.numberOfPackedObjects);
- assertEquals(2, stats.numberOfPackFiles);
-
- // check that no object is packed twice
- Iterator<PackFile> packs = repo.getObjectDatabase().getPacks()
- .iterator();
- PackIndex ind1 = packs.next().getIndex();
- assertEquals(4, ind1.getObjectCount());
- PackIndex ind2 = packs.next().getIndex();
- assertEquals(4, ind2.getObjectCount());
- for (MutableEntry e: ind1)
- if (ind2.hasObject(e.toObjectId()))
- assertFalse(
- "the following object is in both packfiles: "
- + e.toObjectId(),
- ind2.hasObject(e.toObjectId()));
- }
-
- @Test
- public void testPackRepoWithNoRefs() throws Exception {
- tr.commit().add("A", "A").add("B", "B").create();
- stats = gc.getStatistics();
- assertEquals(4, stats.numberOfLooseObjects);
- assertEquals(0, stats.numberOfPackedObjects);
- gc.gc();
- stats = gc.getStatistics();
- assertEquals(4, stats.numberOfLooseObjects);
- assertEquals(0, stats.numberOfPackedObjects);
- assertEquals(0, stats.numberOfPackFiles);
- }
-
- @Test
- public void testPack2Commits() throws Exception {
- BranchBuilder bb = tr.branch("refs/heads/master");
- bb.commit().add("A", "A").add("B", "B").create();
- bb.commit().add("A", "A2").add("B", "B2").create();
-
- stats = gc.getStatistics();
- assertEquals(8, stats.numberOfLooseObjects);
- assertEquals(0, stats.numberOfPackedObjects);
- gc.gc();
- stats = gc.getStatistics();
- assertEquals(0, stats.numberOfLooseObjects);
- assertEquals(8, stats.numberOfPackedObjects);
- assertEquals(1, stats.numberOfPackFiles);
- }
-
- @Test
- public void testPackCommitsAndLooseOne() throws Exception {
- BranchBuilder bb = tr.branch("refs/heads/master");
- RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
- bb.commit().add("A", "A2").add("B", "B2").create();
- tr.update("refs/heads/master", first);
-
- stats = gc.getStatistics();
- assertEquals(8, stats.numberOfLooseObjects);
- assertEquals(0, stats.numberOfPackedObjects);
- gc.gc();
- stats = gc.getStatistics();
- assertEquals(0, stats.numberOfLooseObjects);
- assertEquals(8, stats.numberOfPackedObjects);
- assertEquals(2, stats.numberOfPackFiles);
- }
-
- @Test
- public void testNotPackTwice() throws Exception {
- BranchBuilder bb = tr.branch("refs/heads/master");
- RevCommit first = bb.commit().message("M").add("M", "M").create();
- bb.commit().message("B").add("B", "Q").create();
- bb.commit().message("A").add("A", "A").create();
- RevCommit second = tr.commit().parent(first).message("R").add("R", "Q")
- .create();
- tr.update("refs/tags/t1", second);
-
- Collection<PackFile> oldPacks = tr.getRepository().getObjectDatabase()
- .getPacks();
- assertEquals(0, oldPacks.size());
- stats = gc.getStatistics();
- assertEquals(11, stats.numberOfLooseObjects);
- assertEquals(0, stats.numberOfPackedObjects);
-
- gc.setExpireAgeMillis(0);
- fsTick();
- gc.gc();
- stats = gc.getStatistics();
- assertEquals(0, stats.numberOfLooseObjects);
-
- Iterator<PackFile> pIt = repo.getObjectDatabase().getPacks().iterator();
- long c = pIt.next().getObjectCount();
- if (c == 9)
- assertEquals(2, pIt.next().getObjectCount());
- else {
- assertEquals(2, c);
- assertEquals(9, pIt.next().getObjectCount());
- }
- }
-
- @Test
- public void testPackCommitsAndLooseOneNoReflog() throws Exception {
- BranchBuilder bb = tr.branch("refs/heads/master");
- RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
- bb.commit().add("A", "A2").add("B", "B2").create();
- tr.update("refs/heads/master", first);
-
- stats = gc.getStatistics();
- assertEquals(8, stats.numberOfLooseObjects);
- assertEquals(0, stats.numberOfPackedObjects);
-
- FileUtils.delete(new File(repo.getDirectory(), "logs/HEAD"),
- FileUtils.RETRY | FileUtils.SKIP_MISSING);
- FileUtils.delete(
- new File(repo.getDirectory(), "logs/refs/heads/master"),
- FileUtils.RETRY | FileUtils.SKIP_MISSING);
- gc.gc();
-
- stats = gc.getStatistics();
- assertEquals(4, stats.numberOfLooseObjects);
- assertEquals(4, stats.numberOfPackedObjects);
- assertEquals(1, stats.numberOfPackFiles);
- }
-
- @Test
- public void testPackCommitsAndLooseOneWithPruneNow() throws Exception {
- BranchBuilder bb = tr.branch("refs/heads/master");
- RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
- bb.commit().add("A", "A2").add("B", "B2").create();
- tr.update("refs/heads/master", first);
-
- stats = gc.getStatistics();
- assertEquals(8, stats.numberOfLooseObjects);
- assertEquals(0, stats.numberOfPackedObjects);
- gc.setExpireAgeMillis(0);
- fsTick();
- gc.gc();
- stats = gc.getStatistics();
- assertEquals(0, stats.numberOfLooseObjects);
- assertEquals(8, stats.numberOfPackedObjects);
- assertEquals(2, stats.numberOfPackFiles);
- }
-
- @Test
- public void testPackCommitsAndLooseOneWithPruneNowNoReflog()
- throws Exception {
- BranchBuilder bb = tr.branch("refs/heads/master");
- RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
- bb.commit().add("A", "A2").add("B", "B2").create();
- tr.update("refs/heads/master", first);
-
- stats = gc.getStatistics();
- assertEquals(8, stats.numberOfLooseObjects);
- assertEquals(0, stats.numberOfPackedObjects);
-
- FileUtils.delete(new File(repo.getDirectory(), "logs/HEAD"),
- FileUtils.RETRY | FileUtils.SKIP_MISSING);
- FileUtils.delete(
- new File(repo.getDirectory(), "logs/refs/heads/master"),
- FileUtils.RETRY | FileUtils.SKIP_MISSING);
- gc.setExpireAgeMillis(0);
- gc.gc();
-
- stats = gc.getStatistics();
- assertEquals(0, stats.numberOfLooseObjects);
- assertEquals(4, stats.numberOfPackedObjects);
- assertEquals(1, stats.numberOfPackFiles);
- }
-
- @Test
- public void testIndexSavesObjects() throws Exception {
- BranchBuilder bb = tr.branch("refs/heads/master");
- bb.commit().add("A", "A").add("B", "B").create();
- bb.commit().add("A", "A2").add("B", "B2").create();
- bb.commit().add("A", "A3"); // this new content in index should survive
- stats = gc.getStatistics();
- assertEquals(9, stats.numberOfLooseObjects);
- assertEquals(0, stats.numberOfPackedObjects);
- gc.gc();
- stats = gc.getStatistics();
- assertEquals(1, stats.numberOfLooseObjects);
- assertEquals(8, stats.numberOfPackedObjects);
- assertEquals(1, stats.numberOfPackFiles);
- }
-
- @Test
- public void testIndexSavesObjectsWithPruneNow() throws Exception {
- BranchBuilder bb = tr.branch("refs/heads/master");
- bb.commit().add("A", "A").add("B", "B").create();
- bb.commit().add("A", "A2").add("B", "B2").create();
- bb.commit().add("A", "A3"); // this new content in index should survive
- stats = gc.getStatistics();
- assertEquals(9, stats.numberOfLooseObjects);
- assertEquals(0, stats.numberOfPackedObjects);
- gc.setExpireAgeMillis(0);
- fsTick();
- gc.gc();
- stats = gc.getStatistics();
- assertEquals(0, stats.numberOfLooseObjects);
- assertEquals(8, stats.numberOfPackedObjects);
- assertEquals(1, stats.numberOfPackFiles);
- }
-
- @Test
- public void testPruneNone() throws Exception {
- BranchBuilder bb = tr.branch("refs/heads/master");
- bb.commit().add("A", "A").add("B", "B").create();
- bb.commit().add("A", "A2").add("B", "B2").create();
- new File(repo.getDirectory(), Constants.LOGS + "/refs/heads/master")
- .delete();
- stats = gc.getStatistics();
- assertEquals(8, stats.numberOfLooseObjects);
- gc.setExpireAgeMillis(0);
- fsTick();
- gc.prune(Collections.<ObjectId> emptySet());
- stats = gc.getStatistics();
- assertEquals(8, stats.numberOfLooseObjects);
- tr.blob("x");
- stats = gc.getStatistics();
- assertEquals(9, stats.numberOfLooseObjects);
- gc.prune(Collections.<ObjectId> emptySet());
- stats = gc.getStatistics();
- assertEquals(8, stats.numberOfLooseObjects);
- }
-
- /**
- * Create a chain of commits of given depth.
- * <p>
- * Each commit contains one file named "a" containing the index of the
- * commit in the chain as its content. The created commit chain is
- * referenced from any ref.
- * <p>
- * A chain of depth = N will create 3*N objects in Gits object database. For
- * each depth level three objects are created: the commit object, the
- * top-level tree object and a blob for the content of the file "a".
- *
- * @param depth
- * the depth of the commit chain.
- * @return the commit that is the tip of the commit chain
- * @throws Exception
- */
- private RevCommit commitChain(int depth) throws Exception {
- if (depth <= 0)
- throw new IllegalArgumentException("Chain depth must be > 0");
- CommitBuilder cb = tr.commit();
- RevCommit tip;
- do {
- --depth;
- tip = cb.add("a", "" + depth).message("" + depth).create();
- cb = cb.child();
- } while (depth > 0);
- return tip;
- }
-
- private long lastModified(AnyObjectId objectId) {
- return repo.getObjectDatabase().fileFor(objectId).lastModified();
- }
-
- private static void fsTick() throws InterruptedException, IOException {
- RepositoryTestCase.fsTick(null);
- }
-}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
new file mode 100644
index 0000000000..0f27099c09
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2012, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Test;
+
+public class GcBasicPackingTest extends GcTestCase {
+ @Test
+ public void repackEmptyRepo_noPackCreated() throws IOException {
+ gc.repack();
+ assertEquals(0, repo.getObjectDatabase().getPacks().size());
+ }
+
+ @Test
+ public void testPackRepoWithNoRefs() throws Exception {
+ tr.commit().add("A", "A").add("B", "B").create();
+ stats = gc.getStatistics();
+ assertEquals(4, stats.numberOfLooseObjects);
+ assertEquals(0, stats.numberOfPackedObjects);
+ gc.gc();
+ stats = gc.getStatistics();
+ assertEquals(4, stats.numberOfLooseObjects);
+ assertEquals(0, stats.numberOfPackedObjects);
+ assertEquals(0, stats.numberOfPackFiles);
+ }
+
+ @Test
+ public void testPack2Commits() throws Exception {
+ BranchBuilder bb = tr.branch("refs/heads/master");
+ bb.commit().add("A", "A").add("B", "B").create();
+ bb.commit().add("A", "A2").add("B", "B2").create();
+
+ stats = gc.getStatistics();
+ assertEquals(8, stats.numberOfLooseObjects);
+ assertEquals(0, stats.numberOfPackedObjects);
+ gc.gc();
+ stats = gc.getStatistics();
+ assertEquals(0, stats.numberOfLooseObjects);
+ assertEquals(8, stats.numberOfPackedObjects);
+ assertEquals(1, stats.numberOfPackFiles);
+ }
+
+ @Test
+ public void testPackAllObjectsInOnePack() throws Exception {
+ tr.branch("refs/heads/master").commit().add("A", "A").add("B", "B")
+ .create();
+ stats = gc.getStatistics();
+ assertEquals(4, stats.numberOfLooseObjects);
+ assertEquals(0, stats.numberOfPackedObjects);
+ gc.gc();
+ stats = gc.getStatistics();
+ assertEquals(0, stats.numberOfLooseObjects);
+ assertEquals(4, stats.numberOfPackedObjects);
+ assertEquals(1, stats.numberOfPackFiles);
+
+ // Do the gc again and check that it hasn't changed anything
+ gc.gc();
+ stats = gc.getStatistics();
+ assertEquals(0, stats.numberOfLooseObjects);
+ assertEquals(4, stats.numberOfPackedObjects);
+ assertEquals(1, stats.numberOfPackFiles);
+ }
+
+ @Test
+ public void testPackCommitsAndLooseOne() throws Exception {
+ BranchBuilder bb = tr.branch("refs/heads/master");
+ RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
+ bb.commit().add("A", "A2").add("B", "B2").create();
+ tr.update("refs/heads/master", first);
+
+ stats = gc.getStatistics();
+ assertEquals(8, stats.numberOfLooseObjects);
+ assertEquals(0, stats.numberOfPackedObjects);
+ gc.gc();
+ stats = gc.getStatistics();
+ assertEquals(0, stats.numberOfLooseObjects);
+ assertEquals(8, stats.numberOfPackedObjects);
+ assertEquals(2, stats.numberOfPackFiles);
+ }
+
+ @Test
+ public void testNotPackTwice() throws Exception {
+ BranchBuilder bb = tr.branch("refs/heads/master");
+ RevCommit first = bb.commit().message("M").add("M", "M").create();
+ bb.commit().message("B").add("B", "Q").create();
+ bb.commit().message("A").add("A", "A").create();
+ RevCommit second = tr.commit().parent(first).message("R").add("R", "Q")
+ .create();
+ tr.update("refs/tags/t1", second);
+
+ Collection<PackFile> oldPacks = tr.getRepository().getObjectDatabase()
+ .getPacks();
+ assertEquals(0, oldPacks.size());
+ stats = gc.getStatistics();
+ assertEquals(11, stats.numberOfLooseObjects);
+ assertEquals(0, stats.numberOfPackedObjects);
+
+ gc.setExpireAgeMillis(0);
+ fsTick();
+ gc.gc();
+ stats = gc.getStatistics();
+ assertEquals(0, stats.numberOfLooseObjects);
+
+ Iterator<PackFile> pIt = repo.getObjectDatabase().getPacks().iterator();
+ long c = pIt.next().getObjectCount();
+ if (c == 9)
+ assertEquals(2, pIt.next().getObjectCount());
+ else {
+ assertEquals(2, c);
+ assertEquals(9, pIt.next().getObjectCount());
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBranchPrunedTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBranchPrunedTest.java
new file mode 100644
index 0000000000..c7ee9256d7
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBranchPrunedTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2012, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+
+import org.eclipse.jgit.junit.TestRepository.CommitBuilder;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.merge.MergeStrategy;
+import org.eclipse.jgit.merge.Merger;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.junit.Test;
+
+public class GcBranchPrunedTest extends GcTestCase {
+
+ @Test
+ public void branch_historyNotPruned() throws Exception {
+ RevCommit tip = commitChain(10);
+ tr.branch("b").update(tip);
+ gc.setExpireAgeMillis(0);
+ fsTick();
+ gc.prune(Collections.<ObjectId> emptySet());
+ do {
+ assertTrue(repo.hasObject(tip));
+ tr.parseBody(tip);
+ RevTree t = tip.getTree();
+ assertTrue(repo.hasObject(t));
+ assertTrue(repo.hasObject(tr.get(t, "a")));
+ tip = tip.getParentCount() > 0 ? tip.getParent(0) : null;
+ } while (tip != null);
+ }
+
+ @Test
+ public void deleteBranch_historyPruned() throws Exception {
+ RevCommit tip = commitChain(10);
+ tr.branch("b").update(tip);
+ RefUpdate update = repo.updateRef("refs/heads/b");
+ update.setForceUpdate(true);
+ update.delete();
+ gc.setExpireAgeMillis(0);
+ fsTick();
+ gc.prune(Collections.<ObjectId> emptySet());
+ assertTrue(gc.getStatistics().numberOfLooseObjects == 0);
+ }
+
+ @Test
+ public void deleteMergedBranch_historyNotPruned() throws Exception {
+ RevCommit parent = tr.commit().create();
+ RevCommit b1Tip = tr.branch("b1").commit().parent(parent).add("x", "x")
+ .create();
+ RevCommit b2Tip = tr.branch("b2").commit().parent(parent).add("y", "y")
+ .create();
+
+ // merge b1Tip and b2Tip and update refs/heads/b1 to the merge commit
+ Merger merger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(repo);
+ merger.merge(b1Tip, b2Tip);
+ CommitBuilder cb = tr.commit();
+ cb.parent(b1Tip).parent(b2Tip);
+ cb.setTopLevelTree(merger.getResultTreeId());
+ RevCommit mergeCommit = cb.create();
+ RefUpdate u = repo.updateRef("refs/heads/b1");
+ u.setNewObjectId(mergeCommit);
+ u.update();
+
+ RefUpdate update = repo.updateRef("refs/heads/b2");
+ update.setForceUpdate(true);
+ update.delete();
+
+ gc.setExpireAgeMillis(0);
+ fsTick();
+ gc.prune(Collections.<ObjectId> emptySet());
+ assertTrue(repo.hasObject(b2Tip));
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java
new file mode 100644
index 0000000000..07a7be7467
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2012, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import static java.lang.Integer.valueOf;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.EmptyProgressMonitor;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.junit.Test;
+
+public class GcConcurrentTest extends GcTestCase {
+ @Test
+ public void concurrentRepack() throws Exception {
+ final CyclicBarrier syncPoint = new CyclicBarrier(2);
+
+ class DoRepack extends EmptyProgressMonitor implements
+ Callable<Integer> {
+
+ public void beginTask(String title, int totalWork) {
+ if (title.equals(JGitText.get().writingObjects)) {
+ try {
+ syncPoint.await();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ } catch (BrokenBarrierException ignored) {
+ //
+ }
+ }
+ }
+
+ /** @return 0 for success, 1 in case of error when writing pack */
+ public Integer call() throws Exception {
+ try {
+ gc.setProgressMonitor(this);
+ gc.repack();
+ return valueOf(0);
+ } catch (IOException e) {
+ // leave the syncPoint in broken state so any awaiting
+ // threads and any threads that call await in the future get
+ // the BrokenBarrierException
+ Thread.currentThread().interrupt();
+ try {
+ syncPoint.await();
+ } catch (InterruptedException ignored) {
+ //
+ }
+ return valueOf(1);
+ }
+ }
+ }
+
+ RevBlob a = tr.blob("a");
+ tr.lightweightTag("t", a);
+
+ ExecutorService pool = Executors.newFixedThreadPool(2);
+ try {
+ DoRepack repack1 = new DoRepack();
+ DoRepack repack2 = new DoRepack();
+ Future<Integer> result1 = pool.submit(repack1);
+ Future<Integer> result2 = pool.submit(repack2);
+ assertEquals(0, result1.get().intValue() + result2.get().intValue());
+ } finally {
+ pool.shutdown();
+ pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDirCacheSavesObjectsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDirCacheSavesObjectsTest.java
new file mode 100644
index 0000000000..50e497e67a
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDirCacheSavesObjectsTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import static org.junit.Assert.assertEquals;
+
+import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
+import org.junit.Test;
+
+public class GcDirCacheSavesObjectsTest extends GcTestCase {
+ @Test
+ public void testDirCacheSavesObjects() throws Exception {
+ BranchBuilder bb = tr.branch("refs/heads/master");
+ bb.commit().add("A", "A").add("B", "B").create();
+ bb.commit().add("A", "A2").add("B", "B2").create();
+ bb.commit().add("A", "A3"); // this new content in index should survive
+ stats = gc.getStatistics();
+ assertEquals(9, stats.numberOfLooseObjects);
+ assertEquals(0, stats.numberOfPackedObjects);
+ gc.gc();
+ stats = gc.getStatistics();
+ assertEquals(1, stats.numberOfLooseObjects);
+ assertEquals(8, stats.numberOfPackedObjects);
+ assertEquals(1, stats.numberOfPackFiles);
+ }
+
+ @Test
+ public void testDirCacheSavesObjectsWithPruneNow() throws Exception {
+ BranchBuilder bb = tr.branch("refs/heads/master");
+ bb.commit().add("A", "A").add("B", "B").create();
+ bb.commit().add("A", "A2").add("B", "B2").create();
+ bb.commit().add("A", "A3"); // this new content in index should survive
+ stats = gc.getStatistics();
+ assertEquals(9, stats.numberOfLooseObjects);
+ assertEquals(0, stats.numberOfPackedObjects);
+ gc.setExpireAgeMillis(0);
+ fsTick();
+ gc.gc();
+ stats = gc.getStatistics();
+ assertEquals(0, stats.numberOfLooseObjects);
+ assertEquals(8, stats.numberOfPackedObjects);
+ assertEquals(1, stats.numberOfPackFiles);
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java
new file mode 100644
index 0000000000..9e28298823
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2012, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.util.Iterator;
+
+import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
+import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
+import org.junit.Test;
+
+public class GcKeepFilesTest extends GcTestCase {
+ @Test
+ public void testKeepFiles() throws Exception {
+ BranchBuilder bb = tr.branch("refs/heads/master");
+ bb.commit().add("A", "A").add("B", "B").create();
+ stats = gc.getStatistics();
+ assertEquals(4, stats.numberOfLooseObjects);
+ assertEquals(0, stats.numberOfPackedObjects);
+ assertEquals(0, stats.numberOfPackFiles);
+ gc.gc();
+ stats = gc.getStatistics();
+ assertEquals(0, stats.numberOfLooseObjects);
+ assertEquals(4, stats.numberOfPackedObjects);
+ assertEquals(1, stats.numberOfPackFiles);
+
+ Iterator<PackFile> packIt = repo.getObjectDatabase().getPacks()
+ .iterator();
+ PackFile singlePack = packIt.next();
+ assertFalse(packIt.hasNext());
+ File keepFile = new File(singlePack.getPackFile().getPath() + ".keep");
+ assertFalse(keepFile.exists());
+ assertTrue(keepFile.createNewFile());
+ bb.commit().add("A", "A2").add("B", "B2").create();
+ stats = gc.getStatistics();
+ assertEquals(4, stats.numberOfLooseObjects);
+ assertEquals(4, stats.numberOfPackedObjects);
+ assertEquals(1, stats.numberOfPackFiles);
+ gc.gc();
+ stats = gc.getStatistics();
+ assertEquals(0, stats.numberOfLooseObjects);
+ assertEquals(8, stats.numberOfPackedObjects);
+ assertEquals(2, stats.numberOfPackFiles);
+
+ // check that no object is packed twice
+ Iterator<PackFile> packs = repo.getObjectDatabase().getPacks()
+ .iterator();
+ PackIndex ind1 = packs.next().getIndex();
+ assertEquals(4, ind1.getObjectCount());
+ PackIndex ind2 = packs.next().getIndex();
+ assertEquals(4, ind2.getObjectCount());
+ for (MutableEntry e: ind1)
+ if (ind2.hasObject(e.toObjectId()))
+ assertFalse(
+ "the following object is in both packfiles: "
+ + e.toObjectId(),
+ ind2.hasObject(e.toObjectId()));
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
new file mode 100644
index 0000000000..0ade902601
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2012, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import static java.lang.Integer.valueOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jgit.lib.Ref.Storage;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.RefUpdate.Result;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.junit.Test;
+
+public class GcPackRefsTest extends GcTestCase {
+ @Test
+ public void looseRefPacked() throws Exception {
+ RevBlob a = tr.blob("a");
+ tr.lightweightTag("t", a);
+
+ gc.packRefs();
+ assertSame(repo.getRef("t").getStorage(), Storage.PACKED);
+ }
+
+ @Test
+ public void concurrentOnlyOneWritesPackedRefs() throws Exception {
+ RevBlob a = tr.blob("a");
+ tr.lightweightTag("t", a);
+
+ final CyclicBarrier syncPoint = new CyclicBarrier(2);
+
+ Callable<Integer> packRefs = new Callable<Integer>() {
+
+ /** @return 0 for success, 1 in case of error when writing pack */
+ public Integer call() throws Exception {
+ syncPoint.await();
+ try {
+ gc.packRefs();
+ return valueOf(0);
+ } catch (IOException e) {
+ return valueOf(1);
+ }
+ }
+ };
+ ExecutorService pool = Executors.newFixedThreadPool(2);
+ try {
+ Future<Integer> p1 = pool.submit(packRefs);
+ Future<Integer> p2 = pool.submit(packRefs);
+ assertEquals(1, p1.get().intValue() + p2.get().intValue());
+ } finally {
+ pool.shutdown();
+ pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
+ }
+ }
+
+ @Test
+ public void whileRefLockedRefNotPackedNoError()
+ throws Exception {
+ RevBlob a = tr.blob("a");
+ tr.lightweightTag("t1", a);
+ tr.lightweightTag("t2", a);
+ LockFile refLock = new LockFile(new File(repo.getDirectory(),
+ "refs/tags/t1"), repo.getFS());
+ try {
+ refLock.lock();
+ gc.packRefs();
+ } finally {
+ refLock.unlock();
+ }
+
+ assertSame(repo.getRef("refs/tags/t1").getStorage(), Storage.LOOSE);
+ assertSame(repo.getRef("refs/tags/t2").getStorage(), Storage.PACKED);
+ }
+
+ @Test
+ public void whileRefUpdatedRefUpdateSucceeds()
+ throws Exception {
+ RevBlob a = tr.blob("a");
+ tr.lightweightTag("t", a);
+ final RevBlob b = tr.blob("b");
+
+ final CyclicBarrier refUpdateLockedRef = new CyclicBarrier(2);
+ final CyclicBarrier packRefsDone = new CyclicBarrier(2);
+ ExecutorService pool = Executors.newFixedThreadPool(2);
+ try {
+ Future<Result> result = pool.submit(new Callable<Result>() {
+
+ public Result call() throws Exception {
+ RefUpdate update = new RefDirectoryUpdate(
+ (RefDirectory) repo.getRefDatabase(),
+ repo.getRef("refs/tags/t")) {
+ @Override
+ public boolean isForceUpdate() {
+ try {
+ refUpdateLockedRef.await();
+ packRefsDone.await();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ } catch (BrokenBarrierException e) {
+ Thread.currentThread().interrupt();
+ }
+ return super.isForceUpdate();
+ }
+ };
+ update.setForceUpdate(true);
+ update.setNewObjectId(b);
+ return update.update();
+ }
+ });
+
+ pool.submit(new Callable<Void>() {
+ public Void call() throws Exception {
+ refUpdateLockedRef.await();
+ gc.packRefs();
+ packRefsDone.await();
+ return null;
+ }
+ });
+
+ assertSame(result.get(), Result.FORCED);
+
+ } finally {
+ pool.shutdownNow();
+ pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
+ }
+
+ assertEquals(repo.getRef("refs/tags/t").getObjectId(), b);
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java
new file mode 100644
index 0000000000..5b1a4178a6
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2012, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Date;
+
+import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.junit.Test;
+
+public class GcPruneNonReferencedTest extends GcTestCase {
+ @Test
+ public void nonReferencedNonExpiredObject_notPruned() throws Exception {
+ RevBlob a = tr.blob("a");
+ gc.setExpire(new Date(lastModified(a)));
+ gc.prune(Collections.<ObjectId> emptySet());
+ assertTrue(repo.hasObject(a));
+ }
+
+ @Test
+ public void nonReferencedExpiredObject_pruned() throws Exception {
+ RevBlob a = tr.blob("a");
+ gc.setExpireAgeMillis(0);
+ fsTick();
+ gc.prune(Collections.<ObjectId> emptySet());
+ assertFalse(repo.hasObject(a));
+ }
+
+ @Test
+ public void nonReferencedExpiredObjectTree_pruned() throws Exception {
+ RevBlob a = tr.blob("a");
+ RevTree t = tr.tree(tr.file("a", a));
+ gc.setExpireAgeMillis(0);
+ fsTick();
+ gc.prune(Collections.<ObjectId> emptySet());
+ assertFalse(repo.hasObject(t));
+ assertFalse(repo.hasObject(a));
+ }
+
+ @Test
+ public void nonReferencedObjects_onlyExpiredPruned() throws Exception {
+ RevBlob a = tr.blob("a");
+ gc.setExpire(new Date(lastModified(a) + 1));
+
+ fsTick();
+ RevBlob b = tr.blob("b");
+
+ gc.prune(Collections.<ObjectId> emptySet());
+ assertFalse(repo.hasObject(a));
+ assertTrue(repo.hasObject(b));
+ }
+
+ @Test
+ public void testPackCommitsAndLooseOneWithPruneNow() throws Exception {
+ BranchBuilder bb = tr.branch("refs/heads/master");
+ RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
+ bb.commit().add("A", "A2").add("B", "B2").create();
+ tr.update("refs/heads/master", first);
+
+ stats = gc.getStatistics();
+ assertEquals(8, stats.numberOfLooseObjects);
+ assertEquals(0, stats.numberOfPackedObjects);
+ gc.setExpireAgeMillis(0);
+ fsTick();
+ gc.gc();
+ stats = gc.getStatistics();
+ assertEquals(0, stats.numberOfLooseObjects);
+ assertEquals(8, stats.numberOfPackedObjects);
+ assertEquals(2, stats.numberOfPackFiles);
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java
new file mode 100644
index 0000000000..2a096fd1c4
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2012, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.util.Collections;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.util.FileUtils;
+import org.junit.Test;
+
+public class GcReflogTest extends GcTestCase {
+ @Test
+ public void testPruneNone() throws Exception {
+ BranchBuilder bb = tr.branch("refs/heads/master");
+ bb.commit().add("A", "A").add("B", "B").create();
+ bb.commit().add("A", "A2").add("B", "B2").create();
+ new File(repo.getDirectory(), Constants.LOGS + "/refs/heads/master")
+ .delete();
+ stats = gc.getStatistics();
+ assertEquals(8, stats.numberOfLooseObjects);
+ gc.setExpireAgeMillis(0);
+ fsTick();
+ gc.prune(Collections.<ObjectId> emptySet());
+ stats = gc.getStatistics();
+ assertEquals(8, stats.numberOfLooseObjects);
+ tr.blob("x");
+ stats = gc.getStatistics();
+ assertEquals(9, stats.numberOfLooseObjects);
+ gc.prune(Collections.<ObjectId> emptySet());
+ stats = gc.getStatistics();
+ assertEquals(8, stats.numberOfLooseObjects);
+ }
+
+ @Test
+ public void testPackRepoWithCorruptReflog() throws Exception {
+ // create a reflog entry "0000... 0000... foobar" by doing an initial
+ // refupdate for HEAD which points to a non-existing ref. The
+ // All-Projects repo of gerrit instances had such entries
+ RefUpdate ru = repo.updateRef(Constants.HEAD);
+ ru.link("refs/to/garbage");
+ tr.branch("refs/heads/master").commit().add("A", "A").add("B", "B")
+ .create();
+ // make sure HEAD exists
+ Git.wrap(repo).checkout().setName("refs/heads/master").call();
+ gc.gc();
+ }
+
+ @Test
+ public void testPackCommitsAndLooseOneNoReflog() throws Exception {
+ BranchBuilder bb = tr.branch("refs/heads/master");
+ RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
+ bb.commit().add("A", "A2").add("B", "B2").create();
+ tr.update("refs/heads/master", first);
+
+ stats = gc.getStatistics();
+ assertEquals(8, stats.numberOfLooseObjects);
+ assertEquals(0, stats.numberOfPackedObjects);
+
+ FileUtils.delete(new File(repo.getDirectory(), "logs/HEAD"),
+ FileUtils.RETRY | FileUtils.SKIP_MISSING);
+ FileUtils.delete(
+ new File(repo.getDirectory(), "logs/refs/heads/master"),
+ FileUtils.RETRY | FileUtils.SKIP_MISSING);
+ gc.gc();
+
+ stats = gc.getStatistics();
+ assertEquals(4, stats.numberOfLooseObjects);
+ assertEquals(4, stats.numberOfPackedObjects);
+ assertEquals(1, stats.numberOfPackFiles);
+ }
+
+ @Test
+ public void testPackCommitsAndLooseOneWithPruneNowNoReflog()
+ throws Exception {
+ BranchBuilder bb = tr.branch("refs/heads/master");
+ RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
+ bb.commit().add("A", "A2").add("B", "B2").create();
+ tr.update("refs/heads/master", first);
+
+ stats = gc.getStatistics();
+ assertEquals(8, stats.numberOfLooseObjects);
+ assertEquals(0, stats.numberOfPackedObjects);
+
+ FileUtils.delete(new File(repo.getDirectory(), "logs/HEAD"),
+ FileUtils.RETRY | FileUtils.SKIP_MISSING);
+ FileUtils.delete(
+ new File(repo.getDirectory(), "logs/refs/heads/master"),
+ FileUtils.RETRY | FileUtils.SKIP_MISSING);
+ gc.setExpireAgeMillis(0);
+ gc.gc();
+
+ stats = gc.getStatistics();
+ assertEquals(0, stats.numberOfLooseObjects);
+ assertEquals(4, stats.numberOfPackedObjects);
+ assertEquals(1, stats.numberOfPackFiles);
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTagTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTagTest.java
new file mode 100644
index 0000000000..4afbeff3ec
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTagTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2012, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.junit.Test;
+
+public class GcTagTest extends GcTestCase {
+ @Test
+ public void lightweightTag_objectNotPruned() throws Exception {
+ RevBlob a = tr.blob("a");
+ tr.lightweightTag("t", a);
+ gc.setExpireAgeMillis(0);
+ fsTick();
+ gc.prune(Collections.<ObjectId> emptySet());
+ assertTrue(repo.hasObject(a));
+ }
+
+ @Test
+ public void annotatedTag_objectNotPruned() throws Exception {
+ RevBlob a = tr.blob("a");
+ RevTag t = tr.tag("t", a); // this doesn't create the refs/tags/t ref
+ tr.lightweightTag("t", t);
+
+ gc.setExpireAgeMillis(0);
+ fsTick();
+ gc.prune(Collections.<ObjectId> emptySet());
+ assertTrue(repo.hasObject(t));
+ assertTrue(repo.hasObject(a));
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java
new file mode 100644
index 0000000000..a764f0fddd
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2012, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.internal.storage.file.GC.RepoStatistics;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.junit.TestRepository.CommitBuilder;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.After;
+import org.junit.Before;
+
+public abstract class GcTestCase extends LocalDiskRepositoryTestCase {
+ protected TestRepository<FileRepository> tr;
+ protected FileRepository repo;
+ protected GC gc;
+ protected RepoStatistics stats;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ repo = createWorkRepository();
+ tr = new TestRepository<FileRepository>((repo));
+ gc = new GC(repo);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Create a chain of commits of given depth.
+ * <p>
+ * Each commit contains one file named "a" containing the index of the
+ * commit in the chain as its content. The created commit chain is
+ * referenced from any ref.
+ * <p>
+ * A chain of depth = N will create 3*N objects in Gits object database. For
+ * each depth level three objects are created: the commit object, the
+ * top-level tree object and a blob for the content of the file "a".
+ *
+ * @param depth
+ * the depth of the commit chain.
+ * @return the commit that is the tip of the commit chain
+ * @throws Exception
+ */
+ protected RevCommit commitChain(int depth) throws Exception {
+ if (depth <= 0)
+ throw new IllegalArgumentException("Chain depth must be > 0");
+ CommitBuilder cb = tr.commit();
+ RevCommit tip;
+ do {
+ --depth;
+ tip = cb.add("a", "" + depth).message("" + depth).create();
+ cb = cb.child();
+ } while (depth > 0);
+ return tip;
+ }
+
+ protected long lastModified(AnyObjectId objectId) {
+ return repo.getObjectDatabase().fileFor(objectId).lastModified();
+ }
+
+ protected static void fsTick() throws InterruptedException, IOException {
+ RepositoryTestCase.fsTick(null);
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
index 75ccb6b576..aba48bc35f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
@@ -67,7 +67,6 @@ import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
import org.eclipse.jgit.internal.storage.pack.PackWriter.ObjectIdSet;
import org.eclipse.jgit.junit.JGitTestUtil;
-import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.lib.AnyObjectId;
@@ -79,6 +78,7 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.pack.PackConfig;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.eclipse.jgit.transport.PackParser;
import org.junit.After;
import org.junit.Before;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
index 21df179ad8..1731a33622 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
@@ -61,7 +61,6 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
@@ -75,6 +74,7 @@ import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.junit.Test;
public class RefUpdateTest extends SampleDataRepositoryTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
index a0dff7ee86..63bd7f4684 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
@@ -55,13 +55,13 @@ import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.List;
-import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
import org.eclipse.jgit.lib.CheckoutEntry;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.ReflogEntry;
import org.eclipse.jgit.lib.ReflogReader;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.junit.Test;
public class ReflogReaderTest extends SampleDataRepositoryTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
index 921ec29d53..0ab013baff 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
@@ -63,7 +63,6 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Constants;
@@ -83,6 +82,7 @@ import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.eclipse.jgit.util.FileUtils;
import org.junit.Test;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0004_PackReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0004_PackReaderTest.java
index dc78909d4a..e8e3d1db68 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0004_PackReaderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0004_PackReaderTest.java
@@ -46,35 +46,35 @@
package org.eclipse.jgit.internal.storage.file;
-import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
-import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import java.io.File;
import java.io.IOException;
-import org.eclipse.jgit.internal.storage.file.PackFile;
-import org.eclipse.jgit.internal.storage.file.WindowCursor;
-import org.eclipse.jgit.junit.JGitTestUtil;
-import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.junit.Test;
public class T0004_PackReaderTest extends SampleDataRepositoryTestCase {
- private static final String PACK_NAME = "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f";
- private static final File TEST_PACK = JGitTestUtil.getTestResourceFile(PACK_NAME + ".pack");
+ private static final String PACK_NAME = "34be9032ac282b11fa9babdc2b2a93ca996c9c2f";
@Test
public void test003_lookupCompressedObject() throws IOException {
- final PackFile pr;
final ObjectId id;
final ObjectLoader or;
+ PackFile pr = null;
+ for (PackFile p : db.getObjectDatabase().getPacks()) {
+ if (PACK_NAME.equals(p.getPackName())) {
+ pr = p;
+ break;
+ }
+ }
+ assertNotNull("have pack-" + PACK_NAME, pr);
+
id = ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327");
- pr = new PackFile(TEST_PACK, PACK.getBit() | INDEX.getBit());
or = pr.get(new WindowCursor(null), id);
assertNotNull(or);
assertEquals(Constants.OBJ_TREE, or.getType());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java
index f0942203b5..aa061bafc0 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java
@@ -56,11 +56,11 @@ import java.util.List;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.junit.JGitTestUtil;
-import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.storage.file.WindowCacheConfig;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.eclipse.jgit.util.MutableInteger;
import org.junit.Before;
import org.junit.Test;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java
index b3219cddb5..d29a75ef71 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java
@@ -405,7 +405,7 @@ public class DirCacheCheckoutMaliciousPathTest extends RepositoryTestCase {
} catch (InvalidPathException e) {
if (good)
throw e;
- assertTrue(e.getMessage().startsWith("Invalid path: "));
+ assertTrue(e.getMessage().startsWith("Invalid path"));
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
index cb2ae64db2..98ec706bec 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
@@ -467,7 +467,9 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
*3 D F D Y N N Keep
*4 D F D N N N Conflict
*5 D F F Y N N Y Keep
+ *5b D F F Y N N N Conflict
*6 D F F N N N Y Keep
+ *6b D F F N N N N Conflict
*7 F D F Y Y N N Update
*8 F D F N Y N N Conflict
*9 F D F Y N N N Update
@@ -540,7 +542,17 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
// 5
doit(mk("DF/DF"), mk("DF"), mk("DF"));
assertRemoved("DF/DF");
+ assertEquals(0, dco.getConflicts().size());
+ assertEquals(0, dco.getUpdated().size());
+ }
+ @Test
+ public void testDirectoryFileConflicts_5b() throws Exception {
+ // 5
+ doit(mk("DF/DF"), mkmap("DF", "different"), mk("DF"));
+ assertRemoved("DF/DF");
+ assertConflict("DF");
+ assertEquals(0, dco.getUpdated().size());
}
@Test
@@ -550,6 +562,19 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
writeTrashFile("DF", "different");
go();
assertRemoved("DF/DF");
+ assertEquals(0, dco.getConflicts().size());
+ assertEquals(0, dco.getUpdated().size());
+ }
+
+ @Test
+ public void testDirectoryFileConflicts_6b() throws Exception {
+ // 6
+ setupCase(mk("DF/DF"), mk("DF"), mkmap("DF", "different"));
+ writeTrashFile("DF", "again different");
+ go();
+ assertRemoved("DF/DF");
+ assertConflict("DF");
+ assertEquals(0, dco.getUpdated().size());
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
index 7c06e86915..26c4d12cc0 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
@@ -59,9 +59,9 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
-import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
import org.eclipse.jgit.lib.Ref.Storage;
import org.eclipse.jgit.lib.RefUpdate.Result;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.junit.Test;
/**
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryResolveTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryResolveTest.java
index 381e1317b1..c9ea286407 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryResolveTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryResolveTest.java
@@ -59,8 +59,8 @@ import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.errors.AmbiguousObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.RevisionSyntaxException;
-import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.junit.Test;
public class RepositoryResolveTest extends SampleDataRepositoryTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0002_TreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0002_TreeTest.java
index 2682cad0a0..651e62c9ca 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0002_TreeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0002_TreeTest.java
@@ -55,7 +55,7 @@ import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
-import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.junit.Test;
@SuppressWarnings("deprecation")
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java
index 434f53d4b7..21ef7479a2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java
@@ -47,13 +47,13 @@ import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.util.Arrays;
-import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.SymbolicRef;
import org.eclipse.jgit.lib.Ref.Storage;
import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.junit.Before;
import org.junit.Test;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
index a06bcfff78..db9d87dd17 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
@@ -52,12 +52,12 @@ import java.io.IOException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
-import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.junit.Test;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java
index 1fddcdc167..b7b2291429 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java
@@ -47,9 +47,9 @@ import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.eclipse.jgit.util.GitDateFormatter;
import org.eclipse.jgit.util.GitDateFormatter.Format;
import org.junit.Before;
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/SampleDataRepositoryTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/test/resources/SampleDataRepositoryTestCase.java
index fb4b9e9f80..3a3b3d8d8f 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/SampleDataRepositoryTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/test/resources/SampleDataRepositoryTestCase.java
@@ -44,10 +44,13 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.junit;
+package org.eclipse.jgit.test.resources;
import java.io.File;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+
/** Test case which includes C Git generated pack files for testing. */
public abstract class SampleDataRepositoryTestCase extends RepositoryTestCase {
@@ -66,11 +69,11 @@ public abstract class SampleDataRepositoryTestCase extends RepositoryTestCase {
};
final File packDir = new File(db.getObjectDatabase().getDirectory(), "pack");
for (String n : packs) {
- copyFile(JGitTestUtil.getTestResourceFile(n + ".pack"), new File(packDir, n + ".pack"));
- copyFile(JGitTestUtil.getTestResourceFile(n + ".idx"), new File(packDir, n + ".idx"));
+ JGitTestUtil.copyTestResource(n + ".pack", new File(packDir, n + ".pack"));
+ JGitTestUtil.copyTestResource(n + ".idx", new File(packDir, n + ".idx"));
}
- copyFile(JGitTestUtil.getTestResourceFile("packed-refs"), new File(db
- .getDirectory(), "packed-refs"));
+ JGitTestUtil.copyTestResource("packed-refs",
+ new File(db.getDirectory(), "packed-refs"));
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java
index 02ce186669..efc3834f45 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java
@@ -61,13 +61,13 @@ import java.util.Set;
import org.eclipse.jgit.errors.MissingBundlePrerequisiteException;
import org.eclipse.jgit.errors.NotSupportedException;
import org.eclipse.jgit.errors.TransportException;
-import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.junit.Test;
public class BundleWriterTest extends SampleDataRepositoryTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java
index e523db981a..28473c79fa 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java
@@ -55,7 +55,6 @@ import java.util.Map;
import org.eclipse.jgit.errors.NotSupportedException;
import org.eclipse.jgit.errors.TransportException;
-import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.ProgressMonitor;
@@ -63,6 +62,7 @@ import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.TextProgressMonitor;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
import org.junit.Before;
import org.junit.Test;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
index 28a3f4428e..55e1e44206 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
@@ -55,12 +55,12 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateParserBadlyFormattedTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateParserBadlyFormattedTest.java
index e0e99a14c2..a6af3a5143 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateParserBadlyFormattedTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateParserBadlyFormattedTest.java
@@ -92,7 +92,8 @@ public class GitDateParserBadlyFormattedTest {
Calendar ref = new GregorianCalendar(SystemReader.getInstance()
.getTimeZone(), SystemReader.getInstance().getLocale());
try {
- GitDateParser.parse(dateStr, ref);
+ GitDateParser.parse(dateStr, ref, SystemReader.getInstance()
+ .getLocale());
fail("The expected ParseException while parsing '" + dateStr
+ "' did not occur.");
} catch (ParseException e) {
@@ -103,7 +104,8 @@ public class GitDateParserBadlyFormattedTest {
@Theory
public void badlyFormattedWithoutRef() {
try {
- GitDateParser.parse(dateStr, null);
+ GitDateParser.parse(dateStr, null, SystemReader.getInstance()
+ .getLocale());
fail("The expected ParseException while parsing '" + dateStr
+ "' did not occur.");
} catch (ParseException e) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateParserTest.java
index 570f4999dd..518ed53fde 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateParserTest.java
@@ -72,7 +72,8 @@ public class GitDateParserTest {
GregorianCalendar cal = new GregorianCalendar(SystemReader
.getInstance().getTimeZone(), SystemReader.getInstance()
.getLocale());
- Date parse = GitDateParser.parse("yesterday", cal);
+ Date parse = GitDateParser.parse("yesterday", cal, SystemReader
+ .getInstance().getLocale());
cal.add(Calendar.DATE, -1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
@@ -87,7 +88,8 @@ public class GitDateParserTest {
GregorianCalendar cal = new GregorianCalendar(SystemReader
.getInstance().getTimeZone(), SystemReader.getInstance()
.getLocale());
- Date parse = GitDateParser.parse("never", cal);
+ Date parse = GitDateParser.parse("never", cal, SystemReader
+ .getInstance().getLocale());
Assert.assertEquals(GitDateParser.NEVER, parse);
parse = GitDateParser.parse("never", null);
Assert.assertEquals(GitDateParser.NEVER, parse);
@@ -104,7 +106,8 @@ public class GitDateParserTest {
.getLocale());
cal.setTime(refDate);
- Date parse = GitDateParser.parse("now", cal);
+ Date parse = GitDateParser.parse("now", cal, SystemReader.getInstance()
+ .getLocale());
Assert.assertEquals(refDate, parse);
long t1 = SystemReader.getInstance().getCurrentTime();
parse = GitDateParser.parse("now", null);
@@ -123,7 +126,8 @@ public class GitDateParserTest {
.getLocale());
cal.setTime(refDate);
- Date parse = GitDateParser.parse("2 weeks ago", cal);
+ Date parse = GitDateParser.parse("2 weeks ago", cal, SystemReader
+ .getInstance().getLocale());
Assert.assertEquals(df.parse("2007-02-07 15:35:00 +0100"), parse);
}
@@ -138,7 +142,8 @@ public class GitDateParserTest {
.getLocale());
cal.setTime(refDate);
- Date parse = GitDateParser.parse("2 weeks ago", cal);
+ Date parse = GitDateParser.parse("2 weeks ago", cal, SystemReader.getInstance()
+ .getLocale());
Assert.assertEquals(df.parse("2007-02-07 15:35:00 +0100"), parse);
parse = GitDateParser.parse("3 days 2 weeks ago", cal);
Assert.assertEquals(df.parse("2007-02-04 15:35:00 +0100"), parse);
@@ -151,7 +156,8 @@ public class GitDateParserTest {
String dateStr = "2007-02-21 15:35:00 +0100";
Date exp = SystemReader.getInstance()
.getSimpleDateFormat("yyyy-MM-dd HH:mm:ss Z").parse(dateStr);
- Date parse = GitDateParser.parse(dateStr, null);
+ Date parse = GitDateParser.parse(dateStr, null, SystemReader
+ .getInstance().getLocale());
Assert.assertEquals(exp, parse);
}
@@ -161,7 +167,8 @@ public class GitDateParserTest {
Date exp = SystemReader.getInstance()
.getSimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z")
.parse(dateStr);
- Date parse = GitDateParser.parse(dateStr, null);
+ Date parse = GitDateParser.parse(dateStr, null, SystemReader
+ .getInstance().getLocale());
Assert.assertEquals(exp, parse);
}
@@ -170,7 +177,8 @@ public class GitDateParserTest {
String dateStr = "2007-02-21";
Date exp = SystemReader.getInstance().getSimpleDateFormat("yyyy-MM-dd")
.parse(dateStr);
- Date parse = GitDateParser.parse(dateStr, null);
+ Date parse = GitDateParser.parse(dateStr, null, SystemReader
+ .getInstance().getLocale());
Assert.assertEquals(exp, parse);
}
@@ -179,7 +187,8 @@ public class GitDateParserTest {
String dateStr = "2007.02.21";
Date exp = SystemReader.getInstance().getSimpleDateFormat("yyyy.MM.dd")
.parse(dateStr);
- Date parse = GitDateParser.parse(dateStr, null);
+ Date parse = GitDateParser.parse(dateStr, null, SystemReader
+ .getInstance().getLocale());
Assert.assertEquals(exp, parse);
}
@@ -188,7 +197,8 @@ public class GitDateParserTest {
String dateStr = "02/21/2007";
Date exp = SystemReader.getInstance().getSimpleDateFormat("MM/dd/yyyy")
.parse(dateStr);
- Date parse = GitDateParser.parse(dateStr, null);
+ Date parse = GitDateParser.parse(dateStr, null, SystemReader
+ .getInstance().getLocale());
Assert.assertEquals(exp, parse);
}
@@ -197,7 +207,8 @@ public class GitDateParserTest {
String dateStr = "21.02.2007";
Date exp = SystemReader.getInstance().getSimpleDateFormat("dd.MM.yyyy")
.parse(dateStr);
- Date parse = GitDateParser.parse(dateStr, null);
+ Date parse = GitDateParser.parse(dateStr, null, SystemReader
+ .getInstance().getLocale());
Assert.assertEquals(exp, parse);
}
@@ -207,7 +218,8 @@ public class GitDateParserTest {
Date exp = SystemReader.getInstance()
.getSimpleDateFormat("EEE MMM dd HH:mm:ss yyyy Z")
.parse(dateStr);
- Date parse = GitDateParser.parse(dateStr, null);
+ Date parse = GitDateParser.parse(dateStr, null, SystemReader
+ .getInstance().getLocale());
Assert.assertEquals(exp, parse);
}
@@ -216,7 +228,8 @@ public class GitDateParserTest {
String dateStr = "Wed Feb 21 15:35:00 2007";
Date exp = SystemReader.getInstance()
.getSimpleDateFormat("EEE MMM dd HH:mm:ss yyyy").parse(dateStr);
- Date parse = GitDateParser.parse(dateStr, null);
+ Date parse = GitDateParser.parse(dateStr, null, SystemReader
+ .getInstance().getLocale());
Assert.assertEquals(exp, parse);
}
}
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
index 474b8f349c..bb67c127a7 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -270,6 +270,10 @@ invalidObject=Invalid {0} {1}:{2}
invalidOldIdSent=invalid old id sent
invalidPacketLineHeader=Invalid packet line header: {0}
invalidPath=Invalid path: {0}
+invalidPathContainsSeparator=Invalid path (contains separator ''{0}''): {1}
+invalidPathPeriodAtEndWindows=Invalid path (period at end is ignored by Windows): {0}
+invalidPathSpaceAtEndWindows=Invalid path (space at end is ignored by Windows): {0}
+invalidPathReservedOnWindows=Invalid path (''{0}'' is reserved on Windows): {1}
invalidReflogRevision=Invalid reflog revision: {0}
invalidRefName=Invalid ref name: {0}
invalidRemote=Invalid remote: {0}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
index ef2e987b4e..c23256c74b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
@@ -93,11 +93,15 @@ public class AddCommand extends GitCommand<DirCache> {
}
/**
+ * Add a path to a file/directory whose content should be added.
+ * <p>
+ * A directory name (e.g. <code>dir</code> to add <code>dir/file1</code> and
+ * <code>dir/file2</code>) can also be given to add all files in the
+ * directory, recursively. Fileglobs (e.g. *.c) are not yet supported.
+ *
* @param filepattern
- * File to add content from. Also a leading directory name (e.g.
- * dir to add dir/file1 and dir/file2) can be given to add all
- * files in the directory, recursively. Fileglobs (e.g. *.c) are
- * not yet supported.
+ * repository-relative path of file/directory to add (with
+ * <code>/</code> as separator)
* @return {@code this}
*/
public AddCommand addFilepattern(String filepattern) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java
index 12be64bb02..11dfd1c585 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java
@@ -86,9 +86,10 @@ public class BlameCommand extends GitCommand<BlameResult> {
}
/**
- * Set file path
+ * Set file path.
*
* @param filePath
+ * file path (with <code>/</code> as separator)
* @return this command
*/
public BlameCommand setFilePath(String filePath) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
index eb447f3142..8dfd211a08 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
@@ -305,15 +305,16 @@ public class CheckoutCommand extends GitCommand<Ref> {
}
/**
- * Add a single path to the list of paths to check out. To check out all
- * paths, use {@link #setAllPaths(boolean)}.
+ * Add a single slash-separated path to the list of paths to check out. To
+ * check out all paths, use {@link #setAllPaths(boolean)}.
* <p>
* If this option is set, neither the {@link #setCreateBranch(boolean)} nor
* {@link #setName(String)} option is considered. In other words, these
* options are exclusive.
*
* @param path
- * path to update in the working tree and index
+ * path to update in the working tree and index (with
+ * <code>/</code> as separator)
* @return {@code this}
*/
public CheckoutCommand addPath(String path) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java
index b42c80f67b..f273eafe1f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java
@@ -174,7 +174,7 @@ public class CleanCommand extends GitCommand<Set<String>> {
* If paths are set, only these paths are affected by the cleaning.
*
* @param paths
- * the paths to set
+ * the paths to set (with <code>/</code> as separator)
* @return {@code this}
*/
public CleanCommand setPaths(Set<String> paths) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
index 399d78e095..21d7138c9f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -667,14 +667,14 @@ public class CommitCommand extends GitCommand<RevCommit> {
}
/**
- * Commit dedicated path only
- *
+ * Commit dedicated path only.
+ * <p>
* This method can be called several times to add multiple paths. Full file
* paths are supported as well as directory paths; in the latter case this
- * commits all files/ directories below the specified path.
+ * commits all files/directories below the specified path.
*
* @param only
- * path to commit
+ * path to commit (with <code>/</code> as separator)
* @return {@code this}
*/
public CommitCommand setOnly(String only) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
index 08ab88005d..983b6b552e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
@@ -125,6 +125,26 @@ public class Git {
}
/**
+ * Frees resources held by the underlying {@link Repository} instance. It is
+ * recommended to call this method as soon as you don't need a reference to
+ * this {@link Git} instance and the underlying {@link Repository} instance
+ * anymore. This method closes the underlying object and ref databases. This
+ * will free memory and file handles. E.g. on Windows the repository will
+ * keep file handles on pack files unless you call this method. Such open
+ * file handles may for example prevent that the repository folder in the
+ * filesystem can be deleted.
+ * <p>
+ * After calling close() you should not use this {@link Git} instance and
+ * the underlying {@link Repository} instance anymore.
+ *
+ * @since 3.2
+ */
+ public void close() {
+ if (repo != null)
+ repo.close();
+ }
+
+ /**
* Returns a command object to execute a {@code clone} command
*
* @see <a
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
index 51a233458f..9690f799c1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
@@ -282,11 +282,11 @@ public class LogCommand extends GitCommand<Iterable<RevCommit>> {
/**
* Show only commits that affect any of the specified paths. The path must
- * either name a file or a directory exactly. Note that regex expressions or
- * wildcards are not supported.
+ * either name a file or a directory exactly and use <code>/</code> (slash)
+ * as separator. Note that regex expressions or wildcards are not supported.
*
* @param path
- * a path is relative to the top level of the repository
+ * a repository-relative path (with <code>/</code> as separator)
* @return {@code this}
*/
public LogCommand addPath(String path) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
index 509203e528..78b260df71 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
@@ -379,8 +379,7 @@ public class MergeCommand extends GitCommand<MergeResult> {
if (failingPaths != null) {
repo.writeMergeCommitMsg(null);
repo.writeMergeHeads(null);
- return new MergeResult(null,
- merger.getBaseCommit(0, 1),
+ return new MergeResult(null, merger.getBaseCommitId(),
new ObjectId[] {
headCommit.getId(), srcCommit.getId() },
MergeStatus.FAILED, mergeStrategy,
@@ -390,8 +389,7 @@ public class MergeCommand extends GitCommand<MergeResult> {
.formatWithConflicts(mergeMessage,
unmergedPaths);
repo.writeMergeCommitMsg(mergeMessageWithConflicts);
- return new MergeResult(null,
- merger.getBaseCommit(0, 1),
+ return new MergeResult(null, merger.getBaseCommitId(),
new ObjectId[] { headCommit.getId(),
srcCommit.getId() },
MergeStatus.CONFLICTING, mergeStrategy,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
index 8ae51d74ce..10b273a744 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -70,6 +70,7 @@ import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.api.errors.NoMessageException;
import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
+import org.eclipse.jgit.api.errors.StashApplyFailureException;
import org.eclipse.jgit.api.errors.UnmergedPathsException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.diff.DiffFormatter;
@@ -79,6 +80,7 @@ import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
@@ -158,6 +160,10 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
private static final String MESSAGE_SQUASH = "message-squash"; //$NON-NLS-1$
+ private static final String AUTOSTASH = "autostash"; //$NON-NLS-1$
+
+ private static final String AUTOSTASH_MSG = "On {0}: autostash";
+
/**
* The available operations
*/
@@ -257,11 +263,26 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
.resolve(upstreamCommitId));
break;
case BEGIN:
+ autoStash();
+ if (stopAfterInitialization
+ || !walk.isMergedInto(
+ walk.parseCommit(repo.resolve(Constants.HEAD)),
+ upstreamCommit)) {
+ org.eclipse.jgit.api.Status status = Git.wrap(repo)
+ .status().call();
+ if (status.hasUncommittedChanges()) {
+ List<String> list = new ArrayList<String>();
+ list.addAll(status.getUncommittedChanges());
+ return RebaseResult.uncommittedChanges(list);
+ }
+ }
RebaseResult res = initFilesAndRewind();
if (stopAfterInitialization)
return RebaseResult.INTERACTIVE_PREPARED_RESULT;
- if (res != null)
+ if (res != null) {
+ autoStashApply();
return res;
+ }
}
if (monitor.isCancelled())
@@ -321,12 +342,63 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}
return finishRebase(newHead, lastStepWasForward);
} catch (CheckoutConflictException cce) {
- return new RebaseResult(cce.getConflictingPaths());
+ return RebaseResult.conflicts(cce.getConflictingPaths());
} catch (IOException ioe) {
throw new JGitInternalException(ioe.getMessage(), ioe);
}
}
+ private void autoStash() throws GitAPIException, IOException {
+ if (repo.getConfig().getBoolean(ConfigConstants.CONFIG_REBASE_SECTION,
+ ConfigConstants.CONFIG_KEY_AUTOSTASH, false)) {
+ String message = MessageFormat.format(
+ AUTOSTASH_MSG,
+ Repository
+ .shortenRefName(getHeadName(getHead())));
+ RevCommit stashCommit = Git.wrap(repo).stashCreate().setRef(null)
+ .setWorkingDirectoryMessage(
+ message)
+ .call();
+ if (stashCommit != null) {
+ FileUtils.mkdir(rebaseState.getDir());
+ rebaseState.createFile(AUTOSTASH, stashCommit.getName());
+ }
+ }
+ }
+
+ private boolean autoStashApply() throws IOException, GitAPIException {
+ boolean conflicts = false;
+ if (rebaseState.getFile(AUTOSTASH).exists()) {
+ String stash = rebaseState.readFile(AUTOSTASH);
+ try {
+ Git.wrap(repo).stashApply().setStashRef(stash)
+ .ignoreRepositoryState(true).call();
+ } catch (StashApplyFailureException e) {
+ conflicts = true;
+ RevWalk rw = new RevWalk(repo);
+ ObjectId stashId = repo.resolve(stash);
+ RevCommit commit = rw.parseCommit(stashId);
+ updateStashRef(commit, commit.getAuthorIdent(),
+ commit.getShortMessage());
+ }
+ }
+ return conflicts;
+ }
+
+ private void updateStashRef(ObjectId commitId, PersonIdent refLogIdent,
+ String refLogMessage) throws IOException {
+ Ref currentRef = repo.getRef(Constants.R_STASH);
+ RefUpdate refUpdate = repo.updateRef(Constants.R_STASH);
+ refUpdate.setNewObjectId(commitId);
+ refUpdate.setRefLogIdent(refLogIdent);
+ refUpdate.setRefLogMessage(refLogMessage, false);
+ if (currentRef != null)
+ refUpdate.setExpectedOldObjectId(currentRef.getObjectId());
+ else
+ refUpdate.setExpectedOldObjectId(ObjectId.zeroId());
+ refUpdate.forceUpdate();
+ }
+
private RebaseResult processStep(RebaseTodoLine step, boolean shouldPick)
throws IOException, GitAPIException {
if (Action.COMMENT.equals(step.getAction()))
@@ -340,7 +412,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
RevCommit commitToPick = walk.parseCommit(ids.iterator().next());
if (shouldPick) {
if (monitor.isCancelled())
- return new RebaseResult(commitToPick, Status.STOPPED);
+ return RebaseResult.result(Status.STOPPED, commitToPick);
RebaseResult result = cherryPickCommit(commitToPick);
if (result != null)
return result;
@@ -403,8 +475,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
switch (cherryPickResult.getStatus()) {
case FAILED:
if (operation == Operation.BEGIN)
- return abort(new RebaseResult(
- cherryPickResult.getFailingPaths()));
+ return abort(RebaseResult.failed(cherryPickResult
+ .getFailingPaths()));
else
return stop(commitToPick, Status.STOPPED);
case CONFLICTING:
@@ -420,10 +492,13 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}
private RebaseResult finishRebase(RevCommit newHead,
- boolean lastStepWasForward) throws IOException {
+ boolean lastStepWasForward) throws IOException, GitAPIException {
String headName = rebaseState.readFile(HEAD_NAME);
updateHead(headName, newHead, upstreamCommit);
+ boolean stashConflicts = autoStashApply();
FileUtils.delete(rebaseState.getDir(), FileUtils.RECURSIVE);
+ if (stashConflicts)
+ return RebaseResult.STASH_APPLY_CONFLICTS_RESULT;
if (lastStepWasForward || newHead == null)
return RebaseResult.FAST_FORWARD_RESULT;
return RebaseResult.OK_RESULT;
@@ -550,7 +625,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
} else {
sb.append("# The ").append(count).append(ordinal)
.append(" commit message will be skipped:\n# ");
- sb.append(commitToPick.getFullMessage().replaceAll("([\n\r]+)",
+ sb.append(commitToPick.getFullMessage().replaceAll("([\n\r])",
"$1# "));
}
// Add the previous message without header (i.e first line)
@@ -735,7 +810,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
// Remove cherry pick state file created by CherryPickCommand, it's not
// needed for rebase
repo.writeCherryPickHead(null);
- return new RebaseResult(commitToPick, status);
+ return RebaseResult.result(status, commitToPick);
}
String toAuthorScript(PersonIdent author) {
@@ -797,16 +872,9 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
// we need to store everything into files so that we can implement
// --skip, --continue, and --abort
- Ref head = repo.getRef(Constants.HEAD);
- if (head == null || head.getObjectId() == null)
- throw new RefNotFoundException(MessageFormat.format(
- JGitText.get().refNotResolved, Constants.HEAD));
+ Ref head = getHead();
- String headName;
- if (head.isSymbolic())
- headName = head.getTarget().getName();
- else
- headName = head.getObjectId().getName();
+ String headName = getHeadName(head);
ObjectId headId = head.getObjectId();
if (headId == null)
throw new RefNotFoundException(MessageFormat.format(
@@ -845,7 +913,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
Collections.reverse(cherryPickList);
// create the folder for the meta information
- FileUtils.mkdir(rebaseState.getDir());
+ FileUtils.mkdir(rebaseState.getDir(), true);
repo.writeOrigHead(headId);
rebaseState.createFile(REBASE_HEAD, headId.name());
@@ -881,6 +949,23 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
return null;
}
+ private static String getHeadName(Ref head) {
+ String headName;
+ if (head.isSymbolic())
+ headName = head.getTarget().getName();
+ else
+ headName = head.getObjectId().getName();
+ return headName;
+ }
+
+ private Ref getHead() throws IOException, RefNotFoundException {
+ Ref head = repo.getRef(Constants.HEAD);
+ if (head == null || head.getObjectId() == null)
+ throw new RefNotFoundException(MessageFormat.format(
+ JGitText.get().refNotResolved, Constants.HEAD));
+ return head;
+ }
+
private boolean isInteractive() {
return interactiveHandler != null;
}
@@ -895,10 +980,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
*/
public RevCommit tryFastForward(RevCommit newCommit) throws IOException,
GitAPIException {
- Ref head = repo.getRef(Constants.HEAD);
- if (head == null || head.getObjectId() == null)
- throw new RefNotFoundException(MessageFormat.format(
- JGitText.get().refNotResolved, Constants.HEAD));
+ Ref head = getHead();
ObjectId headId = head.getObjectId();
if (headId == null)
@@ -908,11 +990,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
if (walk.isMergedInto(newCommit, headCommit))
return newCommit;
- String headName;
- if (head.isSymbolic())
- headName = head.getTarget().getName();
- else
- headName = head.getObjectId().getName();
+ String headName = getHeadName(head);
return tryFastForward(headName, headCommit, newCommit);
}
@@ -992,7 +1070,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}
}
- private RebaseResult abort(RebaseResult result) throws IOException {
+ private RebaseResult abort(RebaseResult result) throws IOException,
+ GitAPIException {
try {
ObjectId origHead = repo.readOrigHead();
String commitId = origHead != null ? origHead.name() : null;
@@ -1041,9 +1120,12 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
JGitText.get().abortingRebaseFailed);
}
}
+ boolean stashConflicts = autoStashApply();
// cleanup the files
FileUtils.delete(rebaseState.getDir(), FileUtils.RECURSIVE);
repo.writeCherryPickHead(null);
+ if (stashConflicts)
+ return RebaseResult.STASH_APPLY_CONFLICTS_RESULT;
return result;
} finally {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java
index aaa75d9b88..92c1347ab2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java
@@ -105,6 +105,18 @@ public class RebaseResult {
}
},
/**
+ * The repository contains uncommitted changes and the rebase is not a
+ * fast-forward
+ *
+ * @since 3.2
+ */
+ UNCOMMITTED_CHANGES {
+ @Override
+ public boolean isSuccessful() {
+ return false;
+ }
+ },
+ /**
* Conflicts: checkout of target HEAD failed
*/
CONFLICTS {
@@ -153,6 +165,18 @@ public class RebaseResult {
public boolean isSuccessful() {
return false;
}
+ },
+
+ /**
+ * Applying stash resulted in conflicts
+ *
+ * @since 3.2
+ */
+ STASH_APPLY_CONFLICTS {
+ @Override
+ public boolean isSuccessful() {
+ return true;
+ }
};
/**
@@ -177,6 +201,9 @@ public class RebaseResult {
static final RebaseResult INTERACTIVE_PREPARED_RESULT = new RebaseResult(
Status.INTERACTIVE_PREPARED);
+ static final RebaseResult STASH_APPLY_CONFLICTS_RESULT = new RebaseResult(
+ Status.STASH_APPLY_CONFLICTS);
+
private final Status status;
private final RevCommit currentCommit;
@@ -185,21 +212,29 @@ public class RebaseResult {
private List<String> conflicts;
+ private List<String> uncommittedChanges;
+
private RebaseResult(Status status) {
this.status = status;
currentCommit = null;
}
+ private RebaseResult(Status status, RevCommit commit) {
+ this.status = status;
+ currentCommit = commit;
+ }
+
/**
- * Create <code>RebaseResult</code> with status {@link Status#STOPPED}
+ * Create <code>RebaseResult</code>
*
+ * @param status
* @param commit
* current commit
- * @param status
+ * @return the RebaseResult
*/
- RebaseResult(RevCommit commit, RebaseResult.Status status) {
- this.status = status;
- currentCommit = commit;
+ static RebaseResult result(RebaseResult.Status status,
+ RevCommit commit) {
+ return new RebaseResult(status, commit);
}
/**
@@ -207,11 +242,13 @@ public class RebaseResult {
*
* @param failingPaths
* list of paths causing this rebase to fail
+ * @return the RebaseResult
*/
- RebaseResult(Map<String, MergeFailureReason> failingPaths) {
- status = Status.FAILED;
- currentCommit = null;
- this.failingPaths = failingPaths;
+ static RebaseResult failed(
+ Map<String, MergeFailureReason> failingPaths) {
+ RebaseResult result = new RebaseResult(Status.FAILED);
+ result.failingPaths = failingPaths;
+ return result;
}
/**
@@ -219,11 +256,26 @@ public class RebaseResult {
*
* @param conflicts
* the list of conflicting paths
+ * @return the RebaseResult
*/
- RebaseResult(List<String> conflicts) {
- status = Status.CONFLICTS;
- currentCommit = null;
- this.conflicts = conflicts;
+ static RebaseResult conflicts(List<String> conflicts) {
+ RebaseResult result = new RebaseResult(Status.CONFLICTS);
+ result.conflicts = conflicts;
+ return result;
+ }
+
+ /**
+ * Create <code>RebaseResult</code> with status
+ * {@link Status#UNCOMMITTED_CHANGES}
+ *
+ * @param uncommittedChanges
+ * the list of paths
+ * @return the RebaseResult
+ */
+ static RebaseResult uncommittedChanges(List<String> uncommittedChanges) {
+ RebaseResult result = new RebaseResult(Status.UNCOMMITTED_CHANGES);
+ result.uncommittedChanges = uncommittedChanges;
+ return result;
}
/**
@@ -256,4 +308,15 @@ public class RebaseResult {
public List<String> getConflicts() {
return conflicts;
}
+
+ /**
+ * @return the list of uncommitted changes if status is
+ * {@link Status#UNCOMMITTED_CHANGES}
+ *
+ * @since 3.2
+ */
+ public List<String> getUncommittedChanges() {
+ return uncommittedChanges;
+ }
+
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
index 3a1c209a2d..7c2192dd9f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
@@ -279,16 +279,17 @@ public class ResetCommand extends GitCommand<Ref> {
}
/**
- * @param file
- * the file to add
+ * @param path
+ * repository-relative path of file/directory to reset (with
+ * <code>/</code> as separator)
* @return this instance
*/
- public ResetCommand addPath(String file) {
+ public ResetCommand addPath(String path) {
if (mode != null)
throw new JGitInternalException(MessageFormat.format(
JGitText.get().illegalCombinationOfArguments, "<paths>...",
"[--mixed | --soft | --hard]")); //$NON-NLS-1$
- filepaths.add(file);
+ filepaths.add(path);
return this;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
index 2bc9d22449..bc1b29c2f6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
@@ -191,14 +191,14 @@ public class RevertCommand extends GitCommand<RevCommit> {
.getFailingPaths();
if (failingPaths != null)
failingResult = new MergeResult(null,
- merger.getBaseCommit(0, 1),
+ merger.getBaseCommitId(),
new ObjectId[] { headCommit.getId(),
srcParent.getId() },
MergeStatus.FAILED, MergeStrategy.RECURSIVE,
merger.getMergeResults(), failingPaths, null);
else
failingResult = new MergeResult(null,
- merger.getBaseCommit(0, 1),
+ merger.getBaseCommitId(),
new ObjectId[] { headCommit.getId(),
srcParent.getId() },
MergeStatus.CONFLICTING,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RmCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RmCommand.java
index ac30ffcb7d..c70b4aeaf2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RmCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RmCommand.java
@@ -104,7 +104,8 @@ public class RmCommand extends GitCommand<DirCache> {
/**
* @param filepattern
- * File to remove.
+ * repository-relative path of file to remove (with
+ * <code>/</code> as separator)
* @return {@code this}
*/
public RmCommand addFilepattern(String filepattern) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
index 020a6dc5f2..8440d8b950 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
@@ -90,6 +90,8 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
private boolean applyIndex = true;
+ private boolean ignoreRepositoryState;
+
/**
* Create command to apply the changes of a stashed commit
*
@@ -113,6 +115,16 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
return this;
}
+ /**
+ * @param ignoreRepositoryState
+ * @return {@code this}
+ * @since 3.2
+ */
+ public StashApplyCommand ignoreRepositoryState(boolean ignoreRepositoryState) {
+ this.ignoreRepositoryState = ignoreRepositoryState;
+ return this;
+ }
+
private ObjectId getStashId() throws GitAPIException {
final String revision = stashRef != null ? stashRef : DEFAULT_REF;
final ObjectId stashId;
@@ -143,7 +155,8 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
StashApplyFailureException {
checkCallable();
- if (repo.getRepositoryState() != RepositoryState.SAFE)
+ if (!ignoreRepositoryState
+ && repo.getRepositoryState() != RepositoryState.SAFE)
throw new WrongRepositoryStateException(MessageFormat.format(
JGitText.get().stashApplyOnUnsafeRepository,
repo.getRepositoryState()));
@@ -185,6 +198,7 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
.newMerger(repo, true);
ixMerger.setCommitNames(new String[] { "stashed HEAD",
"HEAD", "stashed index" });
+ ixMerger.setBase(stashHeadCommit);
boolean ok = ixMerger.merge(headCommit, stashIndexCommit);
if (ok) {
resetIndex(revWalk
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
index fc21b919b6..cf0b6d1d9c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
@@ -154,6 +154,7 @@ public class StashCreateCommand extends GitCommand<RevCommit> {
/**
* Set the reference to update with the stashed commit id
+ * If null, no reference is updated
* <p>
* This value defaults to {@link Constants#R_STASH}
*
@@ -185,6 +186,8 @@ public class StashCreateCommand extends GitCommand<RevCommit> {
private void updateStashRef(ObjectId commitId, PersonIdent refLogIdent,
String refLogMessage) throws IOException {
+ if (ref == null)
+ return;
Ref currentRef = repo.getRef(ref);
RefUpdate refUpdate = repo.updateRef(ref);
refUpdate.setNewObjectId(commitId);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/Status.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Status.java
index e840c2f608..c3fcd8bfe8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Status.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Status.java
@@ -43,6 +43,7 @@
package org.eclipse.jgit.api;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -66,19 +67,22 @@ public class Status {
private final boolean clean;
+ private final boolean hasUncommittedChanges;;
+
/**
* @param diff
*/
public Status(IndexDiff diff) {
super();
this.diff = diff;
- clean = diff.getAdded().isEmpty() //
- && diff.getChanged().isEmpty() //
- && diff.getRemoved().isEmpty() //
- && diff.getMissing().isEmpty() //
- && diff.getModified().isEmpty() //
- && diff.getUntracked().isEmpty() //
- && diff.getConflicting().isEmpty();
+ hasUncommittedChanges = !diff.getAdded().isEmpty() //
+ || !diff.getChanged().isEmpty() //
+ || !diff.getRemoved().isEmpty() //
+ || !diff.getMissing().isEmpty() //
+ || !diff.getModified().isEmpty() //
+ || !diff.getConflicting().isEmpty();
+ clean = !hasUncommittedChanges //
+ && diff.getUntracked().isEmpty();
}
/**
@@ -90,6 +94,15 @@ public class Status {
}
/**
+ * @return true if any tracked file is changed
+ *
+ * @since 3.2
+ */
+ public boolean hasUncommittedChanges() {
+ return hasUncommittedChanges;
+ }
+
+ /**
* @return list of files added to the index, not in HEAD (e.g. what you get
* if you call 'git add ...' on a newly created file)
*/
@@ -168,4 +181,21 @@ public class Status {
public Set<String> getIgnoredNotInIndex() {
return Collections.unmodifiableSet(diff.getIgnoredNotInIndex());
}
+
+ /**
+ * @return set of files and folders that are known to the repo and changed
+ * either in the index or in the working tree.
+ *
+ * @since 3.2
+ */
+ public Set<String> getUncommittedChanges() {
+ Set<String> uncommittedChanges = new HashSet<String>();
+ uncommittedChanges.addAll(diff.getAdded());
+ uncommittedChanges.addAll(diff.getChanged());
+ uncommittedChanges.addAll(diff.getRemoved());
+ uncommittedChanges.addAll(diff.getMissing());
+ uncommittedChanges.addAll(diff.getModified());
+ uncommittedChanges.addAll(diff.getConflicting());
+ return uncommittedChanges;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StatusCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StatusCommand.java
index eaf5483671..dee0a31b91 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StatusCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StatusCommand.java
@@ -89,7 +89,8 @@ public class StatusCommand extends GitCommand<Status> {
* supported.
*
* @param path
- * a path is relative to the top level of the repository
+ * repository-relative path of file/directory to show status for
+ * (with <code>/</code> as separator)
* @return {@code this}
* @since 3.1
*/
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
index bfef053d85..09e4cf0a1b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
@@ -93,6 +93,7 @@ public class SubmoduleAddCommand extends
* Set repository-relative path of submodule
*
* @param path
+ * (with <code>/</code> as separator)
* @return this command
*/
public SubmoduleAddCommand setPath(final String path) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java
index e799bfb8bd..0ed02acc82 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java
@@ -83,6 +83,7 @@ public class SubmoduleInitCommand extends GitCommand<Collection<String>> {
* Add repository-relative submodule path to initialize
*
* @param path
+ * (with <code>/</code> as separator)
* @return this command
*/
public SubmoduleInitCommand addPath(final String path) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java
index bbc01adbae..6e89f9873e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java
@@ -83,6 +83,7 @@ public class SubmoduleStatusCommand extends
* Add repository-relative submodule path to limit status reporting to
*
* @param path
+ * (with <code>/</code> as separator)
* @return this command
*/
public SubmoduleStatusCommand addPath(final String path) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java
index 11d3c5acca..e7fe71a890 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java
@@ -85,6 +85,7 @@ public class SubmoduleSyncCommand extends GitCommand<Map<String, String>> {
* Add repository-relative submodule path to synchronize
*
* @param path
+ * (with <code>/</code> as separator)
* @return this command
*/
public SubmoduleSyncCommand addPath(final String path) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
index 40f6a9f9a4..e2f356a08e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
@@ -110,6 +110,7 @@ public class SubmoduleUpdateCommand extends
* Add repository-relative submodule path to initialize
*
* @param path
+ * (with <code>/</code> as separator)
* @return this command
*/
public SubmoduleUpdateCommand addPath(final String path) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index bd896864d7..f8c8442ff8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -76,6 +76,7 @@ import org.eclipse.jgit.treewalk.WorkingTreeOptions;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
+import org.eclipse.jgit.util.RawParseUtils;
import org.eclipse.jgit.util.SystemReader;
import org.eclipse.jgit.util.io.AutoCRLFOutputStream;
@@ -306,8 +307,7 @@ public class DirCacheCheckout {
void processEntry(CanonicalTreeParser m, DirCacheBuildIterator i,
WorkingTreeIterator f) throws IOException {
if (m != null) {
- if (!isValidPath(m))
- throw new InvalidPathException(m.getEntryPathString());
+ checkValidPath(m);
// There is an entry in the merge commit. Means: we want to update
// what's currently in the index and working-tree to that one
if (i == null) {
@@ -522,8 +522,8 @@ public class DirCacheCheckout {
String name = walk.getPathString();
- if (m != null && !isValidPath(m))
- throw new InvalidPathException(m.getEntryPathString());
+ if (m != null)
+ checkValidPath(m);
if (i == null && m == null && h == null) {
// File/Directory conflict case #20
@@ -555,7 +555,9 @@ public class DirCacheCheckout {
* 3 D F D Y N N Keep
* 4 D F D N N N Conflict
* 5 D F F Y N N Y Keep
+ * 5b D F F Y N N N Conflict
* 6 D F F N N N Y Keep
+ * 6b D F F N N N N Conflict
* 7 F D F Y Y N N Update
* 8 F D F N Y N N Conflict
* 9 F D F Y N N N Update
@@ -620,7 +622,11 @@ public class DirCacheCheckout {
case 0xF0D: // 18
remove(name);
break;
- case 0xDFF: // 5 6
+ case 0xDFF: // 5 5b 6 6b
+ if (equalIdAndMode(iId, iMode, mId, mMode))
+ keep(dce); // 5 6
+ else
+ conflict(name, dce, h, m); // 5b 6b
case 0xFDD: // 10 11
// TODO: make use of tree extension as soon as available in jgit
// we would like to do something like
@@ -1151,14 +1157,14 @@ public class DirCacheCheckout {
forbidden[i] = Constants.encodeASCII(list[i]);
}
- private static boolean isValidPath(CanonicalTreeParser t) {
+ private static void checkValidPath(CanonicalTreeParser t)
+ throws InvalidPathException {
for (CanonicalTreeParser i = t; i != null; i = i.getParent())
- if (!isValidPathSegment(i))
- return false;
- return true;
+ checkValidPathSegment(i);
}
- private static boolean isValidPathSegment(CanonicalTreeParser t) {
+ private static void checkValidPathSegment(CanonicalTreeParser t)
+ throws InvalidPathException {
boolean isWindows = SystemReader.getInstance().isWindows();
boolean isOSX = SystemReader.getInstance().isMacOS();
boolean ignCase = isOSX || isWindows;
@@ -1171,23 +1177,29 @@ public class DirCacheCheckout {
int start = ptr;
while (ptr < end) {
if (raw[ptr] == '/')
- return false;
+ throw new InvalidPathException(
+ JGitText.get().invalidPathContainsSeparator,
+ "/", t.getEntryPathString()); //$NON-NLS-1$
if (isWindows) {
if (raw[ptr] == '\\')
- return false;
+ throw new InvalidPathException(
+ JGitText.get().invalidPathContainsSeparator,
+ "\\", t.getEntryPathString()); //$NON-NLS-1$
if (raw[ptr] == ':')
- return false;
+ throw new InvalidPathException(
+ JGitText.get().invalidPathContainsSeparator,
+ ":", t.getEntryPathString()); //$NON-NLS-1$
}
ptr++;
}
- // '.' and '.'' are invalid here
+ // '.' and '..' are invalid here
if (ptr - start == 1) {
if (raw[start] == '.')
- return false;
+ throw new InvalidPathException(t.getEntryPathString());
} else if (ptr - start == 2) {
if (raw[start] == '.')
if (raw[start + 1] == '.')
- return false;
+ throw new InvalidPathException(t.getEntryPathString());
} else if (ptr - start == 4) {
// .git (possibly case insensitive) is disallowed
if (raw[start] == '.')
@@ -1196,15 +1208,24 @@ public class DirCacheCheckout {
|| (ignCase && raw[start + 2] == 'I'))
if (raw[start + 3] == 't'
|| (ignCase && raw[start + 3] == 'T'))
- return false;
+ throw new InvalidPathException(
+ t.getEntryPathString());
}
if (isWindows) {
// Space or period at end of file name is ignored by Windows.
// Treat this as a bad path for now. We may want to handle
// this as case insensitivity in the future.
- if (ptr > 0)
- if (raw[ptr - 1] == '.' || raw[ptr - 1] == ' ')
- return false;
+ if (ptr > 0) {
+ if (raw[ptr - 1] == '.')
+ throw new InvalidPathException(
+ JGitText.get().invalidPathPeriodAtEndWindows,
+ t.getEntryPathString());
+ if (raw[ptr - 1] == ' ')
+ throw new InvalidPathException(
+ JGitText.get().invalidPathSpaceAtEndWindows,
+ t.getEntryPathString());
+ }
+
int i;
// Bad names, eliminate suffix first
for (i = start; i < ptr; ++i)
@@ -1222,13 +1243,14 @@ public class DirCacheCheckout {
break;
}
if (k == len)
- return false;
+ throw new InvalidPathException(
+ JGitText.get().invalidPathReservedOnWindows,
+ RawParseUtils.decode(forbidden[j]), t
+ .getEntryPathString());
}
}
}
}
-
- return true;
}
private static byte toUpper(byte b) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/InvalidPathException.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/InvalidPathException.java
index 698636c8cf..50d1c4ca38 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/InvalidPathException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/InvalidPathException.java
@@ -60,6 +60,10 @@ public class InvalidPathException extends IllegalArgumentException {
* @param path
*/
public InvalidPathException(String path) {
- super(MessageFormat.format(JGitText.get().invalidPath, path));
+ this(JGitText.get().invalidPath, path);
+ }
+
+ InvalidPathException(String messagePattern, Object... arguments) {
+ super(MessageFormat.format(messagePattern, arguments));
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java
index e0c780feec..980f2094bd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java
@@ -198,6 +198,7 @@ public class IgnoreRule {
}
} else {
+ matcher.reset();
matcher.append(target);
if (matcher.isMatch())
return true;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index b2c27a483c..f9700a1ff4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -332,6 +332,10 @@ public class JGitText extends TranslationBundle {
/***/ public String invalidOldIdSent;
/***/ public String invalidPacketLineHeader;
/***/ public String invalidPath;
+ /***/ public String invalidPathContainsSeparator;
+ /***/ public String invalidPathPeriodAtEndWindows;
+ /***/ public String invalidPathSpaceAtEndWindows;
+ /***/ public String invalidPathReservedOnWindows;
/***/ public String invalidReflogRevision;
/***/ public String invalidRefName;
/***/ public String invalidRemote;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
index a8d797dff2..748a4a38e0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
@@ -556,5 +556,9 @@ public final class DfsBlockCache {
hot = true;
return v;
}
+
+ boolean has() {
+ return value != null;
+ }
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
index 1c588d2c4e..7c4776ea06 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
@@ -191,7 +191,7 @@ public final class DfsPackFile {
*/
public boolean isIndexLoaded() {
DfsBlockCache.Ref<PackIndex> idxref = index;
- return idxref != null && idxref.get() != null;
+ return idxref != null && idxref.has();
}
/** @return bytes cached in memory for this pack, excluding the index. */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
index 3e26bc3e62..e06ff65ee4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
@@ -97,6 +97,7 @@ import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.GitDateParser;
+import org.eclipse.jgit.util.SystemReader;
/**
* A garbage collector for git {@link FileRepository}. Instances of this class
@@ -175,21 +176,9 @@ public class GC {
*
* @param oldPacks
* @param newPacks
- * @param ignoreErrors
- * <code>true</code> if we should ignore the fact that a certain
- * pack files or index files couldn't be deleted.
- * <code>false</code> if an exception should be thrown in such
- * cases
- * @throws IOException
- * if a pack file couldn't be deleted and
- * <code>ignoreErrors</code> is set to <code>false</code>
*/
private void deleteOldPacks(Collection<PackFile> oldPacks,
- Collection<PackFile> newPacks, boolean ignoreErrors)
- throws IOException {
- int deleteOptions = FileUtils.RETRY | FileUtils.SKIP_MISSING;
- if (ignoreErrors)
- deleteOptions |= FileUtils.IGNORE_ERRORS;
+ Collection<PackFile> newPacks) {
oldPackLoop: for (PackFile oldPack : oldPacks) {
String oldName = oldPack.getPackName();
// check whether an old pack file is also among the list of new
@@ -200,10 +189,7 @@ public class GC {
if (!oldPack.shouldBeKept()) {
oldPack.close();
- for (PackExt ext : PackExt.values()) {
- File f = nameFor(oldName, "." + ext.getExtension()); //$NON-NLS-1$
- FileUtils.delete(f, deleteOptions);
- }
+ prunePack(oldName);
}
}
// close the complete object database. Thats my only chance to force
@@ -212,6 +198,42 @@ public class GC {
}
/**
+ * Delete files associated with a single pack file. First try to delete the
+ * ".pack" file because on some platforms the ".pack" file may be locked and
+ * can't be deleted. In such a case it is better to detect this early and
+ * give up on deleting files for this packfile. Otherwise we may delete the
+ * ".index" file and when failing to delete the ".pack" file we are left
+ * with a ".pack" file without a ".index" file.
+ *
+ * @param packName
+ */
+ private void prunePack(String packName) {
+ PackExt[] extensions = PackExt.values();
+ try {
+ // Delete the .pack file first and if this fails give up on deleting
+ // the other files
+ int deleteOptions = FileUtils.RETRY | FileUtils.SKIP_MISSING;
+ for (PackExt ext : extensions)
+ if (PackExt.PACK.equals(ext)) {
+ File f = nameFor(packName, "." + ext.getExtension()); //$NON-NLS-1$
+ FileUtils.delete(f, deleteOptions);
+ break;
+ }
+ // The .pack file has been deleted. Delete as many as the other
+ // files as you can.
+ deleteOptions |= FileUtils.IGNORE_ERRORS;
+ for (PackExt ext : extensions) {
+ if (!PackExt.PACK.equals(ext)) {
+ File f = nameFor(packName, "." + ext.getExtension()); //$NON-NLS-1$
+ FileUtils.delete(f, deleteOptions);
+ }
+ }
+ } catch (IOException e) {
+ // Deletion of the .pack file failed. Silently return.
+ }
+ }
+
+ /**
* Like "git prune-packed" this method tries to prune all loose objects
* which can be found in packs. If certain objects can't be pruned (e.g.
* because the filesystem delete operation fails) this is silently ignored.
@@ -286,7 +308,8 @@ public class GC {
ConfigConstants.CONFIG_KEY_PRUNEEXPIRE);
if (pruneExpireStr == null)
pruneExpireStr = PRUNE_EXPIRE_DEFAULT;
- expire = GitDateParser.parse(pruneExpireStr, null);
+ expire = GitDateParser.parse(pruneExpireStr, null, SystemReader
+ .getInstance().getLocale());
expireAgeMillis = -1;
}
if (expire != null)
@@ -533,7 +556,7 @@ public class GC {
if (rest != null)
ret.add(rest);
}
- deleteOldPacks(toBeDeleted, ret, true);
+ deleteOldPacks(toBeDeleted, ret);
prunePacked();
lastPackedRefs = refsBefore;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
index 28d90590a3..db622f319d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
@@ -100,7 +100,7 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
&& ref[7] == ' ';
}
- private static File getSymRef(File workTree, File dotGit)
+ private static File getSymRef(File workTree, File dotGit, FS fs)
throws IOException {
byte[] content = IO.readFully(dotGit);
if (!isSymRef(content))
@@ -116,7 +116,7 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
JGitText.get().invalidGitdirRef, dotGit.getAbsolutePath()));
String gitdirPath = RawParseUtils.decode(content, pathStart, lineEnd);
- File gitdirFile = new File(gitdirPath);
+ File gitdirFile = fs.resolve(workTree, gitdirPath);
if (gitdirFile.isAbsolute())
return gitdirFile;
else
@@ -516,7 +516,7 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
break;
} else if (dir.isFile())
try {
- setGitDir(getSymRef(current, dir));
+ setGitDir(getSymRef(current, dir, tryFS));
break;
} catch (IOException ignored) {
// Continue searching if gitdir ref isn't found
@@ -597,7 +597,7 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
if (!dotGit.isFile())
setGitDir(dotGit);
else
- setGitDir(getSymRef(getWorkTree(), dotGit));
+ setGitDir(getSymRef(getWorkTree(), dotGit, safeFS()));
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
index 8240ac8f76..81977d74ac 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
@@ -435,7 +435,7 @@ public class Config {
}
/**
- * Get string value
+ * Get string value or null if not found.
*
* @param section
* the section
@@ -443,7 +443,7 @@ public class Config {
* the subsection for the value
* @param name
* the key name
- * @return a String value from git config.
+ * @return a String value from the config, <code>null</code> if not found
*/
public String getString(final String section, String subsection,
final String name) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index 3ff4eefb1c..fd22764b6a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -77,6 +77,13 @@ public class ConfigConstants {
/** The "submodule" section */
public static final String CONFIG_SUBMODULE_SECTION = "submodule";
+ /**
+ * The "rebase" section
+ *
+ * @since 3.2
+ */
+ public static final String CONFIG_REBASE_SECTION = "rebase";
+
/** The "gc" section */
public static final String CONFIG_GC_SECTION = "gc";
@@ -136,6 +143,14 @@ public class ConfigConstants {
/** The "autosetuprebase" key */
public static final String CONFIG_KEY_AUTOSETUPREBASE = "autosetuprebase";
+
+ /**
+ * The "autostash" key
+ *
+ * @since 3.2
+ */
+ public static final String CONFIG_KEY_AUTOSTASH = "autostash";
+
/** The "name" key */
public static final String CONFIG_KEY_NAME = "name";
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
index 15d118c0e9..0cc51d1a52 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
@@ -156,9 +156,10 @@ public abstract class ObjectDatabase {
* @param objectId
* identity of the object to open.
* @param typeHint
- * hint about the type of object being requested;
- * {@link ObjectReader#OBJ_ANY} if the object type is not known,
- * or does not matter to the caller.
+ * hint about the type of object being requested, e.g.
+ * {@link Constants#OBJ_BLOB}; {@link ObjectReader#OBJ_ANY} if
+ * the object type is not known, or does not matter to the
+ * caller.
* @return a {@link ObjectLoader} for accessing the object.
* @throws MissingObjectException
* the object does not exist.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
index 3bd02500cc..58c141870b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
@@ -194,9 +194,9 @@ public abstract class ObjectReader {
* @param objectId
* identity of the object to test for existence of.
* @param typeHint
- * hint about the type of object being requested;
- * {@link #OBJ_ANY} if the object type is not known, or does not
- * matter to the caller.
+ * hint about the type of object being requested, e.g.
+ * {@link Constants#OBJ_BLOB}; {@link #OBJ_ANY} if the object
+ * type is not known, or does not matter to the caller.
* @return true if the specified object is stored in this database.
* @throws IncorrectObjectTypeException
* typeHint was not OBJ_ANY, and the object's actual type does
@@ -235,9 +235,9 @@ public abstract class ObjectReader {
* @param objectId
* identity of the object to open.
* @param typeHint
- * hint about the type of object being requested;
- * {@link #OBJ_ANY} if the object type is not known, or does not
- * matter to the caller.
+ * hint about the type of object being requested, e.g.
+ * {@link Constants#OBJ_BLOB}; {@link #OBJ_ANY} if the object
+ * type is not known, or does not matter to the caller.
* @return a {@link ObjectLoader} for accessing the object.
* @throws MissingObjectException
* the object does not exist.
@@ -323,9 +323,9 @@ public abstract class ObjectReader {
* @param objectId
* identity of the object to open.
* @param typeHint
- * hint about the type of object being requested;
- * {@link #OBJ_ANY} if the object type is not known, or does not
- * matter to the caller.
+ * hint about the type of object being requested, e.g.
+ * {@link Constants#OBJ_BLOB}; {@link #OBJ_ANY} if the object
+ * type is not known, or does not matter to the caller.
* @return size of object in bytes.
* @throws MissingObjectException
* the object does not exist.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java
index cac67e11e9..ef61e22032 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java
@@ -197,6 +197,8 @@ public class RebaseTodoFile {
}
tokenCount++;
}
+ if (tokenCount == 2)
+ return new RebaseTodoLine(action, commit, ""); //$NON-NLS-1$
return null;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
index 4a352e8b69..77734bf6b1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -257,9 +257,10 @@ public abstract class Repository {
* @param objectId
* identity of the object to open.
* @param typeHint
- * hint about the type of object being requested;
- * {@link ObjectReader#OBJ_ANY} if the object type is not known,
- * or does not matter to the caller.
+ * hint about the type of object being requested, e.g.
+ * {@link Constants#OBJ_BLOB}; {@link ObjectReader#OBJ_ANY} if
+ * the object type is not known, or does not matter to the
+ * caller.
* @return a {@link ObjectLoader} for accessing the object.
* @throws MissingObjectException
* the object does not exist.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java
index 1346328761..6dba412987 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2009, Google Inc.
+ * Copyright (C) 2008-2013, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -54,8 +54,8 @@ import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTree;
@@ -63,7 +63,6 @@ import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
-import org.eclipse.jgit.treewalk.EmptyTreeIterator;
/**
* Instance of a specific {@link MergeStrategy} for a single {@link Repository}.
@@ -186,24 +185,11 @@ public abstract class Merger {
}
/**
- * Create an iterator to walk the merge base of two commits.
- *
- * @param a
- * the first commit in {@link #sourceObjects}.
- * @param b
- * the second commit in {@link #sourceObjects}.
- * @return the new iterator
- * @throws IncorrectObjectTypeException
- * one of the input objects is not a commit.
- * @throws IOException
- * objects are missing or multiple merge bases were found.
- * @since 3.0
+ * @return the ID of the commit that was used as merge base for merging, or
+ * null if no merge base was used or it was set manually
+ * @since 3.2
*/
- protected AbstractTreeIterator mergeBase(RevCommit a, RevCommit b)
- throws IOException {
- RevCommit base = getBaseCommit(a, b);
- return (base == null) ? new EmptyTreeIterator() : openTree(base.getTree());
- }
+ public abstract ObjectId getBaseCommitId();
/**
* Return the merge base of two commits.
@@ -217,7 +203,10 @@ public abstract class Merger {
* one of the input objects is not a commit.
* @throws IOException
* objects are missing or multiple merge bases were found.
+ * @deprecated use {@link #getBaseCommitId()} instead, as that does not
+ * require walking the commits again
*/
+ @Deprecated
public RevCommit getBaseCommit(final int aIdx, final int bIdx)
throws IncorrectObjectTypeException,
IOException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyOneSided.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyOneSided.java
index 34bc9f5e4d..12d6c6b413 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyOneSided.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyOneSided.java
@@ -106,5 +106,10 @@ public class StrategyOneSided extends MergeStrategy {
public ObjectId getResultTreeId() {
return sourceTrees[treeIndex];
}
+
+ @Override
+ public ObjectId getBaseCommitId() {
+ return null;
+ }
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java
index 1ad791bb79..fbedaef865 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java
@@ -49,14 +49,19 @@ import java.io.IOException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
+import org.eclipse.jgit.treewalk.EmptyTreeIterator;
/** A merge of 2 trees, using a common base ancestor tree. */
public abstract class ThreeWayMerger extends Merger {
private RevTree baseTree;
+ private ObjectId baseCommitId;
+
/**
* Create a new merge instance for a repository.
*
@@ -109,6 +114,11 @@ public abstract class ThreeWayMerger extends Merger {
return super.merge(tips);
}
+ @Override
+ public ObjectId getBaseCommitId() {
+ return baseCommitId;
+ }
+
/**
* Create an iterator to walk the merge base.
*
@@ -119,6 +129,15 @@ public abstract class ThreeWayMerger extends Merger {
protected AbstractTreeIterator mergeBase() throws IOException {
if (baseTree != null)
return openTree(baseTree);
- return mergeBase(sourceCommits[0], sourceCommits[1]);
+ RevCommit baseCommit = (baseCommitId != null) ? walk
+ .parseCommit(baseCommitId) : getBaseCommit(sourceCommits[0],
+ sourceCommits[1]);
+ if (baseCommit == null) {
+ baseCommitId = null;
+ return new EmptyTreeIterator();
+ } else {
+ baseCommitId = baseCommit.toObjectId();
+ return openTree(baseCommit.getTree());
+ }
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java
index 8f4e491de6..32859c9c58 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java
@@ -49,6 +49,7 @@ import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
+import java.util.Locale;
import java.util.Map;
import org.eclipse.jgit.internal.JGitText;
@@ -71,23 +72,40 @@ public class GitDateParser {
// Since SimpleDateFormat instances are expensive to instantiate they should
// be cached. Since they are also not threadsafe they are cached using
// ThreadLocal.
- private static ThreadLocal<Map<ParseableSimpleDateFormat, SimpleDateFormat>> formatCache = new ThreadLocal<Map<ParseableSimpleDateFormat, SimpleDateFormat>>() {
- protected Map<ParseableSimpleDateFormat, SimpleDateFormat> initialValue() {
- return new HashMap<ParseableSimpleDateFormat, SimpleDateFormat>();
+ private static ThreadLocal<Map<Locale, Map<ParseableSimpleDateFormat, SimpleDateFormat>>> formatCache =
+ new ThreadLocal<Map<Locale, Map<ParseableSimpleDateFormat, SimpleDateFormat>>>() {
+
+ protected Map<Locale, Map<ParseableSimpleDateFormat, SimpleDateFormat>> initialValue() {
+ return new HashMap<Locale, Map<ParseableSimpleDateFormat, SimpleDateFormat>>();
}
};
- // Gets an instance of a SimpleDateFormat. If there is not already an
- // appropriate instance in the (ThreadLocal) cache the create one and put in
- // into the cache
- private static SimpleDateFormat getDateFormat(ParseableSimpleDateFormat f) {
- Map<ParseableSimpleDateFormat, SimpleDateFormat> map = formatCache
+ // Gets an instance of a SimpleDateFormat for the specified locale. If there
+ // is not already an appropriate instance in the (ThreadLocal) cache then
+ // create one and put it into the cache.
+ private static SimpleDateFormat getDateFormat(ParseableSimpleDateFormat f,
+ Locale locale) {
+ Map<Locale, Map<ParseableSimpleDateFormat, SimpleDateFormat>> cache = formatCache
.get();
+ Map<ParseableSimpleDateFormat, SimpleDateFormat> map = cache
+ .get(locale);
+ if (map == null) {
+ map = new HashMap<ParseableSimpleDateFormat, SimpleDateFormat>();
+ cache.put(locale, map);
+ return getNewSimpleDateFormat(f, locale, map);
+ }
SimpleDateFormat dateFormat = map.get(f);
if (dateFormat != null)
return dateFormat;
+ SimpleDateFormat df = getNewSimpleDateFormat(f, locale, map);
+ return df;
+ }
+
+ private static SimpleDateFormat getNewSimpleDateFormat(
+ ParseableSimpleDateFormat f, Locale locale,
+ Map<ParseableSimpleDateFormat, SimpleDateFormat> map) {
SimpleDateFormat df = SystemReader.getInstance().getSimpleDateFormat(
- f.formatStr);
+ f.formatStr, locale);
map.put(f, df);
return df;
}
@@ -115,9 +133,9 @@ public class GitDateParser {
}
/**
- * Parses a string into a {@link Date}. Since this parser also supports
- * relative formats (e.g. "yesterday") the caller can specify the reference
- * date. These types of strings can be parsed:
+ * Parses a string into a {@link Date} using the default locale. Since this
+ * parser also supports relative formats (e.g. "yesterday") the caller can
+ * specify the reference date. These types of strings can be parsed:
* <ul>
* <li>"never"</li>
* <li>"now"</li>
@@ -151,6 +169,49 @@ public class GitDateParser {
*/
public static Date parse(String dateStr, Calendar now)
throws ParseException {
+ return parse(dateStr, now, Locale.getDefault());
+ }
+
+ /**
+ * Parses a string into a {@link Date} using the given locale. Since this
+ * parser also supports relative formats (e.g. "yesterday") the caller can
+ * specify the reference date. These types of strings can be parsed:
+ * <ul>
+ * <li>"never"</li>
+ * <li>"now"</li>
+ * <li>"yesterday"</li>
+ * <li>"(x) years|months|weeks|days|hours|minutes|seconds ago"<br>
+ * Multiple specs can be combined like in "2 weeks 3 days ago". Instead of
+ * ' ' one can use '.' to seperate the words</li>
+ * <li>"yyyy-MM-dd HH:mm:ss Z" (ISO)</li>
+ * <li>"EEE, dd MMM yyyy HH:mm:ss Z" (RFC)</li>
+ * <li>"yyyy-MM-dd"</li>
+ * <li>"yyyy.MM.dd"</li>
+ * <li>"MM/dd/yyyy",</li>
+ * <li>"dd.MM.yyyy"</li>
+ * <li>"EEE MMM dd HH:mm:ss yyyy Z" (DEFAULT)</li>
+ * <li>"EEE MMM dd HH:mm:ss yyyy" (LOCAL)</li>
+ * </ul>
+ *
+ * @param dateStr
+ * the string to be parsed
+ * @param now
+ * the base date which is used for the calculation of relative
+ * formats. E.g. if baseDate is "25.8.2012" then parsing of the
+ * string "1 week ago" would result in a date corresponding to
+ * "18.8.2012". This is used when a JGit command calls this
+ * parser often but wants a consistent starting point for calls.<br>
+ * If set to <code>null</code> then the current time will be used
+ * instead.
+ * @param locale
+ * locale to be used to parse the date string
+ * @return the parsed {@link Date}
+ * @throws ParseException
+ * if the given dateStr was not recognized
+ * @since 3.2
+ */
+ public static Date parse(String dateStr, Calendar now, Locale locale)
+ throws ParseException {
dateStr = dateStr.trim();
Date ret;
@@ -161,7 +222,7 @@ public class GitDateParser {
return ret;
for (ParseableSimpleDateFormat f : ParseableSimpleDateFormat.values()) {
try {
- return parse_simple(dateStr, f);
+ return parse_simple(dateStr, f, locale);
} catch (ParseException e) {
// simply proceed with the next parser
}
@@ -177,9 +238,10 @@ public class GitDateParser {
}
// tries to parse a string with the formats supported by SimpleDateFormat
- private static Date parse_simple(String dateStr, ParseableSimpleDateFormat f)
+ private static Date parse_simple(String dateStr,
+ ParseableSimpleDateFormat f, Locale locale)
throws ParseException {
- SimpleDateFormat dateFormat = getDateFormat(f);
+ SimpleDateFormat dateFormat = getDateFormat(f, locale);
dateFormat.setLenient(false);
return dateFormat.parse(dateStr);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
index b6028610bf..e73f100f96 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -230,6 +230,21 @@ public abstract class SystemReader {
}
/**
+ * Returns a simple date format instance as specified by the given pattern.
+ *
+ * @param pattern
+ * the pattern as defined in
+ * {@link SimpleDateFormat#SimpleDateFormat(String)}
+ * @param locale
+ * locale to be used for the {@code SimpleDateFormat}
+ * @return the simple date format
+ * @since 3.2
+ */
+ public SimpleDateFormat getSimpleDateFormat(String pattern, Locale locale) {
+ return new SimpleDateFormat(pattern, locale);
+ }
+
+ /**
* Returns a date/time format instance for the given styles.
*
* @param dateStyle
diff --git a/pom.xml b/pom.xml
index 2106517940..7bcf49482d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -184,7 +184,7 @@
<commons-compress-version>1.4.1</commons-compress-version>
<osgi-core-version>4.3.1</osgi-core-version>
<servlet-api-version>2.5</servlet-api-version>
- <jetty-version>7.6.11.v20130520</jetty-version>
+ <jetty-version>7.6.14.v20131031</jetty-version>
<clirr-version>2.4</clirr-version>
</properties>