mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[patch]: do best attempt to apply patch
* store failed patches; * if one filePatch has FAILED status it means we need to continue but store this info; * process added/deleted files and mark them as failed if preCheck failed; * refactor path verifier; * notify about failed status with detailed dialog info and rollback button (not implemented yet); * add labels to easily distinguish 'apply patch action' in local history dialog;
This commit is contained in:
+75
-64
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.intellij.openapi.diff.impl.patch.formove;
|
||||
|
||||
import com.intellij.history.Label;
|
||||
import com.intellij.history.LocalHistory;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
@@ -23,9 +25,6 @@ import com.intellij.openapi.diff.impl.patch.ApplyPatchStatus;
|
||||
import com.intellij.openapi.diff.impl.patch.FilePatch;
|
||||
import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase;
|
||||
import com.intellij.openapi.diff.impl.patch.apply.ApplyTextFilePatch;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileTypes.FileTypes;
|
||||
import com.intellij.openapi.fileTypes.ex.FileTypeChooser;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
@@ -63,6 +62,7 @@ public class PatchApplier<BinaryType extends FilePatch> {
|
||||
private final CommitContext myCommitContext;
|
||||
private final Consumer<Collection<FilePath>> myToTargetListsMover;
|
||||
@NotNull private final List<FilePatch> myRemainingPatches;
|
||||
@NotNull private final List<FilePatch> myFailedPatches;
|
||||
private final PathsVerifier<BinaryType> myVerifier;
|
||||
private boolean mySystemOperation;
|
||||
|
||||
@@ -84,6 +84,7 @@ public class PatchApplier<BinaryType extends FilePatch> {
|
||||
myLeftConflictPanelTitle = leftConflictPanelTitle;
|
||||
myRightConflictPanelTitle = rightConflictPanelTitle;
|
||||
myRemainingPatches = new ArrayList<FilePatch>();
|
||||
myFailedPatches = new ArrayList<FilePatch>();
|
||||
myVerifier = new PathsVerifier<BinaryType>(myProject, myBaseDirectory, myPatches, new PathsVerifier.BaseMapper() {
|
||||
@Override
|
||||
@Nullable
|
||||
@@ -132,6 +133,11 @@ public class PatchApplier<BinaryType extends FilePatch> {
|
||||
return myPatches;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Collection<FilePatch> getFailedPatches() {
|
||||
return myFailedPatches;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<BinaryType> getBinaryPatches() {
|
||||
return ContainerUtil.mapNotNull(myVerifier.getBinaryPatches(),
|
||||
@@ -165,17 +171,17 @@ public class PatchApplier<BinaryType extends FilePatch> {
|
||||
myRemainingPatches.addAll(myPatches);
|
||||
|
||||
final ApplyPatchStatus patchStatus = nonWriteActionPreCheck();
|
||||
if (ApplyPatchStatus.FAILURE.equals(patchStatus)) {
|
||||
myStatus = patchStatus;
|
||||
return;
|
||||
}
|
||||
|
||||
final Label beforeLabel = LocalHistory.getInstance().putSystemLabel(myProject, "Before patch");
|
||||
final TriggerAdditionOrDeletion trigger = new TriggerAdditionOrDeletion(myProject);
|
||||
final ApplyPatchStatus applyStatus = getApplyPatchStatus(trigger);
|
||||
myStatus = ApplyPatchStatus.SUCCESS.equals(patchStatus) ? applyStatus :
|
||||
ApplyPatchStatus.and(patchStatus, applyStatus);
|
||||
// listeners finished, all 'legal' file additions/deletions with VCS are done
|
||||
trigger.processIt();
|
||||
LocalHistory.getInstance().putSystemLabel(myProject, "After patch"); // insert a label to be visible in local history dialog
|
||||
if (myStatus == ApplyPatchStatus.FAILURE || myStatus == ApplyPatchStatus.ABORT) {
|
||||
suggestRollback(myProject, Collections.<PatchApplier>singletonList(PatchApplier.this), beforeLabel);
|
||||
}
|
||||
if(myShowNotification || !ApplyPatchStatus.SUCCESS.equals(myStatus)) {
|
||||
showApplyStatus(myProject, myStatus);
|
||||
}
|
||||
@@ -185,19 +191,16 @@ public class PatchApplier<BinaryType extends FilePatch> {
|
||||
@CalledInAwt
|
||||
@NotNull
|
||||
private ApplyPatchStatus getApplyPatchStatus(@NotNull final TriggerAdditionOrDeletion trigger) {
|
||||
final Ref<ApplyPatchStatus> refStatus = Ref.create(ApplyPatchStatus.FAILURE);
|
||||
final Ref<ApplyPatchStatus> refStatus = Ref.create(ApplyPatchStatus.SUCCESS);
|
||||
try {
|
||||
setConfirmationToDefault();
|
||||
CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!createFiles()) {
|
||||
refStatus.set(ApplyPatchStatus.FAILURE);
|
||||
return;
|
||||
}
|
||||
refStatus.set(createFiles());
|
||||
addSkippedItems(trigger);
|
||||
trigger.prepare();
|
||||
refStatus.set(executeWritable());
|
||||
refStatus.set(ApplyPatchStatus.and(refStatus.get(), executeWritable()));
|
||||
}
|
||||
}, VcsBundle.message("patch.apply.command"), null);
|
||||
}
|
||||
@@ -253,19 +256,16 @@ public class PatchApplier<BinaryType extends FilePatch> {
|
||||
ApplyPatchStatus result = ApplyPatchStatus.SUCCESS;
|
||||
for (PatchApplier patchApplier : group) {
|
||||
result = ApplyPatchStatus.and(result, patchApplier.nonWriteActionPreCheck());
|
||||
if (ApplyPatchStatus.FAILURE.equals(result)) return result;
|
||||
}
|
||||
final Label beforeLabel = LocalHistory.getInstance().putSystemLabel(project, "Before patch");
|
||||
final TriggerAdditionOrDeletion trigger = new TriggerAdditionOrDeletion(project);
|
||||
final Ref<ApplyPatchStatus> refStatus = new Ref<ApplyPatchStatus>(null);
|
||||
final Ref<ApplyPatchStatus> refStatus = new Ref<ApplyPatchStatus>(result);
|
||||
try {
|
||||
CommandProcessor.getInstance().executeCommand(project, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (PatchApplier applier : group) {
|
||||
if (! applier.createFiles()) {
|
||||
refStatus.set(ApplyPatchStatus.FAILURE);
|
||||
return;
|
||||
}
|
||||
refStatus.set(ApplyPatchStatus.and(refStatus.get(), applier.createFiles()));
|
||||
applier.addSkippedItems(trigger);
|
||||
}
|
||||
trigger.prepare();
|
||||
@@ -277,6 +277,7 @@ public class PatchApplier<BinaryType extends FilePatch> {
|
||||
}, VcsBundle.message("patch.apply.command"), null);
|
||||
} finally {
|
||||
VcsFileListenerContextHelper.getInstance(project).clearContext();
|
||||
LocalHistory.getInstance().putSystemLabel(project, "After patch");
|
||||
}
|
||||
result = refStatus.get();
|
||||
result = result == null ? ApplyPatchStatus.FAILURE : result;
|
||||
@@ -291,54 +292,81 @@ public class PatchApplier<BinaryType extends FilePatch> {
|
||||
directlyAffected.addAll(trigger.getAffected());
|
||||
final Consumer<Collection<FilePath>> mover = localChangeList == null ? null : createMover(project, localChangeList);
|
||||
refreshPassedFilesAndMoveToChangelist(project, directlyAffected, indirectlyAffected, mover);
|
||||
if (result == ApplyPatchStatus.FAILURE || result == ApplyPatchStatus.ABORT) {
|
||||
suggestRollback(project, group, beforeLabel);
|
||||
}
|
||||
showApplyStatus(project, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void suggestRollback(@NotNull Project project, @NotNull Collection<PatchApplier> group, @NotNull Label beforeLabel) {
|
||||
Collection<FilePatch> allFailed = ContainerUtil.concat(group, new Function<PatchApplier, Collection<? extends FilePatch>>() {
|
||||
@Override
|
||||
public Collection<FilePatch> fun(PatchApplier applier) {
|
||||
return applier.getFailedPatches();
|
||||
}
|
||||
});
|
||||
UndoApplyPatchDialog.rollbackApplyPatch(project, ContainerUtil.map(allFailed, new Function<FilePatch, FilePath>() {
|
||||
@Override
|
||||
public FilePath fun(FilePatch filePatch) {
|
||||
String path =
|
||||
filePatch.getAfterName() == null
|
||||
? filePatch.getBeforeName()
|
||||
: filePatch.getAfterName();
|
||||
return VcsUtil.getFilePath(path);
|
||||
}
|
||||
}), beforeLabel);
|
||||
}
|
||||
|
||||
protected void addSkippedItems(final TriggerAdditionOrDeletion trigger) {
|
||||
trigger.addExisting(myVerifier.getToBeAdded());
|
||||
trigger.addDeleted(myVerifier.getToBeDeleted());
|
||||
}
|
||||
|
||||
public ApplyPatchStatus nonWriteActionPreCheck() {
|
||||
final boolean value = myVerifier.nonWriteActionPreCheck();
|
||||
if (! value) return ApplyPatchStatus.FAILURE;
|
||||
|
||||
final List<FilePatch> failedPreCheck = myVerifier.nonWriteActionPreCheck();
|
||||
myFailedPatches.addAll(failedPreCheck);
|
||||
myPatches.removeAll(failedPreCheck);
|
||||
final List<FilePatch> skipped = myVerifier.getSkipped();
|
||||
final boolean applyAll = skipped.isEmpty();
|
||||
myPatches.removeAll(skipped);
|
||||
return applyAll ? ApplyPatchStatus.SUCCESS : ((skipped.size() == myPatches.size()) ? ApplyPatchStatus.ALREADY_APPLIED : ApplyPatchStatus.PARTIAL) ;
|
||||
if (!failedPreCheck.isEmpty()) return ApplyPatchStatus.FAILURE;
|
||||
return applyAll
|
||||
? ApplyPatchStatus.SUCCESS
|
||||
: ((skipped.size() == myPatches.size()) ? ApplyPatchStatus.ALREADY_APPLIED : ApplyPatchStatus.PARTIAL);
|
||||
}
|
||||
|
||||
protected ApplyPatchStatus executeWritable() {
|
||||
final ReadonlyStatusHandler.OperationStatus readOnlyFilesStatus = getReadOnlyFilesStatus(myVerifier.getWritableFiles());
|
||||
if (readOnlyFilesStatus.hasReadonlyFiles()) {
|
||||
showError(myProject, readOnlyFilesStatus.getReadonlyFilesMessage(), true);
|
||||
return ApplyPatchStatus.FAILURE;
|
||||
return ApplyPatchStatus.ABORT;
|
||||
}
|
||||
|
||||
myFailedPatches.addAll(myVerifier.filterBadFileTypePatches());
|
||||
ApplyPatchStatus result = myFailedPatches.isEmpty() ? ApplyPatchStatus.SUCCESS : ApplyPatchStatus.FAILURE;
|
||||
final List<Pair<VirtualFile, ApplyTextFilePatch>> textPatches = myVerifier.getTextPatches();
|
||||
if (!fileTypesAreOk(textPatches)) {
|
||||
return ApplyPatchStatus.FAILURE;
|
||||
}
|
||||
|
||||
try {
|
||||
markInternalOperation(textPatches, true);
|
||||
return actualApply(myVerifier, myCommitContext);
|
||||
return ApplyPatchStatus.and(result, actualApply(textPatches, myVerifier.getBinaryPatches(), myCommitContext));
|
||||
}
|
||||
finally {
|
||||
markInternalOperation(textPatches, false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean createFiles() {
|
||||
@NotNull
|
||||
private ApplyPatchStatus createFiles() {
|
||||
final Application application = ApplicationManager.getApplication();
|
||||
return application.runWriteAction(new Computable<Boolean>() {
|
||||
Boolean isSuccess = application.runWriteAction(new Computable<Boolean>() {
|
||||
@Override
|
||||
public Boolean compute() {
|
||||
return myVerifier.execute();
|
||||
final List<FilePatch> filePatches = myVerifier.execute();
|
||||
myFailedPatches.addAll(filePatches);
|
||||
myPatches.removeAll(filePatches);
|
||||
return myFailedPatches.isEmpty();
|
||||
}
|
||||
});
|
||||
return isSuccess ? ApplyPatchStatus.SUCCESS : ApplyPatchStatus.FAILURE;
|
||||
}
|
||||
|
||||
private static void markInternalOperation(List<Pair<VirtualFile, ApplyTextFilePatch>> textPatches, boolean set) {
|
||||
@@ -404,20 +432,21 @@ public class PatchApplier<BinaryType extends FilePatch> {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ApplyPatchStatus actualApply(final PathsVerifier<BinaryType> verifier, final CommitContext commitContext) {
|
||||
final List<Pair<VirtualFile, ApplyTextFilePatch>> textPatches = verifier.getTextPatches();
|
||||
private ApplyPatchStatus actualApply(final List<Pair<VirtualFile, ApplyTextFilePatch>> textPatches,
|
||||
final List<Pair<VirtualFile, ApplyFilePatchBase<BinaryType>>> binaryPatches,
|
||||
final CommitContext commitContext) {
|
||||
final ApplyPatchContext context = new ApplyPatchContext(myBaseDirectory, 0, true, true);
|
||||
ApplyPatchStatus status = null;
|
||||
ApplyPatchStatus status;
|
||||
|
||||
try {
|
||||
status = applyList(textPatches, context, status, commitContext);
|
||||
status = applyList(textPatches, context, null, commitContext);
|
||||
|
||||
if (status == ApplyPatchStatus.ABORT) return status;
|
||||
|
||||
if (myCustomForBinaries == null) {
|
||||
status = applyList(verifier.getBinaryPatches(), context, status, commitContext);
|
||||
} else {
|
||||
final List<Pair<VirtualFile, ApplyFilePatchBase<BinaryType>>> binaryPatches = verifier.getBinaryPatches();
|
||||
status = applyList(binaryPatches, context, status, commitContext);
|
||||
}
|
||||
else {
|
||||
ApplyPatchStatus patchStatus = myCustomForBinaries.apply(binaryPatches);
|
||||
final List<FilePatch> appliedPatches = myCustomForBinaries.getAppliedPatches();
|
||||
moveForCustomBinaries(binaryPatches, appliedPatches);
|
||||
@@ -428,7 +457,7 @@ public class PatchApplier<BinaryType extends FilePatch> {
|
||||
}
|
||||
catch (IOException e) {
|
||||
showError(myProject, e.getMessage(), true);
|
||||
return ApplyPatchStatus.FAILURE;
|
||||
return ApplyPatchStatus.ABORT;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -452,7 +481,10 @@ public class PatchApplier<BinaryType extends FilePatch> {
|
||||
|
||||
if (patchStatus == ApplyPatchStatus.ABORT) return patchStatus;
|
||||
status = ApplyPatchStatus.and(status, patchStatus);
|
||||
if (patchStatus == ApplyPatchStatus.FAILURE) return status;
|
||||
if (patchStatus == ApplyPatchStatus.FAILURE) {
|
||||
myFailedPatches.add(patch.getSecond().getPatch());
|
||||
continue;
|
||||
}
|
||||
if (patchStatus != ApplyPatchStatus.SKIP) {
|
||||
myVerifier.doMoveIfNeeded(patch.getFirst());
|
||||
myRemainingPatches.remove(patch.getSecond().getPatch());
|
||||
@@ -483,27 +515,6 @@ public class PatchApplier<BinaryType extends FilePatch> {
|
||||
return ReadonlyStatusHandler.getInstance(myProject).ensureFilesWritable(fileArray);
|
||||
}
|
||||
|
||||
private boolean fileTypesAreOk(final List<Pair<VirtualFile, ApplyTextFilePatch>> textPatches) {
|
||||
for (Pair<VirtualFile, ApplyTextFilePatch> textPatch : textPatches) {
|
||||
final VirtualFile file = textPatch.getFirst();
|
||||
if (! file.isDirectory()) {
|
||||
FileType fileType = file.getFileType();
|
||||
if (fileType == FileTypes.UNKNOWN) {
|
||||
fileType = FileTypeChooser.associateFileType(file.getName());
|
||||
if (fileType == null) {
|
||||
showError(myProject, "Cannot apply patch. File " + file.getPresentableName() + " type not defined.", true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (fileType.isBinary()) {
|
||||
showError(myProject, "Cannot apply patch because it contains binary file " + file.getPresentableName(), true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void showError(final Project project, final String message, final boolean error) {
|
||||
final Application application = ApplicationManager.getApplication();
|
||||
if (application.isUnitTestMode()) {
|
||||
|
||||
+61
-9
@@ -22,8 +22,12 @@ import com.intellij.openapi.diff.impl.patch.TextFilePatch;
|
||||
import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase;
|
||||
import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchFactory;
|
||||
import com.intellij.openapi.diff.impl.patch.apply.ApplyTextFilePatch;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileTypes.FileTypes;
|
||||
import com.intellij.openapi.fileTypes.ex.FileTypeChooser;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.ThrowableComputable;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
@@ -34,6 +38,8 @@ import com.intellij.openapi.vcs.changes.shelf.ShelvedBinaryFilePatch;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.vcsUtil.VcsUtil;
|
||||
import org.jetbrains.annotations.CalledInAwt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -122,26 +128,29 @@ public class PathsVerifier<BinaryType extends FilePatch> {
|
||||
}
|
||||
|
||||
@CalledInAwt
|
||||
public boolean nonWriteActionPreCheck() {
|
||||
public List<FilePatch> nonWriteActionPreCheck() {
|
||||
List<FilePatch> failedToApply = ContainerUtil.newArrayList();
|
||||
myDelayedPrecheckContext = new DelayedPrecheckContext(myProject);
|
||||
for (FilePatch patch : myPatches) {
|
||||
final CheckPath checker = getChecker(patch);
|
||||
if (! checker.canBeApplied(myDelayedPrecheckContext)) {
|
||||
if (!checker.canBeApplied(myDelayedPrecheckContext)) {
|
||||
revert(checker.getErrorMessage());
|
||||
return false;
|
||||
failedToApply.add(patch);
|
||||
}
|
||||
}
|
||||
final Collection<FilePatch> skipped = myDelayedPrecheckContext.doDelayed();
|
||||
mySkipped.addAll(skipped);
|
||||
myPatches.removeAll(skipped);
|
||||
return true;
|
||||
myPatches.removeAll(failedToApply);
|
||||
return failedToApply;
|
||||
}
|
||||
|
||||
public List<FilePatch> getSkipped() {
|
||||
return mySkipped;
|
||||
}
|
||||
|
||||
public boolean execute() {
|
||||
public List<FilePatch> execute() {
|
||||
List<FilePatch> failedPatches = ContainerUtil.newArrayList();
|
||||
try {
|
||||
final List<CheckPath> checkers = new ArrayList<CheckPath>(myPatches.size());
|
||||
for (FilePatch patch : myPatches) {
|
||||
@@ -149,17 +158,17 @@ public class PathsVerifier<BinaryType extends FilePatch> {
|
||||
checkers.add(checker);
|
||||
}
|
||||
for (CheckPath checker : checkers) {
|
||||
if (! checker.check()) {
|
||||
if (!checker.check()) {
|
||||
failedPatches.add(checker.getPatch());
|
||||
revert(checker.getErrorMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (IOException e) {
|
||||
revert(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
myPatches.removeAll(failedPatches);
|
||||
return failedPatches;
|
||||
}
|
||||
|
||||
private CheckPath getChecker(final FilePatch patch) {
|
||||
@@ -187,6 +196,44 @@ public class PathsVerifier<BinaryType extends FilePatch> {
|
||||
return myDeletedPaths;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Collection<FilePatch> filterBadFileTypePatches() {
|
||||
List<Pair<VirtualFile, ApplyTextFilePatch>> failedTextPatches =
|
||||
ContainerUtil.findAll(myTextPatches, new Condition<Pair<VirtualFile, ApplyTextFilePatch>>() {
|
||||
@Override
|
||||
public boolean value(Pair<VirtualFile, ApplyTextFilePatch> textPatch) {
|
||||
final VirtualFile file = textPatch.getFirst();
|
||||
if (file.isDirectory()) return false;
|
||||
return !isFileTypeOk(file);
|
||||
}
|
||||
});
|
||||
myTextPatches.removeAll(failedTextPatches);
|
||||
return ContainerUtil.map(failedTextPatches, new Function<Pair<VirtualFile, ApplyTextFilePatch>, FilePatch>() {
|
||||
@Override
|
||||
public FilePatch fun(Pair<VirtualFile, ApplyTextFilePatch> patchInfo) {
|
||||
return patchInfo.getSecond().getPatch();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isFileTypeOk(@NotNull VirtualFile file) {
|
||||
FileType fileType = file.getFileType();
|
||||
if (fileType == FileTypes.UNKNOWN) {
|
||||
fileType = FileTypeChooser.associateFileType(file.getName());
|
||||
if (fileType == null) {
|
||||
PatchApplier
|
||||
.showError(myProject,
|
||||
"Cannot apply file " + file.getPresentableName() + " from patch because its type not defined.", true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (fileType.isBinary()) {
|
||||
PatchApplier.showError(myProject, "Cannot apply file " + file.getPresentableName() + " from patch because it is binary.", true);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private class CheckModified extends CheckDeleted {
|
||||
private CheckModified(final FilePatch path) {
|
||||
super(path);
|
||||
@@ -343,6 +390,10 @@ public class PathsVerifier<BinaryType extends FilePatch> {
|
||||
private boolean inContent(VirtualFile file) {
|
||||
return myVcsManager.isFileInContent(file);
|
||||
}
|
||||
|
||||
public FilePatch getPatch() {
|
||||
return myPatch;
|
||||
}
|
||||
}
|
||||
|
||||
private void addPatch(final FilePatch patch, final VirtualFile file) {
|
||||
@@ -369,6 +420,7 @@ public class PathsVerifier<BinaryType extends FilePatch> {
|
||||
}
|
||||
|
||||
private void revert(final String errorMessage) {
|
||||
//todo show notification balloon
|
||||
PatchApplier.showError(myProject, errorMessage, true);
|
||||
|
||||
// move back
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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 com.intellij.openapi.diff.impl.patch.formove;
|
||||
|
||||
import com.intellij.history.Label;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vcs.FilePath;
|
||||
import com.intellij.openapi.vcs.changes.ui.FilePathChangesTreeList;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
|
||||
class UndoApplyPatchDialog extends DialogWrapper {
|
||||
|
||||
|
||||
private final List<FilePath> myFailedFilePaths;
|
||||
private final Label myBeforeLabel;
|
||||
private final Project myProject;
|
||||
|
||||
private UndoApplyPatchDialog(@NotNull Project project, @NotNull List<FilePath> filePaths, @NotNull Label beforeLabel) {
|
||||
super(project, true);
|
||||
myProject = project;
|
||||
setTitle("Patch Applying Partly Failed");
|
||||
setOKButtonText("Rollback");
|
||||
myFailedFilePaths = filePaths;
|
||||
myBeforeLabel = beforeLabel;
|
||||
init();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
final JPanel panel = new JPanel(new BorderLayout());
|
||||
int numFiles = myFailedFilePaths.size();
|
||||
String detailedText = numFiles == 0 ? "" : String.format("Failed to apply %s below. ", StringUtil.pluralize("file", numFiles));
|
||||
final JLabel infoLabel = new JBLabel(detailedText + "Would you like to rollback all applied?");
|
||||
panel.add(infoLabel, BorderLayout.NORTH);
|
||||
if (numFiles > 0) {
|
||||
FilePathChangesTreeList browser = new FilePathChangesTreeList(myProject, myFailedFilePaths, false, false, null, null) {
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
return new Dimension(infoLabel.getPreferredSize().width, 50);
|
||||
}
|
||||
};
|
||||
browser.setChangesToDisplay(myFailedFilePaths);
|
||||
panel.add(browser, BorderLayout.CENTER);
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOKAction() {
|
||||
super.doOKAction();
|
||||
}
|
||||
|
||||
static void rollbackApplyPatch(@NotNull Project project, @NotNull List<FilePath> filePaths,
|
||||
@NotNull Label historyLabel) {
|
||||
new UndoApplyPatchDialog(project, filePaths, historyLabel).show();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user