2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 package org
.jetbrains
.idea
.svn
;
20 import com
.intellij
.openapi
.command
.CommandAdapter
;
21 import com
.intellij
.openapi
.command
.CommandEvent
;
22 import com
.intellij
.openapi
.command
.undo
.UndoManager
;
23 import com
.intellij
.openapi
.diagnostic
.Logger
;
24 import com
.intellij
.openapi
.progress
.ProgressManager
;
25 import com
.intellij
.openapi
.project
.Project
;
26 import com
.intellij
.openapi
.project
.ProjectManager
;
27 import com
.intellij
.openapi
.ui
.DialogWrapper
;
28 import com
.intellij
.openapi
.util
.Pair
;
29 import com
.intellij
.openapi
.util
.io
.FileUtil
;
30 import com
.intellij
.openapi
.vcs
.*;
31 import com
.intellij
.openapi
.vcs
.actions
.VcsContextFactory
;
32 import com
.intellij
.openapi
.vcs
.changes
.ChangeListManager
;
33 import com
.intellij
.openapi
.vcs
.changes
.VcsDirtyScopeManager
;
34 import com
.intellij
.openapi
.vfs
.LocalFileOperationsHandler
;
35 import com
.intellij
.openapi
.vfs
.LocalFileSystem
;
36 import com
.intellij
.openapi
.vfs
.VirtualFile
;
37 import com
.intellij
.openapi
.vfs
.newvfs
.RefreshQueue
;
38 import com
.intellij
.openapi
.vfs
.newvfs
.RefreshSession
;
39 import com
.intellij
.util
.ThrowableConsumer
;
40 import com
.intellij
.vcsUtil
.ActionWithTempFile
;
41 import org
.jetbrains
.annotations
.NotNull
;
42 import org
.jetbrains
.annotations
.Nullable
;
43 import org
.jetbrains
.idea
.svn
.dialogs
.SelectIgnorePatternsToRemoveOnDeleteDialog
;
44 import org
.jetbrains
.idea
.svn
.ignore
.SvnPropertyService
;
45 import org
.tmatesoft
.svn
.core
.SVNErrorCode
;
46 import org
.tmatesoft
.svn
.core
.SVNException
;
47 import org
.tmatesoft
.svn
.core
.SVNNodeKind
;
48 import org
.tmatesoft
.svn
.core
.internal
.wc
.SVNFileUtil
;
49 import org
.tmatesoft
.svn
.core
.wc
.*;
52 import java
.io
.IOException
;
55 public class SvnFileSystemListener
extends CommandAdapter
implements LocalFileOperationsHandler
{
56 private static final Logger LOG
= Logger
.getInstance("#org.jetbrains.idea.svn.SvnFileSystemListener");
57 private final LocalFileSystem myLfs
;
59 private static class AddedFileInfo
{
60 private final Project myProject
;
61 private final VirtualFile myDir
;
62 private final String myName
;
63 @Nullable private final File myCopyFrom
;
64 private final boolean myRecursive
;
66 public AddedFileInfo(final Project project
, final VirtualFile dir
, final String name
, @Nullable final File copyFrom
, boolean recursive
) {
70 myCopyFrom
= copyFrom
;
71 myRecursive
= recursive
;
75 private static class DeletedFileInfo
{
76 private final Project myProject
;
77 private final File myFile
;
79 public DeletedFileInfo(final Project project
, final File file
) {
85 private static class MovedFileInfo
{
86 private final Project myProject
;
87 private final File mySrc
;
88 private final File myDst
;
90 private MovedFileInfo(final Project project
, final File src
, final File dst
) {
97 private final Map
<Project
, Map
<String
, IgnoredFileInfo
>> myIgnoredInfo
= new HashMap
<Project
, Map
<String
, IgnoredFileInfo
>>();
99 private final List
<AddedFileInfo
> myAddedFiles
= new ArrayList
<AddedFileInfo
>();
100 private final List
<DeletedFileInfo
> myDeletedFiles
= new ArrayList
<DeletedFileInfo
>();
101 private final List
<MovedFileInfo
> myMovedFiles
= new ArrayList
<MovedFileInfo
>();
102 private final Map
<Project
, List
<VcsException
>> myMoveExceptions
= new HashMap
<Project
, List
<VcsException
>>();
103 private final List
<VirtualFile
> myFilesToRefresh
= new ArrayList
<VirtualFile
>();
104 @Nullable private File myStorageForUndo
;
105 private final List
<Pair
<File
, File
>> myUndoStorageContents
= new ArrayList
<Pair
<File
, File
>>();
106 private boolean myUndoingMove
= false;
108 public SvnFileSystemListener() {
109 myLfs
= LocalFileSystem
.getInstance();
112 private void addToMoveExceptions(final Project project
, final SVNException e
) {
113 List
<VcsException
> exceptionList
= myMoveExceptions
.get(project
);
114 if (exceptionList
== null) {
115 exceptionList
= new ArrayList
<VcsException
>();
116 myMoveExceptions
.put(project
, exceptionList
);
118 VcsException vcsException
;
119 if (SVNErrorCode
.ENTRY_EXISTS
.equals(e
.getErrorMessage().getErrorCode())) {
120 vcsException
= new VcsException(Arrays
.asList("Target of move operation is already under version control.",
121 "Subversion move had not been performed. ", e
.getMessage()));
123 vcsException
= new VcsException(e
);
125 exceptionList
.add(vcsException
);
129 public File
copy(final VirtualFile file
, final VirtualFile toDir
, final String copyName
) throws IOException
{
130 SvnVcs vcs
= getVCS(toDir
);
138 File srcFile
= new File(file
.getPath());
139 File destFile
= new File(new File(toDir
.getPath()), copyName
);
140 final boolean dstDirUnderControl
= SVNWCUtil
.isVersionedDirectory(destFile
.getParentFile());
141 if (! dstDirUnderControl
&& !isPendingAdd(toDir
)) {
145 if (!SVNWCUtil
.isVersionedDirectory(srcFile
.getParentFile())) {
146 myAddedFiles
.add(new AddedFileInfo(vcs
.getProject(), toDir
, copyName
, null, false));
150 final SVNStatus fileStatus
= getFileStatus(vcs
, srcFile
);
151 if (fileStatus
!= null && fileStatus
.getContentsStatus() == SVNStatusType
.STATUS_ADDED
) {
152 myAddedFiles
.add(new AddedFileInfo(vcs
.getProject(), toDir
, copyName
, null, false));
156 if (sameRoot(vcs
, file
.getParent(), toDir
)) {
157 myAddedFiles
.add(new AddedFileInfo(vcs
.getProject(), toDir
, copyName
, srcFile
, false));
161 myAddedFiles
.add(new AddedFileInfo(vcs
.getProject(), toDir
, copyName
, null, false));
165 private boolean sameRoot(final SvnVcs vcs
, final VirtualFile srcDir
, final VirtualFile dstDir
) {
166 final UUIDHelper helper
= new UUIDHelper(vcs
);
167 final String srcUUID
= helper
.getRepositoryUUID(srcDir
);
168 final String dstUUID
= helper
.getRepositoryUUID(dstDir
);
170 return srcUUID
!= null && dstUUID
!= null && srcUUID
.equals(dstUUID
);
173 private class UUIDHelper
{
174 private final SVNWCClient myWcClient
;
176 private UUIDHelper(final SvnVcs vcs
) {
177 myWcClient
= vcs
.createWCClient();
181 * passed dir must be under VC control (it is assumed)
184 public String
getRepositoryUUID(final VirtualFile dir
) {
186 final SVNInfo info1
= myWcClient
.doInfo(new File(dir
.getPath()), SVNRevision
.WORKING
);
187 if (info1
== null || info1
.getRepositoryUUID() == null) {
188 // go deeper if current parent was added (if parent was added, it theoretically could NOT know its repo UUID)
189 final VirtualFile parent
= dir
.getParent();
190 if (parent
== null) {
193 if (isPendingAdd(parent
)) {
194 return getRepositoryUUID(parent
);
197 return info1
.getRepositoryUUID();
199 } catch (SVNException e
) {
200 // go to return default
206 public boolean move(VirtualFile file
, VirtualFile toDir
) throws IOException
{
207 File srcFile
= getIOFile(file
);
208 File dstFile
= new File(getIOFile(toDir
), file
.getName());
210 final SvnVcs vcs
= getVCS(toDir
);
211 final SvnVcs sourceVcs
= getVCS(file
);
212 if (vcs
== null && sourceVcs
== null) return false;
217 if (sourceVcs
== null) {
218 return createItem(toDir
, file
.getName(), file
.isDirectory(), true);
221 if (isPendingAdd(toDir
)) {
222 myMovedFiles
.add(new MovedFileInfo(sourceVcs
.getProject(), srcFile
, dstFile
));
226 final VirtualFile oldParent
= file
.getParent();
227 myFilesToRefresh
.add(oldParent
);
228 myFilesToRefresh
.add(toDir
);
229 return doMove(sourceVcs
, srcFile
, dstFile
);
233 public boolean rename(VirtualFile file
, String newName
) throws IOException
{
234 File srcFile
= getIOFile(file
);
235 File dstFile
= new File(srcFile
.getParentFile(), newName
);
236 SvnVcs vcs
= getVCS(file
);
238 myFilesToRefresh
.add(file
.getParent());
239 return doMove(vcs
, srcFile
, dstFile
);
244 private boolean doMove(@NotNull SvnVcs vcs
, final File src
, final File dst
) {
245 SVNMoveClient mover
= vcs
.createMoveClient();
246 long srcTime
= src
.lastModified();
248 // delete old??? (deleted in listener, but we could do it here)
249 final String list
= SvnChangelistListener
.getCurrentMapping(vcs
.getProject(), src
);
251 myUndoingMove
= true;
252 restoreFromUndoStorage(dst
);
253 mover
.undoMove(src
, dst
);
256 // if src is not under version control, do usual move.
257 SVNStatus srcStatus
= getFileStatus(vcs
, src
);
258 if (srcStatus
== null || srcStatus
.getContentsStatus() == SVNStatusType
.STATUS_UNVERSIONED
||
259 srcStatus
.getContentsStatus() == SVNStatusType
.STATUS_EXTERNAL
||
260 srcStatus
.getContentsStatus() == SVNStatusType
.STATUS_MISSING
||
261 srcStatus
.getContentsStatus() == SVNStatusType
.STATUS_OBSTRUCTED
) {
265 mover
.doMove(src
, dst
);
267 SvnChangelistListener
.putUnderList(vcs
.getProject(), list
, dst
);
270 dst
.setLastModified(srcTime
);
272 catch (SVNException e
) {
273 addToMoveExceptions(vcs
.getProject(), e
);
279 private void restoreFromUndoStorage(final File dst
) {
280 String normPath
= FileUtil
.toSystemIndependentName(dst
.getPath());
281 for (Iterator
<Pair
<File
, File
>> it
= myUndoStorageContents
.iterator(); it
.hasNext();) {
282 Pair
<File
, File
> e
= it
.next();
283 final String p
= FileUtil
.toSystemIndependentName(e
.first
.getPath());
284 if (p
.startsWith(normPath
)) {
286 FileUtil
.rename(e
.second
, e
.first
);
288 catch (IOException ex
) {
290 FileUtil
.asyncDelete(e
.second
);
295 if (myStorageForUndo
!= null) {
296 final File
[] files
= myStorageForUndo
.listFiles();
297 if (files
== null || files
.length
== 0) {
298 FileUtil
.asyncDelete(myStorageForUndo
);
299 myStorageForUndo
= null;
305 public boolean createFile(VirtualFile dir
, String name
) throws IOException
{
306 return createItem(dir
, name
, false, false);
309 public boolean createDirectory(VirtualFile dir
, String name
) throws IOException
{
310 return createItem(dir
, name
, true, false);
314 * delete file or directory (both 'undo' and 'do' modes)
315 * unversioned: do nothing, return false
316 * obstructed: do nothing, return false
317 * external or wc root: do nothing, return false
318 * missing: do nothing, return false
320 * versioned: schedule for deletion, return true
321 * added: schedule for deletion (make unversioned), return true
322 * copied, but not scheduled: schedule for deletion, return true
323 * replaced: schedule for deletion, return true
325 * deleted: do nothing, return true (strange)
327 public boolean delete(VirtualFile file
) throws IOException
{
328 SvnVcs vcs
= getVCS(file
);
329 if (vcs
!= null && SvnUtil
.isAdminDirectory(file
)) {
332 File ioFile
= getIOFile(file
);
333 if (!SVNWCUtil
.isVersionedDirectory(ioFile
.getParentFile())) {
337 if (SVNWCUtil
.isWorkingCopyRoot(ioFile
)) {
340 } catch (SVNException e
) {
344 SVNStatus status
= getFileStatus(ioFile
);
346 if (status
== null ||
347 status
.getContentsStatus() == SVNStatusType
.STATUS_UNVERSIONED
||
348 status
.getContentsStatus() == SVNStatusType
.STATUS_OBSTRUCTED
||
349 status
.getContentsStatus() == SVNStatusType
.STATUS_MISSING
||
350 status
.getContentsStatus() == SVNStatusType
.STATUS_EXTERNAL
) {
352 } else if (status
.getContentsStatus() == SVNStatusType
.STATUS_IGNORED
) {
353 if (vcs
!= null && file
.getParent() != null) {
354 if (! isUndoOrRedo(vcs
.getProject())) {
355 putIgnoreInfo(file
, vcs
, ioFile
);
360 else if (status
.getContentsStatus() == SVNStatusType
.STATUS_DELETED
) {
362 moveToUndoStorage(file
);
368 if (status
.getContentsStatus() == SVNStatusType
.STATUS_ADDED
) {
370 final SVNWCClient wcClient
= vcs
.createWCClient();
371 wcClient
.doRevert(ioFile
, false);
373 catch (SVNException e
) {
378 myDeletedFiles
.add(new DeletedFileInfo(vcs
.getProject(), ioFile
));
379 // packages deleted from disk should not be deleted from svn (IDEADEV-16066)
380 if (file
.isDirectory() || isUndo(vcs
)) return true;
387 private void putIgnoreInfo(VirtualFile file
, SvnVcs vcs
, File ioFile
) {
388 final String key
= file
.getParent().getPresentableUrl();
389 Map
<String
, IgnoredFileInfo
> map
= myIgnoredInfo
.get(vcs
.getProject());
391 final IgnoredFileInfo info
= map
.get(key
);
393 info
.addFileName(file
.getName());
397 final Set
<String
> existingPatterns
= SvnPropertyService
.getIgnoreStringsUnder(vcs
, file
.getParent());
398 if (existingPatterns
!= null) {
400 map
= new HashMap
<String
, IgnoredFileInfo
>();
401 myIgnoredInfo
.put(vcs
.getProject(), map
);
403 final File parentIo
= ioFile
.getParentFile();
404 final IgnoredFileInfo info
= new IgnoredFileInfo(parentIo
, existingPatterns
);
405 info
.addFileName(file
.getName());
410 private void moveToUndoStorage(final VirtualFile file
) {
411 if (myStorageForUndo
== null) {
413 myStorageForUndo
= FileUtil
.createTempDirectory("svnUndoStorage", "");
415 catch (IOException e
) {
420 final File tmpFile
= FileUtil
.findSequentNonexistentFile(myStorageForUndo
, "tmp", "");
421 myUndoStorageContents
.add(0, new Pair
<File
, File
>(new File(file
.getPath()), tmpFile
));
422 new File(file
.getPath()).renameTo(tmpFile
);
426 * add file or directory:
428 * parent directory is:
429 * unversioned: do nothing, return false
432 * null: create entry, schedule for addition
433 * missing: do nothing, return false
434 * deleted, 'do' mode: try to create entry and it schedule for addition if kind is the same, otherwise do nothing, return false.
435 * deleted: 'undo' mode: try to revert non-recursively, if kind is the same, otherwise do nothing, return false.
436 * anything else: return false.
438 private boolean createItem(VirtualFile dir
, String name
, boolean directory
, final boolean recursive
) {
439 SvnVcs vcs
= getVCS(dir
);
443 if (isUndo(vcs
) && SvnUtil
.isAdminDirectory(dir
, name
)) {
446 File ioDir
= getIOFile(dir
);
447 boolean pendingAdd
= isPendingAdd(dir
);
448 if (!SVNWCUtil
.isVersionedDirectory(ioDir
) && !pendingAdd
) {
451 SVNWCClient wcClient
= vcs
.createWCClient();
452 File targetFile
= new File(ioDir
, name
);
453 SVNStatus status
= getFileStatus(vcs
, targetFile
);
455 if (status
== null || status
.getContentsStatus() == SVNStatusType
.STATUS_NONE
) {
456 myAddedFiles
.add(new AddedFileInfo(vcs
.getProject(), dir
, name
, null, recursive
));
459 else if (status
.getContentsStatus() == SVNStatusType
.STATUS_MISSING
) {
462 else if (status
.getContentsStatus() == SVNStatusType
.STATUS_DELETED
) {
463 SVNNodeKind kind
= status
.getKind();
465 if (directory
&& kind
!= SVNNodeKind
.DIR
|| !directory
&& kind
!= SVNNodeKind
.FILE
) {
470 wcClient
.doRevert(targetFile
, false);
473 myAddedFiles
.add(new AddedFileInfo(vcs
.getProject(), dir
, name
, null, recursive
));
476 catch (SVNException e
) {
477 SVNFileUtil
.deleteAll(targetFile
, true);
484 private boolean isPendingAdd(final VirtualFile dir
) {
485 for(AddedFileInfo i
: myAddedFiles
) {
486 if (i
.myDir
== dir
.getParent() && i
.myName
.equals(dir
.getName())) {
493 public void commandStarted(CommandEvent event
) {
494 myUndoingMove
= false;
495 final Project project
= event
.getProject();
496 if (project
== null) return;
497 commandStarted(project
);
500 void commandStarted(final Project project
) {
501 myUndoingMove
= false;
502 myMoveExceptions
.remove(project
);
503 myIgnoredInfo
.remove(project
);
506 public void commandFinished(CommandEvent event
) {
507 final Project project
= event
.getProject();
508 if (project
== null) return;
509 commandFinished(project
);
512 void commandFinished(final Project project
) {
513 if (!myAddedFiles
.isEmpty()) {
514 processAddedFiles(project
);
516 processMovedFiles(project
);
517 if (!myDeletedFiles
.isEmpty()) {
518 processDeletedFiles(project
);
521 final List
<VcsException
> exceptionList
= myMoveExceptions
.get(project
);
522 if (exceptionList
!= null && ! exceptionList
.isEmpty()) {
523 AbstractVcsHelper
.getInstance(project
).showErrors(exceptionList
, SvnBundle
.message("move.files.errors.title"));
526 dealWithIgnorePatterns(project
);
528 if (!myFilesToRefresh
.isEmpty()) {
529 refreshFiles(project
);
533 private void dealWithIgnorePatterns(Project project
) {
534 final Map
<String
, IgnoredFileInfo
> map
= myIgnoredInfo
.get(project
);
536 final SvnVcs vcs
= SvnVcs
.getInstance(project
);
537 final ProgressManager progressManager
= ProgressManager
.getInstance();
538 final Runnable prepare
= new Runnable() {
540 for (Iterator
<String
> iterator
= map
.keySet().iterator(); iterator
.hasNext();) {
541 final String key
= iterator
.next();
542 final IgnoredFileInfo info
= map
.get(key
);
543 info
.calculatePatterns(vcs
);
544 if (info
.getPatterns().isEmpty()) {
550 progressManager
.runProcessWithProgressSynchronously(prepare
, SvnBundle
.message("gather.ignore.patterns.info.progress.title"), false, project
);
551 if (map
.isEmpty()) return;
553 final SelectIgnorePatternsToRemoveOnDeleteDialog dialog
= new SelectIgnorePatternsToRemoveOnDeleteDialog(project
, map
);
555 final Collection
<IgnoredFileInfo
> result
= dialog
.getResult();
556 if (dialog
.getExitCode() == DialogWrapper
.OK_EXIT_CODE
&& ! result
.isEmpty()) {
557 final List
<VcsException
> exceptions
= new ArrayList
<VcsException
>(0);
559 final Runnable deletePatterns
= new Runnable() {
561 for (IgnoredFileInfo info
: result
) {
563 info
.getOldPatterns().removeAll(info
.getPatterns());
564 SvnPropertyService
.setIgnores(vcs
, info
.getOldPatterns(), info
.getFile());
566 catch (SVNException e
) {
567 exceptions
.add(new VcsException(e
));
572 progressManager
.runProcessWithProgressSynchronously(deletePatterns
, "Removing selected 'svn:ignore' patterns", false, project
);
573 if (! exceptions
.isEmpty()) {
574 AbstractVcsHelper
.getInstance(project
).showErrors(exceptions
, SvnBundle
.message("remove.ignore.patterns.errors.title"));
580 private void refreshFiles(final Project project
) {
581 final List
<VirtualFile
> toRefreshFiles
= new ArrayList
<VirtualFile
>();
582 final List
<VirtualFile
> toRefreshDirs
= new ArrayList
<VirtualFile
>();
583 for (VirtualFile file
: myFilesToRefresh
) {
584 if (file
== null) continue;
585 if (file
.isDirectory()) {
586 toRefreshDirs
.add(file
);
588 toRefreshFiles
.add(file
);
591 // if refresh asynchronously, local changes would also be notified that they are dirty asynchronously,
592 // and commit could be executed while not all changes are visible
593 final RefreshSession session
= RefreshQueue
.getInstance().createSession(true, true, new Runnable() {
595 if (project
.isDisposed()) return;
596 filterOutInvalid(toRefreshFiles
);
597 filterOutInvalid(toRefreshDirs
);
599 final VcsDirtyScopeManager vcsDirtyScopeManager
= VcsDirtyScopeManager
.getInstance(project
);
600 vcsDirtyScopeManager
.filesDirty(toRefreshFiles
, toRefreshDirs
);
603 filterOutInvalid(myFilesToRefresh
);
604 session
.addAllFiles(myFilesToRefresh
);
606 myFilesToRefresh
.clear();
609 private static void filterOutInvalid(final Collection
<VirtualFile
> files
) {
610 for (Iterator
<VirtualFile
> iterator
= files
.iterator(); iterator
.hasNext();) {
611 final VirtualFile file
= iterator
.next();
612 if (! file
.isValid() || ! file
.exists()) {
613 LOG
.info("Refresh root is not valid: " + file
.getPath());
619 private void processAddedFiles(Project project
) {
620 SvnVcs vcs
= SvnVcs
.getInstance(project
);
621 List
<VirtualFile
> addedVFiles
= new ArrayList
<VirtualFile
>();
622 Map
<VirtualFile
, File
> copyFromMap
= new HashMap
<VirtualFile
, File
>();
623 final Set
<VirtualFile
> recursiveItems
= new HashSet
<VirtualFile
>();
624 for (Iterator
<AddedFileInfo
> it
= myAddedFiles
.iterator(); it
.hasNext();) {
625 AddedFileInfo addedFileInfo
= it
.next();
626 if (addedFileInfo
.myProject
== project
) {
628 final File ioFile
= new File(getIOFile(addedFileInfo
.myDir
), addedFileInfo
.myName
);
629 VirtualFile addedFile
= addedFileInfo
.myDir
.findChild(addedFileInfo
.myName
);
630 if (addedFile
== null) {
631 addedFile
= myLfs
.refreshAndFindFileByIoFile(ioFile
);
633 if (addedFile
!= null) {
634 final SVNStatus fileStatus
= getFileStatus(vcs
, ioFile
);
635 if (fileStatus
== null || fileStatus
.getContentsStatus() != SVNStatusType
.STATUS_IGNORED
) {
636 boolean isIgnored
= ChangeListManager
.getInstance(addedFileInfo
.myProject
).isIgnoredFile(addedFile
);
638 addedVFiles
.add(addedFile
);
639 copyFromMap
.put(addedFile
, addedFileInfo
.myCopyFrom
);
640 if (addedFileInfo
.myRecursive
) {
641 recursiveItems
.add(addedFile
);
648 if (addedVFiles
.isEmpty()) return;
649 final VcsShowConfirmationOption
.Value value
= vcs
.getAddConfirmation().getValue();
650 if (value
!= VcsShowConfirmationOption
.Value
.DO_NOTHING_SILENTLY
) {
651 final AbstractVcsHelper vcsHelper
= AbstractVcsHelper
.getInstance(project
);
652 Collection
<VirtualFile
> filesToProcess
;
653 if (value
== VcsShowConfirmationOption
.Value
.DO_ACTION_SILENTLY
) {
654 filesToProcess
= addedVFiles
;
657 final String singleFilePrompt
;
658 if (addedVFiles
.size() == 1 && addedVFiles
.get(0).isDirectory()) {
659 singleFilePrompt
= SvnBundle
.getString("confirmation.text.add.dir");
662 singleFilePrompt
= SvnBundle
.getString("confirmation.text.add.file");
664 filesToProcess
= vcsHelper
.selectFilesToProcess(addedVFiles
, SvnBundle
.message("confirmation.title.add.multiple.files"),
666 SvnBundle
.message("confirmation.title.add.file"), singleFilePrompt
,
667 vcs
.getAddConfirmation());
669 if (filesToProcess
!= null) {
670 final List
<VcsException
> exceptions
= new ArrayList
<VcsException
>();
671 SVNWCClient wcClient
= vcs
.createWCClient();
672 final SVNCopyClient copyClient
= vcs
.createCopyClient();
673 for(VirtualFile file
: filesToProcess
) {
674 final File ioFile
= new File(file
.getPath());
676 final File copyFrom
= copyFromMap
.get(file
);
677 if (copyFrom
!= null) {
679 new ActionWithTempFile(ioFile
) {
680 protected void executeInternal() throws VcsException
{
683 final SVNCopySource
[] copySource
= {new SVNCopySource(SVNRevision
.WORKING
, SVNRevision
.WORKING
, copyFrom
)};
684 copyClient
.doCopy(copySource
, ioFile
, false, true, true);
686 catch (SVNException e
) {
687 throw new VcsException(e
);
692 catch (VcsException e
) {
697 wcClient
.doAdd(ioFile
, true, false, false, true);
699 VcsDirtyScopeManager
.getInstance(project
).fileDirty(file
);
701 catch (SVNException e
) {
702 exceptions
.add(new VcsException(e
));
705 if (!exceptions
.isEmpty()) {
706 vcsHelper
.showErrors(exceptions
, SvnBundle
.message("add.files.errors.title"));
712 private void processDeletedFiles(Project project
) {
713 final List
<FilePath
> deletedFiles
= new ArrayList
<FilePath
>();
714 for (Iterator
<DeletedFileInfo
> it
= myDeletedFiles
.iterator(); it
.hasNext();) {
715 DeletedFileInfo deletedFileInfo
= it
.next();
716 if (deletedFileInfo
.myProject
== project
) {
718 final FilePath filePath
= VcsContextFactory
.SERVICE
.getInstance().createFilePathOn(deletedFileInfo
.myFile
);
719 deletedFiles
.add(filePath
);
722 if (deletedFiles
.isEmpty() || myUndoingMove
) return;
723 SvnVcs vcs
= SvnVcs
.getInstance(project
);
724 final VcsShowConfirmationOption
.Value value
= vcs
.getDeleteConfirmation().getValue();
725 if (value
!= VcsShowConfirmationOption
.Value
.DO_NOTHING_SILENTLY
) {
726 final AbstractVcsHelper vcsHelper
= AbstractVcsHelper
.getInstance(project
);
727 Collection
<FilePath
> filesToProcess
;
728 if (value
== VcsShowConfirmationOption
.Value
.DO_ACTION_SILENTLY
) {
729 filesToProcess
= new ArrayList
<FilePath
>(deletedFiles
);
733 final String singleFilePrompt
;
734 if (deletedFiles
.size() == 1 && deletedFiles
.get(0).isDirectory()) {
735 singleFilePrompt
= SvnBundle
.getString("confirmation.text.delete.dir");
738 singleFilePrompt
= SvnBundle
.getString("confirmation.text.delete.file");
740 final Collection
<FilePath
> files
= vcsHelper
741 .selectFilePathsToProcess(deletedFiles
, SvnBundle
.message("confirmation.title.delete.multiple.files"), null,
742 SvnBundle
.message("confirmation.title.delete.file"), singleFilePrompt
, vcs
.getDeleteConfirmation());
743 filesToProcess
= files
== null ?
null : new ArrayList
<FilePath
>(files
);
745 if (filesToProcess
!= null) {
746 List
<VcsException
> exceptions
= new ArrayList
<VcsException
>();
747 SVNWCClient wcClient
= vcs
.createWCClient();
748 for(FilePath file
: filesToProcess
) {
749 VirtualFile vFile
= file
.getVirtualFile(); // for deleted directories
750 File ioFile
= new File(file
.getPath());
752 wcClient
.doDelete(ioFile
, true, false);
753 if (vFile
!= null && vFile
.isValid() && vFile
.isDirectory()) {
754 vFile
.refresh(true, true);
755 VcsDirtyScopeManager
.getInstance(project
).dirDirtyRecursively(vFile
);
758 VcsDirtyScopeManager
.getInstance(project
).fileDirty(file
);
761 catch (SVNException e
) {
762 exceptions
.add(new VcsException(e
));
765 if (!exceptions
.isEmpty()) {
766 vcsHelper
.showErrors(exceptions
, SvnBundle
.message("delete.files.errors.title"));
769 for (FilePath file
: deletedFiles
) {
770 final FilePath parent
= file
.getParentPath();
771 if (parent
!= null) {
772 myFilesToRefresh
.add(parent
.getVirtualFile());
775 if (filesToProcess
!= null) {
776 deletedFiles
.removeAll(filesToProcess
);
778 for (FilePath file
: deletedFiles
) {
779 FileUtil
.delete(file
.getIOFile());
784 private void processMovedFiles(final Project project
) {
785 for (Iterator
<MovedFileInfo
> iterator
= myMovedFiles
.iterator(); iterator
.hasNext();) {
786 MovedFileInfo movedFileInfo
= iterator
.next();
787 if (movedFileInfo
.myProject
== project
) {
788 doMove(SvnVcs
.getInstance(project
), movedFileInfo
.mySrc
, movedFileInfo
.myDst
);
795 private static SvnVcs
getVCS(VirtualFile file
) {
796 Project
[] projects
= ProjectManager
.getInstance().getOpenProjects();
797 for (Project project
: projects
) {
798 AbstractVcs vcs
= ProjectLevelVcsManager
.getInstance(project
).getVcsFor(file
);
799 if (vcs
instanceof SvnVcs
) {
807 private static File
getIOFile(VirtualFile vf
) {
808 return new File(vf
.getPath()).getAbsoluteFile();
812 private static SVNStatus
getFileStatus(File file
) {
813 final SVNClientManager clientManager
= SVNClientManager
.newInstance();
815 SVNStatusClient stClient
= clientManager
.getStatusClient();
816 return getFileStatus(file
, stClient
);
819 clientManager
.dispose();
824 private static SVNStatus
getFileStatus(SvnVcs vcs
, File file
) {
825 SVNStatusClient stClient
= vcs
.createStatusClient();
826 return getFileStatus(file
, stClient
);
830 private static SVNStatus
getFileStatus(final File file
, final SVNStatusClient stClient
) {
832 return stClient
.doStatus(file
, false);
834 catch (SVNException e
) {
839 private static boolean isUndoOrRedo(@NotNull final Project project
) {
840 final UndoManager undoManager
= UndoManager
.getInstance(project
);
841 return undoManager
.isUndoInProgress() || undoManager
.isRedoInProgress();
844 private static boolean isUndo(SvnVcs vcs
) {
845 if (vcs
== null || vcs
.getProject() == null) {
848 Project p
= vcs
.getProject();
849 return UndoManager
.getInstance(p
).isUndoInProgress();
852 public void afterDone(final ThrowableConsumer
<LocalFileOperationsHandler
, IOException
> invoker
) {