summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jgit.test/pom.xml1
-rw-r--r--org.eclipse.jgit.test/src/org/eclipse/jgit/events/ChangeRecorder.java108
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java87
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java1150
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java29
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RmCommand.java31
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java21
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java94
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/events/WorkingTreeModifiedEvent.java129
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/events/WorkingTreeModifiedListener.java61
14 files changed, 1253 insertions, 492 deletions
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index dad1e3cacf..084014c61e 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 0000000000..c5582a8601
--- /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/StashApplyCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java
index f2e4d5b3b3..ad3ab7fbdf 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.InvalidRefNameException;
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
@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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
// expected
}
assertEquals("content3", read(PATH));
+ recorder.assertNoEvent();
}
@Test
@@ -391,10 +438,12 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
} 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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
+ "[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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
} catch (StashApplyFailureException e) {
// expected
}
+ recorder.assertNoEvent();
assertEquals("content2", read(PATH));
}
@@ -549,6 +617,7 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
.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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
assertEquals(e.getMessage(), JGitText.get().stashApplyConflict);
}
assertEquals("committed", read(path));
+ recorder.assertNoEvent();
}
@Test
@@ -727,6 +810,7 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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 @@ public class StashApplyCommandTest extends RepositoryTestCase {
assertEquals(e.getMessage(), JGitText.get().stashApplyConflict);
}
assertEquals("working-directory", read(path));
+ recorder.assertNoEvent();
}
@Test
@@ -747,11 +832,13 @@ public class StashApplyCommandTest extends RepositoryTestCase {
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/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
index f8c2d4536d..05573b9468 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.dircache.DirCacheEntry;
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 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
@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 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
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 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
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 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
@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 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
// 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 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
*/
@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 class DirCacheCheckoutTest extends RepositoryTestCase {
public void testCheckoutChangeLinkToEmptyDir() throws Exception {
Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
String fname = "was_file";
- Git git = Git.wrap(db);
-
- // 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();
-
- 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());
-
- // 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();
-
- assertWorkDir(mkmap(fname, "a", linkName, "a"));
-
- Status st = git.status().call();
- assertTrue(st.isClean());
+ 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 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"));
+
+ // 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();
+
+ // 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);
+
+ 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);
-
- // 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();
-
- 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());
-
- assertFalse("Must not delete non empty directory", link.delete());
-
- // 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();
-
- assertWorkDir(mkmap(fname, "a", linkName, "a"));
-
- Status st = git.status().call();
- assertTrue(st.isClean());
+ 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 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"));
+
+ // 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());
+
+ // modify file
+ writeTrashFile(fname, "b");
+ assertWorkDir(mkmap(fname, "b", linkName + "/dummyDir", "/"));
+ recorder.assertNoEvent();
+
+ // 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);
+
+ 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);
-
- // Add a file
- writeTrashFile(fname, "a");
- git.add().addFilepattern(fname).call();
+ 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 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"));
+ // 2 extra files are created
+ assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
+ linkName + "/dir2/file2", "d"));
+ recorder.assertNoEvent();
- // 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"));
+ // expect only the one added to the index
+ assertWorkDir(mkmap(linkName, "a", fname, "a"));
+ recorder.assertEvent(new String[] { linkName },
+ ChangeRecorder.EMPTY);
- 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 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
throws Exception {
Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
String fname = "file";
- Git git = Git.wrap(db);
-
- // Add a file
- writeTrashFile(fname, "a");
- git.add().addFilepattern(fname).call();
+ 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 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"));
+ // 2 extra files are created
+ assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
+ linkName + "/dir2/file2", "d"));
+ recorder.assertNoEvent();
- // 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"));
+ // original file and link
+ assertWorkDir(mkmap(linkName, "a", fname, "a"));
+ recorder.assertEvent(new String[] { linkName },
+ ChangeRecorder.EMPTY);
- 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);
-
- // 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, "/"));
-
- // 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());
+ 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();
+
+ // 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();
+
+ // 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
public void testCheckoutChangeFileToEmptyDirs() throws Exception {
String fname = "was_file";
- Git git = Git.wrap(db);
-
- // 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());
-
- assertWorkDir(mkmap(fname + "/dummyDir", "/"));
-
- // 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());
+ 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();
+
+ // 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();
+
+ // 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
public void testCheckoutChangeFileToNonEmptyDirs() throws Exception {
String fname = "was_file";
- Git git = Git.wrap(db);
-
- // Add a file
- File file = writeTrashFile(fname, "a");
- git.add().addFilepattern(fname).call();
- git.commit().setMessage("Added file").call();
+ 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();
- 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"));
+ // 2 extra files are created
+ assertWorkDir(mkmap(fname + "/dir1/file1", "c",
+ fname + "/dir2/file2", "d"));
+ recorder.assertNoEvent();
- // 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"));
+ // expect only the one added to the index
+ assertWorkDir(mkmap(fname, "a"));
+ recorder.assertEvent(new String[] { fname }, ChangeRecorder.EMPTY);
- 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);
-
- // Add a file
- File file = writeTrashFile(fname, "a");
- git.add().addFilepattern(fname).call();
- git.commit().setMessage("Added file").call();
-
- assertWorkDir(mkmap(fname, "a"));
-
- // 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 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());
-
- // 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());
+ 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();
+
+ assertWorkDir(mkmap(fname, "a"));
+
+ // 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 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());
+
+ // 2 extra files are created
+ assertWorkDir(mkmap(fname + "/dir/file1", "c", fname + "/dir/file2",
+ "d"));
+ recorder.assertNoEvent();
+
+ // 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 class DirCacheCheckoutTest extends RepositoryTestCase {
public void testOverwriteUntrackedIgnoredFile() throws IOException,
GitAPIException {
String fname="file.txt";
- Git git = Git.wrap(db);
-
- // Add a file
- writeTrashFile(fname, "a");
- git.add().addFilepattern(fname).call();
- git.commit().setMessage("create file").call();
-
- // Create branch
- git.branchCreate().setName("side").call();
-
- // Modify file
- writeTrashFile(fname, "b");
- git.add().addFilepattern(fname).call();
- git.commit().setMessage("modify 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());
+ 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();
+
+ // 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();
+
+ // 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();
+
+ 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);
-
- // 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();
+ 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"));
- // Switch branches
- git.checkout().setName("side").call();
+ // Create branch
+ git.branchCreate().setName("side").call();
- // replace file with directory containing files
- FileUtils.delete(file);
+ // Switch branches
+ git.checkout().setName("side").call();
+ recorder.assertNoEvent();
- // create and add a file in the new directory to the index
- writeTrashFile(fname + "/dir1", "file1", "c");
- git.add().addFilepattern(fname + "/dir1/file1").call();
+ // replace file with directory containing files
+ FileUtils.delete(file);
- // create but do not add a file in the new directory to the index
- writeTrashFile(fname + "/dir2", "file2", "d");
+ // create and add a file in the new directory to the index
+ writeTrashFile(fname + "/dir1", "file1", "c");
+ git.add().addFilepattern(fname + "/dir1/file1").call();
- assertTrue("File must be a directory now", file.isDirectory());
- assertFalse("Must not delete non empty directory", file.delete());
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(fname + "/dir2", "file2", "d");
- // 2 extra files are created
- assertWorkDir(
- mkmap(fname + "/dir1/file1", "c", fname + "/dir2/file2", "d"));
+ assertTrue("File must be a directory now", file.isDirectory());
+ assertFalse("Must not delete non empty directory", file.delete());
- 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 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
throws Exception {
Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
String fname = "file.txt";
- Git git = Git.wrap(db);
-
- // 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();
+ 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();
- assertWorkDir(mkmap(linkName, "a", fname, "a"));
+ // 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();
- // Create branch
- git.branchCreate().setName("side").call();
+ assertWorkDir(mkmap(linkName, "a", fname, "a"));
- // Switch branches
- git.checkout().setName("side").call();
+ // Create branch
+ git.branchCreate().setName("side").call();
- // replace link with directory containing files
- FileUtils.delete(link);
+ // Switch branches
+ git.checkout().setName("side").call();
+ recorder.assertNoEvent();
- // create and add a file in the new directory to the index
- writeTrashFile(linkName + "/dir1", "file1", "c");
- git.add().addFilepattern(linkName + "/dir1/file1").call();
+ // replace link with directory containing files
+ FileUtils.delete(link);
- // create but do not add a file in the new directory to the index
- writeTrashFile(linkName + "/dir2", "file2", "d");
+ // create and add a file in the new directory to the index
+ writeTrashFile(linkName + "/dir1", "file1", "c");
+ git.add().addFilepattern(linkName + "/dir1/file1").call();
- assertTrue("Link must be a directory now", link.isDirectory());
- assertFalse("Must not delete non empty directory", link.delete());
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(linkName + "/dir2", "file2", "d");
- // 2 extra files are created
- assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
- linkName + "/dir2/file2", "d"));
+ assertTrue("Link must be a directory now", link.isDirectory());
+ assertFalse("Must not delete non empty directory", link.delete());
- 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 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
if (!FS.DETECTED.supportsExecute())
return;
- Git git = Git.wrap(db);
-
- // 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();
-
- // 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));
-
- // 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));
+ 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));
+
+ // 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();
+
+ // 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();
+
+ // 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 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
if (!FS.DETECTED.supportsExecute())
return;
- Git git = Git.wrap(db);
-
- // 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();
-
- // 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));
-
- 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"));
+ 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));
+
+ // 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();
+
+ // 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");
+
+ // 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 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
if (!FS.DETECTED.supportsExecute())
return;
- Git git = Git.wrap(db);
-
- // 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 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();
-
- // 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", ""));
+ 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));
+
+ // 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();
+
+ // 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");
- // 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"));
+ assertEquals(
+ "[file.txt, mode:100755, content:a][file2.txt, mode:100644, content:]",
+ indexState(CONTENT));
+ assertWorkDir(mkmap("file.txt", "b", "file2.txt", ""));
+ recorder.assertNoEvent();
+
+ // 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 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
if (!FS.DETECTED.supportsExecute())
return;
- Git git = Git.wrap(db);
-
- // 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 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();
-
- // 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"));
-
- // 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"));
+ 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));
+
+ // 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();
+
+ // 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);
+
+ assertEquals("[file.txt, mode:100644, content:a]",
+ indexState(CONTENT));
+ assertWorkDir(mkmap("file.txt", "c"));
+ recorder.assertNoEvent();
+
+ // 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 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
if (!FS.DETECTED.supportsExecute())
return;
- Git git = Git.wrap(db);
-
- // 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));
-
- // 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();
-
- // Switch back to 'master'
- assertNotNull(git.checkout().setName(Constants.MASTER).call());
+ 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 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();
+
+ // 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" });
+
+ // 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());
+ recorder.assertEvent(new String[] { "file2.txt" },
+ ChangeRecorder.EMPTY);
+ } finally {
+ if (handle != null) {
+ handle.remove();
+ }
+ }
}
@Test(expected = CheckoutConflictException.class)
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 21d62837e9..6b20da3ede 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.io.IOException;
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.DirCacheEntry;
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 @@ public class CheckoutCommand extends GitCommand<Ref> {
private boolean checkoutAllPaths;
+ private Set<String> actuallyModifiedPaths;
+
/**
* @param repo
*/
@@ -410,7 +415,8 @@ public class CheckoutCommand extends GitCommand<Ref> {
}
/**
- * 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 @@ public class CheckoutCommand extends GitCommand<Ref> {
*/
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 @@ public class CheckoutCommand extends GitCommand<Ref> {
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 @@ public class CheckoutCommand extends GitCommand<Ref> {
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 @@ public class CheckoutCommand extends GitCommand<Ref> {
} else {
checkoutPath(ent, r, new CheckoutMetadata(eolStreamType,
filterCommand));
+ actuallyModifiedPaths.add(path);
}
}
});
@@ -492,13 +511,15 @@ public class CheckoutCommand extends GitCommand<Ref> {
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 c58efb1478..e41a03b81a 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 java.util.TreeSet;
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 @@ public class CleanCommand extends GitCommand<Set<String>> {
}
} 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 bae54ce7b5..75460fbd14 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.NoHeadException;
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 @@ public class MergeCommand extends GitCommand<MergeResult> {
.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 9e2cf31100..48c23f59c2 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 @@ package org.eclipse.jgit.api;
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.api.errors.NoFilepatternException;
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 @@ public class RmCommand extends GitCommand<DirCache> {
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 @@ public class RmCommand extends GitCommand<DirCache> {
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 @@ public class RmCommand extends GitCommand<DirCache> {
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 10ec2a6a5a..b56fb2519b 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 @@ package org.eclipse.jgit.api;
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.DirCacheCheckout.CheckoutMetadata;
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 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
"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 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
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 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
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 681f8e65ae..21b06e637a 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.DirCacheEditor.PathEdit;
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 class StashCreateCommand extends GitCommand<RevCommit> {
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 @@ public class StashCreateCommand extends GitCommand<RevCommit> {
// 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 @@ public class StashCreateCommand extends GitCommand<RevCommit> {
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 aed76ac66b..f8c23ca64f 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.nio.file.StandardCopyOption;
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.CorruptObjectException;
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.treewalk.filter.PathFilter;
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 @@ public class DirCacheCheckout {
private boolean emptyDirCache;
+ private boolean performingCheckout;
+
/**
* @return a list of updated paths and smudgeFilterCommands
*/
@@ -432,7 +437,8 @@ public class DirCacheCheckout {
}
/**
- * 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 @@ public class DirCacheCheckout {
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 @@ public class DirCacheCheckout {
// 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 @@ public class DirCacheCheckout {
// 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 @@ public class DirCacheCheckout {
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 12ef533add..cea03db2ec 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 @@ public class ListenerList {
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 0000000000..7a53233bc7
--- /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 0000000000..402a900226
--- /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);
+}