From bc126d17e826b1dfc1ed091a3e2c902dc85056ef Mon Sep 17 00:00:00 2001 From: irengrig Date: Wed, 2 Dec 2009 09:29:33 +0300 Subject: [PATCH] IDEADEV-41774 (Subversion changelist & IJ changelist) r=nick --- .../openapi/vcs/changes/ChangeListAdapter.java | 3 + .../openapi/vcs/changes/ChangeListListener.java | 1 + .../openapi/vcs/changes/ChangeListManagerImpl.java | 6 +- .../openapi/vcs/changes/ChangeListWorker.java | 10 +- .../openapi/vcs/changes/DelayedNotificator.java | 8 + .../openapi/vcs/changes/LocalChangeListImpl.java | 14 +- .../conflicts/ChangelistConflictTracker.java | 2 +- .../openapi/vcs/impl/VcsFileStatusProvider.java | 2 + .../jetbrains/idea/svn/SvnChangelistListener.java | 86 +++++++---- .../jetbrains/idea/svn/SvnFileSystemListener.java | 7 +- .../src/org/jetbrains/idea/svn/SvnVcs.java | 4 - .../org/jetbrains/idea/svn/SvnNativeListsTest.java | 169 +++++++++++++++++++++ 12 files changed, 263 insertions(+), 49 deletions(-) create mode 100644 plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeListsTest.java diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListAdapter.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListAdapter.java index 2294ec8543..8590a1fbde 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListAdapter.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListAdapter.java @@ -51,4 +51,7 @@ public class ChangeListAdapter implements ChangeListListener { public void changesRemoved(final Collection changes, final ChangeList fromList) { } + + public void changesAdded(Collection changes, ChangeList toList) { + } } \ No newline at end of file diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListListener.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListListener.java index ff9fbd0f6c..3c5398f44f 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListListener.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListListener.java @@ -25,6 +25,7 @@ import java.util.Collection; public interface ChangeListListener extends EventListener { void changeListAdded(ChangeList list); void changesRemoved(Collection changes, ChangeList fromList); + void changesAdded(Collection changes, ChangeList toList); void changeListRemoved(ChangeList list); void changeListChanged(ChangeList list); void changeListRenamed(ChangeList list, String oldName); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java index 28466f0346..56e32e7d2c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java @@ -247,11 +247,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec if (invalidated == null || invalidated.isEmpty()) { // a hack here; but otherwise everything here should be refactored ;) if (invalidated.isEmpty() && invalidated.isEverythingDirty()) { - DumbService.getInstance(myProject).runWhenSmart(new Runnable() { - public void run() { - VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); - } - }); + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); } return; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListWorker.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListWorker.java index 5beb3d53aa..8a5fb2a2e8 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListWorker.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListWorker.java @@ -402,19 +402,27 @@ public class ChangeListWorker implements ChangeListsWriteOperations { public void notifyDoneProcessingChanges(final ChangeListListener dispatcher) { List changedLists = new ArrayList(); final Map> removedChanges = new HashMap>(); + final Map> addedChanges = new HashMap>(); for (LocalChangeList list : myMap.values()) { final List removed = new ArrayList(); + final List added = new ArrayList(); final LocalChangeListImpl listImpl = (LocalChangeListImpl)list; - if (listImpl.doneProcessingChanges(removed)) { + if (listImpl.doneProcessingChanges(removed, added)) { changedLists.add(list); } if (! removed.isEmpty()) { removedChanges.put(listImpl, removed); } + if (! added.isEmpty()) { + addedChanges.put(listImpl, added); + } } for (Map.Entry> entry : removedChanges.entrySet()) { dispatcher.changesRemoved(entry.getValue(), entry.getKey()); } + for (Map.Entry> entry : addedChanges.entrySet()) { + dispatcher.changesAdded(entry.getValue(), entry.getKey()); + } for(ChangeList changeList: changedLists) { dispatcher.changeListChanged(changeList); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/DelayedNotificator.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/DelayedNotificator.java index d812fe2b8a..22a59d92d5 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/DelayedNotificator.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/DelayedNotificator.java @@ -62,6 +62,14 @@ public class DelayedNotificator { }); } + public void changesAdded(final Collection changes, final ChangeList toList) { + myService.execute(new Runnable() { + public void run() { + myDispatcher.getMulticaster().changesAdded(changes, toList); + } + }); + } + public void changeListRemoved(final ChangeList list) { myService.execute(new Runnable() { public void run() { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/LocalChangeListImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/LocalChangeListImpl.java index 8196dd8376..0c96f7792c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/LocalChangeListImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/LocalChangeListImpl.java @@ -179,18 +179,16 @@ public class LocalChangeListImpl extends LocalChangeList { return false; } - synchronized boolean doneProcessingChanges(final List removedChanges) { + synchronized boolean doneProcessingChanges(final List removedChanges, final List addedChanges) { boolean changesDetected = (myChanges.size() != myChangesBeforeUpdate.size()); - if (! changesDetected) { - for (Change newChange : myChanges) { - Change oldChange = findOldChange(newChange); - if (oldChange == null) { - changesDetected = true; - break; - } + for (Change newChange : myChanges) { + Change oldChange = findOldChange(newChange); + if (oldChange == null) { + addedChanges.add(newChange); } } + changesDetected |= (! addedChanges.isEmpty()); final List removed = new ArrayList(myChangesBeforeUpdate); // since there are SAME objects... removed.removeAll(myChanges); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/conflicts/ChangelistConflictTracker.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/conflicts/ChangelistConflictTracker.java index daf1f30da6..5a006dac54 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/conflicts/ChangelistConflictTracker.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/conflicts/ChangelistConflictTracker.java @@ -321,4 +321,4 @@ public class ChangelistConflictTracker { public ChangelistConflictResolution LAST_RESOLUTION = ChangelistConflictResolution.IGNORE; } -} \ No newline at end of file +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/VcsFileStatusProvider.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/VcsFileStatusProvider.java index 7c7ab4799a..7dd4928369 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/VcsFileStatusProvider.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/VcsFileStatusProvider.java @@ -32,6 +32,8 @@ import com.intellij.openapi.vcs.rollback.RollbackEnvironment; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.Nullable; +import java.util.Collection; + /** * @author yole */ diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangelistListener.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangelistListener.java index 72280cc286..868c3179c4 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangelistListener.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangelistListener.java @@ -18,12 +18,14 @@ package org.jetbrains.idea.svn; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ChangeList; import com.intellij.openapi.vcs.changes.ChangeListListener; +import org.jetbrains.annotations.Nullable; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNException; @@ -63,6 +65,21 @@ public class SvnChangelistListener implements ChangeListListener { } } + public void changesAdded(Collection changes, ChangeList toList) { + if (SvnChangeProvider.ourDefaultListName.equals(toList.getName())) { + return; + } + final List paths = getPathsFromChanges(changes); + for (String path : paths) { + try { + myClient.doAddToChangelist(new File[]{new File(path)}, SVNDepth.EMPTY, toList.getName(), null); + } + catch (SVNException e) { + LOG.info(e); + } + } + } + public void changeListRemoved(final ChangeList list) { final List paths = getPathsFromChanges(list.getChanges()); for (String path : paths) { @@ -153,43 +170,56 @@ public class SvnChangelistListener implements ChangeListListener { public void changeListUpdateDone() { } - // just to have all work with CLs in one class - public void pathChanged(final File from, final File to) throws SVNException { - // everything under from must became under to - final Map oldMappings = new HashMap(); + @Nullable + public static String getCurrentMapping(final Project project, final File file) { + final SvnVcs vcs = SvnVcs.getInstance(project); + final SVNChangelistClient client = vcs.createChangelistClient(); try { - myClient.doGetChangeLists(from, null, SVNDepth.INFINITY, new ISVNChangelistHandler() { + final Ref refResult = new Ref(); + final ISVNChangelistHandler handler = new ISVNChangelistHandler() { public void handle(final File path, final String changelistName) { - oldMappings.put(path, changelistName); + if (refResult.isNull() && Comparing.equal(path, file)) { + refResult.set(changelistName); + } } - }); + }; + if (file.exists()) { + client.doGetChangeLists(file, null, SVNDepth.EMPTY, handler); + } else if (file.getParentFile() != null) { + client.doGetChangeLists(file.getParentFile(), null, SVNDepth.IMMEDIATES, handler); + } + return refResult.get(); } catch (SVNException e) { LOG.info(e); } - for (Map.Entry entry : oldMappings.entrySet()) { - final File file = entry.getKey(); - try { - myClient.doRemoveFromChangelist(new File[]{file}, SVNDepth.EMPTY, null); - } - catch (SVNException e) { - LOG.info(e); - if ((! SVNErrorCode.WC_NOT_DIRECTORY.equals(e.getErrorMessage().getErrorCode()) && (! SVNErrorCode.WC_NOT_FILE.equals(e.getErrorMessage().getErrorCode())))) { - throw e; - } - } - - final String relativePath = file.getAbsolutePath().substring(from.getAbsolutePath().length()); - final File newPath = new File(to, relativePath); + return null; + } - try { - myClient.doAddToChangelist(new File[]{newPath}, SVNDepth.EMPTY, entry.getValue(), null); + public static void putUnderList(final Project project, final String list, final File after) throws SVNException { + final SvnVcs vcs = SvnVcs.getInstance(project); + final SVNChangelistClient client = vcs.createChangelistClient(); + try { + client.doAddToChangelist(new File[]{after}, SVNDepth.EMPTY, list, null); + } + catch (SVNException e) { + LOG.info(e); + if ((! SVNErrorCode.WC_NOT_DIRECTORY.equals(e.getErrorMessage().getErrorCode()) && (! SVNErrorCode.WC_NOT_FILE.equals(e.getErrorMessage().getErrorCode())))) { + throw e; } - catch (SVNException e) { - LOG.info(e); - if ((! SVNErrorCode.WC_NOT_DIRECTORY.equals(e.getErrorMessage().getErrorCode()) && (! SVNErrorCode.WC_NOT_FILE.equals(e.getErrorMessage().getErrorCode())))) { - throw e; - } + } + } + + public static void removeFromList(final Project project, final File after) throws SVNException { + final SvnVcs vcs = SvnVcs.getInstance(project); + final SVNChangelistClient client = vcs.createChangelistClient(); + try { + client.doRemoveFromChangelist(new File[]{after}, SVNDepth.EMPTY, null); + } + catch (SVNException e) { + LOG.info(e); + if ((! SVNErrorCode.WC_NOT_DIRECTORY.equals(e.getErrorMessage().getErrorCode()) && (! SVNErrorCode.WC_NOT_FILE.equals(e.getErrorMessage().getErrorCode())))) { + throw e; } } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java index e660fa6eaa..adfb7c5078 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java @@ -245,6 +245,8 @@ public class SvnFileSystemListener implements LocalFileOperationsHandler, Comman SVNMoveClient mover = vcs.createMoveClient(); long srcTime = src.lastModified(); try { + // delete old??? (deleted in listener, but we could do it here) + final String list = SvnChangelistListener.getCurrentMapping(vcs.getProject(), src); if (isUndo(vcs)) { myUndoingMove = true; restoreFromUndoStorage(dst); @@ -261,10 +263,11 @@ public class SvnFileSystemListener implements LocalFileOperationsHandler, Comman } // todo move back? mover.doMove(src, dst); + if (list != null) { + SvnChangelistListener.putUnderList(vcs.getProject(), list, dst); + } } dst.setLastModified(srcTime); - - vcs.pathChanged(src, dst); } catch (SVNException e) { addToMoveExceptions(vcs.getProject(), e); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java index 5b2dd2eba9..5987eee1e8 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java @@ -842,10 +842,6 @@ public class SvnVcs extends AbstractVcs { return Arrays.asList(new ShowPropertiesDiffWithLocalAction()); } - public void pathChanged(final File from, final File to) throws SVNException { - myChangeListListener.pathChanged(from, to); - } - private String keyForVf(final VirtualFile vf) { return vf.getUrl(); } diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeListsTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeListsTest.java new file mode 100644 index 0000000000..049567618d --- /dev/null +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeListsTest.java @@ -0,0 +1,169 @@ +package org.jetbrains.idea.svn; + +import com.intellij.execution.process.ProcessOutput; +import com.intellij.openapi.vcs.VcsConfiguration; +import com.intellij.openapi.vcs.changes.*; +import com.intellij.openapi.vfs.VirtualFile; +import org.junit.Test; +import org.tmatesoft.svn.core.SVNException; + +import java.util.Collection; +import java.util.List; + +public class SvnNativeListsTest extends SvnTestCase { + private ChangeListManager myChangeListManager; + private VcsDirtyScopeManager myDirtyScopeManager; + + @Override + public void setUp() throws Exception { + super.setUp(); + myChangeListManager = ChangeListManager.getInstance(myProject); + myDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject); + } + + @Override + public void tearDown() throws Exception { + final List changeListList = myChangeListManager.getChangeLists(); + for (LocalChangeList list : changeListList) { + if (SvnChangeProvider.ourDefaultListName.equals(list.getName())) continue; + final Collection changes = list.getChanges(); + for (Change change : changes) { + clearListForRevision(change.getBeforeRevision()); + clearListForRevision(change.getAfterRevision()); + } + } + + super.tearDown(); + } + + private void clearListForRevision(final ContentRevision revision) throws SVNException { + if (revision == null) return; + SvnChangelistListener.removeFromList(myProject, revision.getFile().getIOFile()); + } + + @Test + public void testAdd() throws Throwable { + final LocalChangeList newL = myChangeListManager.addChangeList("newOne", null); + myChangeListManager.setDefaultChangeList(newL); + + enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); + final VirtualFile file = createFileInCommand("a.txt", "old content"); + + //verify(runSvn("status"), "A a.txt"); + + myDirtyScopeManager.markEverythingDirty(); + myChangeListManager.ensureUpToDate(false); + + verify(runSvn("status"), "", "--- Changelist 'newOne':", "A a.txt"); + } + + @Test + public void testDeleted() throws Throwable { + final LocalChangeList newL = myChangeListManager.addChangeList("newOne", null); + myChangeListManager.setDefaultChangeList(newL); + + enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); + enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); + final VirtualFile file = createFileInCommand("a.txt", "old content"); + checkin(); + deleteFileInCommand(file); + + //verify(runSvn("status"), "D a.txt"); + + myDirtyScopeManager.markEverythingDirty(); + myChangeListManager.ensureUpToDate(false); + + verify(runSvn("status"), "", "--- Changelist 'newOne':", "D a.txt"); + } + + @Test + public void testEdit() throws Throwable { + final LocalChangeList newL = myChangeListManager.addChangeList("newOne", null); + myChangeListManager.setDefaultChangeList(newL); + + enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); + final VirtualFile file = createFileInCommand("a.txt", "old content"); + checkin(); + editFileInCommand(myProject, file, "111"); + + //verify(runSvn("status"), "M a.txt"); + + myDirtyScopeManager.markEverythingDirty(); + myChangeListManager.ensureUpToDate(false); + + verify(runSvn("status"), "", "--- Changelist 'newOne':", "M a.txt"); + } + + @Test + public void testEditAndMove() throws Throwable { + final LocalChangeList newL = myChangeListManager.addChangeList("newOne", null); + myChangeListManager.setDefaultChangeList(newL); + + enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); + enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); + final VirtualFile file = createFileInCommand("a.txt", "old content"); + checkin(); + editFileInCommand(myProject, file, "111"); + + //verify(runSvn("status"), "M a.txt"); + + myDirtyScopeManager.markEverythingDirty(); + myChangeListManager.ensureUpToDate(false); + + verify(runSvn("status"), "", "--- Changelist 'newOne':", "M a.txt"); + + renameFileInCommand(file, "b.txt"); + verify(runSvn("status"), "", "--- Changelist 'newOne':", "A + b.txt", "D a.txt"); + + myDirtyScopeManager.markEverythingDirty(); + myChangeListManager.ensureUpToDate(false); + + verify(runSvn("status"), "", "--- Changelist 'newOne':", "A + b.txt", "D a.txt"); + } + + @Test + public void testMove() throws Throwable { + final LocalChangeList newL = myChangeListManager.addChangeList("newOne", null); + myChangeListManager.setDefaultChangeList(newL); + + enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); + enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); + final VirtualFile file = createFileInCommand("a.txt", "old content"); + checkin(); + + renameFileInCommand(file, "b.txt"); + //verify(runSvn("status"), "A + b.txt", "D a.txt"); + + myDirtyScopeManager.markEverythingDirty(); + myChangeListManager.ensureUpToDate(false); + + verify(runSvn("status"), "", "--- Changelist 'newOne':", "A + b.txt", "D a.txt"); + } + + @Test + public void testMoveMove() throws Throwable { + final LocalChangeList newL = myChangeListManager.addChangeList("newOne", null); + myChangeListManager.setDefaultChangeList(newL); + + enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); + enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); + final VirtualFile file = createFileInCommand("a.txt", "old content"); + checkin(); + + renameFileInCommand(file, "b.txt"); + //verify(runSvn("status"), "A + b.txt", "D a.txt"); + + myDirtyScopeManager.markEverythingDirty(); + myChangeListManager.ensureUpToDate(false); + + verify(runSvn("status"), "", "--- Changelist 'newOne':", "A + b.txt", "D a.txt"); + + renameFileInCommand(file, "c.txt"); + verify(runSvn("status"), "", "--- Changelist 'newOne':", "A + c.txt", "D a.txt"); + + myDirtyScopeManager.markEverythingDirty(); + myChangeListManager.ensureUpToDate(false); + + verify(runSvn("status"), "", "--- Changelist 'newOne':", "A + c.txt", "D a.txt"); + } +} -- 2.11.4.GIT