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.commands;
18 import com.google.common.annotations.VisibleForTesting;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.project.Project;
21 import com.intellij.openapi.util.Computable;
22 import com.intellij.openapi.util.Condition;
23 import com.intellij.openapi.util.Key;
24 import com.intellij.openapi.util.text.StringUtil;
25 import com.intellij.openapi.vcs.VcsException;
26 import com.intellij.openapi.vfs.VfsUtilCore;
27 import com.intellij.openapi.vfs.VirtualFile;
28 import com.intellij.util.Function;
29 import com.intellij.util.ObjectUtils;
30 import com.intellij.util.containers.ContainerUtil;
31 import com.intellij.vcsUtil.VcsFileUtil;
32 import git4idea.GitVcs;
33 import git4idea.branch.GitRebaseParams;
34 import git4idea.config.GitVersionSpecialty;
35 import git4idea.rebase.GitInteractiveRebaseEditorHandler;
36 import git4idea.rebase.GitRebaseEditorService;
37 import git4idea.rebase.GitRebaseResumeMode;
38 import git4idea.repo.GitRemote;
39 import git4idea.repo.GitRepository;
40 import git4idea.reset.GitResetMode;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
46 import java.util.concurrent.atomic.AtomicBoolean;
47 import java.util.concurrent.atomic.AtomicInteger;
48 import java.util.concurrent.atomic.AtomicReference;
50 import static java.util.Collections.singleton;
53 * Easy-to-use wrapper of common native Git commands.
54 * Most of them return result as {@link GitCommandResult}.
56 * @author Kirill Likhodedov
58 @SuppressWarnings("StringToUpperCaseOrToLowerCaseWithoutLocale")
59 public class GitImpl implements Git {
61 private static final Logger LOG = Logger.getInstance(Git.class);
67 * Calls 'git init' on the specified directory.
71 public GitCommandResult init(@NotNull Project project, @NotNull VirtualFile root, @NotNull GitLineHandlerListener... listeners) {
72 GitLineHandler h = new GitLineHandler(project, root, GitCommand.INIT);
73 for (GitLineHandlerListener listener : listeners) {
74 h.addLineListener(listener);
77 h.setStdoutSuppressed(false);
82 * <p>Queries Git for the unversioned files in the given paths. </p>
83 * <p>Ignored files are left ignored, i. e. no information is returned about them (thus this method may also be used as a
84 * ignored files checker.</p>
86 * @param files files that are to be checked for the unversioned files among them.
87 * <b>Pass <code>null</code> to query the whole repository.</b>
88 * @return Unversioned not ignored files from the given scope.
92 public Set<VirtualFile> untrackedFiles(@NotNull Project project, @NotNull VirtualFile root,
93 @Nullable Collection<VirtualFile> files) throws VcsException {
94 final Set<VirtualFile> untrackedFiles = new HashSet<>();
97 untrackedFiles.addAll(untrackedFilesNoChunk(project, root, null));
100 for (List<String> relativePaths : VcsFileUtil.chunkFiles(root, files)) {
101 untrackedFiles.addAll(untrackedFilesNoChunk(project, root, relativePaths));
105 return untrackedFiles;
108 // relativePaths are guaranteed to fit into command line length limitations.
111 public Collection<VirtualFile> untrackedFilesNoChunk(@NotNull Project project,
112 @NotNull VirtualFile root,
113 @Nullable List<String> relativePaths)
114 throws VcsException {
115 final Set<VirtualFile> untrackedFiles = new HashSet<>();
116 GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.LS_FILES);
118 h.addParameters("--exclude-standard", "--others", "-z");
120 if (relativePaths != null) {
121 h.addParameters(relativePaths);
124 final String output = h.run();
125 if (StringUtil.isEmptyOrSpaces(output)) {
126 return untrackedFiles;
129 for (String relPath : output.split("\u0000")) {
130 VirtualFile f = root.findFileByRelativePath(relPath);
132 // files was created on disk, but VirtualFile hasn't yet been created,
133 // when the GitChangeProvider has already been requested about changes.
134 LOG.info(String.format("VirtualFile for path [%s] is null", relPath));
136 untrackedFiles.add(f);
140 return untrackedFiles;
145 public GitCommandResult clone(@NotNull final Project project, @NotNull final File parentDirectory, @NotNull final String url,
146 @NotNull final String clonedDirectoryName, @NotNull final GitLineHandlerListener... listeners) {
147 return run(new Computable<GitLineHandler>() {
149 public GitLineHandler compute() {
150 GitLineHandler handler = new GitLineHandler(project, parentDirectory, GitCommand.CLONE);
151 handler.setStdoutSuppressed(false);
153 handler.addParameters("--progress");
154 handler.addParameters(url);
155 handler.endOptions();
156 handler.addParameters(clonedDirectoryName);
157 addListeners(handler, listeners);
165 public GitCommandResult config(@NotNull GitRepository repository, String... params) {
166 final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.CONFIG);
167 h.addParameters(params);
173 public GitCommandResult diff(@NotNull GitRepository repository, @NotNull List<String> parameters, @NotNull String range) {
174 final GitLineHandler diff = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.DIFF);
175 diff.addParameters(parameters);
176 diff.addParameters(range);
177 diff.setStdoutSuppressed(true);
178 diff.setStderrSuppressed(true);
179 diff.setSilent(true);
185 public GitCommandResult checkAttr(@NotNull final GitRepository repository,
186 @NotNull final Collection<String> attributes,
187 @NotNull Collection<VirtualFile> files) {
188 List<List<String>> listOfPaths = VcsFileUtil.chunkFiles(repository.getRoot(), files);
189 return runAll(ContainerUtil.map(listOfPaths, new Function<List<String>, Computable<GitCommandResult>>() {
191 public Computable<GitCommandResult> fun(final List<String> relativePaths) {
192 return new Computable<GitCommandResult>() {
194 public GitCommandResult compute() {
195 final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.CHECK_ATTR);
196 h.addParameters(new ArrayList<>(attributes));
198 h.addParameters(relativePaths);
208 public GitCommandResult stashSave(@NotNull GitRepository repository, @NotNull String message) {
209 final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.STASH);
210 h.addParameters("save");
211 h.addParameters(message);
217 public GitCommandResult stashPop(@NotNull GitRepository repository, @NotNull GitLineHandlerListener... listeners) {
218 final GitLineHandler handler = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.STASH);
219 handler.addParameters("pop");
220 addListeners(handler, listeners);
226 public GitCommandResult merge(@NotNull GitRepository repository, @NotNull String branchToMerge,
227 @Nullable List<String> additionalParams, @NotNull GitLineHandlerListener... listeners) {
228 final GitLineHandler mergeHandler = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.MERGE);
229 mergeHandler.setSilent(false);
230 mergeHandler.addParameters(branchToMerge);
231 if (additionalParams != null) {
232 mergeHandler.addParameters(additionalParams);
234 for (GitLineHandlerListener listener : listeners) {
235 mergeHandler.addLineListener(listener);
237 return run(mergeHandler);
242 * {@code git checkout <reference>} <br/>
243 * {@code git checkout -b <newBranch> <reference>}
247 public GitCommandResult checkout(@NotNull GitRepository repository,
248 @NotNull String reference,
249 @Nullable String newBranch,
252 @NotNull GitLineHandlerListener... listeners) {
253 final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.CHECKOUT);
255 h.setStdoutSuppressed(false);
257 h.addParameters("--force");
259 if (newBranch == null) { // simply checkout
260 h.addParameters(detach ? reference + "^0" : reference); // we could use `--detach` here, but it is supported only since 1.7.5.
262 else { // checkout reference as new branch
263 h.addParameters("-b", newBranch, reference);
266 for (GitLineHandlerListener listener : listeners) {
267 h.addLineListener(listener);
273 * {@code git checkout -b <branchName>}
277 public GitCommandResult checkoutNewBranch(@NotNull GitRepository repository, @NotNull String branchName,
278 @Nullable GitLineHandlerListener listener) {
279 final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.CHECKOUT.readLockingCommand());
281 h.setStdoutSuppressed(false);
282 h.addParameters("-b");
283 h.addParameters(branchName);
284 if (listener != null) {
285 h.addLineListener(listener);
292 public GitCommandResult createNewTag(@NotNull GitRepository repository, @NotNull String tagName,
293 @Nullable GitLineHandlerListener listener, @NotNull String reference) {
294 final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.TAG);
296 h.addParameters(tagName);
297 if (!reference.isEmpty()) {
298 h.addParameters(reference);
300 if (listener != null) {
301 h.addLineListener(listener);
307 * {@code git branch -d <reference>} or {@code git branch -D <reference>}
311 public GitCommandResult branchDelete(@NotNull GitRepository repository,
312 @NotNull String branchName,
314 @NotNull GitLineHandlerListener... listeners) {
315 final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.BRANCH);
317 h.setStdoutSuppressed(false);
318 h.addParameters(force ? "-D" : "-d");
319 h.addParameters(branchName);
320 for (GitLineHandlerListener listener : listeners) {
321 h.addLineListener(listener);
327 * Get branches containing the commit.
328 * {@code git branch --contains <commit>}
332 public GitCommandResult branchContains(@NotNull GitRepository repository, @NotNull String commit) {
333 final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.BRANCH);
334 h.addParameters("--contains", commit);
340 public GitCommandResult branchCreate(@NotNull GitRepository repository, @NotNull String branchName, @NotNull String startPoint) {
341 final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.BRANCH);
342 h.setStdoutSuppressed(false);
343 h.addParameters(branchName);
344 h.addParameters(startPoint);
350 public GitCommandResult renameBranch(@NotNull GitRepository repository,
351 @NotNull String currentName,
352 @NotNull String newName,
353 @NotNull GitLineHandlerListener... listeners) {
354 GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.BRANCH);
356 h.setStdoutSuppressed(false);
357 h.addParameters("-m", currentName, newName);
363 public GitCommandResult reset(@NotNull GitRepository repository, @NotNull GitResetMode mode, @NotNull String target,
364 @NotNull GitLineHandlerListener... listeners) {
365 return reset(repository, mode.getArgument(), target, listeners);
370 public GitCommandResult resetMerge(@NotNull GitRepository repository, @Nullable String revision) {
371 return reset(repository, "--merge", revision);
375 private static GitCommandResult reset(@NotNull GitRepository repository, @NotNull String argument, @Nullable String target,
376 @NotNull GitLineHandlerListener... listeners) {
377 final GitLineHandler handler = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.RESET);
378 handler.addParameters(argument);
379 if (target != null) {
380 handler.addParameters(target);
382 addListeners(handler, listeners);
387 * Returns the last (tip) commit on the given branch.<br/>
388 * {@code git rev-list -1 <branchName>}
392 public GitCommandResult tip(@NotNull GitRepository repository, @NotNull String branchName) {
393 final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.REV_LIST);
394 h.addParameters("-1");
395 h.addParameters(branchName);
401 public GitCommandResult push(@NotNull GitRepository repository,
402 @NotNull String remote,
403 @Nullable String url,
404 @NotNull String spec,
405 boolean updateTracking,
406 @NotNull GitLineHandlerListener... listeners) {
407 return doPush(repository, remote, singleton(url), spec, false, updateTracking, null, listeners);
412 public GitCommandResult push(@NotNull GitRepository repository,
413 @NotNull GitRemote remote,
414 @NotNull String spec,
416 boolean updateTracking,
417 @Nullable String tagMode,
418 GitLineHandlerListener... listeners) {
419 return doPush(repository, remote.getName(), remote.getPushUrls(), spec, force, updateTracking, tagMode, listeners);
423 private GitCommandResult doPush(@NotNull final GitRepository repository,
424 @NotNull final String remoteName,
425 @NotNull final Collection<String> remoteUrls,
426 @NotNull final String spec,
428 final boolean updateTracking,
429 @Nullable final String tagMode,
430 @NotNull final GitLineHandlerListener... listeners) {
431 return runCommand(new Computable<GitLineHandler>() {
433 public GitLineHandler compute() {
434 final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.PUSH);
435 h.setUrls(remoteUrls);
437 h.setStdoutSuppressed(false);
438 addListeners(h, listeners);
439 h.addProgressParameter();
440 h.addParameters("--porcelain");
441 h.addParameters(remoteName);
442 h.addParameters(spec);
443 if (updateTracking) {
444 h.addParameters("--set-upstream");
447 h.addParameters("--force");
449 if (tagMode != null) {
450 h.addParameters(tagMode);
459 public GitCommandResult show(@NotNull GitRepository repository, @NotNull String... params) {
460 final GitLineHandler handler = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.SHOW);
461 handler.addParameters(params);
467 public GitCommandResult cherryPick(@NotNull GitRepository repository, @NotNull String hash, boolean autoCommit,
468 @NotNull GitLineHandlerListener... listeners) {
469 final GitLineHandler handler = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.CHERRY_PICK);
470 handler.addParameters("-x");
472 handler.addParameters("-n");
474 handler.addParameters(hash);
475 addListeners(handler, listeners);
476 handler.setSilent(false);
477 handler.setStdoutSuppressed(false);
483 public GitCommandResult getUnmergedFiles(@NotNull GitRepository repository) {
484 GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.LS_FILES);
485 h.addParameters("--unmerged");
491 * Fetch remote branch
492 * {@code git fetch <remote> <params>}
496 public GitCommandResult fetch(@NotNull final GitRepository repository,
497 @NotNull final GitRemote remote,
498 @NotNull final List<GitLineHandlerListener> listeners,
499 final String... params) {
500 return runCommand(new Computable<GitLineHandler>() {
502 public GitLineHandler compute() {
503 final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.FETCH);
505 h.setStdoutSuppressed(false);
506 h.setUrls(remote.getUrls());
507 h.addParameters(remote.getName());
508 h.addParameters(params);
509 h.addProgressParameter();
510 GitVcs vcs = GitVcs.getInstance(repository.getProject());
511 if (vcs != null && GitVersionSpecialty.SUPPORTS_FETCH_PRUNE.existsIn(vcs.getVersion())) {
512 h.addParameters("--prune");
514 addListeners(h, listeners);
522 public GitCommandResult addRemote(@NotNull GitRepository repository, @NotNull String name, @NotNull String url) {
523 GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.REMOTE);
524 h.addParameters("add", name, url);
530 public GitCommandResult removeRemote(@NotNull GitRepository repository, @NotNull GitRemote remote) {
531 GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.REMOTE);
532 h.addParameters("remove", remote.getName());
538 public GitCommandResult renameRemote(@NotNull GitRepository repository, @NotNull String oldName, @NotNull String newName) {
539 GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.REMOTE);
540 h.addParameters("rename", oldName, newName);
546 public GitCommandResult setRemoteUrl(@NotNull GitRepository repository, @NotNull String remoteName, @NotNull String newUrl) {
547 GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.REMOTE);
548 h.addParameters("set-url", remoteName, newUrl);
554 public GitCommandResult lsRemote(@NotNull final Project project,
555 @NotNull final File workingDir,
556 @NotNull final String url) {
557 return doLsRemote(project, workingDir, url, singleton(url));
562 public GitCommandResult lsRemote(@NotNull Project project,
563 @NotNull VirtualFile workingDir,
564 @NotNull GitRemote remote,
565 String... additionalParameters) {
566 return doLsRemote(project, VfsUtilCore.virtualToIoFile(workingDir), remote.getName(), remote.getUrls(), additionalParameters);
571 public GitCommandResult remotePrune(@NotNull final GitRepository repository, @NotNull final GitRemote remote) {
572 return run(new Computable<GitLineHandler>() {
574 public GitLineHandler compute() {
575 GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.REMOTE.writeLockingCommand());
576 h.setStdoutSuppressed(false);
577 h.addParameters("prune");
578 h.addParameters(remote.getName());
579 h.setUrls(remote.getUrls());
587 public GitCommandResult rebase(@NotNull GitRepository repository,
588 @NotNull GitRebaseParams parameters,
589 @NotNull GitLineHandlerListener... listeners) {
590 Project project = repository.getProject();
591 VirtualFile root = repository.getRoot();
592 GitLineHandler handler = new GitLineHandler(project, root, GitCommand.REBASE);
593 handler.addParameters(parameters.asCommandLineArguments());
594 addListeners(handler, listeners);
595 return parameters.isInteractive() ? runWithEditor(project, root, handler, true) : run(handler);
600 public GitCommandResult rebaseAbort(@NotNull GitRepository repository, @NotNull GitLineHandlerListener... listeners) {
601 GitLineHandler handler = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.REBASE);
602 handler.addParameters("--abort");
603 addListeners(handler, listeners);
609 public GitCommandResult rebaseContinue(@NotNull GitRepository repository, @NotNull GitLineHandlerListener... listeners) {
610 return rebaseResume(repository, GitRebaseResumeMode.CONTINUE, listeners);
615 public GitCommandResult rebaseSkip(@NotNull GitRepository repository, @NotNull GitLineHandlerListener... listeners) {
616 return rebaseResume(repository, GitRebaseResumeMode.SKIP, listeners);
620 private GitCommandResult rebaseResume(@NotNull GitRepository repository,
621 @NotNull GitRebaseResumeMode rebaseMode,
622 @NotNull GitLineHandlerListener[] listeners) {
623 Project project = repository.getProject();
624 VirtualFile root = repository.getRoot();
625 GitLineHandler handler = new GitLineHandler(project, root, GitCommand.REBASE);
626 handler.addParameters(rebaseMode.asCommandLineArgument());
627 addListeners(handler, listeners);
628 return runWithEditor(project, root, handler, false);
632 private GitCommandResult runWithEditor(@NotNull Project project,
633 @NotNull VirtualFile root,
634 @NotNull GitLineHandler handler,
635 boolean commitListAware) {
636 GitInteractiveRebaseEditorHandler editor = configureEditor(project, root, handler, commitListAware);
638 GitCommandResult result = run(handler);
639 return editor.wasEditorCancelled() ? toCancelledResult(result) : result;
647 private static GitCommandResult toCancelledResult(@NotNull GitCommandResult result) {
648 int exitCode = result.getExitCode() == 0 ? 1 : result.getExitCode();
649 return new GitCommandResult(false, exitCode, result.getErrorOutput(), result.getOutput(), result.getException()) {
651 public boolean cancelled() {
659 protected GitInteractiveRebaseEditorHandler configureEditor(@NotNull Project project,
660 @NotNull VirtualFile root,
661 @NotNull GitLineHandler handler,
662 boolean commitListAware) {
663 GitRebaseEditorService service = GitRebaseEditorService.getInstance();
664 GitInteractiveRebaseEditorHandler editor = new GitInteractiveRebaseEditorHandler(service, project, root, handler);
665 if (!commitListAware) {
666 editor.setRebaseEditorShown();
668 service.configureHandler(handler, editor.getHandlerNo());
673 private static GitCommandResult doLsRemote(@NotNull final Project project,
674 @NotNull final File workingDir,
675 @NotNull final String remoteId,
676 @NotNull final Collection<String> authenticationUrls,
677 final String... additionalParameters) {
678 return run(new Computable<GitLineHandler>() {
680 public GitLineHandler compute() {
681 GitLineHandler h = new GitLineHandler(project, workingDir, GitCommand.LS_REMOTE);
682 h.addParameters(additionalParameters);
683 h.addParameters(remoteId);
684 h.setUrls(authenticationUrls);
690 private static void addListeners(@NotNull GitLineHandler handler, @NotNull GitLineHandlerListener... listeners) {
691 addListeners(handler, Arrays.asList(listeners));
694 private static void addListeners(@NotNull GitLineHandler handler, @NotNull List<GitLineHandlerListener> listeners) {
695 for (GitLineHandlerListener listener : listeners) {
696 handler.addLineListener(listener);
701 private static GitCommandResult run(@NotNull Computable<GitLineHandler> handlerConstructor) {
702 final List<String> errorOutput = new ArrayList<>();
703 final List<String> output = new ArrayList<>();
704 final AtomicInteger exitCode = new AtomicInteger();
705 final AtomicBoolean startFailed = new AtomicBoolean();
706 final AtomicReference<Throwable> exception = new AtomicReference<>();
715 startFailed.set(false);
718 GitLineHandler handler = handlerConstructor.compute();
719 handler.addLineListener(new GitLineHandlerListener() {
720 @Override public void onLineAvailable(String line, Key outputType) {
721 if (looksLikeError(line)) {
722 errorOutput.add(line);
728 @Override public void processTerminated(int code) {
732 @Override public void startFailed(Throwable t) {
733 startFailed.set(true);
734 errorOutput.add("Failed to start Git process");
739 handler.runInCurrentThread(null);
740 authFailed = handler.hasHttpAuthFailed();
741 success = !startFailed.get() && (handler.isIgnoredErrorCode(exitCode.get()) || exitCode.get() == 0);
743 while (authFailed && authAttempt++ < 2);
744 return new GitCommandResult(success, exitCode.get(), errorOutput, output, null);
748 * Runs the given {@link GitLineHandler} in the current thread and returns the {@link GitCommandResult}.
751 private static GitCommandResult run(@NotNull GitLineHandler handler) {
752 return run(new Computable.PredefinedValueComputable<>(handler));
757 public GitCommandResult runCommand(@NotNull Computable<GitLineHandler> handlerConstructor) {
758 return run(handlerConstructor);
763 public GitCommandResult runCommand(@NotNull final GitLineHandler handler) {
764 return runCommand(new Computable<GitLineHandler>() {
766 public GitLineHandler compute() {
773 private static GitCommandResult runAll(@NotNull List<Computable<GitCommandResult>> commands) {
774 if (commands.isEmpty()) {
775 LOG.error("List of commands should not be empty", new Exception());
776 return GitCommandResult.error("Internal error");
778 GitCommandResult compoundResult = null;
779 for (Computable<GitCommandResult> command : commands) {
780 compoundResult = GitCommandResult.merge(compoundResult, command.compute());
782 return ObjectUtils.assertNotNull(compoundResult);
785 private static boolean looksLikeError(@NotNull final String text) {
786 return ContainerUtil.exists(ERROR_INDICATORS, new Condition<String>() {
788 public boolean value(@NotNull String indicator) {
789 return StringUtil.startsWithIgnoreCase(text.trim(), indicator);
794 // could be upper-cased, so should check case-insensitively
795 public static final String[] ERROR_INDICATORS = {
796 "error:", "remote: error", "fatal:",
797 "Cannot", "Could not", "Interactive rebase already started", "refusing to pull", "cannot rebase:", "conflict",