GitRebaseStatus (@NotNull GitRepository repository, @Nullable GitRebaseResumeMode customMode, @NotNull Map<GitRepository, GitSuccessfulRebase> alreadyRebased, @NotNull ProgressIndicator indicator) { VirtualFile root = repository.getRoot(); String repoName = getShortRepositoryName(repository); LOG.info("Rebasing root " + repoName + ", mode: " + notNull(customMode, "standard")); boolean retryWhenDirty = false; int commitsToRebase = 0; try { GitRebaseParams params = myRebaseSpec.getParams(); if (params != null) { String upstream = params.getUpstream(); String branch = params.getBranch(); commitsToRebase = GitRebaseUtils.getNumberOfCommitsToRebase(repository, upstream, branch); } } catch (VcsException e) { LOG.warn("Couldn't get the number of commits to rebase", e); } GitRebaseProgressListener progressListener = new GitRebaseProgressListener(commitsToRebase, indicator); while (true) { GitRebaseProblemDetector rebaseDetector = new GitRebaseProblemDetector(); GitUntrackedFilesOverwrittenByOperationDetector untrackedDetector = new GitUntrackedFilesOverwrittenByOperationDetector(root); GitRebaseCommandResult rebaseCommandResult = callRebase(repository, customMode, rebaseDetector, untrackedDetector, progressListener); GitCommandResult result = rebaseCommandResult.getCommandResult(); boolean somethingRebased = customMode != null || progressListener.currentCommit > 1; if (rebaseCommandResult.wasCancelledInCommitList()) { return GitRebaseStatus.notStarted(); } else if (rebaseCommandResult.wasCancelledInCommitMessage()) { showStoppedForEditingMessage(); return new GitRebaseStatus(GitRebaseStatus.Type.SUSPENDED); } else if (result.success()) { if (rebaseDetector.hasStoppedForEditing()) { showStoppedForEditingMessage(); return new GitRebaseStatus(GitRebaseStatus.Type.SUSPENDED); } LOG.debug("Successfully rebased " + repoName); return new GitSuccessfulRebase(); } else if (rebaseDetector.isDirtyTree() && customMode == null && !retryWhenDirty) { // if the initial dirty tree check doesn't find all local changes, we are still ready to stash-on-demand, // but only once per repository (if the error happens again, that means that the previous stash attempt failed for some reason), // and not in the case of --continue (where all local changes are expected to be committed) or --skip. LOG.debug("Dirty tree detected in " + repoName); String saveError = saveLocalChanges(singleton(repository.getRoot())); if (saveError == null) { retryWhenDirty = true; // try same repository again } else { LOG.warn(String.format( "Couldn't %s root %s: %s", mySaver.getSaveMethod() == GitSaveChangesPolicy.SHELVE ? "shelve" : "stash", repository.getRoot(), saveError )); showFatalError(saveError, repository, somethingRebased, alreadyRebased.keySet()); GitRebaseStatus.Type type = somethingRebased ? GitRebaseStatus.Type.SUSPENDED : GitRebaseStatus.Type.ERROR; return new GitRebaseStatus(type); } } else if (untrackedDetector.wasMessageDetected()) { LOG.info("Untracked files detected in " + repoName); showUntrackedFilesError(untrackedDetector.getRelativeFilePaths(), repository, somethingRebased, alreadyRebased.keySet()); GitRebaseStatus.Type type = somethingRebased ? GitRebaseStatus.Type.SUSPENDED : GitRebaseStatus.Type.ERROR; return new GitRebaseStatus(type); } else if (rebaseDetector.isNoChangeError()) { LOG.info("'No changes' situation detected in " + repoName); customMode = GitRebaseResumeMode.SKIP; } else if (rebaseDetector.isMergeConflict()) { LOG.info("Merge conflict in " + repoName); ResolveConflictResult resolveResult = showConflictResolver(repository, false); if (resolveResult == ResolveConflictResult.ALL_RESOLVED) { customMode = GitRebaseResumeMode.CONTINUE; } else if (resolveResult == ResolveConflictResult.NOTHING_TO_MERGE) { // the output is the same for the cases: // (1) "unresolved conflicts" // (2) "manual editing of a file not followed by `git add` // => we check if there are any unresolved conflicts, and if not, then it is the case #2 which we are not handling LOG.info("Unmerged changes while rebasing root " + repoName + ": " + result.getErrorOutputAsJoinedString()); showFatalError(result.getErrorOutputAsHtmlString(), repository, somethingRebased, alreadyRebased.keySet()); GitRebaseStatus.Type type = somethingRebased ? GitRebaseStatus.Type.SUSPENDED : GitRebaseStatus.Type.ERROR; return new GitRebaseStatus(type); } else { notifyNotAllConflictsResolved(repository); return new GitRebaseStatus(GitRebaseStatus.Type.SUSPENDED); } } else { LOG.info("Error rebasing root " + repoName + ": " + result.getErrorOutputAsJoinedString()); showFatalError(result.getErrorOutputAsHtmlString(), repository, somethingRebased, alreadyRebased.keySet()); GitRebaseStatus.Type type = somethingRebased ? GitRebaseStatus.Type.SUSPENDED : GitRebaseStatus.Type.ERROR; return new GitRebaseStatus(type); } } }