aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit.test/tst
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit.test/tst')
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java30
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java46
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java42
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StatusCommandTest.java25
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java117
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java88
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmappedObjectReachabilityTest.java31
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FirstParentRevWalkTest.java52
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectReachabilityTestCase.java143
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityTest.java25
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkSortTest.java170
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java11
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java78
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java37
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java32
15 files changed, 918 insertions, 9 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java
index 80bcb19d5e..63cd21f59d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java
@@ -250,6 +250,36 @@ public class ApplyCommandTest extends RepositoryTestCase {
assertFalse(new File(db.getWorkTree(), "NonASCIIDel").exists());
}
+ @Test
+ public void testRenameNoHunks() throws Exception {
+ ApplyResult result = init("RenameNoHunks", true, true);
+ assertEquals(1, result.getUpdatedFiles().size());
+ assertEquals(new File(db.getWorkTree(), "RenameNoHunks"), result.getUpdatedFiles()
+ .get(0));
+ checkFile(new File(db.getWorkTree(), "nested/subdir/Renamed"),
+ b.getString(0, b.size(), false));
+ }
+
+ @Test
+ public void testRenameWithHunks() throws Exception {
+ ApplyResult result = init("RenameWithHunks", true, true);
+ assertEquals(1, result.getUpdatedFiles().size());
+ assertEquals(new File(db.getWorkTree(), "RenameWithHunks"), result.getUpdatedFiles()
+ .get(0));
+ checkFile(new File(db.getWorkTree(), "nested/subdir/Renamed"),
+ b.getString(0, b.size(), false));
+ }
+
+ @Test
+ public void testCopyWithHunks() throws Exception {
+ ApplyResult result = init("CopyWithHunks", true, true);
+ assertEquals(1, result.getUpdatedFiles().size());
+ assertEquals(new File(db.getWorkTree(), "CopyWithHunks"), result.getUpdatedFiles()
+ .get(0));
+ checkFile(new File(db.getWorkTree(), "CopyResult"),
+ b.getString(0, b.size(), false));
+ }
+
private static byte[] readFile(String patchFile) throws IOException {
final InputStream in = getTestResource(patchFile);
if (in == null) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
index 3d0dacab3d..b737bbec0e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
@@ -44,6 +44,7 @@ import org.eclipse.jgit.submodule.SubmoduleStatusType;
import org.eclipse.jgit.submodule.SubmoduleWalk;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.TagOpt;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.util.SystemReader;
import org.junit.Test;
@@ -111,6 +112,7 @@ public class CloneCommandTest extends RepositoryTestCase {
.size());
assertEquals(new RefSpec("+refs/heads/*:refs/remotes/origin/*"),
fetchRefSpec(git2.getRepository()));
+ assertTagOption(git2.getRepository(), TagOpt.AUTO_FOLLOW);
}
@Test
@@ -801,6 +803,50 @@ public class CloneCommandTest extends RepositoryTestCase {
}
}
+ @Test
+ public void testCloneNoTags() throws IOException, JGitInternalException,
+ GitAPIException, URISyntaxException {
+ File directory = createTempDirectory("testCloneRepository");
+ CloneCommand command = Git.cloneRepository();
+ command.setDirectory(directory);
+ command.setURI(fileUri());
+ command.setNoTags();
+ Git git2 = command.call();
+ addRepoToClose(git2.getRepository());
+ assertNotNull(git2);
+ assertNotNull(git2.getRepository().resolve("refs/heads/test"));
+ assertNull(git2.getRepository().resolve("tag-initial"));
+ assertNull(git2.getRepository().resolve("tag-for-blob"));
+ assertTagOption(git2.getRepository(), TagOpt.NO_TAGS);
+ }
+
+ @Test
+ public void testCloneFollowTags() throws IOException, JGitInternalException,
+ GitAPIException, URISyntaxException {
+ File directory = createTempDirectory("testCloneRepository");
+ CloneCommand command = Git.cloneRepository();
+ command.setDirectory(directory);
+ command.setURI(fileUri());
+ command.setBranch("refs/heads/master");
+ command.setBranchesToClone(
+ Collections.singletonList("refs/heads/master"));
+ command.setTagOption(TagOpt.FETCH_TAGS);
+ Git git2 = command.call();
+ addRepoToClose(git2.getRepository());
+ assertNotNull(git2);
+ assertNull(git2.getRepository().resolve("refs/heads/test"));
+ assertNotNull(git2.getRepository().resolve("tag-initial"));
+ assertNotNull(git2.getRepository().resolve("tag-for-blob"));
+ assertTagOption(git2.getRepository(), TagOpt.FETCH_TAGS);
+ }
+
+ private void assertTagOption(Repository repo, TagOpt expectedTagOption)
+ throws URISyntaxException {
+ RemoteConfig remoteConfig = new RemoteConfig(
+ repo.getConfig(), "origin");
+ assertEquals(expectedTagOption, remoteConfig.getTagOpt());
+ }
+
private String fileUri() {
return "file://" + git.getRepository().getWorkTree().getAbsolutePath();
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
index 4cc3ca0a53..8084505c10 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
@@ -16,6 +16,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import java.io.File;
import java.util.Date;
@@ -600,41 +601,64 @@ public class CommitCommandTest extends RepositoryTestCase {
}
}
- @Test
- public void commitWithAutoCrlfAndNonNormalizedIndex() throws Exception {
+ private void nonNormalizedIndexTest(boolean executable) throws Exception {
+ String mode = executable ? "100755" : "100644";
try (Git git = new Git(db)) {
// Commit a file with CR/LF into the index
FileBasedConfig config = db.getConfig();
config.setString("core", null, "autocrlf", "false");
config.save();
- writeTrashFile("file.txt", "line 1\r\nline 2\r\n");
+ File testFile = writeTrashFile("file.txt", "line 1\r\nline 2\r\n");
+ if (executable) {
+ FS.DETECTED.setExecute(testFile, true);
+ }
git.add().addFilepattern("file.txt").call();
git.commit().setMessage("Initial").call();
assertEquals(
- "[file.txt, mode:100644, content:line 1\r\nline 2\r\n]",
+ "[file.txt, mode:" + mode
+ + ", content:line 1\r\nline 2\r\n]",
indexState(CONTENT));
config.setString("core", null, "autocrlf", "true");
config.save();
writeTrashFile("file.txt", "line 1\r\nline 1.5\r\nline 2\r\n");
- writeTrashFile("file2.txt", "new\r\nfile\r\n");
+ testFile = writeTrashFile("file2.txt", "new\r\nfile\r\n");
+ if (executable) {
+ FS.DETECTED.setExecute(testFile, true);
+ }
git.add().addFilepattern("file.txt").addFilepattern("file2.txt")
.call();
git.commit().setMessage("Second").call();
assertEquals(
- "[file.txt, mode:100644, content:line 1\r\nline 1.5\r\nline 2\r\n]"
- + "[file2.txt, mode:100644, content:new\nfile\n]",
+ "[file.txt, mode:" + mode
+ + ", content:line 1\r\nline 1.5\r\nline 2\r\n]"
+ + "[file2.txt, mode:" + mode
+ + ", content:new\nfile\n]",
indexState(CONTENT));
writeTrashFile("file2.txt", "new\r\nfile\r\ncontent\r\n");
git.add().addFilepattern("file2.txt").call();
git.commit().setMessage("Third").call();
assertEquals(
- "[file.txt, mode:100644, content:line 1\r\nline 1.5\r\nline 2\r\n]"
- + "[file2.txt, mode:100644, content:new\nfile\ncontent\n]",
+ "[file.txt, mode:" + mode
+ + ", content:line 1\r\nline 1.5\r\nline 2\r\n]"
+ + "[file2.txt, mode:" + mode
+ + ", content:new\nfile\ncontent\n]",
indexState(CONTENT));
}
}
@Test
+ public void commitWithAutoCrlfAndNonNormalizedIndex() throws Exception {
+ nonNormalizedIndexTest(false);
+ }
+
+ @Test
+ public void commitExecutableWithAutoCrlfAndNonNormalizedIndex()
+ throws Exception {
+ assumeTrue(FS.DETECTED.supportsExecute());
+ nonNormalizedIndexTest(true);
+ }
+
+ @Test
public void testDeletionConflictWithAutoCrlf() throws Exception {
try (Git git = new Git(db)) {
// Commit a file with CR/LF into the index
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StatusCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StatusCommandTest.java
index e0d9cbc197..7e0de82d82 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StatusCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StatusCommandTest.java
@@ -12,6 +12,7 @@ package org.eclipse.jgit.api;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import java.io.File;
import java.io.IOException;
@@ -21,6 +22,8 @@ import org.eclipse.jgit.api.errors.NoFilepatternException;
import org.eclipse.jgit.errors.NoWorkTreeException;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.Sets;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
import org.junit.Test;
public class StatusCommandTest extends RepositoryTestCase {
@@ -135,4 +138,26 @@ public class StatusCommandTest extends RepositoryTestCase {
assertEquals(Sets.of("a", "D/b", "D/D/d"), stat.getModified());
}
}
+
+ @Test
+ public void testExecutableWithNonNormalizedIndex() throws Exception {
+ assumeTrue(FS.DETECTED.supportsExecute());
+ try (Git git = new Git(db)) {
+ // Commit a file with CR/LF into the index
+ FileBasedConfig config = db.getConfig();
+ config.setString("core", null, "autocrlf", "false");
+ config.save();
+ File testFile = writeTrashFile("file.txt", "line 1\r\nline 2\r\n");
+ FS.DETECTED.setExecute(testFile, true);
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("Initial").call();
+ assertEquals(
+ "[file.txt, mode:100755, content:line 1\r\nline 2\r\n]",
+ indexState(CONTENT));
+ config.setString("core", null, "autocrlf", "true");
+ config.save();
+ Status status = git.status().call();
+ assertTrue("Expected no differences", status.isClean());
+ }
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java
index 2a2aeb592f..a246ac9e9c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java
@@ -287,6 +287,123 @@ public class MergedReftableTest {
}
@Test
+ public void nonOverlappedUpdateIndices() throws IOException {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ ReftableWriter writer = new ReftableWriter(buf)
+ .setMinUpdateIndex(1)
+ .setMaxUpdateIndex(2)
+ .begin();
+ writer.writeRef(ref("refs/heads/a", 1), 1);
+ writer.writeRef(ref("refs/heads/b", 2), 2);
+ writer.finish();
+ byte[] base = buf.toByteArray();
+
+ buf = new ByteArrayOutputStream();
+ writer = new ReftableWriter(buf)
+ .setMinUpdateIndex(3)
+ .setMaxUpdateIndex(4)
+ .begin();
+ writer.writeRef(ref("refs/heads/a", 10), 3);
+ writer.writeRef(ref("refs/heads/b", 20), 4);
+ writer.finish();
+ byte[] delta = buf.toByteArray();
+
+ MergedReftable mr = merge(base, delta);
+ assertEquals(1, mr.minUpdateIndex());
+ assertEquals(4, mr.maxUpdateIndex());
+
+ try (RefCursor rc = mr.allRefs()) {
+ assertTrue(rc.next());
+ assertEquals("refs/heads/a", rc.getRef().getName());
+ assertEquals(id(10), rc.getRef().getObjectId());
+ assertEquals(3, rc.getRef().getUpdateIndex());
+
+ assertTrue(rc.next());
+ assertEquals("refs/heads/b", rc.getRef().getName());
+ assertEquals(id(20), rc.getRef().getObjectId());
+ assertEquals(4, rc.getRef().getUpdateIndex());
+ }
+ }
+
+ @Test
+ public void overlappedUpdateIndices() throws IOException {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ ReftableWriter writer = new ReftableWriter(buf)
+ .setMinUpdateIndex(1)
+ .setMaxUpdateIndex(3)
+ .begin();
+ writer.writeRef(ref("refs/heads/a", 1), 1);
+ writer.writeRef(ref("refs/heads/b", 2), 3);
+ writer.finish();
+ byte[] base = buf.toByteArray();
+
+ buf = new ByteArrayOutputStream();
+ writer = new ReftableWriter(buf)
+ .setMinUpdateIndex(2)
+ .setMaxUpdateIndex(4)
+ .begin();
+ writer.writeRef(ref("refs/heads/a", 10), 2);
+ writer.writeRef(ref("refs/heads/b", 20), 4);
+ writer.finish();
+ byte[] delta = buf.toByteArray();
+
+ MergedReftable mr = merge(base, delta);
+ assertEquals(1, mr.minUpdateIndex());
+ assertEquals(4, mr.maxUpdateIndex());
+
+ try (RefCursor rc = mr.allRefs()) {
+ assertTrue(rc.next());
+ assertEquals("refs/heads/a", rc.getRef().getName());
+ assertEquals(id(10), rc.getRef().getObjectId());
+ assertEquals(2, rc.getRef().getUpdateIndex());
+
+ assertTrue(rc.next());
+ assertEquals("refs/heads/b", rc.getRef().getName());
+ assertEquals(id(20), rc.getRef().getObjectId());
+ assertEquals(4, rc.getRef().getUpdateIndex());
+ }
+ }
+
+ @Test
+ public void enclosedUpdateIndices() throws IOException {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ ReftableWriter writer = new ReftableWriter(buf)
+ .setMinUpdateIndex(1)
+ .setMaxUpdateIndex(4)
+ .begin();
+ writer.writeRef(ref("refs/heads/a", 1), 1);
+ writer.writeRef(ref("refs/heads/b", 20), 4);
+ writer.finish();
+ byte[] base = buf.toByteArray();
+
+ buf = new ByteArrayOutputStream();
+ writer = new ReftableWriter(buf)
+ .setMinUpdateIndex(2)
+ .setMaxUpdateIndex(3)
+ .begin();
+ writer.writeRef(ref("refs/heads/a", 10), 2);
+ writer.writeRef(ref("refs/heads/b", 2), 3);
+ writer.finish();
+ byte[] delta = buf.toByteArray();
+
+ MergedReftable mr = merge(base, delta);
+ assertEquals(1, mr.minUpdateIndex());
+ assertEquals(4, mr.maxUpdateIndex());
+
+ try (RefCursor rc = mr.allRefs()) {
+ assertTrue(rc.next());
+ assertEquals("refs/heads/a", rc.getRef().getName());
+ assertEquals(id(10), rc.getRef().getObjectId());
+ assertEquals(2, rc.getRef().getUpdateIndex());
+
+ assertTrue(rc.next());
+ assertEquals("refs/heads/b", rc.getRef().getName());
+ assertEquals(id(20), rc.getRef().getObjectId());
+ assertEquals(4, rc.getRef().getUpdateIndex());
+ }
+ }
+
+ @Test
public void compaction() throws IOException {
List<Ref> delta1 = Arrays.asList(
ref("refs/heads/next", 4),
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
index 032349d5f8..7a244e1d8b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
@@ -1254,6 +1254,94 @@ public class MergerTest extends RepositoryTestCase {
}
}
+ /**
+ * Merging two commits with a conflict in the virtual ancestor.
+ *
+ * Content conflicts while merging the virtual ancestor must be ignored.
+ *
+ * In the following tree, while merging A and B, the recursive algorithm
+ * finds as base commits X and Y and tries to merge them: X deletes file "a"
+ * and Y modifies it.
+ *
+ * Note: we delete "a" in (master) and (second-branch) to make avoid manual
+ * merges. The situation is the same without those deletions and fixing
+ * manually the merge of (merge-both-sides) on both branches.
+ *
+ * <pre>
+ * A (second-branch) Merge branch 'merge-both-sides' into second-branch
+ * |\
+ * o | Delete modified a
+ * | |
+ * | | B (master) Merge branch 'merge-both-sides' (into master)
+ * | |/|
+ * | X | (merge-both-sides) Delete original a
+ * | | |
+ * | | o Delete modified a
+ * | |/
+ * |/|
+ * Y | Modify a
+ * |/
+ * o Initial commit
+ * </pre>
+ *
+ * @param strategy
+ * @throws Exception
+ */
+ @Theory
+ public void checkMergeConflictInVirtualAncestor(
+ MergeStrategy strategy) throws Exception {
+ if (!strategy.equals(MergeStrategy.RECURSIVE)) {
+ return;
+ }
+
+ Git git = Git.wrap(db);
+
+ // master
+ writeTrashFile("a", "aaaaaaaa");
+ writeTrashFile("b", "bbbbbbbb");
+ git.add().addFilepattern("a").addFilepattern("b").call();
+ RevCommit first = git.commit().setMessage("Initial commit").call();
+
+ writeTrashFile("a", "aaaaaaaaaaaaaaa");
+ git.add().addFilepattern("a").call();
+ RevCommit commitY = git.commit().setMessage("Modify a").call();
+
+ git.rm().addFilepattern("a").call();
+ // Do more in this commits, so it is not identical to the deletion in
+ // second-branch
+ writeTrashFile("c", "cccccccc");
+ git.add().addFilepattern("c").call();
+ git.commit().setMessage("Delete modified a").call();
+
+ // merge-both-sides: starts before "a" is modified and deletes it
+ git.checkout().setCreateBranch(true).setStartPoint(first)
+ .setName("merge-both-sides").call();
+ git.rm().addFilepattern("a").call();
+ RevCommit commitX = git.commit().setMessage("Delete original a").call();
+
+ // second branch
+ git.checkout().setCreateBranch(true).setStartPoint(commitY)
+ .setName("second-branch").call();
+ git.rm().addFilepattern("a").call();
+ git.commit().setMessage("Delete modified a").call();
+
+ // Merge merge-both-sides into second-branch
+ MergeResult mergeResult = git.merge().include(commitX)
+ .setStrategy(strategy)
+ .call();
+ ObjectId commitB = mergeResult.getNewHead();
+
+ // Merge merge-both-sides into master
+ git.checkout().setName("master").call();
+ mergeResult = git.merge().include(commitX).setStrategy(strategy)
+ .call();
+
+ // Now, merge commit A and B (i.e. "master" and "second-branch").
+ // None of them have the file "a", so there is no conflict, BUT while
+ // building the virtual ancestor it will find a conflict between Y and X
+ git.merge().include(commitB).call();
+ }
+
private void writeSubmodule(String path, ObjectId commit)
throws IOException, ConfigInvalidException {
addSubmoduleToIndex(path, commit);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmappedObjectReachabilityTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmappedObjectReachabilityTest.java
new file mode 100644
index 0000000000..d2b6e89168
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmappedObjectReachabilityTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.revwalk;
+
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.internal.storage.file.GC;
+import org.eclipse.jgit.junit.TestRepository;
+
+public class BitmappedObjectReachabilityTest
+ extends ObjectReachabilityTestCase {
+
+ @Override
+ ObjectReachabilityChecker getChecker(
+ TestRepository<FileRepository> repository) throws Exception {
+ // GC generates the bitmaps
+ GC gc = new GC(repository.getRepository());
+ gc.setAuto(false);
+ gc.gc();
+
+ return new BitmappedObjectReachabilityChecker(
+ repository.getRevWalk().toObjectWalkWithSameObjects());
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FirstParentRevWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FirstParentRevWalkTest.java
index 4a3b04d4e2..c8256b89c0 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FirstParentRevWalkTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FirstParentRevWalkTest.java
@@ -164,6 +164,23 @@ public class FirstParentRevWalkTest extends RevWalkTestCase {
}
@Test
+ public void testTopoNonIntermixSort() throws Exception {
+ RevCommit a = commit();
+ RevCommit b1 = commit(a);
+ RevCommit b2 = commit(a);
+ RevCommit c = commit(b1, b2);
+
+ rw.reset();
+ rw.sort(RevSort.TOPO_KEEP_BRANCH_TOGETHER);
+ rw.setFirstParent(true);
+ markStart(c);
+ assertCommit(c, rw.next());
+ assertCommit(b1, rw.next());
+ assertCommit(a, rw.next());
+ assertNull(rw.next());
+ }
+
+ @Test
public void testCommitTimeSort() throws Exception {
RevCommit a = commit();
RevCommit b1 = commit(a);
@@ -428,4 +445,39 @@ public class FirstParentRevWalkTest extends RevWalkTestCase {
assertCommit(c, rw.next());
assertNull(rw.next());
}
+
+ @Test
+ public void testWithTopoNonIntermixSortAndTreeFilter() throws Exception {
+ RevCommit a = commit();
+ RevCommit b = commit(tree(file("0", blob("b"))), a);
+ RevCommit c = commit(tree(file("0", blob("c"))), b, a);
+ RevCommit d = commit(tree(file("0", blob("d"))), c);
+
+ rw.reset();
+ rw.setFirstParent(true);
+ rw.sort(RevSort.TOPO_KEEP_BRANCH_TOGETHER, true);
+ rw.setTreeFilter(PathFilterGroup.createFromStrings("0"));
+ markStart(d);
+ assertCommit(d, rw.next());
+ assertCommit(c, rw.next());
+ assertCommit(b, rw.next());
+ assertNull(rw.next());
+ }
+
+ @Test
+ public void testWithTopoNonIntermixSortAndTreeFilter2() throws Exception {
+ RevCommit a = commit();
+ RevCommit b = commit(tree(file("0", blob("b"))), a);
+ RevCommit c = commit(tree(file("0", blob("c"))), a, b);
+ RevCommit d = commit(tree(file("0", blob("d"))), c);
+
+ rw.reset();
+ rw.setFirstParent(true);
+ rw.sort(RevSort.TOPO_KEEP_BRANCH_TOGETHER, true);
+ rw.setTreeFilter(PathFilterGroup.createFromStrings("0"));
+ markStart(d);
+ assertCommit(d, rw.next());
+ assertCommit(c, rw.next());
+ assertNull(rw.next());
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectReachabilityTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectReachabilityTestCase.java
new file mode 100644
index 0000000000..267b163f43
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectReachabilityTestCase.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2020, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.revwalk;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.junit.TestRepository.CommitBuilder;
+import org.junit.Before;
+import org.junit.Test;
+
+public abstract class ObjectReachabilityTestCase
+ extends LocalDiskRepositoryTestCase {
+
+ private TestRepository<FileRepository> repo;
+ private AddressableRevCommit baseCommit;
+ private AddressableRevCommit branchACommit;
+ private AddressableRevCommit branchBCommit;
+ private AddressableRevCommit mergeCommit;
+
+ abstract ObjectReachabilityChecker getChecker(
+ TestRepository<FileRepository> repository) throws Exception;
+
+ // Pair of commit and blob inside it
+ protected static class AddressableRevCommit {
+ RevCommit commit;
+
+ RevBlob blob;
+
+ AddressableRevCommit(RevCommit commit, RevBlob blob) {
+ this.commit = commit;
+ this.blob = blob;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ FileRepository db = createWorkRepository();
+ repo = new TestRepository<>(db);
+ prepareRepo();
+ }
+
+ @Test
+ public void blob_in_base_reachable_from_branches() throws Exception {
+ ObjectReachabilityChecker checker = getChecker(repo);
+
+ RevObject baseBlob = baseCommit.blob;
+ assertReachable("reachable from one branch", checker.areAllReachable(
+ Arrays.asList(baseBlob), Stream.of(branchACommit.commit)));
+ assertReachable("reachable from another branch",
+ checker.areAllReachable(
+ Arrays.asList(baseBlob),
+ Stream.of(branchBCommit.commit)));
+ }
+
+ @Test
+ public void blob_reachable_from_owning_commit() throws Exception {
+ ObjectReachabilityChecker checker = getChecker(repo);
+
+ RevObject branchABlob = branchACommit.blob;
+ assertReachable("reachable from itself",
+ checker.areAllReachable(Arrays.asList(branchABlob),
+ Stream.of(branchACommit.commit)));
+ }
+
+ @Test
+ public void blob_in_branch_reachable_from_merge() throws Exception {
+ ObjectReachabilityChecker checker = getChecker(repo);
+
+ RevObject branchABlob = branchACommit.blob;
+ assertReachable("reachable from merge", checker.areAllReachable(
+ Arrays.asList(branchABlob), Stream.of(mergeCommit.commit)));
+ }
+
+ @Test
+ public void blob_unreachable_from_earlier_commit() throws Exception {
+ ObjectReachabilityChecker checker = getChecker(repo);
+
+ RevObject branchABlob = branchACommit.blob;
+ assertUnreachable("unreachable from earlier commit",
+ checker.areAllReachable(Arrays.asList(branchABlob),
+ Stream.of(baseCommit.commit)));
+ }
+
+ @Test
+ public void blob_unreachable_from_parallel_branch() throws Exception {
+ ObjectReachabilityChecker checker = getChecker(repo);
+
+ RevObject branchABlob = branchACommit.blob;
+ assertUnreachable("unreachable from another branch",
+ checker.areAllReachable(Arrays.asList(branchABlob),
+ Stream.of(branchBCommit.commit)));
+ }
+
+ private void prepareRepo() throws Exception {
+ baseCommit = createCommit("base");
+ branchACommit = createCommit("branchA", baseCommit);
+ branchBCommit = createCommit("branchB", baseCommit);
+ mergeCommit = createCommit("merge", branchACommit, branchBCommit);
+
+ // Bitmaps are generated from references
+ repo.update("refs/heads/a", branchACommit.commit);
+ repo.update("refs/heads/b", branchBCommit.commit);
+ repo.update("refs/heads/merge", mergeCommit.commit);
+ }
+
+ private AddressableRevCommit createCommit(String blobPath, AddressableRevCommit... parents) throws Exception {
+ RevBlob blob = repo.blob(blobPath + " content");
+ CommitBuilder commitBuilder = repo.commit();
+ for (int i = 0; i < parents.length; i++) {
+ commitBuilder.parent(parents[i].commit);
+ }
+ commitBuilder.add(blobPath, blob);
+
+ RevCommit commit = commitBuilder.create();
+ return new AddressableRevCommit(commit, blob);
+ }
+
+ private static void assertReachable(String msg, Optional<RevObject> result) {
+ assertFalse(msg, result.isPresent());
+ }
+
+ private static void assertUnreachable(String msg, Optional<RevObject> result) {
+ assertTrue(msg, result.isPresent());
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityTest.java
new file mode 100644
index 0000000000..b1c9556df8
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianObjectReachabilityTest.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.revwalk;
+
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.junit.TestRepository;
+
+public class PedestrianObjectReachabilityTest
+ extends ObjectReachabilityTestCase {
+
+ @Override
+ ObjectReachabilityChecker getChecker(
+ TestRepository<FileRepository> repository)
+ throws Exception {
+ return new PedestrianObjectReachabilityChecker(
+ repository.getRevWalk().toObjectWalkWithSameObjects());
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkSortTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkSortTest.java
index 6f110fa317..8af6747739 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkSortTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkSortTest.java
@@ -10,9 +10,12 @@
package org.eclipse.jgit.revwalk;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import org.eclipse.jgit.internal.JGitText;
import org.junit.Test;
public class RevWalkSortTest extends RevWalkTestCase {
@@ -144,4 +147,171 @@ public class RevWalkSortTest extends RevWalkTestCase {
assertCommit(d, rw.next());
assertNull(rw.next());
}
+
+ @Test
+ public void testSort_TOPO_NON_INTERMIX() throws Exception {
+ // c1 is back dated before its parent.
+ //
+ final RevCommit a = commit();
+ final RevCommit b = commit(a);
+ final RevCommit c1 = commit(-5, b);
+ final RevCommit c2 = commit(10, b);
+ final RevCommit d = commit(c1, c2);
+
+ rw.sort(RevSort.TOPO_KEEP_BRANCH_TOGETHER);
+ markStart(d);
+ assertCommit(d, rw.next());
+ assertCommit(c2, rw.next());
+ assertCommit(c1, rw.next());
+ assertCommit(b, rw.next());
+ assertCommit(a, rw.next());
+ assertNull(rw.next());
+ }
+
+ @Test
+ public void testSort_TOPO_NON_INTERMIX_OutOfOrderCommitTimes()
+ throws Exception {
+ // b is committed before c2 in a different line of history.
+ //
+ final RevCommit a = commit();
+ final RevCommit c1 = commit(a);
+ final RevCommit b = commit(a);
+ final RevCommit c2 = commit(c1);
+ final RevCommit d = commit(b, c2);
+
+ rw.sort(RevSort.TOPO_KEEP_BRANCH_TOGETHER);
+ markStart(d);
+ assertCommit(d, rw.next());
+ assertCommit(c2, rw.next());
+ assertCommit(c1, rw.next());
+ assertCommit(b, rw.next());
+ assertCommit(a, rw.next());
+ assertNull(rw.next());
+ }
+
+ @Test
+ public void testSort_TOPO_NON_INTERMIX_MultipleLinesOfHistory()
+ throws Exception {
+ final RevCommit a1 = commit();
+ final RevCommit b1 = commit(a1);
+ final RevCommit a2 = commit(a1, b1);
+ final RevCommit b2 = commit(b1);
+ final RevCommit b3 = commit(b1);
+ final RevCommit a3 = commit(a2, b2);
+ final RevCommit a4 = commit(a3, b3);
+
+ rw.sort(RevSort.TOPO_KEEP_BRANCH_TOGETHER);
+ markStart(a4);
+ assertCommit(a4, rw.next());
+ assertCommit(b3, rw.next());
+ assertCommit(a3, rw.next());
+ assertCommit(b2, rw.next());
+ assertCommit(a2, rw.next());
+ assertCommit(b1, rw.next());
+ assertCommit(a1, rw.next());
+ assertNull(rw.next());
+ }
+
+ @Test
+ public void testSort_TOPO_NON_INTERMIX_REVERSE() throws Exception {
+ // c1 is back dated before its parent.
+ //
+ final RevCommit a = commit();
+ final RevCommit b = commit(a);
+ final RevCommit c1 = commit(-5, b);
+ final RevCommit c2 = commit(10, b);
+ final RevCommit d = commit(c1, c2);
+
+ rw.sort(RevSort.TOPO_KEEP_BRANCH_TOGETHER);
+ rw.sort(RevSort.REVERSE, true);
+ markStart(d);
+ assertCommit(a, rw.next());
+ assertCommit(b, rw.next());
+ assertCommit(c1, rw.next());
+ assertCommit(c2, rw.next());
+ assertCommit(d, rw.next());
+ assertNull(rw.next());
+ }
+
+ @Test
+ public void testSort_TOPO_NON_INTERMIX_REVERSE_MultipleLinesOfHistory()
+ throws Exception {
+ final RevCommit a1 = commit();
+ final RevCommit b1 = commit(a1);
+ final RevCommit a2 = commit(a1, b1);
+ final RevCommit b2 = commit(b1);
+ final RevCommit b3 = commit(b1);
+ final RevCommit a3 = commit(a2, b2);
+ final RevCommit a4 = commit(a3, b3);
+
+ rw.sort(RevSort.TOPO_KEEP_BRANCH_TOGETHER);
+ rw.sort(RevSort.REVERSE, true);
+ markStart(a4);
+ assertCommit(a1, rw.next());
+ assertCommit(b1, rw.next());
+ assertCommit(a2, rw.next());
+ assertCommit(b2, rw.next());
+ assertCommit(a3, rw.next());
+ assertCommit(b3, rw.next());
+ assertCommit(a4, rw.next());
+ assertNull(rw.next());
+ }
+
+ @Test
+ public void testSort_TOPO_NON_INTERMIX_ParentOfMultipleStartChildren()
+ throws Exception {
+ final RevCommit a = commit();
+ final RevCommit b = commit(a);
+ final RevCommit c = commit(a);
+ final RevCommit d1 = commit(a);
+ final RevCommit d2 = commit(d1);
+ final RevCommit e = commit(a);
+
+ rw.sort(RevSort.TOPO_KEEP_BRANCH_TOGETHER);
+ markStart(b);
+ markStart(c);
+ markStart(d2);
+ markStart(e);
+ assertCommit(e, rw.next());
+ assertCommit(d2, rw.next());
+ assertCommit(d1, rw.next());
+ assertCommit(c, rw.next());
+ assertCommit(b, rw.next());
+ assertCommit(a, rw.next());
+ assertNull(rw.next());
+ }
+
+ @Test
+ public void testSort_TOPO_NON_INTERMIX_Uninteresting() throws Exception {
+ final RevCommit a1 = commit();
+ final RevCommit a2 = commit(a1);
+ final RevCommit a3 = commit(a2);
+ final RevCommit b = commit(a1);
+ final RevCommit a4 = commit(a3, b);
+
+ rw.sort(RevSort.TOPO_KEEP_BRANCH_TOGETHER);
+ markStart(a4);
+ markUninteresting(a2);
+ assertCommit(a4, rw.next());
+ assertCommit(b, rw.next());
+ assertCommit(a3, rw.next());
+ assertNull(rw.next());
+ }
+
+ @Test
+ public void testSort_TOPO_NON_INTERMIX_and_TOPO_throws() throws Exception {
+ final RevCommit a = commit();
+
+ rw.sort(RevSort.TOPO_KEEP_BRANCH_TOGETHER);
+ rw.sort(RevSort.TOPO, true);
+ markStart(a);
+ try {
+ rw.next();
+ fail("did not throw IllegalStateException");
+ } catch (IllegalStateException e) {
+ assertEquals(
+ JGitText.get().cannotCombineTopoSortWithTopoKeepBranchTogetherSort,
+ e.getMessage());
+ }
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
index 6bf8b4c7d8..d403624b71 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
@@ -953,6 +953,17 @@ public class URIishTest {
assertEquals(-1, u.getPort());
assertNull(u.getUser());
assertEquals("b.txt", u.getHumanishName());
+
+ u = new URIish("file:/a/test.bundle");
+ assertEquals("file", u.getScheme());
+ assertFalse(u.isRemote());
+ assertNull(u.getHost());
+ assertNull(u.getPass());
+ assertEquals("/a/test.bundle", u.getRawPath());
+ assertEquals("/a/test.bundle", u.getPath());
+ assertEquals(-1, u.getPort());
+ assertNull(u.getUser());
+ assertEquals("test", u.getHumanishName());
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
index ea86563da8..d58e576984 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
@@ -44,6 +44,7 @@ import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.Sets;
import org.eclipse.jgit.lib.TextProgressMonitor;
@@ -2238,4 +2239,81 @@ public class UploadPackTest {
}
}
+ @Test
+ public void testSafeToClearRefsInFetchV0() throws Exception {
+ server =
+ new RefCallsCountingRepository(
+ new DfsRepositoryDescription("server"));
+ remote = new TestRepository<>(server);
+ RevCommit one = remote.commit().message("1").create();
+ remote.update("one", one);
+ testProtocol = new TestProtocol<>((Object req, Repository db) -> {
+ UploadPack up = new UploadPack(db);
+ return up;
+ }, null);
+ uri = testProtocol.register(ctx, server);
+ try (Transport tn = testProtocol.open(uri, client, "server")) {
+ tn.fetch(NullProgressMonitor.INSTANCE,
+ Collections.singletonList(new RefSpec(one.name())));
+ }
+ assertTrue(client.getObjectDatabase().has(one.toObjectId()));
+ assertEquals(1, ((RefCallsCountingRepository)server).numRefCalls());
+ }
+
+ @Test
+ public void testSafeToClearRefsInFetchV2() throws Exception {
+ server =
+ new RefCallsCountingRepository(
+ new DfsRepositoryDescription("server"));
+ remote = new TestRepository<>(server);
+ RevCommit one = remote.commit().message("1").create();
+ RevCommit two = remote.commit().message("2").create();
+ remote.update("one", one);
+ remote.update("two", two);
+ server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
+ ByteArrayInputStream recvStream = uploadPackV2(
+ "command=fetch\n",
+ PacketLineIn.delimiter(),
+ "want-ref refs/heads/one\n",
+ "want-ref refs/heads/two\n",
+ "done\n",
+ PacketLineIn.end());
+ PacketLineIn pckIn = new PacketLineIn(recvStream);
+ assertThat(pckIn.readString(), is("wanted-refs"));
+ assertThat(
+ Arrays.asList(pckIn.readString(), pckIn.readString()),
+ hasItems(
+ one.toObjectId().getName() + " refs/heads/one",
+ two.toObjectId().getName() + " refs/heads/two"));
+ assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
+ assertThat(pckIn.readString(), is("packfile"));
+ parsePack(recvStream);
+ assertTrue(client.getObjectDatabase().has(one.toObjectId()));
+ assertEquals(1, ((RefCallsCountingRepository)server).numRefCalls());
+ }
+
+ private class RefCallsCountingRepository extends InMemoryRepository {
+ private final InMemoryRepository.MemRefDatabase refdb;
+ private int numRefCalls;
+
+ public RefCallsCountingRepository(DfsRepositoryDescription repoDesc) {
+ super(repoDesc);
+ refdb = new InMemoryRepository.MemRefDatabase() {
+ @Override
+ public List<Ref> getRefs() throws IOException {
+ numRefCalls++;
+ return super.getRefs();
+ }
+ };
+ }
+
+ public int numRefCalls() {
+ return numRefCalls;
+ }
+
+ @Override
+ public RefDatabase getRefDatabase() {
+ return refdb;
+ }
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java
index f9ec5d8d6f..2b1fb2ef04 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java
@@ -79,6 +79,15 @@ public class FileUtilsTest {
}
@Test
+ public void testDeleteReadOnlyFile() throws IOException {
+ File f = new File(trash, "f");
+ FileUtils.createNewFile(f);
+ assertTrue(f.setReadOnly());
+ FileUtils.delete(f);
+ assertFalse(f.exists());
+ }
+
+ @Test
public void testDeleteRecursive() throws IOException {
File f1 = new File(trash, "test/test/a");
FileUtils.mkdirs(f1.getParentFile());
@@ -339,6 +348,34 @@ public class FileUtilsTest {
}
@Test
+ public void testDeleteNonRecursiveTreeNotOk() throws IOException {
+ File t = new File(trash, "t");
+ FileUtils.mkdir(t);
+ File f = new File(t, "f");
+ FileUtils.createNewFile(f);
+ try {
+ FileUtils.delete(t, FileUtils.EMPTY_DIRECTORIES_ONLY);
+ fail("expected failure to delete f");
+ } catch (IOException e) {
+ assertTrue(e.getMessage().endsWith(t.getAbsolutePath()));
+ }
+ assertTrue(f.exists());
+ assertTrue(t.exists());
+ }
+
+ @Test
+ public void testDeleteNonRecursiveTreeIgnoreError() throws IOException {
+ File t = new File(trash, "t");
+ FileUtils.mkdir(t);
+ File f = new File(t, "f");
+ FileUtils.createNewFile(f);
+ FileUtils.delete(t,
+ FileUtils.EMPTY_DIRECTORIES_ONLY | FileUtils.IGNORE_ERRORS);
+ assertTrue(f.exists());
+ assertTrue(t.exists());
+ }
+
+ @Test
public void testRenameOverNonExistingFile() throws IOException {
File d = new File(trash, "d");
FileUtils.mkdirs(d);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
index 254878ae0b..33ed360efd 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
@@ -259,6 +259,38 @@ public class HookTest extends RepositoryTestCase {
}
@Test
+ public void testHookPathWithBlank() throws Exception {
+ assumeSupportedPlatform();
+
+ File file = writeHookFile("../../a directory/" + PreCommitHook.NAME,
+ "#!/bin/sh\necho \"test $1 $2\"\nread INPUT\necho $INPUT\n"
+ + "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\"");
+ StoredConfig cfg = db.getConfig();
+ cfg.load();
+ cfg.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_HOOKS_PATH,
+ file.getParentFile().getAbsolutePath());
+ cfg.save();
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayOutputStream err = new ByteArrayOutputStream()) {
+ ProcessResult res = FS.DETECTED.runHookIfPresent(db,
+ PreCommitHook.NAME, new String[] { "arg1", "arg2" },
+ new PrintStream(out), new PrintStream(err), "stdin");
+
+ assertEquals("unexpected hook output",
+ "test arg1 arg2\nstdin\n"
+ + db.getDirectory().getAbsolutePath() + '\n'
+ + db.getWorkTree().getAbsolutePath() + '\n',
+ out.toString("UTF-8"));
+ assertEquals("unexpected output on stderr stream", "stderr\n",
+ err.toString("UTF-8"));
+ assertEquals("unexpected exit code", 0, res.getExitCode());
+ assertEquals("unexpected process status", ProcessResult.Status.OK,
+ res.getStatus());
+ }
+ }
+
+ @Test
public void testFailedPreCommitHookBlockCommit() throws Exception {
assumeSupportedPlatform();