[git] IDEA-103211 Support urls of different nature for a single remote

* GitHandler: set up both SSH and HTTP authentication environments for
  any remote command without analyzing urls.
* Let specify several urls to the GitHandler, and do it for pull, push,
  fetch, and ls-remote commands
* Modify Git interface for some commands to accept GitRemote instead of
  remoteName + remoteUrl, but still keep the previous signature in some
  cases where GitRemote object doesn't exist yet (e.g. in clone).
This commit is contained in:
Kirill Likhodedov
2015-03-20 19:43:23 +03:00
parent e9e28e9787
commit 30eba5a9e1
16 changed files with 187 additions and 240 deletions
@@ -402,7 +402,7 @@ public class CloudGitDeploymentRuntime extends CloudDeploymentRuntime {
protected void push(@NotNull CloudGitApplication application, @NotNull GitRepository repository, @NotNull String remote)
throws ServerRuntimeException {
GitCommandResult gitPushResult
= getGit().push(repository, remote, application.getGitUrl(), "master:master", createGitLineHandlerListener());
= getGit().push(repository, remote, application.getGitUrl(), "master:master", false, createGitLineHandlerListener());
checkGitResult(gitPushResult);
}
@@ -27,6 +27,7 @@ import git4idea.repo.GitRepository;
import git4idea.repo.GitRepositoryManager;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
public class GitPull extends GitMergeAction {
@@ -49,17 +50,16 @@ public class GitPull extends GitMergeAction {
GitRepository repository = repositoryManager.getRepositoryForRoot(dialog.gitRoot());
assert repository != null : "Repository can't be null for root " + dialog.gitRoot();
String remoteOrUrl = dialog.getRemote();
GitRemote remote = GitUtil.findRemoteByName(repository, remoteOrUrl);
final String url = (remote == null) ? remoteOrUrl : remote.getFirstUrl();
if (url == null) {
if (remoteOrUrl == null) {
return null;
}
GitRemote remote = GitUtil.findRemoteByName(repository, remoteOrUrl);
final List<String> urls = remote == null ? Collections.singletonList(remoteOrUrl) : remote.getUrls();
Computable<GitLineHandler> handlerProvider = new Computable<GitLineHandler>() {
@Override
public GitLineHandler compute() {
return dialog.makeHandler(url);
return dialog.makeHandler(urls);
}
};
return new DialogState(dialog.gitRoot(), GitBundle.message("pulling.title", dialog.getRemote()), handlerProvider);
@@ -157,18 +157,12 @@ class GitDeleteRemoteBranchOperation extends GitBranchOperation {
return GitCommandResult.error(error);
}
String remoteUrl = remote.getFirstUrl();
if (remoteUrl == null) {
LOG.warn("No urls are defined for remote: " + remote);
return GitCommandResult.error("There is no urls defined for remote " + remote.getName());
}
return pushDeletionNatively(repository, remoteName, remoteUrl, branchName);
return pushDeletionNatively(repository, remote, branchName);
}
@NotNull
private GitCommandResult pushDeletionNatively(@NotNull GitRepository repository, @NotNull String remoteName, @NotNull String url,
@NotNull String branchName) {
return myGit.push(repository, remoteName, url,":" + branchName);
private GitCommandResult pushDeletionNatively(@NotNull GitRepository repository, @NotNull GitRemote remote, @NotNull String branchName) {
return myGit.push(repository, remote, ":" + branchName, false, false, null);
}
@Nullable
@@ -20,8 +20,6 @@ import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitCommit;
import git4idea.GitLocalBranch;
import git4idea.GitRemoteBranch;
import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
import git4idea.reset.GitResetMode;
@@ -106,14 +104,10 @@ public interface Git {
GitCommandResult push(@NotNull GitRepository repository, @NotNull String remote, @Nullable String url, @NotNull String spec,
boolean updateTracking, @NotNull GitLineHandlerListener... listeners);
@NotNull
GitCommandResult push(@NotNull GitRepository repository, @NotNull String remote, @Nullable String url, @NotNull String spec,
@NotNull GitLineHandlerListener... listeners);
@NotNull
GitCommandResult push(@NotNull GitRepository repository,
@NotNull GitLocalBranch source,
@NotNull GitRemoteBranch target,
@NotNull GitRemote remote,
@NotNull String spec,
boolean force,
boolean updateTracking,
@Nullable String tagMode,
@@ -143,8 +137,10 @@ public interface Git {
List<GitCommit> history(@NotNull GitRepository repository, @NotNull String range);
@NotNull
GitCommandResult fetch(@NotNull GitRepository repository, @NotNull String url, @NotNull String remote,
@NotNull List<GitLineHandlerListener> listeners, String... params);
GitCommandResult fetch(@NotNull GitRepository repository,
@NotNull GitRemote remote,
@NotNull List<GitLineHandlerListener> listeners,
String... params);
@NotNull
GitCommandResult addRemote(@NotNull GitRepository repository, @NotNull String name, @NotNull String url);
@@ -22,6 +22,7 @@ import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
@@ -36,6 +37,7 @@ import com.intellij.util.EnvironmentUtil;
import com.intellij.util.EventDispatcher;
import com.intellij.util.ObjectUtils;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.URLUtil;
import com.intellij.util.net.HttpConfigurable;
import com.intellij.util.net.IdeaWideProxySelector;
@@ -52,11 +54,14 @@ import org.jetbrains.git4idea.ssh.GitSSHHandler;
import org.jetbrains.git4idea.ssh.GitXmlRpcSshService;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import static java.util.Collections.singletonList;
/**
* A handler for git commands
*/
@@ -81,7 +86,8 @@ public abstract class GitHandler {
private final File myWorkingDirectory;
private boolean myEnvironmentCleanedUp = true; // the flag indicating that environment has been cleaned up, by default is true because there is nothing to clean
private int myHandlerNo;
private int mySshHandler;
private int myHttpHandler;
private Processor<OutputStream> myInputProcessor; // The processor for stdin
// if true process might be cancelled
@@ -110,7 +116,7 @@ public abstract class GitHandler {
private long myStartTime; // git execution start timestamp
private static final long LONG_TIME = 10 * 1000;
@Nullable private String myUrl;
@Nullable private Collection<String> myUrls;
private boolean myHttpAuthFailed;
@@ -234,13 +240,16 @@ public abstract class GitHandler {
return file;
}
@SuppressWarnings("NullableProblems")
public void setUrl(@NotNull String url) {
myUrl = url;
setUrls(singletonList(url));
}
public void setUrls(@NotNull Collection<String> urls) {
myUrls = urls;
}
protected boolean isRemote() {
return myUrl != null;
return myUrls != null;
}
/**
@@ -435,46 +444,12 @@ public abstract class GitHandler {
}
// setup environment
GitRemoteProtocol remoteProtocol = GitRemoteProtocol.fromUrl(myUrl);
if (remoteProtocol == GitRemoteProtocol.SSH && myProjectSettings.isIdeaSsh()) {
GitXmlRpcSshService ssh = ServiceManager.getService(GitXmlRpcSshService.class);
myEnv.put(GitSSHHandler.GIT_SSH_ENV, ssh.getScriptPath().getPath());
myHandlerNo = ssh.registerHandler(new GitSSHGUIHandler(myProject));
myEnvironmentCleanedUp = false;
myEnv.put(GitSSHHandler.SSH_HANDLER_ENV, Integer.toString(myHandlerNo));
int port = ssh.getXmlRcpPort();
myEnv.put(GitSSHHandler.SSH_PORT_ENV, Integer.toString(port));
LOG.debug(String.format("handler=%s, port=%s", myHandlerNo, port));
final HttpConfigurable httpConfigurable = HttpConfigurable.getInstance();
boolean useHttpProxy = httpConfigurable.USE_HTTP_PROXY && !isSshUrlExcluded(httpConfigurable, myUrl);
myEnv.put(GitSSHHandler.SSH_USE_PROXY_ENV, String.valueOf(useHttpProxy));
if (useHttpProxy) {
myEnv.put(GitSSHHandler.SSH_PROXY_HOST_ENV, StringUtil.notNullize(httpConfigurable.PROXY_HOST));
myEnv.put(GitSSHHandler.SSH_PROXY_PORT_ENV, String.valueOf(httpConfigurable.PROXY_PORT));
boolean proxyAuthentication = httpConfigurable.PROXY_AUTHENTICATION;
myEnv.put(GitSSHHandler.SSH_PROXY_AUTHENTICATION_ENV, String.valueOf(proxyAuthentication));
if (proxyAuthentication) {
myEnv.put(GitSSHHandler.SSH_PROXY_USER_ENV, StringUtil.notNullize(httpConfigurable.PROXY_LOGIN));
myEnv.put(GitSSHHandler.SSH_PROXY_PASSWORD_ENV, StringUtil.notNullize(httpConfigurable.getPlainProxyPassword()));
}
if (isRemote()) {
setupHttpAuthenticator();
if (myProjectSettings.isIdeaSsh()) {
setupSshAuthenticator();
}
}
else if (remoteProtocol == GitRemoteProtocol.HTTP) {
GitHttpAuthService service = ServiceManager.getService(GitHttpAuthService.class);
myEnv.put(GitAskPassXmlRpcHandler.GIT_ASK_PASS_ENV, service.getScriptPath().getPath());
assert myUrl != null : "myUrl can't be null here";
GitHttpAuthenticator httpAuthenticator = service.createAuthenticator(myProject, myCommand, myUrl);
myHandlerNo = service.registerHandler(httpAuthenticator);
myEnvironmentCleanedUp = false;
myEnv.put(GitAskPassXmlRpcHandler.GIT_ASK_PASS_HANDLER_ENV, Integer.toString(myHandlerNo));
int port = service.getXmlRcpPort();
myEnv.put(GitAskPassXmlRpcHandler.GIT_ASK_PASS_PORT_ENV, Integer.toString(port));
LOG.debug(String.format("handler=%s, port=%s", myHandlerNo, port));
addAuthListener(httpAuthenticator);
}
myCommandLine.getEnvironment().clear();
myCommandLine.getEnvironment().putAll(myEnv);
// start process
@@ -493,9 +468,54 @@ public abstract class GitHandler {
}
}
protected static boolean isSshUrlExcluded(@NotNull HttpConfigurable httpConfigurable, @NotNull String url) {
String host = URLUtil.parseHostFromSshUrl(url);
return ((IdeaWideProxySelector)httpConfigurable.getOnlyBySettingsSelector()).isProxyException(host);
private void setupHttpAuthenticator() throws IOException {
GitHttpAuthService service = ServiceManager.getService(GitHttpAuthService.class);
myEnv.put(GitAskPassXmlRpcHandler.GIT_ASK_PASS_ENV, service.getScriptPath().getPath());
GitHttpAuthenticator httpAuthenticator = service.createAuthenticator(myProject, myCommand, ObjectUtils.assertNotNull(myUrls));
myHttpHandler = service.registerHandler(httpAuthenticator);
myEnvironmentCleanedUp = false;
myEnv.put(GitAskPassXmlRpcHandler.GIT_ASK_PASS_HANDLER_ENV, Integer.toString(myHttpHandler));
int port = service.getXmlRcpPort();
myEnv.put(GitAskPassXmlRpcHandler.GIT_ASK_PASS_PORT_ENV, Integer.toString(port));
LOG.debug(String.format("handler=%s, port=%s", myHttpHandler, port));
addAuthListener(httpAuthenticator);
}
private void setupSshAuthenticator() throws IOException {
GitXmlRpcSshService ssh = ServiceManager.getService(GitXmlRpcSshService.class);
myEnv.put(GitSSHHandler.GIT_SSH_ENV, ssh.getScriptPath().getPath());
mySshHandler = ssh.registerHandler(new GitSSHGUIHandler(myProject));
myEnvironmentCleanedUp = false;
myEnv.put(GitSSHHandler.SSH_HANDLER_ENV, Integer.toString(mySshHandler));
int port = ssh.getXmlRcpPort();
myEnv.put(GitSSHHandler.SSH_PORT_ENV, Integer.toString(port));
LOG.debug(String.format("handler=%s, port=%s", mySshHandler, port));
final HttpConfigurable httpConfigurable = HttpConfigurable.getInstance();
boolean useHttpProxy = httpConfigurable.USE_HTTP_PROXY && !isSshUrlExcluded(httpConfigurable, ObjectUtils.assertNotNull(myUrls));
myEnv.put(GitSSHHandler.SSH_USE_PROXY_ENV, String.valueOf(useHttpProxy));
if (useHttpProxy) {
myEnv.put(GitSSHHandler.SSH_PROXY_HOST_ENV, StringUtil.notNullize(httpConfigurable.PROXY_HOST));
myEnv.put(GitSSHHandler.SSH_PROXY_PORT_ENV, String.valueOf(httpConfigurable.PROXY_PORT));
boolean proxyAuthentication = httpConfigurable.PROXY_AUTHENTICATION;
myEnv.put(GitSSHHandler.SSH_PROXY_AUTHENTICATION_ENV, String.valueOf(proxyAuthentication));
if (proxyAuthentication) {
myEnv.put(GitSSHHandler.SSH_PROXY_USER_ENV, StringUtil.notNullize(httpConfigurable.PROXY_LOGIN));
myEnv.put(GitSSHHandler.SSH_PROXY_PASSWORD_ENV, StringUtil.notNullize(httpConfigurable.getPlainProxyPassword()));
}
}
}
protected static boolean isSshUrlExcluded(@NotNull final HttpConfigurable httpConfigurable, @NotNull Collection<String> urls) {
return ContainerUtil.exists(urls, new Condition<String>() {
@Override
public boolean value(String url) {
String host = URLUtil.parseHostFromSshUrl(url);
return ((IdeaWideProxySelector)httpConfigurable.getOnlyBySettingsSelector()).isProxyException(host);
}
});
}
private void addAuthListener(@NotNull final GitHttpAuthenticator authenticator) {
@@ -586,17 +606,11 @@ public abstract class GitHandler {
if (myEnvironmentCleanedUp) {
return;
}
GitRemoteProtocol remoteProtocol = GitRemoteProtocol.fromUrl(myUrl);
if (remoteProtocol == GitRemoteProtocol.SSH) {
GitXmlRpcSshService ssh = ServiceManager.getService(GitXmlRpcSshService.class);
myEnvironmentCleanedUp = true;
ssh.unregisterHandler(myHandlerNo);
}
else if (remoteProtocol == GitRemoteProtocol.HTTP) {
GitHttpAuthService service = ServiceManager.getService(GitHttpAuthService.class);
myEnvironmentCleanedUp = true;
service.unregisterHandler(myHandlerNo);
if (isRemote()) {
ServiceManager.getService(GitXmlRpcSshService.class).unregisterHandler(mySshHandler);
ServiceManager.getService(GitHttpAuthService.class).unregisterHandler(myHttpHandler);
}
myEnvironmentCleanedUp = true;
}
/**
@@ -22,6 +22,8 @@ import org.jetbrains.git4idea.http.GitAskPassXmlRpcHandler;
import org.jetbrains.git4idea.ssh.GitXmlRpcHandlerService;
import org.jetbrains.git4idea.util.ScriptGenerator;
import java.util.Collection;
/**
* Provides the authentication mechanism for Git HTTP connections.
*/
@@ -45,7 +47,9 @@ public abstract class GitHttpAuthService extends GitXmlRpcHandlerService<GitHttp
* Creates new {@link GitHttpAuthenticator} that will be requested to handle username and password requests from Git.
*/
@NotNull
public abstract GitHttpAuthenticator createAuthenticator(@NotNull Project project, @NotNull GitCommand command, @NotNull String url);
public abstract GitHttpAuthenticator createAuthenticator(@NotNull Project project,
@NotNull GitCommand command,
@NotNull Collection<String> urls);
/**
* Internal handler implementation class, it is made public to be accessible via XML RPC.
@@ -18,6 +18,8 @@ package git4idea.commands;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
/**
* @author Kirill Likhodedov
*/
@@ -25,8 +27,8 @@ class GitHttpAuthServiceImpl extends GitHttpAuthService {
@Override
@NotNull
public GitHttpAuthenticator createAuthenticator(@NotNull Project project, @NotNull GitCommand command, @NotNull String url) {
return new GitHttpGuiAuthenticator(project, command, url);
public GitHttpAuthenticator createAuthenticator(@NotNull Project project, @NotNull GitCommand command, @NotNull Collection<String> urls) {
return new GitHttpGuiAuthenticator(project, command, urls);
}
}
@@ -39,6 +39,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
@@ -57,9 +58,9 @@ class GitHttpGuiAuthenticator implements GitHttpAuthenticator {
private static final Logger LOG = Logger.getInstance(GitHttpGuiAuthenticator.class);
private static final Class<GitHttpAuthenticator> PASS_REQUESTER = GitHttpAuthenticator.class;
@NotNull private final Project myProject;
@NotNull private final String myTitle;
@NotNull private final String myUrlFromCommand;
@NotNull private final Project myProject;
@NotNull private final String myTitle;
@NotNull private final Collection<String> myUrlsFromCommand;
@Nullable private String myPassword;
@Nullable private String myPasswordKey;
@@ -69,10 +70,10 @@ class GitHttpGuiAuthenticator implements GitHttpAuthenticator {
@Nullable private GitHttpAuthDataProvider myDataProvider;
private boolean myWasCancelled;
GitHttpGuiAuthenticator(@NotNull Project project, @NotNull GitCommand command, @NotNull String url) {
GitHttpGuiAuthenticator(@NotNull Project project, @NotNull GitCommand command, @NotNull Collection<String> url) {
myProject = project;
myTitle = "Git " + StringUtil.capitalize(command.name());
myUrlFromCommand = url;
myUrlsFromCommand = url;
}
@Override
@@ -195,18 +196,25 @@ class GitHttpGuiAuthenticator implements GitHttpAuthenticator {
if (StringUtil.isEmptyOrSpaces(url)) {
// if Git doesn't specify the URL in the username/password query, we use the url from the Git command
// We only take the host, to avoid entering the same password for different repositories on the same host.
return adjustHttpUrl(getHost(myUrlFromCommand));
return adjustHttpUrl(getHost(myUrlsFromCommand));
}
return adjustHttpUrl(url);
}
@NotNull
private static String getHost(@NotNull String url) {
Couple<String> split = UriUtil.splitScheme(url);
String scheme = split.getFirst();
String urlItself = split.getSecond();
int pathStart = urlItself.indexOf("/");
return scheme + URLUtil.SCHEME_SEPARATOR + urlItself.substring(0, pathStart);
private static String getHost(@NotNull Collection<String> urls) {
String host = "unknown";
for (String url : urls) {
Couple<String> split = UriUtil.splitScheme(url);
String scheme = split.getFirst();
String urlItself = split.getSecond();
int pathStart = urlItself.indexOf("/");
host = scheme + URLUtil.SCHEME_SEPARATOR + urlItself.substring(0, pathStart);
if (scheme.startsWith("http")) {
return host;
}
}
return host;
}
/**
@@ -39,6 +39,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import static java.util.Collections.singleton;
/**
* Easy-to-use wrapper of common native Git commands.
* Most of them return result as {@link GitCommandResult}.
@@ -374,29 +376,47 @@ public class GitImpl implements Git {
@Override
@NotNull
public GitCommandResult push(@NotNull GitRepository repository, @NotNull String remote, @Nullable String url, @NotNull String spec,
boolean updateTracking, @NotNull GitLineHandlerListener... listeners) {
return doPush(repository, remote, url, spec, false, updateTracking, null, listeners);
public GitCommandResult push(@NotNull GitRepository repository,
@NotNull String remote,
@Nullable String url,
@NotNull String spec,
boolean updateTracking,
@NotNull GitLineHandlerListener... listeners) {
return doPush(repository, remote, singleton(url), spec, false, updateTracking, null, listeners);
}
@Override
@NotNull
public GitCommandResult push(@NotNull GitRepository repository,
@NotNull GitRemote remote,
@NotNull String spec,
boolean force,
boolean updateTracking,
@Nullable String tagMode,
GitLineHandlerListener... listeners) {
return doPush(repository, remote.getName(), remote.getPushUrls(), spec, force, updateTracking, tagMode, listeners);
}
@NotNull
private GitCommandResult doPush(@NotNull final GitRepository repository, @NotNull final String remote, @Nullable final String url,
@NotNull final String spec, final boolean force, final boolean updateTracking,
@Nullable final String tagMode,
@NotNull final GitLineHandlerListener... listeners) {
private GitCommandResult doPush(@NotNull final GitRepository repository,
@NotNull final String remoteName,
@NotNull final Collection<String> remoteUrls,
@NotNull final String spec,
final boolean force,
final boolean updateTracking,
@Nullable final String tagMode,
@NotNull final GitLineHandlerListener... listeners) {
return runCommand(new Computable<GitLineHandler>() {
@Override
public GitLineHandler compute() {
final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.PUSH);
if (url != null) {
h.setUrl(url);
}
h.setUrls(remoteUrls);
h.setSilent(false);
h.setStdoutSuppressed(false);
addListeners(h, listeners);
h.addProgressParameter();
h.addParameters("--porcelain");
h.addParameters(remote);
h.addParameters(remoteName);
h.addParameters(spec);
if (updateTracking) {
h.addParameters("--set-upstream");
@@ -412,31 +432,6 @@ public class GitImpl implements Git {
});
}
@Override
@NotNull
public GitCommandResult push(@NotNull GitRepository repository, @NotNull String remote, @Nullable String url, @NotNull String spec,
@NotNull GitLineHandlerListener... listeners) {
return push(repository, remote, url, spec, false, listeners);
}
@Override
@NotNull
public GitCommandResult push(@NotNull GitRepository repository, @NotNull GitLocalBranch source, @NotNull GitRemoteBranch target,
boolean force, boolean updateTracking, @Nullable String tagMode, GitLineHandlerListener... listeners) {
GitRemote remote = target.getRemote();
Collection<String> pushUrls = remote.getPushUrls(); // TODO handle the case with multiple pushurls with different protocols
String url;
if (pushUrls.isEmpty()) {
LOG.error("No urls or pushUrls are defined for " + remote);
url = null;
}
else {
url = pushUrls.iterator().next();
}
String spec = source.getFullName() + ":" + target.getNameForRemoteOperations();
return doPush(repository, remote.getName(), url, spec, force, updateTracking, tagMode, listeners);
}
@NotNull
@Override
public GitCommandResult show(@NotNull GitRepository repository, @NotNull String... params) {
@@ -476,16 +471,18 @@ public class GitImpl implements Git {
*/
@Override
@NotNull
public GitCommandResult fetch(@NotNull final GitRepository repository, @NotNull final String url, @NotNull final String remote,
@NotNull final List<GitLineHandlerListener> listeners, final String... params) {
public GitCommandResult fetch(@NotNull final GitRepository repository,
@NotNull final GitRemote remote,
@NotNull final List<GitLineHandlerListener> listeners,
final String... params) {
return runCommand(new Computable<GitLineHandler>() {
@Override
public GitLineHandler compute() {
final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.FETCH);
h.setSilent(false);
h.setStdoutSuppressed(false);
h.setUrl(url);
h.addParameters(remote);
h.setUrls(remote.getUrls());
h.addParameters(remote.getName());
h.addParameters(params);
h.addProgressParameter();
GitVcs vcs = GitVcs.getInstance(repository.getProject());
@@ -511,7 +508,7 @@ public class GitImpl implements Git {
public GitCommandResult lsRemote(@NotNull final Project project,
@NotNull final File workingDir,
@NotNull final String url) {
return doLsRemote(project, workingDir, url, url);
return doLsRemote(project, workingDir, url, singleton(url));
}
@NotNull
@@ -520,27 +517,22 @@ public class GitImpl implements Git {
@NotNull VirtualFile workingDir,
@NotNull GitRemote remote,
String... additionalParameters) {
return doLsRemote(project, VfsUtilCore.virtualToIoFile(workingDir), remote.getName(), remote.getFirstUrl(), additionalParameters);
return doLsRemote(project, VfsUtilCore.virtualToIoFile(workingDir), remote.getName(), remote.getUrls(), additionalParameters);
}
@NotNull
private GitCommandResult doLsRemote(@NotNull final Project project,
@NotNull final File workingDir,
@NotNull final String remoteId,
@Nullable final String authenticationUrl,
final String... additionalParameters) {
private static GitCommandResult doLsRemote(@NotNull final Project project,
@NotNull final File workingDir,
@NotNull final String remoteId,
@NotNull final Collection<String> authenticationUrls,
final String... additionalParameters) {
return run(new Computable<GitLineHandler>() {
@Override
public GitLineHandler compute() {
GitLineHandler h = new GitLineHandler(project, workingDir, GitCommand.LS_REMOTE);
h.addParameters(additionalParameters);
h.addParameters(remoteId);
if (authenticationUrl != null) {
h.setUrl(authenticationUrl);
}
else {
LOG.error("No valid URLs for remote " + remoteId);
}
h.setUrls(authenticationUrls);
return h;
}
});
@@ -1,43 +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 git4idea.commands;
import org.jetbrains.annotations.Nullable;
/**
* @author Kirill Likhodedov
*/
public enum GitRemoteProtocol {
GIT,
SSH,
HTTP;
@Nullable
public static GitRemoteProtocol fromUrl(@Nullable String url) {
if (url == null) {
return null;
}
url = url.toLowerCase();
if (url.startsWith("http")) {
return HTTP;
}
if (url.startsWith("git://")) { // "://" are there not to mix with scp-like syntax used for SSH: git@host.com/path/to.git
return GIT;
}
return SSH;
}
}
@@ -178,11 +178,11 @@ public class GitPullDialog extends DialogWrapper {
setOKActionEnabled(myBranchChooser.getMarkedElements().size() != 0);
}
public GitLineHandler makeHandler(@NotNull String url) {
public GitLineHandler makeHandler(@NotNull List<String> urls) {
GitLineHandler h = new GitLineHandler(myProject, gitRoot(), GitCommand.PULL);
// ignore merge failure for the pull
h.ignoreErrorCode(1);
h.setUrl(url);
h.setUrls(urls);
h.addProgressParameter();
h.addParameters("--no-stat");
if (myNoCommitCheckBox.isSelected()) {
@@ -368,7 +368,9 @@ public class GitPushOperation {
GitLineHandlerListener progressListener = GitStandardProgressAnalyzer.createListener(myProgressIndicator);
boolean setUpstream = pushSpec.getTarget().isNewBranchCreated() && !branchTrackingInfoIsSet(repository, sourceBranch);
String tagMode = myTagMode == null ? null : myTagMode.getArgument();
GitCommandResult res = myGit.push(repository, sourceBranch, targetBranch, myForce, setUpstream, tagMode, progressListener);
String spec = sourceBranch.getFullName() + ":" + targetBranch.getNameForRemoteOperations();
GitCommandResult res = myGit.push(repository, targetBranch.getRemote(), spec, myForce, setUpstream, tagMode, progressListener);
return new ResultWithOutput(res);
}
@@ -102,11 +102,7 @@ public class GitFetcher {
if (remote == null) {
return logError("Couldn't find remote with the name " + remoteName, null);
}
String url = remote.getFirstUrl();
if (url == null) {
return logError("URL is null for remote " + remote.getName(), null);
}
return fetchRemote(repository, remote, url, branch);
return fetchRemote(repository, remote, branch);
}
private static GitFetchResult logError(@NotNull String message, @Nullable String additionalInfo) {
@@ -123,16 +119,14 @@ public class GitFetcher {
}
GitRemote remote = fetchParams.getRemote();
String url = fetchParams.getUrl();
return fetchRemote(repository, remote, url, null);
return fetchRemote(repository, remote, null);
}
@NotNull
private GitFetchResult fetchRemote(@NotNull GitRepository repository,
@NotNull GitRemote remote,
@NotNull String url,
@Nullable String branch) {
return fetchNatively(repository, remote, url, branch);
return fetchNatively(repository, remote, branch);
}
// leaving this unused method, because the wanted behavior can change again
@@ -146,8 +140,7 @@ public class GitFetcher {
GitRemote remote = fetchParams.getRemote();
String remoteBranch = fetchParams.getRemoteBranch().getNameForRemoteOperations();
String url = fetchParams.getUrl();
return fetchNatively(repository, remote, url, remoteBranch);
return fetchNatively(repository, remote, remoteBranch);
}
@NotNull
@@ -167,14 +160,7 @@ public class GitFetcher {
}
GitRemote remote = trackInfo.getRemote();
String url = remote.getFirstUrl();
if (url == null) {
String message = "URL is null for remote " + remote.getName();
LOG.error(message);
return new FetchParams(GitFetchResult.error(new Exception(message)));
}
return new FetchParams(remote, trackInfo.getRemoteBranch(), url);
return new FetchParams(remote, trackInfo.getRemoteBranch());
}
@NotNull
@@ -185,7 +171,7 @@ public class GitFetcher {
LOG.error("URL is null for remote " + remote.getName());
continue;
}
GitFetchResult res = fetchNatively(repository, remote, url, null);
GitFetchResult res = fetchNatively(repository, remote, null);
res.addPruneInfo(fetchResult.getPrunedRefs());
fetchResult = res;
if (!fetchResult.isSuccess()) {
@@ -196,16 +182,15 @@ public class GitFetcher {
}
@NotNull
private static GitFetchResult fetchNatively(@NotNull GitRepository repository, @NotNull GitRemote remote, @NotNull String url,
@Nullable String branch) {
private static GitFetchResult fetchNatively(@NotNull GitRepository repository, @NotNull GitRemote remote, @Nullable String branch) {
Git git = ServiceManager.getService(Git.class);
String[] additionalParams = branch != null ?
new String[]{ getFetchSpecForBranch(branch, remote.getName()) } :
ArrayUtil.EMPTY_STRING_ARRAY;
GitFetchPruneDetector pruneDetector = new GitFetchPruneDetector();
GitCommandResult result =
git.fetch(repository, url, remote.getName(), Collections.<GitLineHandlerListener>singletonList(pruneDetector), additionalParams);
GitCommandResult result = git.fetch(repository, remote,
Collections.<GitLineHandlerListener>singletonList(pruneDetector), additionalParams);
GitFetchResult fetchResult;
if (result.success()) {
@@ -347,16 +332,14 @@ public class GitFetcher {
private GitRemote myRemote;
private GitRemoteBranch myRemoteBranch;
private GitFetchResult myError;
private String myUrl;
FetchParams(GitFetchResult error) {
myError = error;
}
FetchParams(GitRemote remote, GitRemoteBranch remoteBranch, String url) {
FetchParams(GitRemote remote, GitRemoteBranch remoteBranch) {
myRemote = remote;
myRemoteBranch = remoteBranch;
myUrl = url;
}
boolean isError() {
@@ -374,9 +357,5 @@ public class GitFetcher {
public GitRemoteBranch getRemoteBranch() {
return myRemoteBranch;
}
public String getUrl() {
return myUrl;
}
}
}
@@ -19,12 +19,11 @@ import com.intellij.dvcs.push.PushSpec;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Trinity;
import com.intellij.util.containers.ContainerUtil;
import git4idea.GitLocalBranch;
import git4idea.GitRemoteBranch;
import git4idea.commands.Git;
import git4idea.commands.GitCommandResult;
import git4idea.commands.GitImpl;
import git4idea.commands.GitLineHandlerListener;
import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
import git4idea.test.GitTestUtil;
import git4idea.update.GitUpdateResult;
@@ -80,20 +79,20 @@ public class GitPushOperationMultiRepoTest extends GitPushOperationBaseTest {
private static class FailingPushGit extends GitImpl {
private Condition<GitRepository> myPushShouldFail;
@NotNull
@Override
@NotNull
public GitCommandResult push(@NotNull GitRepository repository,
@NotNull GitLocalBranch source,
@NotNull GitRemoteBranch target,
@NotNull GitRemote remote,
@NotNull String spec,
boolean force,
boolean updateTracking,
@Nullable String tagMode,
GitLineHandlerListener... listeners) {
if (myPushShouldFail.value(repository)) {
return new GitCommandResult(false, 128, Collections.singletonList("Failed to push to " + target.getName()),
return new GitCommandResult(false, 128, Collections.singletonList("Failed to push to " + remote.getName()),
Collections.<String>emptyList(), null);
}
return super.push(repository, source, target, force, updateTracking, tagMode, listeners);
return super.push(repository, remote, spec, force, updateTracking, tagMode, listeners);
}
}
@@ -123,7 +122,7 @@ public class GitPushOperationMultiRepoTest extends GitPushOperationBaseTest {
GitPushRepoResult result2 = result.getResults().get(myCommunity);
assertResult(GitPushRepoResult.Type.ERROR, -1, "master", "origin/master", null, result1);
assertEquals("Error text is incorrect", "Failed to push to origin/master", result1.getError());
assertEquals("Error text is incorrect", "Failed to push to origin", result1.getError());
assertResult(GitPushRepoResult.Type.SUCCESS, 1, "master", "origin/master", null, result2);
}
@@ -21,6 +21,8 @@ import git4idea.commands.GitHttpAuthService;
import git4idea.commands.GitHttpAuthenticator;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
/**
* @author Kirill Likhodedov
*/
@@ -30,23 +32,21 @@ public class GitHttpAuthTestService extends GitHttpAuthService {
@NotNull
@Override
public String askPassword(@NotNull String url) {
throw new IllegalStateException("Authenticator was not registered");
return "";
}
@NotNull
@Override
public String askUsername(@NotNull String url) {
throw new IllegalStateException("Authenticator was not registered");
return "";
}
@Override
public void saveAuthData() {
throw new IllegalStateException("Authenticator was not registered");
}
@Override
public void forgetPassword() {
throw new IllegalStateException("Authenticator was not registered");
}
@Override
@@ -59,7 +59,7 @@ public class GitHttpAuthTestService extends GitHttpAuthService {
@NotNull
@Override
public GitHttpAuthenticator createAuthenticator(@NotNull Project project, @NotNull GitCommand command, @NotNull String url) {
public GitHttpAuthenticator createAuthenticator(@NotNull Project project, @NotNull GitCommand command, @NotNull Collection<String> urls) {
return myAuthenticator;
}
@@ -67,7 +67,7 @@ public abstract class GithubCreatePullRequestTestBase extends GithubTest {
protected void deleteRemoteBranch() {
GitRepository repository = GithubUtil.getGitRepository(myProject, myProjectRoot);
if (repository != null) {
ServiceManager.getService(Git.class).push(repository, "origin", PROJECT_URL, ":" + BRANCH_NAME);
ServiceManager.getService(Git.class).push(repository, "origin", PROJECT_URL, ":" + BRANCH_NAME, false);
}
}