WorkingTreeModifiedEvent: must be fired explicitly after merge
A merge may write files to the working tree. After a successful merge one must fire a WorkingTreeModifiedEvent explicitly if getModifiedFiles() is not empty. Also, any touched files must be reported by the WorkingTreeModifiedEvent fired by DirCacheCheckout.checkout(). Bug: 552636 Change-Id: I5fab8279ed8be8a4ae34cddfa726836b9277aea6 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
This commit is contained in:
parent
b29e9bd1cb
commit
64f2407f19
|
@ -43,7 +43,6 @@
|
||||||
|
|
||||||
package org.eclipse.jgit.events;
|
package org.eclipse.jgit.events;
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -99,10 +98,12 @@ public void assertEvent(String[] expectedModified,
|
||||||
Arrays.sort(expectedModified);
|
Arrays.sort(expectedModified);
|
||||||
Arrays.sort(actuallyDeleted);
|
Arrays.sort(actuallyDeleted);
|
||||||
Arrays.sort(expectedDeleted);
|
Arrays.sort(expectedDeleted);
|
||||||
assertArrayEquals("Unexpected modifications reported", expectedModified,
|
assertEquals("Unexpected modifications reported",
|
||||||
actuallyModified);
|
Arrays.toString(expectedModified),
|
||||||
assertArrayEquals("Unexpected deletions reported", expectedDeleted,
|
Arrays.toString(actuallyModified));
|
||||||
actuallyDeleted);
|
assertEquals("Unexpected deletions reported",
|
||||||
|
Arrays.toString(expectedDeleted),
|
||||||
|
Arrays.toString(actuallyDeleted));
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -345,6 +345,40 @@ public void testCherryPickConflictFiresModifiedEvent() throws Exception {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCherryPickNewFileFiresModifiedEvent() throws Exception {
|
||||||
|
ListenerHandle listener = null;
|
||||||
|
try (Git git = new Git(db)) {
|
||||||
|
writeTrashFile("test.txt", "a");
|
||||||
|
git.add().addFilepattern("test.txt").call();
|
||||||
|
git.commit().setMessage("commit1").call();
|
||||||
|
git.checkout().setCreateBranch(true).setName("a").call();
|
||||||
|
|
||||||
|
writeTrashFile("side.txt", "side");
|
||||||
|
git.add().addFilepattern("side.txt").call();
|
||||||
|
RevCommit side = git.commit().setMessage("side").call();
|
||||||
|
assertNotNull(side);
|
||||||
|
|
||||||
|
assertNotNull(git.checkout().setName(Constants.MASTER).call());
|
||||||
|
writeTrashFile("test.txt", "b");
|
||||||
|
assertNotNull(git.add().addFilepattern("test.txt").call());
|
||||||
|
assertNotNull(git.commit().setMessage("commit2").call());
|
||||||
|
|
||||||
|
ChangeRecorder recorder = new ChangeRecorder();
|
||||||
|
listener = db.getListenerList()
|
||||||
|
.addWorkingTreeModifiedListener(recorder);
|
||||||
|
CherryPickResult result = git.cherryPick()
|
||||||
|
.include(side.getId()).call();
|
||||||
|
assertEquals(CherryPickStatus.OK, result.getStatus());
|
||||||
|
recorder.assertEvent(new String[] { "side.txt" },
|
||||||
|
ChangeRecorder.EMPTY);
|
||||||
|
} finally {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCherryPickOurCommitName() throws Exception {
|
public void testCherryPickOurCommitName() throws Exception {
|
||||||
try (Git git = new Git(db)) {
|
try (Git git = new Git(db)) {
|
||||||
|
|
|
@ -57,12 +57,9 @@
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.eclipse.jgit.api.MergeResult.MergeStatus;
|
import org.eclipse.jgit.api.MergeResult.MergeStatus;
|
||||||
import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler;
|
import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler;
|
||||||
|
@ -78,6 +75,7 @@
|
||||||
import org.eclipse.jgit.errors.IllegalTodoFileModification;
|
import org.eclipse.jgit.errors.IllegalTodoFileModification;
|
||||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
||||||
import org.eclipse.jgit.errors.MissingObjectException;
|
import org.eclipse.jgit.errors.MissingObjectException;
|
||||||
|
import org.eclipse.jgit.events.ChangeRecorder;
|
||||||
import org.eclipse.jgit.events.ListenerHandle;
|
import org.eclipse.jgit.events.ListenerHandle;
|
||||||
import org.eclipse.jgit.junit.RepositoryTestCase;
|
import org.eclipse.jgit.junit.RepositoryTestCase;
|
||||||
import org.eclipse.jgit.lib.AbbreviatedObjectId;
|
import org.eclipse.jgit.lib.AbbreviatedObjectId;
|
||||||
|
@ -2014,10 +2012,9 @@ public void testRebaseWithAutoStashAndSubdirs() throws Exception {
|
||||||
checkoutBranch("refs/heads/topic");
|
checkoutBranch("refs/heads/topic");
|
||||||
writeTrashFile("sub/file0", "unstaged modified file0");
|
writeTrashFile("sub/file0", "unstaged modified file0");
|
||||||
|
|
||||||
Set<String> modifiedFiles = new HashSet<>();
|
ChangeRecorder recorder = new ChangeRecorder();
|
||||||
ListenerHandle handle = db.getListenerList()
|
ListenerHandle handle = db.getListenerList()
|
||||||
.addWorkingTreeModifiedListener(
|
.addWorkingTreeModifiedListener(recorder);
|
||||||
event -> modifiedFiles.addAll(event.getModified()));
|
|
||||||
try {
|
try {
|
||||||
// rebase
|
// rebase
|
||||||
assertEquals(Status.OK, git.rebase()
|
assertEquals(Status.OK, git.rebase()
|
||||||
|
@ -2035,9 +2032,8 @@ public void testRebaseWithAutoStashAndSubdirs() throws Exception {
|
||||||
+ "[sub/file0, mode:100644, content:file0]",
|
+ "[sub/file0, mode:100644, content:file0]",
|
||||||
indexState(CONTENT));
|
indexState(CONTENT));
|
||||||
assertEquals(RepositoryState.SAFE, db.getRepositoryState());
|
assertEquals(RepositoryState.SAFE, db.getRepositoryState());
|
||||||
List<String> modified = new ArrayList<>(modifiedFiles);
|
recorder.assertEvent(new String[] { "file1", "file2", "sub/file0" },
|
||||||
Collections.sort(modified);
|
new String[0]);
|
||||||
assertEquals("[file1, sub/file0]", modified.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -160,6 +160,10 @@ public CherryPickResult call() throws GitAPIException, NoMessageException,
|
||||||
merger.setCommitNames(new String[] { "BASE", ourName, //$NON-NLS-1$
|
merger.setCommitNames(new String[] { "BASE", ourName, //$NON-NLS-1$
|
||||||
cherryPickName });
|
cherryPickName });
|
||||||
if (merger.merge(newHead, srcCommit)) {
|
if (merger.merge(newHead, srcCommit)) {
|
||||||
|
if (!merger.getModifiedFiles().isEmpty()) {
|
||||||
|
repo.fireEvent(new WorkingTreeModifiedEvent(
|
||||||
|
merger.getModifiedFiles(), null));
|
||||||
|
}
|
||||||
if (AnyObjectId.isEqual(newHead.getTree().getId(),
|
if (AnyObjectId.isEqual(newHead.getTree().getId(),
|
||||||
merger.getResultTreeId())) {
|
merger.getResultTreeId())) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
import org.eclipse.jgit.api.errors.UnmergedPathsException;
|
import org.eclipse.jgit.api.errors.UnmergedPathsException;
|
||||||
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
|
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
|
||||||
import org.eclipse.jgit.dircache.DirCacheCheckout;
|
import org.eclipse.jgit.dircache.DirCacheCheckout;
|
||||||
|
import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
|
||||||
import org.eclipse.jgit.internal.JGitText;
|
import org.eclipse.jgit.internal.JGitText;
|
||||||
import org.eclipse.jgit.lib.AnyObjectId;
|
import org.eclipse.jgit.lib.AnyObjectId;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
|
@ -175,6 +176,10 @@ public RevCommit call() throws NoMessageException, UnmergedPathsException,
|
||||||
+ "This reverts commit " + srcCommit.getId().getName() //$NON-NLS-1$
|
+ "This reverts commit " + srcCommit.getId().getName() //$NON-NLS-1$
|
||||||
+ ".\n"; //$NON-NLS-1$
|
+ ".\n"; //$NON-NLS-1$
|
||||||
if (merger.merge(headCommit, srcParent)) {
|
if (merger.merge(headCommit, srcParent)) {
|
||||||
|
if (!merger.getModifiedFiles().isEmpty()) {
|
||||||
|
repo.fireEvent(new WorkingTreeModifiedEvent(
|
||||||
|
merger.getModifiedFiles(), null));
|
||||||
|
}
|
||||||
if (AnyObjectId.isEqual(headCommit.getTree().getId(),
|
if (AnyObjectId.isEqual(headCommit.getTree().getId(),
|
||||||
merger.getResultTreeId()))
|
merger.getResultTreeId()))
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -53,9 +53,11 @@
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.eclipse.jgit.api.errors.CanceledException;
|
import org.eclipse.jgit.api.errors.CanceledException;
|
||||||
import org.eclipse.jgit.api.errors.FilterFailedException;
|
import org.eclipse.jgit.api.errors.FilterFailedException;
|
||||||
|
@ -142,6 +144,8 @@ public CheckoutMetadata(EolStreamType eolStreamType,
|
||||||
|
|
||||||
private ArrayList<String> removed = new ArrayList<>();
|
private ArrayList<String> removed = new ArrayList<>();
|
||||||
|
|
||||||
|
private ArrayList<String> kept = new ArrayList<>();
|
||||||
|
|
||||||
private ObjectId mergeCommitTree;
|
private ObjectId mergeCommitTree;
|
||||||
|
|
||||||
private DirCache dc;
|
private DirCache dc;
|
||||||
|
@ -432,11 +436,11 @@ void processEntry(CanonicalTreeParser m, DirCacheBuildIterator i,
|
||||||
if (mtime == null || mtime.equals(Instant.EPOCH)) {
|
if (mtime == null || mtime.equals(Instant.EPOCH)) {
|
||||||
entry.setLastModified(f.getEntryLastModifiedInstant());
|
entry.setLastModified(f.getEntryLastModifiedInstant());
|
||||||
}
|
}
|
||||||
keep(entry, f);
|
keep(i.getEntryPathString(), entry, f);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
// The index contains a folder
|
// The index contains a folder
|
||||||
keep(i.getDirCacheEntry(), f);
|
keep(i.getEntryPathString(), i.getDirCacheEntry(), f);
|
||||||
} else {
|
} else {
|
||||||
// There is no entry in the merge commit. Means: we want to delete
|
// There is no entry in the merge commit. Means: we want to delete
|
||||||
// what's currently in the index and working tree
|
// what's currently in the index and working tree
|
||||||
|
@ -496,8 +500,11 @@ public boolean checkout() throws IOException {
|
||||||
dc.unlock();
|
dc.unlock();
|
||||||
} finally {
|
} finally {
|
||||||
if (performingCheckout) {
|
if (performingCheckout) {
|
||||||
|
Set<String> touched = new HashSet<>(conflicts);
|
||||||
|
touched.addAll(getUpdated().keySet());
|
||||||
|
touched.addAll(kept);
|
||||||
WorkingTreeModifiedEvent event = new WorkingTreeModifiedEvent(
|
WorkingTreeModifiedEvent event = new WorkingTreeModifiedEvent(
|
||||||
getUpdated().keySet(), getRemoved());
|
touched, getRemoved());
|
||||||
if (!event.isEmpty()) {
|
if (!event.isEmpty()) {
|
||||||
repo.fireEvent(event);
|
repo.fireEvent(event);
|
||||||
}
|
}
|
||||||
|
@ -826,14 +833,14 @@ void processEntry(CanonicalTreeParser h, CanonicalTreeParser m,
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 0xDFD: // 3 4
|
case 0xDFD: // 3 4
|
||||||
keep(dce, f);
|
keep(name, dce, f);
|
||||||
break;
|
break;
|
||||||
case 0xF0D: // 18
|
case 0xF0D: // 18
|
||||||
remove(name);
|
remove(name);
|
||||||
break;
|
break;
|
||||||
case 0xDFF: // 5 5b 6 6b
|
case 0xDFF: // 5 5b 6 6b
|
||||||
if (equalIdAndMode(iId, iMode, mId, mMode))
|
if (equalIdAndMode(iId, iMode, mId, mMode))
|
||||||
keep(dce, f); // 5 6
|
keep(name, dce, f); // 5 6
|
||||||
else
|
else
|
||||||
conflict(name, dce, h, m); // 5b 6b
|
conflict(name, dce, h, m); // 5b 6b
|
||||||
break;
|
break;
|
||||||
|
@ -863,7 +870,7 @@ void processEntry(CanonicalTreeParser h, CanonicalTreeParser m,
|
||||||
conflict(name, dce, h, m); // 9
|
conflict(name, dce, h, m); // 9
|
||||||
break;
|
break;
|
||||||
case 0xFD0: // keep without a rule
|
case 0xFD0: // keep without a rule
|
||||||
keep(dce, f);
|
keep(name, dce, f);
|
||||||
break;
|
break;
|
||||||
case 0xFFD: // 12 13 14
|
case 0xFFD: // 12 13 14
|
||||||
if (equalIdAndMode(hId, hMode, iId, iMode))
|
if (equalIdAndMode(hId, hMode, iId, iMode))
|
||||||
|
@ -883,7 +890,7 @@ void processEntry(CanonicalTreeParser h, CanonicalTreeParser m,
|
||||||
conflict(name, dce, h, m);
|
conflict(name, dce, h, m);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
keep(dce, f);
|
keep(name, dce, f);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -968,7 +975,7 @@ else if (m == null)
|
||||||
if (initialCheckout)
|
if (initialCheckout)
|
||||||
update(name, mId, mMode);
|
update(name, mId, mMode);
|
||||||
else
|
else
|
||||||
keep(dce, f);
|
keep(name, dce, f);
|
||||||
} else
|
} else
|
||||||
conflict(name, dce, h, m);
|
conflict(name, dce, h, m);
|
||||||
}
|
}
|
||||||
|
@ -1031,7 +1038,7 @@ else if (m == null)
|
||||||
// Nothing in Head
|
// Nothing in Head
|
||||||
// Something in Index
|
// Something in Index
|
||||||
// -> Merge contains nothing new. Keep the index.
|
// -> Merge contains nothing new. Keep the index.
|
||||||
keep(dce, f);
|
keep(name, dce, f);
|
||||||
} else
|
} else
|
||||||
// Merge contains something and it is not the same as Index
|
// Merge contains something and it is not the same as Index
|
||||||
// Nothing in Head
|
// Nothing in Head
|
||||||
|
@ -1182,7 +1189,7 @@ && isModified_IndexTree(name, iId, iMode, mId, mMode,
|
||||||
// to the other one.
|
// to the other one.
|
||||||
// -> In all three cases we don't touch index and file.
|
// -> In all three cases we don't touch index and file.
|
||||||
|
|
||||||
keep(dce, f);
|
keep(name, dce, f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1231,12 +1238,13 @@ private void conflict(String path, DirCacheEntry e, AbstractTreeIterator h, Abst
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void keep(DirCacheEntry e, WorkingTreeIterator f)
|
private void keep(String path, DirCacheEntry e, WorkingTreeIterator f)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (e != null && !FileMode.TREE.equals(e.getFileMode()))
|
if (e != null && !FileMode.TREE.equals(e.getFileMode()))
|
||||||
builder.add(e);
|
builder.add(e);
|
||||||
if (force) {
|
if (force) {
|
||||||
if (f.isModified(e, true, this.walk.getObjectReader())) {
|
if (f.isModified(e, true, this.walk.getObjectReader())) {
|
||||||
|
kept.add(path);
|
||||||
checkoutEntry(repo, e, this.walk.getObjectReader());
|
checkoutEntry(repo, e, this.walk.getObjectReader());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue