Merge "Fix off-by-one error in Strings.count()"
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target
index b2099ae..8051080 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.5" sequenceNumber="1496008880">
+<target name="jgit-4.5" sequenceNumber="1502749391">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.jetty.client" version="9.4.5.v20170502"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
index e4baa5d..b6bbcda 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.6" sequenceNumber="1496008884">
+<target name="jgit-4.6" sequenceNumber="1502749371">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.jetty.client" version="9.4.5.v20170502"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
index 9a15741..6071c8f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.7" sequenceNumber="1496008862">
+<target name="jgit-4.7" sequenceNumber="1502749365">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.jetty.client" version="9.4.5.v20170502"/>
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 8666c34..7c9816c 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
@@ -257,7 +257,7 @@
usage_ReadDirCache= Read the DirCache 100 times
usage_RebuildCommitGraph=Recreate a repository from another one's commit graph
usage_RebuildRefTree=Copy references into a RefTree
-usage_RebuildRefTreeEnable=set extensions.refsStorage = reftree
+usage_RebuildRefTreeEnable=set extensions.refStorage = reftree
usage_Remote=Manage set of tracked repositories
usage_RepositoryToReadFrom=Repository to read from
usage_RepositoryToReceiveInto=Repository to receive into
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildRefTree.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildRefTree.java
index 57345e2..8cde513 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildRefTree.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildRefTree.java
@@ -133,7 +133,7 @@
if (enable && !(db.getRefDatabase() instanceof RefTreeDatabase)) {
StoredConfig cfg = db.getConfig();
cfg.setInt("core", null, "repositoryformatversion", 1); //$NON-NLS-1$ //$NON-NLS-2$
- cfg.setString("extensions", null, "refsStorage", "reftree"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ cfg.setString("extensions", null, "refStorage", "reftree"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
cfg.save();
errw.println("Enabled reftree."); //$NON-NLS-1$
errw.flush();
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index dad1e3c..084014c 100644
--- a/org.eclipse.jgit.test/pom.xml
+++ b/org.eclipse.jgit.test/pom.xml
@@ -66,7 +66,6 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
- <scope>test</scope>
</dependency>
<!-- Optional security provider for encryption tests. -->
diff --git a/org.eclipse.jgit.test/src/org/eclipse/jgit/events/ChangeRecorder.java b/org.eclipse.jgit.test/src/org/eclipse/jgit/events/ChangeRecorder.java
new file mode 100644
index 0000000..c5582a8
--- /dev/null
+++ b/org.eclipse.jgit.test/src/org/eclipse/jgit/events/ChangeRecorder.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017, Thomas Wolf <thomas.wolf@paranor.ch>
+ * 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.events;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A {@link WorkingTreeModifiedListener} that can be used in tests to check
+ * expected events.
+ */
+public class ChangeRecorder implements WorkingTreeModifiedListener {
+
+ public static final String[] EMPTY = new String[0];
+
+ private Set<String> modified = new HashSet<>();
+
+ private Set<String> deleted = new HashSet<>();
+
+ private int eventCount;
+
+ @Override
+ public void onWorkingTreeModified(WorkingTreeModifiedEvent event) {
+ eventCount++;
+ modified.removeAll(event.getDeleted());
+ deleted.removeAll(event.getModified());
+ modified.addAll(event.getModified());
+ deleted.addAll(event.getDeleted());
+ }
+
+ private String[] getModified() {
+ return modified.toArray(new String[modified.size()]);
+ }
+
+ private String[] getDeleted() {
+ return deleted.toArray(new String[deleted.size()]);
+ }
+
+ private void reset() {
+ eventCount = 0;
+ modified.clear();
+ deleted.clear();
+ }
+
+ public void assertNoEvent() {
+ assertEquals("Unexpected WorkingTreeModifiedEvent ", 0, eventCount);
+ }
+
+ public void assertEvent(String[] expectedModified,
+ String[] expectedDeleted) {
+ String[] actuallyModified = getModified();
+ String[] actuallyDeleted = getDeleted();
+ Arrays.sort(actuallyModified);
+ Arrays.sort(expectedModified);
+ Arrays.sort(actuallyDeleted);
+ Arrays.sort(expectedDeleted);
+ assertArrayEquals("Unexpected modifications reported", expectedModified,
+ actuallyModified);
+ assertArrayEquals("Unexpected deletions reported", expectedDeleted,
+ actuallyDeleted);
+ reset();
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
index ed3907e..aafda01 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
@@ -303,6 +303,21 @@
}
@Test
+ public void testAttributesConflictingMatch() throws Exception {
+ writeTrashFile(".gitattributes", "foo/** crlf=input\n*.jar binary");
+ writeTrashFile("foo/bar.jar", "\r\n");
+ // We end up with attributes [binary -diff -merge -text crlf=input].
+ // crlf should have no effect when -text is present.
+ try (Git git = new Git(db)) {
+ git.add().addFilepattern(".").call();
+ assertEquals(
+ "[.gitattributes, mode:100644, content:foo/** crlf=input\n*.jar binary]"
+ + "[foo/bar.jar, mode:100644, content:\r\n]",
+ indexState(CONTENT));
+ }
+ }
+
+ @Test
public void testCleanFilterEnvironment()
throws IOException, GitAPIException {
writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
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 f2e4d5b..ad3ab7f 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
@@ -55,12 +55,15 @@
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.api.errors.StashApplyFailureException;
+import org.eclipse.jgit.events.ChangeRecorder;
+import org.eclipse.jgit.events.ListenerHandle;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.util.FileUtils;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -77,15 +80,31 @@
private File committedFile;
+ private ChangeRecorder recorder;
+
+ private ListenerHandle handle;
+
@Override
@Before
public void setUp() throws Exception {
super.setUp();
git = Git.wrap(db);
+ recorder = new ChangeRecorder();
+ handle = db.getListenerList().addWorkingTreeModifiedListener(recorder);
committedFile = writeTrashFile(PATH, "content");
git.add().addFilepattern(PATH).call();
head = git.commit().setMessage("add file").call();
assertNotNull(head);
+ recorder.assertNoEvent();
+ }
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ if (handle != null) {
+ handle.remove();
+ }
+ super.tearDown();
}
@Test
@@ -95,10 +114,12 @@
RevCommit stashed = git.stashCreate().call();
assertNotNull(stashed);
assertEquals("content", read(committedFile));
+ recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
ObjectId unstashed = git.stashApply().call();
assertEquals(stashed, unstashed);
assertFalse(committedFile.exists());
+ recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { PATH });
Status status = git.status().call();
assertTrue(status.getAdded().isEmpty());
@@ -121,11 +142,13 @@
RevCommit stashed = git.stashCreate().call();
assertNotNull(stashed);
assertFalse(addedFile.exists());
+ recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { addedPath });
ObjectId unstashed = git.stashApply().call();
assertEquals(stashed, unstashed);
assertTrue(addedFile.exists());
assertEquals("content2", read(addedFile));
+ recorder.assertEvent(new String[] { addedPath }, ChangeRecorder.EMPTY);
Status status = git.status().call();
assertTrue(status.getChanged().isEmpty());
@@ -142,14 +165,17 @@
@Test
public void indexDelete() throws Exception {
git.rm().addFilepattern("file.txt").call();
+ recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { "file.txt" });
RevCommit stashed = git.stashCreate().call();
assertNotNull(stashed);
assertEquals("content", read(committedFile));
+ recorder.assertEvent(new String[] { "file.txt" }, ChangeRecorder.EMPTY);
ObjectId unstashed = git.stashApply().call();
assertEquals(stashed, unstashed);
assertFalse(committedFile.exists());
+ recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { "file.txt" });
Status status = git.status().call();
assertTrue(status.getAdded().isEmpty());
@@ -170,10 +196,12 @@
RevCommit stashed = git.stashCreate().call();
assertNotNull(stashed);
assertEquals("content", read(committedFile));
+ recorder.assertEvent(new String[] { "file.txt" }, ChangeRecorder.EMPTY);
ObjectId unstashed = git.stashApply().call();
assertEquals(stashed, unstashed);
assertEquals("content2", read(committedFile));
+ recorder.assertEvent(new String[] { "file.txt" }, ChangeRecorder.EMPTY);
Status status = git.status().call();
assertTrue(status.getAdded().isEmpty());
@@ -193,16 +221,21 @@
File subfolderFile = writeTrashFile(path, "content");
git.add().addFilepattern(path).call();
head = git.commit().setMessage("add file").call();
+ recorder.assertNoEvent();
writeTrashFile(path, "content2");
RevCommit stashed = git.stashCreate().call();
assertNotNull(stashed);
assertEquals("content", read(subfolderFile));
+ recorder.assertEvent(new String[] { "d1/d2/f.txt" },
+ ChangeRecorder.EMPTY);
ObjectId unstashed = git.stashApply().call();
assertEquals(stashed, unstashed);
assertEquals("content2", read(subfolderFile));
+ recorder.assertEvent(new String[] { "d1/d2/f.txt", "d1/d2", "d1" },
+ ChangeRecorder.EMPTY);
Status status = git.status().call();
assertTrue(status.getAdded().isEmpty());
@@ -225,10 +258,12 @@
RevCommit stashed = git.stashCreate().call();
assertNotNull(stashed);
assertEquals("content", read(committedFile));
+ recorder.assertEvent(new String[] { "file.txt" }, ChangeRecorder.EMPTY);
ObjectId unstashed = git.stashApply().call();
assertEquals(stashed, unstashed);
assertEquals("content3", read(committedFile));
+ recorder.assertEvent(new String[] { "file.txt" }, ChangeRecorder.EMPTY);
Status status = git.status().call();
assertTrue(status.getAdded().isEmpty());
@@ -252,10 +287,12 @@
RevCommit stashed = git.stashCreate().call();
assertNotNull(stashed);
assertEquals("content", read(committedFile));
+ recorder.assertEvent(new String[] { "file.txt" }, ChangeRecorder.EMPTY);
ObjectId unstashed = git.stashApply().call();
assertEquals(stashed, unstashed);
assertEquals("content2", read(committedFile));
+ recorder.assertEvent(new String[] { "file.txt" }, ChangeRecorder.EMPTY);
Status status = git.status().call();
assertTrue(status.getAdded().isEmpty());
@@ -281,10 +318,12 @@
RevCommit stashed = git.stashCreate().call();
assertNotNull(stashed);
assertFalse(added.exists());
+ recorder.assertNoEvent();
ObjectId unstashed = git.stashApply().call();
assertEquals(stashed, unstashed);
assertEquals("content2", read(added));
+ recorder.assertEvent(new String[] { path }, ChangeRecorder.EMPTY);
Status status = git.status().call();
assertTrue(status.getChanged().isEmpty());
@@ -308,10 +347,12 @@
RevCommit stashed = git.stashCreate().call();
assertNotNull(stashed);
assertEquals("content", read(committedFile));
+ recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
ObjectId unstashed = git.stashApply().call();
assertEquals(stashed, unstashed);
assertFalse(committedFile.exists());
+ recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { PATH });
Status status = git.status().call();
assertTrue(status.getAdded().isEmpty());
@@ -337,9 +378,13 @@
assertNotNull(stashed);
assertTrue(committedFile.exists());
assertFalse(addedFile.exists());
+ recorder.assertEvent(new String[] { PATH },
+ new String[] { "file2.txt" });
ObjectId unstashed = git.stashApply().call();
assertEquals(stashed, unstashed);
+ recorder.assertEvent(new String[] { "file2.txt" },
+ new String[] { PATH });
Status status = git.status().call();
assertTrue(status.getChanged().isEmpty());
@@ -362,6 +407,7 @@
assertNotNull(stashed);
assertEquals("content", read(committedFile));
assertTrue(git.status().call().isClean());
+ recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
writeTrashFile(PATH, "content3");
@@ -372,6 +418,7 @@
// expected
}
assertEquals("content3", read(PATH));
+ recorder.assertNoEvent();
}
@Test
@@ -391,10 +438,12 @@
assertEquals("content\nhead change\nmore content\n",
read(committedFile));
assertTrue(git.status().call().isClean());
+ recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
writeTrashFile(PATH, "content\nmore content\ncommitted change\n");
git.add().addFilepattern(PATH).call();
git.commit().setMessage("committed change").call();
+ recorder.assertNoEvent();
try {
git.stashApply().call();
@@ -402,6 +451,7 @@
} catch (StashApplyFailureException e) {
// expected
}
+ recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
Status status = new StatusCommand(db).call();
assertEquals(1, status.getConflicting().size());
assertEquals(
@@ -426,12 +476,15 @@
writeTrashFile(PATH, "master content");
git.add().addFilepattern(PATH).call();
git.commit().setMessage("even content").call();
+ recorder.assertNoEvent();
git.checkout().setName(otherBranch).call();
+ recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
writeTrashFile(PATH, "otherBranch content");
git.add().addFilepattern(PATH).call();
git.commit().setMessage("even more content").call();
+ recorder.assertNoEvent();
writeTrashFile(path2, "content\nstashed change\nmore content\n");
@@ -442,12 +495,15 @@
assertEquals("otherBranch content",
read(committedFile));
assertTrue(git.status().call().isClean());
+ recorder.assertEvent(new String[] { path2 }, ChangeRecorder.EMPTY);
git.checkout().setName("master").call();
+ recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
git.stashApply().call();
assertEquals("content\nstashed change\nmore content\n", read(file2));
assertEquals("master content",
read(committedFile));
+ recorder.assertEvent(new String[] { path2 }, ChangeRecorder.EMPTY);
}
@Test
@@ -467,12 +523,15 @@
writeTrashFile(PATH, "master content");
git.add().addFilepattern(PATH).call();
git.commit().setMessage("even content").call();
+ recorder.assertNoEvent();
git.checkout().setName(otherBranch).call();
+ recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
writeTrashFile(PATH, "otherBranch content");
git.add().addFilepattern(PATH).call();
git.commit().setMessage("even more content").call();
+ recorder.assertNoEvent();
writeTrashFile(path2,
"content\nstashed change in index\nmore content\n");
@@ -485,8 +544,10 @@
assertEquals("content\nmore content\n", read(file2));
assertEquals("otherBranch content", read(committedFile));
assertTrue(git.status().call().isClean());
+ recorder.assertEvent(new String[] { path2 }, ChangeRecorder.EMPTY);
git.checkout().setName("master").call();
+ recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
git.stashApply().call();
assertEquals("content\nstashed change\nmore content\n", read(file2));
assertEquals(
@@ -494,6 +555,7 @@
+ "[file2.txt, mode:100644, content:content\nstashed change in index\nmore content\n]",
indexState(CONTENT));
assertEquals("master content", read(committedFile));
+ recorder.assertEvent(new String[] { path2 }, ChangeRecorder.EMPTY);
}
@Test
@@ -501,6 +563,7 @@
writeTrashFile(PATH, "content\nmore content\n");
git.add().addFilepattern(PATH).call();
git.commit().setMessage("more content").call();
+ recorder.assertNoEvent();
writeTrashFile(PATH, "content\nstashed change\nmore content\n");
@@ -508,15 +571,18 @@
assertNotNull(stashed);
assertEquals("content\nmore content\n", read(committedFile));
assertTrue(git.status().call().isClean());
+ recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
writeTrashFile(PATH, "content\nmore content\ncommitted change\n");
git.add().addFilepattern(PATH).call();
git.commit().setMessage("committed change").call();
+ recorder.assertNoEvent();
git.stashApply().call();
assertEquals(
"content\nstashed change\nmore content\ncommitted change\n",
read(committedFile));
+ recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
}
@Test
@@ -527,6 +593,7 @@
assertNotNull(stashed);
assertEquals("content", read(committedFile));
assertTrue(git.status().call().isClean());
+ recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
writeTrashFile(PATH, "content3");
git.add().addFilepattern(PATH).call();
@@ -538,6 +605,7 @@
} catch (StashApplyFailureException e) {
// expected
}
+ recorder.assertNoEvent();
assertEquals("content2", read(PATH));
}
@@ -549,6 +617,7 @@
assertNotNull(stashed);
assertEquals("content", read(committedFile));
assertTrue(git.status().call().isClean());
+ recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
String path2 = "file2.txt";
writeTrashFile(path2, "content3");
@@ -557,6 +626,7 @@
ObjectId unstashed = git.stashApply().call();
assertEquals(stashed, unstashed);
+ recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
Status status = git.status().call();
assertTrue(status.getAdded().isEmpty());
@@ -583,12 +653,15 @@
RevCommit stashed = git.stashCreate().call();
assertNotNull(stashed);
assertTrue(git.status().call().isClean());
+ recorder.assertEvent(ChangeRecorder.EMPTY,
+ new String[] { subdir, path });
git.branchCreate().setName(otherBranch).call();
git.checkout().setName(otherBranch).call();
ObjectId unstashed = git.stashApply().call();
assertEquals(stashed, unstashed);
+ recorder.assertEvent(new String[] { path }, ChangeRecorder.EMPTY);
Status status = git.status().call();
assertTrue(status.getChanged().isEmpty());
@@ -643,12 +716,15 @@
git.commit().setMessage("x").call();
file.delete();
git.rm().addFilepattern("file").call();
+ recorder.assertNoEvent();
git.stashCreate().call();
+ recorder.assertEvent(new String[] { "file" }, ChangeRecorder.EMPTY);
file.delete();
git.stashApply().setStashRef("stash@{0}").call();
assertFalse(file.exists());
+ recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { "file" });
}
@Test
@@ -660,9 +736,11 @@
git.add().addFilepattern(PATH).call();
git.stashCreate().call();
assertTrue(untrackedFile.exists());
+ recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
git.stashApply().setStashRef("stash@{0}").call();
assertTrue(untrackedFile.exists());
+ recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
Status status = git.status().call();
assertEquals(1, status.getUntracked().size());
@@ -684,11 +762,14 @@
.call();
assertNotNull(stashedCommit);
assertFalse(untrackedFile.exists());
+ recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { path });
+
deleteTrashFile("a/b"); // checkout should create parent dirs
git.stashApply().setStashRef("stash@{0}").call();
assertTrue(untrackedFile.exists());
assertEquals("content", read(path));
+ recorder.assertEvent(new String[] { path }, ChangeRecorder.EMPTY);
Status status = git.status().call();
assertEquals(1, status.getUntracked().size());
@@ -706,6 +787,7 @@
String path = "untracked.txt";
writeTrashFile(path, "untracked");
git.stashCreate().setIncludeUntracked(true).call();
+ recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { path });
writeTrashFile(path, "committed");
head = git.commit().setMessage("add file").call();
@@ -719,6 +801,7 @@
assertEquals(e.getMessage(), JGitText.get().stashApplyConflict);
}
assertEquals("committed", read(path));
+ recorder.assertNoEvent();
}
@Test
@@ -727,6 +810,7 @@
String path = "untracked.txt";
writeTrashFile(path, "untracked");
git.stashCreate().setIncludeUntracked(true).call();
+ recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { path });
writeTrashFile(path, "working-directory");
try {
@@ -736,6 +820,7 @@
assertEquals(e.getMessage(), JGitText.get().stashApplyConflict);
}
assertEquals("working-directory", read(path));
+ recorder.assertNoEvent();
}
@Test
@@ -747,11 +832,13 @@
assertTrue(PATH + " should exist", check(PATH));
assertEquals(PATH + " should have been reset", "content", read(PATH));
assertFalse(path + " should not exist", check(path));
+ recorder.assertEvent(new String[] { PATH }, new String[] { path });
git.stashApply().setStashRef("stash@{0}").call();
assertTrue(PATH + " should exist", check(PATH));
assertEquals(PATH + " should have new content", "changed", read(PATH));
assertTrue(path + " should exist", check(path));
assertEquals(path + " should have new content", "untracked",
read(path));
+ recorder.assertEvent(new String[] { PATH, path }, ChangeRecorder.EMPTY);
}
}
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
index ebb5a4f..643bb49 100644
--- 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
@@ -45,8 +45,12 @@
import static java.lang.Integer.valueOf;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.Callable;
import java.util.concurrent.CyclicBarrier;
@@ -56,8 +60,14 @@
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
+import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.EmptyProgressMonitor;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Sets;
import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Test;
public class GcConcurrentTest extends GcTestCase {
@@ -118,4 +128,97 @@
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
}
}
+
+ @Test
+ public void repackAndGetStats() throws Exception {
+ TestRepository<FileRepository>.BranchBuilder test = tr.branch("test");
+ test.commit().add("a", "a").create();
+ GC gc1 = new GC(tr.getRepository());
+ gc1.setPackExpireAgeMillis(0);
+ gc1.gc();
+ test.commit().add("b", "b").create();
+
+ // Create a new Repository instance and trigger a gc
+ // from that instance. Reusing the existing repo instance
+ // tr.getRepository() would not show the problem.
+ FileRepository r2 = new FileRepository(
+ tr.getRepository().getDirectory());
+ GC gc2 = new GC(r2);
+ gc2.setPackExpireAgeMillis(0);
+ gc2.gc();
+
+ new GC(tr.getRepository()).getStatistics();
+ }
+
+ @Test
+ public void repackAndUploadPack() throws Exception {
+ TestRepository<FileRepository>.BranchBuilder test = tr.branch("test");
+ // RevCommit a = test.commit().add("a", "a").create();
+ test.commit().add("a", "a").create();
+
+ GC gc1 = new GC(tr.getRepository());
+ gc1.setPackExpireAgeMillis(0);
+ gc1.gc();
+
+ RevCommit b = test.commit().add("b", "b").create();
+
+ FileRepository r2 = new FileRepository(
+ tr.getRepository().getDirectory());
+ GC gc2 = new GC(r2);
+ gc2.setPackExpireAgeMillis(0);
+ gc2.gc();
+
+ // Simulate parts of an UploadPack. This is the situation on
+ // server side (e.g. gerrit) when when clients are
+ // cloning/fetching while the server side repo's
+ // are gc'ed by an external process (e.g. scheduled
+ // native git gc)
+ try (PackWriter pw = new PackWriter(tr.getRepository())) {
+ pw.setUseBitmaps(true);
+ pw.preparePack(NullProgressMonitor.INSTANCE, Sets.of(b),
+ Collections.<ObjectId> emptySet());
+ new GC(tr.getRepository()).getStatistics();
+ }
+ }
+
+ PackFile getSinglePack(FileRepository r) {
+ Collection<PackFile> packs = r.getObjectDatabase().getPacks();
+ assertEquals(1, packs.size());
+ return packs.iterator().next();
+ }
+
+ @Test
+ public void repackAndCheckBitmapUsage() throws Exception {
+ // create a test repository with one commit and pack all objects. After
+ // packing create loose objects to trigger creation of a new packfile on
+ // the next gc
+ TestRepository<FileRepository>.BranchBuilder test = tr.branch("test");
+ test.commit().add("a", "a").create();
+ FileRepository repository = tr.getRepository();
+ GC gc1 = new GC(repository);
+ gc1.setPackExpireAgeMillis(0);
+ gc1.gc();
+ String oldPackName = getSinglePack(repository).getPackName();
+ RevCommit b = test.commit().add("b", "b").create();
+
+ // start the garbage collection on a new repository instance,
+ FileRepository repository2 = new FileRepository(repository.getDirectory());
+ GC gc2 = new GC(repository2);
+ gc2.setPackExpireAgeMillis(0);
+ gc2.gc();
+ String newPackName = getSinglePack(repository2).getPackName();
+ // make sure gc() has caused creation of a new packfile
+ assertNotEquals(oldPackName, newPackName);
+
+ // Even when asking again for the set of packfiles outdated data
+ // will be returned. As long as the repository can work on cached data
+ // it will do so and not detect that a new packfile exists.
+ assertNotEquals(getSinglePack(repository).getPackName(), newPackName);
+
+ // Only when accessing object content it is required to rescan the pack
+ // directory and the new packfile will be detected.
+ repository.getObjectDatabase().open(b).getSize();
+ assertEquals(getSinglePack(repository).getPackName(), newPackName);
+ assertNotNull(getSinglePack(repository).getBitmapIndex());
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/LocalDiskRefTreeDatabaseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/LocalDiskRefTreeDatabaseTest.java
index 67a7819..d5a07e0 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/LocalDiskRefTreeDatabaseTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/LocalDiskRefTreeDatabaseTest.java
@@ -83,7 +83,7 @@
FileRepository init = createWorkRepository();
FileBasedConfig cfg = init.getConfig();
cfg.setInt("core", null, "repositoryformatversion", 1);
- cfg.setString("extensions", null, "refsStorage", "reftree");
+ cfg.setString("extensions", null, "refStorage", "reftree");
cfg.save();
repo = (FileRepository) new FileRepositoryBuilder()
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 f8c2d45..05573b9 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
@@ -72,6 +72,8 @@
import org.eclipse.jgit.errors.CheckoutConflictException;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.NoWorkTreeException;
+import org.eclipse.jgit.events.ChangeRecorder;
+import org.eclipse.jgit.events.ListenerHandle;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
@@ -141,14 +143,19 @@
@Test
public void testResetHard() throws IOException, NoFilepatternException,
GitAPIException {
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
writeTrashFile("f", "f()");
writeTrashFile("D/g", "g()");
git.add().addFilepattern(".").call();
git.commit().setMessage("inital").call();
assertIndex(mkmap("f", "f()", "D/g", "g()"));
-
+ recorder.assertNoEvent();
git.branchCreate().setName("topic").call();
+ recorder.assertNoEvent();
writeTrashFile("f", "f()\nmaster");
writeTrashFile("D/g", "g()\ng2()");
@@ -156,9 +163,12 @@
git.add().addFilepattern(".").call();
RevCommit master = git.commit().setMessage("master-1").call();
assertIndex(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h", "h()"));
+ recorder.assertNoEvent();
checkoutBranch("refs/heads/topic");
assertIndex(mkmap("f", "f()", "D/g", "g()"));
+ recorder.assertEvent(new String[] { "f", "D/g" },
+ new String[] { "E/h" });
writeTrashFile("f", "f()\nside");
assertTrue(new File(db.getWorkTree(), "D/g").delete());
@@ -167,26 +177,41 @@
git.add().addFilepattern(".").setUpdate(true).call();
RevCommit topic = git.commit().setMessage("topic-1").call();
assertIndex(mkmap("f", "f()\nside", "G/i", "i()"));
+ recorder.assertNoEvent();
writeTrashFile("untracked", "untracked");
resetHard(master);
assertIndex(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h", "h()"));
+ recorder.assertEvent(new String[] { "f", "D/g", "E/h" },
+ new String[] { "G", "G/i" });
+
resetHard(topic);
assertIndex(mkmap("f", "f()\nside", "G/i", "i()"));
assertWorkDir(mkmap("f", "f()\nside", "G/i", "i()", "untracked",
"untracked"));
+ recorder.assertEvent(new String[] { "f", "G/i" },
+ new String[] { "D", "D/g", "E", "E/h" });
assertEquals(MergeStatus.CONFLICTING, git.merge().include(master)
.call().getMergeStatus());
assertEquals(
"[D/g, mode:100644, stage:1][D/g, mode:100644, stage:3][E/h, mode:100644][G/i, mode:100644][f, mode:100644, stage:1][f, mode:100644, stage:2][f, mode:100644, stage:3]",
indexState(0));
+ recorder.assertEvent(new String[] { "f", "D/g", "E/h" },
+ ChangeRecorder.EMPTY);
resetHard(master);
assertIndex(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h", "h()"));
assertWorkDir(mkmap("f", "f()\nmaster", "D/g", "g()\ng2()", "E/h",
"h()", "untracked", "untracked"));
+ recorder.assertEvent(new String[] { "f", "D/g" },
+ new String[] { "G", "G/i" });
+
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
}
}
@@ -202,13 +227,18 @@
@Test
public void testResetHardFromIndexEntryWithoutFileToTreeWithoutFile()
throws Exception {
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
writeTrashFile("x", "x");
git.add().addFilepattern("x").call();
RevCommit id1 = git.commit().setMessage("c1").call();
writeTrashFile("f/g", "f/g");
git.rm().addFilepattern("x").call();
+ recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { "x" });
git.add().addFilepattern("f/g").call();
git.commit().setMessage("c2").call();
deleteTrashFile("f/g");
@@ -217,6 +247,11 @@
// The actual test
git.reset().setMode(ResetType.HARD).setRef(id1.getName()).call();
assertIndex(mkmap("x", "x"));
+ recorder.assertEvent(new String[] { "x" }, ChangeRecorder.EMPTY);
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
}
}
@@ -227,13 +262,22 @@
*/
@Test
public void testInitialCheckout() throws Exception {
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
TestRepository<Repository> db_t = new TestRepository<>(db);
BranchBuilder master = db_t.branch("master");
master.commit().add("f", "1").message("m0").create();
assertFalse(new File(db.getWorkTree(), "f").exists());
git.checkout().setName("master").call();
assertTrue(new File(db.getWorkTree(), "f").exists());
+ recorder.assertEvent(new String[] { "f" }, ChangeRecorder.EMPTY);
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
}
}
@@ -930,120 +974,154 @@
public void testCheckoutChangeLinkToEmptyDir() throws Exception {
Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
String fname = "was_file";
- Git git = Git.wrap(db);
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
+ try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
+ // Add a file
+ writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
- // Add a file
- writeTrashFile(fname, "a");
- git.add().addFilepattern(fname).call();
+ // Add a link to file
+ String linkName = "link";
+ File link = writeLink(linkName, fname).toFile();
+ git.add().addFilepattern(linkName).call();
+ git.commit().setMessage("Added file and link").call();
- // Add a link to file
- String linkName = "link";
- File link = writeLink(linkName, fname).toFile();
- git.add().addFilepattern(linkName).call();
- git.commit().setMessage("Added file and link").call();
+ assertWorkDir(mkmap(linkName, "a", fname, "a"));
- assertWorkDir(mkmap(linkName, "a", fname, "a"));
+ // replace link with empty directory
+ FileUtils.delete(link);
+ FileUtils.mkdir(link);
+ assertTrue("Link must be a directory now", link.isDirectory());
- // replace link with empty directory
- FileUtils.delete(link);
- FileUtils.mkdir(link);
- assertTrue("Link must be a directory now", link.isDirectory());
+ // modify file
+ writeTrashFile(fname, "b");
+ assertWorkDir(mkmap(fname, "b", linkName, "/"));
+ recorder.assertNoEvent();
- // modify file
- writeTrashFile(fname, "b");
- assertWorkDir(mkmap(fname, "b", linkName, "/"));
+ // revert both paths to HEAD state
+ git.checkout().setStartPoint(Constants.HEAD).addPath(fname)
+ .addPath(linkName).call();
- // revert both paths to HEAD state
- git.checkout().setStartPoint(Constants.HEAD)
- .addPath(fname).addPath(linkName).call();
+ assertWorkDir(mkmap(fname, "a", linkName, "a"));
+ recorder.assertEvent(new String[] { fname, linkName },
+ ChangeRecorder.EMPTY);
- assertWorkDir(mkmap(fname, "a", linkName, "a"));
-
- Status st = git.status().call();
- assertTrue(st.isClean());
+ Status st = git.status().call();
+ assertTrue(st.isClean());
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
+ }
}
@Test
public void testCheckoutChangeLinkToEmptyDirs() throws Exception {
Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
String fname = "was_file";
- Git git = Git.wrap(db);
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
+ try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
+ // Add a file
+ writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
- // Add a file
- writeTrashFile(fname, "a");
- git.add().addFilepattern(fname).call();
+ // Add a link to file
+ String linkName = "link";
+ File link = writeLink(linkName, fname).toFile();
+ git.add().addFilepattern(linkName).call();
+ git.commit().setMessage("Added file and link").call();
- // Add a link to file
- String linkName = "link";
- File link = writeLink(linkName, fname).toFile();
- git.add().addFilepattern(linkName).call();
- git.commit().setMessage("Added file and link").call();
+ assertWorkDir(mkmap(linkName, "a", fname, "a"));
- assertWorkDir(mkmap(linkName, "a", fname, "a"));
+ // replace link with directory containing only directories, no files
+ FileUtils.delete(link);
+ FileUtils.mkdirs(new File(link, "dummyDir"));
+ assertTrue("Link must be a directory now", link.isDirectory());
- // replace link with directory containing only directories, no files
- FileUtils.delete(link);
- FileUtils.mkdirs(new File(link, "dummyDir"));
- assertTrue("Link must be a directory now", link.isDirectory());
+ assertFalse("Must not delete non empty directory", link.delete());
- assertFalse("Must not delete non empty directory", link.delete());
+ // modify file
+ writeTrashFile(fname, "b");
+ assertWorkDir(mkmap(fname, "b", linkName + "/dummyDir", "/"));
+ recorder.assertNoEvent();
- // modify file
- writeTrashFile(fname, "b");
- assertWorkDir(mkmap(fname, "b", linkName + "/dummyDir", "/"));
+ // revert both paths to HEAD state
+ git.checkout().setStartPoint(Constants.HEAD).addPath(fname)
+ .addPath(linkName).call();
- // revert both paths to HEAD state
- git.checkout().setStartPoint(Constants.HEAD)
- .addPath(fname).addPath(linkName).call();
+ assertWorkDir(mkmap(fname, "a", linkName, "a"));
+ recorder.assertEvent(new String[] { fname, linkName },
+ ChangeRecorder.EMPTY);
- assertWorkDir(mkmap(fname, "a", linkName, "a"));
-
- Status st = git.status().call();
- assertTrue(st.isClean());
+ Status st = git.status().call();
+ assertTrue(st.isClean());
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
+ }
}
@Test
public void testCheckoutChangeLinkToNonEmptyDirs() throws Exception {
Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
String fname = "file";
- Git git = Git.wrap(db);
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
+ try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
+ // Add a file
+ writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
- // Add a file
- writeTrashFile(fname, "a");
- git.add().addFilepattern(fname).call();
+ // Add a link to file
+ String linkName = "link";
+ File link = writeLink(linkName, fname).toFile();
+ git.add().addFilepattern(linkName).call();
+ git.commit().setMessage("Added file and link").call();
- // Add a link to file
- String linkName = "link";
- File link = writeLink(linkName, fname).toFile();
- git.add().addFilepattern(linkName).call();
- git.commit().setMessage("Added file and link").call();
+ assertWorkDir(mkmap(linkName, "a", fname, "a"));
- assertWorkDir(mkmap(linkName, "a", fname, "a"));
+ // replace link with directory containing only directories, no files
+ FileUtils.delete(link);
- // replace link with directory containing only directories, no files
- FileUtils.delete(link);
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(linkName + "/dir1", "file1", "c");
- // create but do not add a file in the new directory to the index
- writeTrashFile(linkName + "/dir1", "file1", "c");
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(linkName + "/dir2", "file2", "d");
- // create but do not add a file in the new directory to the index
- writeTrashFile(linkName + "/dir2", "file2", "d");
+ assertTrue("File must be a directory now", link.isDirectory());
+ assertFalse("Must not delete non empty directory", link.delete());
- assertTrue("File must be a directory now", link.isDirectory());
- assertFalse("Must not delete non empty directory", link.delete());
+ // 2 extra files are created
+ assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
+ linkName + "/dir2/file2", "d"));
+ recorder.assertNoEvent();
- // 2 extra files are created
- assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
- linkName + "/dir2/file2", "d"));
+ // revert path to HEAD state
+ git.checkout().setStartPoint(Constants.HEAD).addPath(linkName)
+ .call();
- // revert path to HEAD state
- git.checkout().setStartPoint(Constants.HEAD).addPath(linkName).call();
+ // expect only the one added to the index
+ assertWorkDir(mkmap(linkName, "a", fname, "a"));
+ recorder.assertEvent(new String[] { linkName },
+ ChangeRecorder.EMPTY);
- // expect only the one added to the index
- assertWorkDir(mkmap(linkName, "a", fname, "a"));
-
- Status st = git.status().call();
- assertTrue(st.isClean());
+ Status st = git.status().call();
+ assertTrue(st.isClean());
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
+ }
}
@Test
@@ -1051,174 +1129,222 @@
throws Exception {
Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
String fname = "file";
- Git git = Git.wrap(db);
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
+ try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
+ // Add a file
+ writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
- // Add a file
- writeTrashFile(fname, "a");
- git.add().addFilepattern(fname).call();
+ // Add a link to file
+ String linkName = "link";
+ File link = writeLink(linkName, fname).toFile();
+ git.add().addFilepattern(linkName).call();
+ git.commit().setMessage("Added file and link").call();
- // Add a link to file
- String linkName = "link";
- File link = writeLink(linkName, fname).toFile();
- git.add().addFilepattern(linkName).call();
- git.commit().setMessage("Added file and link").call();
+ assertWorkDir(mkmap(linkName, "a", fname, "a"));
- assertWorkDir(mkmap(linkName, "a", fname, "a"));
+ // replace link with directory containing only directories, no files
+ FileUtils.delete(link);
- // replace link with directory containing only directories, no files
- FileUtils.delete(link);
+ // create and add a file in the new directory to the index
+ writeTrashFile(linkName + "/dir1", "file1", "c");
+ git.add().addFilepattern(linkName + "/dir1/file1").call();
- // create and add a file in the new directory to the index
- writeTrashFile(linkName + "/dir1", "file1", "c");
- git.add().addFilepattern(linkName + "/dir1/file1").call();
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(linkName + "/dir2", "file2", "d");
- // create but do not add a file in the new directory to the index
- writeTrashFile(linkName + "/dir2", "file2", "d");
+ assertTrue("File must be a directory now", link.isDirectory());
+ assertFalse("Must not delete non empty directory", link.delete());
- assertTrue("File must be a directory now", link.isDirectory());
- assertFalse("Must not delete non empty directory", link.delete());
+ // 2 extra files are created
+ assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
+ linkName + "/dir2/file2", "d"));
+ recorder.assertNoEvent();
- // 2 extra files are created
- assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
- linkName + "/dir2/file2", "d"));
+ // revert path to HEAD state
+ git.checkout().setStartPoint(Constants.HEAD).addPath(linkName)
+ .call();
- // revert path to HEAD state
- git.checkout().setStartPoint(Constants.HEAD).addPath(linkName).call();
+ // original file and link
+ assertWorkDir(mkmap(linkName, "a", fname, "a"));
+ recorder.assertEvent(new String[] { linkName },
+ ChangeRecorder.EMPTY);
- // original file and link
- assertWorkDir(mkmap(linkName, "a", fname, "a"));
-
- Status st = git.status().call();
- assertTrue(st.isClean());
+ Status st = git.status().call();
+ assertTrue(st.isClean());
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
+ }
}
@Test
public void testCheckoutChangeFileToEmptyDir() throws Exception {
String fname = "was_file";
- Git git = Git.wrap(db);
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
+ try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
+ // Add a file
+ File file = writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
+ git.commit().setMessage("Added file").call();
- // Add a file
- File file = writeTrashFile(fname, "a");
- git.add().addFilepattern(fname).call();
- git.commit().setMessage("Added file").call();
+ // replace file with empty directory
+ FileUtils.delete(file);
+ FileUtils.mkdir(file);
+ assertTrue("File must be a directory now", file.isDirectory());
+ assertWorkDir(mkmap(fname, "/"));
+ recorder.assertNoEvent();
- // replace file with empty directory
- FileUtils.delete(file);
- FileUtils.mkdir(file);
- assertTrue("File must be a directory now", file.isDirectory());
+ // revert path to HEAD state
+ git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
+ assertWorkDir(mkmap(fname, "a"));
+ recorder.assertEvent(new String[] { fname }, ChangeRecorder.EMPTY);
- assertWorkDir(mkmap(fname, "/"));
-
- // revert path to HEAD state
- git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
-
- assertWorkDir(mkmap(fname, "a"));
-
- Status st = git.status().call();
- assertTrue(st.isClean());
+ Status st = git.status().call();
+ assertTrue(st.isClean());
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
+ }
}
@Test
public void testCheckoutChangeFileToEmptyDirs() throws Exception {
String fname = "was_file";
- Git git = Git.wrap(db);
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
+ try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
+ // Add a file
+ File file = writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
+ git.commit().setMessage("Added file").call();
- // Add a file
- File file = writeTrashFile(fname, "a");
- git.add().addFilepattern(fname).call();
- git.commit().setMessage("Added file").call();
+ // replace file with directory containing only directories, no files
+ FileUtils.delete(file);
+ FileUtils.mkdirs(new File(file, "dummyDir"));
+ assertTrue("File must be a directory now", file.isDirectory());
+ assertFalse("Must not delete non empty directory", file.delete());
- // replace file with directory containing only directories, no files
- FileUtils.delete(file);
- FileUtils.mkdirs(new File(file, "dummyDir"));
- assertTrue("File must be a directory now", file.isDirectory());
- assertFalse("Must not delete non empty directory", file.delete());
+ assertWorkDir(mkmap(fname + "/dummyDir", "/"));
+ recorder.assertNoEvent();
- assertWorkDir(mkmap(fname + "/dummyDir", "/"));
+ // revert path to HEAD state
+ git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
+ assertWorkDir(mkmap(fname, "a"));
+ recorder.assertEvent(new String[] { fname }, ChangeRecorder.EMPTY);
- // revert path to HEAD state
- git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
-
- assertWorkDir(mkmap(fname, "a"));
-
- Status st = git.status().call();
- assertTrue(st.isClean());
+ Status st = git.status().call();
+ assertTrue(st.isClean());
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
+ }
}
@Test
public void testCheckoutChangeFileToNonEmptyDirs() throws Exception {
String fname = "was_file";
- Git git = Git.wrap(db);
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
+ try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
+ // Add a file
+ File file = writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
+ git.commit().setMessage("Added file").call();
- // Add a file
- File file = writeTrashFile(fname, "a");
- git.add().addFilepattern(fname).call();
- git.commit().setMessage("Added file").call();
+ assertWorkDir(mkmap(fname, "a"));
- assertWorkDir(mkmap(fname, "a"));
+ // replace file with directory containing only directories, no files
+ FileUtils.delete(file);
- // replace file with directory containing only directories, no files
- FileUtils.delete(file);
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(fname + "/dir1", "file1", "c");
- // create but do not add a file in the new directory to the index
- writeTrashFile(fname + "/dir1", "file1", "c");
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(fname + "/dir2", "file2", "d");
- // create but do not add a file in the new directory to the index
- writeTrashFile(fname + "/dir2", "file2", "d");
+ assertTrue("File must be a directory now", file.isDirectory());
+ assertFalse("Must not delete non empty directory", file.delete());
- assertTrue("File must be a directory now", file.isDirectory());
- assertFalse("Must not delete non empty directory", file.delete());
+ // 2 extra files are created
+ assertWorkDir(mkmap(fname + "/dir1/file1", "c",
+ fname + "/dir2/file2", "d"));
+ recorder.assertNoEvent();
- // 2 extra files are created
- assertWorkDir(
- mkmap(fname + "/dir1/file1", "c", fname + "/dir2/file2", "d"));
+ // revert path to HEAD state
+ git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
- // revert path to HEAD state
- git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
+ // expect only the one added to the index
+ assertWorkDir(mkmap(fname, "a"));
+ recorder.assertEvent(new String[] { fname }, ChangeRecorder.EMPTY);
- // expect only the one added to the index
- assertWorkDir(mkmap(fname, "a"));
-
- Status st = git.status().call();
- assertTrue(st.isClean());
+ Status st = git.status().call();
+ assertTrue(st.isClean());
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
+ }
}
@Test
public void testCheckoutChangeFileToNonEmptyDirsAndNewIndexEntry()
throws Exception {
String fname = "was_file";
- Git git = Git.wrap(db);
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
+ try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
+ // Add a file
+ File file = writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
+ git.commit().setMessage("Added file").call();
- // Add a file
- File file = writeTrashFile(fname, "a");
- git.add().addFilepattern(fname).call();
- git.commit().setMessage("Added file").call();
+ assertWorkDir(mkmap(fname, "a"));
- assertWorkDir(mkmap(fname, "a"));
+ // replace file with directory containing only directories, no files
+ FileUtils.delete(file);
- // replace file with directory containing only directories, no files
- FileUtils.delete(file);
+ // create and add a file in the new directory to the index
+ writeTrashFile(fname + "/dir", "file1", "c");
+ git.add().addFilepattern(fname + "/dir/file1").call();
- // create and add a file in the new directory to the index
- writeTrashFile(fname + "/dir", "file1", "c");
- git.add().addFilepattern(fname + "/dir/file1").call();
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(fname + "/dir", "file2", "d");
- // create but do not add a file in the new directory to the index
- writeTrashFile(fname + "/dir", "file2", "d");
+ assertTrue("File must be a directory now", file.isDirectory());
+ assertFalse("Must not delete non empty directory", file.delete());
- assertTrue("File must be a directory now", file.isDirectory());
- assertFalse("Must not delete non empty directory", file.delete());
+ // 2 extra files are created
+ assertWorkDir(mkmap(fname + "/dir/file1", "c", fname + "/dir/file2",
+ "d"));
+ recorder.assertNoEvent();
- // 2 extra files are created
- assertWorkDir(
- mkmap(fname + "/dir/file1", "c", fname + "/dir/file2", "d"));
-
- // revert path to HEAD state
- git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
- assertWorkDir(mkmap(fname, "a"));
-
- Status st = git.status().call();
- assertTrue(st.isClean());
+ // revert path to HEAD state
+ git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
+ assertWorkDir(mkmap(fname, "a"));
+ recorder.assertEvent(new String[] { fname }, ChangeRecorder.EMPTY);
+ Status st = git.status().call();
+ assertTrue(st.isClean());
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
+ }
}
@Test
@@ -1293,76 +1419,100 @@
public void testOverwriteUntrackedIgnoredFile() throws IOException,
GitAPIException {
String fname="file.txt";
- Git git = Git.wrap(db);
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
+ try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
+ // Add a file
+ writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
+ git.commit().setMessage("create file").call();
- // Add a file
- writeTrashFile(fname, "a");
- git.add().addFilepattern(fname).call();
- git.commit().setMessage("create file").call();
+ // Create branch
+ git.branchCreate().setName("side").call();
- // Create branch
- git.branchCreate().setName("side").call();
+ // Modify file
+ writeTrashFile(fname, "b");
+ git.add().addFilepattern(fname).call();
+ git.commit().setMessage("modify file").call();
+ recorder.assertNoEvent();
- // Modify file
- writeTrashFile(fname, "b");
- git.add().addFilepattern(fname).call();
- git.commit().setMessage("modify file").call();
+ // Switch branches
+ git.checkout().setName("side").call();
+ recorder.assertEvent(new String[] { fname }, ChangeRecorder.EMPTY);
+ git.rm().addFilepattern(fname).call();
+ recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { fname });
+ writeTrashFile(".gitignore", fname);
+ git.add().addFilepattern(".gitignore").call();
+ git.commit().setMessage("delete and ignore file").call();
- // Switch branches
- git.checkout().setName("side").call();
- git.rm().addFilepattern(fname).call();
- writeTrashFile(".gitignore", fname);
- git.add().addFilepattern(".gitignore").call();
- git.commit().setMessage("delete and ignore file").call();
-
- writeTrashFile(fname, "Something different");
- git.checkout().setName("master").call();
- assertWorkDir(mkmap(fname, "b"));
- assertTrue(git.status().call().isClean());
+ writeTrashFile(fname, "Something different");
+ recorder.assertNoEvent();
+ git.checkout().setName("master").call();
+ assertWorkDir(mkmap(fname, "b"));
+ recorder.assertEvent(new String[] { fname },
+ new String[] { ".gitignore" });
+ assertTrue(git.status().call().isClean());
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
+ }
}
@Test
public void testOverwriteUntrackedFileModeChange()
throws IOException, GitAPIException {
String fname = "file.txt";
- Git git = Git.wrap(db);
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
+ try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
+ // Add a file
+ File file = writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
+ git.commit().setMessage("create file").call();
+ assertWorkDir(mkmap(fname, "a"));
- // Add a file
- File file = writeTrashFile(fname, "a");
- git.add().addFilepattern(fname).call();
- git.commit().setMessage("create file").call();
- assertWorkDir(mkmap(fname, "a"));
+ // Create branch
+ git.branchCreate().setName("side").call();
- // Create branch
- git.branchCreate().setName("side").call();
+ // Switch branches
+ git.checkout().setName("side").call();
+ recorder.assertNoEvent();
- // Switch branches
- git.checkout().setName("side").call();
+ // replace file with directory containing files
+ FileUtils.delete(file);
- // replace file with directory containing files
- FileUtils.delete(file);
+ // create and add a file in the new directory to the index
+ writeTrashFile(fname + "/dir1", "file1", "c");
+ git.add().addFilepattern(fname + "/dir1/file1").call();
- // create and add a file in the new directory to the index
- writeTrashFile(fname + "/dir1", "file1", "c");
- git.add().addFilepattern(fname + "/dir1/file1").call();
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(fname + "/dir2", "file2", "d");
- // create but do not add a file in the new directory to the index
- writeTrashFile(fname + "/dir2", "file2", "d");
+ assertTrue("File must be a directory now", file.isDirectory());
+ assertFalse("Must not delete non empty directory", file.delete());
- assertTrue("File must be a directory now", file.isDirectory());
- assertFalse("Must not delete non empty directory", file.delete());
-
- // 2 extra files are created
- assertWorkDir(
- mkmap(fname + "/dir1/file1", "c", fname + "/dir2/file2", "d"));
-
- try {
- git.checkout().setName("master").call();
- fail("did not throw exception");
- } catch (Exception e) {
- // 2 extra files are still there
+ // 2 extra files are created
assertWorkDir(mkmap(fname + "/dir1/file1", "c",
fname + "/dir2/file2", "d"));
+
+ try {
+ git.checkout().setName("master").call();
+ fail("did not throw exception");
+ } catch (Exception e) {
+ // 2 extra files are still there
+ assertWorkDir(mkmap(fname + "/dir1/file1", "c",
+ fname + "/dir2/file2", "d"));
+ }
+ recorder.assertNoEvent();
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
}
}
@@ -1371,50 +1521,60 @@
throws Exception {
Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
String fname = "file.txt";
- Git git = Git.wrap(db);
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
+ try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
+ // Add a file
+ writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
- // Add a file
- writeTrashFile(fname, "a");
- git.add().addFilepattern(fname).call();
+ // Add a link to file
+ String linkName = "link";
+ File link = writeLink(linkName, fname).toFile();
+ git.add().addFilepattern(linkName).call();
+ git.commit().setMessage("Added file and link").call();
- // Add a link to file
- String linkName = "link";
- File link = writeLink(linkName, fname).toFile();
- git.add().addFilepattern(linkName).call();
- git.commit().setMessage("Added file and link").call();
+ assertWorkDir(mkmap(linkName, "a", fname, "a"));
- assertWorkDir(mkmap(linkName, "a", fname, "a"));
+ // Create branch
+ git.branchCreate().setName("side").call();
- // Create branch
- git.branchCreate().setName("side").call();
+ // Switch branches
+ git.checkout().setName("side").call();
+ recorder.assertNoEvent();
- // Switch branches
- git.checkout().setName("side").call();
+ // replace link with directory containing files
+ FileUtils.delete(link);
- // replace link with directory containing files
- FileUtils.delete(link);
+ // create and add a file in the new directory to the index
+ writeTrashFile(linkName + "/dir1", "file1", "c");
+ git.add().addFilepattern(linkName + "/dir1/file1").call();
- // create and add a file in the new directory to the index
- writeTrashFile(linkName + "/dir1", "file1", "c");
- git.add().addFilepattern(linkName + "/dir1/file1").call();
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(linkName + "/dir2", "file2", "d");
- // create but do not add a file in the new directory to the index
- writeTrashFile(linkName + "/dir2", "file2", "d");
+ assertTrue("Link must be a directory now", link.isDirectory());
+ assertFalse("Must not delete non empty directory", link.delete());
- assertTrue("Link must be a directory now", link.isDirectory());
- assertFalse("Must not delete non empty directory", link.delete());
-
- // 2 extra files are created
- assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
- linkName + "/dir2/file2", "d"));
-
- try {
- git.checkout().setName("master").call();
- fail("did not throw exception");
- } catch (Exception e) {
- // 2 extra files are still there
+ // 2 extra files are created
assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
linkName + "/dir2/file2", "d"));
+
+ try {
+ git.checkout().setName("master").call();
+ fail("did not throw exception");
+ } catch (Exception e) {
+ // 2 extra files are still there
+ assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
+ linkName + "/dir2/file2", "d"));
+ }
+ recorder.assertNoEvent();
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
}
}
@@ -1423,36 +1583,47 @@
if (!FS.DETECTED.supportsExecute())
return;
- Git git = Git.wrap(db);
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
+ try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
+ // Add non-executable file
+ File file = writeTrashFile("file.txt", "a");
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("commit1").call();
+ assertFalse(db.getFS().canExecute(file));
- // Add non-executable file
- File file = writeTrashFile("file.txt", "a");
- git.add().addFilepattern("file.txt").call();
- git.commit().setMessage("commit1").call();
- assertFalse(db.getFS().canExecute(file));
+ // Create branch
+ git.branchCreate().setName("b1").call();
- // Create branch
- git.branchCreate().setName("b1").call();
+ // Make file executable
+ db.getFS().setExecute(file, true);
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("commit2").call();
+ recorder.assertNoEvent();
- // Make file executable
- db.getFS().setExecute(file, true);
- git.add().addFilepattern("file.txt").call();
- git.commit().setMessage("commit2").call();
+ // Verify executable and working directory is clean
+ Status status = git.status().call();
+ assertTrue(status.getModified().isEmpty());
+ assertTrue(status.getChanged().isEmpty());
+ assertTrue(db.getFS().canExecute(file));
- // Verify executable and working directory is clean
- Status status = git.status().call();
- assertTrue(status.getModified().isEmpty());
- assertTrue(status.getChanged().isEmpty());
- assertTrue(db.getFS().canExecute(file));
+ // Switch branches
+ git.checkout().setName("b1").call();
- // Switch branches
- git.checkout().setName("b1").call();
-
- // Verify not executable and working directory is clean
- status = git.status().call();
- assertTrue(status.getModified().isEmpty());
- assertTrue(status.getChanged().isEmpty());
- assertFalse(db.getFS().canExecute(file));
+ // Verify not executable and working directory is clean
+ status = git.status().call();
+ assertTrue(status.getModified().isEmpty());
+ assertTrue(status.getChanged().isEmpty());
+ assertFalse(db.getFS().canExecute(file));
+ recorder.assertEvent(new String[] { "file.txt" },
+ ChangeRecorder.EMPTY);
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
+ }
}
@Test
@@ -1460,41 +1631,50 @@
if (!FS.DETECTED.supportsExecute())
return;
- Git git = Git.wrap(db);
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
+ try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
+ // Add non-executable file
+ File file = writeTrashFile("file.txt", "a");
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("commit1").call();
+ assertFalse(db.getFS().canExecute(file));
- // Add non-executable file
- File file = writeTrashFile("file.txt", "a");
- git.add().addFilepattern("file.txt").call();
- git.commit().setMessage("commit1").call();
- assertFalse(db.getFS().canExecute(file));
+ // Create branch
+ git.branchCreate().setName("b1").call();
- // Create branch
- git.branchCreate().setName("b1").call();
+ // Make file executable
+ db.getFS().setExecute(file, true);
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("commit2").call();
- // Make file executable
- db.getFS().setExecute(file, true);
- git.add().addFilepattern("file.txt").call();
- git.commit().setMessage("commit2").call();
+ // Verify executable and working directory is clean
+ Status status = git.status().call();
+ assertTrue(status.getModified().isEmpty());
+ assertTrue(status.getChanged().isEmpty());
+ assertTrue(db.getFS().canExecute(file));
- // Verify executable and working directory is clean
- Status status = git.status().call();
- assertTrue(status.getModified().isEmpty());
- assertTrue(status.getChanged().isEmpty());
- assertTrue(db.getFS().canExecute(file));
+ writeTrashFile("file.txt", "b");
- writeTrashFile("file.txt", "b");
-
- // Switch branches
- CheckoutCommand checkout = git.checkout().setName("b1");
- try {
- checkout.call();
- fail("Checkout exception not thrown");
- } catch (org.eclipse.jgit.api.errors.CheckoutConflictException e) {
- CheckoutResult result = checkout.getResult();
- assertNotNull(result);
- assertNotNull(result.getConflictList());
- assertEquals(1, result.getConflictList().size());
- assertTrue(result.getConflictList().contains("file.txt"));
+ // Switch branches
+ CheckoutCommand checkout = git.checkout().setName("b1");
+ try {
+ checkout.call();
+ fail("Checkout exception not thrown");
+ } catch (org.eclipse.jgit.api.errors.CheckoutConflictException e) {
+ CheckoutResult result = checkout.getResult();
+ assertNotNull(result);
+ assertNotNull(result.getConflictList());
+ assertEquals(1, result.getConflictList().size());
+ assertTrue(result.getConflictList().contains("file.txt"));
+ }
+ recorder.assertNoEvent();
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
}
}
@@ -1504,40 +1684,52 @@
if (!FS.DETECTED.supportsExecute())
return;
- Git git = Git.wrap(db);
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
+ try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
+ // Add non-executable file
+ File file = writeTrashFile("file.txt", "a");
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("commit1").call();
+ assertFalse(db.getFS().canExecute(file));
- // Add non-executable file
- File file = writeTrashFile("file.txt", "a");
- git.add().addFilepattern("file.txt").call();
- git.commit().setMessage("commit1").call();
- assertFalse(db.getFS().canExecute(file));
+ // Create branch
+ git.branchCreate().setName("b1").call();
- // Create branch
- git.branchCreate().setName("b1").call();
+ // Create second commit and don't touch file
+ writeTrashFile("file2.txt", "");
+ git.add().addFilepattern("file2.txt").call();
+ git.commit().setMessage("commit2").call();
- // Create second commit and don't touch file
- writeTrashFile("file2.txt", "");
- git.add().addFilepattern("file2.txt").call();
- git.commit().setMessage("commit2").call();
+ // stage a mode change
+ writeTrashFile("file.txt", "a");
+ db.getFS().setExecute(file, true);
+ git.add().addFilepattern("file.txt").call();
- // stage a mode change
- writeTrashFile("file.txt", "a");
- db.getFS().setExecute(file, true);
- git.add().addFilepattern("file.txt").call();
+ // dirty the file
+ writeTrashFile("file.txt", "b");
- // dirty the file
- writeTrashFile("file.txt", "b");
+ assertEquals(
+ "[file.txt, mode:100755, content:a][file2.txt, mode:100644, content:]",
+ indexState(CONTENT));
+ assertWorkDir(mkmap("file.txt", "b", "file2.txt", ""));
+ recorder.assertNoEvent();
- assertEquals(
- "[file.txt, mode:100755, content:a][file2.txt, mode:100644, content:]",
- indexState(CONTENT));
- assertWorkDir(mkmap("file.txt", "b", "file2.txt", ""));
-
- // Switch branches and check that the dirty file survived in worktree
- // and index
- git.checkout().setName("b1").call();
- assertEquals("[file.txt, mode:100755, content:a]", indexState(CONTENT));
- assertWorkDir(mkmap("file.txt", "b"));
+ // Switch branches and check that the dirty file survived in
+ // worktree and index
+ git.checkout().setName("b1").call();
+ assertEquals("[file.txt, mode:100755, content:a]",
+ indexState(CONTENT));
+ assertWorkDir(mkmap("file.txt", "b"));
+ recorder.assertEvent(ChangeRecorder.EMPTY,
+ new String[] { "file2.txt" });
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
+ }
}
@Test
@@ -1546,40 +1738,53 @@
if (!FS.DETECTED.supportsExecute())
return;
- Git git = Git.wrap(db);
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
+ try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
+ // Add non-executable file
+ File file = writeTrashFile("file.txt", "a");
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("commit1").call();
+ assertFalse(db.getFS().canExecute(file));
- // Add non-executable file
- File file = writeTrashFile("file.txt", "a");
- git.add().addFilepattern("file.txt").call();
- git.commit().setMessage("commit1").call();
- assertFalse(db.getFS().canExecute(file));
+ // Create branch
+ git.branchCreate().setName("b1").call();
- // Create branch
- git.branchCreate().setName("b1").call();
+ // Create second commit with executable file
+ file = writeTrashFile("file.txt", "b");
+ db.getFS().setExecute(file, true);
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("commit2").call();
- // Create second commit with executable file
- file = writeTrashFile("file.txt", "b");
- db.getFS().setExecute(file, true);
- git.add().addFilepattern("file.txt").call();
- git.commit().setMessage("commit2").call();
+ // stage the same content as in the branch we want to switch to
+ writeTrashFile("file.txt", "a");
+ db.getFS().setExecute(file, false);
+ git.add().addFilepattern("file.txt").call();
- // stage the same content as in the branch we want to switch to
- writeTrashFile("file.txt", "a");
- db.getFS().setExecute(file, false);
- git.add().addFilepattern("file.txt").call();
+ // dirty the file
+ writeTrashFile("file.txt", "c");
+ db.getFS().setExecute(file, true);
- // dirty the file
- writeTrashFile("file.txt", "c");
- db.getFS().setExecute(file, true);
+ assertEquals("[file.txt, mode:100644, content:a]",
+ indexState(CONTENT));
+ assertWorkDir(mkmap("file.txt", "c"));
+ recorder.assertNoEvent();
- assertEquals("[file.txt, mode:100644, content:a]", indexState(CONTENT));
- assertWorkDir(mkmap("file.txt", "c"));
-
- // Switch branches and check that the dirty file survived in worktree
- // and index
- git.checkout().setName("b1").call();
- assertEquals("[file.txt, mode:100644, content:a]", indexState(CONTENT));
- assertWorkDir(mkmap("file.txt", "c"));
+ // Switch branches and check that the dirty file survived in
+ // worktree
+ // and index
+ git.checkout().setName("b1").call();
+ assertEquals("[file.txt, mode:100644, content:a]",
+ indexState(CONTENT));
+ assertWorkDir(mkmap("file.txt", "c"));
+ recorder.assertNoEvent();
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
+ }
}
@Test
@@ -1587,31 +1792,44 @@
if (!FS.DETECTED.supportsExecute())
return;
- Git git = Git.wrap(db);
+ ChangeRecorder recorder = new ChangeRecorder();
+ ListenerHandle handle = null;
+ try (Git git = new Git(db)) {
+ handle = db.getListenerList()
+ .addWorkingTreeModifiedListener(recorder);
+ // Add first file
+ File file1 = writeTrashFile("file1.txt", "a");
+ git.add().addFilepattern("file1.txt").call();
+ git.commit().setMessage("commit1").call();
+ assertFalse(db.getFS().canExecute(file1));
- // Add first file
- File file1 = writeTrashFile("file1.txt", "a");
- git.add().addFilepattern("file1.txt").call();
- git.commit().setMessage("commit1").call();
- assertFalse(db.getFS().canExecute(file1));
+ // Add second file
+ File file2 = writeTrashFile("file2.txt", "b");
+ git.add().addFilepattern("file2.txt").call();
+ git.commit().setMessage("commit2").call();
+ assertFalse(db.getFS().canExecute(file2));
+ recorder.assertNoEvent();
- // Add second file
- File file2 = writeTrashFile("file2.txt", "b");
- git.add().addFilepattern("file2.txt").call();
- git.commit().setMessage("commit2").call();
- assertFalse(db.getFS().canExecute(file2));
+ // Create branch from first commit
+ assertNotNull(git.checkout().setCreateBranch(true).setName("b1")
+ .setStartPoint(Constants.HEAD + "~1").call());
+ recorder.assertEvent(ChangeRecorder.EMPTY,
+ new String[] { "file2.txt" });
- // Create branch from first commit
- assertNotNull(git.checkout().setCreateBranch(true).setName("b1")
- .setStartPoint(Constants.HEAD + "~1").call());
+ // Change content and file mode in working directory and index
+ file1 = writeTrashFile("file1.txt", "c");
+ db.getFS().setExecute(file1, true);
+ git.add().addFilepattern("file1.txt").call();
- // Change content and file mode in working directory and index
- file1 = writeTrashFile("file1.txt", "c");
- db.getFS().setExecute(file1, true);
- git.add().addFilepattern("file1.txt").call();
-
- // Switch back to 'master'
- assertNotNull(git.checkout().setName(Constants.MASTER).call());
+ // Switch back to 'master'
+ assertNotNull(git.checkout().setName(Constants.MASTER).call());
+ recorder.assertEvent(new String[] { "file2.txt" },
+ ChangeRecorder.EMPTY);
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
+ }
}
@Test(expected = CheckoutConflictException.class)
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
index 0111b94..d89aabe 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
@@ -43,11 +43,13 @@
package org.eclipse.jgit.lib;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
+import java.util.Set;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
@@ -118,6 +120,31 @@
assertTrue(indexDiff.diff());
}
+ private void assertDiff(IndexDiff indexDiff, IgnoreSubmoduleMode mode,
+ IgnoreSubmoduleMode... expectedEmptyModes) throws IOException {
+ boolean diffResult = indexDiff.diff();
+ Set<String> submodulePaths = indexDiff
+ .getPathsWithIndexMode(FileMode.GITLINK);
+ boolean emptyExpected = false;
+ for (IgnoreSubmoduleMode empty : expectedEmptyModes) {
+ if (mode.equals(empty)) {
+ emptyExpected = true;
+ break;
+ }
+ }
+ if (emptyExpected) {
+ assertFalse("diff should be false with mode=" + mode,
+ diffResult);
+ assertEquals("should have no paths with FileMode.GITLINK", 0,
+ submodulePaths.size());
+ } else {
+ assertTrue("diff should be true with mode=" + mode,
+ diffResult);
+ assertTrue("submodule path should have FileMode.GITLINK",
+ submodulePaths.contains("modules/submodule"));
+ }
+ }
+
@Theory
public void testDirtySubmoduleWorktree(IgnoreSubmoduleMode mode)
throws IOException {
@@ -125,13 +152,8 @@
IndexDiff indexDiff = new IndexDiff(db, Constants.HEAD,
new FileTreeIterator(db));
indexDiff.setIgnoreSubmoduleMode(mode);
- if (mode.equals(IgnoreSubmoduleMode.ALL)
- || mode.equals(IgnoreSubmoduleMode.DIRTY))
- assertFalse("diff should be false with mode=" + mode,
- indexDiff.diff());
- else
- assertTrue("diff should be true with mode=" + mode,
- indexDiff.diff());
+ assertDiff(indexDiff, mode, IgnoreSubmoduleMode.ALL,
+ IgnoreSubmoduleMode.DIRTY);
}
@Theory
@@ -145,12 +167,7 @@
IndexDiff indexDiff = new IndexDiff(db, Constants.HEAD,
new FileTreeIterator(db));
indexDiff.setIgnoreSubmoduleMode(mode);
- if (mode.equals(IgnoreSubmoduleMode.ALL))
- assertFalse("diff should be false with mode=" + mode,
- indexDiff.diff());
- else
- assertTrue("diff should be true with mode=" + mode,
- indexDiff.diff());
+ assertDiff(indexDiff, mode, IgnoreSubmoduleMode.ALL);
}
@Theory
@@ -163,13 +180,8 @@
IndexDiff indexDiff = new IndexDiff(db, Constants.HEAD,
new FileTreeIterator(db));
indexDiff.setIgnoreSubmoduleMode(mode);
- if (mode.equals(IgnoreSubmoduleMode.ALL)
- || mode.equals(IgnoreSubmoduleMode.DIRTY))
- assertFalse("diff should be false with mode=" + mode,
- indexDiff.diff());
- else
- assertTrue("diff should be true with mode=" + mode,
- indexDiff.diff());
+ assertDiff(indexDiff, mode, IgnoreSubmoduleMode.ALL,
+ IgnoreSubmoduleMode.DIRTY);
}
@Theory
@@ -183,13 +195,8 @@
IndexDiff indexDiff = new IndexDiff(db, Constants.HEAD,
new FileTreeIterator(db));
indexDiff.setIgnoreSubmoduleMode(mode);
- if (mode.equals(IgnoreSubmoduleMode.ALL)
- || mode.equals(IgnoreSubmoduleMode.DIRTY))
- assertFalse("diff should be false with mode=" + mode,
- indexDiff.diff());
- else
- assertTrue("diff should be true with mode=" + mode,
- indexDiff.diff());
+ assertDiff(indexDiff, mode, IgnoreSubmoduleMode.ALL,
+ IgnoreSubmoduleMode.DIRTY);
}
@Theory
@@ -200,13 +207,7 @@
IndexDiff indexDiff = new IndexDiff(db, Constants.HEAD,
new FileTreeIterator(db));
indexDiff.setIgnoreSubmoduleMode(mode);
- if (mode.equals(IgnoreSubmoduleMode.ALL)
- || mode.equals(IgnoreSubmoduleMode.DIRTY)
- || mode.equals(IgnoreSubmoduleMode.UNTRACKED))
- assertFalse("diff should be false with mode=" + mode,
- indexDiff.diff());
- else
- assertTrue("diff should be true with mode=" + mode,
- indexDiff.diff());
+ assertDiff(indexDiff, mode, IgnoreSubmoduleMode.ALL,
+ IgnoreSubmoduleMode.DIRTY, IgnoreSubmoduleMode.UNTRACKED);
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RemoteConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RemoteConfigTest.java
index 0cada5c..a0cf0d2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RemoteConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RemoteConfigTest.java
@@ -51,6 +51,7 @@
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -498,24 +499,48 @@
}
@Test
- public void singlePushInsteadOf() throws Exception {
+ public void pushInsteadOfNotAppliedToPushUri() throws Exception {
config.setString("remote", "origin", "pushurl", "short:project.git");
config.setString("url", "https://server/repos/", "pushInsteadOf",
"short:");
RemoteConfig rc = new RemoteConfig(config, "origin");
assertFalse(rc.getPushURIs().isEmpty());
+ assertEquals("short:project.git",
+ rc.getPushURIs().get(0).toASCIIString());
+ }
+
+ @Test
+ public void pushInsteadOfAppliedToUri() throws Exception {
+ config.setString("remote", "origin", "url", "short:project.git");
+ config.setString("url", "https://server/repos/", "pushInsteadOf",
+ "short:");
+ RemoteConfig rc = new RemoteConfig(config, "origin");
+ assertFalse(rc.getPushURIs().isEmpty());
+ assertEquals("https://server/repos/project.git",
+ rc.getPushURIs().get(0).toASCIIString());
+ }
+
+ @Test
+ public void multiplePushInsteadOf() throws Exception {
+ config.setString("remote", "origin", "url", "prefixproject.git");
+ config.setStringList("url", "https://server/repos/", "pushInsteadOf",
+ Arrays.asList("pre", "prefix", "pref", "perf"));
+ RemoteConfig rc = new RemoteConfig(config, "origin");
+ assertFalse(rc.getPushURIs().isEmpty());
assertEquals("https://server/repos/project.git", rc.getPushURIs()
.get(0).toASCIIString());
}
@Test
- public void multiplePushInsteadOf() throws Exception {
- config.setString("remote", "origin", "pushurl", "prefixproject.git");
- config.setStringList("url", "https://server/repos/", "pushInsteadOf",
- Arrays.asList("pre", "prefix", "pref", "perf"));
+ public void pushInsteadOfNoPushUrl() throws Exception {
+ config.setString("remote", "origin", "url",
+ "http://git.eclipse.org/gitroot/jgit/jgit");
+ config.setStringList("url", "ssh://someone@git.eclipse.org:29418/",
+ "pushInsteadOf",
+ Collections.singletonList("http://git.eclipse.org/gitroot/"));
RemoteConfig rc = new RemoteConfig(config, "origin");
assertFalse(rc.getPushURIs().isEmpty());
- assertEquals("https://server/repos/project.git", rc.getPushURIs()
- .get(0).toASCIIString());
+ assertEquals("ssh://someone@git.eclipse.org:29418/jgit/jgit",
+ rc.getPushURIs().get(0).toASCIIString());
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/LongMapTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/LongMapTest.java
similarity index 98%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/LongMapTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/util/LongMapTest.java
index 1a86aaf..054c61e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/LongMapTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/LongMapTest.java
@@ -41,7 +41,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.transport;
+package org.eclipse.jgit.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/NBTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/NBTest.java
index 7e11a61..d2d44ff 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/NBTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/NBTest.java
@@ -90,6 +90,24 @@
}
@Test
+ public void testDecodeUInt24() {
+ assertEquals(0, NB.decodeUInt24(b(0, 0, 0), 0));
+ assertEquals(0, NB.decodeUInt24(padb(3, 0, 0, 0), 3));
+
+ assertEquals(3, NB.decodeUInt24(b(0, 0, 3), 0));
+ assertEquals(3, NB.decodeUInt24(padb(3, 0, 0, 3), 3));
+
+ assertEquals(0xcede03, NB.decodeUInt24(b(0xce, 0xde, 3), 0));
+ assertEquals(0xbade03, NB.decodeUInt24(padb(3, 0xba, 0xde, 3), 3));
+
+ assertEquals(0x03bade, NB.decodeUInt24(b(3, 0xba, 0xde), 0));
+ assertEquals(0x03bade, NB.decodeUInt24(padb(3, 3, 0xba, 0xde), 3));
+
+ assertEquals(0xffffff, NB.decodeUInt24(b(0xff, 0xff, 0xff), 0));
+ assertEquals(0xffffff, NB.decodeUInt24(padb(3, 0xff, 0xff, 0xff), 3));
+ }
+
+ @Test
public void testDecodeInt32() {
assertEquals(0, NB.decodeInt32(b(0, 0, 0, 0), 0));
assertEquals(0, NB.decodeInt32(padb(3, 0, 0, 0, 0), 3));
@@ -198,6 +216,39 @@
}
@Test
+ public void testEncodeInt24() {
+ byte[] out = new byte[16];
+
+ prepareOutput(out);
+ NB.encodeInt24(out, 0, 0);
+ assertOutput(b(0, 0, 0), out, 0);
+
+ prepareOutput(out);
+ NB.encodeInt24(out, 3, 0);
+ assertOutput(b(0, 0, 0), out, 3);
+
+ prepareOutput(out);
+ NB.encodeInt24(out, 0, 3);
+ assertOutput(b(0, 0, 3), out, 0);
+
+ prepareOutput(out);
+ NB.encodeInt24(out, 3, 3);
+ assertOutput(b(0, 0, 3), out, 3);
+
+ prepareOutput(out);
+ NB.encodeInt24(out, 0, 0xc0deac);
+ assertOutput(b(0xc0, 0xde, 0xac), out, 0);
+
+ prepareOutput(out);
+ NB.encodeInt24(out, 3, 0xbadeac);
+ assertOutput(b(0xba, 0xde, 0xac), out, 3);
+
+ prepareOutput(out);
+ NB.encodeInt24(out, 3, -1);
+ assertOutput(b(0xff, 0xff, 0xff), out, 3);
+ }
+
+ @Test
public void testEncodeInt32() {
final byte[] out = new byte[16];
@@ -315,10 +366,24 @@
return r;
}
+ private static byte[] b(int a, int b, int c) {
+ return new byte[] { (byte) a, (byte) b, (byte) c };
+ }
+
private static byte[] b(final int a, final int b, final int c, final int d) {
return new byte[] { (byte) a, (byte) b, (byte) c, (byte) d };
}
+ private static byte[] padb(int len, int a, int b, int c) {
+ final byte[] r = new byte[len + 4];
+ for (int i = 0; i < len; i++)
+ r[i] = (byte) 0xaf;
+ r[len] = (byte) a;
+ r[len + 1] = (byte) b;
+ r[len + 2] = (byte) c;
+ return r;
+ }
+
private static byte[] padb(final int len, final int a, final int b,
final int c, final int d) {
final byte[] r = new byte[len + 4];
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 21d6283..6b20da3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
@@ -47,8 +47,10 @@
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.EnumSet;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
import org.eclipse.jgit.api.CheckoutResult.Status;
import org.eclipse.jgit.api.errors.CheckoutConflictException;
@@ -66,6 +68,7 @@
import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.errors.AmbiguousObjectException;
import org.eclipse.jgit.errors.UnmergedPathException;
+import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
@@ -175,6 +178,8 @@
private boolean checkoutAllPaths;
+ private Set<String> actuallyModifiedPaths;
+
/**
* @param repo
*/
@@ -410,7 +415,8 @@
}
/**
- * Checkout paths into index and working directory
+ * Checkout paths into index and working directory, firing a
+ * {@link WorkingTreeModifiedEvent} if the working tree was modified.
*
* @return this instance
* @throws IOException
@@ -418,6 +424,7 @@
*/
protected CheckoutCommand checkoutPaths() throws IOException,
RefNotFoundException {
+ actuallyModifiedPaths = new HashSet<>();
DirCache dc = repo.lockDirCache();
try (RevWalk revWalk = new RevWalk(repo);
TreeWalk treeWalk = new TreeWalk(repo,
@@ -432,7 +439,16 @@
checkoutPathsFromCommit(treeWalk, dc, commit);
}
} finally {
- dc.unlock();
+ try {
+ dc.unlock();
+ } finally {
+ WorkingTreeModifiedEvent event = new WorkingTreeModifiedEvent(
+ actuallyModifiedPaths, null);
+ actuallyModifiedPaths = null;
+ if (!event.isEmpty()) {
+ repo.fireEvent(event);
+ }
+ }
}
return this;
}
@@ -461,9 +477,11 @@
int stage = ent.getStage();
if (stage > DirCacheEntry.STAGE_0) {
if (checkoutStage != null) {
- if (stage == checkoutStage.number)
+ if (stage == checkoutStage.number) {
checkoutPath(ent, r, new CheckoutMetadata(
eolStreamType, filterCommand));
+ actuallyModifiedPaths.add(path);
+ }
} else {
UnmergedPathException e = new UnmergedPathException(
ent);
@@ -472,6 +490,7 @@
} else {
checkoutPath(ent, r, new CheckoutMetadata(eolStreamType,
filterCommand));
+ actuallyModifiedPaths.add(path);
}
}
});
@@ -492,13 +511,15 @@
final EolStreamType eolStreamType = treeWalk.getEolStreamType();
final String filterCommand = treeWalk
.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE);
- editor.add(new PathEdit(treeWalk.getPathString()) {
+ final String path = treeWalk.getPathString();
+ editor.add(new PathEdit(path) {
@Override
public void apply(DirCacheEntry ent) {
ent.setObjectId(blobId);
ent.setFileMode(mode);
checkoutPath(ent, r,
new CheckoutMetadata(eolStreamType, filterCommand));
+ actuallyModifiedPaths.add(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 c58efb1..e41a03b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java
@@ -54,6 +54,7 @@
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.errors.NoWorkTreeException;
+import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
@@ -135,6 +136,10 @@
}
} catch (IOException e) {
throw new JGitInternalException(e.getMessage(), e);
+ } finally {
+ if (!files.isEmpty()) {
+ repo.fireEvent(new WorkingTreeModifiedEvent(null, files));
+ }
}
return files;
}
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 bae54ce..75460fb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
@@ -64,6 +64,7 @@
import org.eclipse.jgit.api.errors.NoMessageException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.dircache.DirCacheCheckout;
+import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Config.ConfigEnum;
@@ -355,6 +356,10 @@
.getMergeResults();
failingPaths = resolveMerger.getFailingPaths();
unmergedPaths = resolveMerger.getUnmergedPaths();
+ if (!resolveMerger.getModifiedFiles().isEmpty()) {
+ repo.fireEvent(new WorkingTreeModifiedEvent(
+ resolveMerger.getModifiedFiles(), null));
+ }
} else
noProblems = merger.merge(headCommit, srcCommit);
refLogMessage.append(": Merge made by "); //$NON-NLS-1$
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 9e2cf31..48c23f5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RmCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RmCommand.java
@@ -44,8 +44,10 @@
import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
+import java.util.List;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
@@ -53,6 +55,7 @@
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuildIterator;
import org.eclipse.jgit.dircache.DirCacheBuilder;
+import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
@@ -145,6 +148,7 @@
checkCallable();
DirCache dc = null;
+ List<String> actuallyDeletedFiles = new ArrayList<>();
try (final TreeWalk tw = new TreeWalk(repo)) {
dc = repo.lockDirCache();
DirCacheBuilder builder = dc.builder();
@@ -157,11 +161,14 @@
if (!cached) {
final FileMode mode = tw.getFileMode(0);
if (mode.getObjectType() == Constants.OBJ_BLOB) {
+ String relativePath = tw.getPathString();
final File path = new File(repo.getWorkTree(),
- tw.getPathString());
+ relativePath);
// Deleting a blob is simply a matter of removing
// the file or symlink named by the tree entry.
- delete(path);
+ if (delete(path)) {
+ actuallyDeletedFiles.add(relativePath);
+ }
}
}
}
@@ -171,16 +178,28 @@
throw new JGitInternalException(
JGitText.get().exceptionCaughtDuringExecutionOfRmCommand, e);
} finally {
- if (dc != null)
- dc.unlock();
+ try {
+ if (dc != null) {
+ dc.unlock();
+ }
+ } finally {
+ if (!actuallyDeletedFiles.isEmpty()) {
+ repo.fireEvent(new WorkingTreeModifiedEvent(null,
+ actuallyDeletedFiles));
+ }
+ }
}
return dc;
}
- private void delete(File p) {
- while (p != null && !p.equals(repo.getWorkTree()) && p.delete())
+ private boolean delete(File p) {
+ boolean deleted = false;
+ while (p != null && !p.equals(repo.getWorkTree()) && p.delete()) {
+ deleted = true;
p = p.getParentFile();
+ }
+ return deleted;
}
}
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 10ec2a6..b56fb25 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, GitHub Inc.
+ * Copyright (C) 2012, 2017 GitHub Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -44,6 +44,9 @@
import java.io.IOException;
import java.text.MessageFormat;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.InvalidRefNameException;
@@ -58,6 +61,7 @@
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.errors.CheckoutConflictException;
+import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
@@ -198,7 +202,13 @@
"stash" }); //$NON-NLS-1$
merger.setBase(stashHeadCommit);
merger.setWorkingTreeIterator(new FileTreeIterator(repo));
- if (merger.merge(headCommit, stashCommit)) {
+ boolean mergeSucceeded = merger.merge(headCommit, stashCommit);
+ List<String> modifiedByMerge = merger.getModifiedFiles();
+ if (!modifiedByMerge.isEmpty()) {
+ repo.fireEvent(
+ new WorkingTreeModifiedEvent(modifiedByMerge, null));
+ }
+ if (mergeSucceeded) {
DirCache dc = repo.lockDirCache();
DirCacheCheckout dco = new DirCacheCheckout(repo, headTree,
dc, merger.getResultTreeId());
@@ -329,6 +339,7 @@
private void resetUntracked(RevTree tree) throws CheckoutConflictException,
IOException {
+ Set<String> actuallyModifiedPaths = new HashSet<>();
// TODO maybe NameConflictTreeWalk ?
try (TreeWalk walk = new TreeWalk(repo)) {
walk.addTree(tree);
@@ -361,6 +372,12 @@
checkoutPath(entry, reader,
new CheckoutMetadata(eolStreamType, null));
+ actuallyModifiedPaths.add(entry.getPathString());
+ }
+ } finally {
+ if (!actuallyModifiedPaths.isEmpty()) {
+ repo.fireEvent(new WorkingTreeModifiedEvent(
+ actuallyModifiedPaths, null));
}
}
}
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 681f8e6..21b06e6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
@@ -62,6 +62,7 @@
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.errors.UnmergedPathException;
+import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Constants;
@@ -240,6 +241,7 @@
public RevCommit call() throws GitAPIException {
checkCallable();
+ List<String> deletedFiles = new ArrayList<>();
Ref head = getHead();
try (ObjectReader reader = repo.newObjectReader()) {
RevCommit headCommit = parseCommit(reader, head.getObjectId());
@@ -377,9 +379,11 @@
// Remove untracked files
if (includeUntracked) {
for (DirCacheEntry entry : untracked) {
+ String repoRelativePath = entry.getPathString();
File file = new File(repo.getWorkTree(),
- entry.getPathString());
+ repoRelativePath);
FileUtils.delete(file);
+ deletedFiles.add(repoRelativePath);
}
}
@@ -394,6 +398,11 @@
return parseCommit(reader, commitId);
} catch (IOException e) {
throw new JGitInternalException(JGitText.get().stashFailed, e);
+ } finally {
+ if (!deletedFiles.isEmpty()) {
+ repo.fireEvent(
+ new WorkingTreeModifiedEvent(null, deletedFiles));
+ }
}
}
}
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 aed76ac..f8c23ca 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -50,6 +50,7 @@
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -61,6 +62,7 @@
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.IndexWriteException;
import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
@@ -85,6 +87,7 @@
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FS.ExecutionResult;
import org.eclipse.jgit.util.FileUtils;
+import org.eclipse.jgit.util.IntList;
import org.eclipse.jgit.util.RawParseUtils;
import org.eclipse.jgit.util.SystemReader;
import org.eclipse.jgit.util.io.EolStreamTypeUtil;
@@ -151,6 +154,8 @@
private boolean emptyDirCache;
+ private boolean performingCheckout;
+
/**
* @return a list of updated paths and smudgeFilterCommands
*/
@@ -432,7 +437,8 @@
}
/**
- * Execute this checkout
+ * Execute this checkout. A {@link WorkingTreeModifiedEvent} is fired if the
+ * working tree was modified; even if the checkout fails.
*
* @return <code>false</code> if this method could not delete all the files
* which should be deleted (e.g. because of of the files was
@@ -448,7 +454,17 @@
try {
return doCheckout();
} finally {
- dc.unlock();
+ try {
+ dc.unlock();
+ } finally {
+ if (performingCheckout) {
+ WorkingTreeModifiedEvent event = new WorkingTreeModifiedEvent(
+ getUpdated().keySet(), getRemoved());
+ if (!event.isEmpty()) {
+ repo.fireEvent(event);
+ }
+ }
+ }
}
}
@@ -472,11 +488,13 @@
// update our index
builder.finish();
+ performingCheckout = true;
File file = null;
String last = null;
// when deleting files process them in the opposite order as they have
// been reported. This ensures the files are deleted before we delete
// their parent folders
+ IntList nonDeleted = new IntList();
for (int i = removed.size() - 1; i >= 0; i--) {
String r = removed.get(i);
file = new File(repo.getWorkTree(), r);
@@ -486,25 +504,47 @@
// a submodule, in which case we shall not attempt
// to delete it. A submodule is not empty, so it
// is safe to check this after a failed delete.
- if (!repo.getFS().isDirectory(file))
+ if (!repo.getFS().isDirectory(file)) {
+ nonDeleted.add(i);
toBeDeleted.add(r);
+ }
} else {
if (last != null && !isSamePrefix(r, last))
removeEmptyParents(new File(repo.getWorkTree(), last));
last = r;
}
}
- if (file != null)
+ if (file != null) {
removeEmptyParents(file);
-
- for (Map.Entry<String, CheckoutMetadata> e : updated.entrySet()) {
- String path = e.getKey();
- CheckoutMetadata meta = e.getValue();
- DirCacheEntry entry = dc.getEntry(path);
- if (!FileMode.GITLINK.equals(entry.getRawMode()))
- checkoutEntry(repo, entry, objectReader, false, meta);
}
-
+ removed = filterOut(removed, nonDeleted);
+ nonDeleted = null;
+ Iterator<Map.Entry<String, CheckoutMetadata>> toUpdate = updated
+ .entrySet().iterator();
+ Map.Entry<String, CheckoutMetadata> e = null;
+ try {
+ while (toUpdate.hasNext()) {
+ e = toUpdate.next();
+ String path = e.getKey();
+ CheckoutMetadata meta = e.getValue();
+ DirCacheEntry entry = dc.getEntry(path);
+ if (!FileMode.GITLINK.equals(entry.getRawMode())) {
+ checkoutEntry(repo, entry, objectReader, false, meta);
+ }
+ e = null;
+ }
+ } catch (Exception ex) {
+ // We didn't actually modify the current entry nor any that
+ // might follow.
+ if (e != null) {
+ toUpdate.remove();
+ }
+ while (toUpdate.hasNext()) {
+ e = toUpdate.next();
+ toUpdate.remove();
+ }
+ throw ex;
+ }
// commit the index builder - a new index is persisted
if (!builder.commit())
throw new IndexWriteException();
@@ -512,6 +552,36 @@
return toBeDeleted.size() == 0;
}
+ private static ArrayList<String> filterOut(ArrayList<String> strings,
+ IntList indicesToRemove) {
+ int n = indicesToRemove.size();
+ if (n == strings.size()) {
+ return new ArrayList<>(0);
+ }
+ switch (n) {
+ case 0:
+ return strings;
+ case 1:
+ strings.remove(indicesToRemove.get(0));
+ return strings;
+ default:
+ int length = strings.size();
+ ArrayList<String> result = new ArrayList<>(length - n);
+ // Process indicesToRemove from the back; we know that it
+ // contains indices in descending order.
+ int j = n - 1;
+ int idx = indicesToRemove.get(j);
+ for (int i = 0; i < length; i++) {
+ if (i == idx) {
+ idx = (--j >= 0) ? indicesToRemove.get(j) : -1;
+ } else {
+ result.add(strings.get(i));
+ }
+ }
+ return result;
+ }
+ }
+
private static boolean isSamePrefix(String a, String b) {
int as = a.lastIndexOf('/');
int bs = b.lastIndexOf('/');
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java
index 12ef533..cea03db 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java
@@ -53,6 +53,19 @@
private final ConcurrentMap<Class<? extends RepositoryListener>, CopyOnWriteArrayList<ListenerHandle>> lists = new ConcurrentHashMap<>();
/**
+ * Register a {@link WorkingTreeModifiedListener}.
+ *
+ * @param listener
+ * the listener implementation.
+ * @return handle to later remove the listener.
+ * @since 4.9
+ */
+ public ListenerHandle addWorkingTreeModifiedListener(
+ WorkingTreeModifiedListener listener) {
+ return addListener(WorkingTreeModifiedListener.class, listener);
+ }
+
+ /**
* Register an IndexChangedListener.
*
* @param listener
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/events/WorkingTreeModifiedEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/WorkingTreeModifiedEvent.java
new file mode 100644
index 0000000..7a53233
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/WorkingTreeModifiedEvent.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2017, Thomas Wolf <thomas.wolf@paranor.ch>
+ * 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.events;
+
+import java.util.Collections;
+import java.util.Collection;
+
+import org.eclipse.jgit.annotations.NonNull;
+
+/**
+ * A {@link RepositoryEvent} describing changes to the working tree. It is fired
+ * whenever a {@link org.eclipse.jgit.dircache.DirCacheCheckout} modifies
+ * (adds/deletes/updates) files in the working tree.
+ *
+ * @since 4.9
+ */
+public class WorkingTreeModifiedEvent
+ extends RepositoryEvent<WorkingTreeModifiedListener> {
+
+ private Collection<String> modified;
+
+ private Collection<String> deleted;
+
+ /**
+ * Creates a new {@link WorkingTreeModifiedEvent} with the given
+ * collections.
+ *
+ * @param modified
+ * repository-relative paths that were added or updated
+ * @param deleted
+ * repository-relative paths that were deleted
+ */
+ public WorkingTreeModifiedEvent(Collection<String> modified,
+ Collection<String> deleted) {
+ this.modified = modified;
+ this.deleted = deleted;
+ }
+
+ /**
+ * Determines whether there are any changes recorded in this event.
+ *
+ * @return {@code true} if no files were modified or deleted, {@code false}
+ * otherwise
+ */
+ public boolean isEmpty() {
+ return (modified == null || modified.isEmpty())
+ && (deleted == null || deleted.isEmpty());
+ }
+
+ /**
+ * Retrieves the {@link Collection} of repository-relative paths of files
+ * that were modified (added or updated).
+ *
+ * @return the set
+ */
+ public @NonNull Collection<String> getModified() {
+ Collection<String> result = modified;
+ if (result == null) {
+ result = Collections.emptyList();
+ modified = result;
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the {@link Collection} of repository-relative paths of files
+ * that were deleted.
+ *
+ * @return the set
+ */
+ public @NonNull Collection<String> getDeleted() {
+ Collection<String> result = deleted;
+ if (result == null) {
+ result = Collections.emptyList();
+ deleted = result;
+ }
+ return result;
+ }
+
+ @Override
+ public Class<WorkingTreeModifiedListener> getListenerType() {
+ return WorkingTreeModifiedListener.class;
+ }
+
+ @Override
+ public void dispatch(WorkingTreeModifiedListener listener) {
+ listener.onWorkingTreeModified(this);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/events/WorkingTreeModifiedListener.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/WorkingTreeModifiedListener.java
new file mode 100644
index 0000000..402a900
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/WorkingTreeModifiedListener.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017, Thomas Wolf <thomas.wolf@paranor.ch>
+ * 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.events;
+
+/**
+ * Receives {@link WorkingTreeModifiedEvent}s, which are fired whenever a
+ * {@link org.eclipse.jgit.dircache.DirCacheCheckout} modifies
+ * (adds/deletes/updates) files in the working tree.
+ *
+ * @since 4.9
+ */
+public interface WorkingTreeModifiedListener extends RepositoryListener {
+
+ /**
+ * Respond to working tree modifications.
+ *
+ * @param event
+ */
+ void onWorkingTreeModified(WorkingTreeModifiedEvent event);
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
index 6a674aa..646feac 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
@@ -216,7 +216,7 @@
ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, 0);
String reftype = repoConfig.getString(
- "extensions", null, "refsStorage"); //$NON-NLS-1$ //$NON-NLS-2$
+ "extensions", null, "refStorage"); //$NON-NLS-1$ //$NON-NLS-2$
if (repositoryFormatVersion >= 1 && reftype != null) {
if (StringUtils.equalsIgnoreCase(reftype, "reftree")) { //$NON-NLS-1$
refs = new RefTreeDatabase(this, new RefDirectory(this));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
index fcc47fb..0611d3e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
@@ -1108,8 +1108,17 @@
if (invalid || invalidBitmap)
return null;
if (bitmapIdx == null && hasExt(BITMAP_INDEX)) {
- final PackBitmapIndex idx = PackBitmapIndex.open(
- extFile(BITMAP_INDEX), idx(), getReverseIdx());
+ final PackBitmapIndex idx;
+ try {
+ idx = PackBitmapIndex.open(extFile(BITMAP_INDEX), idx(),
+ getReverseIdx());
+ } catch (FileNotFoundException e) {
+ // Once upon a time this bitmap file existed. Now it
+ // has been removed. Most likely an external gc has
+ // removed this packfile and the bitmap
+ invalidBitmap = true;
+ return null;
+ }
// At this point, idx() will have set packChecksum.
if (Arrays.equals(packChecksum, idx.packChecksum))
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
index e544b72..ea573a4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
@@ -513,14 +513,10 @@
}
}
- for (int i = 0; i < treeWalk.getTreeCount(); i++) {
- Set<String> values = fileModes.get(treeWalk.getFileMode(i));
- String path = treeWalk.getPathString();
- if (path != null) {
- if (values == null)
- values = new HashSet<>();
- values.add(path);
- fileModes.put(treeWalk.getFileMode(i), values);
+ String path = treeWalk.getPathString();
+ if (path != null) {
+ for (int i = 0; i < treeWalk.getTreeCount(); i++) {
+ recordFileMode(path, treeWalk.getFileMode(i));
}
}
}
@@ -545,19 +541,21 @@
}
Repository subRepo = smw.getRepository();
if (subRepo != null) {
+ String subRepoPath = smw.getPath();
try {
ObjectId subHead = subRepo.resolve("HEAD"); //$NON-NLS-1$
if (subHead != null
- && !subHead.equals(smw.getObjectId()))
- modified.add(smw.getPath());
- else if (ignoreSubmoduleMode != IgnoreSubmoduleMode.DIRTY) {
+ && !subHead.equals(smw.getObjectId())) {
+ modified.add(subRepoPath);
+ recordFileMode(subRepoPath, FileMode.GITLINK);
+ } else if (ignoreSubmoduleMode != IgnoreSubmoduleMode.DIRTY) {
IndexDiff smid = submoduleIndexDiffs.get(smw
.getPath());
if (smid == null) {
smid = new IndexDiff(subRepo,
smw.getObjectId(),
wTreeIt.getWorkingTreeIterator(subRepo));
- submoduleIndexDiffs.put(smw.getPath(), smid);
+ submoduleIndexDiffs.put(subRepoPath, smid);
}
if (smid.diff()) {
if (ignoreSubmoduleMode == IgnoreSubmoduleMode.UNTRACKED
@@ -569,7 +567,8 @@
&& smid.getRemoved().isEmpty()) {
continue;
}
- modified.add(smw.getPath());
+ modified.add(subRepoPath);
+ recordFileMode(subRepoPath, FileMode.GITLINK);
}
}
} finally {
@@ -593,6 +592,17 @@
return true;
}
+ private void recordFileMode(String path, FileMode mode) {
+ Set<String> values = fileModes.get(mode);
+ if (path != null) {
+ if (values == null) {
+ values = new HashSet<>();
+ fileModes.put(mode, values);
+ }
+ values.add(path);
+ }
+ }
+
private boolean isEntryGitLink(AbstractTreeIterator ti) {
return ((ti != null) && (ti.getEntryRawMode() == FileMode.GITLINK
.getBits()));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
index db3578b..2f6b271 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
@@ -82,6 +82,7 @@
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.util.BlockList;
import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.LongMap;
import org.eclipse.jgit.util.NB;
import org.eclipse.jgit.util.sha1.SHA1;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java
index d91684e..a0d81c0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java
@@ -170,16 +170,28 @@
vlst = rc.getStringList(SECTION, name, KEY_URL);
Map<String, String> insteadOf = getReplacements(rc, KEY_INSTEADOF);
uris = new ArrayList<>(vlst.length);
- for (final String s : vlst)
+ for (final String s : vlst) {
uris.add(new URIish(replaceUri(s, insteadOf)));
-
- Map<String, String> pushInsteadOf = getReplacements(rc,
- KEY_PUSHINSTEADOF);
- vlst = rc.getStringList(SECTION, name, KEY_PUSHURL);
- pushURIs = new ArrayList<>(vlst.length);
- for (final String s : vlst)
- pushURIs.add(new URIish(replaceUri(s, pushInsteadOf)));
-
+ }
+ String[] plst = rc.getStringList(SECTION, name, KEY_PUSHURL);
+ pushURIs = new ArrayList<>(plst.length);
+ for (final String s : plst) {
+ pushURIs.add(new URIish(s));
+ }
+ if (pushURIs.isEmpty()) {
+ // Would default to the uris. If we have pushinsteadof, we must
+ // supply rewritten push uris.
+ Map<String, String> pushInsteadOf = getReplacements(rc,
+ KEY_PUSHINSTEADOF);
+ if (!pushInsteadOf.isEmpty()) {
+ for (String s : vlst) {
+ String replaced = replaceUri(s, pushInsteadOf);
+ if (!s.equals(replaced)) {
+ pushURIs.add(new URIish(replaced));
+ }
+ }
+ }
+ }
vlst = rc.getStringList(SECTION, name, KEY_FETCH);
fetch = new ArrayList<>(vlst.length);
for (final String s : vlst)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/LongMap.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/LongMap.java
similarity index 82%
rename from org.eclipse.jgit/src/org/eclipse/jgit/transport/LongMap.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/util/LongMap.java
index 4d60202..7b0b0c7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/LongMap.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/LongMap.java
@@ -41,15 +41,16 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.transport;
+package org.eclipse.jgit.util;
/**
- * Simple Map<long,Object> helper for {@link PackParser}.
+ * Simple Map<long,Object>.
*
* @param <V>
* type of the value instance.
+ * @since 4.9
*/
-final class LongMap<V> {
+public class LongMap<V> {
private static final float LOAD_FACTOR = 0.75f;
private Node<V>[] table;
@@ -60,16 +61,27 @@
/** Next {@link #size} to trigger a {@link #grow()}. */
private int growAt;
- LongMap() {
+ /** Initialize an empty LongMap. */
+ public LongMap() {
table = createArray(64);
growAt = (int) (table.length * LOAD_FACTOR);
}
- boolean containsKey(final long key) {
+ /**
+ * @param key
+ * the key to find.
+ * @return {@code true} if {@code key} is present in the map.
+ */
+ public boolean containsKey(long key) {
return get(key) != null;
}
- V get(final long key) {
+ /**
+ * @param key
+ * the key to find.
+ * @return stored value of the key, or {@code null}.
+ */
+ public V get(long key) {
for (Node<V> n = table[index(key)]; n != null; n = n.next) {
if (n.key == key)
return n.value;
@@ -77,7 +89,12 @@
return null;
}
- V remove(final long key) {
+ /**
+ * @param key
+ * key to remove from the map.
+ * @return old value of the key, or {@code null}.
+ */
+ public V remove(long key) {
Node<V> n = table[index(key)];
Node<V> prior = null;
while (n != null) {
@@ -95,7 +112,14 @@
return null;
}
- V put(final long key, final V value) {
+ /**
+ * @param key
+ * key to store {@code value} under.
+ * @param value
+ * new value.
+ * @return prior value, or null.
+ */
+ public V put(long key, V value) {
for (Node<V> n = table[index(key)]; n != null; n = n.next) {
if (n.key == key) {
final V o = n.value;
@@ -145,9 +169,7 @@
private static class Node<V> {
final long key;
-
V value;
-
Node<V> next;
Node(final long k, final V v) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java
index 8536f1d..471a499 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java
@@ -113,6 +113,24 @@
}
/**
+ * Convert sequence of 3 bytes (network byte order) into unsigned value.
+ *
+ * @param intbuf
+ * buffer to acquire the 3 bytes of data from.
+ * @param offset
+ * position within the buffer to begin reading from. This
+ * position and the next 2 bytes after it (for a total of 3
+ * bytes) will be read.
+ * @return signed integer value that matches the 24 bits read.
+ * @since 4.9
+ */
+ public static int decodeUInt24(byte[] intbuf, int offset) {
+ int r = (intbuf[offset] & 0xff) << 8;
+ r |= intbuf[offset + 1] & 0xff;
+ return (r << 8) | (intbuf[offset + 2] & 0xff);
+ }
+
+ /**
* Convert sequence of 4 bytes (network byte order) into signed value.
*
* @param intbuf
@@ -223,6 +241,29 @@
}
/**
+ * Write a 24 bit integer as a sequence of 3 bytes (network byte order).
+ *
+ * @param intbuf
+ * buffer to write the 3 bytes of data into.
+ * @param offset
+ * position within the buffer to begin writing to. This position
+ * and the next 2 bytes after it (for a total of 3 bytes) will be
+ * replaced.
+ * @param v
+ * the value to write.
+ * @since 4.9
+ */
+ public static void encodeInt24(byte[] intbuf, int offset, int v) {
+ intbuf[offset + 2] = (byte) v;
+ v >>>= 8;
+
+ intbuf[offset + 1] = (byte) v;
+ v >>>= 8;
+
+ intbuf[offset] = (byte) v;
+ }
+
+ /**
* Write a 32 bit integer as a sequence of 4 bytes (network byte order).
*
* @param intbuf
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java
index c95992f..727c1f4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java
@@ -144,6 +144,11 @@
private static EolStreamType checkInStreamType(WorkingTreeOptions options,
Attributes attrs) {
+ if (attrs.isUnset("text")) {//$NON-NLS-1$
+ // "binary" or "-text" (which is included in the binary expansion)
+ return EolStreamType.DIRECT;
+ }
+
// old git system
if (attrs.isSet("crlf")) {//$NON-NLS-1$
return EolStreamType.TEXT_LF;
@@ -154,9 +159,6 @@
}
// new git system
- if (attrs.isUnset("text")) {//$NON-NLS-1$
- return EolStreamType.DIRECT;
- }
String eol = attrs.getValue("eol"); //$NON-NLS-1$
if (eol != null)
// check-in is always normalized to LF
@@ -183,6 +185,11 @@
private static EolStreamType checkOutStreamType(WorkingTreeOptions options,
Attributes attrs) {
+ if (attrs.isUnset("text")) {//$NON-NLS-1$
+ // "binary" or "-text" (which is included in the binary expansion)
+ return EolStreamType.DIRECT;
+ }
+
// old git system
if (attrs.isSet("crlf")) {//$NON-NLS-1$
return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF
@@ -194,9 +201,6 @@
}
// new git system
- if (attrs.isUnset("text")) {//$NON-NLS-1$
- return EolStreamType.DIRECT;
- }
String eol = attrs.getValue("eol"); //$NON-NLS-1$
if (eol != null && "crlf".equals(eol)) //$NON-NLS-1$
return EolStreamType.TEXT_CRLF;