VCS: P4 <-> SVN: some intermediate state - taking from svn does not do svn delete...
[fedora-idea.git] / plugins / svn4idea / src / org / jetbrains / idea / svn / SvnFileSystemListener.java
blobcfea2e6b0467bbc7ebbdf616c13eed5599fb0c60
1 /**
2 * @copyright
3 * ====================================================================
4 * Copyright (c) 2003-2004 QintSoft. All rights reserved.
6 * This software is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at http://subversion.tigris.org/license-1.html.
9 * If newer versions of this license are posted there, you may use a
10 * newer version instead, at your option.
12 * This software consists of voluntary contributions made by many
13 * individuals. For exact contribution history, see the revision
14 * history and logs, available at http://svnup.tigris.org/.
15 * ====================================================================
16 * @endcopyright
19 * Copyright 2000-2005 JetBrains s.r.o.
21 * Licensed under the Apache License, Version 2.0 (the "License");
22 * you may not use this file except in compliance with the License.
23 * You may obtain a copy of the License at
25 * http://www.apache.org/licenses/LICENSE-2.0
27 * Unless required by applicable law or agreed to in writing, software
28 * distributed under the License is distributed on an "AS IS" BASIS,
29 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30 * See the License for the specific language governing permissions and
31 * limitations under the License.
33 package org.jetbrains.idea.svn;
35 import com.intellij.openapi.command.CommandEvent;
36 import com.intellij.openapi.command.CommandListener;
37 import com.intellij.openapi.command.undo.UndoManager;
38 import com.intellij.openapi.diagnostic.Logger;
39 import com.intellij.openapi.progress.ProgressManager;
40 import com.intellij.openapi.project.Project;
41 import com.intellij.openapi.project.ProjectManager;
42 import com.intellij.openapi.ui.DialogWrapper;
43 import com.intellij.openapi.util.Pair;
44 import com.intellij.openapi.util.io.FileUtil;
45 import com.intellij.openapi.vcs.*;
46 import com.intellij.openapi.vcs.actions.VcsContextFactory;
47 import com.intellij.openapi.vcs.changes.ChangeListManager;
48 import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
49 import com.intellij.openapi.vfs.LocalFileOperationsHandler;
50 import com.intellij.openapi.vfs.VirtualFile;
51 import com.intellij.openapi.vfs.newvfs.RefreshQueue;
52 import com.intellij.openapi.vfs.newvfs.RefreshSession;
53 import com.intellij.util.ThrowableConsumer;
54 import com.intellij.vcsUtil.ActionWithTempFile;
55 import org.jetbrains.annotations.NotNull;
56 import org.jetbrains.annotations.Nullable;
57 import org.jetbrains.idea.svn.dialogs.SelectIgnorePatternsToRemoveOnDeleteDialog;
58 import org.jetbrains.idea.svn.ignore.SvnPropertyService;
59 import org.tmatesoft.svn.core.SVNErrorCode;
60 import org.tmatesoft.svn.core.SVNException;
61 import org.tmatesoft.svn.core.SVNNodeKind;
62 import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
63 import org.tmatesoft.svn.core.wc.*;
65 import java.io.File;
66 import java.io.IOException;
67 import java.util.*;
69 public class SvnFileSystemListener implements LocalFileOperationsHandler, CommandListener {
70 private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnFileSystemListener");
72 private static class AddedFileInfo {
73 private final Project myProject;
74 private final VirtualFile myDir;
75 private final String myName;
76 @Nullable private final File myCopyFrom;
77 private final boolean myRecursive;
79 public AddedFileInfo(final Project project, final VirtualFile dir, final String name, @Nullable final File copyFrom, boolean recursive) {
80 myProject = project;
81 myDir = dir;
82 myName = name;
83 myCopyFrom = copyFrom;
84 myRecursive = recursive;
88 private static class DeletedFileInfo {
89 private final Project myProject;
90 private final File myFile;
92 public DeletedFileInfo(final Project project, final File file) {
93 myProject = project;
94 myFile = file;
98 private static class MovedFileInfo {
99 private final Project myProject;
100 private final File mySrc;
101 private final File myDst;
103 private MovedFileInfo(final Project project, final File src, final File dst) {
104 myProject = project;
105 mySrc = src;
106 myDst = dst;
110 private final Map<Project, Map<String, IgnoredFileInfo>> myIgnoredInfo = new HashMap<Project, Map<String, IgnoredFileInfo>>();
112 private final List<AddedFileInfo> myAddedFiles = new ArrayList<AddedFileInfo>();
113 private final List<DeletedFileInfo> myDeletedFiles = new ArrayList<DeletedFileInfo>();
114 private final List<MovedFileInfo> myMovedFiles = new ArrayList<MovedFileInfo>();
115 private final Map<Project, List<VcsException>> myMoveExceptions = new HashMap<Project, List<VcsException>>();
116 private final List<VirtualFile> myFilesToRefresh = new ArrayList<VirtualFile>();
117 @Nullable private File myStorageForUndo;
118 private final List<Pair<File, File>> myUndoStorageContents = new ArrayList<Pair<File, File>>();
119 private boolean myUndoingMove = false;
121 private void addToMoveExceptions(final Project project, final SVNException e) {
122 List<VcsException> exceptionList = myMoveExceptions.get(project);
123 if (exceptionList == null) {
124 exceptionList = new ArrayList<VcsException>();
125 myMoveExceptions.put(project, exceptionList);
127 VcsException vcsException;
128 if (SVNErrorCode.ENTRY_EXISTS.equals(e.getErrorMessage().getErrorCode())) {
129 vcsException = new VcsException(Arrays.asList("Target of move operation is already under version control.",
130 "Subversion move had not been performed. ", e.getMessage()));
131 } else {
132 vcsException = new VcsException(e);
134 exceptionList.add(vcsException);
137 @Nullable
138 public File copy(final VirtualFile file, final VirtualFile toDir, final String copyName) throws IOException {
139 SvnVcs vcs = getVCS(toDir);
140 if (vcs == null) {
141 vcs = getVCS(file);
143 if (vcs == null) {
144 return null;
147 File srcFile = new File(file.getPath());
148 File destFile = new File(new File(toDir.getPath()), copyName);
149 final boolean dstDirUnderControl = SVNWCUtil.isVersionedDirectory(destFile.getParentFile());
150 if ((! dstDirUnderControl) && !isPendingAdd(toDir)) {
151 return null;
154 if (!SVNWCUtil.isVersionedDirectory(srcFile.getParentFile())) {
155 myAddedFiles.add(new AddedFileInfo(vcs.getProject(), toDir, copyName, null, false));
156 return null;
159 final SVNStatus fileStatus = getFileStatus(vcs, srcFile);
160 if (fileStatus != null && fileStatus.getContentsStatus() == SVNStatusType.STATUS_ADDED) {
161 myAddedFiles.add(new AddedFileInfo(vcs.getProject(), toDir, copyName, null, false));
162 return null;
165 if (sameRoot(vcs, file.getParent(), toDir)) {
166 myAddedFiles.add(new AddedFileInfo(vcs.getProject(), toDir, copyName, srcFile, false));
167 return null;
170 myAddedFiles.add(new AddedFileInfo(vcs.getProject(), toDir, copyName, null, false));
171 return null;
174 private boolean sameRoot(final SvnVcs vcs, final VirtualFile srcDir, final VirtualFile dstDir) {
175 final UUIDHelper helper = new UUIDHelper(vcs);
176 final String srcUUID = helper.getRepositoryUUID(srcDir);
177 final String dstUUID = helper.getRepositoryUUID(dstDir);
179 return (srcUUID != null) && (dstUUID != null) && (srcUUID.equals(dstUUID));
182 private class UUIDHelper {
183 private final SVNWCClient myWcClient;
185 private UUIDHelper(final SvnVcs vcs) {
186 myWcClient = vcs.createWCClient();
190 * passed dir must be under VC control (it is assumed)
192 @Nullable
193 public String getRepositoryUUID(final VirtualFile dir) {
194 try {
195 final SVNInfo info1 = myWcClient.doInfo(new File(dir.getPath()), SVNRevision.WORKING);
196 if ((info1 == null) || (info1.getRepositoryUUID() == null)) {
197 // go deeper if current parent was added (if parent was added, it theoretically could NOT know its repo UUID)
198 final VirtualFile parent = dir.getParent();
199 if (parent == null) {
200 return null;
202 if (isPendingAdd(parent)) {
203 return getRepositoryUUID(parent);
205 } else {
206 return info1.getRepositoryUUID();
208 } catch (SVNException e) {
209 // go to return default
211 return null;
215 public boolean move(VirtualFile file, VirtualFile toDir) throws IOException {
216 File srcFile = getIOFile(file);
217 File dstFile = new File(getIOFile(toDir), file.getName());
219 final SvnVcs vcs = getVCS(toDir);
220 final SvnVcs sourceVcs = getVCS(file);
221 if (vcs == null && sourceVcs == null) return false;
223 if (vcs == null) {
224 return false;
226 if (sourceVcs == null) {
227 return createItem(toDir, file.getName(), file.isDirectory(), true);
230 if (isPendingAdd(toDir)) {
231 myMovedFiles.add(new MovedFileInfo(sourceVcs.getProject(), srcFile, dstFile));
232 return true;
234 else {
235 final VirtualFile oldParent = file.getParent();
236 myFilesToRefresh.add(oldParent);
237 myFilesToRefresh.add(toDir);
238 return doMove(sourceVcs, srcFile, dstFile);
242 public boolean rename(VirtualFile file, String newName) throws IOException {
243 File srcFile = getIOFile(file);
244 File dstFile = new File(srcFile.getParentFile(), newName);
245 SvnVcs vcs = getVCS(file);
246 if (vcs != null) {
247 myFilesToRefresh.add(file.getParent());
248 return doMove(vcs, srcFile, dstFile);
250 return false;
253 private boolean doMove(@NotNull SvnVcs vcs, final File src, final File dst) {
254 SVNMoveClient mover = vcs.createMoveClient();
255 long srcTime = src.lastModified();
256 try {
257 if (isUndo(vcs)) {
258 myUndoingMove = true;
259 restoreFromUndoStorage(dst);
260 mover.undoMove(src, dst);
262 else {
263 // if src is not under version control, do usual move.
264 SVNStatus srcStatus = getFileStatus(vcs, src);
265 if (srcStatus == null || srcStatus.getContentsStatus() == SVNStatusType.STATUS_UNVERSIONED ||
266 srcStatus.getContentsStatus() == SVNStatusType.STATUS_EXTERNAL ||
267 srcStatus.getContentsStatus() == SVNStatusType.STATUS_MISSING ||
268 srcStatus.getContentsStatus() == SVNStatusType.STATUS_OBSTRUCTED) {
269 return false;
271 // todo move back?
272 mover.doMove(src, dst);
274 dst.setLastModified(srcTime);
276 vcs.pathChanged(src, dst);
278 catch (SVNException e) {
279 addToMoveExceptions(vcs.getProject(), e);
280 return false;
282 return true;
285 private void restoreFromUndoStorage(final File dst) {
286 String normPath = FileUtil.toSystemIndependentName(dst.getPath());
287 for (Iterator<Pair<File, File>> it = myUndoStorageContents.iterator(); it.hasNext();) {
288 Pair<File, File> e = it.next();
289 final String p = FileUtil.toSystemIndependentName(e.first.getPath());
290 if (p.startsWith(normPath)) {
291 try {
292 FileUtil.rename(e.second, e.first);
294 catch (IOException ex) {
295 LOG.error(ex);
296 FileUtil.asyncDelete(e.second);
298 it.remove();
301 if (myStorageForUndo != null) {
302 final File[] files = myStorageForUndo.listFiles();
303 if (files == null || files.length == 0) {
304 FileUtil.asyncDelete(myStorageForUndo);
305 myStorageForUndo = null;
311 public boolean createFile(VirtualFile dir, String name) throws IOException {
312 return createItem(dir, name, false, false);
315 public boolean createDirectory(VirtualFile dir, String name) throws IOException {
316 return createItem(dir, name, true, false);
320 * delete file or directory (both 'undo' and 'do' modes)
321 * unversioned: do nothing, return false
322 * obstructed: do nothing, return false
323 * external or wc root: do nothing, return false
324 * missing: do nothing, return false
325 * <p/>
326 * versioned: schedule for deletion, return true
327 * added: schedule for deletion (make unversioned), return true
328 * copied, but not scheduled: schedule for deletion, return true
329 * replaced: schedule for deletion, return true
330 * <p/>
331 * deleted: do nothing, return true (strange)
333 public boolean delete(VirtualFile file) throws IOException {
334 SvnVcs vcs = getVCS(file);
335 if (vcs != null && SvnUtil.isAdminDirectory(file)) {
336 return true;
338 File ioFile = getIOFile(file);
339 if (!SVNWCUtil.isVersionedDirectory(ioFile.getParentFile())) {
340 return false;
342 else {
343 try {
344 if (SVNWCUtil.isWorkingCopyRoot(ioFile)) {
345 return false;
347 } catch (SVNException e) {
352 SVNStatus status = getFileStatus(ioFile);
354 if (status == null ||
355 status.getContentsStatus() == SVNStatusType.STATUS_UNVERSIONED ||
356 status.getContentsStatus() == SVNStatusType.STATUS_OBSTRUCTED ||
357 status.getContentsStatus() == SVNStatusType.STATUS_MISSING ||
358 status.getContentsStatus() == SVNStatusType.STATUS_EXTERNAL) {
359 return false;
360 } else if (status.getContentsStatus() == SVNStatusType.STATUS_IGNORED) {
361 if (vcs != null && (file.getParent() != null)) {
362 if (! isUndoOrRedo(vcs.getProject())) {
363 putIgnoreInfo(file, vcs, ioFile);
366 return false;
368 else if (status.getContentsStatus() == SVNStatusType.STATUS_DELETED) {
369 if (isUndo(vcs)) {
370 moveToUndoStorage(file);
372 return true;
374 else {
375 if (vcs != null) {
376 if (status.getContentsStatus() == SVNStatusType.STATUS_ADDED) {
377 try {
378 final SVNWCClient wcClient = vcs.createWCClient();
379 wcClient.doRevert(ioFile, false);
381 catch (SVNException e) {
382 // ignore
385 else {
386 myDeletedFiles.add(new DeletedFileInfo(vcs.getProject(), ioFile));
387 // packages deleted from disk should not be deleted from svn (IDEADEV-16066)
388 if (file.isDirectory() || isUndo(vcs)) return true;
391 return false;
395 private void putIgnoreInfo(VirtualFile file, SvnVcs vcs, File ioFile) {
396 final String key = file.getParent().getPresentableUrl();
397 Map<String, IgnoredFileInfo> map = myIgnoredInfo.get(vcs.getProject());
398 if (map != null) {
399 final IgnoredFileInfo info = map.get(key);
400 if (info != null) {
401 info.addFileName(file.getName());
402 return;
405 final Set<String> existingPatterns = SvnPropertyService.getIgnoreStringsUnder(vcs, file.getParent());
406 if (existingPatterns != null) {
407 if (map == null) {
408 map = new HashMap<String, IgnoredFileInfo>();
409 myIgnoredInfo.put(vcs.getProject(), map);
411 final File parentIo = ioFile.getParentFile();
412 final IgnoredFileInfo info = new IgnoredFileInfo(parentIo, existingPatterns);
413 info.addFileName(file.getName());
414 map.put(key, info);
418 private void moveToUndoStorage(final VirtualFile file) {
419 if (myStorageForUndo == null) {
420 try {
421 myStorageForUndo = FileUtil.createTempDirectory("svnUndoStorage", "");
423 catch (IOException e) {
424 LOG.error(e);
425 return;
428 final File tmpFile = FileUtil.findSequentNonexistentFile(myStorageForUndo, "tmp", "");
429 myUndoStorageContents.add(0, new Pair<File, File>(new File(file.getPath()), tmpFile));
430 new File(file.getPath()).renameTo(tmpFile);
434 * add file or directory:
435 * <p/>
436 * parent directory is:
437 * unversioned: do nothing, return false
438 * versioned:
439 * entry is:
440 * null: create entry, schedule for addition
441 * missing: do nothing, return false
442 * deleted, 'do' mode: try to create entry and it schedule for addition if kind is the same, otherwise do nothing, return false.
443 * deleted: 'undo' mode: try to revert non-recursively, if kind is the same, otherwise do nothing, return false.
444 * anything else: return false.
446 private boolean createItem(VirtualFile dir, String name, boolean directory, final boolean recursive) throws IOException {
447 SvnVcs vcs = getVCS(dir);
448 if (vcs == null) {
449 return false;
451 if (isUndo(vcs) && SvnUtil.isAdminDirectory(dir, name)) {
452 return false;
454 File ioDir = getIOFile(dir);
455 boolean pendingAdd = isPendingAdd(dir);
456 if (!SVNWCUtil.isVersionedDirectory(ioDir) && !pendingAdd) {
457 return false;
459 SVNWCClient wcClient = vcs.createWCClient();
460 File targetFile = new File(ioDir, name);
461 SVNStatus status = getFileStatus(vcs, targetFile);
463 if (status == null || status.getContentsStatus() == SVNStatusType.STATUS_NONE) {
464 myAddedFiles.add(new AddedFileInfo(vcs.getProject(), dir, name, null, recursive));
465 return false;
467 else if (status.getContentsStatus() == SVNStatusType.STATUS_MISSING) {
468 return false;
470 else if (status.getContentsStatus() == SVNStatusType.STATUS_DELETED) {
471 SVNNodeKind kind = status.getKind();
472 // kind differs.
473 if ((directory && kind != SVNNodeKind.DIR) || (!directory && kind != SVNNodeKind.FILE)) {
474 return false;
476 try {
477 if (isUndo(vcs)) {
478 wcClient.doRevert(targetFile, false);
479 return true;
481 myAddedFiles.add(new AddedFileInfo(vcs.getProject(), dir, name, null, recursive));
482 return false;
484 catch (SVNException e) {
485 SVNFileUtil.deleteAll(targetFile, true);
486 return false;
489 return false;
492 private boolean isPendingAdd(final VirtualFile dir) {
493 for(AddedFileInfo i: myAddedFiles) {
494 if (i.myDir == dir.getParent() && i.myName.equals(dir.getName())) {
495 return true;
498 return false;
501 public void commandStarted(CommandEvent event) {
502 myUndoingMove = false;
503 final Project project = event.getProject();
504 if (project == null) return;
505 commandStarted(project);
508 void commandStarted(final Project project) {
509 myUndoingMove = false;
510 myMoveExceptions.remove(project);
511 myIgnoredInfo.remove(project);
514 public void beforeCommandFinished(CommandEvent event) {
517 public void commandFinished(CommandEvent event) {
518 final Project project = event.getProject();
519 if (project == null) return;
520 commandFinished(project);
523 void commandFinished(final Project project) {
524 if (myAddedFiles.size() > 0) {
525 processAddedFiles(project);
527 if (myDeletedFiles.size() > 0) {
528 processDeletedFiles(project);
530 processMovedFiles(project);
532 final List<VcsException> exceptionList = myMoveExceptions.get(project);
533 if ((exceptionList != null) && (! exceptionList.isEmpty())) {
534 AbstractVcsHelper.getInstance(project).showErrors(exceptionList, SvnBundle.message("move.files.errors.title"));
537 dealWithIgnorePatterns(project);
539 if (myFilesToRefresh.size() > 0) {
540 refreshFiles(project);
544 private void dealWithIgnorePatterns(Project project) {
545 final Map<String, IgnoredFileInfo> map = myIgnoredInfo.get(project);
546 if (map != null) {
547 final SvnVcs vcs = SvnVcs.getInstance(project);
548 final ProgressManager progressManager = ProgressManager.getInstance();
549 final Runnable prepare = new Runnable() {
550 public void run() {
551 for (Iterator<String> iterator = map.keySet().iterator(); iterator.hasNext();) {
552 final String key = iterator.next();
553 final IgnoredFileInfo info = map.get(key);
554 info.calculatePatterns(vcs);
555 if (info.getPatterns().isEmpty()) {
556 iterator.remove();
561 progressManager.runProcessWithProgressSynchronously(prepare, SvnBundle.message("gather.ignore.patterns.info.progress.title"), false, project);
562 if (map.isEmpty()) return;
564 final SelectIgnorePatternsToRemoveOnDeleteDialog dialog = new SelectIgnorePatternsToRemoveOnDeleteDialog(project, map);
565 dialog.show();
566 final Collection<IgnoredFileInfo> result = dialog.getResult();
567 if ((dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) && (! result.isEmpty())) {
568 final List<VcsException> exceptions = new ArrayList<VcsException>(0);
570 final Runnable deletePatterns = new Runnable() {
571 public void run() {
572 for (IgnoredFileInfo info : result) {
573 try {
574 info.getOldPatterns().removeAll(info.getPatterns());
575 SvnPropertyService.setIgnores(vcs, info.getOldPatterns(), info.getFile());
577 catch (SVNException e) {
578 exceptions.add(new VcsException(e));
583 progressManager.runProcessWithProgressSynchronously(deletePatterns, "Removing selected 'svn:ignore' patterns", false, project);
584 if (! exceptions.isEmpty()) {
585 AbstractVcsHelper.getInstance(project).showErrors(exceptions, SvnBundle.message("remove.ignore.patterns.errors.title"));
591 private void refreshFiles(final Project project) {
592 final List<VirtualFile> toRefreshFiles = new ArrayList<VirtualFile>();
593 final List<VirtualFile> toRefreshDirs = new ArrayList<VirtualFile>();
594 for (VirtualFile file : myFilesToRefresh) {
595 if (file == null) continue;
596 if (file.isDirectory()) {
597 toRefreshDirs.add(file);
598 } else {
599 toRefreshFiles.add(file);
602 // if refresh asynchronously, local changes would also be notified that they are dirty asynchronously,
603 // and commit could be executed while not all changes are visible
604 final RefreshSession session = RefreshQueue.getInstance().createSession(false, true, new Runnable() {
605 public void run() {
606 if (project.isDisposed()) return;
607 filterOutInvalid(toRefreshFiles);
608 filterOutInvalid(toRefreshDirs);
610 final VcsDirtyScopeManager vcsDirtyScopeManager = VcsDirtyScopeManager.getInstance(project);
611 vcsDirtyScopeManager.filesDirty(toRefreshFiles, toRefreshDirs);
614 filterOutInvalid(myFilesToRefresh);
615 session.addAllFiles(myFilesToRefresh);
616 session.launch();
617 myFilesToRefresh.clear();
620 private void filterOutInvalid(final Collection<VirtualFile> files) {
621 for (Iterator<VirtualFile> iterator = files.iterator(); iterator.hasNext();) {
622 final VirtualFile file = iterator.next();
623 if ((! file.isValid()) || (! file.exists())) {
624 LOG.info("Refresh root is not valid: " + file.getPath());
625 iterator.remove();
630 private void processAddedFiles(Project project) {
631 SvnVcs vcs = SvnVcs.getInstance(project);
632 List<VirtualFile> addedVFiles = new ArrayList<VirtualFile>();
633 Map<VirtualFile, File> copyFromMap = new HashMap<VirtualFile, File>();
634 final Set<VirtualFile> recursiveItems = new HashSet<VirtualFile>();
635 for (Iterator<AddedFileInfo> it = myAddedFiles.iterator(); it.hasNext();) {
636 AddedFileInfo addedFileInfo = it.next();
637 if (addedFileInfo.myProject == project) {
638 it.remove();
639 VirtualFile addedFile = addedFileInfo.myDir.findChild(addedFileInfo.myName);
640 if (addedFile != null) {
641 final SVNStatus fileStatus = getFileStatus(vcs, new File(getIOFile(addedFileInfo.myDir), addedFileInfo.myName));
642 if (fileStatus == null || fileStatus.getContentsStatus() != SVNStatusType.STATUS_IGNORED) {
643 boolean isIgnored = ChangeListManager.getInstance(addedFileInfo.myProject).isIgnoredFile(addedFile);
644 if (!isIgnored) {
645 addedVFiles.add(addedFile);
646 copyFromMap.put(addedFile, addedFileInfo.myCopyFrom);
647 if (addedFileInfo.myRecursive) {
648 recursiveItems.add(addedFile);
655 if (addedVFiles.size() == 0) return;
656 final VcsShowConfirmationOption.Value value = vcs.getAddConfirmation().getValue();
657 if (value != VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY) {
658 final AbstractVcsHelper vcsHelper = AbstractVcsHelper.getInstance(project);
659 Collection<VirtualFile> filesToProcess;
660 if (value == VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY) {
661 filesToProcess = addedVFiles;
663 else {
664 final String singleFilePrompt;
665 if (addedVFiles.size() == 1 && addedVFiles.get(0).isDirectory()) {
666 singleFilePrompt = SvnBundle.message("confirmation.text.add.dir");
668 else {
669 singleFilePrompt = SvnBundle.message("confirmation.text.add.file");
671 filesToProcess = vcsHelper.selectFilesToProcess(addedVFiles, SvnBundle.message("confirmation.title.add.multiple.files"),
672 null,
673 SvnBundle.message("confirmation.title.add.file"), singleFilePrompt,
674 vcs.getAddConfirmation());
676 if (filesToProcess != null) {
677 final List<VcsException> exceptions = new ArrayList<VcsException>();
678 SVNWCClient wcClient = vcs.createWCClient();
679 final SVNCopyClient copyClient = vcs.createCopyClient();
680 for(VirtualFile file: filesToProcess) {
681 final File ioFile = new File(file.getPath());
682 try {
683 final File copyFrom = copyFromMap.get(file);
684 if (copyFrom != null) {
685 try {
686 new ActionWithTempFile(ioFile) {
687 protected void executeInternal() throws VcsException {
688 try {
689 // not recursive
690 final SVNCopySource[] copySource = new SVNCopySource[]
691 {new SVNCopySource(SVNRevision.WORKING, SVNRevision.WORKING, copyFrom)};
692 copyClient.doCopy(copySource, ioFile, false, true, true);
694 catch (SVNException e) {
695 throw new VcsException(e);
698 }.execute();
700 catch (VcsException e) {
701 exceptions.add(e);
704 else {
705 wcClient.doAdd(ioFile, true, false, false, recursiveItems.contains(file));
707 VcsDirtyScopeManager.getInstance(project).fileDirty(file);
709 catch (SVNException e) {
710 exceptions.add(new VcsException(e));
713 if (!exceptions.isEmpty()) {
714 vcsHelper.showErrors(exceptions, SvnBundle.message("add.files.errors.title"));
720 private void processDeletedFiles(Project project) {
721 final List<FilePath> deletedFiles = new ArrayList<FilePath>();
722 for (Iterator<DeletedFileInfo> it = myDeletedFiles.iterator(); it.hasNext();) {
723 DeletedFileInfo deletedFileInfo = it.next();
724 if (deletedFileInfo.myProject == project) {
725 it.remove();
726 final FilePath filePath = VcsContextFactory.SERVICE.getInstance().createFilePathOn(deletedFileInfo.myFile);
727 deletedFiles.add(filePath);
730 if (deletedFiles.size() == 0 || myUndoingMove) return;
731 SvnVcs vcs = SvnVcs.getInstance(project);
732 final VcsShowConfirmationOption.Value value = vcs.getDeleteConfirmation().getValue();
733 if (value != VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY) {
734 final AbstractVcsHelper vcsHelper = AbstractVcsHelper.getInstance(project);
735 Collection<FilePath> filesToProcess;
736 if (value == VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY) {
737 filesToProcess = new ArrayList<FilePath>(deletedFiles);
739 else {
741 final String singleFilePrompt;
742 if (deletedFiles.size() == 1 && deletedFiles.get(0).isDirectory()) {
743 singleFilePrompt = SvnBundle.message("confirmation.text.delete.dir");
745 else {
746 singleFilePrompt = SvnBundle.message("confirmation.text.delete.file");
748 final Collection<FilePath> files = vcsHelper
749 .selectFilePathsToProcess(deletedFiles, SvnBundle.message("confirmation.title.delete.multiple.files"), null,
750 SvnBundle.message("confirmation.title.delete.file"), singleFilePrompt, vcs.getDeleteConfirmation());
751 filesToProcess = (files == null) ? null : new ArrayList<FilePath>(files);
753 if (filesToProcess != null) {
754 List<VcsException> exceptions = new ArrayList<VcsException>();
755 SVNWCClient wcClient = vcs.createWCClient();
756 for(FilePath file: filesToProcess) {
757 VirtualFile vFile = file.getVirtualFile(); // for deleted directories
758 File ioFile = new File(file.getPath());
759 try {
760 wcClient.doDelete(ioFile, true, false);
761 if (vFile != null && vFile.isValid() && vFile.isDirectory()) {
762 vFile.refresh(true, true);
763 VcsDirtyScopeManager.getInstance(project).dirDirtyRecursively(vFile);
765 else {
766 VcsDirtyScopeManager.getInstance(project).fileDirty(file);
769 catch (SVNException e) {
770 exceptions.add(new VcsException(e));
773 if (!exceptions.isEmpty()) {
774 vcsHelper.showErrors(exceptions, SvnBundle.message("delete.files.errors.title"));
777 for (FilePath file : deletedFiles) {
778 final FilePath parent = file.getParentPath();
779 if (parent != null) {
780 myFilesToRefresh.add(parent.getVirtualFile());
783 if (filesToProcess != null) {
784 deletedFiles.removeAll(filesToProcess);
786 for (FilePath file : deletedFiles) {
787 FileUtil.delete(file.getIOFile());
792 private void processMovedFiles(final Project project) {
793 for (Iterator<MovedFileInfo> iterator = myMovedFiles.iterator(); iterator.hasNext();) {
794 MovedFileInfo movedFileInfo = iterator.next();
795 if (movedFileInfo.myProject == project) {
796 doMove(SvnVcs.getInstance(project), movedFileInfo.mySrc, movedFileInfo.myDst);
797 iterator.remove();
802 public void undoTransparentActionStarted() {
805 public void undoTransparentActionFinished() {
808 @Nullable
809 private static SvnVcs getVCS(VirtualFile file) {
810 Project[] projects = ProjectManager.getInstance().getOpenProjects();
811 for (Project project : projects) {
812 AbstractVcs vcs = ProjectLevelVcsManager.getInstance(project).getVcsFor(file);
813 if (vcs instanceof SvnVcs) {
814 return (SvnVcs)vcs;
817 return null;
821 private static File getIOFile(VirtualFile vf) {
822 return new File(vf.getPath()).getAbsoluteFile();
825 @Nullable
826 private static SVNStatus getFileStatus(File file) {
827 final SVNClientManager clientManager = SVNClientManager.newInstance();
828 try {
829 SVNStatusClient stClient = clientManager.getStatusClient();
830 return getFileStatus(file, stClient);
832 finally {
833 clientManager.dispose();
837 @Nullable
838 private static SVNStatus getFileStatus(SvnVcs vcs, File file) {
839 SVNStatusClient stClient = vcs.createStatusClient();
840 return getFileStatus(file, stClient);
843 @Nullable
844 private static SVNStatus getFileStatus(final File file, final SVNStatusClient stClient) {
845 try {
846 return stClient.doStatus(file, false);
848 catch (SVNException e) {
849 return null;
853 private static boolean isUndoOrRedo(@NotNull final Project project) {
854 final UndoManager undoManager = UndoManager.getInstance(project);
855 return undoManager.isUndoInProgress() || undoManager.isRedoInProgress();
858 private static boolean isUndo(SvnVcs vcs) {
859 if (vcs == null || vcs.getProject() == null) {
860 return false;
862 Project p = vcs.getProject();
863 return UndoManager.getInstance(p).isUndoInProgress();
866 public void afterDone(final ThrowableConsumer<LocalFileOperationsHandler, IOException> invoker) {