blob: 55b79b0636146b5a6ac86bccfd5765849f23c11a [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn;
import com.intellij.ide.FrameStateListener;
import com.intellij.ide.FrameStateManager;
import com.intellij.idea.RareLogger;
import com.intellij.notification.*;
import com.intellij.notification.impl.NotificationsConfigurationImpl;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.DumbAwareRunnable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.util.PopupUtil;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.Trinity;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.annotate.AnnotationProvider;
import com.intellij.openapi.vcs.changes.*;
import com.intellij.openapi.vcs.checkin.CheckinEnvironment;
import com.intellij.openapi.vcs.diff.DiffProvider;
import com.intellij.openapi.vcs.history.VcsHistoryProvider;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vcs.merge.MergeProvider;
import com.intellij.openapi.vcs.rollback.RollbackEnvironment;
import com.intellij.openapi.vcs.update.UpdateEnvironment;
import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.util.Consumer;
import com.intellij.util.Processor;
import com.intellij.util.ThreeState;
import com.intellij.util.containers.Convertor;
import com.intellij.util.containers.SoftHashMap;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.messages.Topic;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.actions.CleanupWorker;
import org.jetbrains.idea.svn.actions.ShowPropertiesDiffWithLocalAction;
import org.jetbrains.idea.svn.actions.SvnMergeProvider;
import org.jetbrains.idea.svn.annotate.SvnAnnotationProvider;
import org.jetbrains.idea.svn.api.ClientFactory;
import org.jetbrains.idea.svn.api.CmdClientFactory;
import org.jetbrains.idea.svn.api.SvnKitClientFactory;
import org.jetbrains.idea.svn.checkin.SvnCheckinEnvironment;
import org.jetbrains.idea.svn.checkout.SvnCheckoutProvider;
import org.jetbrains.idea.svn.commandLine.SvnExecutableChecker;
import org.jetbrains.idea.svn.dialogs.SvnBranchPointsCalculator;
import org.jetbrains.idea.svn.dialogs.WCInfo;
import org.jetbrains.idea.svn.history.LoadedRevisionsCache;
import org.jetbrains.idea.svn.history.SvnChangeList;
import org.jetbrains.idea.svn.history.SvnCommittedChangesProvider;
import org.jetbrains.idea.svn.history.SvnHistoryProvider;
import org.jetbrains.idea.svn.lowLevel.PrimitivePool;
import org.jetbrains.idea.svn.networking.SSLProtocolExceptionParser;
import org.jetbrains.idea.svn.portable.SvnWcClientI;
import org.jetbrains.idea.svn.properties.PropertyClient;
import org.jetbrains.idea.svn.rollback.SvnRollbackEnvironment;
import org.jetbrains.idea.svn.update.SvnIntegrateEnvironment;
import org.jetbrains.idea.svn.update.SvnUpdateEnvironment;
import org.tmatesoft.sqljet.core.SqlJetErrorCode;
import org.tmatesoft.sqljet.core.SqlJetException;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.internal.util.SVNSSLUtil;
import org.tmatesoft.svn.core.internal.util.jna.SVNJNAUtil;
import org.tmatesoft.svn.core.internal.wc.SVNAdminUtil;
import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminArea14;
import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminAreaFactory;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.*;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import org.tmatesoft.svn.util.SVNDebugLog;
import org.tmatesoft.svn.util.SVNDebugLogAdapter;
import org.tmatesoft.svn.util.SVNLogType;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLProtocolException;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.security.cert.CertificateException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
@SuppressWarnings({"IOResourceOpenedButNotSafelyClosed"})
public class SvnVcs extends AbstractVcs<CommittedChangeList> {
private static final String DO_NOT_LISTEN_TO_WC_DB = "svn.do.not.listen.to.wc.db";
private static final String KEEP_CONNECTIONS_KEY = "svn.keep.connections";
private static final Logger REFRESH_LOG = Logger.getInstance("#svn_refresh");
public static boolean ourListenToWcDb = true;
private static final int ourLogUsualInterval = 20 * 1000;
private static final int ourLogRareInterval = 30 * 1000;
private static final Set<SVNErrorCode> ourLogRarely = new HashSet<SVNErrorCode>(
Arrays.asList(new SVNErrorCode[]{SVNErrorCode.WC_UNSUPPORTED_FORMAT, SVNErrorCode.WC_CORRUPT, SVNErrorCode.WC_CORRUPT_TEXT_BASE,
SVNErrorCode.WC_NOT_FILE, SVNErrorCode.WC_NOT_DIRECTORY, SVNErrorCode.WC_PATH_NOT_FOUND}));
private static final Logger LOG = wrapLogger(Logger.getInstance("org.jetbrains.idea.svn.SvnVcs"));
@NonNls public static final String VCS_NAME = "svn";
public static final String VCS_DISPLAY_NAME = "Subversion";
private static final VcsKey ourKey = createKey(VCS_NAME);
public static final Topic<Runnable> WC_CONVERTED = new Topic<Runnable>("WC_CONVERTED", Runnable.class);
private final Map<String, Map<String, Pair<SVNPropertyValue, Trinity<Long, Long, Long>>>> myPropertyCache =
new SoftHashMap<String, Map<String, Pair<SVNPropertyValue, Trinity<Long, Long, Long>>>>();
private final SvnConfiguration myConfiguration;
private final SvnEntriesFileListener myEntriesFileListener;
private CheckinEnvironment myCheckinEnvironment;
private RollbackEnvironment myRollbackEnvironment;
private UpdateEnvironment mySvnUpdateEnvironment;
private UpdateEnvironment mySvnIntegrateEnvironment;
private VcsHistoryProvider mySvnHistoryProvider;
private AnnotationProvider myAnnotationProvider;
private DiffProvider mySvnDiffProvider;
private final VcsShowConfirmationOption myAddConfirmation;
private final VcsShowConfirmationOption myDeleteConfirmation;
private EditFileProvider myEditFilesProvider;
private SvnCommittedChangesProvider myCommittedChangesProvider;
private final VcsShowSettingOption myCheckoutOptions;
private ChangeProvider myChangeProvider;
private MergeProvider myMergeProvider;
private final WorkingCopiesContent myWorkingCopiesContent;
@NonNls public static final String LOG_PARAMETER_NAME = "javasvn.log";
@NonNls public static final String TRACE_NATIVE_CALLS = "javasvn.log.native";
public static final String pathToEntries = SvnUtil.SVN_ADMIN_DIR_NAME + File.separatorChar + SvnUtil.ENTRIES_FILE_NAME;
public static final String pathToDirProps = SvnUtil.SVN_ADMIN_DIR_NAME + File.separatorChar + SvnUtil.DIR_PROPS_FILE_NAME;
private final SvnChangelistListener myChangeListListener;
private SvnCopiesRefreshManager myCopiesRefreshManager;
private SvnFileUrlMappingImpl myMapping;
private final MyFrameStateListener myFrameStateListener;
//Consumer<Boolean>
public static final Topic<Consumer> ROOTS_RELOADED = new Topic<Consumer>("ROOTS_RELOADED", Consumer.class);
private VcsListener myVcsListener;
private SvnBranchPointsCalculator mySvnBranchPointsCalculator;
private final RootsToWorkingCopies myRootsToWorkingCopies;
private final SvnAuthenticationNotifier myAuthNotifier;
private static RareLogger.LogFilter[] ourLogFilters;
private final SvnLoadedBrachesStorage myLoadedBranchesStorage;
public static final String SVNKIT_HTTP_SSL_PROTOCOLS = "svnkit.http.sslProtocols";
private static boolean ourSSLProtocolsExplicitlySet = false;
private final SvnExecutableChecker myChecker;
public static final Processor<Exception> ourBusyExceptionProcessor = new Processor<Exception>() {
@Override
public boolean process(Exception e) {
if (e instanceof SVNException) {
final SVNErrorCode errorCode = ((SVNException)e).getErrorMessage().getErrorCode();
if (SVNErrorCode.WC_LOCKED.equals(errorCode)) {
return true;
} else if (SVNErrorCode.SQLITE_ERROR.equals(errorCode)) {
Throwable cause = ((SVNException)e).getErrorMessage().getCause();
if (cause instanceof SqlJetException) {
return SqlJetErrorCode.BUSY.equals(((SqlJetException)cause).getErrorCode());
}
}
}
return false;
}
};
private SvnCheckoutProvider myCheckoutProvider;
private final ClientFactory cmdClientFactory;
private final ClientFactory svnKitClientFactory;
private final boolean myLogExceptions;
static {
System.setProperty("svnkit.log.native.calls", "true");
if (Boolean.getBoolean(DO_NOT_LISTEN_TO_WC_DB)) {
ourListenToWcDb = false;
}
final JavaSVNDebugLogger logger = new JavaSVNDebugLogger(Boolean.getBoolean(LOG_PARAMETER_NAME), Boolean.getBoolean(TRACE_NATIVE_CALLS), LOG);
SVNDebugLog.setDefaultLog(logger);
SVNJNAUtil.setJNAEnabled(true);
SvnHttpAuthMethodsDefaultChecker.check();
SVNAdminAreaFactory.setSelector(new SvnFormatSelector());
DAVRepositoryFactory.setup();
SVNRepositoryFactoryImpl.setup();
FSRepositoryFactory.setup();
// non-optimized writing is fast enough on Linux/MacOS, and somewhat more reliable
if (SystemInfo.isWindows) {
SVNAdminArea14.setOptimizedWritingEnabled(true);
}
if (!SVNJNAUtil.isJNAPresent()) {
LOG.warn("JNA is not found by svnkit library");
}
initLogFilters();
ourSSLProtocolsExplicitlySet = System.getProperty(SVNKIT_HTTP_SSL_PROTOCOLS) != null;
}
public static boolean isSSLProtocolExplicitlySet() {
return ourSSLProtocolsExplicitlySet;
}
public SvnVcs(final Project project, MessageBus bus, SvnConfiguration svnConfiguration, final SvnLoadedBrachesStorage storage) {
super(project, VCS_NAME);
cmdClientFactory = new CmdClientFactory(this);
svnKitClientFactory = new SvnKitClientFactory(this);
myLoadedBranchesStorage = storage;
myRootsToWorkingCopies = new RootsToWorkingCopies(this);
myConfiguration = svnConfiguration;
myAuthNotifier = new SvnAuthenticationNotifier(this);
dumpFileStatus(FileStatus.ADDED);
dumpFileStatus(FileStatus.DELETED);
dumpFileStatus(FileStatus.MERGE);
dumpFileStatus(FileStatus.MODIFIED);
dumpFileStatus(FileStatus.NOT_CHANGED);
dumpFileStatus(FileStatus.UNKNOWN);
dumpFileStatus(SvnFileStatus.REPLACED);
dumpFileStatus(SvnFileStatus.EXTERNAL);
dumpFileStatus(SvnFileStatus.OBSTRUCTED);
refreshSSLProperty();
final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(project);
myAddConfirmation = vcsManager.getStandardConfirmation(VcsConfiguration.StandardConfirmation.ADD, this);
myDeleteConfirmation = vcsManager.getStandardConfirmation(VcsConfiguration.StandardConfirmation.REMOVE, this);
myCheckoutOptions = vcsManager.getStandardOption(VcsConfiguration.StandardOption.CHECKOUT, this);
if (myProject.isDefault()) {
myChangeListListener = null;
myEntriesFileListener = null;
}
else {
myEntriesFileListener = new SvnEntriesFileListener(project);
upgradeIfNeeded(bus);
myChangeListListener = new SvnChangelistListener(myProject, this);
myVcsListener = new VcsListener() {
@Override
public void directoryMappingChanged() {
invokeRefreshSvnRoots();
}
};
}
myFrameStateListener = project.isDefault() ? null : new MyFrameStateListener(ChangeListManager.getInstance(project),
VcsDirtyScopeManager.getInstance(project));
myWorkingCopiesContent = new WorkingCopiesContent(this);
// remove used some time before old notification group ids
correctNotificationIds();
myChecker = new SvnExecutableChecker(myProject);
Application app = ApplicationManager.getApplication();
myLogExceptions = app != null && (app.isInternal() || app.isUnitTestMode());
}
private void correctNotificationIds() {
boolean notEmpty = NotificationsConfigurationImpl.getNotificationsConfigurationImpl().isRegistered("SVN_NO_JNA") ||
NotificationsConfigurationImpl.getNotificationsConfigurationImpl().isRegistered("SVN_NO_CRYPT32") ||
NotificationsConfigurationImpl.getNotificationsConfigurationImpl().isRegistered("SubversionId");
if (notEmpty) {
NotificationsConfigurationImpl.remove("SVN_NO_JNA", "SVN_NO_CRYPT32", "SubversionId");
NotificationsConfiguration.getNotificationsConfiguration().register(getDisplayName(), NotificationDisplayType.BALLOON);
}
}
public void postStartup() {
if (myProject.isDefault()) return;
myCopiesRefreshManager = new SvnCopiesRefreshManager((SvnFileUrlMappingImpl) getSvnFileUrlMapping());
if (! myConfiguration.isCleanupRun()) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
cleanup17copies();
myConfiguration.setCleanupRun(true);
}
}, ModalityState.NON_MODAL, myProject.getDisposed());
} else {
invokeRefreshSvnRoots();
}
myWorkingCopiesContent.activate();
}
private void cleanup17copies() {
final Runnable callCleanupWorker = new Runnable() {
public void run() {
if (myProject.isDisposed()) return;
new CleanupWorker(new VirtualFile[]{}, myProject, "action.Subversion.cleanup.progress.title") {
@Override
protected void chanceToFillRoots() {
final List<WCInfo> infos = getAllWcInfos();
final LocalFileSystem lfs = LocalFileSystem.getInstance();
final List<VirtualFile> roots = new ArrayList<VirtualFile>(infos.size());
for (WCInfo info : infos) {
if (WorkingCopyFormat.ONE_DOT_SEVEN.equals(info.getFormat())) {
final VirtualFile file = lfs.refreshAndFindFileByIoFile(new File(info.getPath()));
if (file == null) {
LOG.info("Wasn't able to find virtual file for wc root: " + info.getPath());
} else {
roots.add(file);
}
}
}
myRoots = roots.toArray(new VirtualFile[roots.size()]);
}
}.execute();
}
};
myCopiesRefreshManager.waitRefresh(new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().invokeLater(callCleanupWorker, ModalityState.any());
}
});
}
public boolean checkCommandLineVersion() {
boolean isValid = true;
if (!isProject16() && (myConfiguration.isCommandLine() || isProject18())) {
isValid = myChecker.checkExecutableAndNotifyIfNeeded();
}
return isValid;
}
public void invokeRefreshSvnRoots() {
if (REFRESH_LOG.isDebugEnabled()) {
REFRESH_LOG.debug("refresh: ", new Throwable());
}
if (myCopiesRefreshManager != null) {
myCopiesRefreshManager.asynchRequest();
}
}
@Override
public boolean checkImmediateParentsBeforeCommit() {
return true;
}
private void upgradeIfNeeded(final MessageBus bus) {
final MessageBusConnection connection = bus.connect();
connection.subscribe(ChangeListManagerImpl.LISTS_LOADED, new LocalChangeListsLoadedListener() {
@Override
public void processLoadedLists(final List<LocalChangeList> lists) {
if (lists.isEmpty()) return;
SvnConfiguration.SvnSupportOptions supportOptions = null;
try {
ChangeListManager.getInstance(myProject).setReadOnly(SvnChangeProvider.ourDefaultListName, true);
supportOptions = myConfiguration.getSupportOptions(myProject);
if (!supportOptions.changeListsSynchronized()) {
processChangeLists(lists);
}
}
catch (ProcessCanceledException e) {
//
}
finally {
if (supportOptions != null) {
supportOptions.upgrade();
}
}
connection.disconnect();
}
});
}
public void processChangeLists(final List<LocalChangeList> lists) {
final ProjectLevelVcsManager plVcsManager = ProjectLevelVcsManager.getInstanceChecked(myProject);
plVcsManager.startBackgroundVcsOperation();
try {
for (LocalChangeList list : lists) {
if (!list.isDefault()) {
final Collection<Change> changes = list.getChanges();
for (Change change : changes) {
correctListForRevision(plVcsManager, change.getBeforeRevision(), list.getName());
correctListForRevision(plVcsManager, change.getAfterRevision(), list.getName());
}
}
}
}
finally {
final Application appManager = ApplicationManager.getApplication();
if (appManager.isDispatchThread()) {
appManager.executeOnPooledThread(new Runnable() {
@Override
public void run() {
plVcsManager.stopBackgroundVcsOperation();
}
});
}
else {
plVcsManager.stopBackgroundVcsOperation();
}
}
}
private void correctListForRevision(@NotNull final ProjectLevelVcsManager plVcsManager,
@Nullable final ContentRevision revision,
@NotNull final String name) {
if (revision != null) {
final FilePath path = revision.getFile();
final AbstractVcs vcs = plVcsManager.getVcsFor(path);
if (vcs != null && VCS_NAME.equals(vcs.getName())) {
try {
getFactory(path.getIOFile()).createChangeListClient().add(name, path.getIOFile(), null);
}
catch (VcsException e) {
// left in default list
}
}
}
}
@Override
public void activate() {
final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myProject);
if (!myProject.isDefault()) {
ChangeListManager.getInstance(myProject).addChangeListListener(myChangeListListener);
vcsManager.addVcsListener(myVcsListener);
}
SvnApplicationSettings.getInstance().svnActivated();
if (myEntriesFileListener != null) {
VirtualFileManager.getInstance().addVirtualFileListener(myEntriesFileListener);
}
// this will initialize its inner listener for committed changes upload
LoadedRevisionsCache.getInstance(myProject);
FrameStateManager.getInstance().addListener(myFrameStateListener);
myAuthNotifier.init();
mySvnBranchPointsCalculator = new SvnBranchPointsCalculator(myProject);
mySvnBranchPointsCalculator.activate();
if (SystemInfo.isWindows) {
if (!SVNJNAUtil.isJNAPresent()) {
Notifications.Bus.notify(new Notification(getDisplayName(), "Subversion plugin: no JNA",
"A problem with JNA initialization for SVNKit library. Encryption is not available.",
NotificationType.WARNING), myProject);
}
else if (!SVNJNAUtil.isWinCryptEnabled()) {
Notifications.Bus.notify(new Notification(getDisplayName(), "Subversion plugin: no encryption",
"A problem with encryption module (Crypt32.dll) initialization for SVNKit library. " +
"Encryption is not available.",
NotificationType.WARNING), myProject);
}
}
final SvnConfiguration.UseAcceleration accelerationType = SvnConfiguration.getInstance(myProject).myUseAcceleration;
if (SvnConfiguration.UseAcceleration.javaHL.equals(accelerationType)) {
CheckJavaHL.runtimeCheck(myProject);
}
else if (!ApplicationManager.getApplication().isHeadlessEnvironment()) {
checkCommandLineVersion();
}
// do one time after project loaded
StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new DumbAwareRunnable() {
@Override
public void run() {
postStartup();
// for IDEA, it takes 2 minutes - and anyway this can be done in background, no sense...
// once it could be mistaken about copies for 2 minutes on start...
/*if (! myMapping.getAllWcInfos().isEmpty()) {
invokeRefreshSvnRoots();
return;
}
ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
public void run() {
myCopiesRefreshManager.getCopiesRefresh().ensureInit();
}
}, SvnBundle.message("refreshing.working.copies.roots.progress.text"), true, myProject);*/
}
});
vcsManager.addVcsListener(myRootsToWorkingCopies);
myLoadedBranchesStorage.activate();
}
private static void initLogFilters() {
if (ourLogFilters != null) return;
ourLogFilters = new RareLogger.LogFilter[]{new RareLogger.LogFilter() {
@Override
public Object getKey(@NotNull org.apache.log4j.Level level,
@NonNls String message,
@Nullable Throwable t,
@NonNls String... details) {
SVNException svnExc = null;
if (t instanceof SVNException) {
svnExc = (SVNException)t;
}
else if (t instanceof VcsException && t.getCause() instanceof SVNException) {
svnExc = (SVNException)t.getCause();
}
if (svnExc != null) {
// only filter a few cases
if (ourLogRarely.contains(svnExc.getErrorMessage().getErrorCode())) {
return svnExc.getErrorMessage().getErrorCode();
}
}
return null;
}
@Override
@NotNull
public Integer getAllowedLoggingInterval(org.apache.log4j.Level level, String message, Throwable t, String[] details) {
SVNException svnExc = null;
if (t instanceof SVNException) {
svnExc = (SVNException)t;
}
else if (t instanceof VcsException && t.getCause() instanceof SVNException) {
svnExc = (SVNException)t.getCause();
}
if (svnExc != null) {
if (ourLogRarely.contains(svnExc.getErrorMessage().getErrorCode())) {
return ourLogRareInterval;
}
else {
return ourLogUsualInterval;
}
}
return 0;
}
}};
}
public static Logger wrapLogger(final Logger logger) {
initLogFilters();
return RareLogger.wrap(logger, Boolean.getBoolean("svn.logger.fairsynch"), ourLogFilters);
}
public RootsToWorkingCopies getRootsToWorkingCopies() {
return myRootsToWorkingCopies;
}
public SvnAuthenticationNotifier getAuthNotifier() {
return myAuthNotifier;
}
@Override
public void deactivate() {
FrameStateManager.getInstance().removeListener(myFrameStateListener);
final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myProject);
if (myVcsListener != null) {
vcsManager.removeVcsListener(myVcsListener);
}
if (myEntriesFileListener != null) {
VirtualFileManager.getInstance().removeVirtualFileListener(myEntriesFileListener);
}
SvnApplicationSettings.getInstance().svnDeactivated();
if (myCommittedChangesProvider != null) {
myCommittedChangesProvider.deactivate();
}
if (myChangeListListener != null && !myProject.isDefault()) {
ChangeListManager.getInstance(myProject).removeChangeListListener(myChangeListListener);
}
vcsManager.removeVcsListener(myRootsToWorkingCopies);
myRootsToWorkingCopies.clear();
myAuthNotifier.stop();
myAuthNotifier.clear();
mySvnBranchPointsCalculator.deactivate();
mySvnBranchPointsCalculator = null;
myWorkingCopiesContent.deactivate();
myLoadedBranchesStorage.deactivate();
}
public VcsShowConfirmationOption getAddConfirmation() {
return myAddConfirmation;
}
public VcsShowConfirmationOption getDeleteConfirmation() {
return myDeleteConfirmation;
}
public VcsShowSettingOption getCheckoutOptions() {
return myCheckoutOptions;
}
@Override
public EditFileProvider getEditFileProvider() {
if (myEditFilesProvider == null) {
myEditFilesProvider = new SvnEditFileProvider(this);
}
return myEditFilesProvider;
}
@Override
@NotNull
public ChangeProvider getChangeProvider() {
if (myChangeProvider == null) {
myChangeProvider = new SvnChangeProvider(this);
}
return myChangeProvider;
}
public SVNRepository createRepository(String url) throws SVNException {
SVNRepository repos = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(url));
repos.setAuthenticationManager(myConfiguration.getAuthenticationManager(this));
repos.setTunnelProvider(myConfiguration.getOptions(myProject));
return repos;
}
public SVNRepository createRepository(SVNURL url) throws SVNException {
SVNRepository repos = SVNRepositoryFactory.create(url);
repos.setAuthenticationManager(myConfiguration.getAuthenticationManager(this));
repos.setTunnelProvider(myConfiguration.getOptions(myProject));
return repos;
}
@NotNull
private ISVNRepositoryPool getPool() {
return getPool(myConfiguration.getAuthenticationManager(this));
}
@NotNull
private ISVNRepositoryPool getPool(ISVNAuthenticationManager manager) {
if (myProject.isDisposed()) {
throw new ProcessCanceledException();
}
return new PrimitivePool(manager, myConfiguration.getOptions(myProject));
}
public SVNUpdateClient createUpdateClient() {
final SVNUpdateClient client = new SVNUpdateClient(getPool(), myConfiguration.getOptions(myProject));
client.getOperationsFactory().setAuthenticationManager(myConfiguration.getAuthenticationManager(this));
return client;
}
public SVNUpdateClient createUpdateClient(@NotNull ISVNAuthenticationManager manager) {
final SVNUpdateClient client = new SVNUpdateClient(getPool(manager), myConfiguration.getOptions(myProject));
client.getOperationsFactory().setAuthenticationManager(manager);
return client;
}
public SVNStatusClient createStatusClient() {
SVNStatusClient client = new SVNStatusClient(getPool(), myConfiguration.getOptions(myProject));
client.getOperationsFactory().setAuthenticationManager(myConfiguration.getAuthenticationManager(this));
client.setIgnoreExternals(false);
return client;
}
public SVNWCClient createWCClient() {
final SVNWCClient client = new SVNWCClient(getPool(), myConfiguration.getOptions(myProject));
client.getOperationsFactory().setAuthenticationManager(myConfiguration.getAuthenticationManager(this));
return client;
}
public SVNWCClient createWCClient(@NotNull ISVNAuthenticationManager manager) {
final SVNWCClient client = new SVNWCClient(getPool(manager), myConfiguration.getOptions(myProject));
client.getOperationsFactory().setAuthenticationManager(manager);
return client;
}
public SVNCopyClient createCopyClient() {
final SVNCopyClient client = new SVNCopyClient(getPool(), myConfiguration.getOptions(myProject));
client.getOperationsFactory().setAuthenticationManager(myConfiguration.getAuthenticationManager(this));
return client;
}
public SVNMoveClient createMoveClient() {
final SVNMoveClient client = new SVNMoveClient(getPool(), myConfiguration.getOptions(myProject));
client.getOperationsFactory().setAuthenticationManager(myConfiguration.getAuthenticationManager(this));
return client;
}
public SVNLogClient createLogClient() {
final SVNLogClient client = new SVNLogClient(getPool(), myConfiguration.getOptions(myProject));
client.getOperationsFactory().setAuthenticationManager(myConfiguration.getAuthenticationManager(this));
return client;
}
public SVNLogClient createLogClient(@NotNull ISVNAuthenticationManager manager) {
final SVNLogClient client = new SVNLogClient(getPool(manager), myConfiguration.getOptions(myProject));
client.getOperationsFactory().setAuthenticationManager(manager);
return client;
}
public SVNCommitClient createCommitClient() {
final SVNCommitClient client = new SVNCommitClient(getPool(), myConfiguration.getOptions(myProject));
client.getOperationsFactory().setAuthenticationManager(myConfiguration.getAuthenticationManager(this));
return client;
}
public SVNDiffClient createDiffClient() {
final SVNDiffClient client = new SVNDiffClient(getPool(), myConfiguration.getOptions(myProject));
client.getOperationsFactory().setAuthenticationManager(myConfiguration.getAuthenticationManager(this));
return client;
}
public SVNChangelistClient createChangelistClient() {
final SVNChangelistClient client = new SVNChangelistClient(getPool(), myConfiguration.getOptions(myProject));
client.getOperationsFactory().setAuthenticationManager(myConfiguration.getAuthenticationManager(this));
return client;
}
public ISVNOptions getSvnOptions() {
return myConfiguration.getOptions(myProject);
}
public ISVNAuthenticationManager getSvnAuthenticationManager() {
return myConfiguration.getAuthenticationManager(this);
}
void dumpFileStatus(FileStatus fs) {
if (LOG.isDebugEnabled()) {
LOG.debug("FileStatus:" + fs.getText() + " " + fs.getColor() + " " + " " + fs.getClass().getName());
}
}
@Override
public UpdateEnvironment getIntegrateEnvironment() {
if (mySvnIntegrateEnvironment == null) {
mySvnIntegrateEnvironment = new SvnIntegrateEnvironment(this);
}
return mySvnIntegrateEnvironment;
}
@Override
public UpdateEnvironment createUpdateEnvironment() {
if (mySvnUpdateEnvironment == null) {
mySvnUpdateEnvironment = new SvnUpdateEnvironment(this);
}
return mySvnUpdateEnvironment;
}
@Override
public String getDisplayName() {
LOG.debug("getDisplayName");
return VCS_DISPLAY_NAME;
}
@Override
public Configurable getConfigurable() {
LOG.debug("createConfigurable");
return new SvnConfigurable(myProject);
}
public SvnConfiguration getSvnConfiguration() {
return myConfiguration;
}
public static SvnVcs getInstance(Project project) {
return (SvnVcs)ProjectLevelVcsManager.getInstance(project).findVcsByName(VCS_NAME);
}
@Override
@NotNull
public CheckinEnvironment createCheckinEnvironment() {
if (myCheckinEnvironment == null) {
myCheckinEnvironment = new SvnCheckinEnvironment(this);
}
return myCheckinEnvironment;
}
@Override
@NotNull
public RollbackEnvironment createRollbackEnvironment() {
if (myRollbackEnvironment == null) {
myRollbackEnvironment = new SvnRollbackEnvironment(this);
}
return myRollbackEnvironment;
}
@Override
public VcsHistoryProvider getVcsHistoryProvider() {
// no heavy state, but it would be useful to have place to keep state in -> do not reuse instance
return new SvnHistoryProvider(this);
}
@Override
public VcsHistoryProvider getVcsBlockHistoryProvider() {
return getVcsHistoryProvider();
}
@Override
public AnnotationProvider getAnnotationProvider() {
if (myAnnotationProvider == null) {
myAnnotationProvider = new SvnAnnotationProvider(this);
}
return myAnnotationProvider;
}
@Override
public DiffProvider getDiffProvider() {
if (mySvnDiffProvider == null) {
mySvnDiffProvider = new SvnDiffProvider(this);
}
return mySvnDiffProvider;
}
private static Trinity<Long, Long, Long> getTimestampForPropertiesChange(final File ioFile, final boolean isDir) {
final File dir = isDir ? ioFile : ioFile.getParentFile();
final String relPath = SVNAdminUtil.getPropPath(ioFile.getName(), isDir ? SVNNodeKind.DIR : SVNNodeKind.FILE, false);
final String relPathBase = SVNAdminUtil.getPropBasePath(ioFile.getName(), isDir ? SVNNodeKind.DIR : SVNNodeKind.FILE, false);
final String relPathRevert = SVNAdminUtil.getPropRevertPath(ioFile.getName(), isDir ? SVNNodeKind.DIR : SVNNodeKind.FILE, false);
return new Trinity<Long, Long, Long>(new File(dir, relPath).lastModified(), new File(dir, relPathBase).lastModified(),
new File(dir, relPathRevert).lastModified());
}
private static boolean trinitiesEqual(final Trinity<Long, Long, Long> t1, final Trinity<Long, Long, Long> t2) {
if (t2.first == 0 && t2.second == 0 && t2.third == 0) return false;
return t1.equals(t2);
}
@Nullable
public SVNPropertyValue getPropertyWithCaching(final VirtualFile file, final String propName) throws VcsException {
Map<String, Pair<SVNPropertyValue, Trinity<Long, Long, Long>>> cachedMap = myPropertyCache.get(keyForVf(file));
final Pair<SVNPropertyValue, Trinity<Long, Long, Long>> cachedValue = cachedMap == null ? null : cachedMap.get(propName);
final File ioFile = new File(file.getPath());
final Trinity<Long, Long, Long> tsTrinity = getTimestampForPropertiesChange(ioFile, file.isDirectory());
if (cachedValue != null) {
// zero means that a file was not found
if (trinitiesEqual(cachedValue.getSecond(), tsTrinity)) {
return cachedValue.getFirst();
}
}
PropertyClient client = getFactory(ioFile).createPropertyClient();
final SVNPropertyData value = client.getProperty(SvnTarget.fromFile(ioFile, SVNRevision.WORKING), propName, false, SVNRevision.WORKING);
final SVNPropertyValue propValue = value == null ? null : value.getValue();
if (cachedMap == null) {
cachedMap = new HashMap<String, Pair<SVNPropertyValue, Trinity<Long, Long, Long>>>();
myPropertyCache.put(keyForVf(file), cachedMap);
}
cachedMap.put(propName, new Pair<SVNPropertyValue, Trinity<Long, Long, Long>>(propValue, tsTrinity));
return propValue;
}
@Override
public boolean fileExistsInVcs(FilePath path) {
File file = path.getIOFile();
try {
SVNStatus status = getFactory(file).createStatusClient().doStatus(file, false);
if (status != null) {
if (svnStatusIs(status, SVNStatusType.STATUS_ADDED)) {
return status.isCopied();
}
return !(svnStatusIsUnversioned(status) ||
svnStatusIs(status, SVNStatusType.STATUS_IGNORED) ||
svnStatusIs(status, SVNStatusType.STATUS_OBSTRUCTED));
}
}
catch (SVNException e) {
LOG.info(e);
}
return false;
}
public static boolean svnStatusIsUnversioned(final SVNStatus status) {
return svnStatusIs(status, SVNStatusType.STATUS_UNVERSIONED);
}
public static boolean svnStatusIs(final SVNStatus status, @NotNull final SVNStatusType value) {
return value.equals(status.getNodeStatus()) || value.equals(status.getContentsStatus());
}
@Override
public boolean fileIsUnderVcs(FilePath path) {
final ChangeListManager clManager = ChangeListManager.getInstance(myProject);
final VirtualFile file = path.getVirtualFile();
if (file == null) {
return false;
}
return !SvnStatusUtil.isIgnoredInAnySense(clManager, file) && !clManager.isUnversioned(file);
}
private static File getEntriesFile(File file) {
return file.isDirectory() ? new File(file, pathToEntries) : new File(file.getParentFile(), pathToEntries);
}
private static File getDirPropsFile(File file) {
return new File(file, pathToDirProps);
}
@Nullable
public SVNInfo getInfo(@NotNull SVNURL url,
SVNRevision pegRevision,
SVNRevision revision) throws SVNException {
return getFactory().createInfoClient().doInfo(url, pegRevision, revision);
}
@Nullable
public SVNInfo getInfo(@NotNull SVNURL url, SVNRevision revision) throws SVNException {
return getInfo(url, SVNRevision.UNDEFINED, revision);
}
@Nullable
public SVNInfo getInfo(@NotNull final VirtualFile file) {
return getInfo(new File(file.getPath()));
}
@Nullable
public SVNInfo getInfo(@NotNull String path) {
return getInfo(new File(path));
}
@Nullable
public SVNInfo getInfo(@NotNull File ioFile) {
SVNInfo result = null;
SvnWcClientI client = getFactory(ioFile).createInfoClient();
try {
// applying such behavior only when info requested w/o explicitly specifying revision
// TODO: logs should be analyzed and decision taken if we need it or just need to explicitly specify HEAD revision in certain calls
result = client.doInfo(ioFile, SVNRevision.UNDEFINED);
SVNInfo localInfo = result;
if (result == null || result.getRepositoryRootURL() == null) {
LOG.info("Failed to get local info for " + ioFile + ". Trying to get HEAD info.");
result = client.doInfo(ioFile, SVNRevision.HEAD);
if (result != null) {
LOG.info("Local info was " + localInfo + ", HEAD info was " + result);
}
}
}
catch (SVNException e) {
handleInfoException(e);
}
return result;
}
@Nullable
public SVNInfo getInfo(@NotNull File ioFile, @NotNull SVNRevision revision) {
SVNInfo result = null;
try {
result = getFactory(ioFile).createInfoClient().doInfo(ioFile, revision);
}
catch (SVNException e) {
handleInfoException(e);
}
return result;
}
private void handleInfoException(SVNException e) {
final SVNErrorCode errorCode = e.getErrorMessage().getErrorCode();
if (!myLogExceptions ||
SVNErrorCode.WC_PATH_NOT_FOUND.equals(errorCode) ||
SVNErrorCode.UNVERSIONED_RESOURCE.equals(errorCode) ||
SVNErrorCode.WC_NOT_WORKING_COPY.equals(errorCode) ||
// thrown when getting info from repository for non-existent item - like HEAD revision for deleted file
SVNErrorCode.ILLEGAL_TARGET.equals(errorCode) ||
// do not log working copy format vs client version inconsistencies as errors
SVNErrorCode.WC_UNSUPPORTED_FORMAT.equals(errorCode) ||
SVNErrorCode.WC_UPGRADE_REQUIRED.equals(errorCode)) {
LOG.debug(e);
}
else {
LOG.error(e);
}
}
public WorkingCopyFormat getWorkingCopyFormat(@NotNull File ioFile) {
RootUrlInfo rootInfo = getSvnFileUrlMapping().getWcRootForFilePath(ioFile);
WorkingCopyFormat format = rootInfo != null ? rootInfo.getFormat() : WorkingCopyFormat.UNKNOWN;
if (WorkingCopyFormat.UNKNOWN.equals(format)) {
format = SvnFormatSelector.findRootAndGetFormat(ioFile);
}
return format;
}
public void refreshSSLProperty() {
if (ourSSLProtocolsExplicitlySet) return;
if (SvnConfiguration.SSLProtocols.all.equals(myConfiguration.SSL_PROTOCOLS)) {
System.clearProperty(SVNKIT_HTTP_SSL_PROTOCOLS);
} else if (SvnConfiguration.SSLProtocols.sslv3.equals(myConfiguration.SSL_PROTOCOLS)) {
System.setProperty(SVNKIT_HTTP_SSL_PROTOCOLS, "SSLv3");
} else if (SvnConfiguration.SSLProtocols.tlsv1.equals(myConfiguration.SSL_PROTOCOLS)) {
System.setProperty(SVNKIT_HTTP_SSL_PROTOCOLS, "TLSv1");
}
}
public boolean isWcRoot(FilePath filePath) {
boolean isWcRoot = false;
WorkingCopy wcRoot = myRootsToWorkingCopies.getWcRoot(filePath.getVirtualFile());
if (wcRoot != null) {
isWcRoot = wcRoot.getFile().getAbsolutePath().equals(filePath.getIOFile().getAbsolutePath());
}
return isWcRoot;
}
private static class JavaSVNDebugLogger extends SVNDebugLogAdapter {
private final boolean myLoggingEnabled;
private final boolean myLogNative;
private final Logger myLog;
private final static long ourErrorNotificationInterval = TimeUnit.MINUTES.toMillis(2);
private long myPreviousTime = 0;
public JavaSVNDebugLogger(boolean loggingEnabled, boolean logNative, Logger log) {
myLoggingEnabled = loggingEnabled;
myLogNative = logNative;
myLog = log;
}
private boolean shouldLog(final SVNLogType logType) {
return myLoggingEnabled || myLogNative && SVNLogType.NATIVE_CALL.equals(logType);
}
@Override
public void log(final SVNLogType logType, final Throwable th, final Level logLevel) {
handleSpecificSSLExceptions(th);
if (shouldLog(logType)) {
myLog.info(th);
}
}
private void handleSpecificSSLExceptions(Throwable th) {
final long time = System.currentTimeMillis();
if ((time - myPreviousTime) <= ourErrorNotificationInterval) {
return;
}
if (th instanceof SSLHandshakeException) {
// not trusted certificate exception is not the problem, just part of normal behaviour
if (th.getCause() instanceof SVNSSLUtil.CertificateNotTrustedException) {
LOG.info(th);
return;
}
myPreviousTime = time;
String info = SSLExceptionsHelper.getAddInfo();
info = info == null ? "" : " (" + info + ") ";
if (th.getCause() instanceof CertificateException) {
PopupUtil.showBalloonForActiveFrame("Subversion: " + info + th.getCause().getMessage(), MessageType.ERROR);
} else {
final String postMessage = "\nPlease check Subversion SSL settings (Settings | Version Control | Subversion | Network)\n" +
"Maybe you should specify SSL protocol manually - SSLv3 or TLSv1";
PopupUtil.showBalloonForActiveFrame("Subversion: " + info + th.getMessage() + postMessage, MessageType.ERROR);
}
} else if (th instanceof SSLProtocolException) {
final String message = th.getMessage();
if (! StringUtil.isEmptyOrSpaces(message)) {
myPreviousTime = time;
String info = SSLExceptionsHelper.getAddInfo();
info = info == null ? "" : " (" + info + ") ";
final SSLProtocolExceptionParser parser = new SSLProtocolExceptionParser(message);
parser.parse();
final String errMessage = "Subversion: " + info + parser.getParsedMessage();
PopupUtil.showBalloonForActiveFrame(errMessage, MessageType.ERROR);
}
}
}
@Override
public void log(final SVNLogType logType, final String message, final Level logLevel) {
if (SVNLogType.NATIVE_CALL.equals(logType)) {
logNative(message);
}
if (shouldLog(logType)) {
myLog.info(message);
}
}
private static void logNative(String message) {
if (message == null) return;
final NativeLogReader.CallInfo callInfo = SvnNativeLogParser.parse(message);
if (callInfo == null) return;
NativeLogReader.putInfo(callInfo);
}
@Override
public void log(final SVNLogType logType, final String message, final byte[] data) {
if (shouldLog(logType)) {
if (data != null) {
try {
myLog.info(message + "\n" + new String(data, "UTF-8"));
}
catch (UnsupportedEncodingException e) {
myLog.info(message + "\n" + new String(data));
}
}
else {
myLog.info(message);
}
}
}
}
@Override
public FileStatus[] getProvidedStatuses() {
return new FileStatus[]{SvnFileStatus.EXTERNAL,
SvnFileStatus.OBSTRUCTED,
SvnFileStatus.REPLACED};
}
@Override
@NotNull
public CommittedChangesProvider<SvnChangeList, ChangeBrowserSettings> getCommittedChangesProvider() {
if (myCommittedChangesProvider == null) {
myCommittedChangesProvider = new SvnCommittedChangesProvider(myProject);
}
return myCommittedChangesProvider;
}
@Nullable
@Override
public VcsRevisionNumber parseRevisionNumber(final String revisionNumberString) {
final SVNRevision revision = SVNRevision.parse(revisionNumberString);
if (revision.equals(SVNRevision.UNDEFINED)) {
return null;
}
return new SvnRevisionNumber(revision);
}
@Override
public String getRevisionPattern() {
return ourIntegerPattern;
}
@Override
public boolean isVersionedDirectory(final VirtualFile dir) {
return SvnUtil.seemsLikeVersionedDir(dir);
}
@NotNull
public SvnFileUrlMapping getSvnFileUrlMapping() {
if (myMapping == null) {
myMapping = SvnFileUrlMappingImpl.getInstance(myProject);
}
return myMapping;
}
/**
* Returns real working copies roots - if there is <Project Root> -> Subversion setting,
* and there is one working copy, will return one root
*/
public List<WCInfo> getAllWcInfos() {
final SvnFileUrlMapping urlMapping = getSvnFileUrlMapping();
final List<RootUrlInfo> infoList = urlMapping.getAllWcInfos();
final List<WCInfo> infos = new ArrayList<WCInfo>();
for (RootUrlInfo info : infoList) {
final File file = info.getIoFile();
infos.add(new WCInfo(file.getAbsolutePath(), info.getAbsoluteUrlAsUrl(),
info.getFormat(), info.getRepositoryUrl(), SvnUtil.isWorkingCopyRoot(file), info.getType(),
SvnUtil.getDepth(this, file)));
}
return infos;
}
@Override
public RootsConvertor getCustomConvertor() {
if (myProject.isDefault()) return null;
return getSvnFileUrlMapping();
}
@Override
public MergeProvider getMergeProvider() {
if (myMergeProvider == null) {
myMergeProvider = new SvnMergeProvider(myProject);
}
return myMergeProvider;
}
@Override
public List<AnAction> getAdditionalActionsForLocalChange() {
return Arrays.<AnAction>asList(new ShowPropertiesDiffWithLocalAction());
}
private static String keyForVf(final VirtualFile vf) {
return vf.getUrl();
}
@Override
public boolean allowsNestedRoots() {
return true;
}
@Override
public <S> List<S> filterUniqueRoots(final List<S> in, final Convertor<S, VirtualFile> convertor) {
if (in.size() <= 1) return in;
final List<MyPair<S>> infos = new ArrayList<MyPair<S>>(in.size());
final SvnFileUrlMappingImpl mapping = (SvnFileUrlMappingImpl)getSvnFileUrlMapping();
final List<S> notMatched = new LinkedList<S>();
for (S s : in) {
final VirtualFile vf = convertor.convert(s);
if (vf == null) continue;
final File ioFile = new File(vf.getPath());
SVNURL url = mapping.getUrlForFile(ioFile);
if (url == null) {
url = SvnUtil.getUrl(this, ioFile);
if (url == null) {
notMatched.add(s);
continue;
}
}
infos.add(new MyPair<S>(vf, url.toString(), s));
}
final List<MyPair<S>> filtered = new ArrayList<MyPair<S>>(infos.size());
ForNestedRootChecker.filterOutSuperfluousChildren(this, infos, filtered);
final List<S> converted = ObjectsConvertor.convert(filtered, new Convertor<MyPair<S>, S>() {
@Override
public S convert(final MyPair<S> o) {
return o.getSrc();
}
});
if (!notMatched.isEmpty()) {
// potential bug is here: order is not kept. but seems it only occurs for cases where result is sorted after filtering so ok
converted.addAll(notMatched);
}
return converted;
}
private static class MyPair<T> implements RootUrlPair {
private final VirtualFile myFile;
private final String myUrl;
private final T mySrc;
private MyPair(VirtualFile file, String url, T src) {
myFile = file;
myUrl = url;
mySrc = src;
}
public T getSrc() {
return mySrc;
}
@Override
public VirtualFile getVirtualFile() {
return myFile;
}
@Override
public String getUrl() {
return myUrl;
}
}
private static class MyFrameStateListener extends FrameStateListener.Adapter {
private final ChangeListManager myClManager;
private final VcsDirtyScopeManager myDirtyScopeManager;
private MyFrameStateListener(ChangeListManager clManager, VcsDirtyScopeManager dirtyScopeManager) {
myClManager = clManager;
myDirtyScopeManager = dirtyScopeManager;
}
@Override
public void onFrameActivated() {
final List<VirtualFile> folders = ((ChangeListManagerImpl)myClManager).getLockedFolders();
if (!folders.isEmpty()) {
myDirtyScopeManager.filesDirty(null, folders);
}
}
}
public static VcsKey getKey() {
return ourKey;
}
@Override
public boolean isVcsBackgroundOperationsAllowed(VirtualFile root) {
return ThreeState.YES.equals(myAuthNotifier.isAuthenticatedFor(root));
}
public SvnBranchPointsCalculator getSvnBranchPointsCalculator() {
return mySvnBranchPointsCalculator;
}
@Override
public boolean areDirectoriesVersionedItems() {
return true;
}
@Override
public CheckoutProvider getCheckoutProvider() {
if (myCheckoutProvider == null) {
myCheckoutProvider = new SvnCheckoutProvider();
}
return myCheckoutProvider;
}
public boolean isProject18() {
return WorkingCopyFormat.ONE_DOT_EIGHT.equals(getProjectRootFormat());
}
public boolean isProject16() {
return WorkingCopyFormat.ONE_DOT_SIX.equals(getProjectRootFormat());
}
private WorkingCopyFormat getProjectRootFormat() {
return !getProject().isDefault() ? getWorkingCopyFormat(new File(getProject().getBaseDir().getPath())) : WorkingCopyFormat.UNKNOWN;
}
/**
* Detects appropriate client factory based on project root directory working copy format.
*
* Try to avoid usages of this method (for now) as it could not correctly for all cases
* detect svn 1.8 working copy format to guarantee command line client.
*
* For instance, when working copies of several formats are presented in project
* (though it seems to be rather unlikely case).
*
* @return
*/
@NotNull
public ClientFactory getFactory() {
return getFactory(getProjectRootFormat(), false);
}
public ClientFactory getSvnKitFactory() {
return svnKitClientFactory;
}
@NotNull
public ClientFactory getFactory(@NotNull File file) {
return getFactory(getWorkingCopyFormat(file), true);
}
@NotNull
private ClientFactory getFactory(@NotNull WorkingCopyFormat format, boolean useProjectRootForUnknown) {
boolean is18 = WorkingCopyFormat.ONE_DOT_EIGHT.equals(format);
boolean is16 = WorkingCopyFormat.ONE_DOT_SIX.equals(format);
boolean isUnknown = WorkingCopyFormat.UNKNOWN.equals(format);
return is18
? cmdClientFactory
: (is16 ? svnKitClientFactory : (useProjectRootForUnknown && isUnknown ? getFactory() : getFactoryFromSettings()));
}
@NotNull
public ClientFactory getFactory(@NotNull SvnTarget target) {
return target.isFile() ? getFactory(target.getFile()) : getFactory();
}
@NotNull
public ClientFactory getFactoryFromSettings() {
return myConfiguration.isCommandLine() ? cmdClientFactory : svnKitClientFactory;
}
@NotNull
public ClientFactory getCommandLineFactory() {
return cmdClientFactory;
}
}