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
.ide
.FrameStateListener
;
21 import com
.intellij
.ide
.FrameStateManager
;
22 import com
.intellij
.notification
.*;
23 import com
.intellij
.openapi
.actionSystem
.AnAction
;
24 import com
.intellij
.openapi
.application
.Application
;
25 import com
.intellij
.openapi
.application
.ApplicationManager
;
26 import com
.intellij
.openapi
.diagnostic
.Logger
;
27 import com
.intellij
.openapi
.options
.Configurable
;
28 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
29 import com
.intellij
.openapi
.progress
.ProgressManager
;
30 import com
.intellij
.openapi
.project
.DumbAwareRunnable
;
31 import com
.intellij
.openapi
.project
.Project
;
32 import com
.intellij
.openapi
.startup
.StartupManager
;
33 import com
.intellij
.openapi
.ui
.DialogWrapper
;
34 import com
.intellij
.openapi
.ui
.Messages
;
35 import com
.intellij
.openapi
.util
.Pair
;
36 import com
.intellij
.openapi
.util
.SystemInfo
;
37 import com
.intellij
.openapi
.util
.Trinity
;
38 import com
.intellij
.openapi
.vcs
.*;
39 import com
.intellij
.openapi
.vcs
.annotate
.AnnotationProvider
;
40 import com
.intellij
.openapi
.vcs
.changes
.*;
41 import com
.intellij
.openapi
.vcs
.checkin
.CheckinEnvironment
;
42 import com
.intellij
.openapi
.vcs
.diff
.DiffProvider
;
43 import com
.intellij
.openapi
.vcs
.history
.VcsHistoryProvider
;
44 import com
.intellij
.openapi
.vcs
.history
.VcsRevisionNumber
;
45 import com
.intellij
.openapi
.vcs
.merge
.MergeProvider
;
46 import com
.intellij
.openapi
.vcs
.rollback
.RollbackEnvironment
;
47 import com
.intellij
.openapi
.vcs
.update
.UpdateEnvironment
;
48 import com
.intellij
.openapi
.vcs
.versionBrowser
.ChangeBrowserSettings
;
49 import com
.intellij
.openapi
.vfs
.VirtualFile
;
50 import com
.intellij
.openapi
.vfs
.VirtualFileManager
;
51 import com
.intellij
.util
.containers
.Convertor
;
52 import com
.intellij
.util
.containers
.SoftHashMap
;
53 import com
.intellij
.util
.messages
.MessageBus
;
54 import com
.intellij
.util
.messages
.MessageBusConnection
;
55 import com
.intellij
.util
.messages
.Topic
;
56 import org
.jetbrains
.annotations
.NonNls
;
57 import org
.jetbrains
.annotations
.NotNull
;
58 import org
.jetbrains
.annotations
.Nullable
;
59 import org
.jetbrains
.idea
.svn
.actions
.ShowPropertiesDiffWithLocalAction
;
60 import org
.jetbrains
.idea
.svn
.actions
.SvnMergeProvider
;
61 import org
.jetbrains
.idea
.svn
.annotate
.SvnAnnotationProvider
;
62 import org
.jetbrains
.idea
.svn
.checkin
.SvnCheckinEnvironment
;
63 import org
.jetbrains
.idea
.svn
.dialogs
.SvnFormatWorker
;
64 import org
.jetbrains
.idea
.svn
.dialogs
.WCInfo
;
65 import org
.jetbrains
.idea
.svn
.history
.LoadedRevisionsCache
;
66 import org
.jetbrains
.idea
.svn
.history
.SvnChangeList
;
67 import org
.jetbrains
.idea
.svn
.history
.SvnCommittedChangesProvider
;
68 import org
.jetbrains
.idea
.svn
.history
.SvnHistoryProvider
;
69 import org
.jetbrains
.idea
.svn
.rollback
.SvnRollbackEnvironment
;
70 import org
.jetbrains
.idea
.svn
.update
.SvnIntegrateEnvironment
;
71 import org
.jetbrains
.idea
.svn
.update
.SvnUpdateEnvironment
;
72 import org
.tmatesoft
.svn
.core
.*;
73 import org
.tmatesoft
.svn
.core
.auth
.ISVNAuthenticationManager
;
74 import org
.tmatesoft
.svn
.core
.internal
.io
.dav
.DAVRepositoryFactory
;
75 import org
.tmatesoft
.svn
.core
.internal
.io
.fs
.FSRepositoryFactory
;
76 import org
.tmatesoft
.svn
.core
.internal
.io
.svn
.SVNRepositoryFactoryImpl
;
77 import org
.tmatesoft
.svn
.core
.internal
.util
.jna
.SVNJNAUtil
;
78 import org
.tmatesoft
.svn
.core
.internal
.wc
.SVNAdminUtil
;
79 import org
.tmatesoft
.svn
.core
.internal
.wc
.admin
.SVNAdminArea14
;
80 import org
.tmatesoft
.svn
.core
.internal
.wc
.admin
.SVNAdminAreaFactory
;
81 import org
.tmatesoft
.svn
.core
.internal
.wc
.admin
.SVNWCAccess
;
82 import org
.tmatesoft
.svn
.core
.io
.SVNRepository
;
83 import org
.tmatesoft
.svn
.core
.io
.SVNRepositoryFactory
;
84 import org
.tmatesoft
.svn
.core
.wc
.*;
85 import org
.tmatesoft
.svn
.util
.SVNDebugLog
;
86 import org
.tmatesoft
.svn
.util
.SVNDebugLogAdapter
;
87 import org
.tmatesoft
.svn
.util
.SVNLogType
;
89 import javax
.swing
.event
.HyperlinkEvent
;
91 import java
.io
.UnsupportedEncodingException
;
93 import java
.util
.logging
.Level
;
95 @SuppressWarnings({"IOResourceOpenedButNotSafelyClosed"})
96 public class SvnVcs
extends AbstractVcs
{
97 private final static Logger REFRESH_LOG
= Logger
.getInstance("#svn_refresh");
99 private static final Logger LOG
= Logger
.getInstance("org.jetbrains.idea.svn.SvnVcs");
100 @NonNls public static final String VCS_NAME
= "svn";
101 private static final VcsKey ourKey
= createKey(VCS_NAME
);
102 private final Map
<String
, Map
<String
, Pair
<SVNPropertyValue
, Trinity
<Long
, Long
, Long
>>>> myPropertyCache
= new SoftHashMap
<String
, Map
<String
, Pair
<SVNPropertyValue
, Trinity
<Long
, Long
, Long
>>>>();
104 private final SvnConfiguration myConfiguration
;
105 private final SvnEntriesFileListener myEntriesFileListener
;
107 private CheckinEnvironment myCheckinEnvironment
;
108 private RollbackEnvironment myRollbackEnvironment
;
109 private UpdateEnvironment mySvnUpdateEnvironment
;
110 private UpdateEnvironment mySvnIntegrateEnvironment
;
111 private VcsHistoryProvider mySvnHistoryProvider
;
112 private AnnotationProvider myAnnotationProvider
;
113 private DiffProvider mySvnDiffProvider
;
114 private final VcsShowConfirmationOption myAddConfirmation
;
115 private final VcsShowConfirmationOption myDeleteConfirmation
;
116 private EditFileProvider myEditFilesProvider
;
117 private SvnCommittedChangesProvider myCommittedChangesProvider
;
118 private final VcsShowSettingOption myCheckoutOptions
;
120 private ChangeProvider myChangeProvider
;
121 private MergeProvider myMergeProvider
;
123 @NonNls public static final String LOG_PARAMETER_NAME
= "javasvn.log";
124 public static final String pathToEntries
= SvnUtil
.SVN_ADMIN_DIR_NAME
+ File
.separatorChar
+ SvnUtil
.ENTRIES_FILE_NAME
;
125 public static final String pathToDirProps
= SvnUtil
.SVN_ADMIN_DIR_NAME
+ File
.separatorChar
+ SvnUtil
.DIR_PROPS_FILE_NAME
;
126 private final SvnChangelistListener myChangeListListener
;
128 private SvnCopiesRefreshManager myCopiesRefreshManager
;
129 private SvnFileUrlMappingImpl myMapping
;
130 private final MyFrameStateListener myFrameStateListener
;
132 public static final Topic
<Runnable
> ROOTS_RELOADED
= new Topic
<Runnable
>("ROOTS_RELOADED", Runnable
.class);
133 private VcsListener myVcsListener
;
136 SvnHttpAuthMethodsDefaultChecker
.check();
138 //noinspection UseOfArchaicSystemPropertyAccessors
139 final JavaSVNDebugLogger logger
= new JavaSVNDebugLogger(Boolean
.getBoolean(LOG_PARAMETER_NAME
), LOG
);
140 SVNDebugLog
.setDefaultLog(logger
);
141 SVNAdminAreaFactory
.setSelector(new SvnFormatSelector());
143 DAVRepositoryFactory
.setup();
144 SVNRepositoryFactoryImpl
.setup();
145 FSRepositoryFactory
.setup();
147 // non-optimized writing is fast enough on Linux/MacOS, and somewhat more reliable
148 if (SystemInfo
.isWindows
) {
149 SVNAdminArea14
.setOptimizedWritingEnabled(true);
152 if (! SVNJNAUtil
.isJNAPresent()) {
153 LOG
.warn("JNA is not found by svnkit library");
157 private static Boolean
booleanProperty(final String systemParameterName
) {
158 return Boolean
.valueOf(System
.getProperty(systemParameterName
));
161 public SvnVcs(final Project project
, MessageBus bus
, SvnConfiguration svnConfiguration
, final ChangeListManager changeListManager
,
162 final VcsDirtyScopeManager vcsDirtyScopeManager
, final StartupManager startupManager
) {
163 super(project
, VCS_NAME
);
165 myConfiguration
= svnConfiguration
;
167 dumpFileStatus(FileStatus
.ADDED
);
168 dumpFileStatus(FileStatus
.DELETED
);
169 dumpFileStatus(FileStatus
.MERGE
);
170 dumpFileStatus(FileStatus
.MODIFIED
);
171 dumpFileStatus(FileStatus
.NOT_CHANGED
);
172 dumpFileStatus(FileStatus
.UNKNOWN
);
174 dumpFileStatus(SvnFileStatus
.REPLACED
);
175 dumpFileStatus(SvnFileStatus
.EXTERNAL
);
176 dumpFileStatus(SvnFileStatus
.OBSTRUCTED
);
178 myEntriesFileListener
= new SvnEntriesFileListener(project
);
180 final ProjectLevelVcsManager vcsManager
= ProjectLevelVcsManager
.getInstance(project
);
181 myAddConfirmation
= vcsManager
.getStandardConfirmation(VcsConfiguration
.StandardConfirmation
.ADD
, this);
182 myDeleteConfirmation
= vcsManager
.getStandardConfirmation(VcsConfiguration
.StandardConfirmation
.REMOVE
, this);
183 myCheckoutOptions
= vcsManager
.getStandardOption(VcsConfiguration
.StandardOption
.CHECKOUT
, this);
185 if (myProject
.isDefault()) {
186 myChangeListListener
= null;
189 upgradeIfNeeded(bus
);
191 myChangeListListener
= new SvnChangelistListener(myProject
, createChangelistClient());
193 myVcsListener
= new VcsListener() {
194 public void directoryMappingChanged() {
195 invokeRefreshSvnRoots(true);
200 // do one time after project loaded
201 startupManager
.runWhenProjectIsInitialized(new DumbAwareRunnable() {
205 // for IDEA, it takes 2 minutes - and anyway this can be done in background, no sence...
206 // once it could be mistaken about copies for 2 minutes on start...
208 /*if (! myMapping.getAllWcInfos().isEmpty()) {
209 invokeRefreshSvnRoots();
212 ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
214 myCopiesRefreshManager.getCopiesRefresh().ensureInit();
216 }, SvnBundle.message("refreshing.working.copies.roots.progress.text"), true, myProject);*/
220 myFrameStateListener
= new MyFrameStateListener(changeListManager
, vcsDirtyScopeManager
);
223 public void postStartup() {
224 if (myProject
.isDefault()) return;
225 myCopiesRefreshManager
= new SvnCopiesRefreshManager(myProject
, (SvnFileUrlMappingImpl
) getSvnFileUrlMapping());
227 invokeRefreshSvnRoots(true);
230 public void invokeRefreshSvnRoots(final boolean asynchronous
) {
231 REFRESH_LOG
.debug("refresh: ", new Throwable());
232 if (myCopiesRefreshManager
!= null) {
234 myCopiesRefreshManager
.getCopiesRefresh().asynchRequest();
236 ProgressManager
.getInstance().runProcessWithProgressSynchronously(new Runnable() {
238 myCopiesRefreshManager
.getCopiesRefresh().synchRequest();
240 }, SvnBundle
.message("refreshing.working.copies.roots.progress.text"), true, myProject
);
246 public boolean checkImmediateParentsBeforeCommit() {
250 private void upgradeIfNeeded(final MessageBus bus
) {
251 final MessageBusConnection connection
= bus
.connect();
252 connection
.subscribe(ChangeListManagerImpl
.LISTS_LOADED
, new LocalChangeListsLoadedListener() {
253 public void processLoadedLists(final List
<LocalChangeList
> lists
) {
254 SvnConfiguration
.SvnSupportOptions supportOptions
= null;
256 ChangeListManager
.getInstanceChecked(myProject
).setReadOnly(SvnChangeProvider
.ourDefaultListName
, true);
258 supportOptions
= myConfiguration
.getSupportOptions();
260 upgradeToRecentVersion(supportOptions
);
261 if (! supportOptions
.changeListsSynchronized()) {
262 processChangeLists(lists
);
265 catch (ProcessCanceledException e
) {
268 if (supportOptions
!= null) {
269 supportOptions
.upgrade();
273 connection
.disconnect();
278 public void processChangeLists(final List
<LocalChangeList
> lists
) {
279 final ProjectLevelVcsManager plVcsManager
= ProjectLevelVcsManager
.getInstanceChecked(myProject
);
280 plVcsManager
.startBackgroundVcsOperation();
282 final SVNChangelistClient client
= createChangelistClient();
283 for (LocalChangeList list
: lists
) {
284 if (! list
.isDefault()) {
285 final Collection
<Change
> changes
= list
.getChanges();
286 for (Change change
: changes
) {
287 correctListForRevision(plVcsManager
, change
.getBeforeRevision(), client
, list
.getName());
288 correctListForRevision(plVcsManager
, change
.getAfterRevision(), client
, list
.getName());
294 final Application appManager
= ApplicationManager
.getApplication();
295 if (appManager
.isDispatchThread()) {
296 appManager
.executeOnPooledThread(new Runnable() {
298 plVcsManager
.stopBackgroundVcsOperation();
302 plVcsManager
.stopBackgroundVcsOperation();
307 private void correctListForRevision(final ProjectLevelVcsManager plVcsManager
, final ContentRevision revision
,
308 final SVNChangelistClient client
, final String name
) {
309 if (revision
!= null) {
310 final FilePath path
= revision
.getFile();
311 final AbstractVcs vcs
= plVcsManager
.getVcsFor(path
);
312 if ((vcs
!= null) && VCS_NAME
.equals(vcs
.getName())) {
314 client
.doAddToChangelist(new File
[] {path
.getIOFile()}, SVNDepth
.EMPTY
, name
, null);
316 catch (SVNException e
) {
317 // left in default list
323 private final static String UPGRADE_SUBVERSION_FORMAT
= "Subversion";
325 private void upgradeToRecentVersion(final SvnConfiguration
.SvnSupportOptions supportOptions
) {
326 if (! supportOptions
.upgradeTo16Asked()) {
327 final SvnWorkingCopyChecker workingCopyChecker
= new SvnWorkingCopyChecker();
329 if (workingCopyChecker
.upgradeNeeded()) {
331 Notifications
.Bus
.notify(new Notification(UPGRADE_SUBVERSION_FORMAT
, SvnBundle
.message("upgrade.format.to16.question.title"),
332 "Old format Subversion working copies <a href=\"\">could be upgraded to version 1.6</a>.",
333 NotificationType
.INFORMATION
, new NotificationListener() {
334 public void hyperlinkUpdate(@NotNull Notification notification
, @NotNull HyperlinkEvent event
) {
335 final int upgradeAnswer
= Messages
.showYesNoDialog(SvnBundle
.message("upgrade.format.to16.question.text",
336 SvnBundle
.message("label.where.svn.format.can.be.changed.text", SvnBundle
.message("action.show.svn.map.text"))),
337 SvnBundle
.message("upgrade.format.to16.question.title"), Messages
.getWarningIcon());
338 if (DialogWrapper
.OK_EXIT_CODE
== upgradeAnswer
) {
339 workingCopyChecker
.doUpgrade();
342 notification
.expire();
350 public void activate() {
351 if (! myProject
.isDefault()) {
352 ChangeListManager
.getInstance(myProject
).addChangeListListener(myChangeListListener
);
353 ProjectLevelVcsManager
.getInstance(myProject
).addVcsListener(myVcsListener
);
356 SvnApplicationSettings
.getInstance().svnActivated();
357 VirtualFileManager
.getInstance().addVirtualFileListener(myEntriesFileListener
);
358 // this will initialize its inner listener for committed changes upload
359 LoadedRevisionsCache
.getInstance(myProject
);
360 FrameStateManager
.getInstance().addListener(myFrameStateListener
);
364 public void deactivate() {
365 FrameStateManager
.getInstance().removeListener(myFrameStateListener
);
367 if (myVcsListener
!= null) {
368 ProjectLevelVcsManager
.getInstance(myProject
).removeVcsListener(myVcsListener
);
371 VirtualFileManager
.getInstance().removeVirtualFileListener(myEntriesFileListener
);
372 SvnApplicationSettings
.getInstance().svnDeactivated();
373 new DefaultSVNRepositoryPool(null, null).shutdownConnections(true);
374 if (myCommittedChangesProvider
!= null) {
375 myCommittedChangesProvider
.deactivate();
377 if (myChangeListListener
!= null && (! myProject
.isDefault())) {
378 ChangeListManager
.getInstance(myProject
).removeChangeListListener(myChangeListListener
);
382 public VcsShowConfirmationOption
getAddConfirmation() {
383 return myAddConfirmation
;
386 public VcsShowConfirmationOption
getDeleteConfirmation() {
387 return myDeleteConfirmation
;
390 public VcsShowSettingOption
getCheckoutOptions() {
391 return myCheckoutOptions
;
394 public EditFileProvider
getEditFileProvider() {
395 if (myEditFilesProvider
== null) {
396 myEditFilesProvider
= new SvnEditFileProvider(this);
398 return myEditFilesProvider
;
402 public ChangeProvider
getChangeProvider() {
403 if (myChangeProvider
== null) {
404 myChangeProvider
= new SvnChangeProvider(this);
406 return myChangeProvider
;
409 public SVNRepository
createRepository(String url
) throws SVNException
{
410 SVNRepository repos
= SVNRepositoryFactory
.create(SVNURL
.parseURIEncoded(url
));
411 repos
.setAuthenticationManager(myConfiguration
.getAuthenticationManager(myProject
));
412 repos
.setTunnelProvider(myConfiguration
.getOptions(myProject
));
416 public SVNRepository
createRepository(SVNURL url
) throws SVNException
{
417 SVNRepository repos
= SVNRepositoryFactory
.create(url
);
418 repos
.setAuthenticationManager(myConfiguration
.getAuthenticationManager(myProject
));
419 repos
.setTunnelProvider(myConfiguration
.getOptions(myProject
));
423 public SVNUpdateClient
createUpdateClient() {
424 return new SVNUpdateClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
427 public SVNStatusClient
createStatusClient() {
428 return new SVNStatusClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
431 public SVNWCClient
createWCClient() {
432 return new SVNWCClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
435 public SVNCopyClient
createCopyClient() {
436 return new SVNCopyClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
439 public SVNMoveClient
createMoveClient() {
440 return new SVNMoveClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
443 public SVNLogClient
createLogClient() {
444 return new SVNLogClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
447 public SVNCommitClient
createCommitClient() {
448 return new SVNCommitClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
451 public SVNDiffClient
createDiffClient() {
452 return new SVNDiffClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
455 public SVNChangelistClient
createChangelistClient() {
456 return new SVNChangelistClient(myConfiguration
.getAuthenticationManager(myProject
), myConfiguration
.getOptions(myProject
));
459 public SVNWCAccess
createWCAccess() {
460 final SVNWCAccess access
= SVNWCAccess
.newInstance(null);
461 access
.setOptions(myConfiguration
.getOptions(myProject
));
465 public ISVNOptions
getSvnOptions() {
466 return myConfiguration
.getOptions(myProject
);
469 public ISVNAuthenticationManager
getSvnAuthenticationManager() {
470 return myConfiguration
.getAuthenticationManager(myProject
);
473 void dumpFileStatus(FileStatus fs
) {
474 if (LOG
.isDebugEnabled()) {
475 LOG
.debug("FileStatus:" + fs
.getText() + " " + fs
.getColor() + " " + " " + fs
.getClass().getName());
479 public UpdateEnvironment
getIntegrateEnvironment() {
480 if (mySvnIntegrateEnvironment
== null) {
481 mySvnIntegrateEnvironment
= new SvnIntegrateEnvironment(this);
483 return mySvnIntegrateEnvironment
;
486 public UpdateEnvironment
getUpdateEnvironment() {
487 if (mySvnUpdateEnvironment
== null) {
488 mySvnUpdateEnvironment
= new SvnUpdateEnvironment(this);
490 return mySvnUpdateEnvironment
;
493 public String
getDisplayName() {
494 LOG
.debug("getDisplayName");
498 public Configurable
getConfigurable() {
499 LOG
.debug("createConfigurable");
500 return new SvnConfigurable(myProject
);
504 public SvnConfiguration
getSvnConfiguration() {
505 return myConfiguration
;
508 public static SvnVcs
getInstance(Project project
) {
509 return (SvnVcs
) ProjectLevelVcsManager
.getInstance(project
).findVcsByName(VCS_NAME
);
513 public CheckinEnvironment
getCheckinEnvironment() {
514 if (myCheckinEnvironment
== null) {
515 myCheckinEnvironment
= new SvnCheckinEnvironment(this);
517 return myCheckinEnvironment
;
521 public RollbackEnvironment
getRollbackEnvironment() {
522 if (myRollbackEnvironment
== null) {
523 myRollbackEnvironment
= new SvnRollbackEnvironment(this);
525 return myRollbackEnvironment
;
528 public VcsHistoryProvider
getVcsHistoryProvider() {
529 // no heavy state, but it would be useful to have place to keep state in -> do not reuse instance
530 return new SvnHistoryProvider(this);
533 public VcsHistoryProvider
getVcsBlockHistoryProvider() {
534 return getVcsHistoryProvider();
537 public AnnotationProvider
getAnnotationProvider() {
538 if (myAnnotationProvider
== null) {
539 myAnnotationProvider
= new SvnAnnotationProvider(this);
541 return myAnnotationProvider
;
544 public SvnEntriesFileListener
getSvnEntriesFileListener() {
545 return myEntriesFileListener
;
548 public DiffProvider
getDiffProvider() {
549 if (mySvnDiffProvider
== null) {
550 mySvnDiffProvider
= new SvnDiffProvider(this);
552 return mySvnDiffProvider
;
555 private Trinity
<Long
, Long
, Long
> getTimestampForPropertiesChange(final File ioFile
, final boolean isDir
) {
556 final File dir
= isDir ? ioFile
: ioFile
.getParentFile();
557 final String relPath
= SVNAdminUtil
.getPropPath(ioFile
.getName(), isDir ? SVNNodeKind
.DIR
: SVNNodeKind
.FILE
, false);
558 final String relPathBase
= SVNAdminUtil
.getPropBasePath(ioFile
.getName(), isDir ? SVNNodeKind
.DIR
: SVNNodeKind
.FILE
, false);
559 final String relPathRevert
= SVNAdminUtil
.getPropRevertPath(ioFile
.getName(), isDir ? SVNNodeKind
.DIR
: SVNNodeKind
.FILE
, false);
560 return new Trinity
<Long
, Long
, Long
>(new File(dir
, relPath
).lastModified(), new File(dir
, relPathBase
).lastModified(),
561 new File(dir
, relPathRevert
).lastModified());
564 private boolean trinitiesEqual(final Trinity
<Long
, Long
, Long
> t1
, final Trinity
<Long
, Long
, Long
> t2
) {
565 if (t2
.first
== 0 && t2
.second
== 0 && t2
.third
== 0) return false;
566 return t1
.equals(t2
);
570 public SVNPropertyValue
getPropertyWithCaching(final VirtualFile file
, final String propName
) throws SVNException
{
571 Map
<String
, Pair
<SVNPropertyValue
, Trinity
<Long
, Long
, Long
>>> cachedMap
= myPropertyCache
.get(keyForVf(file
));
572 final Pair
<SVNPropertyValue
, Trinity
<Long
, Long
, Long
>> cachedValue
= (cachedMap
== null) ?
null : cachedMap
.get(propName
);
574 final File ioFile
= new File(file
.getPath());
575 final Trinity
<Long
, Long
, Long
> tsTrinity
= getTimestampForPropertiesChange(ioFile
, file
.isDirectory());
577 if (cachedValue
!= null) {
578 // zero means that a file was not found
579 if (trinitiesEqual(cachedValue
.getSecond(), tsTrinity
)) {
580 return cachedValue
.getFirst();
584 final SVNPropertyData value
= createWCClient().doGetProperty(ioFile
, propName
, SVNRevision
.WORKING
, SVNRevision
.WORKING
);
585 final SVNPropertyValue propValue
= (value
== null) ?
null : value
.getValue();
587 if (cachedMap
== null) {
588 cachedMap
= new HashMap
<String
, Pair
<SVNPropertyValue
, Trinity
<Long
, Long
, Long
>>>();
589 myPropertyCache
.put(keyForVf(file
), cachedMap
);
592 cachedMap
.put(propName
, new Pair
<SVNPropertyValue
, Trinity
<Long
, Long
, Long
>>(propValue
, tsTrinity
));
597 public boolean fileExistsInVcs(FilePath path
) {
598 File file
= path
.getIOFile();
601 status
= createStatusClient().doStatus(file
, false);
602 if (status
!= null) {
603 final SVNStatusType statusType
= status
.getContentsStatus();
604 if (statusType
== SVNStatusType
.STATUS_ADDED
) {
605 return status
.isCopied();
607 return !(status
.getContentsStatus() == SVNStatusType
.STATUS_UNVERSIONED
||
608 status
.getContentsStatus() == SVNStatusType
.STATUS_IGNORED
||
609 status
.getContentsStatus() == SVNStatusType
.STATUS_OBSTRUCTED
);
612 catch (SVNException e
) {
618 public boolean fileIsUnderVcs(FilePath path
) {
619 final ChangeListManager clManager
= ChangeListManager
.getInstance(myProject
);
620 return (! SvnStatusUtil
.isIgnoredInAnySense(clManager
, path
.getVirtualFile())) && (! clManager
.isUnversioned(path
.getVirtualFile()));
623 private static File
getEntriesFile(File file
) {
624 return file
.isDirectory() ?
new File(file
, pathToEntries
) : new File(file
.getParentFile(), pathToEntries
);
627 private static File
getDirPropsFile(File file
) {
628 return new File(file
, pathToDirProps
);
632 public SVNInfo
getInfo(final VirtualFile file
) {
634 SVNWCClient wcClient
= new SVNWCClient(getSvnAuthenticationManager(), getSvnOptions());
635 SVNInfo info
= wcClient
.doInfo(new File(file
.getPath()), SVNRevision
.WORKING
);
636 if (info
== null || info
.getRepositoryRootURL() == null) {
637 info
= wcClient
.doInfo(new File(file
.getPath()), SVNRevision
.HEAD
);
641 catch (SVNException e
) {
646 public static class SVNStatusHolder
{
648 private final SVNStatus myValue
;
649 private final long myEntriesTimestamp
;
650 private final long myFileTimestamp
;
651 private final boolean myIsLocked
;
653 public SVNStatusHolder(long entriesStamp
, long fileStamp
, SVNStatus value
) {
655 myEntriesTimestamp
= entriesStamp
;
656 myFileTimestamp
= fileStamp
;
657 myIsLocked
= value
!= null && value
.isLocked();
660 public long getEntriesTimestamp() {
661 return myEntriesTimestamp
;
664 public long getFileTimestamp() {
665 return myFileTimestamp
;
668 public boolean isLocked() {
672 public SVNStatus
getStatus() {
677 public static class SVNInfoHolder
{
679 private final SVNInfo myValue
;
680 private final long myEntriesTimestamp
;
681 private final long myFileTimestamp
;
683 public SVNInfoHolder(long entriesStamp
, long fileStamp
, SVNInfo value
) {
685 myEntriesTimestamp
= entriesStamp
;
686 myFileTimestamp
= fileStamp
;
689 public long getEntriesTimestamp() {
690 return myEntriesTimestamp
;
693 public long getFileTimestamp() {
694 return myFileTimestamp
;
697 public SVNInfo
getInfo() {
702 private static class JavaSVNDebugLogger
extends SVNDebugLogAdapter
{
703 private final boolean myLoggingEnabled
;
704 private final Logger myLog
;
705 @NonNls public static final String TRACE_LOG_PARAMETER_NAME
= "javasvn.log.trace";
707 public JavaSVNDebugLogger(boolean loggingEnabled
, Logger log
) {
708 myLoggingEnabled
= loggingEnabled
;
712 public void log(final SVNLogType logType
, final Throwable th
, final Level logLevel
) {
713 if (myLoggingEnabled
) {
718 public void log(final SVNLogType logType
, final String message
, final Level logLevel
) {
719 if (myLoggingEnabled
) {
724 public void log(final SVNLogType logType
, final String message
, final byte[] data
) {
725 if (myLoggingEnabled
) {
728 myLog
.info(message
+ "\n" + new String(data
, "UTF-8"));
730 catch (UnsupportedEncodingException e
) {
731 myLog
.info(message
+ "\n" + new String(data
));
740 public FileStatus
[] getProvidedStatuses() {
741 return new FileStatus
[]{SvnFileStatus
.EXTERNAL
,
742 SvnFileStatus
.OBSTRUCTED
,
743 SvnFileStatus
.REPLACED
};
748 public CommittedChangesProvider
<SvnChangeList
, ChangeBrowserSettings
> getCommittedChangesProvider() {
749 if (myCommittedChangesProvider
== null) {
750 myCommittedChangesProvider
= new SvnCommittedChangesProvider(myProject
);
752 return myCommittedChangesProvider
;
757 public VcsRevisionNumber
parseRevisionNumber(final String revisionNumberString
) {
758 final SVNRevision revision
= SVNRevision
.parse(revisionNumberString
);
759 if (revision
.equals(SVNRevision
.UNDEFINED
)) {
762 return new SvnRevisionNumber(revision
);
766 public String
getRevisionPattern() {
767 return ourIntegerPattern
;
771 public boolean isVersionedDirectory(final VirtualFile dir
) {
772 return SvnUtil
.seemsLikeVersionedDir(dir
);
776 public SvnFileUrlMapping
getSvnFileUrlMapping() {
777 if (myMapping
== null) {
778 myMapping
= SvnFileUrlMappingImpl
.getInstance(myProject
);
784 * Returns real working copies roots - if there is <Project Root> -> Subversion setting,
785 * and there is one working copy, will return one root
787 public List
<WCInfo
> getAllWcInfos() {
788 final SvnFileUrlMapping urlMapping
= getSvnFileUrlMapping();
790 final List
<RootUrlInfo
> infoList
= urlMapping
.getAllWcInfos();
791 final List
<WCInfo
> infos
= new ArrayList
<WCInfo
>();
792 for (RootUrlInfo info
: infoList
) {
793 final File file
= info
.getIoFile();
794 infos
.add(new WCInfo(file
.getAbsolutePath(), info
.getAbsoluteUrlAsUrl(),
795 SvnFormatSelector
.getWorkingCopyFormat(file
), info
.getRepositoryUrl(), SvnUtil
.isWorkingCopyRoot(file
)));
800 private class SvnWorkingCopyChecker
{
801 private List
<WCInfo
> myAllWcInfos
;
803 public boolean upgradeNeeded() {
804 myAllWcInfos
= getAllWcInfos();
805 for (WCInfo info
: myAllWcInfos
) {
806 if (! WorkingCopyFormat
.ONE_DOT_SIX
.equals(info
.getFormat())) {
813 public void doUpgrade() {
814 ApplicationManager
.getApplication().invokeLater(new Runnable() {
816 final SvnFormatWorker formatWorker
= new SvnFormatWorker(myProject
, WorkingCopyFormat
.ONE_DOT_SIX
, myAllWcInfos
);
817 // additionally ask about working copies with roots above the project root
818 formatWorker
.checkForOutsideCopies();
819 if (formatWorker
.haveStuffToConvert()) {
820 ProgressManager
.getInstance().run(formatWorker
);
828 public RootsConvertor
getCustomConvertor() {
829 return getSvnFileUrlMapping();
833 public MergeProvider
getMergeProvider() {
834 if (myMergeProvider
== null) {
835 myMergeProvider
= new SvnMergeProvider(myProject
);
837 return myMergeProvider
;
841 public List
<AnAction
> getAdditionalActionsForLocalChange() {
842 return Arrays
.<AnAction
>asList(new ShowPropertiesDiffWithLocalAction());
845 public void pathChanged(final File from
, final File to
) throws SVNException
{
846 myChangeListListener
.pathChanged(from
, to
);
849 private String
keyForVf(final VirtualFile vf
) {
854 public boolean allowsNestedRoots() {
855 return SvnConfiguration
.getInstance(myProject
).DETECT_NESTED_COPIES
;
859 public <S
> List
<S
> filterUniqueRoots(final List
<S
> in
, final Convertor
<S
, VirtualFile
> convertor
) {
860 if (in
.size() <= 1) return in
;
862 final List
<MyPair
<S
>> infos
= new ArrayList
<MyPair
<S
>>(in
.size());
863 final SvnFileUrlMappingImpl mapping
= (SvnFileUrlMappingImpl
) getSvnFileUrlMapping();
865 final VirtualFile vf
= convertor
.convert(s
);
867 final File ioFile
= new File(vf
.getPath());
868 final SVNURL url
= mapping
.getUrlForFile(ioFile
);
869 if (url
== null) continue;
870 infos
.add(new MyPair
<S
>(vf
, url
.toString(), s
));
872 final List
<MyPair
<S
>> filtered
= new ArrayList
<MyPair
<S
>>(infos
.size());
873 ForNestedRootChecker
.filterOutSuperfluousChildren(this, infos
, filtered
);
875 return ObjectsConvertor
.convert(filtered
, new Convertor
<MyPair
<S
>, S
>() {
876 public S
convert(final MyPair
<S
> o
) {
882 private static class MyPair
<T
> implements RootUrlPair
{
883 private final VirtualFile myFile
;
884 private final String myUrl
;
885 private final T mySrc
;
887 private MyPair(VirtualFile file
, String url
, T src
) {
897 public VirtualFile
getVirtualFile() {
901 public String
getUrl() {
906 private static class MyFrameStateListener
implements FrameStateListener
{
907 private final ChangeListManager myClManager
;
908 private final VcsDirtyScopeManager myDirtyScopeManager
;
910 private MyFrameStateListener(ChangeListManager clManager
, VcsDirtyScopeManager dirtyScopeManager
) {
911 myClManager
= clManager
;
912 myDirtyScopeManager
= dirtyScopeManager
;
915 public void onFrameDeactivated() {
918 public void onFrameActivated() {
919 final List
<VirtualFile
> folders
= ((ChangeListManagerImpl
)myClManager
).getLockedFolders();
920 if (! folders
.isEmpty()) {
921 myDirtyScopeManager
.filesDirty(null, folders
);
926 public static VcsKey
getKey() {