From ecc84d5602f2cc64cc73164326caa30bc0a17282 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Tue, 15 Apr 2014 17:28:17 +0400 Subject: [PATCH] [git] Use GitImpl for all remote commands Relates to IDEA-98189. --- .../src/git4idea/actions/GitPull.java | 49 +++++----- .../git4idea/src/git4idea/commands/Git.java | 14 ++- .../git4idea/commands/GitCommandResult.java | 5 + .../src/git4idea/commands/GitImpl.java | 95 +++++++++++++------ .../src/git4idea/update/GitFetcher.java | 87 ++++++----------- .../git4idea/src/git4idea/util/GitUIUtil.java | 3 +- .../tests/git4idea/test/GitTestImpl.java | 5 +- 7 files changed, 140 insertions(+), 118 deletions(-) diff --git a/plugins/git4idea/src/git4idea/actions/GitPull.java b/plugins/git4idea/src/git4idea/actions/GitPull.java index 9aaa6c1e874a..1831be7c37d9 100644 --- a/plugins/git4idea/src/git4idea/actions/GitPull.java +++ b/plugins/git4idea/src/git4idea/actions/GitPull.java @@ -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() { + @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(); } diff --git a/plugins/git4idea/src/git4idea/commands/Git.java b/plugins/git4idea/src/git4idea/commands/Git.java index 0dfbdb0bdc17..eaf58ff824d2 100644 --- a/plugins/git4idea/src/git4idea/commands/Git.java +++ b/plugins/git4idea/src/git4idea/commands/Git.java @@ -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 handlerConstructor); + @NotNull GitCommandResult init(@NotNull Project project, @NotNull VirtualFile root, @NotNull GitLineHandlerListener... listeners); @@ -128,5 +137,6 @@ public interface Git { List 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 listeners, String... params); } diff --git a/plugins/git4idea/src/git4idea/commands/GitCommandResult.java b/plugins/git4idea/src/git4idea/commands/GitCommandResult.java index dad9f2d8aaa1..f89a4890a76b 100644 --- a/plugins/git4idea/src/git4idea/commands/GitCommandResult.java +++ b/plugins/git4idea/src/git4idea/commands/GitCommandResult.java @@ -85,4 +85,9 @@ public class GitCommandResult { public static GitCommandResult error(@NotNull String error) { return new GitCommandResult(false, 1, Collections.singletonList(error), Collections.emptyList(), null); } + + public boolean cancelled() { + return false; // will be implemented later + } + } diff --git a/plugins/git4idea/src/git4idea/commands/GitImpl.java b/plugins/git4idea/src/git4idea/commands/GitImpl.java index 1fcd7067ddde..942ba242f730 100644 --- a/plugins/git4idea/src/git4idea/commands/GitImpl.java +++ b/plugins/git4idea/src/git4idea/commands/GitImpl.java @@ -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() { + @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 listeners, final String... params) { + return runRemoteCommand(new Computable() { + @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 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 handlerConstructor) { final List errorOutput = new ArrayList(); final List output = new ArrayList(); final AtomicInteger exitCode = new AtomicInteger(); final AtomicBoolean startFailed = new AtomicBoolean(); final AtomicReference exception = new AtomicReference(); - + + 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(handler)); + } + + @Override + @NotNull + public GitCommandResult runRemoteCommand(@NotNull Computable handlerConstructor) { + return run(handlerConstructor); + } /** * Check if the line looks line an error message diff --git a/plugins/git4idea/src/git4idea/update/GitFetcher.java b/plugins/git4idea/src/git4idea/update/GitFetcher.java index 71111366bdbd..44dbb54343a3 100644 --- a/plugins/git4idea/src/git4idea/update/GitFetcher.java +++ b/plugins/git4idea/src/git4idea/update/GitFetcher.java @@ -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.singletonList(pruneDetector), additionalParams); - final AtomicReference result = new AtomicReference(); - 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) { diff --git a/plugins/git4idea/src/git4idea/util/GitUIUtil.java b/plugins/git4idea/src/git4idea/util/GitUIUtil.java index 71a818d186b8..b6c255806c08 100644 --- a/plugins/git4idea/src/git4idea/util/GitUIUtil.java +++ b/plugins/git4idea/src/git4idea/util/GitUIUtil.java @@ -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)); } /** diff --git a/plugins/git4idea/tests/git4idea/test/GitTestImpl.java b/plugins/git4idea/tests/git4idea/test/GitTestImpl.java index 2cced1f34084..7919124c2da1 100644 --- a/plugins/git4idea/tests/git4idea/test/GitTestImpl.java +++ b/plugins/git4idea/tests/git4idea/test/GitTestImpl.java @@ -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 listeners, String... params) { throw new UnsupportedOperationException(); }