2 * Copyright 2000-2011 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package git4idea.update;
18 import com.intellij.notification.NotificationType;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.progress.ProgressIndicator;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.vcs.VcsException;
23 import com.intellij.openapi.vfs.VirtualFile;
24 import git4idea.GitVcs;
25 import git4idea.commands.*;
26 import git4idea.jgit.GitHttpAdapter;
27 import git4idea.repo.GitRemote;
28 import git4idea.repo.GitRepository;
29 import git4idea.repo.GitRepositoryManager;
30 import git4idea.util.GitUIUtil;
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
34 import java.util.ArrayList;
35 import java.util.Collection;
36 import java.util.concurrent.atomic.AtomicBoolean;
39 * @author Kirill Likhodedov
41 public class GitFetcher {
43 private static final Logger LOG = Logger.getInstance(GitFetcher.class);
45 private final Project myProject;
46 private final GitRepositoryManager myRepositoryManager;
47 private final ProgressIndicator myProgressIndicator;
49 private final Collection<Exception> myErrors = new ArrayList<Exception>();
51 public GitFetcher(@NotNull Project project, @NotNull ProgressIndicator progressIndicator) {
53 myProgressIndicator = progressIndicator;
54 myRepositoryManager = GitRepositoryManager.getInstance(project);
58 * Invokes 'git fetch'.
59 * @return true if fetch was successful, false in the case of error.
61 public GitFetchResult fetch(@NotNull VirtualFile root) {
62 GitRepository repository = myRepositoryManager.getRepositoryForRoot(root);
63 assert repository != null : "Repository can't be null for " + root + "\n" + myRepositoryManager;
65 GitFetchResult result = GitFetchResult.success();
66 for (GitRemote remote : repository.getRemotes()) {
67 String url = remote.getFirstUrl();
71 if (GitHttpAdapter.shouldUseJGit(url)) {
72 GitFetchResult res = GitHttpAdapter.fetch(repository, remote, url);
73 myErrors.addAll(res.getErrors());
74 if (!res.isSuccess()) {
79 if (!fetchNatively(root, remote)) {
80 result = GitFetchResult.error(myErrors);
86 repository.update(GitRepository.TrackedTopic.BRANCHES);
90 private boolean fetchNatively(@NotNull VirtualFile root, @NotNull GitRemote remote) {
91 final GitLineHandlerPasswordRequestAware h = new GitLineHandlerPasswordRequestAware(myProject, root, GitCommand.FETCH);
92 h.addParameters(remote.getName());
93 final GitTask fetchTask = new GitTask(myProject, h, "Fetching...");
94 fetchTask.setProgressIndicator(myProgressIndicator);
95 fetchTask.setProgressAnalyzer(new GitStandardProgressAnalyzer());
96 final AtomicBoolean success = new AtomicBoolean();
97 fetchTask.execute(true, false, new GitTaskResultHandlerAdapter() {
99 protected void onSuccess() {
104 protected void onCancel() {
105 LOG.info("Cancelled fetch.");
109 protected void onFailure() {
110 LOG.info("Error fetching: " + h.errors());
111 if (!h.hadAuthRequest()) {
112 myErrors.addAll(h.errors());
114 myErrors.add(new VcsException("Authentication failed"));
118 return success.get();
122 public Collection<Exception> getErrors() {
126 public static void displayFetchResult(@NotNull Project project,
127 @NotNull GitFetchResult result,
128 @Nullable String errorNotificationTitle, @NotNull Collection<? extends Exception> errors) {
129 if (result.isSuccess()) {
130 GitVcs.NOTIFICATION_GROUP_ID.createNotification("Fetched successfully", NotificationType.WARNING).notify(project);
131 } else if (result.isCancelled()) {
132 GitVcs.NOTIFICATION_GROUP_ID.createNotification("Fetch cancelled by user", NotificationType.WARNING).notify(project);
133 } else if (result.isNotAuthorized()) {
136 if (errorNotificationTitle != null) {
137 title = errorNotificationTitle;
138 description = "Fetch failed: couldn't authorize";
140 title = "Fetch failed";
141 description = "Couldn't authorize";
143 GitUIUtil.notifyMessage(project, title, description, NotificationType.ERROR, true, null);
145 GitVcs instance = GitVcs.getInstance(project);
146 if (instance != null && instance.getExecutableValidator().isExecutableValid()) {
147 GitUIUtil.notifyMessage(project, "Fetch failed", null, NotificationType.ERROR, true, errors);
153 * Fetches all specified roots.
154 * Once a root has failed, stops and displays the notification.
155 * If needed, displays the successful notification at the end.
156 * @param roots roots to fetch.
157 * @param errorNotificationTitle if specified, this notification title will be used instead of the standard "Fetch failed".
158 * Use this when fetch is a part of a compound process.
159 * @param notifySuccess if set to {@code true} successful notification will be displayed.
160 * @return true if all fetches were successful, false if at least one fetch failed.
162 public boolean fetchRootsAndNotify(@NotNull Collection<VirtualFile> roots, @Nullable String errorNotificationTitle, boolean notifySuccess) {
163 for (VirtualFile root : roots) {
164 GitFetchResult result = fetch(root);
165 if (!result.isSuccess()) {
166 displayFetchResult(myProject, result, errorNotificationTitle, getErrors());
171 GitUIUtil.notifySuccess(myProject, "", "Fetched successfully");