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 * ====================================================================
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
.ide
.FrameStateListener
;
36 import com
.intellij
.ide
.FrameStateManager
;
37 import com
.intellij
.openapi
.actionSystem
.AnAction
;
38 import com
.intellij
.openapi
.application
.Application
;
39 import com
.intellij
.openapi
.application
.ApplicationManager
;
40 import com
.intellij
.openapi
.diagnostic
.Logger
;
41 import com
.intellij
.openapi
.options
.Configurable
;
42 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
43 import com
.intellij
.openapi
.progress
.ProgressManager
;
44 import com
.intellij
.openapi
.project
.DumbAwareRunnable
;
45 import com
.intellij
.openapi
.project
.Project
;
46 import com
.intellij
.openapi
.startup
.StartupManager
;
47 import com
.intellij
.openapi
.ui
.DialogWrapper
;
48 import com
.intellij
.openapi
.ui
.Messages
;
49 import com
.intellij
.openapi
.util
.Pair
;
50 import com
.intellij
.openapi
.util
.SystemInfo
;
51 import com
.intellij
.openapi
.util
.Trinity
;
52 import com
.intellij
.openapi
.vcs
.*;
53 import com
.intellij
.openapi
.vcs
.annotate
.AnnotationProvider
;
54 import com
.intellij
.openapi
.vcs
.changes
.*;
55 import com
.intellij
.openapi
.vcs
.checkin
.CheckinEnvironment
;
56 import com
.intellij
.openapi
.vcs
.diff
.DiffProvider
;
57 import com
.intellij
.openapi
.vcs
.history
.VcsHistoryProvider
;
58 import com
.intellij
.openapi
.vcs
.history
.VcsRevisionNumber
;
59 import com
.intellij
.openapi
.vcs
.impl
.ProjectLevelVcsManagerImpl
;
60 import com
.intellij
.openapi
.vcs
.merge
.MergeProvider
;
61 import com
.intellij
.openapi
.vcs
.rollback
.RollbackEnvironment
;
62 import com
.intellij
.openapi
.vcs
.update
.UpdateEnvironment
;
63 import com
.intellij
.openapi
.vcs
.versionBrowser
.ChangeBrowserSettings
;
64 import com
.intellij
.openapi
.vfs
.VirtualFile
;
65 import com
.intellij
.openapi
.vfs
.VirtualFileManager
;
66 import com
.intellij
.util
.containers
.Convertor
;
67 import com
.intellij
.util
.containers
.SoftHashMap
;
68 import com
.intellij
.util
.messages
.MessageBus
;
69 import com
.intellij
.util
.messages
.MessageBusConnection
;
70 import com
.intellij
.util
.messages
.Topic
;
71 import org
.jetbrains
.annotations
.NonNls
;
72 import org
.jetbrains
.annotations
.NotNull
;
73 import org
.jetbrains
.annotations
.Nullable
;
74 import org
.jetbrains
.idea
.svn
.actions
.ShowPropertiesDiffWithLocalAction
;
75 import org
.jetbrains
.idea
.svn
.actions
.SvnMergeProvider
;
76 import org
.jetbrains
.idea
.svn
.annotate
.SvnAnnotationProvider
;
77 import org
.jetbrains
.idea
.svn
.checkin
.SvnCheckinEnvironment
;
78 import org
.jetbrains
.idea
.svn
.dialogs
.SvnFormatWorker
;
79 import org
.jetbrains
.idea
.svn
.dialogs
.WCInfo
;
80 import org
.jetbrains
.idea
.svn
.history
.LoadedRevisionsCache
;
81 import org
.jetbrains
.idea
.svn
.history
.SvnChangeList
;
82 import org
.jetbrains
.idea
.svn
.history
.SvnCommittedChangesProvider
;
83 import org
.jetbrains
.idea
.svn
.history
.SvnHistoryProvider
;
84 import org
.jetbrains
.idea
.svn
.rollback
.SvnRollbackEnvironment
;
85 import org
.jetbrains
.idea
.svn
.update
.SvnIntegrateEnvironment
;
86 import org
.jetbrains
.idea
.svn
.update
.SvnUpdateEnvironment
;
87 import org
.tmatesoft
.svn
.core
.*;
88 import org
.tmatesoft
.svn
.core
.auth
.ISVNAuthenticationManager
;
89 import org
.tmatesoft
.svn
.core
.internal
.io
.dav
.DAVRepositoryFactory
;
90 import org
.tmatesoft
.svn
.core
.internal
.io
.fs
.FSRepositoryFactory
;
91 import org
.tmatesoft
.svn
.core
.internal
.io
.svn
.SVNRepositoryFactoryImpl
;
92 import org
.tmatesoft
.svn
.core
.internal
.wc
.SVNAdminUtil
;
93 import org
.tmatesoft
.svn
.core
.internal
.wc
.admin
.SVNAdminArea14
;
94 import org
.tmatesoft
.svn
.core
.internal
.wc
.admin
.SVNAdminAreaFactory
;
95 import org
.tmatesoft
.svn
.core
.internal
.wc
.admin
.SVNWCAccess
;
96 import org
.tmatesoft
.svn
.core
.io
.SVNRepository
;
97 import org
.tmatesoft
.svn
.core
.io
.SVNRepositoryFactory
;
98 import org
.tmatesoft
.svn
.core
.wc
.*;
99 import org
.tmatesoft
.svn
.util
.SVNDebugLog
;
100 import org
.tmatesoft
.svn
.util
.SVNDebugLogAdapter
;
101 import org
.tmatesoft
.svn
.util
.SVNLogType
;
104 import java
.io
.UnsupportedEncodingException
;
106 import java
.util
.logging
.Level
;
108 @SuppressWarnings({"IOResourceOpenedButNotSafelyClosed"})
109 public class SvnVcs
extends AbstractVcs
{
110 private final static Logger REFRESH_LOG
= Logger
.getInstance("#svn_refresh");
112 private static final Logger LOG
= Logger
.getInstance("org.jetbrains.idea.svn.SvnVcs");
113 private final Map
<String
, Map
<String
, Pair
<SVNPropertyValue
, Trinity
<Long
, Long
, Long
>>>> myPropertyCache
= new SoftHashMap
<String
, Map
<String
, Pair
<SVNPropertyValue
, Trinity
<Long
, Long
, Long
>>>>();
115 private final SvnConfiguration myConfiguration
;
116 private final SvnEntriesFileListener myEntriesFileListener
;
118 private CheckinEnvironment myCheckinEnvironment
;
119 private RollbackEnvironment myRollbackEnvironment
;
120 private UpdateEnvironment mySvnUpdateEnvironment
;
121 private UpdateEnvironment mySvnIntegrateEnvironment
;
122 private VcsHistoryProvider mySvnHistoryProvider
;
123 private AnnotationProvider myAnnotationProvider
;
124 private DiffProvider mySvnDiffProvider
;
125 private final VcsShowConfirmationOption myAddConfirmation
;
126 private final VcsShowConfirmationOption myDeleteConfirmation
;
127 private EditFileProvider myEditFilesProvider
;
128 private SvnCommittedChangesProvider myCommittedChangesProvider
;
129 private final VcsShowSettingOption myCheckoutOptions
;
131 private ChangeProvider myChangeProvider
;
132 private MergeProvider myMergeProvider
;
134 @NonNls public static final String LOG_PARAMETER_NAME
= "javasvn.log";
135 @NonNls public static final String VCS_NAME
= "svn";
136 public static final String pathToEntries
= SvnUtil
.SVN_ADMIN_DIR_NAME
+ File
.separatorChar
+ SvnUtil
.ENTRIES_FILE_NAME
;
137 public static final String pathToDirProps
= SvnUtil
.SVN_ADMIN_DIR_NAME
+ File
.separatorChar
+ SvnUtil
.DIR_PROPS_FILE_NAME
;
138 private final SvnChangelistListener myChangeListListener
;
139 private MessageBusConnection myMessageBusConnection
;
141 private SvnCopiesRefreshManager myCopiesRefreshManager
;
142 private SvnFileUrlMappingImpl myMapping
;
143 private final MyFrameStateListener myFrameStateListener
;
145 public static final Topic
<Runnable
> ROOTS_RELOADED
= new Topic
<Runnable
>("ROOTS_RELOADED", Runnable
.class);
148 //noinspection UseOfArchaicSystemPropertyAccessors
149 final JavaSVNDebugLogger logger
= new JavaSVNDebugLogger(Boolean
.getBoolean(LOG_PARAMETER_NAME
), LOG
);
150 SVNDebugLog
.setDefaultLog(logger
);
151 SVNAdminAreaFactory
.setSelector(new SvnFormatSelector());
153 DAVRepositoryFactory
.setup();
154 SVNRepositoryFactoryImpl
.setup();
155 FSRepositoryFactory
.setup();
157 // non-optimized writing is fast enough on Linux/MacOS, and somewhat more reliable
158 if (SystemInfo
.isWindows
) {
159 SVNAdminArea14
.setOptimizedWritingEnabled(true);
163 private static Boolean
booleanProperty(final String systemParameterName
) {
164 return Boolean
.valueOf(System
.getProperty(systemParameterName
));
167 public SvnVcs(final Project project
, MessageBus bus
, SvnConfiguration svnConfiguration
, final ChangeListManager changeListManager
,
168 final VcsDirtyScopeManager vcsDirtyScopeManager
) {
171 myConfiguration
= svnConfiguration
;
173 dumpFileStatus(FileStatus
.ADDED
);
174 dumpFileStatus(FileStatus
.DELETED
);
175 dumpFileStatus(FileStatus
.MERGE
);
176 dumpFileStatus(FileStatus
.MODIFIED
);
177 dumpFileStatus(FileStatus
.NOT_CHANGED
);
178 dumpFileStatus(FileStatus
.UNKNOWN
);
180 dumpFileStatus(SvnFileStatus
.REPLACED
);
181 dumpFileStatus(SvnFileStatus
.EXTERNAL
);
182 dumpFileStatus(SvnFileStatus
.OBSTRUCTED
);
184 myEntriesFileListener
= new SvnEntriesFileListener(project
);
186 final ProjectLevelVcsManager vcsManager
= ProjectLevelVcsManager
.getInstance(project
);
187 myAddConfirmation
= vcsManager
.getStandardConfirmation(VcsConfiguration
.StandardConfirmation
.ADD
, this);
188 myDeleteConfirmation
= vcsManager
.getStandardConfirmation(VcsConfiguration
.StandardConfirmation
.REMOVE
, this);
189 myCheckoutOptions
= vcsManager
.getStandardOption(VcsConfiguration
.StandardOption
.CHECKOUT
, this);
191 if (myProject
.isDefault()) {
192 myChangeListListener
= null;
194 upgradeIfNeeded(bus
);
196 myChangeListListener
= new SvnChangelistListener(myProject
, createChangelistClient());
197 changeListManager
.addChangeListListener(myChangeListListener
);
199 myMessageBusConnection
= bus
.connect();
200 myMessageBusConnection
.subscribe(ProjectLevelVcsManagerImpl
.VCS_MAPPING_CHANGED
, new Runnable() {
202 invokeRefreshSvnRoots(true);
207 // do one time after project loaded
208 StartupManager
.getInstance(myProject
).registerPostStartupActivity(new DumbAwareRunnable() {
212 // for IDEA, it takes 2 minutes - and anyway this can be done in background, no sence...
213 // once it could be mistaken about copies for 2 minutes on start...
215 /*if (! myMapping.getAllWcInfos().isEmpty()) {
216 invokeRefreshSvnRoots();
219 ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
221 myCopiesRefreshManager.getCopiesRefresh().ensureInit();
223 }, SvnBundle.message("refreshing.working.copies.roots.progress.text"), true, myProject);*/
227 myFrameStateListener
= new MyFrameStateListener(changeListManager
, vcsDirtyScopeManager
);
230 public void postStartup() {
231 myMapping
= SvnFileUrlMappingImpl
.getInstance(myProject
);
232 // only mapping for default
233 if (myProject
.isDefault()) return;
234 myCopiesRefreshManager
= new SvnCopiesRefreshManager(myProject
, myMapping
);
236 invokeRefreshSvnRoots(true);
239 public void invokeRefreshSvnRoots(final boolean asynchronous
) {
240 REFRESH_LOG
.debug("refresh: ", new Throwable());
241 if (myCopiesRefreshManager
!= null) {
243 myCopiesRefreshManager
.getCopiesRefresh().asynchRequest();
245 ProgressManager
.getInstance().runProcessWithProgressSynchronously(new Runnable() {
247 myCopiesRefreshManager
.getCopiesRefresh().synchRequest();
249 }, SvnBundle
.message("refreshing.working.copies.roots.progress.text"), true, myProject
);
254 private void upgradeIfNeeded(final MessageBus bus
) {
255 final MessageBusConnection connection
= bus
.connect();
256 connection
.subscribe(ChangeListManagerImpl
.LISTS_LOADED
, new LocalChangeListsLoadedListener() {
257 public void processLoadedLists(final List
<LocalChangeList
> lists
) {
259 ChangeListManager
.getInstanceChecked(myProject
).setReadOnly(SvnChangeProvider
.ourDefaultListName
, true);
261 final SvnConfiguration
.SvnSupportOptions supportOptions
= myConfiguration
.getSupportOptions();
263 upgradeTo15(supportOptions
);
264 if (! supportOptions
.changeListsSynchronized()) {
265 processChangeLists(lists
);
266 supportOptions
.upgradeToChangeListsSynchronized();
269 catch (ProcessCanceledException e
) {
272 connection
.disconnect();
277 public void processChangeLists(final List
<LocalChangeList
> lists
) {
278 final ProjectLevelVcsManager plVcsManager
= ProjectLevelVcsManager
.getInstanceChecked(myProject
);
279 plVcsManager
.startBackgroundVcsOperation();
281 final SVNChangelistClient client
= createChangelistClient();
282 for (LocalChangeList list
: lists
) {
283 if (! list
.isDefault()) {
284 final Collection
<Change
> changes
= list
.getChanges();
285 for (Change change
: changes
) {
286 correctListForRevision(plVcsManager
, change
.getBeforeRevision(), client
, list
.getName());
287 correctListForRevision(plVcsManager
, change
.getAfterRevision(), client
, list
.getName());
293 final Application appManager
= ApplicationManager
.getApplication();
294 if (appManager
.isDispatchThread()) {
295 appManager
.executeOnPooledThread(new Runnable() {
297 plVcsManager
.stopBackgroundVcsOperation();
301 plVcsManager
.stopBackgroundVcsOperation();
306 private void correctListForRevision(final ProjectLevelVcsManager plVcsManager
, final ContentRevision revision
,
307 final SVNChangelistClient client
, final String name
) {
308 if (revision
!= null) {
309 final FilePath path
= revision
.getFile();
310 final AbstractVcs vcs
= plVcsManager
.getVcsFor(path
);
311 if ((vcs
!= null) && VCS_NAME
.equals(vcs
.getName())) {
313 client
.doAddToChangelist(new File
[] {path
.getIOFile()}, SVNDepth
.EMPTY
, name
, null);
315 catch (SVNException e
) {
316 // left in default list
322 private void upgradeTo15(final SvnConfiguration
.SvnSupportOptions supportOptions
) {
323 if (! supportOptions
.upgradeTo15Asked()) {
324 final SvnWorkingCopyChecker workingCopyChecker
= new SvnWorkingCopyChecker();
326 if (workingCopyChecker
.upgradeNeeded()) {
327 ApplicationManager
.getApplication().invokeLater(new Runnable() {
330 final int upgradeAnswer
= Messages
.showYesNoDialog(SvnBundle
.message("upgrade.format.to15.question.text",
331 SvnBundle
.message("label.where.svn.format.can.be.changed.text", SvnBundle
.message("action.show.svn.map.text"))),
332 SvnBundle
.message("upgrade.format.to15.question.title"), Messages
.getWarningIcon());
333 if (DialogWrapper
.OK_EXIT_CODE
== upgradeAnswer
) {
334 workingCopyChecker
.doUpgrade();
343 public void activate() {
345 SvnApplicationSettings
.getInstance().svnActivated();
346 VirtualFileManager
.getInstance().addVirtualFileListener(myEntriesFileListener
);
347 // this will initialize its inner listener for committed changes upload
348 LoadedRevisionsCache
.getInstance(myProject
);
349 FrameStateManager
.getInstance().addListener(myFrameStateListener
);
353 public void deactivate() {
354 FrameStateManager
.getInstance().removeListener(myFrameStateListener
);
355 if (myMessageBusConnection
!= null) {
356 myMessageBusConnection
.disconnect();
358 VirtualFileManager
.getInstance().removeVirtualFileListener(myEntriesFileListener
);
359 SvnApplicationSettings
.getInstance().svnDeactivated();
360 new DefaultSVNRepositoryPool(null, null).shutdownConnections(true);
361 if (myCommittedChangesProvider
!= null) {
362 myCommittedChangesProvider
.deactivate();
364 if (myChangeListListener
!= null && (! myProject
.isDefault())) {
365 ChangeListManager
.getInstance(myProject
).removeChangeListListener(myChangeListListener
);
370 public VcsShowConfirmationOption
getAddConfirmation() {
371 return myAddConfirmation
;
374 public VcsShowConfirmationOption
getDeleteConfirmation() {
375 return myDeleteConfirmation
;
378 public VcsShowSettingOption
getCheckoutOptions() {
379 return myCheckoutOptions
;
382 public EditFileProvider
getEditFileProvider() {
383 if (myEditFilesProvider
== null) {
384 myEditFilesProvider
= new SvnEditFileProvider(this);
386 return myEditFilesProvider
;
390 public ChangeProvider
getChangeProvider() {
391 if (myChangeProvider
== null) {
392 myChangeProvider
= new SvnChangeProvider(this);
394 return myChangeProvider
;
397 public SVNRepository
createRepository(String url
) throws SVNException
{
398 SVNRepository repos
= SVNRepositoryFactory
.create(SVNURL
.parseURIEncoded(url
));
399 repos
.setAuthenticationManager(myConfiguration
.getAuthenticationManager(myProject
));
400 repos
.setTunnelProvider(myConfiguration
.getOptions(myProject
));
404 public SVNRepository
createRepository(SVNURL url
) throws SVNException
{
405 SVNRepository repos
= SVNRepositoryFactory
.create(url
);
406 repos
.setAuthenticationManager(myConfiguration
.getAuthenticationManager(myProject
));
407 repos
.setTunnelProvider(myConfiguration
.getOptions(myProject
));
411 public SVNUpdateClient
createUpdateClient() {
412 return new SVNUpdateClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
415 public SVNStatusClient
createStatusClient() {
416 return new SVNStatusClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
419 public SVNWCClient
createWCClient() {
420 return new SVNWCClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
423 public SVNCopyClient
createCopyClient() {
424 return new SVNCopyClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
427 public SVNMoveClient
createMoveClient() {
428 return new SVNMoveClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
431 public SVNLogClient
createLogClient() {
432 return new SVNLogClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
435 public SVNCommitClient
createCommitClient() {
436 return new SVNCommitClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
439 public SVNDiffClient
createDiffClient() {
440 return new SVNDiffClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
443 public SVNChangelistClient
createChangelistClient() {
444 return new SVNChangelistClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
447 public SVNWCAccess
createWCAccess() {
448 final SVNWCAccess access
= SVNWCAccess
.newInstance(null);
449 access
.setOptions(myConfiguration
.getOptions(myProject
));
453 public ISVNOptions
getSvnOptions() {
454 return myConfiguration
.getOptions(myProject
);
457 public ISVNAuthenticationManager
getSvnAuthenticationManager() {
458 return myConfiguration
.getAuthenticationManager(myProject
);
461 void dumpFileStatus(FileStatus fs
) {
462 if (LOG
.isDebugEnabled()) {
463 LOG
.debug("FileStatus:" + fs
.getText() + " " + fs
.getColor() + " " + " " + fs
.getClass().getName());
467 public UpdateEnvironment
getIntegrateEnvironment() {
468 if (mySvnIntegrateEnvironment
== null) {
469 mySvnIntegrateEnvironment
= new SvnIntegrateEnvironment(this);
471 return mySvnIntegrateEnvironment
;
474 public UpdateEnvironment
getUpdateEnvironment() {
475 if (mySvnUpdateEnvironment
== null) {
476 mySvnUpdateEnvironment
= new SvnUpdateEnvironment(this);
478 return mySvnUpdateEnvironment
;
481 public String
getName() {
482 LOG
.debug("getName");
486 public String
getDisplayName() {
487 LOG
.debug("getDisplayName");
491 public Configurable
getConfigurable() {
492 LOG
.debug("createConfigurable");
493 return new SvnConfigurable(myProject
);
496 public Project
getProject() {
500 public SvnConfiguration
getSvnConfiguration() {
501 return myConfiguration
;
504 public static SvnVcs
getInstance(Project project
) {
505 return (SvnVcs
) ProjectLevelVcsManager
.getInstance(project
).findVcsByName(VCS_NAME
);
509 public CheckinEnvironment
getCheckinEnvironment() {
510 if (myCheckinEnvironment
== null) {
511 myCheckinEnvironment
= new SvnCheckinEnvironment(this);
513 return myCheckinEnvironment
;
517 public RollbackEnvironment
getRollbackEnvironment() {
518 if (myRollbackEnvironment
== null) {
519 myRollbackEnvironment
= new SvnRollbackEnvironment(this);
521 return myRollbackEnvironment
;
524 public VcsHistoryProvider
getVcsHistoryProvider() {
525 // no heavy state, but it would be useful to have place to keep state in -> do not reuse instance
526 return new SvnHistoryProvider(this);
529 public VcsHistoryProvider
getVcsBlockHistoryProvider() {
530 return getVcsHistoryProvider();
533 public AnnotationProvider
getAnnotationProvider() {
534 if (myAnnotationProvider
== null) {
535 myAnnotationProvider
= new SvnAnnotationProvider(this);
537 return myAnnotationProvider
;
540 public SvnEntriesFileListener
getSvnEntriesFileListener() {
541 return myEntriesFileListener
;
544 public DiffProvider
getDiffProvider() {
545 if (mySvnDiffProvider
== null) {
546 mySvnDiffProvider
= new SvnDiffProvider(this);
548 return mySvnDiffProvider
;
551 private Trinity
<Long
, Long
, Long
> getTimestampForPropertiesChange(final File ioFile
, final boolean isDir
) {
552 final File dir
= isDir ? ioFile
: ioFile
.getParentFile();
553 final String relPath
= SVNAdminUtil
.getPropPath(ioFile
.getName(), isDir ? SVNNodeKind
.DIR
: SVNNodeKind
.FILE
, false);
554 final String relPathBase
= SVNAdminUtil
.getPropBasePath(ioFile
.getName(), isDir ? SVNNodeKind
.DIR
: SVNNodeKind
.FILE
, false);
555 final String relPathRevert
= SVNAdminUtil
.getPropRevertPath(ioFile
.getName(), isDir ? SVNNodeKind
.DIR
: SVNNodeKind
.FILE
, false);
556 return new Trinity
<Long
, Long
, Long
>(new File(dir
, relPath
).lastModified(), new File(dir
, relPathBase
).lastModified(),
557 new File(dir
, relPathRevert
).lastModified());
560 private boolean trinitiesEqual(final Trinity
<Long
, Long
, Long
> t1
, final Trinity
<Long
, Long
, Long
> t2
) {
561 if (t2
.first
== 0 && t2
.second
== 0 && t2
.third
== 0) return false;
562 return t1
.equals(t2
);
566 public SVNPropertyValue
getPropertyWithCaching(final VirtualFile file
, final String propName
) throws SVNException
{
567 Map
<String
, Pair
<SVNPropertyValue
, Trinity
<Long
, Long
, Long
>>> cachedMap
= myPropertyCache
.get(keyForVf(file
));
568 final Pair
<SVNPropertyValue
, Trinity
<Long
, Long
, Long
>> cachedValue
= (cachedMap
== null) ?
null : cachedMap
.get(propName
);
570 final File ioFile
= new File(file
.getPath());
571 final Trinity
<Long
, Long
, Long
> tsTrinity
= getTimestampForPropertiesChange(ioFile
, file
.isDirectory());
573 if (cachedValue
!= null) {
574 // zero means that a file was not found
575 if (trinitiesEqual(cachedValue
.getSecond(), tsTrinity
)) {
576 return cachedValue
.getFirst();
580 final SVNPropertyData value
= createWCClient().doGetProperty(ioFile
, propName
, SVNRevision
.WORKING
, SVNRevision
.WORKING
);
581 final SVNPropertyValue propValue
= (value
== null) ?
null : value
.getValue();
583 if (cachedMap
== null) {
584 cachedMap
= new HashMap
<String
, Pair
<SVNPropertyValue
, Trinity
<Long
, Long
, Long
>>>();
585 myPropertyCache
.put(keyForVf(file
), cachedMap
);
588 cachedMap
.put(propName
, new Pair
<SVNPropertyValue
, Trinity
<Long
, Long
, Long
>>(propValue
, tsTrinity
));
593 public boolean fileExistsInVcs(FilePath path
) {
594 File file
= path
.getIOFile();
597 status
= createStatusClient().doStatus(file
, false);
598 if (status
!= null) {
599 final SVNStatusType statusType
= status
.getContentsStatus();
600 if (statusType
== SVNStatusType
.STATUS_ADDED
) {
601 return status
.isCopied();
603 return !(status
.getContentsStatus() == SVNStatusType
.STATUS_UNVERSIONED
||
604 status
.getContentsStatus() == SVNStatusType
.STATUS_IGNORED
||
605 status
.getContentsStatus() == SVNStatusType
.STATUS_OBSTRUCTED
);
608 catch (SVNException e
) {
614 public boolean fileIsUnderVcs(FilePath path
) {
615 final ChangeListManager clManager
= ChangeListManager
.getInstance(myProject
);
616 return (! SvnStatusUtil
.isIgnoredInAnySense(clManager
, path
.getVirtualFile())) && (! clManager
.isUnversioned(path
.getVirtualFile()));
619 private static File
getEntriesFile(File file
) {
620 return file
.isDirectory() ?
new File(file
, pathToEntries
) : new File(file
.getParentFile(), pathToEntries
);
623 private static File
getDirPropsFile(File file
) {
624 return new File(file
, pathToDirProps
);
628 public SVNInfo
getInfo(final VirtualFile file
) {
630 SVNWCClient wcClient
= new SVNWCClient(getSvnAuthenticationManager(), getSvnOptions());
631 SVNInfo info
= wcClient
.doInfo(new File(file
.getPath()), SVNRevision
.WORKING
);
632 if (info
== null || info
.getRepositoryRootURL() == null) {
633 info
= wcClient
.doInfo(new File(file
.getPath()), SVNRevision
.HEAD
);
637 catch (SVNException e
) {
642 public static class SVNStatusHolder
{
644 private final SVNStatus myValue
;
645 private final long myEntriesTimestamp
;
646 private final long myFileTimestamp
;
647 private final boolean myIsLocked
;
649 public SVNStatusHolder(long entriesStamp
, long fileStamp
, SVNStatus value
) {
651 myEntriesTimestamp
= entriesStamp
;
652 myFileTimestamp
= fileStamp
;
653 myIsLocked
= value
!= null && value
.isLocked();
656 public long getEntriesTimestamp() {
657 return myEntriesTimestamp
;
660 public long getFileTimestamp() {
661 return myFileTimestamp
;
664 public boolean isLocked() {
668 public SVNStatus
getStatus() {
673 public static class SVNInfoHolder
{
675 private final SVNInfo myValue
;
676 private final long myEntriesTimestamp
;
677 private final long myFileTimestamp
;
679 public SVNInfoHolder(long entriesStamp
, long fileStamp
, SVNInfo value
) {
681 myEntriesTimestamp
= entriesStamp
;
682 myFileTimestamp
= fileStamp
;
685 public long getEntriesTimestamp() {
686 return myEntriesTimestamp
;
689 public long getFileTimestamp() {
690 return myFileTimestamp
;
693 public SVNInfo
getInfo() {
698 private static class JavaSVNDebugLogger
extends SVNDebugLogAdapter
{
699 private final boolean myLoggingEnabled
;
700 private final Logger myLog
;
701 @NonNls public static final String TRACE_LOG_PARAMETER_NAME
= "javasvn.log.trace";
703 public JavaSVNDebugLogger(boolean loggingEnabled
, Logger log
) {
704 myLoggingEnabled
= loggingEnabled
;
708 public void log(final SVNLogType logType
, final Throwable th
, final Level logLevel
) {
709 if (myLoggingEnabled
) {
714 public void log(final SVNLogType logType
, final String message
, final Level logLevel
) {
715 if (myLoggingEnabled
) {
720 public void log(final SVNLogType logType
, final String message
, final byte[] data
) {
721 if (myLoggingEnabled
) {
724 myLog
.info(message
+ "\n" + new String(data
, "UTF-8"));
726 catch (UnsupportedEncodingException e
) {
727 myLog
.info(message
+ "\n" + new String(data
));
736 public FileStatus
[] getProvidedStatuses() {
737 return new FileStatus
[]{SvnFileStatus
.EXTERNAL
,
738 SvnFileStatus
.OBSTRUCTED
,
739 SvnFileStatus
.REPLACED
};
744 public CommittedChangesProvider
<SvnChangeList
, ChangeBrowserSettings
> getCommittedChangesProvider() {
745 if (myCommittedChangesProvider
== null) {
746 myCommittedChangesProvider
= new SvnCommittedChangesProvider(myProject
);
748 return myCommittedChangesProvider
;
753 public VcsRevisionNumber
parseRevisionNumber(final String revisionNumberString
) {
754 final SVNRevision revision
= SVNRevision
.parse(revisionNumberString
);
755 if (revision
.equals(SVNRevision
.UNDEFINED
)) {
758 return new SvnRevisionNumber(revision
);
762 public String
getRevisionPattern() {
763 return ourIntegerPattern
;
767 public boolean isVersionedDirectory(final VirtualFile dir
) {
768 return SvnUtil
.seemsLikeVersionedDir(dir
);
772 public SvnFileUrlMapping
getSvnFileUrlMapping() {
777 * Returns real working copies roots - if there is <Project Root> -> Subversion setting,
778 * and there is one working copy, will return one root
780 public List
<WCInfo
> getAllWcInfos() {
781 final SvnFileUrlMapping urlMapping
= getSvnFileUrlMapping();
783 final List
<RootUrlInfo
> infoList
= urlMapping
.getAllWcInfos();
784 final List
<WCInfo
> infos
= new ArrayList
<WCInfo
>();
785 for (RootUrlInfo info
: infoList
) {
786 final File file
= info
.getIoFile();
787 infos
.add(new WCInfo(file
.getAbsolutePath(), info
.getAbsoluteUrlAsUrl(),
788 SvnFormatSelector
.getWorkingCopyFormat(file
), info
.getRepositoryUrl(), SvnUtil
.isWorkingCopyRoot(file
)));
793 private class SvnWorkingCopyChecker
{
794 private List
<WCInfo
> myAllWcInfos
;
796 public boolean upgradeNeeded() {
797 myAllWcInfos
= getAllWcInfos();
798 for (WCInfo info
: myAllWcInfos
) {
799 if (! WorkingCopyFormat
.ONE_DOT_FIVE
.equals(info
.getFormat())) {
806 public void doUpgrade() {
807 ApplicationManager
.getApplication().invokeLater(new Runnable() {
809 final SvnFormatWorker formatWorker
= new SvnFormatWorker(myProject
, WorkingCopyFormat
.ONE_DOT_FIVE
, myAllWcInfos
);
810 // additionally ask about working copies with roots above the project root
811 formatWorker
.checkForOutsideCopies();
812 if (formatWorker
.haveStuffToConvert()) {
813 ProgressManager
.getInstance().run(formatWorker
);
821 public RootsConvertor
getCustomConvertor() {
826 public MergeProvider
getMergeProvider() {
827 if (myMergeProvider
== null) {
828 myMergeProvider
= new SvnMergeProvider(myProject
);
830 return myMergeProvider
;
834 public List
<AnAction
> getAdditionalActionsForLocalChange() {
835 return Arrays
.<AnAction
>asList(new ShowPropertiesDiffWithLocalAction());
838 public void pathChanged(final File from
, final File to
) throws SVNException
{
839 myChangeListListener
.pathChanged(from
, to
);
842 private String
keyForVf(final VirtualFile vf
) {
847 public boolean allowsNestedRoots() {
848 return SvnConfiguration
.getInstance(myProject
).DETECT_NESTED_COPIES
;
852 public List
<VirtualFile
> filterUniqueRoots(final List
<VirtualFile
> in
) {
853 if (in
.size() <= 1) return in
;
855 final List
<RootUrlPair
> infos
= new ArrayList
<RootUrlPair
>(in
.size());
856 final SvnFileUrlMappingImpl mapping
= myMapping
;
857 for (VirtualFile vf
: in
) {
858 final File ioFile
= new File(vf
.getPath());
859 final SVNURL url
= mapping
.getUrlForFile(ioFile
);
860 if (url
== null) continue;
861 /*if ((info == null) || (! ioFile.getAbsolutePath().equals(info.getIoFile().getAbsolutePath()))) {
862 // we get one of roots there, there shouldn't be other paths
865 infos
.add(new MyPair(vf
, url
.toString()));
867 final List
<RootUrlPair
> filtered
= new ArrayList
<RootUrlPair
>(infos
.size());
868 ForNestedRootChecker
.filterOutSuperfluousChildren(this, infos
, filtered
);
870 return ObjectsConvertor
.convert(filtered
, new Convertor
<RootUrlPair
, VirtualFile
>() {
871 public VirtualFile
convert(RootUrlPair o
) {
872 return o
.getVirtualFile();
877 private static class MyPair
implements RootUrlPair
{
878 private final VirtualFile myFile
;
879 private final String myUrl
;
881 private MyPair(VirtualFile file
, String url
) {
886 public VirtualFile
getVirtualFile() {
890 public String
getUrl() {
895 private static class MyFrameStateListener
implements FrameStateListener
{
896 private final ChangeListManager myClManager
;
897 private final VcsDirtyScopeManager myDirtyScopeManager
;
899 private MyFrameStateListener(ChangeListManager clManager
, VcsDirtyScopeManager dirtyScopeManager
) {
900 myClManager
= clManager
;
901 myDirtyScopeManager
= dirtyScopeManager
;
904 public void onFrameDeactivated() {
907 public void onFrameActivated() {
908 final List
<VirtualFile
> folders
= ((ChangeListManagerImpl
)myClManager
).getLockedFolders();
909 if (! folders
.isEmpty()) {
910 myDirtyScopeManager
.filesDirty(null, folders
);