mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'svn_18_3'
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
+309
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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.*;
|
||||
|
||||
+168
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
-37
@@ -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);
|
||||
+3
-3
@@ -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");
|
||||
+1
-3
@@ -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;
|
||||
+3
-4
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-5
@@ -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;
|
||||
}
|
||||
+91
-35
@@ -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();
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
-704
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-26
@@ -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;
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
+16
-19
@@ -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;
|
||||
|
||||
+25
-29
@@ -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);
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+12
-18
@@ -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());
|
||||
+1
-1
@@ -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;
|
||||
+9
-4
@@ -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;
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+2
-1
@@ -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;
|
||||
+1
-2
@@ -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.*;
|
||||
+11
-11
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+50
@@ -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;
|
||||
}
|
||||
}
|
||||
+61
@@ -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);
|
||||
}
|
||||
}
|
||||
+94
@@ -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
Reference in New Issue
Block a user