From 27901e20aaacbcef397837eb2265d08078790030 Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Tue, 11 Apr 2017 20:23:54 +0300 Subject: [PATCH] [shelf]: implement show diff and show diff with local from shelvedView * for new and deleted files get content directly from patch/local file; * for modified files: - try to detect base if base found show diff local or base with patch applied on base; - if no base detected then show diff with local and patch applied to local (maybe somehow); --- .../src/idea/VcsActions.xml | 3 + .../patch/ApplyPatchForBaseRevisionTexts.java | 33 +++++- .../shelf/DiffShelvedChangesAction.java | 106 ++++++++++++++---- .../DiffShelvedChangesWithLocalAction.java | 25 +++++ 4 files changed, 141 insertions(+), 26 deletions(-) create mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesWithLocalAction.java diff --git a/platform/platform-resources/src/idea/VcsActions.xml b/platform/platform-resources/src/idea/VcsActions.xml index 738b0c6464b5..6e5e27906863 100644 --- a/platform/platform-resources/src/idea/VcsActions.xml +++ b/platform/platform-resources/src/idea/VcsActions.xml @@ -147,6 +147,9 @@ + diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchForBaseRevisionTexts.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchForBaseRevisionTexts.java index 9bd2f9814c5f..9ea5821099a0 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchForBaseRevisionTexts.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchForBaseRevisionTexts.java @@ -16,6 +16,7 @@ package com.intellij.openapi.vcs.changes.patch; import com.intellij.openapi.application.ReadAction; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.impl.patch.PatchHunk; import com.intellij.openapi.diff.impl.patch.TextFilePatch; import com.intellij.openapi.diff.impl.patch.apply.GenericPatchApplier; @@ -34,15 +35,22 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import static com.intellij.util.ObjectUtils.chooseNotNull; + public class ApplyPatchForBaseRevisionTexts { + + private static final Logger LOG = Logger.getInstance(ApplyPatchForBaseRevisionTexts.class); + private final CharSequence myLocal; private CharSequence myBase; private String myPatched; + private boolean myIsAppliedSomehow; private final List myWarnings; + private boolean myBaseRevisionLoaded; @NotNull @CalledInAny - public static ApplyPatchForBaseRevisionTexts create(final Project project, final VirtualFile file, final FilePath pathBeforeRename, + public static ApplyPatchForBaseRevisionTexts create(final Project project, @NotNull final VirtualFile file, final FilePath pathBeforeRename, final TextFilePatch patch, @Nullable final CharSequence baseContents) { assert ! patch.isNewFile(); final String beforeVersionId = patch.getBeforeVersionId(); @@ -61,7 +69,7 @@ public class ApplyPatchForBaseRevisionTexts { private ApplyPatchForBaseRevisionTexts(final DefaultPatchBaseVersionProvider provider, final FilePath pathBeforeRename, final TextFilePatch patch, - final VirtualFile file, + @NotNull final VirtualFile file, @Nullable CharSequence baseContents) { myWarnings = new ArrayList<>(); myLocal = getLocalFileContent(file); @@ -70,8 +78,13 @@ public class ApplyPatchForBaseRevisionTexts { if (baseContents != null) { myBase = StringUtil.convertLineSeparators(baseContents.toString()); + myBaseRevisionLoaded = true; final GenericPatchApplier applier = new GenericPatchApplier(myBase, hunks); if (!applier.execute()) { + myIsAppliedSomehow = true; + LOG.warn( + String.format("Patch for %s has wrong base and can't be applied properly", + chooseNotNull(patch.getBeforeName(), patch.getAfterName()))); applier.trySolveSomehow(); } setPatched(applier.getAfter()); @@ -86,6 +99,7 @@ public class ApplyPatchForBaseRevisionTexts { return true; } myBase = text; + myBaseRevisionLoaded = true; setPatched(applier.getAfter()); return false; }, myWarnings); @@ -97,7 +111,8 @@ public class ApplyPatchForBaseRevisionTexts { } final GenericPatchApplier applier = new GenericPatchApplier(myLocal, hunks); - if (! applier.execute()) { + if (!applier.execute()) { + myIsAppliedSomehow = true; applier.trySolveSomehow(); } setPatched(applier.getAfter()); @@ -121,6 +136,10 @@ public class ApplyPatchForBaseRevisionTexts { public CharSequence getBase() { return myBase; } + + public void clearBase() { + myBase = null; + } private void setPatched(final String text) { myPatched = StringUtil.convertLineSeparators(text); @@ -129,4 +148,12 @@ public class ApplyPatchForBaseRevisionTexts { public String getPatched() { return myPatched; } + + public boolean isAppliedSomehow() { + return myIsAppliedSomehow; + } + + public boolean isBaseRevisionLoaded() { + return myBaseRevisionLoaded; + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java index ac7578eaee77..34f02a38687a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java @@ -15,14 +15,19 @@ */ package com.intellij.openapi.vcs.changes.shelf; +import com.intellij.diff.DiffContentFactory; import com.intellij.diff.DiffDialogHints; import com.intellij.diff.DiffManager; import com.intellij.diff.actions.impl.GoToChangePopupBuilder; import com.intellij.diff.chains.DiffRequestChain; import com.intellij.diff.chains.DiffRequestProducer; import com.intellij.diff.chains.DiffRequestProducerException; +import com.intellij.diff.contents.DiffContent; import com.intellij.diff.requests.DiffRequest; +import com.intellij.diff.requests.SimpleDiffRequest; import com.intellij.diff.requests.UnknownFileTypeDiffRequest; +import com.intellij.diff.tools.util.DiffNotifications; +import com.intellij.diff.util.DiffUtil; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; @@ -47,10 +52,8 @@ import com.intellij.openapi.vcs.changes.CommitContext; import com.intellij.openapi.vcs.changes.FilePathsHelper; import com.intellij.openapi.vcs.changes.actions.diff.ChangeGoToChangePopupAction; import com.intellij.openapi.vcs.changes.patch.ApplyPatchForBaseRevisionTexts; -import com.intellij.openapi.vcs.changes.patch.PatchDiffRequestFactory; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Consumer; -import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.NotNull; @@ -61,9 +64,18 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.*; +import static com.intellij.openapi.vcs.changes.patch.PatchDiffRequestFactory.createConflictDiffRequest; +import static com.intellij.openapi.vcs.changes.patch.PatchDiffRequestFactory.createDiffRequest; +import static com.intellij.util.ObjectUtils.assertNotNull; import static com.intellij.util.ObjectUtils.chooseNotNull; public class DiffShelvedChangesAction extends AnAction implements DumbAware { + + private static final String DIFF_WITH_BASE_ERROR = "Base content not found or not applicable."; + public static final String SHELVED_VERSION = "Shelved Version"; + public static final String BASE_VERSION = "Base Version"; + public static final String CURRENT_VERSION = "Current Version"; + public void update(final AnActionEvent e) { e.getPresentation().setEnabled(isEnabled(e.getDataContext())); } @@ -80,19 +92,23 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware { } public static void showShelvedChangesDiff(final DataContext dc) { + showShelvedChangesDiff(dc, false); + } + + public static void showShelvedChangesDiff(final DataContext dc, boolean withLocal) { final Project project = CommonDataKeys.PROJECT.getData(dc); if (project == null) return; if (ChangeListManager.getInstance(project).isFreezedWithNotification(null)) return; List changeLists = ShelvedChangesViewManager.getShelvedLists(dc); - ShelvedChangeList changeList = ObjectUtils.assertNotNull(ContainerUtil.getFirstItem(changeLists)); + ShelvedChangeList changeList = assertNotNull(ContainerUtil.getFirstItem(changeLists)); final List textChanges = changeList.getChanges(project); final List binaryChanges = changeList.getBinaryFiles(); final List diffRequestProducers = new ArrayList<>(); - processTextChanges(project, textChanges, diffRequestProducers); + processTextChanges(project, textChanges, diffRequestProducers, withLocal); processBinaryFiles(project, binaryChanges, diffRequestProducers); Collections.sort(diffRequestProducers, ChangeDiffRequestComparator.getInstance()); @@ -141,7 +157,7 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware { public DiffRequest process(@NotNull UserDataHolder context, @NotNull ProgressIndicator indicator) throws DiffRequestProducerException, ProcessCanceledException { Change change = shelvedChange.createChange(project); - return PatchDiffRequestFactory.createDiffRequest(project, change, getName(), context, indicator); + return createDiffRequest(project, change, getName(), context, indicator); } }); } @@ -149,7 +165,8 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware { private static void processTextChanges(@NotNull final Project project, @NotNull List changesFromFirstList, - @NotNull List diffRequestProducers) { + @NotNull List diffRequestProducers, + boolean withLocal) { final String base = project.getBasePath(); final ApplyPatchContext patchContext = new ApplyPatchContext(project.getBaseDir(), 0, false, false); final PatchesPreloader preloader = new PatchesPreloader(project); @@ -185,27 +202,70 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware { if (!isNewFile && file.getFileType() == UnknownFileType.INSTANCE) { return new UnknownFileTypeDiffRequest(file, getName()); } + if (isNewFile) return createDiffRequest(project, shelvedChange.getChange(project), getName(), context, indicator); + + final CommitContext commitContext; + final TextFilePatch patch; + try { + commitContext = new CommitContext(); + patch = preloader.getPatch(shelvedChange, commitContext); + } + catch (VcsException e) { + throw new DiffRequestProducerException("Can't show diff for '" + getName() + "'", e); + } - if (shelvedChange.isConflictingChange(project)) { - try { - final CommitContext commitContext = new CommitContext(); - final TextFilePatch patch = preloader.getPatch(shelvedChange, commitContext); - final FilePath pathBeforeRename = patchContext.getPathBeforeRename(file); + if (patch.isDeletedFile()) { + return createDiffRequestForDeleted(patch); + } + return createDiffRequestForModified(patch, commitContext, context, indicator); + } - CharSequence baseContents = Extensions.findExtension(PatchEP.EP_NAME, project, BaseRevisionTextPatchEP.class) - .provideContent(chooseNotNull(patch.getAfterName(), patch.getBeforeName()), commitContext); - ApplyPatchForBaseRevisionTexts texts = - ApplyPatchForBaseRevisionTexts.create(project, file, pathBeforeRename, patch, baseContents); - return PatchDiffRequestFactory - .createConflictDiffRequest(project, file, patch, "Shelved Version", texts, getName()); - } - catch (VcsException e) { - throw new DiffRequestProducerException("Can't show diff for '" + getName() + "'", e); - } + @NotNull + private DiffRequest createDiffRequestForDeleted(@NotNull TextFilePatch patch) { + assert file != null; + DiffContentFactory contentFactory = DiffContentFactory.getInstance(); + DiffContent leftContent = withLocal + ? contentFactory.create(project, file) + : contentFactory.create(project, patch.getSingleHunkPatchText()); + return new SimpleDiffRequest(getName(), leftContent, + contentFactory.createEmpty(), + withLocal ? CURRENT_VERSION : SHELVED_VERSION, null); + } + + @NotNull + private DiffRequest createDiffRequestForModified(@NotNull TextFilePatch patch, + @NotNull CommitContext commitContext, + @NotNull UserDataHolder context, + @NotNull ProgressIndicator indicator) throws DiffRequestProducerException { + assert file != null; + CharSequence baseContents = Extensions.findExtension(PatchEP.EP_NAME, project, BaseRevisionTextPatchEP.class) + .provideContent(chooseNotNull(patch.getAfterName(), patch.getBeforeName()), commitContext); + ApplyPatchForBaseRevisionTexts texts = + ApplyPatchForBaseRevisionTexts.create(project, file, patchContext.getPathBeforeRename(file), patch, baseContents); + //found base + if (texts.isBaseRevisionLoaded() && !texts.isAppliedSomehow()) { + //normal diff + DiffContentFactory contentFactory = DiffContentFactory.getInstance(); + DiffContent leftContent = withLocal + ? contentFactory.create(project, file) + : contentFactory.create(project, texts.getBase().toString()); + return new SimpleDiffRequest(getName(), leftContent, contentFactory.create(project, texts.getPatched()), + withLocal ? CURRENT_VERSION : BASE_VERSION, SHELVED_VERSION); } else { - final Change change = shelvedChange.getChange(project); - return PatchDiffRequestFactory.createDiffRequest(project, change, getName(), context, indicator); + //try applying on local + if (texts.isAppliedSomehow()) { + texts.clearBase(); // wrong base should not be used even it exists + } + DiffRequest diffRequest = shelvedChange.isConflictingChange(project) + ? createConflictDiffRequest(project, file, patch, SHELVED_VERSION, texts, getName()) + : createDiffRequest(project, shelvedChange.getChange(project), getName(), context, indicator); + if (!withLocal) { + DiffUtil + .addNotification(DiffNotifications.createNotification(DIFF_WITH_BASE_ERROR + " Showing difference with local version"), + diffRequest); + } + return diffRequest; } } }); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesWithLocalAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesWithLocalAction.java new file mode 100644 index 000000000000..663b8197a98a --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesWithLocalAction.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2017 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.vcs.changes.shelf; + +import com.intellij.openapi.actionSystem.AnActionEvent; + +public class DiffShelvedChangesWithLocalAction extends DiffShelvedChangesAction { + @Override + public void actionPerformed(AnActionEvent e) { + showShelvedChangesDiff(e.getDataContext(), true); + } +}