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
;
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
) {
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));
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("/", "\\"));
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() {
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());
126 myGroupWorkers
= Arrays
.asList(new MyTextConflictWorker(), new MyConflictWorker(FileGroup
.MERGED_WITH_PROPERTY_CONFLICT_ID
) {
127 protected List
<VirtualFile
> merge() {
130 }, new MyTreeConflictWorker(), new MyReplacedWorker());
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();
145 myDirtyScopeManager
.filesDirty(vfColl
, null);
148 public void onRefreshFilesCompleted() {
151 for (Runnable groupWorker
: myGroupWorkers
) {
156 // not a conflict worker; to correctly show replaced items
157 private class MyReplacedWorker
implements Runnable
{
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
{
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
);
189 vf
= lfs
.refreshAndFindFileByIoFile(parent
);
197 if (! parents
.isEmpty()) {
198 final RefreshQueue refreshQueue
= RefreshQueue
.getInstance();
199 final RefreshSession session
= refreshQueue
.createSession(true, true, new Runnable() {
201 myDirtyScopeManager
.filesDirty(parents
, null);
204 session
.addAllFiles(parents
);
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());
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
))) {
243 final ReadonlyStatusHandler
.OperationStatus operationStatus
=
244 ReadonlyStatusHandler
.getInstance(myVcs
.getProject()).ensureFilesWritable(writeable
);
245 writeable
.removeAll(Arrays
.asList(operationStatus
.getReadonlyFiles()));
251 protected abstract List
<VirtualFile
> merge();
254 fillAndRefreshFiles();
255 if (! myFiles
.isEmpty()) {
256 final List
<VirtualFile
> merged
= merge();
257 if (merged
!= null && (! merged
.isEmpty())) {
258 moveToMergedGroup(merged
);
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
);
287 vf
= myLfs
.refreshAndFindFileByIoFile(file
);
291 final VirtualFile parent
= vf
.getParent();
292 if (parent
!= null) {
299 if (! myFiles
.isEmpty()) {
300 final RefreshQueue refreshQueue
= RefreshQueue
.getInstance();
301 final RefreshSession session
= refreshQueue
.createSession(true, true, null);
302 session
.addAllFiles(parents
);
305 myDirtyScopeManager
.filesDirty(myFiles
, null);
309 public String
getGroupId() {
315 protected boolean isDryRun() {
319 protected abstract AbstractUpdateIntegrateCrawler
createCrawler(ISVNEventHandler eventHandler
,
321 ArrayList
<VcsException
> exceptions
, UpdatedFiles updatedFiles
);
324 public abstract Configurable
createConfigurable(Collection
<FilePath
> collection
);