mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
git: rebase: make rebase dialog reuse the existing common rebase process
This fixes: IDEA-81093, IDEA-55479, IDEA-55672, IDEA-76778, IDEA-55672
This commit is contained in:
@@ -444,7 +444,7 @@ public class DvcsUtil {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static VirtualFile guessVcsRoot(@NotNull Project project, @Nullable VirtualFile file) {
|
||||
public static VirtualFile guessVcsRoot(@NotNull Project project, @Nullable VirtualFile file) {
|
||||
VirtualFile root = null;
|
||||
if (file != null) {
|
||||
root = ProjectLevelVcsManager.getInstance(project).getVcsRootFor(file);
|
||||
|
||||
@@ -44,7 +44,7 @@ abstract class GitAbstractRebaseAction extends DumbAwareAction {
|
||||
super.update(e);
|
||||
Project project = e.getProject();
|
||||
if (project == null || !hasGitRepositories(project)) {
|
||||
e.getPresentation().setVisible(false);
|
||||
e.getPresentation().setEnabledAndVisible(false);
|
||||
}
|
||||
else {
|
||||
e.getPresentation().setEnabledAndVisible(hasRebaseInProgress(project));
|
||||
|
||||
@@ -15,38 +15,58 @@
|
||||
*/
|
||||
package git4idea.actions;
|
||||
|
||||
import com.intellij.dvcs.DvcsUtil;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import git4idea.commands.GitLineHandler;
|
||||
import git4idea.i18n.GitBundle;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import git4idea.rebase.GitRebaseDialog;
|
||||
import git4idea.rebase.GitRebaseUtils;
|
||||
import git4idea.repo.GitRepository;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Git rebase action
|
||||
*/
|
||||
public class GitRebase extends GitRebaseActionBase {
|
||||
import static com.intellij.dvcs.DvcsUtil.sortRepositories;
|
||||
import static git4idea.GitUtil.*;
|
||||
import static git4idea.rebase.GitRebaseUtils.getRebasingRepositories;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@NotNull
|
||||
protected String getActionName() {
|
||||
return GitBundle.getString("rebase.action.name");
|
||||
public class GitRebase extends DumbAwareAction {
|
||||
|
||||
@Override
|
||||
public void update(@NotNull AnActionEvent e) {
|
||||
super.update(e);
|
||||
Project project = e.getProject();
|
||||
if (project == null || !hasGitRepositories(project)) {
|
||||
e.getPresentation().setEnabledAndVisible(false);
|
||||
}
|
||||
else {
|
||||
e.getPresentation().setVisible(true);
|
||||
e.getPresentation().setEnabled(getRebasingRepositories(project).size() < getRepositories(project).size());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Nullable
|
||||
protected GitLineHandler createHandler(Project project, List<VirtualFile> gitRoots, VirtualFile defaultRoot) {
|
||||
GitRebaseDialog dialog = new GitRebaseDialog(project, gitRoots, defaultRoot);
|
||||
if (!dialog.showAndGet()) {
|
||||
return null;
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
final Project project = e.getRequiredData(CommonDataKeys.PROJECT);
|
||||
ArrayList<GitRepository> repositories = ContainerUtil.newArrayList(getRepositories(project));
|
||||
repositories.removeAll(getRebasingRepositories(project));
|
||||
List<VirtualFile> roots = ContainerUtil.newArrayList(getRootsFromRepositories(sortRepositories(repositories)));
|
||||
VirtualFile defaultRoot = DvcsUtil.guessVcsRoot(project, e.getData(CommonDataKeys.VIRTUAL_FILE));
|
||||
final GitRebaseDialog dialog = new GitRebaseDialog(project, roots, defaultRoot);
|
||||
if (dialog.showAndGet()) {
|
||||
ProgressManager.getInstance().run(new Task.Backgroundable(project, "Rebasing...") {
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
GitRebaseUtils.rebase(project, singletonList(dialog.getSelectedRepository()), dialog.getSelectedParams(), indicator);
|
||||
}
|
||||
});
|
||||
}
|
||||
return dialog.handler();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package git4idea.actions;
|
||||
|
||||
import com.intellij.dvcs.DvcsUtil;
|
||||
import com.intellij.openapi.application.AccessToken;
|
||||
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.vcs.VcsException;
|
||||
import com.intellij.openapi.vcs.VcsNotifier;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.commands.Git;
|
||||
import git4idea.commands.GitCommandResult;
|
||||
import git4idea.commands.GitLineHandler;
|
||||
import git4idea.i18n.GitBundle;
|
||||
import git4idea.rebase.GitInteractiveRebaseEditorHandler;
|
||||
import git4idea.rebase.GitRebaseEditorService;
|
||||
import git4idea.rebase.GitRebaseLineListener;
|
||||
import git4idea.repo.GitRepositoryManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The base class for rebase actions that use editor
|
||||
*/
|
||||
public abstract class GitRebaseActionBase extends GitRepositoryAction {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected void perform(@NotNull final Project project,
|
||||
@NotNull final List<VirtualFile> gitRoots,
|
||||
@NotNull final VirtualFile defaultRoot,
|
||||
final Set<VirtualFile> affectedRoots,
|
||||
final List<VcsException> exceptions) throws VcsException {
|
||||
final GitLineHandler h = createHandler(project, gitRoots, defaultRoot);
|
||||
if (h == null) {
|
||||
return;
|
||||
}
|
||||
final VirtualFile root = h.workingDirectoryFile();
|
||||
GitRebaseEditorService service = GitRebaseEditorService.getInstance();
|
||||
final GitInteractiveRebaseEditorHandler editor = new GitInteractiveRebaseEditorHandler(service, project, root, h);
|
||||
final GitRebaseLineListener resultListener = new GitRebaseLineListener();
|
||||
h.addLineListener(resultListener);
|
||||
configureEditor(editor);
|
||||
affectedRoots.add(root);
|
||||
|
||||
service.configureHandler(h, editor.getHandlerNo());
|
||||
new Task.Backgroundable(project, GitBundle.getString("rebasing.title"), true) {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
AccessToken token = DvcsUtil.workingTreeChangeStarted(project);
|
||||
try {
|
||||
GitCommandResult result = ServiceManager.getService(Git.class).runCommand(h);
|
||||
editor.close();
|
||||
GitRepositoryManager manager = GitUtil.getRepositoryManager(project);
|
||||
manager.updateRepository(root);
|
||||
VfsUtil.markDirtyAndRefresh(false, true, false, root);
|
||||
notifyAboutResult(result, resultListener, editor.wasNoopSituationDetected(), exceptions, project);
|
||||
}
|
||||
finally {
|
||||
DvcsUtil.workingTreeChangeFinished(project, token);
|
||||
}
|
||||
}
|
||||
}.queue();
|
||||
}
|
||||
|
||||
private static void notifyAboutResult(@NotNull GitCommandResult commandResult,
|
||||
@NotNull GitRebaseLineListener resultListener,
|
||||
boolean noopSituation,
|
||||
@NotNull List<VcsException> exceptions,
|
||||
@NotNull Project project) {
|
||||
final GitRebaseLineListener.Result result = resultListener.getResult();
|
||||
String messageId;
|
||||
String message = null;
|
||||
boolean isError = true;
|
||||
switch (result.status) {
|
||||
case CONFLICT:
|
||||
messageId = "rebase.result.conflict";
|
||||
break;
|
||||
case ERROR:
|
||||
messageId = "rebase.result.error";
|
||||
message = commandResult.getErrorOutputAsHtmlString();
|
||||
break;
|
||||
case CANCELLED:
|
||||
// we do not need to show a message if editing was cancelled.
|
||||
exceptions.clear();
|
||||
return;
|
||||
case EDIT:
|
||||
isError = false;
|
||||
messageId = "rebase.result.amend";
|
||||
break;
|
||||
case FINISHED:
|
||||
isError = false;
|
||||
messageId = "rebase.result.success";
|
||||
if (noopSituation) {
|
||||
message = "Current branch was reset to the base branch";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unsupported rebase result: " + result.status);
|
||||
}
|
||||
|
||||
String title = GitBundle.message(messageId + ".title");
|
||||
if (message == null) {
|
||||
message = GitBundle.message(messageId, result.current, result.total);
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
VcsNotifier.getInstance(project).notifyError(title, message);
|
||||
}
|
||||
else {
|
||||
VcsNotifier.getInstance(project).notifySuccess(title, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method could be overridden to supply additional information to the editor.
|
||||
*
|
||||
* @param editor the editor to configure
|
||||
*/
|
||||
protected void configureEditor(GitInteractiveRebaseEditorHandler editor) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create line handler that represents a git operation
|
||||
*
|
||||
* @param project the context project
|
||||
* @param gitRoots the git roots
|
||||
* @param defaultRoot the default root
|
||||
* @return the line handler or null
|
||||
*/
|
||||
@Nullable
|
||||
protected abstract GitLineHandler createHandler(Project project, List<VirtualFile> gitRoots, VirtualFile defaultRoot);
|
||||
}
|
||||
@@ -18,31 +18,71 @@ package git4idea.branch;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.openapi.util.text.StringUtil.nullize;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
public class GitRebaseParams {
|
||||
|
||||
@NotNull private final String myNewBase;
|
||||
@Nullable private final String myBranch;
|
||||
@Nullable private final String myNewBase;
|
||||
@NotNull private final String myUpstream;
|
||||
private final boolean myInteractive;
|
||||
private final boolean myPreserveMerges;
|
||||
|
||||
public GitRebaseParams(@NotNull String newBase) {
|
||||
myNewBase = newBase;
|
||||
public GitRebaseParams(@NotNull String upstream) {
|
||||
this(null, null, upstream, false, false);
|
||||
}
|
||||
public GitRebaseParams(@Nullable String branch,
|
||||
@Nullable String newBase,
|
||||
@NotNull String upstream,
|
||||
boolean interactive,
|
||||
boolean preserveMerges) {
|
||||
myBranch = nullize(branch, true);
|
||||
myNewBase = nullize(newBase, true);
|
||||
myUpstream = upstream;
|
||||
myInteractive = interactive;
|
||||
myPreserveMerges = preserveMerges;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<String> asCommandLineArguments() {
|
||||
List<String> args = ContainerUtil.newArrayList();
|
||||
args.add(myNewBase);
|
||||
if (myInteractive) {
|
||||
args.add("--interactive");
|
||||
}
|
||||
if (myPreserveMerges) {
|
||||
args.add("--preserve-merges");
|
||||
}
|
||||
if (myNewBase != null) {
|
||||
args.addAll(asList("--onto", myNewBase));
|
||||
}
|
||||
args.add(myUpstream);
|
||||
if (myBranch != null) {
|
||||
args.add(myBranch);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Nullable
|
||||
public String getNewBase() {
|
||||
return myNewBase;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getUpstream() {
|
||||
return myUpstream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringUtil.join(asCommandLineArguments(), " ");
|
||||
}
|
||||
|
||||
public boolean isInteractive() {
|
||||
return myInteractive;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package git4idea.commands;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
@@ -567,10 +568,12 @@ public class GitImpl implements Git {
|
||||
public GitCommandResult rebase(@NotNull GitRepository repository,
|
||||
@NotNull GitRebaseParams parameters,
|
||||
@NotNull GitLineHandlerListener... listeners) {
|
||||
GitLineHandler handler = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.REBASE);
|
||||
Project project = repository.getProject();
|
||||
VirtualFile root = repository.getRoot();
|
||||
GitLineHandler handler = new GitLineHandler(project, root, GitCommand.REBASE);
|
||||
handler.addParameters(parameters.asCommandLineArguments());
|
||||
addListeners(handler, listeners);
|
||||
return run(handler);
|
||||
return parameters.isInteractive() ? runWithEditor(project, root, handler, true) : run(handler);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -595,9 +598,9 @@ public class GitImpl implements Git {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static GitCommandResult rebaseResume(@NotNull GitRepository repository,
|
||||
@NotNull GitRebaseResumeMode rebaseMode,
|
||||
@NotNull GitLineHandlerListener[] listeners) {
|
||||
private GitCommandResult rebaseResume(@NotNull GitRepository repository,
|
||||
@NotNull GitRebaseResumeMode rebaseMode,
|
||||
@NotNull GitLineHandlerListener[] listeners) {
|
||||
Project project = repository.getProject();
|
||||
VirtualFile root = repository.getRoot();
|
||||
GitLineHandler handler = new GitLineHandler(project, root, GitCommand.REBASE);
|
||||
@@ -607,10 +610,10 @@ public class GitImpl implements Git {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static GitCommandResult runWithEditor(@NotNull Project project,
|
||||
@NotNull VirtualFile root,
|
||||
@NotNull GitLineHandler handler,
|
||||
boolean commitListAware) {
|
||||
private GitCommandResult runWithEditor(@NotNull Project project,
|
||||
@NotNull VirtualFile root,
|
||||
@NotNull GitLineHandler handler,
|
||||
boolean commitListAware) {
|
||||
GitInteractiveRebaseEditorHandler editor = configureEditor(project, root, handler, commitListAware);
|
||||
try {
|
||||
return run(handler);
|
||||
@@ -620,11 +623,12 @@ public class GitImpl implements Git {
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@NotNull
|
||||
private static GitInteractiveRebaseEditorHandler configureEditor(@NotNull Project project,
|
||||
@NotNull VirtualFile root,
|
||||
@NotNull GitLineHandler handler,
|
||||
boolean commitListAware) {
|
||||
protected GitInteractiveRebaseEditorHandler configureEditor(@NotNull Project project,
|
||||
@NotNull VirtualFile root,
|
||||
@NotNull GitLineHandler handler,
|
||||
boolean commitListAware) {
|
||||
GitRebaseEditorService service = GitRebaseEditorService.getInstance();
|
||||
GitInteractiveRebaseEditorHandler editor = new GitInteractiveRebaseEditorHandler(service, project, root, handler);
|
||||
if (!commitListAware) {
|
||||
|
||||
@@ -26,14 +26,14 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.DocumentAdapter;
|
||||
import git4idea.*;
|
||||
import git4idea.branch.GitBranchUtil;
|
||||
import git4idea.commands.GitCommand;
|
||||
import git4idea.commands.GitLineHandler;
|
||||
import git4idea.branch.GitRebaseParams;
|
||||
import git4idea.config.GitConfigUtil;
|
||||
import git4idea.config.GitRebaseSettings;
|
||||
import git4idea.i18n.GitBundle;
|
||||
import git4idea.merge.GitMergeUtil;
|
||||
import git4idea.repo.GitRemote;
|
||||
import git4idea.repo.GitRepository;
|
||||
import git4idea.repo.GitRepositoryManager;
|
||||
import git4idea.ui.GitReferenceValidator;
|
||||
import git4idea.util.GitUIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -46,6 +46,9 @@ import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.openapi.util.text.StringUtil.isEmptyOrSpaces;
|
||||
import static com.intellij.util.ObjectUtils.assertNotNull;
|
||||
|
||||
/**
|
||||
* The dialog that allows initiating git rebase activity
|
||||
*/
|
||||
@@ -53,6 +56,8 @@ public class GitRebaseDialog extends DialogWrapper {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(GitRebaseDialog.class);
|
||||
|
||||
@NotNull private final GitRepositoryManager myRepositoryManager;
|
||||
|
||||
/**
|
||||
* Git root selector
|
||||
*/
|
||||
@@ -151,6 +156,7 @@ public class GitRebaseDialog extends DialogWrapper {
|
||||
init();
|
||||
myProject = project;
|
||||
mySettings = ServiceManager.getService(myProject, GitRebaseSettings.class);
|
||||
myRepositoryManager = GitUtil.getRepositoryManager(myProject);
|
||||
final Runnable validateRunnable = new Runnable() {
|
||||
public void run() {
|
||||
validateFields();
|
||||
@@ -210,39 +216,6 @@ public class GitRebaseDialog extends DialogWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
public GitLineHandler handler() {
|
||||
GitLineHandler h = new GitLineHandler(myProject, gitRoot(), GitCommand.REBASE);
|
||||
h.setStdoutSuppressed(false);
|
||||
if (myInteractiveCheckBox.isSelected() && myInteractiveCheckBox.isEnabled()) {
|
||||
h.addParameters("-i");
|
||||
}
|
||||
h.addParameters("-v");
|
||||
if (!myDoNotUseMergeCheckBox.isSelected()) {
|
||||
if (myMergeStrategyComboBox.getSelectedItem().equals(GitMergeUtil.DEFAULT_STRATEGY)) {
|
||||
h.addParameters("-m");
|
||||
}
|
||||
else {
|
||||
h.addParameters("-s", myMergeStrategyComboBox.getSelectedItem().toString());
|
||||
}
|
||||
}
|
||||
if (myPreserveMergesCheckBox.isSelected()) {
|
||||
h.addParameters("-p");
|
||||
}
|
||||
String from = GitUIUtil.getTextField(myFromComboBox).getText();
|
||||
String onto = GitUIUtil.getTextField(myOntoComboBox).getText();
|
||||
if (from.length() == 0) {
|
||||
h.addParameters(onto);
|
||||
}
|
||||
else {
|
||||
h.addParameters("--onto", onto, from);
|
||||
}
|
||||
final String selectedBranch = (String)myBranchComboBox.getSelectedItem();
|
||||
if (myCurrentBranch != null && !myCurrentBranch.getName().equals(selectedBranch)) {
|
||||
h.addParameters(selectedBranch);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOKAction() {
|
||||
try {
|
||||
@@ -457,6 +430,31 @@ public class GitRebaseDialog extends DialogWrapper {
|
||||
return (VirtualFile)myGitRootComboBox.getSelectedItem();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public GitRepository getSelectedRepository() {
|
||||
return assertNotNull(myRepositoryManager.getRepositoryForRoot(gitRoot()));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public GitRebaseParams getSelectedParams() {
|
||||
String selectedBranch = (String)myBranchComboBox.getSelectedItem();
|
||||
String branch = myCurrentBranch != null && !myCurrentBranch.getName().equals(selectedBranch) ? selectedBranch : null;
|
||||
|
||||
String from = GitUIUtil.getTextField(myFromComboBox).getText();
|
||||
String onto = GitUIUtil.getTextField(myOntoComboBox).getText();
|
||||
String upstream;
|
||||
String newBase;
|
||||
if (isEmptyOrSpaces(from)) {
|
||||
upstream = onto;
|
||||
newBase = null;
|
||||
}
|
||||
else {
|
||||
upstream = from;
|
||||
newBase = onto;
|
||||
}
|
||||
|
||||
return new GitRebaseParams(branch, newBase, upstream, myInteractiveCheckBox.isSelected(), myPreserveMergesCheckBox.isSelected());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
|
||||
@@ -42,10 +42,12 @@ public class GitRebaseProblemDetector extends GitLineHandlerAdapter {
|
||||
"you have unstaged changes",
|
||||
"your index contains uncommitted changes"
|
||||
};
|
||||
private static final String STOPPED_FOR_EDITING = "You can amend the commit now";
|
||||
|
||||
private volatile boolean myMergeConflict;
|
||||
private volatile boolean myNoChangeError;
|
||||
private volatile boolean myDirtyTree;
|
||||
private volatile boolean myStoppedForEditing;
|
||||
|
||||
public boolean isNoChangeError() {
|
||||
return myNoChangeError;
|
||||
@@ -59,6 +61,10 @@ public class GitRebaseProblemDetector extends GitLineHandlerAdapter {
|
||||
return myDirtyTree;
|
||||
}
|
||||
|
||||
public boolean hasStoppedForEditing() {
|
||||
return myStoppedForEditing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLineAvailable(String line, Key outputType) {
|
||||
for (String conflictIndicator : REBASE_CONFLICT_INDICATORS) {
|
||||
@@ -79,5 +85,9 @@ public class GitRebaseProblemDetector extends GitLineHandlerAdapter {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtil.containsIgnoreCase(line, STOPPED_FOR_EDITING)) {
|
||||
myStoppedForEditing = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,6 +200,10 @@ public class GitRebaseProcess {
|
||||
boolean somethingRebased = customMode != null || progressListener.getResult().current > 1;
|
||||
|
||||
if (result.success()) {
|
||||
if (rebaseDetector.hasStoppedForEditing()) {
|
||||
showStoppedForEditingMessage(repository);
|
||||
return new GitRebaseStatus(GitRebaseStatus.Type.SUSPENDED, skippedCommits);
|
||||
}
|
||||
LOG.debug("Successfully rebased " + repoName);
|
||||
return GitSuccessfulRebase.parseFromOutput(result.getOutput(), skippedCommits);
|
||||
}
|
||||
@@ -346,7 +350,7 @@ public class GitRebaseProcess {
|
||||
});
|
||||
SuccessType commonType = getItemIfAllTheSame(successTypes, SuccessType.REBASED);
|
||||
GitRebaseParams params = myRebaseSpec.getParams();
|
||||
String message = commonType.formatMessage(rebasedBranch, params == null ? null : params.getNewBase());
|
||||
String message = commonType.formatMessage(rebasedBranch, params == null ? null : notNull(params.getNewBase(), params.getUpstream()));
|
||||
message += mentionSkippedCommits(skippedCommits);
|
||||
myNotifier.notifyMinorInfo("Rebase Successful", message, new NotificationListener.Adapter() {
|
||||
@Override
|
||||
@@ -391,6 +395,12 @@ public class GitRebaseProcess {
|
||||
return ResolveConflictResult.UNRESOLVED_REMAIN;
|
||||
}
|
||||
|
||||
private void showStoppedForEditingMessage(@NotNull GitRepository repository) {
|
||||
String description = "Once you are satisfied with your changes you may <a href='continue'>continue</a>";
|
||||
myNotifier.notifyImportantInfo("Rebase Stopped for Editing", description,
|
||||
new RebaseNotificationListener(repository, MultiMap.<GitRepository, GitRebaseUtils.CommitInfo>empty()));
|
||||
}
|
||||
|
||||
private void showFatalError(@NotNull final String error,
|
||||
@NotNull final GitRepository currentRepository,
|
||||
boolean somethingWasRebased,
|
||||
@@ -507,10 +517,10 @@ public class GitRebaseProcess {
|
||||
|
||||
private class RebaseNotificationListener extends NotificationListener.Adapter {
|
||||
@NotNull private final GitRepository myCurrentRepository;
|
||||
private final MultiMap<GitRepository, GitRebaseUtils.CommitInfo> mySkippedCommits;
|
||||
@NotNull private final MultiMap<GitRepository, GitRebaseUtils.CommitInfo> mySkippedCommits;
|
||||
|
||||
RebaseNotificationListener(@NotNull GitRepository currentRepository,
|
||||
MultiMap<GitRepository, GitRebaseUtils.CommitInfo> skippedCommits) {
|
||||
@NotNull MultiMap<GitRepository, GitRebaseUtils.CommitInfo> skippedCommits) {
|
||||
myCurrentRepository = currentRepository;
|
||||
mySkippedCommits = skippedCommits;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class GitRebaseStatus {
|
||||
SUCCESS,
|
||||
/**
|
||||
* Rebase started, and some commits were already applied,
|
||||
* but then rebase stopped because of conflicts, or because of an error.<br/>
|
||||
* but then rebase stopped because of conflicts, or to edit during interactive rebase, or because of an error.<br/>
|
||||
* Such rebase can be retried/continued by calling `git rebase --continue/--skip`, or
|
||||
* it can be aborted by calling `git rebase --abort`.
|
||||
*/
|
||||
|
||||
@@ -18,6 +18,7 @@ package git4idea.rebase
|
||||
import com.intellij.dvcs.DvcsUtil
|
||||
import com.intellij.openapi.progress.EmptyProgressIndicator
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.util.LineSeparator
|
||||
import git4idea.branch.GitRebaseParams
|
||||
import git4idea.repo.GitRepository
|
||||
import git4idea.test.GitExecutor.file
|
||||
@@ -174,7 +175,7 @@ class GitSingleRepoRebaseTest : GitRebaseBaseTest() {
|
||||
myRepo.`diverge feature and master`()
|
||||
val localChange = LocalChange(myRepo, "new.txt").generate()
|
||||
|
||||
object : GitTestingRebaseProcess(myProject, GitRebaseParams("master"), myRepo) {
|
||||
object : GitTestingRebaseProcess(myProject, simpleParams("master"), myRepo) {
|
||||
override fun getDirtyRoots(repositories: Collection<GitRepository>): Collection<GitRepository> {
|
||||
return listOf(myRepo)
|
||||
}
|
||||
@@ -190,7 +191,7 @@ class GitSingleRepoRebaseTest : GitRebaseBaseTest() {
|
||||
myRepo.`diverge feature and master`()
|
||||
val localChange = LocalChange(myRepo, "new.txt").generate()
|
||||
|
||||
object : GitTestingRebaseProcess(myProject, GitRebaseParams("master"), myRepo) {
|
||||
object : GitTestingRebaseProcess(myProject, simpleParams("master"), myRepo) {
|
||||
override fun getDirtyRoots(repositories: Collection<GitRepository>): Collection<GitRepository> {
|
||||
return emptyList()
|
||||
}
|
||||
@@ -392,12 +393,48 @@ class GitSingleRepoRebaseTest : GitRebaseBaseTest() {
|
||||
""")
|
||||
}
|
||||
|
||||
fun `test interactive rebase stopped for editing`() {
|
||||
build {
|
||||
master {
|
||||
0()
|
||||
1()
|
||||
}
|
||||
feature(1) {
|
||||
2()
|
||||
3()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
myGit.setInteractiveRebaseEditor {
|
||||
it.lines().mapIndexed { i, s ->
|
||||
if (i != 0) s
|
||||
else s.replace("pick", "edit")
|
||||
}.joinToString(LineSeparator.getSystemLineSeparator().separatorString)
|
||||
}
|
||||
|
||||
GitTestingRebaseProcess(myProject, GitRebaseParams(null, null, "master", true, false), myRepo).rebase()
|
||||
|
||||
assertSuccessfulNotification("Rebase Stopped for Editing", "Once you are satisfied with your changes you may <a href='continue'>continue</a>")
|
||||
assertEquals("The repository must be in the 'SUSPENDED' state", myRepo, myGitRepositoryManager.ongoingRebaseSpec!!.ongoingRebase)
|
||||
|
||||
GitRebaseUtils.continueRebase(myProject)
|
||||
|
||||
assertSuccessfulNotification("Rebased feature on master")
|
||||
myRepo.`assert feature rebased on master`()
|
||||
assertNoRebaseInProgress(myRepo)
|
||||
}
|
||||
|
||||
private fun build(f: RepoBuilder.() -> Unit) {
|
||||
build(myRepo, f)
|
||||
}
|
||||
|
||||
private fun rebaseOnMaster() {
|
||||
GitTestingRebaseProcess(myProject, GitRebaseParams("master"), myRepo).rebase()
|
||||
GitTestingRebaseProcess(myProject, simpleParams("master"), myRepo).rebase()
|
||||
}
|
||||
|
||||
private fun simpleParams(newBase: String): GitRebaseParams {
|
||||
return GitRebaseParams(newBase)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -216,8 +216,12 @@ abstract class GitPlatformTest : PlatformTestCase() {
|
||||
hookFile.setExecutable(true, false)
|
||||
}
|
||||
|
||||
protected fun assertSuccessfulNotification(title: String, message: String) {
|
||||
GitTestUtil.assertNotification(NotificationType.INFORMATION, title, message, myVcsNotifier.lastNotification)
|
||||
}
|
||||
|
||||
protected fun assertSuccessfulNotification(message: String) {
|
||||
GitTestUtil.assertNotification(NotificationType.INFORMATION, "Rebase Successful", message, myVcsNotifier.lastNotification)
|
||||
assertSuccessfulNotification("Rebase Successful", message)
|
||||
}
|
||||
|
||||
protected fun assertWarningNotification(title: String, message: String) {
|
||||
|
||||
@@ -15,12 +15,20 @@
|
||||
*/
|
||||
package git4idea.test
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import git4idea.branch.GitRebaseParams
|
||||
import git4idea.commands.GitCommandResult
|
||||
import git4idea.commands.GitImpl
|
||||
import git4idea.commands.GitLineHandler
|
||||
import git4idea.commands.GitLineHandlerListener
|
||||
import git4idea.rebase.GitInteractiveRebaseEditorHandler
|
||||
import git4idea.rebase.GitRebaseEditorService
|
||||
import git4idea.repo.GitRemote
|
||||
import git4idea.repo.GitRepository
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Any unknown error that could be returned by Git.
|
||||
@@ -28,9 +36,11 @@ import git4idea.repo.GitRepository
|
||||
val UNKNOWN_ERROR_TEXT: String = "unknown error"
|
||||
|
||||
class TestGitImpl : GitImpl() {
|
||||
private val LOG = Logger.getInstance(TestGitImpl::class.java)
|
||||
|
||||
private var myRebaseShouldFail: (GitRepository) -> Boolean = { false }
|
||||
private var myPushHandler: (GitRepository) -> GitCommandResult? = { null }
|
||||
@Volatile private var myRebaseShouldFail: (GitRepository) -> Boolean = { false }
|
||||
@Volatile private var myPushHandler: (GitRepository) -> GitCommandResult? = { null }
|
||||
@Volatile private var myInteractiveRebaseEditor: ((String) -> String)? = null
|
||||
|
||||
override fun push(repository: GitRepository,
|
||||
remote: GitRemote,
|
||||
@@ -67,6 +77,28 @@ class TestGitImpl : GitImpl() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun configureEditor(project: Project, root: VirtualFile, handler: GitLineHandler,
|
||||
commitListAware: Boolean): GitInteractiveRebaseEditorHandler {
|
||||
if (myInteractiveRebaseEditor == null) return super.configureEditor(project, root, handler, commitListAware)
|
||||
|
||||
val service = GitRebaseEditorService.getInstance()
|
||||
val editor = object: GitInteractiveRebaseEditorHandler(service, project, root, handler) {
|
||||
override fun editCommits(path: String?): Int {
|
||||
try {
|
||||
val file = File(path)
|
||||
FileUtil.writeToFile(file, myInteractiveRebaseEditor!!(FileUtil.loadFile(file)))
|
||||
}
|
||||
catch(e: Exception) {
|
||||
LOG.error(e)
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
service.configureHandler(handler, editor.handlerNo)
|
||||
return editor
|
||||
}
|
||||
|
||||
fun setShouldRebaseFail(shouldFail: (GitRepository) -> Boolean) {
|
||||
myRebaseShouldFail = shouldFail
|
||||
}
|
||||
@@ -75,9 +107,14 @@ class TestGitImpl : GitImpl() {
|
||||
myPushHandler = pushHandler;
|
||||
}
|
||||
|
||||
fun setInteractiveRebaseEditor(editor: (String) -> String) {
|
||||
myInteractiveRebaseEditor = editor
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
myRebaseShouldFail = { false }
|
||||
myPushHandler = { null }
|
||||
myInteractiveRebaseEditor = null
|
||||
}
|
||||
|
||||
private fun failOrCall(repository: GitRepository, delegate: () -> GitCommandResult): GitCommandResult {
|
||||
|
||||
Reference in New Issue
Block a user