[git] Use GitImpl for all remote commands

Relates to IDEA-98189.
This commit is contained in:
Kirill Likhodedov
2014-04-16 15:32:57 +04:00
parent 85cad871d3
commit ecc84d5602
7 changed files with 140 additions and 118 deletions
@@ -17,19 +17,20 @@ package git4idea.actions;
import com.intellij.history.Label;
import com.intellij.history.LocalHistory;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.update.ActionInfo;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitRevisionNumber;
import git4idea.GitUtil;
import git4idea.GitVcs;
import git4idea.commands.Git;
import git4idea.commands.GitCommandResult;
import git4idea.commands.GitLineHandler;
import git4idea.commands.GitStandardProgressAnalyzer;
import git4idea.commands.GitTask;
import git4idea.commands.GitTaskResultHandlerAdapter;
import git4idea.i18n.GitBundle;
import git4idea.merge.GitMergeUtil;
import git4idea.merge.GitPullDialog;
@@ -68,7 +69,7 @@ public class GitPull extends GitRepositoryAction {
new Task.Backgroundable(project, GitBundle.message("pulling.title", dialog.getRemote()), true) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
final GitRepositoryManager repositoryManager = GitUtil.getRepositoryManager(myProject);
final GitRepositoryManager repositoryManager = GitUtil.getRepositoryManager(project);
GitRepository repository = repositoryManager.getRepositoryForRoot(dialog.gitRoot());
assert repository != null : "Repository can't be null for root " + dialog.gitRoot();
@@ -76,13 +77,18 @@ public class GitPull extends GitRepositoryAction {
GitRemote remote = GitUtil.findRemoteByName(repository, remoteOrUrl);
String url = (remote == null) ? remoteOrUrl : remote.getFirstUrl();
final String url = (remote == null) ? remoteOrUrl : remote.getFirstUrl();
if (url == null) {
return;
}
final GitLineHandler handler = dialog.makeHandler(url);
final Git git = ServiceManager.getService(Git.class);
GitCommandResult result = git.runRemoteCommand(new Computable<GitLineHandler>() {
@Override
public GitLineHandler compute() {
return dialog.makeHandler(url);
}
});
final VirtualFile root = dialog.gitRoot();
affectedRoots.add(root);
String revision = repository.getCurrentRevision();
@@ -90,25 +96,16 @@ public class GitPull extends GitRepositoryAction {
return;
}
final GitRevisionNumber currentRev = new GitRevisionNumber(revision);
GitTask pullTask = new GitTask(project, handler, GitBundle.message("pulling.title", dialog.getRemote()));
pullTask.setProgressIndicator(indicator);
pullTask.setProgressAnalyzer(new GitStandardProgressAnalyzer());
pullTask.execute(true, false, new GitTaskResultHandlerAdapter() {
@Override
protected void onSuccess() {
root.refresh(false, true);
GitMergeUtil.showUpdates(GitPull.this, project, exceptions, root, currentRev, beforeLabel, getActionName(), ActionInfo.UPDATE);
repositoryManager.updateRepository(root);
runFinalTasks(project, GitVcs.getInstance(project), affectedRoots, getActionName(), exceptions);
}
@Override
protected void onFailure() {
GitUIUtil.notifyGitErrors(project, "Error pulling " + dialog.getRemote(), "", handler.errors());
repositoryManager.updateRepository(root);
}
});
if (result.success()) {
root.refresh(false, true);
GitMergeUtil.showUpdates(GitPull.this, project, exceptions, root, currentRev, beforeLabel, getActionName(), ActionInfo.UPDATE);
repositoryManager.updateRepository(root);
runFinalTasks(project, GitVcs.getInstance(project), affectedRoots, getActionName(), exceptions);
}
else {
GitUIUtil.notifyError(project, "Error pulling " + dialog.getRemote(), result.getErrorOutputAsJoinedString(), true, null);
repositoryManager.updateRepository(root);
}
}
}.queue();
}
@@ -16,10 +16,10 @@
package git4idea.commands;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitCommit;
import git4idea.history.browser.GitHeavyCommit;
import git4idea.push.GitPushSpec;
import git4idea.repo.GitRepository;
import org.jetbrains.annotations.NotNull;
@@ -35,6 +35,15 @@ import java.util.Set;
*/
public interface Git {
/**
* A generic method to run a Git remote command, when existing methods like {@link #fetch(GitRepository, String, String, List, String...)}
* are not sufficient.
* @param handlerConstructor this is needed, since the operation may need to repeat (e.g. in case of authentication failure).
* make sure to supply a stateless constructor.
*/
@NotNull
GitCommandResult runRemoteCommand(@NotNull Computable<GitLineHandler> handlerConstructor);
@NotNull
GitCommandResult init(@NotNull Project project, @NotNull VirtualFile root, @NotNull GitLineHandlerListener... listeners);
@@ -128,5 +137,6 @@ public interface Git {
List<GitCommit> history(@NotNull GitRepository repository, @NotNull String range);
@NotNull
GitCommandResult fetch(@NotNull GitRepository repository, @NotNull String url, @NotNull String remote, String... params);
GitCommandResult fetch(@NotNull GitRepository repository, @NotNull String url, @NotNull String remote,
@NotNull List<GitLineHandlerListener> listeners, String... params);
}
@@ -85,4 +85,9 @@ public class GitCommandResult {
public static GitCommandResult error(@NotNull String error) {
return new GitCommandResult(false, 1, Collections.singletonList(error), Collections.<String>emptyList(), null);
}
public boolean cancelled() {
return false; // will be implemented later
}
}
@@ -17,6 +17,7 @@ package git4idea.commands;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsException;
@@ -25,6 +26,8 @@ import com.intellij.vcsUtil.VcsFileUtil;
import git4idea.GitBranch;
import git4idea.GitCommit;
import git4idea.GitExecutionException;
import git4idea.GitVcs;
import git4idea.config.GitVersionSpecialty;
import git4idea.history.GitHistoryUtils;
import git4idea.push.GitPushSpec;
import git4idea.repo.GitRemote;
@@ -362,21 +365,27 @@ public class GitImpl implements Git {
@Override
@NotNull
public GitCommandResult push(@NotNull GitRepository repository, @NotNull String remote, @NotNull String url, @NotNull String spec,
boolean updateTracking, @NotNull GitLineHandlerListener... listeners) {
final GitLineHandlerPasswordRequestAware h = new GitLineHandlerPasswordRequestAware(repository.getProject(), repository.getRoot(),
GitCommand.PUSH);
h.setUrl(url);
h.setSilent(false);
h.setStdoutSuppressed(false);
addListeners(h, listeners);
h.addProgressParameter();
h.addParameters(remote);
h.addParameters(spec);
if (updateTracking) {
h.addParameters("--set-upstream");
}
return run(h);
public GitCommandResult push(@NotNull final GitRepository repository, @NotNull final String remote, @NotNull final String url,
@NotNull final String spec, final boolean updateTracking,
@NotNull final GitLineHandlerListener... listeners) {
return runRemoteCommand(new Computable<GitLineHandler>() {
@Override
public GitLineHandler compute() {
final GitLineHandlerPasswordRequestAware h = new GitLineHandlerPasswordRequestAware(repository.getProject(), repository.getRoot(),
GitCommand.PUSH);
h.setUrl(url);
h.setSilent(false);
h.setStdoutSuppressed(false);
addListeners(h, listeners);
h.addProgressParameter();
h.addParameters(remote);
h.addParameters(spec);
if (updateTracking) {
h.addParameters("--set-upstream");
}
return h;
}
});
}
@Override
@@ -435,32 +444,46 @@ public class GitImpl implements Git {
*/
@Override
@NotNull
public GitCommandResult fetch(@NotNull GitRepository repository, @NotNull String url, @NotNull String remote, String... params) {
final GitLineHandlerPasswordRequestAware h =
new GitLineHandlerPasswordRequestAware(repository.getProject(), repository.getRoot(), GitCommand.FETCH);
h.setUrl(url);
h.addParameters(remote);
h.addParameters(params);
h.addProgressParameter();
return run(h);
public GitCommandResult fetch(@NotNull final GitRepository repository, @NotNull final String url, @NotNull final String remote,
@NotNull final List<GitLineHandlerListener> listeners, final String... params) {
return runRemoteCommand(new Computable<GitLineHandler>() {
@Override
public GitLineHandler compute() {
final GitLineHandlerPasswordRequestAware h = new GitLineHandlerPasswordRequestAware(repository.getProject(), repository.getRoot(),
GitCommand.FETCH);
h.setUrl(url);
h.addParameters(remote);
h.addParameters(params);
h.addProgressParameter();
GitVcs vcs = GitVcs.getInstance(repository.getProject());
if (vcs != null && GitVersionSpecialty.SUPPORTS_FETCH_PRUNE.existsIn(vcs.getVersion())) {
h.addParameters("--prune");
}
addListeners(h, listeners);
return h;
}
});
}
private static void addListeners(@NotNull GitLineHandler handler, @NotNull GitLineHandlerListener... listeners) {
addListeners(handler, Arrays.asList(listeners));
}
private static void addListeners(@NotNull GitLineHandler handler, @NotNull List<GitLineHandlerListener> listeners) {
for (GitLineHandlerListener listener : listeners) {
handler.addLineListener(listener);
}
}
/**
* Runs the given {@link GitLineHandler} in the current thread and returns the {@link GitCommandResult}.
*/
private static GitCommandResult run(@NotNull GitLineHandler handler) {
@NotNull
private static GitCommandResult run(@NotNull Computable<GitLineHandler> handlerConstructor) {
final List<String> errorOutput = new ArrayList<String>();
final List<String> output = new ArrayList<String>();
final AtomicInteger exitCode = new AtomicInteger();
final AtomicBoolean startFailed = new AtomicBoolean();
final AtomicReference<Throwable> exception = new AtomicReference<Throwable>();
GitLineHandler handler = handlerConstructor.compute();
handler.addLineListener(new GitLineHandlerListener() {
@Override public void onLineAvailable(String line, Key outputType) {
if (isError(line)) {
@@ -480,7 +503,7 @@ public class GitImpl implements Git {
exception.set(t);
}
});
handler.runInCurrentThread(null);
if (handler instanceof GitLineHandlerPasswordRequestAware && ((GitLineHandlerPasswordRequestAware)handler).hadAuthRequest()) {
@@ -491,6 +514,20 @@ public class GitImpl implements Git {
(handler.isIgnoredErrorCode(exitCode.get()) || exitCode.get() == 0);
return new GitCommandResult(success, exitCode.get(), errorOutput, output, null);
}
/**
* Runs the given {@link GitLineHandler} in the current thread and returns the {@link GitCommandResult}.
*/
@NotNull
private static GitCommandResult run(@NotNull GitLineHandler handler) {
return run(new Computable.PredefinedValueComputable<GitLineHandler>(handler));
}
@Override
@NotNull
public GitCommandResult runRemoteCommand(@NotNull Computable<GitLineHandler> handlerConstructor) {
return run(handlerConstructor);
}
/**
* Check if the line looks line an error message
@@ -16,21 +16,24 @@
package git4idea.update;
import com.intellij.dvcs.DvcsUtil;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import git4idea.GitLocalBranch;
import git4idea.GitRemoteBranch;
import git4idea.GitUtil;
import git4idea.GitVcs;
import git4idea.branch.GitBranchUtil;
import git4idea.commands.*;
import git4idea.config.GitVersionSpecialty;
import git4idea.commands.Git;
import git4idea.commands.GitCommandResult;
import git4idea.commands.GitLineHandlerAdapter;
import git4idea.commands.GitLineHandlerListener;
import git4idea.jgit.GitHttpAdapter;
import git4idea.repo.GitBranchTrackInfo;
import git4idea.repo.GitRemote;
@@ -40,11 +43,7 @@ import git4idea.util.GitUIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -136,7 +135,7 @@ public class GitFetcher {
if (GitHttpAdapter.shouldUseJGit(url)) {
return GitHttpAdapter.fetch(repository, remote, url, branch);
}
return fetchNatively(repository.getRoot(), remote, url, branch);
return fetchNatively(repository, remote, url, branch);
}
// leaving this unused method, because the wanted behavior can change again
@@ -154,7 +153,7 @@ public class GitFetcher {
if (GitHttpAdapter.shouldUseJGit(url)) {
return GitHttpAdapter.fetch(repository, remote, url, remoteBranch);
}
return fetchNatively(repository.getRoot(), remote, url, remoteBranch);
return fetchNatively(repository, remote, url, remoteBranch);
}
@NotNull
@@ -202,7 +201,7 @@ public class GitFetcher {
}
}
else {
GitFetchResult res = fetchNatively(repository.getRoot(), remote, url, null);
GitFetchResult res = fetchNatively(repository, remote, url, null);
res.addPruneInfo(fetchResult.getPrunedRefs());
fetchResult = res;
if (!fetchResult.isSuccess()) {
@@ -213,54 +212,30 @@ public class GitFetcher {
return fetchResult;
}
private GitFetchResult fetchNatively(@NotNull VirtualFile root, @NotNull GitRemote remote, @NotNull String url, @Nullable String branch) {
final GitLineHandlerPasswordRequestAware h = new GitLineHandlerPasswordRequestAware(myProject, root, GitCommand.FETCH);
h.setUrl(url);
h.addProgressParameter();
if (GitVersionSpecialty.SUPPORTS_FETCH_PRUNE.existsIn(myVcs.getVersion())) {
h.addParameters("--prune");
}
String remoteName = remote.getName();
h.addParameters(remoteName);
if (branch != null) {
h.addParameters(getFetchSpecForBranch(branch, remoteName));
}
final GitTask fetchTask = new GitTask(myProject, h, "Fetching " + remote.getFirstUrl());
fetchTask.setProgressIndicator(myProgressIndicator);
fetchTask.setProgressAnalyzer(new GitStandardProgressAnalyzer());
@NotNull
private static GitFetchResult fetchNatively(@NotNull GitRepository repository, @NotNull GitRemote remote, @NotNull String url,
@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();
h.addLineListener(pruneDetector);
GitCommandResult result =
git.fetch(repository, url, remote.getName(), Collections.<GitLineHandlerListener>singletonList(pruneDetector), additionalParams);
final AtomicReference<GitFetchResult> result = new AtomicReference<GitFetchResult>();
fetchTask.execute(true, false, new GitTaskResultHandlerAdapter() {
@Override
protected void onSuccess() {
result.set(GitFetchResult.success());
}
@Override
protected void onCancel() {
LOG.info("Cancelled fetch.");
result.set(GitFetchResult.cancel());
}
@Override
protected void onFailure() {
LOG.info("Error fetching: " + h.errors());
if (!h.hadAuthRequest()) {
myErrors.addAll(h.errors());
} else {
myErrors.add(new VcsException("Authentication failed"));
}
result.set(GitFetchResult.error(myErrors));
}
});
result.get().addPruneInfo(pruneDetector.getPrunedRefs());
return result.get();
GitFetchResult fetchResult;
if (result.success()) {
fetchResult = GitFetchResult.success();
}
else if (result.cancelled()) {
fetchResult = GitFetchResult.cancel();
}
else {
fetchResult = GitFetchResult.error(result.getErrorOutputAsJoinedString());
}
fetchResult.addPruneInfo(pruneDetector.getPrunedRefs());
return fetchResult;
}
private static String getRidOfPrefixIfExists(String branch) {
@@ -99,8 +99,7 @@ public class GitUIUtil {
}
public static void notifyError(Project project, String title, String description, boolean important, @Nullable Exception error) {
notifyMessage(
project, title, description, important, Collections.singleton(error));
notifyMessage(project, title, description, important, error == null ? null : Collections.singleton(error));
}
/**
@@ -40,8 +40,6 @@ import java.lang.reflect.Method;
import java.util.*;
import static com.intellij.openapi.util.text.StringUtil.join;
import static git4idea.test.GitExecutor.cd;
import static git4idea.test.GitExecutor.git;
import static java.lang.String.format;
/**
@@ -227,7 +225,8 @@ public class GitTestImpl implements Git {
@NotNull
@Override
public GitCommandResult fetch(@NotNull GitRepository repository, @NotNull String url, @NotNull String remote, String... params) {
public GitCommandResult fetch(@NotNull GitRepository repository, @NotNull String url, @NotNull String remote,
@NotNull List<GitLineHandlerListener> listeners, String... params) {
throw new UnsupportedOperationException();
}