IDEA-26360 (Performance and inconsistency issues with svn:externals and "Detect neste...
[fedora-idea.git] / plugins / svn4idea / src / org / jetbrains / idea / svn / update / AbstractSvnUpdateIntegrateEnvironment.java
bloba54115fe405adc2a0e5bb3aa5237c5f7ec02fc09
1 /*
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.
16 package org.jetbrains.idea.svn.update;
18 import com.intellij.openapi.application.ApplicationManager;
19 import com.intellij.openapi.options.Configurable;
20 import com.intellij.openapi.progress.ProcessCanceledException;
21 import com.intellij.openapi.progress.ProgressIndicator;
22 import com.intellij.openapi.ui.Messages;
23 import com.intellij.openapi.util.Ref;
24 import com.intellij.openapi.util.SystemInfo;
25 import com.intellij.openapi.util.io.FileUtil;
26 import com.intellij.openapi.vcs.*;
27 import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
28 import com.intellij.openapi.vcs.history.VcsRevisionNumber;
29 import com.intellij.openapi.vcs.update.*;
30 import com.intellij.openapi.vfs.LocalFileSystem;
31 import com.intellij.openapi.vfs.ReadonlyStatusHandler;
32 import com.intellij.openapi.vfs.VirtualFile;
33 import com.intellij.openapi.vfs.newvfs.RefreshQueue;
34 import com.intellij.openapi.vfs.newvfs.RefreshSession;
35 import org.jetbrains.annotations.NonNls;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.annotations.Nullable;
38 import org.jetbrains.idea.svn.SvnBundle;
39 import org.jetbrains.idea.svn.SvnUtil;
40 import org.jetbrains.idea.svn.SvnVcs;
41 import org.jetbrains.idea.svn.actions.SvnMergeProvider;
42 import org.tmatesoft.svn.core.wc.ISVNEventHandler;
44 import java.io.File;
45 import java.util.*;
47 public abstract class AbstractSvnUpdateIntegrateEnvironment implements UpdateEnvironment {
48 protected final SvnVcs myVcs;
49 private final ProjectLevelVcsManager myVcsManager;
50 @NonNls public static final String REPLACED_ID = "replaced";
51 @NonNls public static final String EXTERNAL_ID = "external";
53 protected AbstractSvnUpdateIntegrateEnvironment(final SvnVcs vcs) {
54 myVcs = vcs;
55 myVcsManager = ProjectLevelVcsManager.getInstance(vcs.getProject());
58 public void fillGroups(UpdatedFiles updatedFiles) {
59 updatedFiles.registerGroup(new FileGroup(VcsBundle.message("update.group.name.merged.with.property.conflicts"),
60 VcsBundle.message("status.group.name.will.be.merged.with.property.conflicts"), false,
61 FileGroup.MERGED_WITH_PROPERTY_CONFLICT_ID, false));
62 updatedFiles.registerGroup(new FileGroup(VcsBundle.message("update.group.name.merged.with.tree.conflicts"),
63 VcsBundle.message("status.group.name.will.be.merged.with.tree.conflicts"), false,
64 SvnUpdateGroups.MERGED_WITH_TREE_CONFLICT, false));
67 @NotNull
68 public UpdateSession updateDirectories(@NotNull final FilePath[] contentRoots,
69 final UpdatedFiles updatedFiles,
70 final ProgressIndicator progressIndicator, @NotNull final Ref<SequentialUpdatesContext> context)
71 throws ProcessCanceledException {
73 if (context.isNull()) {
74 context.set(new SvnUpdateContext(myVcs));
77 final ArrayList<VcsException> exceptions = new ArrayList<VcsException>();
78 UpdateEventHandler eventHandler = new UpdateEventHandler(myVcs, progressIndicator, (SvnUpdateContext) context.get());
79 eventHandler.setUpdatedFiles(updatedFiles);
81 boolean totalUpdate = true;
82 AbstractUpdateIntegrateCrawler crawler = createCrawler(eventHandler, totalUpdate, exceptions, updatedFiles);
84 Collection<File> updatedRoots = new HashSet<File>();
85 Arrays.sort(contentRoots, new Comparator<FilePath>() {
86 public int compare(FilePath o1, FilePath o2) {
87 return SystemInfo.isFileSystemCaseSensitive ? o1.getPath().replace("/", "\\").compareTo(o2.getPath().replace("/", "\\")) :
88 o1.getPath().replace("/", "\\").compareToIgnoreCase(o2.getPath().replace("/", "\\"));
90 });
91 for (FilePath contentRoot : contentRoots) {
92 if (progressIndicator != null && progressIndicator.isCanceled()) {
93 throw new ProcessCanceledException();
95 final File ioRoot = contentRoot.getIOFile();
96 if (! ((SvnUpdateContext)context.get()).shouldRunFor(ioRoot)) continue;
97 Collection<File> roots = SvnUtil.crawlWCRoots(ioRoot, crawler, progressIndicator);
98 updatedRoots.addAll(roots);
100 if (updatedRoots.isEmpty()) {
101 ApplicationManager.getApplication().invokeLater(new Runnable() {
102 public void run() {
103 Messages.showErrorDialog(myVcs.getProject(), SvnBundle.message("message.text.update.no.directories.found"),
104 SvnBundle.message("messate.text.update.error"));
107 return new UpdateSessionAdapter(Collections.<VcsException>emptyList(), true);
110 return new MyUpdateSessionAdapter(contentRoots, updatedFiles, exceptions);
113 private class MyUpdateSessionAdapter extends UpdateSessionAdapter {
114 private final FilePath[] myContentRoots;
115 private final UpdatedFiles myUpdatedFiles;
116 private final VcsDirtyScopeManager myDirtyScopeManager;
117 private final List<Runnable> myGroupWorkers;
119 private MyUpdateSessionAdapter(@NotNull final FilePath[] contentRoots, final UpdatedFiles updatedFiles, final List<VcsException> exceptions) {
120 super(exceptions, false);
121 myContentRoots = contentRoots;
122 myUpdatedFiles = updatedFiles;
123 myDirtyScopeManager = VcsDirtyScopeManager.getInstance(myVcs.getProject());
125 if (! isDryRun()) {
126 myGroupWorkers = Arrays.asList(new MyTextConflictWorker(), new MyConflictWorker(FileGroup.MERGED_WITH_PROPERTY_CONFLICT_ID) {
127 protected List<VirtualFile> merge() {
128 return null;
130 }, new MyTreeConflictWorker(), new MyReplacedWorker());
131 } else {
132 myGroupWorkers = Collections.emptyList();
136 // update switched/ignored status of directories
137 private void dirtyRoots() {
138 final Collection<VirtualFile> vfColl = new ArrayList<VirtualFile>(myContentRoots.length);
139 for (FilePath contentRoot: myContentRoots) {
140 final VirtualFile vf = contentRoot.getVirtualFile();
141 if (vf != null) {
142 vfColl.add(vf);
145 myDirtyScopeManager.filesDirty(vfColl, null);
148 public void onRefreshFilesCompleted() {
149 dirtyRoots();
151 for (Runnable groupWorker : myGroupWorkers) {
152 groupWorker.run();
156 // not a conflict worker; to correctly show replaced items
157 private class MyReplacedWorker implements Runnable {
158 public void run() {
159 final FileGroup replacedGroup = myUpdatedFiles.getGroupById(REPLACED_ID);
160 final FileGroup deletedGroup = myUpdatedFiles.getGroupById(FileGroup.REMOVED_FROM_REPOSITORY_ID);
161 if ((deletedGroup != null) && (replacedGroup != null) && (! deletedGroup.isEmpty()) && (! replacedGroup.isEmpty())) {
162 final Set<String> replacedFiles = new HashSet<String>(replacedGroup.getFiles());
163 final Collection<String> deletedFiles = new HashSet<String>(deletedGroup.getFiles());
165 for (String deletedFile : deletedFiles) {
166 if (replacedFiles.contains(deletedFile)) {
167 deletedGroup.remove(deletedFile);
174 // at the moment no resolve, only refresh files & statuses
175 private class MyTreeConflictWorker implements Runnable {
176 public void run() {
177 final LocalFileSystem lfs = LocalFileSystem.getInstance();
178 final FileGroup conflictedGroup = myUpdatedFiles.getGroupById(SvnUpdateGroups.MERGED_WITH_TREE_CONFLICT);
179 final Collection<String> conflictedFiles = conflictedGroup.getFiles();
180 final Collection<VirtualFile> parents = new ArrayList<VirtualFile>();
182 if ((conflictedFiles != null) && (! conflictedFiles.isEmpty())) {
183 for (final String conflictedFile : conflictedFiles) {
184 final File file = new File(conflictedFile);
185 final File parent = file.getParentFile();
187 VirtualFile vf = lfs.findFileByIoFile(parent);
188 if (vf == null) {
189 vf = lfs.refreshAndFindFileByIoFile(parent);
191 if (vf != null) {
192 parents.add(vf);
197 if (! parents.isEmpty()) {
198 final RefreshQueue refreshQueue = RefreshQueue.getInstance();
199 final RefreshSession session = refreshQueue.createSession(true, true, new Runnable() {
200 public void run() {
201 myDirtyScopeManager.filesDirty(parents, null);
204 session.addAllFiles(parents);
205 session.launch();
210 private class MyTextConflictWorker extends MyConflictWorker {
211 private MyTextConflictWorker() {
212 super(FileGroup.MERGED_WITH_CONFLICT_ID);
215 protected List<VirtualFile> merge() {
216 final List<VirtualFile> writeable = prepareWriteable(myFiles);
217 final AbstractVcsHelper vcsHelper = AbstractVcsHelper.getInstance(myVcs.getProject());
218 return vcsHelper.showMergeDialog(writeable, new SvnMergeProvider(myVcs.getProject()));
222 private abstract class MyConflictWorker implements Runnable {
223 private final String groupId;
224 protected final List<VirtualFile> myFiles;
225 private LocalFileSystem myLfs;
226 private final ProjectLevelVcsManager myPlVcsManager;
228 protected MyConflictWorker(final String groupId) {
229 this.groupId = groupId;
230 myFiles = new ArrayList<VirtualFile>();
231 myLfs = LocalFileSystem.getInstance();
232 myPlVcsManager = ProjectLevelVcsManager.getInstance(myVcs.getProject());
235 // for reuse
236 protected List<VirtualFile> prepareWriteable(final Collection<VirtualFile> files) {
237 final List<VirtualFile> writeable = new ArrayList<VirtualFile>();
238 for (VirtualFile vf : files) {
239 if (myVcs.equals(myPlVcsManager.getVcsFor(vf))) {
240 writeable.add(vf);
243 final ReadonlyStatusHandler.OperationStatus operationStatus =
244 ReadonlyStatusHandler.getInstance(myVcs.getProject()).ensureFilesWritable(writeable);
245 writeable.removeAll(Arrays.asList(operationStatus.getReadonlyFiles()));
247 return writeable;
250 @Nullable
251 protected abstract List<VirtualFile> merge();
253 public void run() {
254 fillAndRefreshFiles();
255 if (! myFiles.isEmpty()) {
256 final List<VirtualFile> merged = merge();
257 if (merged != null && (! merged.isEmpty())) {
258 moveToMergedGroup(merged);
260 // do we need this
261 myDirtyScopeManager.filesDirty(merged, null);
266 protected void moveToMergedGroup(final List<VirtualFile> merged) {
267 final FileGroup conflictedGroup = myUpdatedFiles.getGroupById(groupId);
268 FileGroup mergedGroup = myUpdatedFiles.getGroupById(FileGroup.MERGED_ID);
269 for (VirtualFile mergedFile: merged) {
270 final String path = FileUtil.toSystemDependentName(mergedFile.getPresentableUrl());
271 final VcsRevisionNumber revision = conflictedGroup.getRevision(myVcsManager, path);
272 conflictedGroup.remove(path);
273 mergedGroup.add(path, myVcs.getKeyInstanceMethod(), revision);
277 protected void fillAndRefreshFiles() {
278 final FileGroup conflictedGroup = myUpdatedFiles.getGroupById(groupId);
279 final Collection<String> conflictedFiles = conflictedGroup.getFiles();
280 final Collection<VirtualFile> parents = new ArrayList<VirtualFile>();
282 if ((conflictedFiles != null) && (! conflictedFiles.isEmpty())) {
283 for (final String conflictedFile : conflictedFiles) {
284 final File file = new File(conflictedFile);
285 VirtualFile vf = myLfs.findFileByIoFile(file);
286 if (vf == null) {
287 vf = myLfs.refreshAndFindFileByIoFile(file);
289 if (vf != null) {
290 myFiles.add(vf);
291 final VirtualFile parent = vf.getParent();
292 if (parent != null) {
293 parents.add(parent);
299 if (! myFiles.isEmpty()) {
300 final RefreshQueue refreshQueue = RefreshQueue.getInstance();
301 final RefreshSession session = refreshQueue.createSession(true, true, null);
302 session.addAllFiles(parents);
303 session.launch();
305 myDirtyScopeManager.filesDirty(myFiles, null);
309 public String getGroupId() {
310 return groupId;
315 protected boolean isDryRun() {
316 return false;
319 protected abstract AbstractUpdateIntegrateCrawler createCrawler(ISVNEventHandler eventHandler,
320 boolean totalUpdate,
321 ArrayList<VcsException> exceptions, UpdatedFiles updatedFiles);
323 @Nullable
324 public abstract Configurable createConfigurable(Collection<FilePath> collection);