Merge branch 'svn_18_3'

This commit is contained in:
Konstantin Kolosovsky
2014-05-06 16:52:42 +04:00
134 changed files with 3456 additions and 3217 deletions
@@ -22,9 +22,6 @@ import org.jetbrains.idea.svn.config.ProxyGroup;
import org.tmatesoft.svn.core.internal.wc.SVNConfigFile;
import java.io.File;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@@ -49,59 +46,16 @@ public class IdeaSVNConfigFile {
myPatternsMap = new HashMap<String, String>();
}
public static void putProxyIntoServersFile(final File configDir, final String host, final Proxy proxyInfo) {
final IdeaSVNConfigFile configFile = new IdeaSVNConfigFile(new File(configDir, SERVERS_FILE_NAME));
configFile.updateGroups();
String groupName = ensureHostGroup(host, configFile);
final HashMap<String, String> map = new HashMap<String, String>();
final InetSocketAddress address = ((InetSocketAddress) proxyInfo.address());
map.put(SvnAuthenticationManager.HTTP_PROXY_HOST, address.getHostName());
map.put(SvnAuthenticationManager.HTTP_PROXY_PORT, String.valueOf(address.getPort()));
configFile.addGroup(groupName, host + "*", map);
configFile.save();
}
@NotNull
public static String ensureHostGroup(@NotNull String host, @NotNull IdeaSVNConfigFile configFile) {
String groupName = SvnAuthenticationManager.getGroupForHost(host, configFile);
if (StringUtil.isEmptyOrSpaces(groupName)) {
groupName = getNewGroupName(host, configFile);
}
return groupName;
}
@NotNull
public static String getNewGroupName(@NotNull String host, @NotNull IdeaSVNConfigFile configFile) {
String groupName = host;
final Map<String,ProxyGroup> groups = configFile.getAllGroups();
final Map<String, ProxyGroup> groups = configFile.getAllGroups();
while (StringUtil.isEmptyOrSpaces(groupName) || groups.containsKey(groupName)) {
groupName += "1";
}
return groupName;
}
public static boolean putProxyCredentialsIntoServerFile(@NotNull final File configDir, @NotNull final String host,
@NotNull final PasswordAuthentication authentication) {
final IdeaSVNConfigFile configFile = new IdeaSVNConfigFile(new File(configDir, SERVERS_FILE_NAME));
configFile.updateGroups();
String groupName = SvnAuthenticationManager.getGroupForHost(host, configFile);
// no proxy defined in group -> no sense in password
if (StringUtil.isEmptyOrSpaces(groupName)) return false;
final Map<String, String> properties = configFile.getAllGroups().get(groupName).getProperties();
if (StringUtil.isEmptyOrSpaces(properties.get(SvnAuthenticationManager.HTTP_PROXY_HOST))) return false;
if (StringUtil.isEmptyOrSpaces(properties.get(SvnAuthenticationManager.HTTP_PROXY_PORT))) return false;
configFile.setValue(groupName, SvnAuthenticationManager.HTTP_PROXY_USERNAME, authentication.getUserName());
configFile.setValue(groupName, SvnAuthenticationManager.HTTP_PROXY_PASSWORD, String.valueOf(authentication.getPassword()));
configFile.save();
return true;
}
public void updateGroups() {
if (myLatestUpdate != myFile.lastModified()) {
myPatternsMap.clear();
@@ -155,10 +109,10 @@ public class IdeaSVNConfigFile {
public void modifyGroup(final String name, final String patterns, final Collection<String> delete, final Map<String, String> addOrModify,
final boolean isDefault) {
if (! isDefault) {
if (!isDefault) {
mySVNConfigFile.setPropertyValue(GROUPS_GROUP_NAME, name, patterns, false);
}
final Map<String,String> deletedPrepared = new HashMap<String, String>(delete.size());
final Map<String, String> deletedPrepared = new HashMap<String, String>(delete.size());
for (String property : delete) {
deletedPrepared.put(property, null);
}
@@ -1,110 +0,0 @@
/*
* Copyright 2000-2009 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.openapi.diagnostic.Logger;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.wc.ISVNStatusHandler;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNStatusType;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class IgnoredFileInfo {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.IgnoredFileInfo");
// directory for which properties are collected
private final File myFile;
private final List<String> myPatterns;
private final Set<String> myFileNames;
private final Set<String> myOldPatterns;
public IgnoredFileInfo(final File file, final Set<String> oldPatterns) {
myFile = file;
myPatterns = new ArrayList<String>();
myFileNames = new HashSet<String>();
myOldPatterns = oldPatterns;
}
public void addFileName(final String name) {
myFileNames.add(name);
}
public void addPattern(final String value) {
myPatterns.add(value);
}
public void calculatePatterns(final SvnVcs vcs) {
final List<String> names = new ArrayList<String>();
try {
vcs.createStatusClient().doStatus(myFile, SVNRevision.WORKING, SVNDepth.IMMEDIATES, false, true, true, false, new ISVNStatusHandler() {
public void handleStatus(SVNStatus status) throws SVNException {
if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_IGNORED)) {
final String name = status.getFile().getName();
if (! myFileNames.contains(name)) {
names.add(name);
}
}
}
}, null);
}
catch (SVNException e) {
LOG.info(e);
}
for (String pattern : myOldPatterns) {
boolean usedSomewhereElse = false;
for (String name : names) {
if (DefaultSVNOptions.matches(pattern, name)) {
usedSomewhereElse = true;
break;
}
}
if (! usedSomewhereElse) {
for (String name : myFileNames) {
if (DefaultSVNOptions.matches(pattern, name)) {
myPatterns.add(pattern);
break;
}
}
}
}
}
public File getFile() {
return myFile;
}
public List<String> getPatterns() {
return myPatterns;
}
public Set<String> getFileNames() {
return myFileNames;
}
public Set<String> getOldPatterns() {
return myOldPatterns;
}
}
@@ -1,55 +0,0 @@
/*
* Copyright 2000-2009 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.openapi.vfs.VirtualFile;
import org.tmatesoft.svn.core.SVNURL;
public class RootMixedInfo {
/**
* working copy root path
*/
private final String myFilePath;
private final VirtualFile myFile;
/**
* url corresponding to working copy root
*/
private final SVNURL myUrl;
private final VirtualFile myParentVcsRoot;
public RootMixedInfo(final String filePath, final VirtualFile file, final SVNURL url, final VirtualFile parentVcsRoot) {
myFilePath = filePath;
myFile = file;
myUrl = url;
myParentVcsRoot = parentVcsRoot;
}
public String getFilePath() {
return myFilePath;
}
public VirtualFile getFile() {
return myFile;
}
public SVNURL getUrl() {
return myUrl;
}
public VirtualFile getParentVcsRoot() {
return myParentVcsRoot;
}
}
@@ -15,12 +15,77 @@
*/
package org.jetbrains.idea.svn;
import com.intellij.lifecycle.PeriodicalTasksCloser;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.roots.FileIndexFacade;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vfs.VirtualFile;
import org.tmatesoft.svn.core.wc.SVNStatusClient;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.tmatesoft.svn.core.SVNCancelException;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.ISVNStatusFileProvider;
import org.tmatesoft.svn.core.wc.SVNEvent;
public interface StatusWalkerPartner {
SVNStatusClient createStatusClient();
void checkCanceled();
boolean isExcluded(final VirtualFile vFile);
boolean isIgnoredIdeaLevel(final VirtualFile vFile);
public class StatusWalkerPartner {
private final SvnVcs myVcs;
private final ChangeListManager myClManager;
private final FileIndexFacade myExcludedFileIndex;
private final ProgressIndicator myIndicator;
private ISVNStatusFileProvider myFileProvider;
public StatusWalkerPartner(final SvnVcs vcs, final ProgressIndicator pi) {
myVcs = vcs;
myClManager = ChangeListManager.getInstance(myVcs.getProject());
myExcludedFileIndex = PeriodicalTasksCloser.getInstance().safeGetService(myVcs.getProject(), FileIndexFacade.class);
myIndicator = pi;
}
public void setFileProvider(final ISVNStatusFileProvider fileProvider) {
myFileProvider = fileProvider;
}
@NotNull
public ISVNEventHandler getEventHandler() {
return new ISVNEventHandler() {
public void handleEvent(SVNEvent event, double progress) throws SVNException {
//
}
public void checkCancelled() throws SVNCancelException {
if (myIndicator != null) {
myIndicator.checkCanceled();
}
}
};
}
@Nullable
public ISVNStatusFileProvider getFileProvider() {
return myFileProvider;
}
public void checkCanceled() {
if (myIndicator != null) {
myIndicator.checkCanceled();
}
}
public boolean isExcluded(final VirtualFile vFile) {
return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
@Override
public Boolean compute() {
if (myVcs.getProject().isDisposed()) throw new ProcessCanceledException();
return myExcludedFileIndex.isExcludedFile(vFile);
}
});
}
public boolean isIgnoredIdeaLevel(VirtualFile vFile) {
return myClManager.isIgnoredFile(vFile);
}
}
@@ -1,87 +0,0 @@
/*
* Copyright 2000-2009 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.lifecycle.PeriodicalTasksCloser;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.roots.FileIndexFacade;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vfs.VirtualFile;
import org.tmatesoft.svn.core.SVNCancelException;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.ISVNStatusFileProvider;
import org.tmatesoft.svn.core.wc.SVNEvent;
import org.tmatesoft.svn.core.wc.SVNStatusClient;
public class StatusWalkerPartnerImpl implements StatusWalkerPartner {
private final SvnVcs myVcs;
private final ChangeListManager myClManager;
private final FileIndexFacade myExcludedFileIndex;
private final ProgressIndicator myIndicator;
private ISVNStatusFileProvider myFileProvider;
public StatusWalkerPartnerImpl(final SvnVcs vcs, final ProgressIndicator pi) {
myVcs = vcs;
myClManager = ChangeListManager.getInstance(myVcs.getProject());
myExcludedFileIndex = PeriodicalTasksCloser.getInstance().safeGetService(myVcs.getProject(), FileIndexFacade.class);
myIndicator = pi;
}
public void setFileProvider(final ISVNStatusFileProvider fileProvider) {
myFileProvider = fileProvider;
}
public SVNStatusClient createStatusClient() {
final SVNStatusClient result = myVcs.createStatusClient();
result.setFilesProvider(myFileProvider);
result.setEventHandler(new ISVNEventHandler() {
public void handleEvent(SVNEvent event, double progress) throws SVNException {
//
}
public void checkCancelled() throws SVNCancelException {
if (myIndicator != null) {
myIndicator.checkCanceled();
}
}
});
return result;
}
public void checkCanceled() {
if (myIndicator != null) {
myIndicator.checkCanceled();
}
}
public boolean isExcluded(final VirtualFile vFile) {
return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
@Override
public Boolean compute() {
if (myVcs.getProject().isDisposed()) throw new ProcessCanceledException();
return myExcludedFileIndex.isExcludedFile(vFile);
}
});
}
public boolean isIgnoredIdeaLevel(VirtualFile vFile) {
return myClManager.isIgnoredFile(vFile);
}
}
@@ -71,6 +71,22 @@ public class SvnBranchConfigurationManager implements PersistentStateComponent<S
myVcsManager = vcsManager;
myStorage = storage;
myBranchesLoader = new ProgressManagerQueue(myProject, "Subversion Branches Preloader");
// TODO: Seems that ProgressManagerQueue is not suitable here at least for some branches loading tasks. For instance,
// TODO: for DefaultConfigLoader it would be better to run modal cancellable task - so branches structure could be detected and
// TODO: shown in dialog. Currently when "Configure Branches" is invoked for the first time - no branches are shown.
// TODO: If "Cancel" is pressed and "Configure Branches" invoked once again - already detected (in background) branches are shown.
((ProjectLevelVcsManagerImpl) vcsManager).addInitializationRequest(VcsInitObject.BRANCHES, new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
if (myProject.isDisposed()) return;
myBranchesLoader.start();
}
});
}
});
myBunch = new NewRootBunch(project, myBranchesLoader);
}
@@ -34,7 +34,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.EventDispatcher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.actions.CleanupWorker;
import org.jetbrains.idea.svn.portable.SvnExceptionWrapper;
import org.jetbrains.idea.svn.commandLine.SvnExceptionWrapper;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
@@ -76,7 +76,7 @@ public class SvnChangeProvider implements ChangeProvider {
try {
final SvnChangeProviderContext context = new SvnChangeProviderContext(myVcs, builder, progress);
final StatusWalkerPartnerImpl partner = new StatusWalkerPartnerImpl(myVcs, progress);
final StatusWalkerPartner partner = new StatusWalkerPartner(myVcs, progress);
final NestedCopiesBuilder nestedCopiesBuilder = new NestedCopiesBuilder(myVcs, mySvnFileUrlMapping);
final EventDispatcher<StatusReceiver> statusReceiver = EventDispatcher.create(StatusReceiver.class);
@@ -94,12 +94,6 @@ public class SvnChangeProvider implements ChangeProvider {
walker.go(item.getDir(), SVNDepth.IMMEDIATES);
}
// they are taken under non recursive: ENTRIES file is read anyway, so we get to know parent status also for free
/*for (FilePath path : zipper.getSingleFiles()) {
FileStatus status = getParentStatus(context, path);
processFile(path, context, status, false, context.getClient());
}*/
processCopiedAndDeleted(context, dirtyScope);
processUnsaved(dirtyScope, addGate, context);
@@ -177,7 +171,7 @@ public class SvnChangeProvider implements ChangeProvider {
public void getChanges(final FilePath path, final boolean recursive, final ChangelistBuilder builder) throws SVNException {
final SvnChangeProviderContext context = new SvnChangeProviderContext(myVcs, builder, null);
final StatusWalkerPartnerImpl partner = new StatusWalkerPartnerImpl(myVcs, ProgressManager.getInstance().getProgressIndicator());
final StatusWalkerPartner partner = new StatusWalkerPartner(myVcs, ProgressManager.getInstance().getProgressIndicator());
final SvnRecursiveStatusWalker walker = new SvnRecursiveStatusWalker(myVcs, context, partner);
walker.go(path, recursive ? SVNDepth.INFINITY : SVNDepth.IMMEDIATES);
processCopiedAndDeleted(context, null);
@@ -251,9 +245,10 @@ public class SvnChangeProvider implements ChangeProvider {
File wcPath = guessWorkingCopyPath(copiedStatus.getFile(), copiedStatus.getURL(), copyFromURL);
SVNStatus status;
try {
status = context.getClient().doStatus(wcPath, false);
status = myVcs.getFactory(wcPath).createStatusClient().doStatus(wcPath, false);
}
catch(SVNException ex) {
LOG.info(ex);
status = null;
}
if (status != null && SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_DELETED)) {
@@ -26,9 +26,13 @@ import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.actions.AbstractShowPropertiesDiffAction;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb;
import org.tmatesoft.svn.core.wc.*;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLock;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.SVNInfo;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNStatusType;
import java.io.File;
import java.util.*;
@@ -37,7 +41,6 @@ class SvnChangeProviderContext implements StatusReceiver {
private static final Logger LOG = Logger.getInstance("org.jetbrains.idea.svn.SvnChangeProviderContext");
private final ChangelistBuilder myChangelistBuilder;
private final SVNStatusClient myStatusClient;
private List<SvnChangedFile> myCopiedFiles = null;
private final List<SvnChangedFile> myDeletedFiles = new ArrayList<SvnChangedFile>();
// for files moved in a subtree, which were the targets of merge (for instance).
@@ -50,7 +53,6 @@ class SvnChangeProviderContext implements StatusReceiver {
public SvnChangeProviderContext(SvnVcs vcs, final ChangelistBuilder changelistBuilder, final ProgressIndicator progress) {
myVcs = vcs;
myStatusClient = vcs.createStatusClient();
myChangelistBuilder = changelistBuilder;
myProgress = progress;
myTreeConflicted = new HashMap<String, SVNStatus>();
@@ -81,10 +83,6 @@ class SvnChangeProviderContext implements StatusReceiver {
return myChangelistBuilder;
}
public SVNStatusClient getClient() {
return myStatusClient;
}
public void reportTreeConflict(final SVNStatus status) {
myTreeConflicted.put(status.getFile().getAbsolutePath(), status);
}
@@ -131,7 +129,7 @@ class SvnChangeProviderContext implements StatusReceiver {
return null;
}
StringBuilder relPathBuilder = new StringBuilder();
while(filePath != null) {
while (filePath != null) {
String copyFromURL = myCopyFromURLs.get(filePath);
if (copyFromURL != null) {
return copyFromURL + relPathBuilder.toString();
@@ -160,7 +158,7 @@ class SvnChangeProviderContext implements StatusReceiver {
myCopyFromURLs.put(filePath, url);
}
//
//
void processStatusFirstPass(final FilePath filePath, final SVNStatus status) throws SVNException {
if (status == null) {
@@ -170,12 +168,14 @@ class SvnChangeProviderContext implements StatusReceiver {
if (status.getRemoteLock() != null) {
final SVNLock lock = status.getRemoteLock();
myChangelistBuilder.processLogicallyLockedFolder(filePath.getVirtualFile(),
new LogicalLock(false, lock.getOwner(), lock.getComment(), lock.getCreationDate(), lock.getExpirationDate()));
new LogicalLock(false, lock.getOwner(), lock.getComment(), lock.getCreationDate(),
lock.getExpirationDate()));
}
if (status.getLocalLock() != null) {
final SVNLock lock = status.getLocalLock();
myChangelistBuilder.processLogicallyLockedFolder(filePath.getVirtualFile(),
new LogicalLock(true, lock.getOwner(), lock.getComment(), lock.getCreationDate(), lock.getExpirationDate()));
new LogicalLock(true, lock.getOwner(), lock.getComment(), lock.getCreationDate(),
lock.getExpirationDate()));
}
if (filePath.isDirectory() && status.isLocked()) {
myChangelistBuilder.processLockedFolder(filePath.getVirtualFile());
@@ -191,7 +191,8 @@ class SvnChangeProviderContext implements StatusReceiver {
String parentCopyFromURL = getParentCopyFromURL(filePath);
if (parentCopyFromURL != null) {
addCopiedFile(filePath, status, parentCopyFromURL);
} else {
}
else {
processStatus(filePath, status);
}
}
@@ -225,7 +226,8 @@ class SvnChangeProviderContext implements StatusReceiver {
propStatus == SVNStatusType.STATUS_CONFLICTED) {
myChangelistBuilder.processChangeInList(
createChange(SvnContentRevision.createBaseRevision(myVcs, filePath, status), CurrentContentRevision.create(filePath), fStatus,
status), SvnUtil.getChangelistName(status), SvnVcs.getKey());
status), SvnUtil.getChangelistName(status), SvnVcs.getKey()
);
checkSwitched(filePath, myChangelistBuilder, status, fStatus);
}
else if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_DELETED)) {
@@ -249,16 +251,19 @@ class SvnChangeProviderContext implements StatusReceiver {
if (file != null && FileDocumentManager.getInstance().isFileModified(file)) {
myChangelistBuilder.processChangeInList(
createChange(SvnContentRevision.createBaseRevision(myVcs, filePath, status), CurrentContentRevision.create(filePath),
FileStatus.MODIFIED, status), SvnUtil.getChangelistName(status), SvnVcs.getKey());
} else if (status.getTreeConflict() != null) {
FileStatus.MODIFIED, status), SvnUtil.getChangelistName(status), SvnVcs.getKey()
);
}
else if (status.getTreeConflict() != null) {
myChangelistBuilder.processChange(createChange(SvnContentRevision.createBaseRevision(myVcs, filePath, status),
CurrentContentRevision.create(filePath), FileStatus.MODIFIED, status), SvnVcs.getKey());
CurrentContentRevision.create(filePath), FileStatus.MODIFIED, status),
SvnVcs.getKey());
}
checkSwitched(filePath, myChangelistBuilder, status, fStatus);
}
}
}
public void addModifiedNotSavedChange(final VirtualFile file) throws SVNException {
final FilePath filePath = new FilePathImpl(file);
final SVNInfo svnInfo = myVcs.getInfo(file);
@@ -268,7 +273,8 @@ class SvnChangeProviderContext implements StatusReceiver {
svnStatus.setRevision(svnInfo.getRevision());
myChangelistBuilder.processChangeInList(
createChange(SvnContentRevision.createBaseRevision(myVcs, filePath, svnInfo.getRevision()), CurrentContentRevision.create(filePath),
FileStatus.MODIFIED, svnStatus), (String)null, SvnVcs.getKey());
FileStatus.MODIFIED, svnStatus), (String)null, SvnVcs.getKey()
);
}
}
@@ -317,7 +323,7 @@ class SvnChangeProviderContext implements StatusReceiver {
// seems here we can only have a tree conflict; which can be marked on either path (?)
// .. ok try to merge states
Change createMovedChange(final ContentRevision before, final ContentRevision after, final SVNStatus copiedStatus,
final SVNStatus deletedStatus) throws SVNException {
final SVNStatus deletedStatus) throws SVNException {
// todo no convertion needed for the contents status?
final ConflictedSvnChange conflictedSvnChange =
new ConflictedSvnChange(before, after, ConflictState.mergeState(getState(copiedStatus), getState(deletedStatus)),
@@ -331,12 +337,16 @@ class SvnChangeProviderContext implements StatusReceiver {
return patchWithPropertyChange(conflictedSvnChange, copiedStatus, deletedStatus);
}
private Change createChange(final ContentRevision before, final ContentRevision after, final FileStatus fStatus, final SVNStatus svnStatus)
private Change createChange(final ContentRevision before,
final ContentRevision after,
final FileStatus fStatus,
final SVNStatus svnStatus)
throws SVNException {
final ConflictedSvnChange conflictedSvnChange = new ConflictedSvnChange(before, after, correctContentsStatus(fStatus, svnStatus),
getState(svnStatus), after == null ? before.getFile() : after.getFile());
getState(svnStatus),
after == null ? before.getFile() : after.getFile());
if (svnStatus != null) {
if (SVNStatusType.STATUS_DELETED.equals(svnStatus.getNodeStatus()) && ! svnStatus.getRevision().isValid()) {
if (SVNStatusType.STATUS_DELETED.equals(svnStatus.getNodeStatus()) && !svnStatus.getRevision().isValid()) {
conflictedSvnChange.setIsPhantom(true);
}
conflictedSvnChange.setBeforeDescription(svnStatus.getTreeConflict());
@@ -354,7 +364,8 @@ class SvnChangeProviderContext implements StatusReceiver {
return new SvnLocallyDeletedChange(filePath, getState(status));
}
private Change patchWithPropertyChange(final Change change, final SVNStatus svnStatus, final SVNStatus deletedStatus) throws SVNException {
private Change patchWithPropertyChange(final Change change, final SVNStatus svnStatus, final SVNStatus deletedStatus)
throws SVNException {
if (svnStatus == null) return change;
final SVNStatusType propertiesStatus = svnStatus.getPropertiesStatus();
if (SVNStatusType.STATUS_CONFLICTED.equals(propertiesStatus) || SVNStatusType.CHANGED.equals(propertiesStatus) ||
@@ -378,8 +389,10 @@ class SvnChangeProviderContext implements StatusReceiver {
final String afterRevisionNu = change.getAfterRevision() == null ? null : change.getAfterRevision().getRevisionNumber().asString();
final Change propertyChange = new Change(beforeList == null ? null : new SimpleContentRevision(beforeList, path, beforeRevisionNu),
afterList == null ? null : new SimpleContentRevision(afterList, path, afterRevisionNu),
deletedStatus != null ? FileStatus.MODIFIED : SvnStatusConvertor.convertPropertyStatus(propertiesStatus));
afterList == null ? null : new SimpleContentRevision(afterList, path, afterRevisionNu),
deletedStatus != null
? FileStatus.MODIFIED
: SvnStatusConvertor.convertPropertyStatus(propertiesStatus));
change.addAdditionalLayerElement(SvnChangeProvider.PROPERTY_LAYER, propertyChange);
}
return change;
@@ -38,6 +38,7 @@ import com.intellij.util.Consumer;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.auth.SvnAuthenticationNotifier;
import org.jetbrains.idea.svn.config.SvnConfigureProxiesDialog;
import javax.swing.*;
@@ -22,9 +22,10 @@ import com.intellij.openapi.components.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.*;
import com.intellij.openapi.vcs.changes.VcsAnnotationRefresher;
import org.jetbrains.idea.svn.auth.SvnAuthenticationManager;
import org.jetbrains.idea.svn.config.SvnServerFileKeys;
import org.jetbrains.idea.svn.dialogs.SvnAuthenticationProvider;
import org.jetbrains.idea.svn.dialogs.SvnInteractiveAuthenticationProvider;
import org.jetbrains.idea.svn.auth.SvnAuthenticationProvider;
import org.jetbrains.idea.svn.auth.SvnInteractiveAuthenticationProvider;
import org.jetbrains.idea.svn.update.MergeRootInfo;
import org.jetbrains.idea.svn.update.UpdateRootInfo;
import org.tmatesoft.svn.core.SVNDepth;
@@ -1,46 +0,0 @@
/*
* Copyright 2000-2009 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.notification.NotificationType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.impl.GenericNotifierImpl;
import org.jetbrains.annotations.NotNull;
// todo be done
// todo : two kinds. positive: either into common proxy, or into system properties
public class SvnProxyAuthenticationNotifier extends GenericNotifierImpl<String, String> {
public SvnProxyAuthenticationNotifier(Project project, @NotNull String groupId, @NotNull String title, @NotNull NotificationType type) {
super(project, groupId, title, type);
}
@Override
protected boolean ask(String obj, String description) {
return false; //To change body of implemented methods use File | Settings | File Templates.
}
@NotNull
@Override
protected String getKey(String obj) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@NotNull
@Override
protected String getNotificationContent(String obj) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
}
@@ -29,16 +29,18 @@ import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Processor;
import com.intellij.vcsUtil.VcsUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient;
import org.jetbrains.idea.svn.portable.SvnStatusClientI;
import org.jetbrains.idea.svn.portable.SvnkitSvnStatusClient;
import org.jetbrains.idea.svn.status.StatusClient;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.wc.*;
import org.tmatesoft.svn.core.wc.ISVNStatusHandler;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNStatusType;
import java.io.File;
import java.util.LinkedList;
@@ -62,7 +64,7 @@ public class SvnRecursiveStatusWalker {
}
public void go(final FilePath rootPath, final SVNDepth depth) throws SVNException {
final MyItem root = new MyItem(myVcs, rootPath, depth, myPartner.createStatusClient(), false);
final MyItem root = createItem(rootPath, depth, false);
myQueue.add(root);
while (! myQueue.isEmpty()) {
@@ -75,7 +77,7 @@ public class SvnRecursiveStatusWalker {
if (path.isDirectory()) {
myHandler.setCurrentItem(item);
try {
final SvnStatusClientI client = item.getClient(ioFile);
final StatusClient client = item.getClient();
client.doStatus(ioFile, SVNRevision.WORKING, item.getDepth(), false, false, true, true, myHandler, null);
myHandler.checkIfCopyRootWasReported(null, ioFile);
}
@@ -84,7 +86,7 @@ public class SvnRecursiveStatusWalker {
}
} else {
try {
final SVNStatus status = item.getClient(ioFile).doStatus(ioFile, false, false);
final SVNStatus status = item.getClient().doStatus(ioFile, false, false);
myReceiver.process(path, status);
} catch (SVNException e) {
handleStatusException(item, path, e);
@@ -113,48 +115,31 @@ public class SvnRecursiveStatusWalker {
}
private static class MyItem {
private final Project myProject;
private final FilePath myPath;
private final SVNDepth myDepth;
private final SvnStatusClientI mySvnClient;
private final SvnStatusClientI myCommandLineClient;
@NotNull private final FilePath myPath;
@NotNull private final SVNDepth myDepth;
@NotNull private final StatusClient myStatusClient;
private final boolean myIsInnerCopyRoot;
private final SvnConfiguration myConfiguration17;
private final SvnVcs myVcs;
private MyItem(SvnVcs vcs, FilePath path, SVNDepth depth, SVNStatusClient client, boolean isInnerCopyRoot) {
myVcs = vcs;
myProject = vcs.getProject();
myConfiguration17 = SvnConfiguration.getInstance(myProject);
private MyItem(@NotNull FilePath path, @NotNull SVNDepth depth, boolean isInnerCopyRoot, @NotNull StatusClient statusClient) {
myPath = path;
myDepth = depth;
mySvnClient = new SvnkitSvnStatusClient(myVcs, client);
myCommandLineClient = new SvnCommandLineStatusClient(myVcs);
myStatusClient = statusClient;
myIsInnerCopyRoot = isInnerCopyRoot;
}
@NotNull
public FilePath getPath() {
return myPath;
}
@NotNull
public SVNDepth getDepth() {
return myDepth;
}
public SvnStatusClientI getClient(final File file) {
// TODO: refactor to ClientFactory usage but carefully save all parameters passed in myClient - fileProvider and
// TODO: event handler (for cancel support)
WorkingCopyFormat format = myVcs.getWorkingCopyFormat(file);
if (format == WorkingCopyFormat.ONE_DOT_EIGHT) {
return myCommandLineClient;
}
if (format == WorkingCopyFormat.ONE_DOT_SIX) {
return mySvnClient;
}
return myConfiguration17.isCommandLine() ? myCommandLineClient : mySvnClient;
@NotNull
public StatusClient getClient() {
return myStatusClient;
}
public boolean isIsInnerCopyRoot() {
@@ -187,7 +172,7 @@ public class SvnRecursiveStatusWalker {
return true;
}
if (file.isDirectory() && new File(file, SVNFileUtil.getAdminDirectoryName()).exists()) {
final MyItem childItem = new MyItem(myVcs, path, newDepth, myPartner.createStatusClient(), true);
final MyItem childItem = createItem(path, newDepth, true);
myQueue.add(childItem);
} else if (vf != null) {
myReceiver.processUnversioned(vf);
@@ -219,6 +204,14 @@ public class SvnRecursiveStatusWalker {
FileUtil.processFilesRecursively(ioFile, processor, directoryFilter);
}
@NotNull
private MyItem createItem(@NotNull FilePath path, @NotNull SVNDepth depth, boolean isInnerCopyRoot) {
StatusClient statusClient =
myVcs.getFactory(path.getIOFile()).createStatusClient(myPartner.getFileProvider(), myPartner.getEventHandler());
return new MyItem(path, depth, isInnerCopyRoot, statusClient);
}
private class MyHandler implements ISVNStatusHandler {
private MyItem myCurrentItem;
private boolean myMetCurrentItem;
@@ -235,7 +228,7 @@ public class SvnRecursiveStatusWalker {
SVNStatus statusInner;
try {
statusInner = ioFileStatus != null ? ioFileStatus :
myCurrentItem.getClient(itemFile).doStatus(itemFile, false);
myCurrentItem.getClient().doStatus(itemFile, false);
}
catch (SVNException e) {
LOG.info(e);
@@ -295,8 +288,7 @@ public class SvnRecursiveStatusWalker {
//myReceiver.processUnversioned(vFile);
//processRecursively(vFile, myCurrentItem.getDepth());
} else {
final MyItem childItem = new MyItem(myVcs, new FilePathImpl(vFile), SVNDepth.INFINITY,
myPartner.createStatusClient(), true);
final MyItem childItem = createItem(new FilePathImpl(vFile), SVNDepth.INFINITY, true);
myQueue.add(childItem);
}
} else {
@@ -309,7 +301,7 @@ public class SvnRecursiveStatusWalker {
}
}
private VirtualFile getVirtualFile(File ioFile) {
private static VirtualFile getVirtualFile(File ioFile) {
final LocalFileSystem lfs = LocalFileSystem.getInstance();
VirtualFile vFile = lfs.findFileByIoFile(ioFile);
if (vFile == null) {
@@ -18,7 +18,7 @@ package org.jetbrains.idea.svn;
import com.intellij.openapi.vcs.FileStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.portable.PortableStatus;
import org.jetbrains.idea.svn.status.PortableStatus;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNStatusType;
@@ -73,6 +73,7 @@ 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.auth.SvnAuthenticationNotifier;
import org.jetbrains.idea.svn.checkin.SvnCheckinEnvironment;
import org.jetbrains.idea.svn.checkout.SvnCheckoutProvider;
import org.jetbrains.idea.svn.commandLine.SvnExecutableChecker;
@@ -84,7 +85,6 @@ 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;
@@ -1386,6 +1386,11 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> {
return getFactory(getProjectRootFormat(), false);
}
@NotNull
public ClientFactory getFactory(@NotNull WorkingCopyFormat format) {
return getFactory(format, false);
}
public ClientFactory getSvnKitFactory() {
return svnKitClientFactory;
}
@@ -41,12 +41,4 @@ public class ChangeListsMergerFactory implements MergerFactory {
String branchName) {
return new Merger(vcs, myChangeListsList, target, handler, currentBranchUrl, branchName);
}
public List<CommittedChangeList> getListsToMerge() {
return myChangeListsList;
}
public boolean isMergeAll() {
return false;
}
}
@@ -26,7 +26,6 @@ import org.tmatesoft.svn.core.SVNURL;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ChangeSetMergerFactory implements MergerFactory {
@@ -45,12 +44,4 @@ public class ChangeSetMergerFactory implements MergerFactory {
String branchName) {
return new PointMerger(vcs, mySelectedList, target, handler, currentBranchUrl, mySelectedChanges, branchName);
}
public List<CommittedChangeList> getListsToMerge() {
return Collections.singletonList(mySelectedList);
}
public boolean isMergeAll() {
return false;
}
}
@@ -35,20 +35,18 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.SvnBundle;
import org.jetbrains.idea.svn.SvnStatusUtil;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.checkin.CommitEventHandler;
import org.jetbrains.idea.svn.checkin.IdeaCommitHandler;
import org.jetbrains.idea.svn.commandLine.CommitEventHandler;
import org.jetbrains.idea.svn.dialogs.CreateBranchOrTagDialog;
import org.jetbrains.idea.svn.update.AutoSvnUpdater;
import org.jetbrains.idea.svn.update.SingleRootSwitcher;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.*;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class CreateBranchOrTagAction extends BasicAction {
protected String getActionName(AbstractVcs vcs) {
@@ -89,14 +87,13 @@ public class CreateBranchOrTagAction extends BasicAction {
dstSvnUrl = SVNURL.parseURIEncoded(dstURL);
parentUrl = dstSvnUrl.removePathTail();
if (!dirExists(activeVcs, project, parentUrl)) {
if (!dirExists(activeVcs, parentUrl)) {
int rc = Messages.showYesNoDialog(project, "The repository path '" + parentUrl + "' does not exist. Would you like to create it?",
"Branch or Tag", Messages.getQuestionIcon());
if (rc == Messages.NO) {
return;
}
}
}
catch (SVNException e) {
throw new VcsException(e);
@@ -111,11 +108,10 @@ public class CreateBranchOrTagAction extends BasicAction {
progress.setText(SvnBundle.message("progress.text.copy.to", dstURL));
handler = new IdeaCommitHandler(progress);
}
checkCreateDir(parentUrl, activeVcs, comment);
SvnTarget source = isSrcFile ? SvnTarget.fromFile(srcFile, revision) : SvnTarget.fromURL(srcUrl, revision);
long newRevision = activeVcs.getFactory(source).createCopyMoveClient()
.copy(source, SvnTarget.fromURL(dstSvnUrl), revision, true, comment, handler);
.copy(source, SvnTarget.fromURL(dstSvnUrl), revision, true, false, comment, handler);
updateStatusBar(newRevision, project);
}
@@ -124,7 +120,8 @@ public class CreateBranchOrTagAction extends BasicAction {
}
}
};
ProgressManager.getInstance().runProcessWithProgressSynchronously(copyCommand, SvnBundle.message("progress.title.copy"), false, project);
ProgressManager.getInstance()
.runProcessWithProgressSynchronously(copyCommand, SvnBundle.message("progress.title.copy"), false, project);
if (!exception.isNull()) {
throw new VcsException(exception.get());
}
@@ -148,25 +145,7 @@ public class CreateBranchOrTagAction extends BasicAction {
}
}
private static void checkCreateDir(@NotNull SVNURL url, @NotNull final SvnVcs activeVcs, @NotNull final String comment)
throws SVNException, VcsException {
final SVNURL baseUrl = url;
List<SVNURL> dirsToCreate = new ArrayList<SVNURL>();
while(!dirExists(activeVcs, activeVcs.getProject(), url)) {
dirsToCreate.add(0, url);
url = url.removePathTail();
if (url.getPath().length() == 0) {
throw new VcsException("Invalid repository root path for " + baseUrl);
}
}
if (! dirsToCreate.isEmpty()) {
// TODO: Works for 1.8 - implement this for command line
SVNCommitClient commitClient = activeVcs.createCommitClient();
commitClient.doMkDir(dirsToCreate.toArray(new SVNURL[dirsToCreate.size()]), comment);
}
}
private static boolean dirExists(@NotNull final SvnVcs vcs, @NotNull Project project, @NotNull final SVNURL url) throws SVNException {
private static boolean dirExists(@NotNull final SvnVcs vcs, @NotNull final SVNURL url) throws SVNException {
final Ref<SVNException> excRef = new Ref<SVNException>();
final Ref<Boolean> resultRef = new Ref<Boolean>(Boolean.TRUE);
@@ -176,7 +155,6 @@ public class CreateBranchOrTagAction extends BasicAction {
vcs.getInfo(url, SVNRevision.HEAD);
}
catch (SVNException e) {
// TODO: Check what error code returned by cmd client and implement corresponding support
if (e.getErrorMessage().getErrorCode().equals(SVNErrorCode.RA_ILLEGAL_URL)) {
resultRef.set(Boolean.FALSE);
}
@@ -189,11 +167,12 @@ public class CreateBranchOrTagAction extends BasicAction {
final Application application = ApplicationManager.getApplication();
if (application.isDispatchThread()) {
ProgressManager.getInstance().runProcessWithProgressSynchronously(taskImpl, "Checking target folder", true, project);
} else {
ProgressManager.getInstance().runProcessWithProgressSynchronously(taskImpl, "Checking target folder", true, vcs.getProject());
}
else {
taskImpl.run();
}
if (! excRef.isNull()) throw excRef.get();
if (!excRef.isNull()) throw excRef.get();
return resultRef.get();
}
@@ -204,5 +183,4 @@ public class CreateBranchOrTagAction extends BasicAction {
protected boolean isBatchAction() {
return false;
}
}
@@ -35,7 +35,7 @@ import org.jetbrains.idea.svn.SvnStatusUtil;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.conflict.ConflictClient;
import org.jetbrains.idea.svn.dialogs.SelectFilesDialog;
import org.jetbrains.idea.svn.portable.SvnStatusClientI;
import org.jetbrains.idea.svn.status.StatusClient;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.*;
@@ -122,7 +122,7 @@ public class MarkResolvedAction extends BasicAction {
for (VirtualFile file : files) {
try {
File path = new File(file.getPath());
SvnStatusClientI client = vcs.getFactory(path).createStatusClient();
StatusClient client = vcs.getFactory(path).createStatusClient();
client.doStatus(path, true, false, false, false, new ISVNStatusHandler() {
public void handleStatus(SVNStatus status) {
@@ -37,7 +37,6 @@ import org.jetbrains.idea.svn.*;
import org.jetbrains.idea.svn.api.ClientFactory;
import org.jetbrains.idea.svn.checkout.SvnCheckoutProvider;
import org.jetbrains.idea.svn.dialogs.ShareDialog;
import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
@@ -220,10 +219,10 @@ public class ShareProjectAction extends BasicAction {
return folderEmpty[0];
}
private static Pair<SVNRevision, SVNURL> createRemoteFolder(final SvnVcs vcs,
final SVNURL parent,
private static Pair<SVNRevision, SVNURL> createRemoteFolder(@NotNull final SvnVcs vcs,
@NotNull final SVNURL parent,
final String folderName,
String commitText) throws SVNException {
String commitText) throws VcsException, SVNException {
SVNURL url = parent.appendPath(folderName, false);
final String urlText = url.toString();
final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
@@ -231,11 +230,13 @@ public class ShareProjectAction extends BasicAction {
indicator.checkCanceled();
indicator.setText(SvnBundle.message("share.directory.create.dir.progress.text", urlText));
}
// TODO: Implement with command line client
final SVNCommitInfo info =
vcs.createCommitClient().doMkDir(new SVNURL[]{url}, SvnBundle.message("share.directory.commit.message", folderName,
ApplicationNamesInfo.getInstance().getFullProductName(), commitText));
return new Pair<SVNRevision, SVNURL>(SVNRevision.create(info.getNewRevision()), url);
String message =
SvnBundle.message("share.directory.commit.message", folderName, ApplicationNamesInfo.getInstance().getFullProductName(), commitText);
SvnTarget target = SvnTarget.fromURL(url);
long revision = vcs.getFactoryFromSettings().createBrowseClient().createDirectory(target, message, false);
return Pair.create(SVNRevision.create(revision), url);
}
@Override
@@ -43,7 +43,7 @@ public class CmdAddClient extends BaseSvnClient implements AddClient {
// TODO: handler should be called in parallel with command execution, but this will be in other thread
// TODO: check if that is ok for current handler implementation
// TODO: add possibility to invoke "handler.checkCancelled" - process should be killed
CommandExecutor command = CommandUtil.execute(myVcs, SvnTarget.fromFile(file), SvnCommandName.add, parameters, null);
CommandExecutor command = execute(myVcs, SvnTarget.fromFile(file), SvnCommandName.add, parameters, null);
FileStatusResultParser parser = new FileStatusResultParser(CHANGED_PATH, handler, new AddStatusConvertor());
parser.parse(command.getOutput());
}
@@ -16,6 +16,10 @@ import java.io.File;
*/
public class SvnKitAddClient extends BaseSvnClient implements AddClient {
/**
* TODO: Implement correct support for includeIgnored parameter. Also check that correct depth will be used for all cases (when another
* TODO: overload of doAdd() is used) as, for instance, SVNDepth.recurseFromDepth(EMPTY) = false, SVNDepth.fromRecursive(false) = FILES.
*/
@Override
public void add(@NotNull File file,
@Nullable SVNDepth depth,
@@ -42,7 +42,7 @@ public class CmdAnnotateClient extends BaseSvnClient implements AnnotateClient {
CommandUtil.put(parameters, diffOptions);
parameters.add("--xml");
CommandExecutor command = CommandUtil.execute(myVcs, target, SvnCommandName.blame, parameters, null);
CommandExecutor command = execute(myVcs, target, SvnCommandName.blame, parameters, null);
parseOutput(command.getOutput(), handler);
}
@@ -6,7 +6,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.WorkingCopyFormat;
import org.jetbrains.idea.svn.commandLine.SvnBindException;
import org.jetbrains.idea.svn.auth.IdeaSvnkitBasedAuthenticationCallback;
import org.jetbrains.idea.svn.commandLine.*;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNEvent;
@@ -15,6 +16,7 @@ import org.tmatesoft.svn.core.wc2.SvnTarget;
import java.io.File;
import java.util.Collection;
import java.util.List;
/**
* @author Konstantin Kolosovsky.
@@ -71,6 +73,42 @@ public abstract class BaseSvnClient implements SvnClient {
}
}
/**
* Utility method for running commands.
* // TODO: Should be replaced with non-static analogue.
*
* @param vcs
* @param target
* @param name
* @param parameters
* @param listener
* @throws com.intellij.openapi.vcs.VcsException
*/
public static CommandExecutor execute(@NotNull SvnVcs vcs,
@NotNull SvnTarget target,
@NotNull SvnCommandName name,
@NotNull List<String> parameters,
@Nullable LineCommandListener listener) throws SvnBindException {
return execute(vcs, target, null, name, parameters, listener);
}
public static CommandExecutor execute(@NotNull SvnVcs vcs,
@NotNull SvnTarget target,
@Nullable File workingDirectory,
@NotNull SvnCommandName name,
@NotNull List<String> parameters,
@Nullable LineCommandListener listener) throws SvnBindException {
Command command = new Command(name);
command.setTarget(target);
command.setWorkingDirectory(workingDirectory);
command.setResultBuilder(listener);
command.put(parameters);
CommandRuntime runtime = new CommandRuntime(vcs, new IdeaSvnkitBasedAuthenticationCallback(vcs));
return runtime.runWithAuthenticationAttempt(command);
}
protected static void callHandler(@Nullable ISVNEventHandler handler, @NotNull SVNEvent event) throws VcsException {
if (handler != null) {
try {
@@ -1,11 +1,13 @@
package org.jetbrains.idea.svn.api;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.add.AddClient;
import org.jetbrains.idea.svn.annotate.AnnotateClient;
import org.jetbrains.idea.svn.browse.BrowseClient;
import org.jetbrains.idea.svn.change.ChangeListClient;
import org.jetbrains.idea.svn.checkin.CheckinClient;
import org.jetbrains.idea.svn.checkin.ImportClient;
import org.jetbrains.idea.svn.checkout.CheckoutClient;
import org.jetbrains.idea.svn.checkout.ExportClient;
@@ -18,13 +20,15 @@ import org.jetbrains.idea.svn.diff.DiffClient;
import org.jetbrains.idea.svn.history.HistoryClient;
import org.jetbrains.idea.svn.integrate.MergeClient;
import org.jetbrains.idea.svn.lock.LockClient;
import org.jetbrains.idea.svn.portable.SvnStatusClientI;
import org.jetbrains.idea.svn.update.UpdateClient;
import org.jetbrains.idea.svn.portable.SvnWcClientI;
import org.jetbrains.idea.svn.info.InfoClient;
import org.jetbrains.idea.svn.status.StatusClient;
import org.jetbrains.idea.svn.properties.PropertyClient;
import org.jetbrains.idea.svn.revert.RevertClient;
import org.jetbrains.idea.svn.update.RelocateClient;
import org.jetbrains.idea.svn.update.UpdateClient;
import org.jetbrains.idea.svn.upgrade.UpgradeClient;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.ISVNStatusFileProvider;
/**
* @author Konstantin Kolosovsky.
@@ -40,8 +44,8 @@ public abstract class ClientFactory {
protected HistoryClient historyClient;
protected RevertClient revertClient;
protected DeleteClient deleteClient;
protected SvnStatusClientI statusClient;
protected SvnWcClientI infoClient;
protected StatusClient statusClient;
protected InfoClient infoClient;
protected CopyMoveClient copyMoveClient;
protected ConflictClient conflictClient;
protected PropertyClient propertyClient;
@@ -57,6 +61,7 @@ public abstract class ClientFactory {
protected UpgradeClient myUpgradeClient;
protected BrowseClient myBrowseClient;
protected DiffClient myDiffClient;
protected CheckinClient myCheckinClient;
protected ClientFactory(@NotNull SvnVcs vcs) {
myVcs = vcs;
@@ -91,15 +96,18 @@ public abstract class ClientFactory {
}
@NotNull
public SvnStatusClientI createStatusClient() {
// TODO: Update this in same like other clients - move to corresponding package, rename clients
return statusClient;
public StatusClient createStatusClient() {
return prepare(statusClient);
}
@NotNull
public SvnWcClientI createInfoClient() {
// TODO: Update this in same like other clients - move to corresponding package, rename clients
return infoClient;
public StatusClient createStatusClient(@Nullable ISVNStatusFileProvider provider, @NotNull ISVNEventHandler handler) {
return createStatusClient();
}
@NotNull
public InfoClient createInfoClient() {
return prepare(infoClient);
}
// TODO: Update this in same like other clients - move to corresponding package, rename clients
@@ -187,6 +195,11 @@ public abstract class ClientFactory {
return prepare(myDiffClient);
}
@NotNull
public CheckinClient createCheckinClient() {
return prepare(myCheckinClient);
}
@NotNull
protected <T extends SvnClient> T prepare(@NotNull T client) {
client.setVcs(myVcs);
@@ -6,25 +6,26 @@ import org.jetbrains.idea.svn.add.CmdAddClient;
import org.jetbrains.idea.svn.annotate.CmdAnnotateClient;
import org.jetbrains.idea.svn.browse.CmdBrowseClient;
import org.jetbrains.idea.svn.change.CmdChangeListClient;
import org.jetbrains.idea.svn.checkin.CmdCheckinClient;
import org.jetbrains.idea.svn.checkin.CmdImportClient;
import org.jetbrains.idea.svn.checkout.CmdCheckoutClient;
import org.jetbrains.idea.svn.checkout.CmdExportClient;
import org.jetbrains.idea.svn.cleanup.CmdCleanupClient;
import org.jetbrains.idea.svn.diff.CmdDiffClient;
import org.jetbrains.idea.svn.update.CmdUpdateClient;
import org.jetbrains.idea.svn.commandLine.SvnCommandLineInfoClient;
import org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient;
import org.jetbrains.idea.svn.status.CmdStatusClient;
import org.jetbrains.idea.svn.info.CmdInfoClient;
import org.jetbrains.idea.svn.conflict.CmdConflictClient;
import org.jetbrains.idea.svn.content.CmdContentClient;
import org.jetbrains.idea.svn.copy.CmdCopyMoveClient;
import org.jetbrains.idea.svn.delete.CmdDeleteClient;
import org.jetbrains.idea.svn.diff.CmdDiffClient;
import org.jetbrains.idea.svn.history.CmdHistoryClient;
import org.jetbrains.idea.svn.integrate.CmdMergeClient;
import org.jetbrains.idea.svn.lock.CmdLockClient;
import org.jetbrains.idea.svn.update.UpdateClient;
import org.jetbrains.idea.svn.properties.CmdPropertyClient;
import org.jetbrains.idea.svn.revert.CmdRevertClient;
import org.jetbrains.idea.svn.update.CmdRelocateClient;
import org.jetbrains.idea.svn.update.CmdUpdateClient;
import org.jetbrains.idea.svn.update.UpdateClient;
import org.jetbrains.idea.svn.upgrade.CmdUpgradeClient;
/**
@@ -59,8 +60,9 @@ public class CmdClientFactory extends ClientFactory {
myUpgradeClient = new CmdUpgradeClient();
myBrowseClient = new CmdBrowseClient();
myDiffClient = new CmdDiffClient();
statusClient = new SvnCommandLineStatusClient(myVcs);
infoClient = new SvnCommandLineInfoClient(myVcs);
myCheckinClient = new CmdCheckinClient();
statusClient = new CmdStatusClient();
infoClient = new CmdInfoClient();
}
@NotNull
@@ -1,11 +1,13 @@
package org.jetbrains.idea.svn.api;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.add.SvnKitAddClient;
import org.jetbrains.idea.svn.annotate.SvnKitAnnotateClient;
import org.jetbrains.idea.svn.browse.SvnKitBrowseClient;
import org.jetbrains.idea.svn.change.SvnKitChangeListClient;
import org.jetbrains.idea.svn.checkin.SvnKitCheckinClient;
import org.jetbrains.idea.svn.checkin.SvnKitImportClient;
import org.jetbrains.idea.svn.checkout.SvnKitCheckoutClient;
import org.jetbrains.idea.svn.checkout.SvnKitExportClient;
@@ -18,14 +20,17 @@ import org.jetbrains.idea.svn.diff.SvnKitDiffClient;
import org.jetbrains.idea.svn.history.SvnKitHistoryClient;
import org.jetbrains.idea.svn.integrate.SvnKitMergeClient;
import org.jetbrains.idea.svn.lock.SvnKitLockClient;
import org.jetbrains.idea.svn.update.SvnKitUpdateClient;
import org.jetbrains.idea.svn.update.UpdateClient;
import org.jetbrains.idea.svn.portable.SvnkitSvnStatusClient;
import org.jetbrains.idea.svn.portable.SvnkitSvnWcClient;
import org.jetbrains.idea.svn.status.StatusClient;
import org.jetbrains.idea.svn.status.SvnKitStatusClient;
import org.jetbrains.idea.svn.info.SvnKitInfoClient;
import org.jetbrains.idea.svn.properties.SvnKitPropertyClient;
import org.jetbrains.idea.svn.revert.SvnKitRevertClient;
import org.jetbrains.idea.svn.update.SvnKitRelocateClient;
import org.jetbrains.idea.svn.update.SvnKitUpdateClient;
import org.jetbrains.idea.svn.update.UpdateClient;
import org.jetbrains.idea.svn.upgrade.SvnKitUpgradeClient;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.ISVNStatusFileProvider;
/**
* @author Konstantin Kolosovsky.
@@ -59,8 +64,15 @@ public class SvnKitClientFactory extends ClientFactory {
myUpgradeClient = new SvnKitUpgradeClient();
myBrowseClient = new SvnKitBrowseClient();
myDiffClient = new SvnKitDiffClient();
statusClient = new SvnkitSvnStatusClient(myVcs, null);
infoClient = new SvnkitSvnWcClient(myVcs);
myCheckinClient = new SvnKitCheckinClient();
statusClient = new SvnKitStatusClient();
infoClient = new SvnKitInfoClient();
}
@NotNull
@Override
public StatusClient createStatusClient(@Nullable ISVNStatusFileProvider provider, @NotNull ISVNEventHandler handler) {
return prepare(new SvnKitStatusClient(provider, handler));
}
@NotNull
@@ -0,0 +1,179 @@
/*
* Copyright 2000-2014 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.auth;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.util.messages.MessageBusConnection;
import org.intellij.lang.annotations.MagicConstant;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.SvnConfiguration;
import org.jetbrains.idea.svn.SvnVcs;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.*;
import org.tmatesoft.svn.core.wc.SVNRevision;
import java.io.IOException;
/**
* @author Konstantin Kolosovsky.
*/
abstract class AbstractAuthenticator {
private static final Logger LOG = Logger.getInstance(AbstractAuthenticator.class);
@NotNull protected final IdeaSvnkitBasedAuthenticationCallback myAuthService;
@NotNull protected final SvnVcs myVcs;
@NotNull protected final SVNURL myUrl;
protected final String myRealm;
protected boolean myStoreInUsual;
protected SvnAuthenticationManager myTmpDirManager;
AbstractAuthenticator(@NotNull IdeaSvnkitBasedAuthenticationCallback authService, @NotNull SVNURL url, String realm) {
myAuthService = authService;
myVcs = myAuthService.getVcs();
myUrl = url;
myRealm = realm;
}
protected boolean tryAuthenticate() {
final SvnAuthenticationManager passive = myVcs.getSvnConfiguration().getPassiveAuthenticationManager(myVcs.getProject());
final SvnAuthenticationManager active = myVcs.getSvnConfiguration().getAuthenticationManager(myVcs);
try {
boolean authenticated = getWithPassive(passive) || getWithActive(active);
if (!authenticated) return false;
SvnAuthenticationManager manager = myStoreInUsual ? active : createTmpManager();
manager.setArtificialSaving(true);
return acknowledge(manager);
}
catch (IOException e) {
LOG.info(e);
VcsBalloonProblemNotifier.showOverChangesView(myVcs.getProject(), e.getMessage(), MessageType.ERROR);
return false;
}
catch (SVNException e) {
LOG.info(e);
VcsBalloonProblemNotifier.showOverChangesView(myVcs.getProject(), e.getMessage(), MessageType.ERROR);
return false;
}
}
@NotNull
protected SvnAuthenticationManager createTmpManager() throws IOException {
if (myTmpDirManager == null) {
myAuthService.initTmpDir(myVcs.getSvnConfiguration());
myTmpDirManager = new SvnAuthenticationManager(myVcs.getProject(), myAuthService.getTempDirectory());
myTmpDirManager.setRuntimeStorage(SvnConfiguration.RUNTIME_AUTH_CACHE);
myTmpDirManager.setAuthenticationProvider(new SvnInteractiveAuthenticationProvider(myVcs, myTmpDirManager));
}
return myTmpDirManager;
}
protected boolean getWithActive(SvnAuthenticationManager active) throws SVNException {
MessageBusConnection connection = null;
try {
final Project project = myVcs.getProject();
connection = project.getMessageBus().connect(project);
connection.subscribe(SvnAuthenticationManager.AUTHENTICATION_PROVIDER_LISTENER, new MyAuthenticationProviderListener());
makeAuthCall(active);
}
finally {
if (connection != null) {
connection.disconnect();
}
}
return afterAuthCall();
}
protected void makeAuthCall(@NotNull SvnAuthenticationManager manager) throws SVNException {
myVcs.createWCClient(manager).doInfo(myUrl, SVNRevision.UNDEFINED, SVNRevision.HEAD);
}
protected void acceptServerAuthentication(SVNURL url, String realm, Object certificate, @MagicConstant int acceptResult) {
}
public void requestClientAuthentication(SVNURL url, String realm, SVNAuthentication authentication) {
}
protected abstract boolean afterAuthCall();
protected abstract boolean getWithPassive(SvnAuthenticationManager passive) throws SVNException;
protected abstract boolean acknowledge(SvnAuthenticationManager manager) throws SVNException;
private class MyAuthenticationProviderListener implements SvnAuthenticationManager.ISVNAuthenticationProviderListener {
@Override
public void requestClientAuthentication(String kind,
SVNURL url,
String realm,
SVNErrorMessage errorMessage,
SVNAuthentication previousAuth,
boolean authMayBeStored,
SVNAuthentication authentication) {
AbstractAuthenticator.this.requestClientAuthentication(url, realm, authentication);
}
@Override
public void acceptServerAuthentication(SVNURL url,
String realm,
Object certificate,
boolean resultMayBeStored,
@MagicConstant int acceptResult) {
AbstractAuthenticator.this.acceptServerAuthentication(url, realm, certificate, acceptResult);
}
}
protected static boolean storeCredentials(@NotNull SvnAuthenticationManager manager, final SVNAuthentication authentication, String realm)
throws SVNException {
try {
if (authentication instanceof SVNSSLAuthentication && (((SVNSSLAuthentication)authentication).getCertificateFile() != null)) {
manager.acknowledgeForSSL(true, authentication);
realm = ((SVNSSLAuthentication)authentication).getCertificateFile().getPath();
}
manager.acknowledgeAuthentication(true, getFromType(authentication), realm, null, authentication, authentication.getURL());
}
catch (SvnAuthenticationManager.CredentialsSavedException e) {
return e.isSuccess();
}
return true;
}
@NotNull
private static String getFromType(SVNAuthentication authentication) {
if (authentication instanceof SVNPasswordAuthentication) {
return ISVNAuthenticationManager.PASSWORD;
}
if (authentication instanceof SVNSSHAuthentication) {
return ISVNAuthenticationManager.SSH;
}
if (authentication instanceof SVNSSLAuthentication) {
return ISVNAuthenticationManager.SSL;
}
if (authentication instanceof SVNUserNameAuthentication) {
return ISVNAuthenticationManager.USERNAME;
}
throw new IllegalArgumentException();
}
}
@@ -0,0 +1,115 @@
/*
* Copyright 2000-2014 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.auth;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.tmatesoft.svn.core.SVNCancelException;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.auth.SVNAuthentication;
import org.tmatesoft.svn.core.auth.SVNPasswordAuthentication;
import org.tmatesoft.svn.core.auth.SVNSSLAuthentication;
import java.util.List;
/**
* @author Konstantin Kolosovsky.
*/
class CredentialsAuthenticator extends AbstractAuthenticator {
private String myKind;
// sometimes realm string is different (with <>), so store credentials for both strings..
private String myRealm2;
private SVNAuthentication myAuthentication;
CredentialsAuthenticator(@NotNull IdeaSvnkitBasedAuthenticationCallback authService,
@NotNull SVNURL url,
@Nullable String realm) {
super(authService, url, realm == null ? url.getHost() : realm);
}
public boolean tryAuthenticate(boolean passwordRequest) {
final List<String> kinds = IdeaSvnkitBasedAuthenticationCallback.getKinds(myUrl, passwordRequest);
for (String kind : kinds) {
myKind = kind;
if (!tryAuthenticate()) {
return false;
}
}
return true;
}
@Override
protected boolean getWithPassive(SvnAuthenticationManager passive) throws SVNException {
myAuthentication = getWithPassiveImpl(passive);
if (myAuthentication != null && !checkAuthOk(myAuthentication)) {
myAuthService.clearPassiveCredentials(myRealm, myUrl,
myAuthentication instanceof SVNPasswordAuthentication); //clear passive also take into acconut ssl filepath
myAuthentication = null;
}
return myAuthentication != null;
}
private SVNAuthentication getWithPassiveImpl(SvnAuthenticationManager passive) throws SVNException {
try {
return passive.getFirstAuthentication(myKind, myRealm, myUrl);
}
catch (SVNCancelException e) {
return null;
}
}
private boolean checkAuthOk(SVNAuthentication authentication) {
if (authentication instanceof SVNPasswordAuthentication && StringUtil.isEmptyOrSpaces(authentication.getUserName())) return false;
if (authentication instanceof SVNSSLAuthentication) {
if (StringUtil.isEmptyOrSpaces(((SVNSSLAuthentication)authentication).getPassword())) return false;
}
return true;
}
@Override
protected boolean getWithActive(final SvnAuthenticationManager active) throws SVNException {
if (ISVNAuthenticationManager.SSL.equals(myKind)) {
if (super.getWithActive(active)) return true;
}
myAuthentication = active.getProvider().requestClientAuthentication(myKind, myUrl, myRealm, null, null, true);
myStoreInUsual = myAuthService.getTempDirectory() == null && myAuthentication != null && myAuthentication.isStorageAllowed();
return myAuthentication != null;
}
public void requestClientAuthentication(SVNURL url, String realm, SVNAuthentication authentication) {
if (!myUrl.equals(url)) return;
myAuthentication = authentication;
myRealm2 = realm;
myStoreInUsual = myAuthentication != null && myAuthentication.isStorageAllowed();
}
@Override
protected boolean afterAuthCall() {
return myAuthentication != null;
}
@Override
protected boolean acknowledge(SvnAuthenticationManager manager) throws SVNException {
if (!StringUtil.isEmptyOrSpaces(myRealm2) && !myRealm2.equals(myRealm)) {
storeCredentials(manager, myAuthentication, myRealm2);
}
return storeCredentials(manager, myAuthentication, myRealm);
}
}
@@ -0,0 +1,309 @@
/*
* 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.auth;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.util.PopupUtil;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.WaitForProgressToShow;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.net.HttpConfigurable;
import com.intellij.util.proxy.CommonProxy;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnBundle;
import org.jetbrains.idea.svn.SvnConfiguration;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.commandLine.AuthenticationCallback;
import org.jetbrains.idea.svn.dialogs.SimpleCredentialsDialog;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.auth.*;
import java.io.File;
import java.io.IOException;
import java.net.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* Created with IntelliJ IDEA.
* User: Irina.Chernushina
* Date: 2/26/13
* Time: 1:27 PM
*/
public class IdeaSvnkitBasedAuthenticationCallback implements AuthenticationCallback {
@NotNull private final SvnVcs myVcs;
private static final Logger LOG = Logger.getInstance(IdeaSvnkitBasedAuthenticationCallback.class);
private File myTempDirectory;
private boolean myProxyCredentialsWereReturned;
private SvnConfiguration myConfiguration;
private final Set<String> myRequestedCredentials;
public IdeaSvnkitBasedAuthenticationCallback(@NotNull SvnVcs vcs) {
myVcs = vcs;
myConfiguration = SvnConfiguration.getInstance(myVcs.getProject());
myRequestedCredentials = ContainerUtil.newHashSet();
}
@NotNull
public SvnVcs getVcs() {
return myVcs;
}
@Nullable
public File getTempDirectory() {
return myTempDirectory;
}
@Override
public boolean authenticateFor(String realm, SVNURL repositoryUrl, boolean previousFailed, boolean passwordRequest) {
if (repositoryUrl == null) {
return false;
}
return new CredentialsAuthenticator(this, repositoryUrl, realm).tryAuthenticate(passwordRequest);
}
@Nullable
@Override
public SVNAuthentication requestCredentials(final SVNURL repositoryUrl, final String type) {
SVNAuthentication authentication = null;
if (repositoryUrl != null) {
final String realm = repositoryUrl.toDecodedString();
authentication = requestCredentials(realm, type, new Getter<Pair<SVNAuthentication, Boolean>>() {
@Override
public Pair<SVNAuthentication, Boolean> get() {
SVNAuthentication result = myVcs.getSvnConfiguration().getInteractiveManager(myVcs).getInnerProvider()
.requestClientAuthentication(type, repositoryUrl, realm, null, null, true);
return new Pair<SVNAuthentication, Boolean>(result, true);
}
});
}
if (authentication == null) {
LOG.warn("Could not get authentication. Type - " + type + ", Url - " + repositoryUrl);
}
return authentication;
}
@Nullable
private <T> T requestCredentials(@NotNull String realm, @NotNull String type, @NotNull Getter<Pair<T, Boolean>> fromUserProvider) {
T result;
Object data = SvnConfiguration.RUNTIME_AUTH_CACHE.getData(type, realm);
// we return credentials from cache if they are asked for the first time during command execution, otherwise - user is asked
if (data != null && !myRequestedCredentials.contains(getKey(realm, type))) {
// we already have credentials in memory cache
result = (T)data;
myRequestedCredentials.add(getKey(realm, type));
}
else {
// ask user for credentials
Pair<T, Boolean> userData = fromUserProvider.get();
result = userData.first;
if (result != null && userData.second) {
// save user credentials to memory cache
myVcs.getSvnConfiguration().acknowledge(type, realm, result);
myRequestedCredentials.add(getKey(realm, type));
}
}
return result;
}
@NotNull
private static String getKey(@NotNull String realm, @NotNull String type) {
return type + "$" + realm;
}
@Override
@Nullable
public String requestSshCredentials(@NotNull final String realm,
@NotNull final SimpleCredentialsDialog.Mode mode,
@NotNull final String key) {
return requestCredentials(realm, ISVNAuthenticationManager.SSH, new Getter<Pair<String, Boolean>>() {
@Override
public Pair<String, Boolean> get() {
final Ref<String> answer = new Ref<String>();
Runnable command = new Runnable() {
public void run() {
SimpleCredentialsDialog dialog = new SimpleCredentialsDialog(myVcs.getProject());
dialog.setup(mode, realm, key, true);
dialog.setTitle(SvnBundle.message("dialog.title.authentication.required"));
dialog.setSaveEnabled(false);
dialog.show();
if (dialog.isOK()) {
answer.set(dialog.getPassword());
}
}
};
// Use ModalityState.any() as currently ssh credentials in terminal mode are requested in the thread that reads output and not in
// the thread that started progress
WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(command, ModalityState.any());
return new Pair<String, Boolean>(answer.get(), true);
}
});
}
@Override
public boolean acceptSSLServerCertificate(final SVNURL repositoryUrl, final String realm) {
if (repositoryUrl == null) {
return false;
}
return new SSLServerCertificateAuthenticator(this, repositoryUrl, realm).tryAuthenticate();
}
@Override
public void clearPassiveCredentials(String realm, SVNURL repositoryUrl, boolean password) {
if (repositoryUrl == null) {
return;
}
final SvnConfiguration configuration = SvnConfiguration.getInstance(myVcs.getProject());
final List<String> kinds = getKinds(repositoryUrl, password);
for (String kind : kinds) {
configuration.clearCredentials(kind, realm);
}
}
@Override
public boolean haveDataForTmpConfig() {
final HttpConfigurable instance = HttpConfigurable.getInstance();
return SvnConfiguration.getInstance(myVcs.getProject()).isIsUseDefaultProxy() &&
(instance.USE_HTTP_PROXY || instance.USE_PROXY_PAC);
}
@Nullable
public static Proxy getIdeaDefinedProxy(@NotNull final SVNURL url) {
// SVNKit authentication implementation sets repositories as noProxy() to provide custom proxy authentication logic - see for instance,
// SvnAuthenticationManager.getProxyManager(). But noProxy() setting is not cleared correctly in all cases - so if svn command
// (for command line) is executed on thread where repository url was added as noProxy() => proxies are not retrieved for such commands
// and execution logic is incorrect.
// To prevent such behavior repositoryUrl is manually removed from noProxy() list (for current thread).
// NOTE, that current method is only called from code flows for executing commands through command line client and should not be called
// from SVNKit code flows.
CommonProxy.getInstance().removeNoProxy(url.getProtocol(), url.getHost(), url.getPort());
final List<Proxy> proxies = CommonProxy.getInstance().select(URI.create(url.toString()));
if (proxies != null && !proxies.isEmpty()) {
for (Proxy proxy : proxies) {
if (HttpConfigurable.isRealProxy(proxy) && Proxy.Type.HTTP.equals(proxy.type())) {
return proxy;
}
}
}
return null;
}
@Override
@Nullable
public PasswordAuthentication getProxyAuthentication(@NotNull SVNURL repositoryUrl) {
Proxy proxy = getIdeaDefinedProxy(repositoryUrl);
PasswordAuthentication result = null;
if (proxy != null) {
if (myProxyCredentialsWereReturned) {
showFailedAuthenticateProxy();
}
else {
result = getProxyAuthentication(proxy, repositoryUrl);
myProxyCredentialsWereReturned = result != null;
}
}
return result;
}
private static void showFailedAuthenticateProxy() {
HttpConfigurable instance = HttpConfigurable.getInstance();
String message = instance.USE_HTTP_PROXY || instance.USE_PROXY_PAC
? "Failed to authenticate to proxy. You can change proxy credentials in HTTP proxy settings."
: "Failed to authenticate to proxy.";
PopupUtil.showBalloonForActiveComponent(message, MessageType.ERROR);
}
@Nullable
private static PasswordAuthentication getProxyAuthentication(@NotNull Proxy proxy, @NotNull SVNURL repositoryUrl) {
PasswordAuthentication result = null;
try {
result = Authenticator.requestPasswordAuthentication(repositoryUrl.getHost(), ((InetSocketAddress)proxy.address()).getAddress(),
repositoryUrl.getPort(), repositoryUrl.getProtocol(), repositoryUrl.getHost(),
repositoryUrl.getProtocol(), new URL(repositoryUrl.toString()),
Authenticator.RequestorType.PROXY);
}
catch (MalformedURLException e) {
LOG.info(e);
}
return result;
}
public void reset() {
if (myTempDirectory != null) {
FileUtil.delete(myTempDirectory);
}
}
@NotNull
public static List<String> getKinds(final SVNURL url, boolean passwordRequest) {
if (passwordRequest || "http".equals(url.getProtocol())) {
return Collections.singletonList(ISVNAuthenticationManager.PASSWORD);
}
else if ("https".equals(url.getProtocol())) {
return Collections.singletonList(ISVNAuthenticationManager.SSL);
}
else if ("svn".equals(url.getProtocol())) {
return Collections.singletonList(ISVNAuthenticationManager.PASSWORD);
}
else if (url.getProtocol().contains("svn+")) { // todo +-
return Arrays.asList(ISVNAuthenticationManager.SSH, ISVNAuthenticationManager.USERNAME);
}
return Collections.singletonList(ISVNAuthenticationManager.USERNAME);
}
@Nullable
@Override
public File getSpecialConfigDir() {
return myTempDirectory != null ? myTempDirectory : new File(myConfiguration.getConfigurationDirectory());
}
public void initTmpDir(SvnConfiguration configuration) throws IOException {
if (myTempDirectory == null) {
myTempDirectory = FileUtil.createTempDirectory("tmp", "Subversion");
FileUtil.copyDir(new File(configuration.getConfigurationDirectory()), myTempDirectory);
}
}
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn;
package org.jetbrains.idea.svn.auth;
import org.tmatesoft.svn.core.auth.*;
@@ -0,0 +1,168 @@
/*
* Copyright 2000-2014 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.auth;
import com.intellij.openapi.util.text.StringUtil;
import org.intellij.lang.annotations.MagicConstant;
import org.jetbrains.annotations.NotNull;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationProvider;
import org.tmatesoft.svn.core.auth.SVNAuthentication;
import org.tmatesoft.svn.core.internal.util.SVNBase64;
import org.tmatesoft.svn.core.internal.util.SVNHashMap;
import org.tmatesoft.svn.core.internal.util.SVNSSLUtil;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.internal.wc.SVNWCProperties;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.security.cert.*;
/**
* @author Konstantin Kolosovsky.
*/ // plus seems that we also should ask for credentials; but we didn't receive realm name yet
class SSLServerCertificateAuthenticator extends AbstractAuthenticator {
private String myCertificateRealm;
private String myCredentialsRealm;
private Object myCertificate;
private int myResult;
private SVNAuthentication myAuthentication;
SSLServerCertificateAuthenticator(@NotNull IdeaSvnkitBasedAuthenticationCallback authService,
@NotNull SVNURL url,
String realm) {
super(authService, url, realm);
}
@Override
public boolean tryAuthenticate() {
myResult = ISVNAuthenticationProvider.ACCEPTED_TEMPORARY;
myStoreInUsual = false;
return super.tryAuthenticate();
}
@Override
protected boolean getWithPassive(SvnAuthenticationManager passive) throws SVNException {
String stored = (String)passive.getRuntimeAuthStorage().getData("svn.ssl.server", myRealm);
if (stored == null) return false;
myCertificate = createCertificate(stored);
myCertificateRealm = myRealm;
return true;
}
@Override
public void requestClientAuthentication(SVNURL url, String realm, SVNAuthentication authentication) {
if (!myUrl.equals(url)) return;
myCredentialsRealm = realm;
myAuthentication = authentication;
if (myAuthentication != null) {
myStoreInUsual &= myAuthentication.isStorageAllowed();
}
}
@Override
public void acceptServerAuthentication(SVNURL url, String realm, Object certificate, @MagicConstant int acceptResult) {
if (!myUrl.equals(url)) return;
myCertificateRealm = realm;
myCertificate = certificate;
myResult = acceptResult;
}
@Override
protected boolean afterAuthCall() {
myStoreInUsual &= myCertificate != null && ISVNAuthenticationProvider.ACCEPTED == myResult;
// TODO: Previous code always returned not null value, so Boolean == null check was always false in tryAuthenticate().
// TODO: This was most likely error in code - check once again.
return ISVNAuthenticationProvider.REJECTED != myResult && myCertificate != null;
}
@Override
protected boolean acknowledge(SvnAuthenticationManager manager) throws SVNException {
// we should store certificate, if it wasn't accepted (if temporally tmp)
if (myCertificate == null) { // this is if certificate was stored only in passive area
String stored = (String)manager.getRuntimeAuthStorage().getData("svn.ssl.server", myRealm);
if (StringUtil.isEmptyOrSpaces(stored)) {
throw new SVNException(
SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, "No stored server certificate was found in runtime"));
}
myCertificate = createCertificate(stored);
myCertificateRealm = myRealm;
}
if (myAuthService.getTempDirectory() != null && myCertificate != null) {
storeServerCertificate();
if (myAuthentication != null) {
final String realm = myCredentialsRealm == null ? myCertificateRealm : myCredentialsRealm;
return storeCredentials(manager, myAuthentication, realm);
}
}
return true;
}
@NotNull
private Certificate createCertificate(@NotNull String stored) throws SVNException {
CertificateFactory factory;
try {
factory = CertificateFactory.getInstance("X509");
final byte[] buffer = new byte[stored.length()];
SVNBase64.base64ToByteArray(new StringBuffer(stored), buffer);
return factory.generateCertificate(new ByteArrayInputStream(buffer));
}
catch (CertificateException e) {
throw new SVNException(SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, e));
}
}
private void storeServerCertificate() throws SVNException {
if (!(myCertificate instanceof X509Certificate)) {
throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Can not store server certificate: " + myCertificate));
}
X509Certificate x509Certificate = (X509Certificate)myCertificate;
String stored;
try {
stored = SVNBase64.byteArrayToBase64(x509Certificate.getEncoded());
}
catch (CertificateEncodingException e) {
throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e));
}
int failures = SVNSSLUtil.getServerCertificateFailures(x509Certificate, myUrl.getHost());
storeServerCertificate(myAuthService.getTempDirectory(), myCertificateRealm, stored, failures);
}
private void storeServerCertificate(final File configDir, String realm, String data, int failures) throws SVNException {
//noinspection ResultOfMethodCallIgnored
configDir.mkdirs();
File file = new File(configDir, "auth/svn.ssl.server/" + SVNFileUtil.computeChecksum(realm));
SVNHashMap map = new SVNHashMap();
map.put("ascii_cert", data);
map.put("svn:realmstring", realm);
map.put("failures", Integer.toString(failures));
SVNFileUtil.deleteFile(file);
File tmpFile = SVNFileUtil.createUniqueFile(configDir, "auth", ".tmp", true);
try {
SVNWCProperties.setProperties(SVNProperties.wrap(map), file, tmpFile, SVNWCProperties.SVN_HASH_TERMINATOR);
}
finally {
SVNFileUtil.deleteFile(tmpFile);
}
}
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn;
package org.jetbrains.idea.svn.auth;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
@@ -25,10 +25,12 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.CalledInAwt;
import com.intellij.openapi.vcs.changes.committed.AbstractCalledLater;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.util.EventDispatcher;
@@ -40,9 +42,7 @@ import com.intellij.util.ui.UIUtil;
import com.trilead.ssh2.auth.AgentProxy;
import org.intellij.lang.annotations.MagicConstant;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.auth.ProviderType;
import org.jetbrains.idea.svn.auth.SvnAuthenticationInteraction;
import org.jetbrains.idea.svn.auth.SvnAuthenticationListener;
import org.jetbrains.idea.svn.*;
import org.jetbrains.idea.svn.config.ProxyGroup;
import org.jetbrains.idea.svn.config.SvnServerFileKeys;
import org.tmatesoft.svn.core.SVNErrorMessage;
@@ -62,7 +62,7 @@ import java.util.*;
* @author alex
*/
public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager implements SvnAuthenticationListener, ISVNAuthenticationManagerExt {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnAuthenticationManager");
private static final Logger LOG = Logger.getInstance(SvnAuthenticationManager.class);
// while Mac storage not working for IDEA, we use this key to check whether to prompt abt plaintext or just store
public static final String SVN_SSH = "svn+ssh";
public static final String HTTP = "http";
@@ -563,7 +563,7 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
}
}
public void acknowledgeForSSL(boolean accepted, String kind, String realm, SVNErrorMessage message, SVNAuthentication proxy) {
public void acknowledgeForSSL(boolean accepted, SVNAuthentication proxy) {
if (accepted && proxy instanceof SVNSSLAuthentication && (((SVNSSLAuthentication) proxy).getCertificateFile() != null)) {
final SVNSSLAuthentication svnsslAuthentication = (SVNSSLAuthentication)proxy;
final SVNURL url = svnsslAuthentication.getURL();
@@ -572,13 +572,9 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
final SVNCompositeConfigFile serversFile = provider.getServersFile();
String groupName = getGroupName(serversFile.getProperties("groups"), url.getHost());
if (StringUtil.isEmptyOrSpaces(groupName)) {
serversFile.setPropertyValue("global", SvnServerFileKeys.SSL_CLIENT_CERT_FILE, svnsslAuthentication.getCertificateFile().getPath(), true);
//serversFile.setPropertyValue("global", SvnServerFileKeys.SSL_CLIENT_CERT_PASSWORD, null, true);
} else {
serversFile.setPropertyValue(groupName, SvnServerFileKeys.SSL_CLIENT_CERT_FILE, svnsslAuthentication.getCertificateFile().getPath(), true);
//serversFile.setPropertyValue(groupName, SvnServerFileKeys.SSL_CLIENT_CERT_PASSWORD, null, true);
}
groupName = StringUtil.isEmptyOrSpaces(groupName) ? "global" : groupName;
serversFile
.setPropertyValue(groupName, SvnServerFileKeys.SSL_CLIENT_CERT_FILE, svnsslAuthentication.getCertificateFile().getPath(), true);
serversFile.save();
}
}
@@ -597,7 +593,7 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
String host = url.getHost();
String proxyHost = getServersPropertyIdea(host, HTTP_PROXY_HOST);
if ((proxyHost == null) || "".equals(proxyHost.trim())) {
if (StringUtil.isEmptyOrSpaces(proxyHost)) {
if (getConfig().isIsUseDefaultProxy()) {
// ! use common proxy if it is set
try {
@@ -607,8 +603,8 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
if (HttpConfigurable.isRealProxy(proxy) && Proxy.Type.HTTP.equals(proxy.type())) {
final SocketAddress address = proxy.address();
if (address instanceof InetSocketAddress) {
return new MyPromptingProxyManager(((InetSocketAddress)address).getHostName(), "" + ((InetSocketAddress)address).getPort(),
url.getProtocol());
return new MyPromptingProxyManager(((InetSocketAddress)address).getHostName(),
String.valueOf(((InetSocketAddress)address).getPort()), url.getProtocol());
}
}
}
@@ -642,7 +638,7 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
private static class MyPromptingProxyManager extends MySimpleProxyManager {
private static final String ourPrompt = "Proxy authentication";
private final String myProtocol;
private MyPromptingProxyManager(final String host, final String port, String protocol) {
@@ -766,7 +762,7 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
return getPropertyIdea(host, serversFile, name);
}
private String getPropertyIdea(String host, SVNCompositeConfigFile serversFile, final String name) {
private static String getPropertyIdea(String host, SVNCompositeConfigFile serversFile, final String name) {
String groupName = getGroupName(serversFile.getProperties("groups"), host);
if (groupName != null) {
Map hostProps = serversFile.getProperties(groupName);
@@ -837,7 +833,7 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
return value == null || "yes".equalsIgnoreCase(value) || "on".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value);
}
private ModalityState getCurrent() {
private static ModalityState getCurrent() {
if (ApplicationManager.getApplication().isDispatchThread()) {
return ModalityState.current();
}
@@ -848,18 +844,6 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
return pi.getModalityState();
}
/**
* Shows a yes/no question whether user wants to store his password in plain text and returns his answer.
* @param title title of the questioning dialog.
* @param message questioning message to be displayed.
* @return true if user agrees to store his password in plaintext, false if he doesn't.
*/
@CalledInAwt
private boolean askToStoreUnencrypted(String title, String message) {
final int answer = Messages.showYesNoDialog(myProject, message, title, Messages.getQuestionIcon());
return answer == Messages.YES;
}
public void setInteraction(SvnAuthenticationInteraction interaction) {
myInteraction = interaction;
}
@@ -914,10 +898,6 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
}
}
private static boolean isLion() {
return SystemInfo.isMac && SystemInfo.isMacOSSnowLeopard && ! SystemInfo.OS_VERSION.startsWith("10.6");
}
public class IdeaSVNHostOptionsProvider extends DefaultSVNHostOptionsProvider {
public IdeaSVNHostOptionsProvider() {
super(myConfigDirectory);
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn;
package org.jetbrains.idea.svn.auth;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.application.Application;
@@ -44,7 +44,7 @@ import com.intellij.util.net.HttpConfigurable;
import com.intellij.util.proxy.CommonProxy;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.dialogs.SvnInteractiveAuthenticationProvider;
import org.jetbrains.idea.svn.*;
import org.tmatesoft.svn.core.SVNAuthenticationException;
import org.tmatesoft.svn.core.SVNCancelException;
import org.tmatesoft.svn.core.SVNException;
@@ -64,7 +64,7 @@ import java.util.List;
import java.util.Timer;
public class SvnAuthenticationNotifier extends GenericNotifierImpl<SvnAuthenticationNotifier.AuthenticationRequest, SVNURL> {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnAuthenticationNotifier");
private static final Logger LOG = Logger.getInstance(SvnAuthenticationNotifier.class);
private static final List<String> ourAuthKinds = Arrays.asList(ISVNAuthenticationManager.PASSWORD, ISVNAuthenticationManager.SSH,
ISVNAuthenticationManager.SSL, ISVNAuthenticationManager.USERNAME, "svn.ssl.server", "svn.ssh.server");
@@ -13,13 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn.dialogs;
package org.jetbrains.idea.svn.auth;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.util.SystemProperties;
import org.jetbrains.idea.svn.SvnAuthenticationManager;
import org.jetbrains.idea.svn.SvnAuthenticationNotifier;
import org.jetbrains.idea.svn.SvnVcs;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNURL;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn.dialogs;
package org.jetbrains.idea.svn.auth;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
@@ -31,11 +31,10 @@ import com.jcraft.jsch.agentproxy.ConnectorFactory;
import com.jcraft.jsch.agentproxy.TrileadAgentProxy;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnAuthenticationManager;
import org.jetbrains.idea.svn.SvnBundle;
import org.jetbrains.idea.svn.SvnConfiguration;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.auth.ProviderType;
import org.jetbrains.idea.svn.dialogs.*;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.*;
@@ -47,7 +46,7 @@ import java.lang.reflect.InvocationTargetException;
import java.security.cert.X509Certificate;
public class SvnInteractiveAuthenticationProvider implements ISVNAuthenticationProvider {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.dialogs.SvnInteractiveAuthenticationProvider");
private static final Logger LOG = Logger.getInstance(SvnInteractiveAuthenticationProvider.class);
private final Project myProject;
private static final ThreadLocal<MyCallState> myCallState = new ThreadLocal<MyCallState>();
private final SvnAuthenticationManager myManager;
@@ -17,23 +17,28 @@ package org.jetbrains.idea.svn.branchConfig;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.integrate.SvnBranchItem;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.wc.SVNInfo;
import org.tmatesoft.svn.core.wc.SVNLogClient;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class DefaultConfigLoader {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.branchConfig.DefaultConfigLoader");
@NonNls private static final String DEFAULT_TRUNK_NAME = "trunk";
@NonNls private static final String DEFAULT_BRANCHES_NAME = "branches";
@NonNls private static final String DEFAULT_TAGS_NAME = "tags";
@@ -59,25 +64,10 @@ public class DefaultConfigLoader {
while(true) {
final String s = SVNPathUtil.tail(baseUrl.getPath());
if (s.equalsIgnoreCase(DEFAULT_TRUNK_NAME) || s.equalsIgnoreCase(DEFAULT_BRANCHES_NAME) || s.equalsIgnoreCase(DEFAULT_TAGS_NAME)) {
final SVNURL rootPath = baseUrl.removePathTail();
SVNLogClient client = vcs.createLogClient();
client.doList(rootPath, SVNRevision.UNDEFINED, SVNRevision.HEAD, false, false, new ISVNDirEntryHandler() {
public void handleDirEntry(final SVNDirEntry dirEntry) throws SVNException {
if (("".equals(dirEntry.getRelativePath())) || (! SVNNodeKind.DIR.equals(dirEntry.getKind()))) {
// do not use itself or files
return;
}
if (dirEntry.getName().toLowerCase().endsWith(DEFAULT_TRUNK_NAME)) {
result.setTrunkUrl(rootPath.appendPath(dirEntry.getName(), false).toString());
}
else {
result.addBranches(rootPath.appendPath(dirEntry.getName(), false).toString(),
new InfoStorage<List<SvnBranchItem>>(new ArrayList<SvnBranchItem>(0), InfoReliability.defaultValues));
}
}
});
SVNURL rootPath = baseUrl.removePathTail();
SvnTarget target = SvnTarget.fromURL(rootPath);
vcs.getFactory(target).createBrowseClient().list(target, SVNRevision.HEAD, SVNDepth.IMMEDIATES, createHandler(result, rootPath));
break;
}
if (SVNPathUtil.removeTail(baseUrl.getPath()).length() == 0) {
@@ -91,5 +81,28 @@ public class DefaultConfigLoader {
LOG.info(e);
return null;
}
catch (VcsException e) {
LOG.info(e);
return null;
}
}
@NotNull
private static ISVNDirEntryHandler createHandler(final SvnBranchConfigurationNew result, final SVNURL rootPath) {
return new ISVNDirEntryHandler() {
public void handleDirEntry(final SVNDirEntry dirEntry) throws SVNException {
if (SVNNodeKind.DIR.equals(dirEntry.getKind())) {
SVNURL childUrl = rootPath.appendPath(dirEntry.getName(), false);
if (StringUtil.endsWithIgnoreCase(dirEntry.getName(), DEFAULT_TRUNK_NAME)) {
result.setTrunkUrl(childUrl.toString());
}
else {
result.addBranches(childUrl.toString(),
new InfoStorage<List<SvnBranchItem>>(new ArrayList<SvnBranchItem>(0), InfoReliability.defaultValues));
}
}
}
};
}
}
@@ -31,4 +31,6 @@ public interface BrowseClient extends SvnClient {
void list(@NotNull SvnTarget target, @Nullable SVNRevision revision, @Nullable SVNDepth depth, @Nullable ISVNDirEntryHandler handler)
throws VcsException;
long createDirectory(@NotNull SvnTarget target, @NotNull String message, boolean makeParents) throws VcsException;
}
@@ -17,9 +17,11 @@ package org.jetbrains.idea.svn.browse;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.util.PathUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.api.BaseSvnClient;
import org.jetbrains.idea.svn.checkin.CmdCheckinClient;
import org.jetbrains.idea.svn.commandLine.CommandExecutor;
import org.jetbrains.idea.svn.commandLine.CommandUtil;
import org.jetbrains.idea.svn.commandLine.SvnBindException;
@@ -55,7 +57,7 @@ public class CmdBrowseClient extends BaseSvnClient implements BrowseClient {
CommandUtil.put(parameters, depth);
parameters.add("--xml");
CommandExecutor command = CommandUtil.execute(myVcs, target, SvnCommandName.list, parameters, null);
CommandExecutor command = execute(myVcs, target, SvnCommandName.list, parameters, null);
try {
parseOutput(target.getURL(), command, handler);
@@ -65,6 +67,24 @@ public class CmdBrowseClient extends BaseSvnClient implements BrowseClient {
}
}
@Override
public long createDirectory(@NotNull SvnTarget target, @NotNull String message, boolean makeParents) throws VcsException {
assertUrl(target);
List<String> parameters = ContainerUtil.newArrayList();
CommandUtil.put(parameters, target);
CommandUtil.put(parameters, makeParents, "--parents");
parameters.add("--message");
parameters.add(message);
CmdCheckinClient.CommandListener listener = new CmdCheckinClient.CommandListener(null);
execute(myVcs, target, SvnCommandName.mkdir, parameters, listener);
return listener.getCommittedRevision();
}
private static void parseOutput(@NotNull SVNURL url, @NotNull CommandExecutor command, @Nullable ISVNDirEntryHandler handler)
throws VcsException, SVNException {
try {
@@ -53,6 +53,20 @@ public class SvnKitBrowseClient extends BaseSvnClient implements BrowseClient {
}
}
@Override
public long createDirectory(@NotNull SvnTarget target, @NotNull String message, boolean makeParents) throws VcsException {
assertUrl(target);
try {
SVNCommitInfo commitInfo = myVcs.createCommitClient().doMkDir(new SVNURL[]{target.getURL()}, message, null, makeParents);
return commitInfo.getNewRevision();
}
catch (SVNException e) {
throw new SvnBindException(e);
}
}
@Nullable
private static ISVNDirEntryHandler wrapHandler(@Nullable ISVNDirEntryHandler handler) {
return handler == null ? null : new SkipEmptyNameDirectoriesHandler(handler);
@@ -32,7 +32,7 @@ public class CmdChangeListClient extends BaseSvnClient implements ChangeListClie
// for now parsing of the output is not required as command is executed only for one file
// and will be either successful or exception will be thrown
CommandUtil.execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.changelist, parameters, null);
execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.changelist, parameters, null);
}
@Override
@@ -45,6 +45,6 @@ public class CmdChangeListClient extends BaseSvnClient implements ChangeListClie
// for now parsing of the output is not required as command is executed only for one file
// and will be either successful or exception will be thrown
CommandUtil.execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.changelist, parameters, null);
execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.changelist, parameters, null);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2014 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.
@@ -13,9 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn.update;
package org.jetbrains.idea.svn.checkin;
public interface SvnUpdateGroups {
// String MERGED_WITH_PROPERTY_CONFLICT = "MERGED_WITH_PROPERTY_CONFLICT";
String MERGED_WITH_TREE_CONFLICT = "MERGED_WITH_TREE_CONFLICT";
import com.intellij.openapi.vcs.VcsException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.api.SvnClient;
import org.tmatesoft.svn.core.SVNCommitInfo;
import java.io.File;
import java.util.Collection;
/**
* @author Konstantin Kolosovsky.
*/
public interface CheckinClient extends SvnClient {
@NotNull
SVNCommitInfo[] commit(@NotNull Collection<File> paths, @NotNull String comment) throws VcsException;
}
@@ -13,17 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn.commandLine;
package org.jetbrains.idea.svn.checkin;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.AbstractFilterChildren;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.api.BaseSvnClient;
import org.jetbrains.idea.svn.commandLine.*;
import org.jetbrains.idea.svn.status.StatusClient;
import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNStatusType;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import java.io.File;
@@ -37,58 +48,56 @@ import java.util.regex.Pattern;
* Date: 2/25/13
* Time: 4:56 PM
*/
public class SvnCommitRunner {
public class CmdCheckinClient extends BaseSvnClient implements CheckinClient {
private static final Logger LOG = Logger.getInstance("org.jetbrains.idea.svn.commandLine.SvnCommitRunner");
private static final Logger LOG = Logger.getInstance(CmdCheckinClient.class);
public static final long INVALID_REVISION_NUMBER = -1L;
private SvnCommitRunner.CommandListener myCommandListener;
private SvnVcs myVcs;
@NotNull
@Override
public SVNCommitInfo[] commit(@NotNull Collection<File> paths, @NotNull String message) throws VcsException {
// if directory renames were used, IDEA reports all files under them as moved, but for svn we can not pass some of them
// to commit command - since not all paths are registered as changes -> so we need to filter these cases, but only if
// there at least some child-parent relationships in passed paths
try {
paths = filterCommittables(paths);
}
catch (SVNException e) {
throw new SvnBindException(e);
}
public SvnCommitRunner(@NotNull SvnVcs vcs, @Nullable CommitEventHandler handler) {
myVcs = vcs;
myCommandListener = new CommandListener(handler);
return commit(ArrayUtil.toObjectArray(paths, File.class), message);
}
public long commit(File[] paths,
String message,
@Nullable SVNDepth depth,
boolean noUnlock,
boolean keepChangelist,
Collection<String> changelists,
Map revpropTable) throws VcsException {
if (paths.length == 0) return INVALID_REVISION_NUMBER;
@NotNull
public SVNCommitInfo[] commit(@NotNull File[] paths, @NotNull String message) throws VcsException {
if (paths.length == 0) return new SVNCommitInfo[]{SVNCommitInfo.NULL};
final List<String> parameters = new ArrayList<String>();
CommandUtil.put(parameters, depth);
CommandUtil.put(parameters, noUnlock, "--no-unlock");
CommandUtil.put(parameters, keepChangelist, "--keep-changelists");
CommandUtil.putChangeLists(parameters, changelists);
CommandUtil.put(parameters, SVNDepth.EMPTY);
CommandUtil.put(parameters, false, "--no-unlock");
CommandUtil.put(parameters, false, "--keep-changelists");
CommandUtil.putChangeLists(parameters, null);
if (revpropTable != null && ! revpropTable.isEmpty()) {
final Set<Map.Entry<Object, Object>> set = revpropTable.entrySet();
for (Map.Entry<Object, Object> entry : set) {
parameters.add("--with-revprop");
parameters.add(entry.getKey() + "=" + entry.getValue());
}
}
parameters.add("-m");
parameters.add(message);
// TODO: seems that sort is not necessary here
Arrays.sort(paths);
CommandUtil.put(parameters, paths);
myCommandListener.setBaseDirectory(CommandUtil.correctUpToExistingParent(paths[0]));
CommandUtil.execute(myVcs, SvnTarget.fromFile(paths[0]), SvnCommandName.ci, parameters, myCommandListener);
myCommandListener.throwExceptionIfOccurred();
IdeaCommitHandler handler = new IdeaCommitHandler(ProgressManager.getInstance().getProgressIndicator());
CmdCheckinClient.CommandListener listener = new CommandListener(handler);
listener.setBaseDirectory(CommandUtil.correctUpToExistingParent(paths[0]));
execute(myVcs, SvnTarget.fromFile(paths[0]), SvnCommandName.ci, parameters, listener);
listener.throwExceptionIfOccurred();
return validateRevisionNumber();
long revision = validateRevisionNumber(listener.getCommittedRevision());
return new SVNCommitInfo[]{new SVNCommitInfo(revision, null, null, null)};
}
private long validateRevisionNumber() throws VcsException {
long revision = myCommandListener.getCommittedRevision();
private static long validateRevisionNumber(long revision) throws VcsException {
if (revision < 0) {
throw new VcsException("Wrong committed revision number: " + revision);
}
@@ -96,6 +105,53 @@ public class SvnCommitRunner {
return revision;
}
private Collection<File> filterCommittables(@NotNull Collection<File> committables) throws SVNException {
final Set<String> childrenOfSomebody = ContainerUtil.newHashSet();
new AbstractFilterChildren<File>() {
@Override
protected void sortAscending(List<File> list) {
Collections.sort(list);
}
@Override
protected boolean isAncestor(File parent, File child) {
// strict here will ensure that for case insensitive file systems paths different only by case will not be treated as ancestors
// of each other which is what we need to perform renames from one case to another on Windows
final boolean isAncestor = FileUtil.isAncestor(parent, child, true);
if (isAncestor) {
childrenOfSomebody.add(child.getPath());
}
return isAncestor;
}
}.doFilter(ContainerUtil.newArrayList(committables));
if (!childrenOfSomebody.isEmpty()) {
List<File> result = ContainerUtil.newArrayList();
StatusClient statusClient = myFactory.createStatusClient();
for (File file : committables) {
if (!childrenOfSomebody.contains(file.getPath())) {
result.add(file);
}
else {
try {
final SVNStatus status = statusClient.doStatus(file, false);
if (status != null && !SVNStatusType.STATUS_NONE.equals(status.getContentsStatus()) &&
!SVNStatusType.STATUS_UNVERSIONED.equals(status.getContentsStatus())) {
result.add(file);
}
}
catch (SVNException e) {
// not versioned
LOG.info(e);
throw e;
}
}
}
return result;
}
return committables;
}
public static class CommandListener extends LineCommandAdapter {
// Status could contain spaces, like "Adding copy of <path>". But at the end we are not interested in "copy of" part and want to have
@@ -5,9 +5,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.api.BaseSvnClient;
import org.jetbrains.idea.svn.commandLine.CommandUtil;
import org.jetbrains.idea.svn.commandLine.CommitEventHandler;
import org.jetbrains.idea.svn.commandLine.SvnCommandName;
import org.jetbrains.idea.svn.commandLine.SvnCommitRunner;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.ISVNCommitHandler;
@@ -41,10 +39,10 @@ public class CmdImportClient extends BaseSvnClient implements ImportClient {
parameters.add("--message");
parameters.add(message);
SvnCommitRunner.CommandListener listener = new SvnCommitRunner.CommandListener(handler);
CmdCheckinClient.CommandListener listener = new CmdCheckinClient.CommandListener(handler);
listener.setBaseDirectory(path);
CommandUtil.execute(myVcs, SvnTarget.fromURL(url), SvnCommandName.importFolder, parameters, listener);
execute(myVcs, SvnTarget.fromURL(url), SvnCommandName.importFolder, parameters, listener);
return listener.getCommittedRevision();
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn.commandLine;
package org.jetbrains.idea.svn.checkin;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn.commandLine;
package org.jetbrains.idea.svn.checkin;
/**
* Created with IntelliJ IDEA.
@@ -28,8 +28,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnBundle;
import org.jetbrains.idea.svn.SvnUtil;
import org.jetbrains.idea.svn.commandLine.CommitEventHandler;
import org.jetbrains.idea.svn.commandLine.CommitEventType;
import org.tmatesoft.svn.core.SVNCancelException;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNEvent;
@@ -1,704 +0,0 @@
/*
* 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.checkin;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.util.PopupUtil;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.util.ThrowableRunnable;
import com.intellij.util.WaitForProgressToShow;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.net.HttpConfigurable;
import com.intellij.util.proxy.CommonProxy;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.*;
import org.jetbrains.idea.svn.commandLine.AuthenticationCallback;
import org.jetbrains.idea.svn.dialogs.SimpleCredentialsDialog;
import org.jetbrains.idea.svn.dialogs.SvnInteractiveAuthenticationProvider;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.auth.*;
import org.tmatesoft.svn.core.internal.util.SVNBase64;
import org.tmatesoft.svn.core.internal.util.SVNHashMap;
import org.tmatesoft.svn.core.internal.util.SVNSSLUtil;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.internal.wc.SVNWCProperties;
import org.tmatesoft.svn.core.wc.SVNRevision;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.*;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* Created with IntelliJ IDEA.
* User: Irina.Chernushina
* Date: 2/26/13
* Time: 1:27 PM
*/
public class IdeaSvnkitBasedAuthenticationCallback implements AuthenticationCallback {
private final SvnVcs myVcs;
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.checkin.IdeaSvnkitBasedAuthenticationCallback");
private File myTempDirectory;
private boolean myProxyCredentialsWereReturned;
private SvnConfiguration myConfiguration;
private final Set<String> myRequestedCredentials;
public IdeaSvnkitBasedAuthenticationCallback(SvnVcs vcs) {
myVcs = vcs;
myConfiguration = SvnConfiguration.getInstance(myVcs.getProject());
myRequestedCredentials = ContainerUtil.newHashSet();
}
@Override
public boolean authenticateFor(String realm, SVNURL repositoryUrl, boolean previousFailed, boolean passwordRequest) {
if (repositoryUrl == null) {
return false;
}
return new CredentialsAuthenticator(myVcs).tryAuthenticate(realm, repositoryUrl, previousFailed, passwordRequest);
}
@Nullable
@Override
public SVNAuthentication requestCredentials(final SVNURL repositoryUrl, final String type) {
SVNAuthentication authentication = null;
if (repositoryUrl != null) {
final String realm = repositoryUrl.toDecodedString();
authentication = requestCredentials(realm, type, new Getter<Pair<SVNAuthentication, Boolean>>() {
@Override
public Pair<SVNAuthentication, Boolean> get() {
SVNAuthentication result = myVcs.getSvnConfiguration().getInteractiveManager(myVcs).getInnerProvider()
.requestClientAuthentication(type, repositoryUrl, realm, null, null, true);
return new Pair<SVNAuthentication, Boolean>(result, true);
}
});
}
if (authentication == null) {
LOG.warn("Could not get authentication. Type - " + type + ", Url - " + repositoryUrl);
}
return authentication;
}
@Nullable
private <T> T requestCredentials(@NotNull String realm, @NotNull String type, @NotNull Getter<Pair<T, Boolean>> fromUserProvider) {
T result;
Object data = SvnConfiguration.RUNTIME_AUTH_CACHE.getData(type, realm);
// we return credentials from cache if they are asked for the first time during command execution, otherwise - user is asked
if (data != null && !myRequestedCredentials.contains(getKey(realm, type))) {
// we already have credentials in memory cache
result = (T) data;
myRequestedCredentials.add(getKey(realm, type));
}
else {
// ask user for credentials
Pair<T, Boolean> userData = fromUserProvider.get();
result = userData.first;
if (result != null && userData.second) {
// save user credentials to memory cache
myVcs.getSvnConfiguration().acknowledge(type, realm, result);
myRequestedCredentials.add(getKey(realm, type));
}
}
return result;
}
@NotNull
private static String getKey(@NotNull String realm, @NotNull String type) {
return type + "$" + realm;
}
@Override
@Nullable
public String requestSshCredentials(@NotNull final String realm,
@NotNull final SimpleCredentialsDialog.Mode mode,
@NotNull final String key) {
return requestCredentials(realm, ISVNAuthenticationManager.SSH, new Getter<Pair<String, Boolean>>() {
@Override
public Pair<String, Boolean> get() {
final Ref<String> answer = new Ref<String>();
Runnable command = new Runnable() {
public void run() {
SimpleCredentialsDialog dialog = new SimpleCredentialsDialog(myVcs.getProject());
dialog.setup(mode, realm, key, true);
dialog.setTitle(SvnBundle.message("dialog.title.authentication.required"));
dialog.setSaveEnabled(false);
dialog.show();
if (dialog.isOK()) {
answer.set(dialog.getPassword());
}
}
};
// Use ModalityState.any() as currently ssh credentials in terminal mode are requested in the thread that reads output and not in
// the thread that started progress
WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(command, ModalityState.any());
return new Pair<String, Boolean>(answer.get(), true);
}
});
}
@Override
public boolean acceptSSLServerCertificate(final SVNURL repositoryUrl, final String realm) {
if (repositoryUrl == null) {
return false;
}
return new SSLServerCertificateAuthenticator(myVcs).tryAuthenticate(repositoryUrl, realm);
}
@Override
public void clearPassiveCredentials(String realm, SVNURL repositoryUrl, boolean password) {
if (repositoryUrl == null) {
return;
}
final SvnConfiguration configuration = SvnConfiguration.getInstance(myVcs.getProject());
final List<String> kinds = getKinds(repositoryUrl, password);
for (String kind : kinds) {
configuration.clearCredentials(kind, realm);
}
}
@Override
public boolean haveDataForTmpConfig() {
final HttpConfigurable instance = HttpConfigurable.getInstance();
return SvnConfiguration.getInstance(myVcs.getProject()).isIsUseDefaultProxy() &&
(instance.USE_HTTP_PROXY || instance.USE_PROXY_PAC);
}
// TODO: not used - should be removed.
@Override
public boolean persistDataToTmpConfig(final SVNURL repositoryUrl) throws IOException {
// TODO: Make repositoryUrl @NotNull after SvnLineCommand.runWithAuthenticationAttempt refactored
if (repositoryUrl == null) {
return false;
}
final SvnConfiguration configuration = SvnConfiguration.getInstance(myVcs.getProject());
initTmpDir(configuration);
final Proxy proxy = getIdeaDefinedProxy(repositoryUrl);
if (proxy != null){
IdeaSVNConfigFile.putProxyIntoServersFile(myTempDirectory, repositoryUrl.getHost(), proxy);
}
return true;
}
@Nullable
public static Proxy getIdeaDefinedProxy(@NotNull final SVNURL url) {
// SVNKit authentication implementation sets repositories as noProxy() to provide custom proxy authentication logic - see for instance,
// SvnAuthenticationManager.getProxyManager(). But noProxy() setting is not cleared correctly in all cases - so if svn command
// (for command line) is executed on thread where repository url was added as noProxy() => proxies are not retrieved for such commands
// and execution logic is incorrect.
// To prevent such behavior repositoryUrl is manually removed from noProxy() list (for current thread).
// NOTE, that current method is only called from code flows for executing commands through command line client and should not be called
// from SVNKit code flows.
CommonProxy.getInstance().removeNoProxy(url.getProtocol(), url.getHost(), url.getPort());
final List<Proxy> proxies = CommonProxy.getInstance().select(URI.create(url.toString()));
if (proxies != null && ! proxies.isEmpty()) {
for (Proxy proxy : proxies) {
if (HttpConfigurable.isRealProxy(proxy) && Proxy.Type.HTTP.equals(proxy.type())) {
return proxy;
}
}
}
return null;
}
@Override
@Nullable
public PasswordAuthentication getProxyAuthentication(@NotNull SVNURL repositoryUrl) {
final Proxy proxy = getIdeaDefinedProxy(repositoryUrl);
if (proxy == null) return null;
if (myProxyCredentialsWereReturned){
// ask loud
final HttpConfigurable instance = HttpConfigurable.getInstance();
if (instance.USE_HTTP_PROXY || instance.USE_PROXY_PAC) {
PopupUtil.showBalloonForActiveComponent("Failed to authenticate to proxy. You can change proxy credentials in HTTP proxy settings.", MessageType.ERROR);
} else {
PopupUtil.showBalloonForActiveComponent("Failed to authenticate to proxy.", MessageType.ERROR);
}
return null;
}
final InetSocketAddress address = (InetSocketAddress)proxy.address();
final PasswordAuthentication authentication;
try {
authentication = Authenticator
.requestPasswordAuthentication(repositoryUrl.getHost(), address.getAddress(), repositoryUrl.getPort(), repositoryUrl.getProtocol(),
repositoryUrl.getHost(), repositoryUrl.getProtocol(), new URL(repositoryUrl.toString()),
Authenticator.RequestorType.PROXY);
} catch (MalformedURLException e) {
LOG.info(e);
return null;
}
if (authentication != null) {
myProxyCredentialsWereReturned = true;
}
return authentication;
}
// TODO: not used - should be removed.
@Override
public boolean askProxyCredentials(SVNURL repositoryUrl) {
if (repositoryUrl == null) {
return false;
}
PasswordAuthentication authentication = getProxyAuthentication(repositoryUrl);
if (authentication != null) {
// for 'generic' proxy variant (suppose user defined proxy in Subversion config but no password)
try {
initTmpDir(SvnConfiguration.getInstance(myVcs.getProject()));
}
catch (IOException e) {
PopupUtil.showBalloonForActiveComponent("Failed to authenticate to proxy: " + e.getMessage(), MessageType.ERROR);
return false;
}
return IdeaSVNConfigFile.putProxyCredentialsIntoServerFile(myTempDirectory, repositoryUrl.getHost(), authentication);
}
return false;
}
public void reset() {
if (myTempDirectory != null) {
FileUtil.delete(myTempDirectory);
}
}
private abstract class AbstractAuthenticator<T> {
protected final SvnVcs myVcs;
protected boolean myStoreInUsual;
protected SvnAuthenticationManager myTmpDirManager;
protected AbstractAuthenticator(SvnVcs vcs) {
myVcs = vcs;
}
protected boolean tryAuthenticate() {
final SvnConfiguration configuration = SvnConfiguration.getInstance(myVcs.getProject());
final SvnAuthenticationManager passive = configuration.getPassiveAuthenticationManager(myVcs.getProject());
final SvnAuthenticationManager manager = configuration.getAuthenticationManager(myVcs);
try {
T svnAuthentication = getWithPassive(passive);
if (svnAuthentication == null) {
svnAuthentication = getWithActive(manager);
}
if (svnAuthentication == null) return false;
if (myStoreInUsual) {
manager.setArtificialSaving(true);
return acknowledge(manager, svnAuthentication);
} else {
if (myTmpDirManager == null) {
initTmpDir(configuration);
myTmpDirManager = createTmpManager();
}
myTmpDirManager.setArtificialSaving(true);
return acknowledge(myTmpDirManager, svnAuthentication);
}
}
catch (IOException e) {
LOG.info(e);
VcsBalloonProblemNotifier.showOverChangesView(myVcs.getProject(), e.getMessage(), MessageType.ERROR);
return false;
}
catch (SVNException e) {
LOG.info(e);
VcsBalloonProblemNotifier.showOverChangesView(myVcs.getProject(), e.getMessage(), MessageType.ERROR);
return false;
}
}
protected SvnAuthenticationManager createTmpManager() {
final SvnAuthenticationManager interactive = new SvnAuthenticationManager(myVcs.getProject(), myTempDirectory);
interactive.setRuntimeStorage(SvnConfiguration.RUNTIME_AUTH_CACHE);
interactive.setAuthenticationProvider(new SvnInteractiveAuthenticationProvider(myVcs, interactive));
return interactive;
}
protected abstract T getWithPassive(SvnAuthenticationManager passive) throws SVNException;
protected abstract T getWithActive(SvnAuthenticationManager active) throws SVNException;
protected abstract boolean acknowledge(SvnAuthenticationManager manager, T svnAuthentication) throws SVNException;
}
private void initTmpDir(SvnConfiguration configuration) throws IOException {
if (myTempDirectory == null) {
myTempDirectory = FileUtil.createTempDirectory("tmp", "Subversion");
FileUtil.copyDir(new File(configuration.getConfigurationDirectory()), myTempDirectory);
}
}
private void doWithSubscribeToAuthProvider(SvnAuthenticationManager.ISVNAuthenticationProviderListener listener,
final ThrowableRunnable<SVNException> runnable) throws SVNException {
MessageBusConnection connection = null;
try {
final Project project = myVcs.getProject();
connection = project.getMessageBus().connect(project);
connection.subscribe(SvnAuthenticationManager.AUTHENTICATION_PROVIDER_LISTENER, listener);
runnable.run();
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
// plus seems that we also should ask for credentials; but we didn't receive realm name yet
private class SSLServerCertificateAuthenticator extends AbstractAuthenticator<Boolean> {
private SVNURL myUrl;
private String myRealm;
private String myCertificateRealm;
private String myCredentialsRealm;
private Object myCertificate;
private int myResult;
private SVNAuthentication myAuthentication;
protected SSLServerCertificateAuthenticator(SvnVcs vcs) {
super(vcs);
}
public boolean tryAuthenticate(final SVNURL url, final String realm) {
myUrl = url;
myRealm = realm;
myResult = ISVNAuthenticationProvider.ACCEPTED_TEMPORARY;
myStoreInUsual = false;
return tryAuthenticate();
}
@Override
protected Boolean getWithPassive(SvnAuthenticationManager passive) throws SVNException {
String stored = (String) passive.getRuntimeAuthStorage().getData("svn.ssl.server", myRealm);
if (stored == null) return null;
CertificateFactory cf;
try {
cf = CertificateFactory.getInstance("X509");
final byte[] buffer = new byte[stored.length()];
SVNBase64.base64ToByteArray(new StringBuffer(stored), buffer);
myCertificate = cf.generateCertificate(new ByteArrayInputStream(buffer));
}
catch (CertificateException e) {
throw new SVNException(SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, e));
}
myCertificateRealm = myRealm;
return myCertificate != null ? true : null;
}
@Override
protected Boolean getWithActive(final SvnAuthenticationManager active) throws SVNException {
doWithSubscribeToAuthProvider(new SvnAuthenticationManager.ISVNAuthenticationProviderListener() {
@Override
public void requestClientAuthentication(String kind,
SVNURL url,
String realm,
SVNErrorMessage errorMessage,
SVNAuthentication previousAuth,
boolean authMayBeStored,
SVNAuthentication authentication) {
if (!myUrl.equals(url)) return;
myCredentialsRealm = realm;
myAuthentication = authentication;
if (myAuthentication != null) {
myStoreInUsual &= myAuthentication.isStorageAllowed();
}
}
@Override
public void acceptServerAuthentication(SVNURL url,
String realm,
Object certificate,
boolean resultMayBeStored,
int accepted) {
if (!myUrl.equals(url)) return;
myCertificateRealm = realm;
myCertificate = certificate;
myResult = accepted;
}
}, new ThrowableRunnable<SVNException>() {
@Override
public void run() throws SVNException {
// NOTE: DO NOT replace this call - SSL authentication highly tied to SVNKit
myVcs.createWCClient(active).doInfo(myUrl, SVNRevision.UNDEFINED, SVNRevision.HEAD);
//myVcs.getInfo(myUrl, SVNRevision.HEAD, active);
}
}
);
myStoreInUsual &= myCertificate != null && ISVNAuthenticationProvider.ACCEPTED == myResult;
return ISVNAuthenticationProvider.REJECTED != myResult && myCertificate != null;
}
@Override
protected boolean acknowledge(SvnAuthenticationManager manager, Boolean svnAuthentication) throws SVNException {
// we should store certificate, if it wasn't accepted (if temporally tmp)
if (myCertificate == null) { // this is if certificate was stored only in passive area
String stored = (String) manager.getRuntimeAuthStorage().getData("svn.ssl.server", myRealm);
if (StringUtil.isEmptyOrSpaces(stored)) {
throw new SVNException(SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, "No stored server certificate was found in runtime"));
}
CertificateFactory cf;
try {
cf = CertificateFactory.getInstance("X509");
final byte[] buffer = new byte[stored.length()];
SVNBase64.base64ToByteArray(new StringBuffer(stored), buffer);
myCertificate = cf.generateCertificate(new ByteArrayInputStream(buffer));
}
catch (CertificateException e) {
throw new SVNException(SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, e));
}
myCertificateRealm = myRealm;
}
if (myTempDirectory != null && myCertificate != null) {
if (! (myCertificate instanceof X509Certificate)) {
throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Can not store server certificate: " + myCertificate));
}
X509Certificate x509Certificate = (X509Certificate) myCertificate;
String stored;
try {
stored = SVNBase64.byteArrayToBase64(x509Certificate.getEncoded());
}
catch (CertificateEncodingException e) {
throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e));
}
int failures = SVNSSLUtil.getServerCertificateFailures(x509Certificate, myUrl.getHost());
storeServerCertificate(myTempDirectory, myCertificateRealm, stored, failures);
if (myAuthentication != null) {
final String realm = myCredentialsRealm == null ? myCertificateRealm : myCredentialsRealm;
return storeCredentials(manager, myAuthentication, realm);
}
}
return true;
}
private void storeServerCertificate(final File configDir, String realm, String data, int failures) throws SVNException {
//noinspection ResultOfMethodCallIgnored
configDir.mkdirs();
File file = new File(configDir, "auth/svn.ssl.server/" + SVNFileUtil.computeChecksum(realm));
SVNHashMap map = new SVNHashMap();
map.put("ascii_cert", data);
map.put("svn:realmstring", realm);
map.put("failures", Integer.toString(failures));
SVNFileUtil.deleteFile(file);
File tmpFile = SVNFileUtil.createUniqueFile(configDir, "auth", ".tmp", true);
try {
SVNWCProperties.setProperties(SVNProperties.wrap(map), file, tmpFile, SVNWCProperties.SVN_HASH_TERMINATOR);
} finally {
SVNFileUtil.deleteFile(tmpFile);
}
}
}
private boolean storeCredentials(SvnAuthenticationManager manager, final SVNAuthentication authentication, final String realm) throws SVNException {
try {
final String kind = getFromType(authentication);
if (! acknowledgeSSL(manager, authentication)) {
manager.acknowledgeAuthentication(true, kind, realm, null, authentication, authentication.getURL());
}
} catch (SvnAuthenticationManager.CredentialsSavedException e) {
return e.isSuccess();
}
return true;
}
private class CredentialsAuthenticator extends AbstractAuthenticator<SVNAuthentication> {
private String myKind;
private String myRealm;
// sometimes realm string is different (with <>), so store credentials for both strings..
private String myRealm2;
private SVNURL myUrl;
private SVNAuthentication myAuthentication;
protected CredentialsAuthenticator(SvnVcs vcs) {
super(vcs);
}
public boolean tryAuthenticate(String realm, SVNURL url, boolean previousFailed, boolean passwordRequest) {
realm = realm == null ? url.getHost() : realm;
myRealm = realm;
myUrl = url;
final List<String> kinds = getKinds(url, passwordRequest);
for (String kind : kinds) {
myKind = kind;
if (! tryAuthenticate()) {
return false;
}
}
return true;
}
@Override
protected SVNAuthentication getWithPassive(SvnAuthenticationManager passive) throws SVNException {
final SVNAuthentication impl = getWithPassiveImpl(passive);
if (impl != null && ! checkAuthOk(impl)) {
clearPassiveCredentials(myRealm, myUrl, impl instanceof SVNPasswordAuthentication); //clear passive also take into acconut ssl filepath
return null;
}
return impl;
}
private SVNAuthentication getWithPassiveImpl(SvnAuthenticationManager passive) throws SVNException {
try {
return passive.getFirstAuthentication(myKind, myRealm, myUrl);
} catch (SVNCancelException e) {
return null;
}
}
private boolean checkAuthOk(SVNAuthentication authentication) {
if (authentication instanceof SVNPasswordAuthentication && StringUtil.isEmptyOrSpaces(authentication.getUserName())) return false;
if (authentication instanceof SVNSSLAuthentication) {
if (StringUtil.isEmptyOrSpaces(((SVNSSLAuthentication)authentication).getPassword())) return false;
}
return true;
}
@Override
protected SVNAuthentication getWithActive(final SvnAuthenticationManager active) throws SVNException {
if (ISVNAuthenticationManager.SSL.equals(myKind)) {
doWithSubscribeToAuthProvider(new SvnAuthenticationManager.ISVNAuthenticationProviderListener() {
@Override
public void requestClientAuthentication(String kind,
SVNURL url,
String realm,
SVNErrorMessage errorMessage,
SVNAuthentication previousAuth,
boolean authMayBeStored,
SVNAuthentication authentication) {
if (!myUrl.equals(url)) return;
myAuthentication = authentication;
myRealm2 = realm;
myStoreInUsual = myAuthentication != null && myAuthentication.isStorageAllowed();
}
@Override
public void acceptServerAuthentication(SVNURL url,
String realm,
Object certificate,
boolean resultMayBeStored,
int accepted) {
}
}, new ThrowableRunnable<SVNException>() {
@Override
public void run() throws SVNException {
myVcs.createWCClient(active).doInfo(myUrl, SVNRevision.UNDEFINED, SVNRevision.HEAD);
}
}
);
if (myAuthentication != null) return myAuthentication;
}
myAuthentication = active.getProvider().requestClientAuthentication(myKind, myUrl, myRealm, null, null, true);
myStoreInUsual = myTempDirectory == null && myAuthentication != null && myAuthentication.isStorageAllowed();
return myAuthentication;
}
@Override
protected boolean acknowledge(SvnAuthenticationManager manager, SVNAuthentication svnAuthentication) throws SVNException {
if (! StringUtil.isEmptyOrSpaces(myRealm2) && ! myRealm2.equals(myRealm)) {
storeCredentials(manager, svnAuthentication, myRealm2);
}
return storeCredentials(manager, svnAuthentication, myRealm);
}
}
private boolean acknowledgeSSL(SvnAuthenticationManager manager, SVNAuthentication svnAuthentication) throws SVNException {
if (svnAuthentication instanceof SVNSSLAuthentication && (((SVNSSLAuthentication) svnAuthentication).getCertificateFile() != null)) {
manager.acknowledgeForSSL(true, getFromType(svnAuthentication),
((SVNSSLAuthentication) svnAuthentication).getCertificateFile().getPath(),
null, svnAuthentication);
manager.acknowledgeAuthentication(true, getFromType(svnAuthentication),
((SVNSSLAuthentication) svnAuthentication).getCertificateFile().getPath(),
null, svnAuthentication, svnAuthentication.getURL());
return true;
}
return false;
}
private static List<String> getKinds(final SVNURL url, boolean passwordRequest) {
if (passwordRequest || "http".equals(url.getProtocol())) {
return Collections.singletonList(ISVNAuthenticationManager.PASSWORD);
} else if ("https".equals(url.getProtocol())) {
return Collections.singletonList(ISVNAuthenticationManager.SSL);
} else if ("svn".equals(url.getProtocol())) {
return Collections.singletonList(ISVNAuthenticationManager.PASSWORD);
} else if (url.getProtocol().contains("svn+")) { // todo +-
return Arrays.asList(ISVNAuthenticationManager.SSH, ISVNAuthenticationManager.USERNAME);
} else if ("file".equals(url.getProtocol())) {
return Collections.singletonList(ISVNAuthenticationManager.USERNAME);
}
return Collections.singletonList(ISVNAuthenticationManager.USERNAME);
}
@Nullable
@Override
public File getSpecialConfigDir() {
return myTempDirectory != null ? myTempDirectory : new File(myConfiguration.getConfigurationDirectory());
}
private String getFromType(SVNAuthentication authentication) {
if (authentication instanceof SVNPasswordAuthentication) {
return ISVNAuthenticationManager.PASSWORD;
}
if (authentication instanceof SVNSSHAuthentication) {
return ISVNAuthenticationManager.SSH;
}
if (authentication instanceof SVNSSLAuthentication) {
return ISVNAuthenticationManager.SSL;
}
if (authentication instanceof SVNUserNameAuthentication) {
return ISVNAuthenticationManager.USERNAME;
}
throw new IllegalArgumentException();
}
}
@@ -4,7 +4,6 @@ import com.intellij.openapi.vcs.VcsException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.api.SvnClient;
import org.jetbrains.idea.svn.commandLine.CommitEventHandler;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.ISVNCommitHandler;
@@ -24,8 +24,6 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.AbstractFilterChildren;
import com.intellij.openapi.vcs.CheckinProjectPanel;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsException;
@@ -38,20 +36,23 @@ import com.intellij.openapi.vcs.ui.RefreshableOnComponent;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.*;
import com.intellij.util.FunctionUtil;
import com.intellij.util.NullableFunction;
import com.intellij.util.PairConsumer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.Convertor;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.*;
import org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient;
import org.jetbrains.idea.svn.commandLine.SvnCommitRunner;
import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.*;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNEvent;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNStatusType;
import javax.swing.*;
import java.awt.*;
@@ -83,103 +84,54 @@ public class SvnCheckinEnvironment implements CheckinEnvironment {
return null;
}
private List<VcsException> commitInt(List<File> paths, final String comment, final boolean force, final Set<String> feedback) {
private List<VcsException> commitInt(List<File> paths, final String comment, final Set<String> feedback) {
final List<VcsException> exception = new ArrayList<VcsException>();
final List<File> committables = getCommitables(paths);
final SVNCommitClient committer = mySvnVcs.createCommitClient();
final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
IdeaCommitHandler handler = new IdeaCommitHandler(progress, true, true);
if (progress != null) {
committer.setEventHandler(handler);
}
if (progress != null) {
doCommit(committables, committer, comment, force, exception, feedback);
doCommit(committables, comment, exception, feedback);
}
else if (ApplicationManager.getApplication().isDispatchThread()) {
ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
public void run() {
doCommit(committables, committer, comment, force, exception, feedback);
doCommit(committables, comment, exception, feedback);
}
}, SvnBundle.message("progress.title.commit"), false, mySvnVcs.getProject());
}
else {
doCommit(committables, committer, comment, force, exception, feedback);
doCommit(committables, comment, exception, feedback);
}
// TODO: Check if such processing of deleted files also necessary for command line. And if yes - use one handler instance for both
// TODO: SVNKit and command line code flows.
for(VirtualFile f : handler.getDeletedFiles()) {
f.putUserData(VirtualFile.REQUESTOR_MARKER, this);
}
return exception;
}
private void doCommit(List<File> committables,
SVNCommitClient committer,
String comment,
boolean force,
List<VcsException> exception, final Set<String> feedback) {
private void doCommit(List<File> committables, String comment, List<VcsException> exception, final Set<String> feedback) {
//noinspection unchecked
final MultiMap<Pair<SVNURL,WorkingCopyFormat>,File> map = SvnUtil.splitIntoRepositoriesMap(mySvnVcs, committables, Convertor.SELF);
for (Map.Entry<Pair<SVNURL, WorkingCopyFormat>, Collection<File>> entry : map.entrySet()) {
doCommitOneRepo(entry.getValue(), committer, comment, force, exception, feedback, entry.getKey().getSecond());
try {
doCommitOneRepo(entry.getValue(), comment, exception, feedback, entry.getKey().getSecond());
}
catch (VcsException e) {
LOG.info(e);
exception.add(e);
}
}
}
private void doCommitOneRepo(Collection<File> committables,
SVNCommitClient committer,
String comment,
boolean force,
List<VcsException> exception, final Set<String> feedback, final WorkingCopyFormat format) {
List<VcsException> exception,
final Set<String> feedback,
final WorkingCopyFormat format)
throws VcsException {
if (committables.isEmpty()) {
return;
}
// TODO: Create CommitClient and refactor to use common ClientFactory model.
if (WorkingCopyFormat.ONE_DOT_EIGHT.equals(format) ||
!WorkingCopyFormat.ONE_DOT_SIX.equals(format) && mySvnVcs.getSvnConfiguration().isCommandLine()) {
doWithCommandLine(committables, comment, exception, feedback);
}
else {
doWithSvnkit(committables, committer, comment, force, exception, feedback);
}
}
private void doWithSvnkit(Collection<File> committables,
SVNCommitClient committer,
String comment,
boolean force,
List<VcsException> exception, Set<String> feedback) {
File[] pathsToCommit = committables.toArray(new File[committables.size()]);
boolean keepLocks = SvnConfiguration.getInstance(mySvnVcs.getProject()).isKeepLocks();
SVNCommitPacket[] commitPackets = null;
SVNCommitInfo[] results;
try {
commitPackets = committer.doCollectCommitItems(pathsToCommit, keepLocks, force, SVNDepth.EMPTY, true, null);
results = committer.doCommit(commitPackets, keepLocks, comment);
commitPackets = null;
}
catch (SVNException e) {
// exception on collecting commitables.
exception.add(new VcsException(e));
LOG.info(e);
return;
}
finally {
if (commitPackets != null) {
for (SVNCommitPacket commitPacket : commitPackets) {
try {
commitPacket.dispose();
}
catch (SVNException e) {
LOG.info(e);
}
}
}
}
SVNCommitInfo[] results = mySvnVcs.getFactory(format).createCheckinClient().commit(committables, comment);
final StringBuilder committedRevisions = new StringBuilder();
for (SVNCommitInfo result : results) {
if (result.getErrorMessage() != null) {
@@ -197,77 +149,6 @@ public class SvnCheckinEnvironment implements CheckinEnvironment {
}
}
private void doWithCommandLine(Collection<File> committables, String comment, List<VcsException> exception, Set<String> feedback) {
// if directory renames were used, IDEA reports all files under them as moved, but for svn we can not pass some of them
// to commit command - since not all paths are registered as changes -> so we need to filter these cases, but only if
// there at least some child-parent relationships in passed paths
try {
committables = filterCommittables(committables);
} catch (SVNException e) {
exception.add(new VcsException(e));
return;
}
IdeaCommitHandler handler = new IdeaCommitHandler(ProgressManager.getInstance().getProgressIndicator());
try {
SvnCommitRunner runner = new SvnCommitRunner(mySvnVcs, handler);
final long revision =
runner.commit(ArrayUtil.toObjectArray(committables, File.class), comment, SVNDepth.EMPTY, false, false, null, null);
reportCommittedRevisions(feedback, String.valueOf(revision));
}
catch (VcsException e) {
exception.add(e);
}
}
private Collection<File> filterCommittables(Collection<File> committables) throws SVNException {
final Set<String> childrenOfSomebody = ContainerUtil.newHashSet();
new AbstractFilterChildren<File>() {
@Override
protected void sortAscending(List<File> list) {
Collections.sort(list);
}
@Override
protected boolean isAncestor(File parent, File child) {
// strict here will ensure that for case insensitive file systems paths different only by case will not be treated as ancestors
// of each other which is what we need to perform renames from one case to another on Windows
final boolean isAncestor = FileUtil.isAncestor(parent, child, true);
if (isAncestor) {
childrenOfSomebody.add(child.getPath());
}
return isAncestor;
}
}.doFilter(new ArrayList<File>(committables));
if (! childrenOfSomebody.isEmpty()) {
List<File> result = ContainerUtil.newArrayList();
SvnCommandLineStatusClient statusClient = new SvnCommandLineStatusClient(mySvnVcs);
for (File file : committables) {
if (!childrenOfSomebody.contains(file.getPath())) {
result.add(file);
} else {
try {
final SVNStatus status = statusClient.doStatus(file, false);
if (status != null && ! SVNStatusType.STATUS_NONE.equals(status.getContentsStatus()) &&
! SVNStatusType.STATUS_UNVERSIONED.equals(status.getContentsStatus())) {
result.add(file);
}
}
catch (SVNException e) {
// not versioned
LOG.info(e);
throw e;
}
}
}
return result;
}
return committables;
}
private void reportCommittedRevisions(Set<String> feedback, String committedRevisions) {
final Project project = mySvnVcs.getProject();
final String message = SvnBundle.message("status.text.comitted.revision", committedRevisions);
@@ -372,7 +253,7 @@ public class SvnCheckinEnvironment implements CheckinEnvironment {
String preparedComment,
@NotNull NullableFunction<Object, Object> parametersHolder,
Set<String> feedback) {
return commitInt(collectPaths(changes), preparedComment, true, feedback);
return commitInt(collectPaths(changes), preparedComment, feedback);
}
public List<VcsException> commit(List<Change> changes, String preparedComment) {
@@ -0,0 +1,81 @@
/*
* Copyright 2000-2014 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.checkin;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.api.BaseSvnClient;
import org.jetbrains.idea.svn.commandLine.SvnBindException;
import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.SVNCommitClient;
import org.tmatesoft.svn.core.wc.SVNCommitPacket;
import java.io.File;
import java.util.Collection;
/**
* @author Konstantin Kolosovsky.
*/
public class SvnKitCheckinClient extends BaseSvnClient implements CheckinClient {
private static final Logger LOG = Logger.getInstance(SvnKitCheckinClient.class);
@NotNull
@Override
public SVNCommitInfo[] commit(@NotNull Collection<File> paths, @NotNull String comment) throws VcsException {
File[] pathsToCommit = ArrayUtil.toObjectArray(paths, File.class);
boolean keepLocks = myVcs.getSvnConfiguration().isKeepLocks();
SVNCommitPacket[] commitPackets = null;
SVNCommitInfo[] results;
SVNCommitClient committer = myVcs.createCommitClient();
IdeaCommitHandler handler = new IdeaCommitHandler(ProgressManager.getInstance().getProgressIndicator(), true, true);
committer.setEventHandler(handler);
try {
commitPackets = committer.doCollectCommitItems(pathsToCommit, keepLocks, true, SVNDepth.EMPTY, true, null);
results = committer.doCommit(commitPackets, keepLocks, comment);
commitPackets = null;
}
catch (SVNException e) {
throw new SvnBindException(e);
}
finally {
if (commitPackets != null) {
for (SVNCommitPacket commitPacket : commitPackets) {
try {
commitPacket.dispose();
}
catch (SVNException e) {
LOG.info(e);
}
}
}
}
// This seems to be necessary only for SVNKit as changes after command line operations should be detected during VFS refresh.
for (VirtualFile f : handler.getDeletedFiles()) {
f.putUserData(VirtualFile.REQUESTOR_MARKER, this);
}
return results;
}
}
@@ -4,7 +4,6 @@ import com.intellij.openapi.vcs.VcsException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.api.BaseSvnClient;
import org.jetbrains.idea.svn.commandLine.CommitEventHandler;
import org.jetbrains.idea.svn.commandLine.SvnBindException;
import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNDepth;
@@ -61,7 +61,7 @@ public class CmdCheckoutClient extends BaseSvnClient implements CheckoutClient {
@NotNull List<String> parameters) throws VcsException {
BaseUpdateCommandListener listener = new BaseUpdateCommandListener(destination, handler);
CommandUtil.execute(myVcs, source, SvnCommandName.checkout, parameters, listener);
execute(myVcs, source, SvnCommandName.checkout, parameters, listener);
listener.throwWrappedIfException();
}
@@ -46,7 +46,7 @@ public class CmdExportClient extends BaseSvnClient implements ExportClient {
BaseUpdateCommandListener listener = new BaseUpdateCommandListener(to, handler);
CommandUtil.execute(myVcs, from, to, SvnCommandName.export, parameters, listener);
execute(myVcs, from, to, SvnCommandName.export, parameters, listener);
listener.throwWrappedIfException();
}
@@ -45,7 +45,7 @@ import org.jetbrains.idea.svn.actions.ExclusiveBackgroundVcsAction;
import org.jetbrains.idea.svn.actions.SvnExcludingIgnoredOperation;
import org.jetbrains.idea.svn.api.ClientFactory;
import org.jetbrains.idea.svn.checkin.IdeaCommitHandler;
import org.jetbrains.idea.svn.commandLine.CommitEventHandler;
import org.jetbrains.idea.svn.checkin.CommitEventHandler;
import org.jetbrains.idea.svn.dialogs.CheckoutDialog;
import org.jetbrains.idea.svn.dialogs.UpgradeFormatDialog;
import org.tmatesoft.svn.core.SVNCancelException;
@@ -24,6 +24,6 @@ public class CmdCleanupClient extends BaseSvnClient implements CleanupClient {
List<String> parameters = new ArrayList<String>();
CommandUtil.put(parameters, path);
CommandUtil.execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.cleanup, parameters, null);
execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.cleanup, parameters, null);
}
}
@@ -22,7 +22,6 @@ import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.SVNAuthentication;
import java.io.File;
import java.io.IOException;
import java.net.PasswordAuthentication;
/**
@@ -46,9 +45,9 @@ public interface AuthenticationCallback {
/**
* Authenticate for realm and base file belonging to corresponding working copy
*
* @param realm - realm that should be used for credential retrieval/storage.
* @param realm - realm that should be used for credential retrieval/storage.
* @param repositoryUrl
* @param previousFailed - whether previous credentials were correct
* @param previousFailed - whether previous credentials were correct
* @param passwordRequest - if true, password should be asked. Otherwise that may be a certificate (determined by the protocol)
* @return false if authentication canceled or was unsuccessful
*/
@@ -59,7 +58,7 @@ public interface AuthenticationCallback {
* For instance, username/password for http/svn protocols. SSL client certificate for two way SSL protocol.
*
* @param repositoryUrl
* @param type authentication protocol type with svn specific values, like "svn.simple" for http.
* @param type authentication protocol type with svn specific values, like "svn.simple" for http.
* @return
*/
@Nullable
@@ -80,7 +79,7 @@ public interface AuthenticationCallback {
* Ask user or read from memory storage whether server certificate should be accepted
*
* @param repositoryUrl
* @param realm - realm that should be used for credential retrieval/storage.
* @param realm - realm that should be used for credential retrieval/storage.
* @return true is certificate was accepted
*/
boolean acceptSSLServerCertificate(SVNURL repositoryUrl, final String realm);
@@ -88,9 +87,9 @@ public interface AuthenticationCallback {
/**
* Clear credentials stored anywhere - in case they were not full, wrong or anything else
*
* @param realm - required that credential
* @param realm - required that credential
* @param repositoryUrl
* @param password - whether password credential should be deleted or certificate, if protocol might demand certificate
* @param password - whether password credential should be deleted or certificate, if protocol might demand certificate
*/
void clearPassiveCredentials(String realm, SVNURL repositoryUrl, boolean password);
@@ -101,27 +100,8 @@ public interface AuthenticationCallback {
*/
boolean haveDataForTmpConfig();
/**
* writes IDEA config settings (that should be written) into tmp config directory
* (now it's IDEA proxy settings)
*
* @param repositoryUrl
* @return true if have written data, false if wasn't able to determine parameters etc
* @throws IOException
*/
boolean persistDataToTmpConfig(SVNURL repositoryUrl) throws IOException;
@Nullable
PasswordAuthentication getProxyAuthentication(@NotNull SVNURL repositoryUrl);
/**
* Ask for IDEA-defined proxy credentials, using standard authenticator
* Store data into tmp config
*
* @param repositoryUrl
* @return false if authentication was canceled or related calculations were unsuccessful
*/
boolean askProxyCredentials(SVNURL repositoryUrl);
void reset();
}
@@ -24,6 +24,7 @@ import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.*;
import org.jetbrains.idea.svn.auth.SvnAuthenticationManager;
import org.tmatesoft.svn.core.SVNURL;
import java.io.File;
@@ -2,11 +2,8 @@ package org.jetbrains.idea.svn.commandLine;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.checkin.IdeaSvnkitBasedAuthenticationCallback;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.wc.SVNDiffOptions;
import org.tmatesoft.svn.core.wc.SVNRevision;
@@ -170,42 +167,6 @@ public class CommandUtil {
return (T) unmarshaller.unmarshal(new StringReader(data.trim()));
}
/**
* Utility method for running commands.
* // TODO: Should be replaced with non-static analogue.
*
* @param vcs
* @param target
* @param name
* @param parameters
* @param listener
* @throws VcsException
*/
public static CommandExecutor execute(@NotNull SvnVcs vcs,
@NotNull SvnTarget target,
@NotNull SvnCommandName name,
@NotNull List<String> parameters,
@Nullable LineCommandListener listener) throws VcsException {
return execute(vcs, target, null, name, parameters, listener);
}
public static CommandExecutor execute(@NotNull SvnVcs vcs,
@NotNull SvnTarget target,
@Nullable File workingDirectory,
@NotNull SvnCommandName name,
@NotNull List<String> parameters,
@Nullable LineCommandListener listener) throws VcsException {
Command command = new Command(name);
command.setTarget(target);
command.setWorkingDirectory(workingDirectory);
command.setResultBuilder(listener);
command.put(parameters);
CommandRuntime runtime = new CommandRuntime(vcs, new IdeaSvnkitBasedAuthenticationCallback(vcs));
return runtime.runWithAuthenticationAttempt(command);
}
@NotNull
public static File getHomeDirectory() {
return new File(PathManager.getHomePath());
@@ -19,8 +19,8 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.IdeaSVNConfigFile;
import org.jetbrains.idea.svn.SvnAuthenticationManager;
import org.jetbrains.idea.svn.checkin.IdeaSvnkitBasedAuthenticationCallback;
import org.jetbrains.idea.svn.auth.SvnAuthenticationManager;
import org.jetbrains.idea.svn.auth.IdeaSvnkitBasedAuthenticationCallback;
import org.tmatesoft.svn.core.SVNURL;
import java.net.InetSocketAddress;
@@ -18,17 +18,16 @@ package org.jetbrains.idea.svn.commandLine;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.util.LineSeparator;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.hash.HashMap;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnUtil;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created with IntelliJ IDEA.
@@ -43,13 +42,15 @@ public class SvnBindException extends VcsException {
public static final int ERROR_BASE = 120000;
public static final int CATEGORY_SIZE = 5000;
private Map<Integer, String> errors = new HashMap<Integer, String>();
@NotNull private final MultiMap<Integer, String> errors = MultiMap.create();
@NotNull private final MultiMap<Integer, String> warnings = MultiMap.create();
public SvnBindException(String message) {
super(message);
if (!StringUtil.isEmpty(message)) {
parseErrors(message);
parse(message, SvnUtil.ERROR_PATTERN, errors);
parse(message, SvnUtil.WARNING_PATTERN, warnings);
}
}
@@ -59,50 +60,42 @@ public class SvnBindException extends VcsException {
if (throwable instanceof SVNException) {
SVNException e = (SVNException)throwable;
int code = e.getErrorMessage().getErrorCode().getCode();
int type = e.getErrorMessage().getType();
put(code, e.getMessage());
(type == SVNErrorMessage.TYPE_ERROR ? errors : warnings).putValue(code, e.getMessage());
}
}
public boolean contains(int error) {
return errors.containsKey(error);
public boolean contains(int code) {
return errors.containsKey(code) || warnings.containsKey(code);
}
public boolean contains(@NotNull SVNErrorCode error) {
return errors.containsKey(error.getCode());
public boolean contains(@NotNull SVNErrorCode code) {
return contains(code.getCode());
}
public boolean containsCategory(int category) {
final int categoryCode = getCategoryCode(category);
return ContainerUtil.exists(errors.keySet(), new Condition<Integer>() {
Condition<Integer> belongsToCategoryCondition = new Condition<Integer>() {
@Override
public boolean value(Integer code) {
return getCategoryCode(code) == categoryCode;
}
});
};
return ContainerUtil.exists(errors.keySet(), belongsToCategoryCondition) ||
ContainerUtil.exists(warnings.keySet(), belongsToCategoryCondition);
}
private static int getCategoryCode(int category) {
return (category - ERROR_BASE) / CATEGORY_SIZE;
}
private void parseErrors(@NotNull String message) {
Matcher matcher = SvnUtil.ERROR_PATTERN.matcher(message);
private static void parse(@NotNull String message, @NotNull Pattern pattern, @NotNull MultiMap<Integer, String> map) {
Matcher matcher = pattern.matcher(message);
while (matcher.find()) {
put(Integer.valueOf(matcher.group(2)), matcher.group());
}
}
private void put(int code, @Nullable String message) {
if (errors.containsKey(code)) {
if (!StringUtil.isEmpty(message)) {
errors.put(code, errors.get(code) + LineSeparator.LF.getSeparatorString() + message);
}
}
else {
errors.put(code, message);
map.putValue(Integer.valueOf(matcher.group(2)), matcher.group());
}
}
}
@@ -52,7 +52,9 @@ public enum SvnCommandName {
export("export", false),
upgrade("upgrade", true),
list("list", false),
diff("diff", false);
diff("diff", false),
// currently used to create only folders directly in repository - so writable = false
mkdir("mkdir", false);
private final String myName;
private final boolean myWriteable;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn.portable;
package org.jetbrains.idea.svn.commandLine;
import org.tmatesoft.svn.core.SVNException;
@@ -16,7 +16,7 @@
package org.jetbrains.idea.svn.config;
import org.jetbrains.idea.svn.SvnApplicationSettings;
import org.jetbrains.idea.svn.SvnAuthenticationManager;
import org.jetbrains.idea.svn.auth.SvnAuthenticationManager;
import java.util.ArrayList;
import java.util.Collection;
@@ -35,6 +35,6 @@ public class CmdConflictClient extends BaseSvnClient implements ConflictClient {
// for now parsing of the output is not required as command is executed only for one file
// and will be either successful or exception will be thrown
CommandUtil.execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.resolve, parameters, null);
execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.resolve, parameters, null);
}
}
@@ -30,7 +30,7 @@ public class CmdContentClient extends BaseSvnClient implements ContentClient {
CommandUtil.put(parameters, target.getPathOrUrlString(), pegRevision);
CommandUtil.put(parameters, revision);
CommandExecutor command = CommandUtil.execute(myVcs, target, SvnCommandName.cat, parameters, null);
CommandExecutor command = execute(myVcs, target, SvnCommandName.cat, parameters, null);
byte[] bytes = command.getBinaryOutput().toByteArray();
ContentRevisionCache.checkContentsSize(target.getPathOrUrlString(), bytes.length);
@@ -6,7 +6,11 @@ import com.intellij.openapi.vcs.VcsException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.api.BaseSvnClient;
import org.jetbrains.idea.svn.commandLine.*;
import org.jetbrains.idea.svn.checkin.CmdCheckinClient;
import org.jetbrains.idea.svn.checkin.CommitEventHandler;
import org.jetbrains.idea.svn.commandLine.BaseUpdateCommandListener;
import org.jetbrains.idea.svn.commandLine.CommandUtil;
import org.jetbrains.idea.svn.commandLine.SvnCommandName;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc2.SvnTarget;
@@ -34,22 +38,21 @@ public class CmdCopyMoveClient extends BaseSvnClient implements CopyMoveClient {
// directory being renamed will be blocked by svn process
File workingDirectory =
isMove && !SystemInfo.isFileSystemCaseSensitive && FileUtil.filesEqual(src, dst) ? CommandUtil.getHomeDirectory() : null;
CommandUtil
.execute(myVcs, SvnTarget.fromFile(dst), workingDirectory, isMove ? SvnCommandName.move : SvnCommandName.copy, parameters, null);
execute(myVcs, SvnTarget.fromFile(dst), workingDirectory, getCommandName(isMove), parameters, null);
}
@Override
public long copy(@NotNull SvnTarget source,
@NotNull SvnTarget destination,
@Nullable SVNRevision revision,
boolean makeParents,
@NotNull String message,
@Nullable CommitEventHandler handler) throws VcsException {
@NotNull SvnTarget destination,
@Nullable SVNRevision revision,
boolean makeParents,
boolean isMove,
@NotNull String message,
@Nullable CommitEventHandler handler) throws VcsException {
if (!destination.isURL()) {
throw new IllegalArgumentException("Only urls are supported as destination " + destination);
}
// TODO: Check that command fails when destination exists
List<String> parameters = new ArrayList<String>();
CommandUtil.put(parameters, source);
@@ -61,12 +64,11 @@ public class CmdCopyMoveClient extends BaseSvnClient implements CopyMoveClient {
// copy to url output is the same as commit output - just statuses have "copy of" suffix
// so "Adding" will be "Adding copy of"
SvnCommitRunner.CommandListener listener = new SvnCommitRunner.CommandListener(handler);
// TODO: Check correctness when source is url
CmdCheckinClient.CommandListener listener = new CmdCheckinClient.CommandListener(handler);
if (source.isFile()) {
listener.setBaseDirectory(source.getFile());
}
CommandUtil.execute(myVcs, source, SvnCommandName.copy, parameters, listener);
execute(myVcs, source, getCommandName(isMove), parameters, listener);
return listener.getCommittedRevision();
}
@@ -87,8 +89,13 @@ public class CmdCopyMoveClient extends BaseSvnClient implements CopyMoveClient {
File workingDirectory = CommandUtil.getHomeDirectory();
BaseUpdateCommandListener listener = new BaseUpdateCommandListener(workingDirectory, handler);
CommandUtil.execute(myVcs, source, workingDirectory, SvnCommandName.copy, parameters, listener);
execute(myVcs, source, workingDirectory, SvnCommandName.copy, parameters, listener);
listener.throwWrappedIfException();
}
@NotNull
private static SvnCommandName getCommandName(boolean isMove) {
return isMove ? SvnCommandName.move : SvnCommandName.copy;
}
}
@@ -4,7 +4,7 @@ import com.intellij.openapi.vcs.VcsException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.api.SvnClient;
import org.jetbrains.idea.svn.commandLine.CommitEventHandler;
import org.jetbrains.idea.svn.checkin.CommitEventHandler;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc2.SvnTarget;
@@ -23,6 +23,7 @@ public interface CopyMoveClient extends SvnClient {
* @param destination
* @param revision
* @param makeParents
* @param isMove
* @param message
* @param handler
* @return new revision number
@@ -32,6 +33,7 @@ public interface CopyMoveClient extends SvnClient {
@NotNull SvnTarget destination,
@Nullable SVNRevision revision,
boolean makeParents,
boolean isMove,
@NotNull String message,
@Nullable CommitEventHandler handler) throws VcsException;
@@ -4,7 +4,7 @@ import com.intellij.openapi.vcs.VcsException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.api.BaseSvnClient;
import org.jetbrains.idea.svn.commandLine.CommitEventHandler;
import org.jetbrains.idea.svn.checkin.CommitEventHandler;
import org.jetbrains.idea.svn.commandLine.SvnBindException;
import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNException;
@@ -40,6 +40,7 @@ public class SvnKitCopyMoveClient extends BaseSvnClient implements CopyMoveClien
@NotNull SvnTarget destination,
@Nullable SVNRevision revision,
boolean makeParents,
boolean isMove,
@NotNull String message,
@Nullable CommitEventHandler handler) throws VcsException {
@@ -53,7 +54,8 @@ public class SvnKitCopyMoveClient extends BaseSvnClient implements CopyMoveClien
SVNCommitInfo info;
try {
info = client.doCopy(new SVNCopySource[]{copySource}, destination.getURL(), false, makeParents, true, message, null);
info = client
.doCopy(new SVNCopySource[]{copySource}, destination.getURL(), isMove, makeParents, true, message, null);
}
catch (SVNException e) {
throw new VcsException(e);
@@ -1,12 +1,15 @@
package org.jetbrains.idea.svn.delete;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.api.BaseSvnClient;
import org.jetbrains.idea.svn.checkin.CmdCheckinClient;
import org.jetbrains.idea.svn.commandLine.BaseUpdateCommandListener;
import org.jetbrains.idea.svn.commandLine.CommandUtil;
import org.jetbrains.idea.svn.commandLine.SvnCommandName;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc2.SvnTarget;
@@ -32,9 +35,25 @@ public class CmdDeleteClient extends BaseSvnClient implements DeleteClient {
File workingDirectory = CommandUtil.getHomeDirectory();
BaseUpdateCommandListener listener = new BaseUpdateCommandListener(workingDirectory, handler);
CommandUtil.execute(myVcs, SvnTarget.fromFile(path), workingDirectory, SvnCommandName.delete, parameters, listener);
execute(myVcs, SvnTarget.fromFile(path), workingDirectory, SvnCommandName.delete, parameters, listener);
listener.throwWrappedIfException();
}
}
@Override
public long delete(@NotNull SVNURL url, @NotNull String message) throws VcsException {
SvnTarget target = SvnTarget.fromURL(url);
List<String> parameters = ContainerUtil.newArrayList();
CommandUtil.put(parameters, target);
parameters.add("--message");
parameters.add(message);
CmdCheckinClient.CommandListener listener = new CmdCheckinClient.CommandListener(null);
execute(myVcs, target, SvnCommandName.delete, parameters, listener);
return listener.getCommittedRevision();
}
}
@@ -4,6 +4,7 @@ import com.intellij.openapi.vcs.VcsException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.api.SvnClient;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import java.io.File;
@@ -14,4 +15,6 @@ import java.io.File;
public interface DeleteClient extends SvnClient {
void delete(@NotNull File path, boolean force, boolean dryRun, @Nullable ISVNEventHandler handler) throws VcsException;
long delete(@NotNull SVNURL url, @NotNull String message) throws VcsException;
}
@@ -4,7 +4,10 @@ import com.intellij.openapi.vcs.VcsException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.api.BaseSvnClient;
import org.jetbrains.idea.svn.commandLine.SvnBindException;
import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNWCClient;
@@ -27,4 +30,16 @@ public class SvnKitDeleteClient extends BaseSvnClient implements DeleteClient {
throw new VcsException(e);
}
}
@Override
public long delete(@NotNull SVNURL url, @NotNull String message) throws VcsException {
try {
SVNCommitInfo commitInfo = myVcs.createCommitClient().doDelete(new SVNURL[]{url}, message);
return commitInfo.getNewRevision();
}
catch (SVNException e) {
throw new SvnBindException(e);
}
}
}
@@ -357,7 +357,7 @@ public class CopiesPanel {
SelectBranchPopup.showForBranchRoot(myProject, root, new SelectBranchPopup.BranchSelectedCallback() {
@Override
public void branchSelected(Project project, SvnBranchConfigurationNew configuration, String url, long revision) {
new QuickMerge(project, url, wcInfo, SVNPathUtil.tail(url), root).execute(new QuickMergeInteractionImpl(myProject));
new QuickMerge(new MergeContext(myVcs, url, wcInfo, SVNPathUtil.tail(url), root)).execute(new QuickMergeInteractionImpl(myProject));
}
}, "Select branch", mergeLabel);
}
@@ -25,14 +25,13 @@ import com.intellij.util.PairConsumer;
import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.continuation.TaskDescriptor;
import com.intellij.util.continuation.Where;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.SvnBundle;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.history.SvnChangeList;
import org.jetbrains.idea.svn.history.SvnCommittedChangesProvider;
import org.jetbrains.idea.svn.history.SvnRepositoryLocation;
import org.jetbrains.idea.svn.history.TreeStructureNode;
import org.jetbrains.idea.svn.mergeinfo.OneShotMergeInfoHelper;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
@@ -45,7 +44,7 @@ import java.util.List;
* Date: 3/30/13
* Time: 7:40 PM
*/
class LoadRecentBranchRevisions extends TaskDescriptor {
public class LoadRecentBranchRevisions extends TaskDescriptor {
public static final String PROP_BUNCH_SIZE = "idea.svn.quick.merge.bunch.size";
private final static int BUNCH_SIZE = 100;
private int myBunchSize;
@@ -53,21 +52,17 @@ class LoadRecentBranchRevisions extends TaskDescriptor {
private boolean myLastLoaded;
private OneShotMergeInfoHelper myHelper;
private List<CommittedChangeList> myCommittedChangeLists;
private final WCInfo myWcInfo;
private final SvnVcs myVcs;
private final String mySourceUrl;
private final Integer myTestBunchSize;
@NotNull private final MergeContext myMergeContext;
LoadRecentBranchRevisions(String branchName, long first, WCInfo info, SvnVcs vcs, String url) {
this(branchName, first, info, vcs, url, -1);
public LoadRecentBranchRevisions(@NotNull MergeContext mergeContext, long first) {
this(mergeContext, first, -1);
}
LoadRecentBranchRevisions(String branchName, long first, WCInfo info, SvnVcs vcs, String url, final int bunchSize) {
super("Loading recent " + branchName + " revisions", Where.POOLED);
public LoadRecentBranchRevisions(@NotNull MergeContext mergeContext, long first, int bunchSize) {
super("Loading recent " + mergeContext.getBranchName() + " revisions", Where.POOLED);
myMergeContext = mergeContext;
myFirst = first;
myWcInfo = info;
myVcs = vcs;
mySourceUrl = url;
// for test purposes!!!
myTestBunchSize = Integer.getInteger(PROP_BUNCH_SIZE);
if (myTestBunchSize != null) {
@@ -84,22 +79,24 @@ class LoadRecentBranchRevisions extends TaskDescriptor {
@Override
public void run(ContinuationContext context) {
final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
final SvnCommittedChangesProvider committedChangesProvider = (SvnCommittedChangesProvider)myVcs.getCommittedChangesProvider();
final SvnCommittedChangesProvider committedChangesProvider =
(SvnCommittedChangesProvider)myMergeContext.getVcs().getCommittedChangesProvider();
final ChangeBrowserSettings settings = new ChangeBrowserSettings();
if (myFirst > 0){
settings.CHANGE_BEFORE = String.valueOf(myFirst);
settings.USE_CHANGE_BEFORE_FILTER = true;
}
String local = SVNPathUtil.getRelativePath(myWcInfo.getRepositoryRoot(), myWcInfo.getRootUrl());
String local = SVNPathUtil.getRelativePath(myMergeContext.getWcInfo().getRepositoryRoot(), myMergeContext.getWcInfo().getRootUrl());
final String relativeLocal = (local.startsWith("/") ? local : "/" + local);
String relativeBranch = SVNPathUtil.getRelativePath(myWcInfo.getRepositoryRoot(), mySourceUrl);
String relativeBranch = SVNPathUtil.getRelativePath(myMergeContext.getWcInfo().getRepositoryRoot(), myMergeContext.getSourceUrl());
relativeBranch = (relativeBranch.startsWith("/") ? relativeBranch : "/" + relativeBranch);
ProgressManager.progress2(SvnBundle.message("progress.text2.collecting.history", mySourceUrl + (myFirst > 0 ? ("@" + myFirst) : "")));
ProgressManager.progress2(
SvnBundle.message("progress.text2.collecting.history", myMergeContext.getSourceUrl() + (myFirst > 0 ? ("@" + myFirst) : "")));
final List<Pair<SvnChangeList, TreeStructureNode<SVNLogEntry>>> list = new ArrayList<Pair<SvnChangeList, TreeStructureNode<SVNLogEntry>>>();
try {
committedChangesProvider.getCommittedChangesWithMergedRevisons(settings, new SvnRepositoryLocation(mySourceUrl),
committedChangesProvider.getCommittedChangesWithMergedRevisons(settings, new SvnRepositoryLocation(myMergeContext.getSourceUrl()),
myBunchSize + (myFirst > 0 ? 2 : 1),
new PairConsumer<SvnChangeList, TreeStructureNode<SVNLogEntry>>() {
public void consume(SvnChangeList svnList, TreeStructureNode<SVNLogEntry> tree) {
@@ -123,7 +120,7 @@ class LoadRecentBranchRevisions extends TaskDescriptor {
}
try {
myHelper = new OneShotMergeInfoHelper(myVcs.getProject(), myWcInfo, mySourceUrl);
myHelper = new OneShotMergeInfoHelper(myMergeContext);
ProgressManager.progress2("Calculating not merged revisions");
myHelper.prepare();
}
@@ -0,0 +1,88 @@
/*
* Copyright 2000-2014 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.dialogs;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.SvnVcs;
/**
* @author Konstantin Kolosovsky.
*/
public class MergeContext {
@NotNull private final Project myProject;
@NotNull private final String myBranchName;
@NotNull private final VirtualFile myRoot;
@NotNull private final WCInfo myWcInfo;
@NotNull private String mySourceUrl;
@NotNull private final SvnVcs myVcs;
@NotNull private final String myTitle;
public MergeContext(@NotNull SvnVcs vcs,
@NotNull String sourceUrl,
@NotNull WCInfo wcInfo,
@NotNull String branchName,
@NotNull VirtualFile root) {
myVcs = vcs;
myProject = vcs.getProject();
myBranchName = branchName;
myRoot = root;
mySourceUrl = sourceUrl;
myWcInfo = wcInfo;
myTitle = "Merge from " + myBranchName;
}
@NotNull
public Project getProject() {
return myProject;
}
@NotNull
public String getBranchName() {
return myBranchName;
}
@NotNull
public VirtualFile getRoot() {
return myRoot;
}
@NotNull
public WCInfo getWcInfo() {
return myWcInfo;
}
@NotNull
public String getSourceUrl() {
return mySourceUrl;
}
@NotNull
public SvnVcs getVcs() {
return myVcs;
}
@NotNull
public String getTitle() {
return myTitle;
}
public void setSourceUrl(@NotNull String sourceUrl) {
mySourceUrl = sourceUrl;
}
}
@@ -15,167 +15,46 @@
*/
package org.jetbrains.idea.svn.dialogs;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
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.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.*;
import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager;
import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.RefreshSessionImpl;
import com.intellij.openapi.vcs.CalledInAwt;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.util.Consumer;
import com.intellij.util.FilePathByPathComparator;
import com.intellij.util.PairConsumer;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.continuation.*;
import com.intellij.util.continuation.Continuation;
import com.intellij.util.continuation.TaskDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.NestedCopyType;
import org.jetbrains.idea.svn.SvnBranchConfigurationManager;
import org.jetbrains.idea.svn.SvnUtil;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.actions.ChangeListsMergerFactory;
import org.jetbrains.idea.svn.commandLine.SvnBindException;
import org.jetbrains.idea.svn.history.SvnChangeList;
import org.jetbrains.idea.svn.history.SvnCommittedChangesProvider;
import org.jetbrains.idea.svn.history.SvnRepositoryLocation;
import org.jetbrains.idea.svn.history.TreeStructureNode;
import org.jetbrains.idea.svn.integrate.*;
import org.jetbrains.idea.svn.mergeinfo.MergeChecker;
import org.jetbrains.idea.svn.mergeinfo.OneShotMergeInfoHelper;
import org.jetbrains.idea.svn.mergeinfo.SvnMergeInfoCache;
import org.jetbrains.idea.svn.update.UpdateEventHandler;
import org.jetbrains.idea.svn.integrate.CheckRepositorySupportsMergeInfoTask;
import org.jetbrains.idea.svn.integrate.MergeInitChecksTask;
import org.jetbrains.idea.svn.integrate.QuickMergeInteraction;
import org.jetbrains.idea.svn.integrate.SourceUrlCorrectionTask;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNLogEntryPath;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.util.SVNURLUtil;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class QuickMerge {
private final Project myProject;
private final String myBranchName;
private final VirtualFile myRoot;
private WCInfo myWcInfo;
private String mySourceUrl;
private SvnVcs myVcs;
private final String myTitle;
@NotNull private final MergeContext myMergeContext;
private final Continuation myContinuation;
private QuickMergeInteraction myInteraction;
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.dialogs.QuickMerge");
public QuickMerge(Project project, String sourceUrl, WCInfo wcInfo, final String branchName, final VirtualFile root) {
myProject = project;
myBranchName = branchName;
myRoot = root;
myVcs = SvnVcs.getInstance(project);
mySourceUrl = sourceUrl;
myWcInfo = wcInfo;
myTitle = "Merge from " + myBranchName;
myContinuation = Continuation.createFragmented(myProject, true);
}
private class SourceUrlCorrection extends TaskDescriptor {
private SourceUrlCorrection() {
super("Checking branch", Where.POOLED);
}
@Override
public void run(ContinuationContext continuationContext) {
final SVNURL branch = SvnBranchConfigurationManager.getInstance(myProject).getSvnBranchConfigManager().getWorkingBranchWithReload(myWcInfo.getUrl(), myRoot);
if (branch != null && (! myWcInfo.getUrl().equals(branch))) {
final String branchString = branch.toString();
if (SVNPathUtil.isAncestor(branchString, myWcInfo.getRootUrl())) {
final String subPath = SVNPathUtil.getRelativePath(branchString, myWcInfo.getRootUrl());
mySourceUrl = SVNPathUtil.append(mySourceUrl, subPath);
}
}
}
}
private class MyInitChecks extends TaskDescriptor {
private MyInitChecks() {
super("initial checks", Where.AWT);
}
@Override
public void run(ContinuationContext continuationContext) {
SVNURL url = parseUrl(continuationContext);
if (url == null) {
return;
}
if (SVNURLUtil.isAncestor(url, myWcInfo.getUrl()) || SVNURLUtil.isAncestor(myWcInfo.getUrl(), url)) {
finishWithError(continuationContext, "Cannot merge from self", true);
return;
}
if (! checkForSwitchedRoots()) {
continuationContext.cancelEverything();
}
}
@Nullable
private SVNURL parseUrl(ContinuationContext continuationContext) {
SVNURL url = null;
try {
url = SvnUtil.createUrl(mySourceUrl);
}
catch (SvnBindException e) {
finishWithError(continuationContext, e.getMessage(), true);
}
return url;
}
}
private class CheckRepositorySupportsMergeinfo extends TaskDescriptor {
private CheckRepositorySupportsMergeinfo() {
super("Checking repository capabilities", Where.POOLED);
}
@Override
public void run(ContinuationContext context) {
final List<TaskDescriptor> tasks = new LinkedList<TaskDescriptor>();
final boolean supportsMergeinfo = myWcInfo.getFormat().supportsMergeInfo() && SvnUtil.checkRepositoryVersion15(myVcs, mySourceUrl);
if (! supportsMergeinfo) {
insertMergeAll(tasks);
} else {
tasks.add(new MergeAllOrSelectedChooser());
}
context.next(tasks);
}
public QuickMerge(@NotNull MergeContext mergeContext) {
myMergeContext = mergeContext;
myContinuation = Continuation.createFragmented(mergeContext.getProject(), true);
}
@CalledInAwt
public void execute(@NotNull final QuickMergeInteraction interaction, @NotNull final TaskDescriptor... finalTasks) {
myInteraction = interaction;
myInteraction.setTitle(myTitle);
myInteraction.setTitle(myMergeContext.getTitle());
FileDocumentManager.getInstance().saveAllDocuments();
final List<TaskDescriptor> tasks = new LinkedList<TaskDescriptor>();
tasks.add(new MyInitChecks());
tasks.add(new SourceUrlCorrection());
tasks.add(new CheckRepositorySupportsMergeinfo());
tasks.add(new MergeInitChecksTask(myMergeContext, myInteraction));
tasks.add(new SourceUrlCorrectionTask(myMergeContext, myInteraction));
tasks.add(new CheckRepositorySupportsMergeInfoTask(myMergeContext, myInteraction));
if (finalTasks.length > 0) {
tasks.addAll(Arrays.asList(finalTasks));
}
@@ -183,13 +62,13 @@ public class QuickMerge {
myContinuation.addExceptionHandler(VcsException.class, new Consumer<VcsException>() {
@Override
public void consume(VcsException e) {
myInteraction.showErrors(myTitle, Collections.singletonList(e));
myInteraction.showErrors(myMergeContext.getTitle(), Collections.singletonList(e));
}
});
myContinuation.addExceptionHandler(SVNException.class, new Consumer<SVNException>() {
@Override
public void consume(SVNException e) {
myInteraction.showErrors(myTitle, Collections.singletonList(new VcsException(e)));
myInteraction.showErrors(myMergeContext.getTitle(), Collections.singletonList(new VcsException(e)));
}
});
myContinuation.addExceptionHandler(RuntimeException.class, new Consumer<RuntimeException>() {
@@ -200,664 +79,4 @@ public class QuickMerge {
});
myContinuation.run(tasks);
}
private class ShowRecentInDialog extends TaskDescriptor {
private final LoadRecentBranchRevisions myLoader;
private ShowRecentInDialog(LoadRecentBranchRevisions loader) {
super("", Where.AWT);
myLoader = loader;
}
@Override
public void run(ContinuationContext context) {
final PairConsumer<Long, MergeDialogI> loader = new PairConsumer<Long, MergeDialogI>() {
@Override
public void consume(Long bunchSize, final MergeDialogI dialog) {
final LoadRecentBranchRevisions loader =
new LoadRecentBranchRevisions(myBranchName, dialog.getLastNumber(), myWcInfo, myVcs, mySourceUrl, bunchSize.intValue());
final TaskDescriptor updater = new TaskDescriptor("", Where.AWT) {
@Override
public void run(ContinuationContext context) {
dialog.addMoreLists(loader.getCommittedChangeLists());
if (loader.isLastLoaded()) {
dialog.setEverythingLoaded(true);
}
}
@Override
public void canceled() {
dialog.addMoreLists(Collections.<CommittedChangeList>emptyList());
dialog.setEverythingLoaded(true);
}
};
final Continuation fragmented = Continuation.createFragmented(myProject, true);
fragmented.addExceptionHandler(VcsException.class, new Consumer<VcsException>() {
@Override
public void consume(VcsException e) {
PopupUtil.showBalloonForActiveComponent(e.getMessage() == null ? e.getClass().getName() : e.getMessage(), MessageType.ERROR);
}
});
fragmented.run(loader, updater);
}
};
final List<CommittedChangeList> lists = myInteraction.showRecentListsForSelection(myLoader.getCommittedChangeLists(),
myTitle, myLoader.getHelper(), loader, myLoader.isLastLoaded());
if (lists != null && ! lists.isEmpty()){
final MergerFactory factory = new ChangeListsMergerFactory(lists) {
@Override
public IMerger createMerger(SvnVcs vcs, File target, UpdateEventHandler handler, SVNURL currentBranchUrl, String branchName) {
return new GroupMerger(vcs, lists, target, handler, currentBranchUrl, branchName, false, false, false);
}
};
// fictive branch point, just for
final SvnBranchPointsCalculator.BranchCopyData copyData =
new SvnBranchPointsCalculator.BranchCopyData(myWcInfo.getUrl().toString(), -1, mySourceUrl, -1);
context.next(new LocalChangesPrompt(false, lists,
new SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>(false, copyData)),
new MergeTask(factory, myTitle));
} else {
context.cancelEverything();
}
}
}
private class MergeAllOrSelectedChooser extends TaskDescriptor {
private MergeAllOrSelectedChooser() {
super("merge source selector", Where.AWT);
}
@Override
public void run(final ContinuationContext context) {
final QuickMergeContentsVariants variant = myInteraction.selectMergeVariant();
if (QuickMergeContentsVariants.cancel == variant) return;
if (QuickMergeContentsVariants.all == variant) {
insertMergeAll(context);
return;
}
if (QuickMergeContentsVariants.showLatest == variant) {
final LoadRecentBranchRevisions loader = new LoadRecentBranchRevisions(myBranchName, -1, myWcInfo, myVcs, mySourceUrl);
final ShowRecentInDialog dialog = new ShowRecentInDialog(loader);
context.next(loader, dialog);
return;
}
final MergeCalculator calculator;
try {
calculator = new MergeCalculator(myWcInfo, mySourceUrl, myBranchName);
}
catch (VcsException e) {
finishWithError(context, e.getMessage(), true);
return;
}
context.next(myVcs.getSvnBranchPointsCalculator().getFirstCopyPointTask(
myWcInfo.getRepositoryRoot(), myWcInfo.getRootUrl(), mySourceUrl, calculator), calculator);
}
}
private void insertMergeAll(final ContinuationContext context) {
final List<TaskDescriptor> queue = new ArrayList<TaskDescriptor>();
insertMergeAll(queue);
context.next(queue);
}
private boolean checkForSwitchedRoots() {
final List<WCInfo> infoList = myVcs.getAllWcInfos();
boolean switchedFound = false;
for (WCInfo wcInfo : infoList) {
if (FileUtil.isAncestor(new File(myWcInfo.getPath()), new File(wcInfo.getPath()), true)
&& NestedCopyType.switched.equals(wcInfo.getType())) {
switchedFound = true;
break;
}
}
if (switchedFound) {
return myInteraction.shouldContinueSwitchedRootFound();
}
return true;
}
@CalledInAny
private void finishWithError(final ContinuationContext context, final String message, final List<VcsException> exceptions) {
if (exceptions != null) {
for (VcsException exception : exceptions) {
LOG.info(message, exception);
}
}
context.cancelEverything();
context.next(new TaskDescriptor(message, Where.AWT) {
@Override
public void run(ContinuationContext context) {
myInteraction.showErrors(message, exceptions);
}
});
}
@CalledInAny
private void finishWithError(final ContinuationContext context, final String message, final boolean isError) {
LOG.info((isError ? "Error: " : "Info: ") + message);
context.next(new TaskDescriptor(message, Where.AWT) {
@Override
public void run(ContinuationContext context) {
myInteraction.showErrors(message, isError);
context.cancelEverything();
}
});
}
private void insertMergeAll(final List<TaskDescriptor> queue) {
queue.add(new LocalChangesPrompt(true, null, null));
final MergeAllWithBranchCopyPoint mergeAllExecutor = new MergeAllWithBranchCopyPoint();
queue.add(myVcs.getSvnBranchPointsCalculator().getFirstCopyPointTask(myWcInfo.getRepositoryRoot(), mySourceUrl, myWcInfo.getRootUrl(), mergeAllExecutor));
queue.add(mergeAllExecutor);
}
private class MergeAllWithBranchCopyPoint extends TaskDescriptor implements Consumer<TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>, SVNException>> {
private final AtomicReference<TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>, SVNException>> myData;
private MergeAllWithBranchCopyPoint() {
super("merge all", Where.AWT);
myData = new AtomicReference<TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>, SVNException>>();
}
@Override
public void consume(TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>, SVNException> value) {
myData.set(value);
}
@Override
public void run(ContinuationContext context) {
SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData> invertor;
try {
final TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>, SVNException>
transparentlyFailedValueI = myData.get();
if (transparentlyFailedValueI == null) {
finishWithError(context, "Merge start wasn't found", true);
return;
}
invertor = transparentlyFailedValueI.get();
}
catch (SVNException e) {
finishWithError(context, "Merge start wasn't found", Collections.singletonList(new VcsException(e)));
return;
}
if (invertor == null) {
finishWithError(context, "Merge start wasn't found", true);
return;
}
final boolean reintegrate = invertor.isInvertedSense();
if (reintegrate && (! myInteraction.shouldReintegrate(mySourceUrl, invertor.inverted().getTarget()))) {
context.cancelEverything();
return;
}
final MergerFactory mergerFactory = createBranchMergerFactory(reintegrate, invertor);
final String title = "Merging all from " + myBranchName + (reintegrate ? " (reintegrate)" : "");
context.next(new MergeTask(mergerFactory, title));
}
private MergerFactory createBranchMergerFactory(final boolean reintegrate,
final SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData> invertor) {
return new MergerFactory() {
public IMerger createMerger(SvnVcs vcs, File target, UpdateEventHandler handler, SVNURL currentBranchUrl, String branchName) {
return new BranchMerger(vcs, currentBranchUrl, myWcInfo.getUrl(), myWcInfo.getPath(), handler, reintegrate, myBranchName,
reintegrate ? invertor.getWrapped().getTargetRevision() : invertor.getWrapped().getSourceRevision());
}
@Nullable
public List<CommittedChangeList> getListsToMerge() {
return null;
}
public boolean isMergeAll() {
return true;
}
};
}
}
private class MergeTask extends TaskDescriptor {
private final MergerFactory myFactory;
// awt since only a wrapper
private MergeTask(final MergerFactory factory, final String mergeTitle) {
super(mergeTitle, Where.AWT);
myFactory = factory;
}
@Override
public void run(ContinuationContext context) {
final SVNURL sourceUrlUrl;
try {
sourceUrlUrl = SVNURL.parseURIEncoded(mySourceUrl);
} catch (SVNException e) {
finishWithError(context, "Cannot merge: " + e.getMessage(), true);
return;
}
final SvnIntegrateChangesTask task = new SvnIntegrateChangesTask(SvnVcs.getInstance(myProject),
new WorkingCopyInfo(myWcInfo.getPath(), true), myFactory, sourceUrlUrl, getName(), false, myBranchName);
final TaskDescriptor taskDescriptor = TaskDescriptor.createForBackgroundableTask(task);
// merge task will be the next after...
context.next(taskDescriptor);
// ... after we create changelist
createChangelist(context);
}
private void createChangelist(final ContinuationPause context) {
final ChangeListManager listManager = ChangeListManager.getInstance(myProject);
String name = myTitle;
int i = 1;
boolean updateDefaultList = false;
while (true) {
final LocalChangeList changeList = listManager.findChangeList(name);
if (changeList == null) {
final LocalChangeList newList = listManager.addChangeList(name, null);
listManager.setDefaultChangeList(newList);
updateDefaultList = true;
break;
}
if (changeList.getChanges().isEmpty()) {
if (! changeList.isDefault()) {
listManager.setDefaultChangeList(changeList);
updateDefaultList = true;
}
break;
}
name = myTitle + " (" + i + ")";
++ i;
}
if (updateDefaultList) {
context.suspend();
listManager.invokeAfterUpdate(new Runnable() {
public void run() {
context.ping();
}
}, InvokeAfterUpdateMode.BACKGROUND_NOT_CANCELLABLE_NOT_AWT, "", ModalityState.NON_MODAL);
}
}
}
// true if errors found
static boolean checkListForPaths(String relativeLocal,
String relativeBranch, Pair<SvnChangeList, TreeStructureNode<SVNLogEntry>> pair) {
// TODO: Such filtering logic is not clear enough so far (and probably not correct for all cases - for instance when we perform merge
// TODO: from branch1 to branch2 and have revision which contain merge changes from branch3 to branch1.
// TODO: In this case paths of child log entries will not contain neither urls from branch1 nor from branch2 - and checkEntry() method
// TODO: will return true => so such revision will not be used (and displayed) further.
// TODO: Why do we check entries recursively - we have a revision - set of changes in the "merge from" branch? Why do we need to check
// TODO: where they came from - we want avoid some circular merges or what? Does subversion itself perform such checks or not?
final List<TreeStructureNode<SVNLogEntry>> children = pair.getSecond().getChildren();
boolean localChange = false;
for (TreeStructureNode<SVNLogEntry> child : children) {
if (checkForSubtree(child, relativeLocal, relativeBranch)) {
localChange = true;
break;
}
}
if (! localChange) {
// check self
return checkForEntry(pair.getSecond().getMe(), relativeLocal, relativeBranch);
}
return localChange;
}
// true if errors found
private static boolean checkForSubtree(final TreeStructureNode<SVNLogEntry> tree,
String relativeBranch, final String localURL) {
final LinkedList<TreeStructureNode<SVNLogEntry>> queue = new LinkedList<TreeStructureNode<SVNLogEntry>>();
queue.addLast(tree);
while (! queue.isEmpty()) {
final TreeStructureNode<SVNLogEntry> element = queue.removeFirst();
ProgressManager.checkCanceled();
if (checkForEntry(element.getMe(), localURL, relativeBranch)) return true;
queue.addAll(element.getChildren());
}
return false;
}
// true if errors found
// checks if either some changed path is in current branch => treat as local change
// or if no changed paths in current branch, checks if at least one path in "merge from" branch
// NOTE: this fails for "merge-source" log entries from other branches - when all changed paths are from some
// third branch - this logic treats such log entry as local.
private static boolean checkForEntry(final SVNLogEntry entry, final String localURL, String relativeBranch) {
boolean atLeastOneUnderBranch = false;
final Map map = entry.getChangedPaths();
for (Object o : map.values()) {
final SVNLogEntryPath path = (SVNLogEntryPath) o;
if (SVNPathUtil.isAncestor(localURL, path.getPath())) {
return true;
}
if (! atLeastOneUnderBranch && SVNPathUtil.isAncestor(relativeBranch, path.getPath())) {
atLeastOneUnderBranch = true;
}
}
return ! atLeastOneUnderBranch;
}
private class MergeCalculator extends TaskDescriptor implements
Consumer<TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>, SVNException>> {
private final static String ourOneShotStrategy = "svn.quickmerge.oneShotStrategy";
private final WCInfo myWcInfo;
private final String mySourceUrl;
private final String myBranchName;
private final
AtomicReference<TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>, SVNException>>
myCopyData;
private boolean myIsReintegrate;
private final List<CommittedChangeList> myNotMerged;
private String myMergeTitle;
private final MergeChecker myMergeChecker;
@Override
public void consume(TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>, SVNException> value) {
myCopyData.set(value);
}
private MergeCalculator(WCInfo wcInfo, String sourceUrl, String branchName) throws VcsException {
super("Calculating not merged revisions", Where.POOLED);
myWcInfo = wcInfo;
mySourceUrl = sourceUrl;
myBranchName = branchName;
myNotMerged = new LinkedList<CommittedChangeList>();
myMergeTitle = "Merge from " + branchName;
// if (Boolean.TRUE.equals(Boolean.getBoolean(ourOneShotStrategy))) {
myMergeChecker = new OneShotMergeInfoHelper(myProject, myWcInfo, mySourceUrl);
((OneShotMergeInfoHelper) myMergeChecker).prepare();
/* } else {
myMergeChecker = new BranchInfo.MyMergeCheckerWrapper(myWcInfo.getPath(), new BranchInfo(myVcs, myWcInfo.getRepositoryRoot(),
myWcInfo.getRootUrl(), mySourceUrl,
mySourceUrl, myVcs.createWCClient()));
}*/
myCopyData = new AtomicReference<TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>, SVNException>>();
}
//"Calculating not merged revisions"
@Override
public void run(ContinuationContext context) {
SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData> copyDataValue = null;
try {
copyDataValue = myCopyData.get().get();
}
catch (SVNException e) {
finishWithError(context, "Merge start wasn't found", Collections.singletonList(new VcsException(e)));
return;
}
if (copyDataValue == null) {
finishWithError(context, "Merge start wasn't found", true);
return;
}
final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
myIsReintegrate = copyDataValue.isInvertedSense();
if (!myWcInfo.getFormat().supportsMergeInfo()) return;
final SvnBranchPointsCalculator.BranchCopyData data = copyDataValue.getTrue();
final long sourceLatest = data.getTargetRevision();
final SvnCommittedChangesProvider committedChangesProvider = (SvnCommittedChangesProvider)myVcs.getCommittedChangesProvider();
final ChangeBrowserSettings settings = new ChangeBrowserSettings();
settings.CHANGE_AFTER = Long.toString(sourceLatest);
settings.USE_CHANGE_AFTER_FILTER = true;
String local = SVNPathUtil.getRelativePath(myWcInfo.getRepositoryRoot(), myWcInfo.getRootUrl());
final String relativeLocal = (local.startsWith("/") ? local : "/" + local);
String relativeBranch = SVNPathUtil.getRelativePath(myWcInfo.getRepositoryRoot(), mySourceUrl);
relativeBranch = (relativeBranch.startsWith("/") ? relativeBranch : "/" + relativeBranch);
final LinkedList<Pair<SvnChangeList, TreeStructureNode<SVNLogEntry>>> list =
new LinkedList<Pair<SvnChangeList, TreeStructureNode<SVNLogEntry>>>();
try {
committedChangesProvider.getCommittedChangesWithMergedRevisons(settings, new SvnRepositoryLocation(mySourceUrl), 0,
new PairConsumer<SvnChangeList, TreeStructureNode<SVNLogEntry>>() {
public void consume(SvnChangeList svnList, TreeStructureNode<SVNLogEntry> tree) {
indicator.checkCanceled();
if (sourceLatest >= svnList.getNumber()) return;
list.add(new Pair<SvnChangeList, TreeStructureNode<SVNLogEntry>>(svnList, tree));
}
});
}
catch (VcsException e) {
finishWithError(context, "Checking revisions for merge fault", Collections.singletonList(e));
}
indicator.setText("Checking merge information...");
// to do not go into file system while asking something on the net
for (Pair<SvnChangeList, TreeStructureNode<SVNLogEntry>> pair : list) {
final SvnChangeList svnList = pair.getFirst();
final SvnMergeInfoCache.MergeCheckResult checkResult = myMergeChecker.checkList(svnList);
indicator.setText2("Processing revision " + svnList.getNumber());
if (SvnMergeInfoCache.MergeCheckResult.NOT_MERGED.equals(checkResult)) {
// additionally check for being 'local'
boolean localChange = checkListForPaths(relativeLocal, relativeBranch, pair);
if (! localChange) {
myNotMerged.add(svnList);
}
}
}
if (myNotMerged.isEmpty()) {
finishWithError(context, "Everything is up-to-date", false);
return;
}
context.next(new ShowRevisionSelector(copyDataValue));
}
private class ShowRevisionSelector extends TaskDescriptor {
private final SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData> myCopyPoint;
private ShowRevisionSelector(SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData> copyPoint) {
super("show revisions to merge", Where.AWT);
myCopyPoint = copyPoint;
}
@Override
public void run(ContinuationContext context) {
final QuickMergeInteraction.SelectMergeItemsResult result = myInteraction.selectMergeItems(myNotMerged, myMergeTitle, myMergeChecker);
if (QuickMergeContentsVariants.cancel == result.getResultCode()) {
context.cancelEverything();
return;
}
if (QuickMergeContentsVariants.all == result.getResultCode()) {
insertMergeAll(context);
} else {
final List<CommittedChangeList> lists = result.getSelectedLists();
if (lists.isEmpty()) return;
final MergerFactory factory = new ChangeListsMergerFactory(lists) {
@Override
public IMerger createMerger(SvnVcs vcs, File target, UpdateEventHandler handler, SVNURL currentBranchUrl, String branchName) {
return new GroupMerger(vcs, lists, target, handler, currentBranchUrl, branchName, false, false, false);
}
};
context.next(new LocalChangesPrompt(false, lists, myCopyPoint), new MergeTask(factory, myMergeTitle));
}
}
}
}
private class LocalChangesPrompt extends TaskDescriptor {
private final boolean myMergeAll;
@Nullable private final List<CommittedChangeList> myLists;
private final SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData> myCopyPoint;
private LocalChangesPrompt(final boolean mergeAll,
@Nullable final List<CommittedChangeList> lists,
@Nullable SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData> copyPoint) {
super("local changes intersection check", Where.AWT);
myMergeAll = mergeAll;
myLists = lists;
myCopyPoint = copyPoint;
}
@Nullable
private File getLocalPath(final String relativeToRepoPath) {
// from source if not inverted
final String pathToCheck = SVNPathUtil.append(myWcInfo.getRepositoryRoot(), relativeToRepoPath);
final SvnBranchPointsCalculator.BranchCopyData wrapped = myCopyPoint.getWrapped();
final String relativeInSource =
SVNPathUtil.getRelativePath(myCopyPoint.isInvertedSense() ? wrapped.getSource() : wrapped.getTarget(), pathToCheck);
if (StringUtil.isEmptyOrSpaces(relativeInSource)) return null;
final File local = new File(myWcInfo.getPath(), relativeInSource);
return local;
}
@Override
public void run(ContinuationContext context) {
final Intersection intersection;
final ChangeListManager listManager = ChangeListManager.getInstance(myProject);
final List<LocalChangeList> localChangeLists = listManager.getChangeListsCopy();
if (myMergeAll) {
intersection = getMergeAllIntersection(localChangeLists);
} else {
intersection = checkIntersection(myLists, localChangeLists);
}
if (intersection == null || intersection.getChangesSubset().isEmpty()) return;
final LocalChangesAction action = myInteraction.selectLocalChangesAction(myMergeAll);
switch (action) {
// shelve
case shelve:
context.next(new ShelveLocalChanges(intersection));
return;
// cancel
case cancel:
context.cancelEverything();
return;
// continue
case continueMerge:
return;
// inspect
case inspect:
// here's cast is due to generic's bug
@SuppressWarnings("unchecked") final Collection<Change> changes = (Collection<Change>) intersection.getChangesSubset().values();
final List<FilePath> paths = ChangesUtil.getPaths(changes);
Collections.sort(paths, FilePathByPathComparator.getInstance());
myInteraction.showIntersectedLocalPaths(paths);
context.cancelEverything();
return;
default:
}
}
@Nullable
private Intersection checkIntersection(@Nullable final List<CommittedChangeList> lists, List<LocalChangeList> localChangeLists) {
if (lists == null || lists.isEmpty()) {
return null;
}
final Set<FilePath> mergePaths = new HashSet<FilePath>();
for (CommittedChangeList list : lists) {
final SvnChangeList svnList = (SvnChangeList)list;
final List<String> paths = new ArrayList<String>(svnList.getAddedPaths());
paths.addAll(svnList.getChangedPaths());
paths.addAll(svnList.getDeletedPaths());
for (String path : paths) {
final File localPath = getLocalPath(path);
if (localPath != null) {
mergePaths.add(new FilePathImpl(localPath, false));
}
}
}
final Intersection intersection = new Intersection();
for (LocalChangeList localChangeList : localChangeLists) {
final Collection<Change> localChanges = localChangeList.getChanges();
for (Change localChange : localChanges) {
final FilePath before = localChange.getBeforeRevision() == null ? null : localChange.getBeforeRevision().getFile();
final FilePath after = localChange.getAfterRevision() == null ? null : localChange.getAfterRevision().getFile();
if ((before != null && mergePaths.contains(before)) || (after != null && mergePaths.contains(after))) {
intersection.add(localChangeList.getName(), localChangeList.getComment(), localChange);
}
}
}
return intersection;
}
}
private class ShelveLocalChanges extends TaskDescriptor {
private final Intersection myIntersection;
private ShelveLocalChanges(final Intersection intersection) {
super("Shelving local changes before merge", Where.POOLED);
myIntersection = intersection;
}
@Override
public void run(final ContinuationContext context) {
final MultiMap<String, Change> map = myIntersection.getChangesSubset();
final RefreshSessionImpl session = new RefreshSessionImpl(true, false, new Runnable() {
public void run() {
context.ping();
}
});
for (String name : map.keySet()) {
try {
final Collection<Change> changes = map.get(name);
ApplicationManager.getApplication().invokeAndWait(new Runnable() {
@Override
public void run() {
FileDocumentManager.getInstance().saveAllDocuments();
}
}, ModalityState.NON_MODAL);
ShelveChangesManager.getInstance(myProject).shelveChanges(changes, myIntersection.getComment(name) + " (auto shelve before merge)",
true);
session.addAllFiles(ChangesUtil.getFilesFromChanges(changes));
}
catch (IOException e) {
finishWithError(context, e.getMessage(), true);
}
catch (VcsException e) {
finishWithError(context, e.getMessage(), true);
}
}
// first suspend to guarantee stop->then start back sequence
context.suspend();
session.launch();
}
}
private Intersection getMergeAllIntersection(List<LocalChangeList> localChangeLists) {
final Intersection intersection = new Intersection();
for (LocalChangeList localChangeList : localChangeLists) {
final Collection<Change> localChanges = localChangeList.getChanges();
for (Change localChange : localChanges) {
intersection.add(localChangeList.getName(), localChangeList.getComment(), localChange);
}
}
return intersection;
}
private static class Intersection {
private final Map<String, String> myLists;
private final MultiMap<String, Change> myChangesSubset;
private Intersection() {
myLists = new HashMap<String, String>();
myChangesSubset = new MultiMap<String, Change>();
}
public void add(@NotNull final String listName, @Nullable final String comment, final Change change) {
myChangesSubset.putValue(listName, change);
final String commentToPut = comment == null ? listName : comment;
myLists.put(listName, commentToPut);
}
public String getComment(final String listName) {
return myLists.get(listName);
}
public MultiMap<String, Change> getChangesSubset() {
return myChangesSubset;
}
}
}
@@ -34,6 +34,7 @@ import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangesUtil;
@@ -66,10 +67,8 @@ import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.ISVNReporter;
import org.tmatesoft.svn.core.io.ISVNReporterBaton;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.SVNCommitClient;
import org.tmatesoft.svn.core.wc.SVNCopyClient;
import org.tmatesoft.svn.core.wc.SVNCopySource;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import javax.swing.*;
import javax.swing.tree.TreeNode;
@@ -88,9 +87,6 @@ public class RepositoryBrowserDialog extends DialogWrapper {
protected final SvnVcs myVCS;
private RepositoryBrowserComponent myRepositoryBrowser;
@NonNls public static final String COPY_OF_PREFIX = "CopyOf";
@NonNls public static final String NEW_FOLDER_POSTFIX = "NewFolder";
private final DeleteAction myDeleteAction;
private AnAction copyUrlAction;
private AnAction mkDirAction;
@@ -807,7 +803,7 @@ public class RepositoryBrowserDialog extends DialogWrapper {
}
}
private boolean doDelete(final SVNURL url, final String comment) {
final SVNException[] exception = new SVNException[1];
final Ref<Exception> exception = new Ref<Exception>();
final Project project = myBrowserComponent.getProject();
Runnable command = new Runnable() {
public void run() {
@@ -817,19 +813,18 @@ public class RepositoryBrowserDialog extends DialogWrapper {
}
SvnVcs vcs = SvnVcs.getInstance(project);
try {
SVNCommitClient committer = vcs.createCommitClient();
committer.doDelete(new SVNURL[]{url}, comment);
vcs.getFactoryFromSettings().createDeleteClient().delete(url, comment);
}
catch (SVNException e) {
exception[0] = e;
catch (VcsException e) {
exception.set(e);
}
}
};
ProgressManager.getInstance().runProcessWithProgressSynchronously(command, SvnBundle.message("progress.title.browser.delete"), false, project);
if (exception[0] != null) {
Messages.showErrorDialog(exception[0].getMessage(), SvnBundle.message("message.text.error"));
if (!exception.isNull()) {
Messages.showErrorDialog(exception.get().getMessage(), SvnBundle.message("message.text.error"));
}
return exception[0] == null;
return exception.isNull();
}
}
@@ -1001,7 +996,7 @@ public class RepositoryBrowserDialog extends DialogWrapper {
}
protected static void doMkdir(final SVNURL url, final String comment, final Project project) {
final SVNException[] exception = new SVNException[1];
final Ref<Exception> exception = new Ref<Exception>();
Runnable command = new Runnable() {
public void run() {
ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
@@ -1009,23 +1004,23 @@ public class RepositoryBrowserDialog extends DialogWrapper {
progress.setText(SvnBundle.message("progress.text.browser.creating", url.toString()));
}
SvnVcs vcs = SvnVcs.getInstance(project);
SvnTarget target = SvnTarget.fromURL(url);
try {
SVNCommitClient committer = vcs.createCommitClient();
committer.doMkDir(new SVNURL[] {url}, comment);
vcs.getFactoryFromSettings().createBrowseClient().createDirectory(target, comment, false);
}
catch (SVNException e) {
exception[0] = e;
catch (VcsException e) {
exception.set(e);
}
}
};
ProgressManager.getInstance().runProcessWithProgressSynchronously(command, SvnBundle.message("progress.text.create.remote.folder"), false, project);
if (exception[0] != null) {
Messages.showErrorDialog(exception[0].getMessage(), SvnBundle.message("message.text.error"));
if (!exception.isNull()) {
Messages.showErrorDialog(exception.get().getMessage(), SvnBundle.message("message.text.error"));
}
}
private void doCopy(final SVNURL src, final SVNURL dst, final boolean move, final String comment) {
final SVNException[] exception = new SVNException[1];
final Ref<Exception> exception = new Ref<Exception>();
Runnable command = new Runnable() {
public void run() {
ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
@@ -1035,19 +1030,18 @@ public class RepositoryBrowserDialog extends DialogWrapper {
}
SvnVcs vcs = SvnVcs.getInstance(myProject);
try {
SVNCopyClient committer = vcs.createCopyClient();
final SVNCopySource[] copySource = new SVNCopySource[] {new SVNCopySource(SVNRevision.HEAD, SVNRevision.HEAD, src)};
committer.doCopy(copySource, dst, move, true, true, comment, null);
vcs.getFactoryFromSettings().createCopyMoveClient().copy(SvnTarget.fromURL(src), SvnTarget.fromURL(dst), SVNRevision.HEAD, true,
move, comment, null);
}
catch (SVNException e) {
exception[0] = e;
catch (VcsException e) {
exception.set(e);
}
}
};
String progressTitle = move ? SvnBundle.message("progress.title.browser.move") : SvnBundle.message("progress.title.browser.copy");
ProgressManager.getInstance().runProcessWithProgressSynchronously(command, progressTitle, false, myProject);
if (exception[0] != null) {
Messages.showErrorDialog(exception[0].getMessage(), SvnBundle.message("message.text.error"));
if (!exception.isNull()) {
Messages.showErrorDialog(exception.get().getMessage(), SvnBundle.message("message.text.error"));
}
}
@@ -1184,16 +1178,6 @@ public class RepositoryBrowserDialog extends DialogWrapper {
return;
}
final Collection<Change> changesList = changes.values();
/*final Collection<Change> changesListConverted = new ArrayList<Change>(changesList.size());
for (Change change : changesList) {
final FilePath path = ChangesUtil.getFilePath(change);
final Change newChange = new Change(
new UrlContentRevision(change.getBeforeRevision(),
FilePathImpl.createNonLocal(SVNPathUtil.append(sourceUrl.toString(), path.getName()), path.isDirectory()), revision),
new UrlContentRevision(change.getAfterRevision(),
FilePathImpl.createNonLocal(SVNPathUtil.append(targetUrl.toString(), path.getName()), path.isDirectory()), revision));
changesListConverted.add(newChange);
}*/
final String title = SvnBundle.message("repository.browser.compare.title", sourceTitle, targetTitle);
SwingUtilities.invokeLater(new Runnable() {
@@ -1231,10 +1215,6 @@ public class RepositoryBrowserDialog extends DialogWrapper {
}
}
public void setDefaultExpander(final NotNullFunction<RepositoryBrowserComponent, Expander> expanderFactory) {
myRepositoryBrowser.setLazyLoadingExpander(expanderFactory);
}
private static class UrlContentRevision implements ContentRevision {
private final ContentRevision myContentRevision;
private final FilePath myPath;
@@ -75,7 +75,7 @@ public class SSHCredentialsDialog extends DialogWrapper implements ActionListene
@NonNls private static final String HELP_ID = "vcs.subversion.authentication";
private boolean myKeyFileEmptyOrCorrect;
protected SSHCredentialsDialog(Project project,
public SSHCredentialsDialog(Project project,
String realm,
String userName,
boolean allowSave,
@@ -39,7 +39,7 @@ public class ServerSSLDialog extends DialogWrapper {
private int myResult;
@NonNls public static final String ALGORITHM_SHA1 = "SHA1";
protected ServerSSLDialog(final Project project, X509Certificate cert, boolean store) {
public ServerSSLDialog(final Project project, X509Certificate cert, boolean store) {
super(project, true);
myCertificate = cert;
myResult = ISVNAuthenticationProvider.REJECTED;
@@ -18,6 +18,7 @@ package org.jetbrains.idea.svn.dialogs;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValue;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI;
import com.intellij.openapi.vcs.persistent.SmallMapSerializer;
@@ -33,7 +34,6 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.history.CopyData;
import org.jetbrains.idea.svn.history.FirstInBranch;
import org.tmatesoft.svn.core.SVNException;
import java.io.DataInput;
import java.io.DataOutput;
@@ -43,7 +43,7 @@ import java.util.*;
public class SvnBranchPointsCalculator {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.dialogs.SvnBranchPointsCalculator");
private FactsCalculator<KeyData, WrapperInvertor<BranchCopyData>, SVNException> myCalculator;
private FactsCalculator<KeyData, WrapperInvertor, VcsException> myCalculator;
private PersistentHolder myPersistentHolder;
private File myFile;
private final Project myProject;
@@ -58,19 +58,19 @@ public class SvnBranchPointsCalculator {
}
public void activate() {
ValueHolder<WrapperInvertor<BranchCopyData>, KeyData> cache = null;
ValueHolder<WrapperInvertor, KeyData> cache = null;
myPersistentHolder = new PersistentHolder(myFile);
cache = new ValueHolder<WrapperInvertor<BranchCopyData>, KeyData>() {
public WrapperInvertor<BranchCopyData> getValue(KeyData dataHolder) {
final WrapperInvertor<BranchCopyData> result =
cache = new ValueHolder<WrapperInvertor, KeyData>() {
public WrapperInvertor getValue(KeyData dataHolder) {
final WrapperInvertor result =
myPersistentHolder.getBestHit(dataHolder.getRepoUrl(), dataHolder.getSourceUrl(), dataHolder.getTargetUrl());
if (LOG.isDebugEnabled()) {
LOG.debug("Persistent for: " + dataHolder.toString() + " returned: " + (result == null ? null : result.toString()));
}
return result;
}
public void setValue(WrapperInvertor<BranchCopyData> value, KeyData dataHolder) {
public void setValue(WrapperInvertor value, KeyData dataHolder) {
if (LOG.isDebugEnabled()) {
LOG.debug("Put into persistent: key: " + dataHolder.toString() + " value: " + value.toString());
}
@@ -78,7 +78,7 @@ public class SvnBranchPointsCalculator {
}
};
myCalculator = new FactsCalculator<KeyData, WrapperInvertor<BranchCopyData>, SVNException>(
myCalculator = new FactsCalculator<KeyData, WrapperInvertor, VcsException>(
myProject, "Looking for branch origin", cache, new Loader(myProject));
}
@@ -118,11 +118,11 @@ public class SvnBranchPointsCalculator {
}
}
public static class WrapperInvertor<T extends Invertor<T>> {
private final T myWrapped;
public static class WrapperInvertor {
private final BranchCopyData myWrapped;
private final boolean myInvertedSense;
public WrapperInvertor(boolean invertedSense, T wrapped) {
public WrapperInvertor(boolean invertedSense, BranchCopyData wrapped) {
myInvertedSense = invertedSense;
myWrapped = wrapped;
}
@@ -131,15 +131,15 @@ public class SvnBranchPointsCalculator {
return myInvertedSense;
}
public T getWrapped() {
public BranchCopyData getWrapped() {
return myWrapped;
}
public T getTrue() {
public BranchCopyData getTrue() {
return myInvertedSense ? myWrapped.invertSelf() : myWrapped;
}
public T inverted() {
public BranchCopyData inverted() {
return myWrapped.invertSelf();
}
@@ -200,7 +200,7 @@ public class SvnBranchPointsCalculator {
}
@Nullable
public WrapperInvertor<BranchCopyData> getBestHit(final String repoUrl, final String source, final String target) {
public WrapperInvertor getBestHit(final String repoUrl, final String source, final String target) {
final List<String> keys;
synchronized (myLock) {
keys = (List<String>) myForSearchMap.get(repoUrl);
@@ -217,14 +217,14 @@ public class SvnBranchPointsCalculator {
final boolean sourceIsOut = sourceMatching == null;
if (sourceIsOut || targetMatching == null) {
// if found by "target" url - we correctly thought that target of copy is target
return sourceIsOut ? new WrapperInvertor<BranchCopyData>(false, map.get(targetMatching)) :
new WrapperInvertor<BranchCopyData>(true, map.get(sourceMatching));
return sourceIsOut ? new WrapperInvertor(false, map.get(targetMatching)) :
new WrapperInvertor(true, map.get(sourceMatching));
}
final BranchCopyData sourceData = map.get(sourceMatching);
final BranchCopyData targetData = map.get(targetMatching);
final boolean inverted = sourceData.getTargetRevision() > targetData.getTargetRevision();
return new WrapperInvertor<BranchCopyData>(inverted, inverted ? sourceData : targetData);
return new WrapperInvertor(inverted, inverted ? sourceData : targetData);
}
}
@@ -240,7 +240,7 @@ public class SvnBranchPointsCalculator {
}
}
private static class Loader implements ThrowableConvertor<KeyData, WrapperInvertor<BranchCopyData>, SVNException> {
private static class Loader implements ThrowableConvertor<KeyData, WrapperInvertor, VcsException> {
private SvnVcs myVcs;
private Loader(final Project project) {
@@ -248,8 +248,8 @@ public class SvnBranchPointsCalculator {
}
@Override
public WrapperInvertor<BranchCopyData> convert(final KeyData keyData) throws SVNException {
final TransparentlyFailedValue<CopyData, SVNException> consumer = new TransparentlyFailedValue<CopyData, SVNException>();
public WrapperInvertor convert(final KeyData keyData) throws VcsException {
final TransparentlyFailedValue<CopyData, VcsException> consumer = new TransparentlyFailedValue<CopyData, VcsException>();
new FirstInBranch(myVcs, keyData.getRepoUrl(), keyData.getTargetUrl(), keyData.getSourceUrl(), consumer).run();
final CopyData copyData = consumer.get();
@@ -263,7 +263,7 @@ public class SvnBranchPointsCalculator {
branchCopyData = new BranchCopyData(keyData.getTargetUrl(), copyData.getCopySourceRevision(), keyData.getSourceUrl(),
copyData.getCopyTargetRevision());
}
WrapperInvertor<BranchCopyData> invertor = new WrapperInvertor<BranchCopyData>(! correct, branchCopyData);
WrapperInvertor invertor = new WrapperInvertor(! correct, branchCopyData);
if (LOG.isDebugEnabled()) {
LOG.debug("Loader17 returned: for key: " + keyData.toString() + " result: " + (invertor.toString()));
}
@@ -305,7 +305,7 @@ public class SvnBranchPointsCalculator {
}
}
public static class BranchCopyData implements Invertor<BranchCopyData> {
public static class BranchCopyData {
private final String mySource;
private final String myTarget;
private final long mySourceRevision;
@@ -344,12 +344,8 @@ public class SvnBranchPointsCalculator {
}
}
private interface Invertor<T> {
T invertSelf();
}
public TaskDescriptor getFirstCopyPointTask(final String repoUID, final String sourceUrl, final String targetUrl,
final Consumer<TransparentlyFailedValueI<WrapperInvertor<BranchCopyData>, SVNException>> consumer) {
return myCalculator.getTask(new KeyData(repoUID, sourceUrl, targetUrl), consumer, SVNException.class);
final Consumer<TransparentlyFailedValueI<WrapperInvertor, VcsException>> consumer) {
return myCalculator.getTask(new KeyData(repoUID, sourceUrl, targetUrl), consumer, VcsException.class);
}
}
@@ -39,7 +39,7 @@ public class UserNameCredentialsDialog extends DialogWrapper implements Document
@NonNls
private static final String HELP_ID = "vcs.subversion.authentication";
protected UserNameCredentialsDialog(Project project) {
public UserNameCredentialsDialog(Project project) {
super(project, true);
setResizable(false);
}
@@ -23,7 +23,7 @@ import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.dialogs.RepositoryTreeNode;
import org.jetbrains.idea.svn.dialogs.SvnAuthenticationProvider;
import org.jetbrains.idea.svn.auth.SvnAuthenticationProvider;
import org.tmatesoft.svn.core.ISVNDirEntryHandler;
import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNErrorMessage;
@@ -30,6 +30,7 @@ import org.jetbrains.idea.svn.SvnStatusConvertor;
import org.jetbrains.idea.svn.api.BaseSvnClient;
import org.jetbrains.idea.svn.commandLine.*;
import org.jetbrains.idea.svn.history.SvnRepositoryContentRevision;
import org.jetbrains.idea.svn.status.SvnStatusHandler;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.wc.SVNRevision;
@@ -63,7 +64,7 @@ public class CmdDiffClient extends BaseSvnClient implements DiffClient {
parameters.add("--xml");
parameters.add("--summarize");
CommandExecutor executor = CommandUtil.execute(myVcs, target1, SvnCommandName.diff, parameters, null);
CommandExecutor executor = execute(myVcs, target1, SvnCommandName.diff, parameters, null);
return parseOutput(target1, target2, executor);
}
@@ -44,7 +44,7 @@ public class CmdHistoryClient extends BaseSvnClient implements HistoryClient {
prepareCommand(target, startRevision, endRevision, stopOnCopy, discoverChangedPaths, includeMergedRevisions, limit);
try {
CommandExecutor command = CommandUtil.execute(myVcs, target, SvnCommandName.log, parameters, null);
CommandExecutor command = execute(myVcs, target, SvnCommandName.log, parameters, null);
// TODO: handler should be called in parallel with command execution, but this will be in other thread
// TODO: check if that is ok for current handler implementation
parseOutput(command, handler);
@@ -18,31 +18,35 @@ package org.jetbrains.idea.svn.history;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI;
import com.intellij.util.Consumer;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.SvnUtil;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.commandLine.SvnBindException;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.wc.SVNLogClient;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import java.util.Map;
import java.util.Set;
// TODO: This one seem to determine revision in which branch was created - copied from trunk.
// TODO: This could be done in one command "svn log <folder> -r 0:HEAD --stop-on-copy --limit 1".
// TODO: Check for 1.7 and rewrite using this approach.
public class FirstInBranch implements Runnable {
private final SvnVcs myVcs;
private final String myBranchUrl;
private final String myTrunkUrl;
private final String myRepositoryRoot;
private final TransparentlyFailedValueI<CopyData, SVNException> myConsumer;
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.history.FirstInBranch");
public FirstInBranch(final SvnVcs vcs, final String repositoryRoot, final String branchUrl, final String trunkUrl,
final TransparentlyFailedValueI<CopyData, SVNException> consumer) {
@NotNull private final SvnVcs myVcs;
@NotNull private final String myBranchUrl;
@NotNull private final String myTrunkUrl;
@NotNull private final String myRepositoryRoot;
@NotNull private final TransparentlyFailedValueI<CopyData, VcsException> myConsumer;
public FirstInBranch(@NotNull SvnVcs vcs, @NotNull String repositoryRoot, @NotNull String branchUrl, @NotNull String trunkUrl,
@NotNull TransparentlyFailedValueI<CopyData, VcsException> consumer) {
myVcs = vcs;
myRepositoryRoot = repositoryRoot;
myConsumer = consumer;
@@ -51,71 +55,83 @@ public class FirstInBranch implements Runnable {
myTrunkUrl = relativePath(repositoryRoot, trunkUrl);
}
private String relativePath(final String parent, final String child) {
private static String relativePath(final String parent, final String child) {
String path = SVNPathUtil.getRelativePath(parent, child);
return path.startsWith("/") ? path : "/" + path;
return SvnUtil.ensureStartSlash(path);
}
private SVNURL getRepositoryRootUrl() {
SVNURL result = null;
try {
result = SvnUtil.createUrl(myRepositoryRoot);
}
catch (SvnBindException e) {
LOG.info(e);
myConsumer.fail(e);
}
return result;
}
public void run() {
final Set<SVNException> exceptions = new HashSet<SVNException>();
final boolean [] called = new boolean[1];
SVNURL repositoryRootUrl = getRepositoryRootUrl();
if (repositoryRootUrl != null) {
run(repositoryRootUrl);
}
}
private void run(@NotNull SVNURL url) {
try {
createTask(SVNURL.parseURIDecoded(myRepositoryRoot), exceptions).consume(new Consumer<CopyData>() {
run(url, new Consumer<CopyData>() {
@Override
public void consume(CopyData data) {
if (data != null) {
myConsumer.set(data);
called[0] = true;
}
}
});
}
catch (SVNException e) {
catch (VcsException e) {
LOG.info(e);
myConsumer.fail(e);
return;
}
if (called[0]) return;
if (! exceptions.isEmpty()) {
LOG.info("Wasn't able to find branch point, exception(s) below");
for (SVNException exception : exceptions) {
LOG.info(exception);
}
myConsumer.fail(exceptions.iterator().next());
} else if (! called[0]) {
myConsumer.set(null);
}
}
private Consumer<Consumer<CopyData>> createTask(final SVNURL branchURL, final Set<SVNException> exceptions) {
return new Consumer<Consumer<CopyData>>() {
public void consume(final Consumer<CopyData> copyDataConsumer) {
final SVNLogClient logClient = ApplicationManager.getApplication().runReadAction(new Computable<SVNLogClient>() {
@Override
public SVNLogClient compute() {
if (myVcs.getProject().isDisposed()) return null;
return myVcs.createLogClient();
}
});
if (logClient == null) return;
try {
logClient.doLog(branchURL, null, SVNRevision.UNDEFINED, SVNRevision.HEAD, SVNRevision.create(0), false, true, -1,
new MyLogEntryHandler(copyDataConsumer, myTrunkUrl, myBranchUrl));
} catch (SVNCancelException e) {
//
} catch (SVNException e) {
exceptions.add(e);
}
private void run(@NotNull SVNURL branchURL, @NotNull Consumer<CopyData> copyDataConsumer) throws VcsException {
final SvnTarget target = SvnTarget.fromURL(branchURL);
HistoryClient client = ApplicationManager.getApplication().runReadAction(new Computable<HistoryClient>() {
@Override
public HistoryClient compute() {
if (myVcs.getProject().isDisposed()) return null;
return myVcs.getFactory(target).createHistoryClient();
}
};
});
if (client == null) return;
try {
client.doLog(target, SVNRevision.HEAD, SVNRevision.create(0), false, true, false, -1, null,
new MyLogEntryHandler(copyDataConsumer, myTrunkUrl, myBranchUrl));
}
catch (SvnBindException e) {
// do not throw cancel exception as this means corresponding copy point is found (if progress indicator was not explicitly cancelled)
if (!e.contains(SVNErrorCode.CANCELLED)) {
throw e;
}
}
}
private static class MyLogEntryHandler implements ISVNLogEntryHandler {
private final SvnPathThroughHistoryCorrection myTrunkCorrector;
private final SvnPathThroughHistoryCorrection myBranchCorrector;
private final Consumer<CopyData> myCopyDataConsumer;
public MyLogEntryHandler(Consumer<CopyData> copyDataConsumer, String trunkUrl, String branchUrl) {
@NotNull private final SvnPathThroughHistoryCorrection myTrunkCorrector;
@NotNull private final SvnPathThroughHistoryCorrection myBranchCorrector;
@NotNull private final Consumer<CopyData> myCopyDataConsumer;
public MyLogEntryHandler(@NotNull Consumer<CopyData> copyDataConsumer, @NotNull String trunkUrl, @NotNull String branchUrl) {
myCopyDataConsumer = copyDataConsumer;
myTrunkCorrector = new SvnPathThroughHistoryCorrection(trunkUrl);
myBranchCorrector = new SvnPathThroughHistoryCorrection(branchUrl);
@@ -15,8 +15,11 @@
*/
package org.jetbrains.idea.svn.history;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.RootUrlInfo;
import org.jetbrains.idea.svn.SvnFileUrlMapping;
@@ -25,10 +28,14 @@ import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.SVNInfo;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import java.util.Map;
public class LatestExistentSearcher {
private static final Logger LOG = Logger.getInstance(LatestExistentSearcher.class);
private long myStartNumber;
private boolean myStartExistsKnown;
private final SVNURL myUrl;
@@ -67,24 +74,18 @@ public class LatestExistentSearcher {
final String urlRelativeString = myUrl.toString().substring(repRoot.toString().length());
final SVNRevision startRevision = SVNRevision.create(myStartNumber);
myVcs.createLogClient().doLog(existingParent, new String[]{""}, startRevision, startRevision, SVNRevision.HEAD, false, true, 0,
new ISVNLogEntryHandler() {
public void handleLogEntry(final SVNLogEntry logEntry) throws SVNException {
final Map changedPaths = logEntry.getChangedPaths();
for (Object o : changedPaths.values()) {
final SVNLogEntryPath path = (SVNLogEntryPath) o;
if ((path.getType() == 'D') && (urlRelativeString.equals(path.getPath()))) {
latest.set(logEntry.getRevision());
throw new SVNException(SVNErrorMessage.UNKNOWN_ERROR_MESSAGE);
}
}
}
});
SvnTarget target = SvnTarget.fromURL(existingParent, startRevision);
myVcs.getFactory(target).createHistoryClient()
.doLog(target, startRevision, SVNRevision.HEAD, false, true, false, 0, null, createHandler(latest, urlRelativeString));
}
}
catch (SVNException e) {
//
} finally {
LOG.info(e);
}
catch (VcsException e) {
LOG.info(e);
}
finally {
if (repository != null) {
repository.closeSession();
}
@@ -93,6 +94,22 @@ public class LatestExistentSearcher {
return latest.get().longValue();
}
@NotNull
private static ISVNLogEntryHandler createHandler(@NotNull final Ref<Long> latest, @NotNull final String urlRelativeString) {
return new ISVNLogEntryHandler() {
public void handleLogEntry(final SVNLogEntry logEntry) throws SVNException {
final Map changedPaths = logEntry.getChangedPaths();
for (Object o : changedPaths.values()) {
final SVNLogEntryPath path = (SVNLogEntryPath)o;
if ((path.getType() == 'D') && (urlRelativeString.equals(path.getPath()))) {
latest.set(logEntry.getRevision());
throw new SVNException(SVNErrorMessage.UNKNOWN_ERROR_MESSAGE);
}
}
}
};
}
public long getLatestExistent() {
if (! detectStartRevision()) return myStartNumber;
@@ -28,6 +28,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.vcsUtil.VcsUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.*;
import org.jetbrains.idea.svn.auth.SvnAuthenticationNotifier;
import org.jetbrains.idea.svn.commandLine.SvnBindException;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.wc.SVNRevision;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn.commandLine;
package org.jetbrains.idea.svn.info;
import com.intellij.execution.process.ProcessOutput;
import com.intellij.execution.process.ProcessOutputTypes;
@@ -26,9 +26,8 @@ import com.intellij.util.Consumer;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.portable.SvnExceptionWrapper;
import org.jetbrains.idea.svn.portable.SvnkitSvnWcClient;
import org.jetbrains.idea.svn.api.BaseSvnClient;
import org.jetbrains.idea.svn.commandLine.*;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.wc.ISVNInfoHandler;
import org.tmatesoft.svn.core.wc.SVNInfo;
@@ -52,16 +51,9 @@ import java.util.List;
* Date: 1/27/12
* Time: 12:59 PM
*/
public class SvnCommandLineInfoClient extends SvnkitSvnWcClient {
public class CmdInfoClient extends BaseSvnClient implements InfoClient {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.commandLine.SvnCommandLineInfoClient");
@NotNull private final SvnVcs myVcs;
public SvnCommandLineInfoClient(@NotNull final SvnVcs vcs) {
super(vcs);
myVcs = vcs;
}
private static final Logger LOG = Logger.getInstance(CmdInfoClient.class);
@Override
public void doInfo(File path, SVNRevision revision, boolean recursive, ISVNInfoHandler handler) throws SVNException {
@@ -98,7 +90,7 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient {
List<String> parameters = new ArrayList<String>();
fillParameters(path.getAbsolutePath(), pegRevision, revision, depth, parameters);
// TODO: Fix this check - update corresponding parameters in SvnWcClientI
// TODO: Fix this check - update corresponding parameters in InfoClient
CommandUtil.putChangeLists(parameters, changeLists);
parseResult(handler, base, execute(parameters, path));
@@ -117,7 +109,7 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient {
};
try {
CommandExecutor command = CommandUtil.execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.info, parameters, listener);
CommandExecutor command = execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.info, parameters, listener);
return command.getOutput();
}
@@ -217,10 +209,12 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient {
fillParameters(path, pegRevision, revision, depth, parameters);
CommandExecutor command;
try {
command = CommandUtil.execute(myVcs, SvnTarget.fromURL(url), SvnCommandName.info, parameters, null);
command = execute(myVcs, SvnTarget.fromURL(url), SvnCommandName.info, parameters, null);
}
catch (VcsException e) {
throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e);
catch (SvnBindException e) {
SVNErrorCode code = e.contains(SVNErrorCode.RA_ILLEGAL_URL) ? SVNErrorCode.RA_ILLEGAL_URL : SVNErrorCode.IO_ERROR;
throw new SVNException(SVNErrorMessage.create(code, e), e);
}
parseResult(handler, null, command.getOutput());
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn.portable;
package org.jetbrains.idea.svn.info;
import org.jetbrains.annotations.Nullable;
import org.tmatesoft.svn.core.SVNDepth;
@@ -13,12 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn.portable;
package org.jetbrains.idea.svn.info;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.wc.*;
import org.jetbrains.idea.svn.api.SvnClient;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.ISVNInfoHandler;
import org.tmatesoft.svn.core.wc.SVNInfo;
import org.tmatesoft.svn.core.wc.SVNRevision;
import java.io.File;
import java.util.Collection;
@@ -29,7 +34,7 @@ import java.util.Collection;
* Date: 1/20/12
* Time: 6:54 PM
*/
public interface SvnWcClientI extends SvnMarkerInterface {
public interface InfoClient extends SvnClient {
void doInfo(File path, SVNRevision revision, boolean recursive, ISVNInfoHandler handler) throws SVNException;
void doInfo(File path, SVNRevision pegRevision, SVNRevision revision, boolean recursive, ISVNInfoHandler handler) throws SVNException;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn.commandLine;
package org.jetbrains.idea.svn.info;
import org.tmatesoft.svn.core.SVNLock;
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn.commandLine;
package org.jetbrains.idea.svn.info;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.commandLine.CommandUtil;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
@@ -13,12 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn.commandLine;
package org.jetbrains.idea.svn.info;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.portable.IdeaSVNInfo;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.internal.wc.SVNConflictVersion;
import org.tmatesoft.svn.core.wc.*;
@@ -13,13 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn.portable;
package org.jetbrains.idea.svn.info;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnVcs;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.wc.*;
import org.jetbrains.idea.svn.api.BaseSvnClient;
import org.jetbrains.idea.svn.info.InfoClient;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.ISVNInfoHandler;
import org.tmatesoft.svn.core.wc.SVNInfo;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNWCClient;
import java.io.File;
import java.util.Collection;
@@ -30,13 +36,7 @@ import java.util.Collection;
* Date: 1/20/12
* Time: 6:55 PM
*/
public class SvnkitSvnWcClient implements SvnWcClientI {
@NotNull
private final SvnVcs myVcs;
public SvnkitSvnWcClient(@NotNull SvnVcs vcs) {
myVcs = vcs;
}
public class SvnKitInfoClient extends BaseSvnClient implements InfoClient {
public SVNWCClient getClient() {
return myVcs.createWCClient();
@@ -0,0 +1,90 @@
/*
* Copyright 2000-2014 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.integrate;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.vcs.CalledInAny;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.continuation.TaskDescriptor;
import com.intellij.util.continuation.Where;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.dialogs.MergeContext;
import java.util.ArrayList;
import java.util.List;
/**
* @author Konstantin Kolosovsky.
*/
public abstract class BaseMergeTask extends TaskDescriptor {
private static final Logger LOG = Logger.getInstance(BaseMergeTask.class);
@NotNull protected final MergeContext myMergeContext;
@NotNull protected final QuickMergeInteraction myInteraction;
public BaseMergeTask(@NotNull MergeContext mergeContext,
@NotNull QuickMergeInteraction interaction, String name,
@NotNull Where where) {
super(name, where);
myMergeContext = mergeContext;
myInteraction = interaction;
}
protected void insertMergeAll(final ContinuationContext context) {
final List<TaskDescriptor> queue = new ArrayList<TaskDescriptor>();
insertMergeAll(queue);
context.next(queue);
}
protected void insertMergeAll(final List<TaskDescriptor> queue) {
queue.add(new LocalChangesPromptTask(myMergeContext, myInteraction, true, null, null));
final MergeAllWithBranchCopyPointTask mergeAllExecutor = new MergeAllWithBranchCopyPointTask(myMergeContext, myInteraction);
queue.add(myMergeContext.getVcs().getSvnBranchPointsCalculator()
.getFirstCopyPointTask(myMergeContext.getWcInfo().getRepositoryRoot(), myMergeContext.getSourceUrl(),
myMergeContext.getWcInfo().getRootUrl(), mergeAllExecutor));
queue.add(mergeAllExecutor);
}
@CalledInAny
protected void finishWithError(final ContinuationContext context, final String message, final boolean isError) {
LOG.info((isError ? "Error: " : "Info: ") + message);
context.next(new TaskDescriptor(message, Where.AWT) {
@Override
public void run(ContinuationContext context) {
myInteraction.showErrors(message, isError);
context.cancelEverything();
}
});
}
@CalledInAny
protected void finishWithError(final ContinuationContext context, final String message, final List<VcsException> exceptions) {
if (exceptions != null) {
for (VcsException exception : exceptions) {
LOG.info(message, exception);
}
}
context.cancelEverything();
context.next(new TaskDescriptor(message, Where.AWT) {
@Override
public void run(ContinuationContext context) {
myInteraction.showErrors(message, exceptions);
}
});
}
}
@@ -0,0 +1,50 @@
/*
* Copyright 2000-2014 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.integrate;
import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.continuation.TaskDescriptor;
import com.intellij.util.continuation.Where;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.SvnUtil;
import org.jetbrains.idea.svn.dialogs.MergeContext;
import java.util.LinkedList;
import java.util.List;
/**
* @author Konstantin Kolosovsky.
*/
public class CheckRepositorySupportsMergeInfoTask extends BaseMergeTask {
public CheckRepositorySupportsMergeInfoTask(@NotNull MergeContext mergeContext, @NotNull QuickMergeInteraction interaction) {
super(mergeContext, interaction, "Checking repository capabilities", Where.POOLED);
}
@Override
public void run(ContinuationContext context) {
final List<TaskDescriptor> tasks = new LinkedList<TaskDescriptor>();
final boolean supportsMergeinfo = myMergeContext.getWcInfo().getFormat().supportsMergeInfo() && SvnUtil.checkRepositoryVersion15(
myMergeContext.getVcs(), myMergeContext.getSourceUrl());
if (!supportsMergeinfo) {
insertMergeAll(tasks);
}
else {
tasks.add(new MergeAllOrSelectedChooserTask(myMergeContext, myInteraction));
}
context.next(tasks);
}
}
@@ -105,7 +105,7 @@ public class CmdMergeClient extends BaseSvnClient implements MergeClient {
private void run(File destination, ISVNEventHandler handler, List<String> parameters) throws VcsException {
BaseUpdateCommandListener listener = new BaseUpdateCommandListener(CommandUtil.correctUpToExistingParent(destination), handler);
CommandUtil.execute(myVcs, SvnTarget.fromFile(destination), SvnCommandName.merge, parameters, listener);
execute(myVcs, SvnTarget.fromFile(destination), SvnCommandName.merge, parameters, listener);
listener.throwWrappedIfException();
}
@@ -0,0 +1,51 @@
/*
* Copyright 2000-2014 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.integrate;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
/**
* @author Konstantin Kolosovsky.
*/
public class Intersection {
private final Map<String, String> myLists;
private final MultiMap<String, Change> myChangesSubset;
public Intersection() {
myLists = new HashMap<String, String>();
myChangesSubset = new MultiMap<String, Change>();
}
public void add(@NotNull final String listName, @Nullable final String comment, final Change change) {
myChangesSubset.putValue(listName, change);
final String commentToPut = comment == null ? listName : comment;
myLists.put(listName, commentToPut);
}
public String getComment(final String listName) {
return myLists.get(listName);
}
public MultiMap<String, Change> getChangesSubset() {
return myChangesSubset;
}
}
@@ -0,0 +1,155 @@
/*
* Copyright 2000-2014 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.integrate;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FilePathImpl;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vcs.changes.ChangesUtil;
import com.intellij.openapi.vcs.changes.LocalChangeList;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
import com.intellij.util.FilePathByPathComparator;
import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.continuation.Where;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.dialogs.LocalChangesAction;
import org.jetbrains.idea.svn.dialogs.MergeContext;
import org.jetbrains.idea.svn.dialogs.SvnBranchPointsCalculator;
import org.jetbrains.idea.svn.history.SvnChangeList;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import java.io.File;
import java.util.*;
/**
* @author Konstantin Kolosovsky.
*/
public class LocalChangesPromptTask extends BaseMergeTask {
private final boolean myMergeAll;
@Nullable private final List<CommittedChangeList> myLists;
private final SvnBranchPointsCalculator.WrapperInvertor myCopyPoint;
public LocalChangesPromptTask(@NotNull MergeContext mergeContext, @NotNull QuickMergeInteraction interaction, final boolean mergeAll,
@Nullable final List<CommittedChangeList> lists,
@Nullable SvnBranchPointsCalculator.WrapperInvertor copyPoint) {
super(mergeContext, interaction, "local changes intersection check", Where.AWT);
myMergeAll = mergeAll;
myLists = lists;
myCopyPoint = copyPoint;
}
private static Intersection getMergeAllIntersection(List<LocalChangeList> localChangeLists) {
final Intersection intersection = new Intersection();
for (LocalChangeList localChangeList : localChangeLists) {
final Collection<Change> localChanges = localChangeList.getChanges();
for (Change localChange : localChanges) {
intersection.add(localChangeList.getName(), localChangeList.getComment(), localChange);
}
}
return intersection;
}
@Nullable
private File getLocalPath(final String relativeToRepoPath) {
// from source if not inverted
final String pathToCheck = SVNPathUtil.append(myMergeContext.getWcInfo().getRepositoryRoot(), relativeToRepoPath);
final SvnBranchPointsCalculator.BranchCopyData wrapped = myCopyPoint.getWrapped();
final String relativeInSource =
SVNPathUtil.getRelativePath(myCopyPoint.isInvertedSense() ? wrapped.getSource() : wrapped.getTarget(), pathToCheck);
if (StringUtil.isEmptyOrSpaces(relativeInSource)) return null;
final File local = new File(myMergeContext.getWcInfo().getPath(), relativeInSource);
return local;
}
@Override
public void run(ContinuationContext context) {
final Intersection intersection;
final ChangeListManager listManager = ChangeListManager.getInstance(myMergeContext.getProject());
final List<LocalChangeList> localChangeLists = listManager.getChangeListsCopy();
if (myMergeAll) {
intersection = getMergeAllIntersection(localChangeLists);
}
else {
intersection = checkIntersection(myLists, localChangeLists);
}
if (intersection == null || intersection.getChangesSubset().isEmpty()) return;
final LocalChangesAction action = myInteraction.selectLocalChangesAction(myMergeAll);
switch (action) {
// shelve
case shelve:
context.next(new ShelveLocalChangesTask(myMergeContext, myInteraction, intersection));
return;
// cancel
case cancel:
context.cancelEverything();
return;
// continue
case continueMerge:
return;
// inspect
case inspect:
// here's cast is due to generic's bug
@SuppressWarnings("unchecked") final Collection<Change> changes = (Collection<Change>)intersection.getChangesSubset().values();
final List<FilePath> paths = ChangesUtil.getPaths(changes);
Collections.sort(paths, FilePathByPathComparator.getInstance());
myInteraction.showIntersectedLocalPaths(paths);
context.cancelEverything();
return;
default:
}
}
@Nullable
private Intersection checkIntersection(@Nullable final List<CommittedChangeList> lists, List<LocalChangeList> localChangeLists) {
if (lists == null || lists.isEmpty()) {
return null;
}
final Set<FilePath> mergePaths = new HashSet<FilePath>();
for (CommittedChangeList list : lists) {
final SvnChangeList svnList = (SvnChangeList)list;
final List<String> paths = new ArrayList<String>(svnList.getAddedPaths());
paths.addAll(svnList.getChangedPaths());
paths.addAll(svnList.getDeletedPaths());
for (String path : paths) {
final File localPath = getLocalPath(path);
if (localPath != null) {
mergePaths.add(new FilePathImpl(localPath, false));
}
}
}
final Intersection intersection = new Intersection();
for (LocalChangeList localChangeList : localChangeLists) {
final Collection<Change> localChanges = localChangeList.getChanges();
for (Change localChange : localChanges) {
final FilePath before = localChange.getBeforeRevision() == null ? null : localChange.getBeforeRevision().getFile();
final FilePath after = localChange.getAfterRevision() == null ? null : localChange.getAfterRevision().getFile();
if ((before != null && mergePaths.contains(before)) || (after != null && mergePaths.contains(after))) {
intersection.add(localChangeList.getName(), localChangeList.getComment(), localChange);
}
}
}
return intersection;
}
}
@@ -0,0 +1,61 @@
/*
* Copyright 2000-2014 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.integrate;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.continuation.Where;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.dialogs.LoadRecentBranchRevisions;
import org.jetbrains.idea.svn.dialogs.MergeContext;
import org.jetbrains.idea.svn.dialogs.QuickMergeContentsVariants;
/**
* @author Konstantin Kolosovsky.
*/
public class MergeAllOrSelectedChooserTask extends BaseMergeTask {
public MergeAllOrSelectedChooserTask(@NotNull MergeContext mergeContext, @NotNull QuickMergeInteraction interaction) {
super(mergeContext, interaction, "merge source selector", Where.AWT);
}
@Override
public void run(final ContinuationContext context) {
final QuickMergeContentsVariants variant = myInteraction.selectMergeVariant();
if (QuickMergeContentsVariants.cancel == variant) return;
if (QuickMergeContentsVariants.all == variant) {
insertMergeAll(context);
return;
}
if (QuickMergeContentsVariants.showLatest == variant) {
final LoadRecentBranchRevisions loader = new LoadRecentBranchRevisions(myMergeContext, -1);
final ShowRecentInDialogTask dialog = new ShowRecentInDialogTask(myMergeContext, myInteraction, loader);
context.next(loader, dialog);
return;
}
final MergeCalculatorTask calculator;
try {
calculator = new MergeCalculatorTask(myMergeContext, myInteraction);
}
catch (VcsException e) {
finishWithError(context, e.getMessage(), true);
return;
}
context.next(myMergeContext.getVcs().getSvnBranchPointsCalculator()
.getFirstCopyPointTask(myMergeContext.getWcInfo().getRepositoryRoot(), myMergeContext.getWcInfo().getRootUrl(),
myMergeContext.getSourceUrl(), calculator), calculator);
}
}
@@ -0,0 +1,94 @@
/*
* Copyright 2000-2014 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.integrate;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI;
import com.intellij.util.Consumer;
import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.continuation.Where;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.dialogs.BranchMerger;
import org.jetbrains.idea.svn.dialogs.MergeContext;
import org.jetbrains.idea.svn.dialogs.SvnBranchPointsCalculator;
import org.jetbrains.idea.svn.update.UpdateEventHandler;
import org.tmatesoft.svn.core.SVNURL;
import java.io.File;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author Konstantin Kolosovsky.
*/
public class MergeAllWithBranchCopyPointTask extends BaseMergeTask
implements Consumer<TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor, VcsException>> {
private final AtomicReference<TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor, VcsException>> myData;
public MergeAllWithBranchCopyPointTask(@NotNull MergeContext mergeContext, @NotNull QuickMergeInteraction interaction) {
super(mergeContext, interaction, "merge all", Where.AWT);
myData = new AtomicReference<TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor, VcsException>>();
}
@Override
public void consume(TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor, VcsException> value) {
myData.set(value);
}
@Override
public void run(ContinuationContext context) {
SvnBranchPointsCalculator.WrapperInvertor invertor;
try {
final TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor, VcsException>
transparentlyFailedValueI = myData.get();
if (transparentlyFailedValueI == null) {
finishWithError(context, "Merge start wasn't found", true);
return;
}
invertor = transparentlyFailedValueI.get();
}
catch (VcsException e) {
finishWithError(context, "Merge start wasn't found", Collections.singletonList(e));
return;
}
if (invertor == null) {
finishWithError(context, "Merge start wasn't found", true);
return;
}
final boolean reintegrate = invertor.isInvertedSense();
if (reintegrate && (!myInteraction.shouldReintegrate(myMergeContext.getSourceUrl(), invertor.inverted().getTarget()))) {
context.cancelEverything();
return;
}
final MergerFactory mergerFactory = createBranchMergerFactory(reintegrate, invertor);
final String title = "Merging all from " + myMergeContext.getBranchName() + (reintegrate ? " (reintegrate)" : "");
context.next(new MergeTask(myMergeContext, myInteraction, mergerFactory, title));
}
private MergerFactory createBranchMergerFactory(final boolean reintegrate,
final SvnBranchPointsCalculator.WrapperInvertor invertor) {
return new MergerFactory() {
public IMerger createMerger(SvnVcs vcs, File target, UpdateEventHandler handler, SVNURL currentBranchUrl, String branchName) {
return new BranchMerger(vcs, currentBranchUrl, myMergeContext.getWcInfo().getUrl(), myMergeContext.getWcInfo().getPath(), handler,
reintegrate, myMergeContext.getBranchName(),
reintegrate ? invertor.getWrapped().getTargetRevision() : invertor.getWrapped().getSourceRevision());
}
};
}
}

Some files were not shown because too many files have changed in this diff Show More