mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
IDEA-78086 Git: Push to HTTP/HTTPS repositories doesn't work
Root cause: 1. git push <url> <spec> used incorrect fetch spec (containing refs/remotes/...) 2. git push <url> doesn't update remote ref Solution: Instead of using PushCommand.call use the almost copy of this method, but calling Transport.openAll not with remote name, but with pre-generated remote config. This remote config is a default remote config read from .git/config, with the change in URL. [reviewed by irengrig]
This commit is contained in:
@@ -42,6 +42,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.ProxySelector;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -91,6 +92,10 @@ public final class GitHttpAdapter {
|
||||
logException(repository, remote.getName(), remoteUrl, e, "fetching");
|
||||
return GitFetchResult.error(e);
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
logException(repository, remote.getName(), remoteUrl, e, "fetching");
|
||||
return GitFetchResult.error(e);
|
||||
}
|
||||
return new GitFetchResult(resultType);
|
||||
}
|
||||
|
||||
@@ -121,7 +126,7 @@ public final class GitHttpAdapter {
|
||||
try {
|
||||
final Git git = convertToGit(repository);
|
||||
final GitHttpCredentialsProvider provider = new GitHttpCredentialsProvider(repository.getProject(), remoteUrl);
|
||||
GitHttpRemoteCommand.Push pushCommand = new GitHttpRemoteCommand.Push(git, provider, remoteUrl, convertRefSpecs(remote.getPushRefSpecs()));
|
||||
GitHttpRemoteCommand.Push pushCommand = new GitHttpRemoteCommand.Push(git, provider, remote.getName(), remoteUrl);
|
||||
GeneralResult result = callWithAuthRetry(pushCommand);
|
||||
GitSimplePushResult pushResult = pushCommand.getResult();
|
||||
if (pushResult == null) {
|
||||
@@ -144,6 +149,10 @@ public final class GitHttpAdapter {
|
||||
logException(repository, remote.getName(), remoteUrl, e, "pushing");
|
||||
return makeErrorResultFromException(e);
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
logException(repository, remote.getName(), remoteUrl, e, "pushing");
|
||||
return makeErrorResultFromException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -162,6 +171,10 @@ public final class GitHttpAdapter {
|
||||
LOG.info("Exception while cloning " + url + " to " + directory, e);
|
||||
return GitFetchResult.error(e);
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
LOG.info("Exception while cloning " + url + " to " + directory, e);
|
||||
return GitFetchResult.error(e);
|
||||
}
|
||||
return new GitFetchResult(resultType);
|
||||
}
|
||||
|
||||
@@ -191,7 +204,7 @@ public final class GitHttpAdapter {
|
||||
* If user enters incorrect data, he has 2 more attempts to go before failure.
|
||||
* Cleanups are executed after each incorrect attempt to enter password, and after other retriable actions.
|
||||
*/
|
||||
private static GeneralResult callWithAuthRetry(@NotNull GitHttpRemoteCommand command) throws InvalidRemoteException, IOException {
|
||||
private static GeneralResult callWithAuthRetry(@NotNull GitHttpRemoteCommand command) throws InvalidRemoteException, IOException, URISyntaxException {
|
||||
ProxySelector defaultProxySelector = ProxySelector.getDefault();
|
||||
if (GitHttpProxySupport.shouldUseProxy()) {
|
||||
ProxySelector.setDefault(GitHttpProxySupport.newProxySelector());
|
||||
|
||||
@@ -17,18 +17,27 @@ package git4idea.jgit;
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import git4idea.push.GitSimplePushResult;
|
||||
import org.eclipse.jgit.JGitText;
|
||||
import org.eclipse.jgit.api.CloneCommand;
|
||||
import org.eclipse.jgit.api.FetchCommand;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.PushCommand;
|
||||
import org.eclipse.jgit.api.errors.InvalidRemoteException;
|
||||
import org.eclipse.jgit.transport.PushResult;
|
||||
import org.eclipse.jgit.transport.RefSpec;
|
||||
import org.eclipse.jgit.transport.RemoteRefUpdate;
|
||||
import org.eclipse.jgit.api.errors.JGitInternalException;
|
||||
import org.eclipse.jgit.errors.NotSupportedException;
|
||||
import org.eclipse.jgit.errors.TransportException;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.transport.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -40,7 +49,7 @@ interface GitHttpRemoteCommand {
|
||||
|
||||
String getUrl();
|
||||
void setUrl(String url);
|
||||
void run() throws InvalidRemoteException;
|
||||
void run() throws InvalidRemoteException, URISyntaxException;
|
||||
void cleanup();
|
||||
GitHttpCredentialsProvider getCredentialsProvider();
|
||||
|
||||
@@ -136,25 +145,41 @@ interface GitHttpRemoteCommand {
|
||||
private final Git myGit;
|
||||
private final GitHttpCredentialsProvider myCredentialsProvider;
|
||||
private GitSimplePushResult myPushResult;
|
||||
private String myRemoteName;
|
||||
private String myUrl;
|
||||
private final List<RefSpec> myRefSpecs;
|
||||
|
||||
Push(@NotNull Git git, @NotNull GitHttpCredentialsProvider credentialsProvider, String url, List<RefSpec> refSpecs) {
|
||||
Push(@NotNull Git git, @NotNull GitHttpCredentialsProvider credentialsProvider, @NotNull String remoteName, @NotNull String url) {
|
||||
myGit = git;
|
||||
myCredentialsProvider = credentialsProvider;
|
||||
myRemoteName = remoteName;
|
||||
myUrl = url;
|
||||
myRefSpecs = refSpecs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() throws InvalidRemoteException {
|
||||
public void run() throws InvalidRemoteException, URISyntaxException {
|
||||
PushCommand pushCommand = myGit.push();
|
||||
if (myUrl != null) {
|
||||
pushCommand.setRemote(myUrl);
|
||||
pushCommand.setRefSpecs(myRefSpecs);
|
||||
}
|
||||
pushCommand.setRemote(myRemoteName);
|
||||
pushCommand.setCredentialsProvider(myCredentialsProvider);
|
||||
Iterable<PushResult> results = pushCommand.call();
|
||||
|
||||
/*
|
||||
Need to push to remote NAME (to let push update the remote reference), but to probably another URL.
|
||||
So constructing RemoteConfig based on the original config for the remote, but with other url.
|
||||
No need in fetch urls => just removing them.
|
||||
Remove all push urls (we don't support pushing to multiple urls anyway yet), leaving only single correct url.
|
||||
Then pass the url to the push command.
|
||||
*/
|
||||
RemoteConfig rc = new RemoteConfig(myGit.getRepository().getConfig(), myRemoteName);
|
||||
List<URIish> uris = new ArrayList<URIish>(rc.getURIs());
|
||||
for (URIish uri : uris) {
|
||||
rc.removeURI(uri);
|
||||
}
|
||||
uris = new ArrayList<URIish>(rc.getPushURIs());
|
||||
for (URIish uri : uris) {
|
||||
rc.removePushURI(uri);
|
||||
}
|
||||
rc.addPushURI(new URIish(myUrl));
|
||||
|
||||
Iterable<PushResult> results = call(pushCommand, rc);
|
||||
myPushResult = analyzeResults(results);
|
||||
}
|
||||
|
||||
@@ -214,6 +239,101 @@ interface GitHttpRemoteCommand {
|
||||
return GitSimplePushResult.error(errorReport.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
A copy-paste from org.eclipse.jgit.api.PushCommand#call with the following differences:
|
||||
1. Fields are not accessible, so they are substituted by getters, except for credentialsProvider, which we have stored as an instance field.
|
||||
2. checkCallable() won't fail (according to the PushCommand code), so it's safe to remove it.
|
||||
3. Actual push is performed via
|
||||
Transport.openAll(repo, remoteConfig, Transport.Operation.PUSH)
|
||||
instead of
|
||||
Transport.openAll(repo, remote, Transport.Operation.PUSH)
|
||||
where remoteConfig is passed to the method.
|
||||
Original code constructs the remoteConfig based on .git/config.
|
||||
*/
|
||||
@NotNull
|
||||
private Iterable<PushResult> call(PushCommand pushCommand, RemoteConfig remoteConfig) throws JGitInternalException, InvalidRemoteException {
|
||||
ArrayList<PushResult> pushResults = new ArrayList<PushResult>(3);
|
||||
|
||||
List<RefSpec> refSpecs = pushCommand.getRefSpecs();
|
||||
Repository repo = pushCommand.getRepository();
|
||||
boolean force = pushCommand.isForce();
|
||||
int timeout = pushCommand.getTimeout();
|
||||
CredentialsProvider credentialsProvider = myCredentialsProvider;
|
||||
String receivePack = pushCommand.getReceivePack();
|
||||
boolean thin = pushCommand.isThin();
|
||||
boolean dryRun = pushCommand.isDryRun();
|
||||
String remote = pushCommand.getRemote();
|
||||
ProgressMonitor monitor = pushCommand.getProgressMonitor();
|
||||
|
||||
try {
|
||||
if (refSpecs.isEmpty()) {
|
||||
RemoteConfig config = new RemoteConfig(repo.getConfig(), pushCommand.getRemote());
|
||||
refSpecs.addAll(config.getPushRefSpecs());
|
||||
}
|
||||
if (refSpecs.isEmpty()) {
|
||||
Ref head = repo.getRef(Constants.HEAD);
|
||||
if (head != null && head.isSymbolic()) {
|
||||
refSpecs.add(new RefSpec(head.getLeaf().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
if (force) {
|
||||
for (int i = 0; i < refSpecs.size(); i++) {
|
||||
refSpecs.set(i, refSpecs.get(i).setForceUpdate(true));
|
||||
}
|
||||
}
|
||||
|
||||
final List<Transport> transports;
|
||||
transports = Transport.openAll(repo, remoteConfig, Transport.Operation.PUSH);
|
||||
for (final Transport transport : transports) {
|
||||
if (0 <= timeout) {
|
||||
transport.setTimeout(timeout);
|
||||
}
|
||||
transport.setPushThin(thin);
|
||||
if (receivePack != null) {
|
||||
transport.setOptionReceivePack(receivePack);
|
||||
}
|
||||
transport.setDryRun(dryRun);
|
||||
if (credentialsProvider != null) {
|
||||
transport.setCredentialsProvider(credentialsProvider);
|
||||
}
|
||||
|
||||
final Collection<RemoteRefUpdate> toPush = transport
|
||||
.findRemoteRefUpdatesFor(refSpecs);
|
||||
|
||||
try {
|
||||
PushResult result = transport.push(monitor, toPush);
|
||||
pushResults.add(result);
|
||||
}
|
||||
catch (TransportException e) {
|
||||
throw new JGitInternalException(
|
||||
JGitText.get().exceptionCaughtDuringExecutionOfPushCommand,
|
||||
e);
|
||||
}
|
||||
finally {
|
||||
transport.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
throw new InvalidRemoteException(MessageFormat.format(
|
||||
JGitText.get().invalidRemote, remote));
|
||||
}
|
||||
catch (NotSupportedException e) {
|
||||
throw new JGitInternalException(
|
||||
JGitText.get().exceptionCaughtDuringExecutionOfPushCommand,
|
||||
e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new JGitInternalException(
|
||||
JGitText.get().exceptionCaughtDuringExecutionOfPushCommand,
|
||||
e);
|
||||
}
|
||||
|
||||
return pushResults;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user