[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);
This commit is contained in:
Nadya Zabrodina
2017-05-17 17:22:50 +03:00
parent 2649c411a4
commit 27901e20aa
4 changed files with 141 additions and 26 deletions
@@ -147,6 +147,9 @@
<action id="ShelvedChanges.Restore" class="com.intellij.openapi.vcs.changes.shelf.RestoreShelvedChange"/>
<action id="ShelvedChanges.Diff" class="com.intellij.openapi.vcs.changes.shelf.DiffShelvedChangesAction"
icon="AllIcons.Actions.Diff" use-shortcut-of="Diff.ShowDiff"/>
<action id="ShelvedChanges.DiffWithLocal" class="com.intellij.openapi.vcs.changes.shelf.DiffShelvedChangesWithLocalAction"
text="Compare with Local" description="Compare shelved version with current" use-shortcut-of="Vcs.ShowDiffWithLocal"
icon="AllIcons.Actions.DiffWithCurrent"/>
<action id="ChangesView.CreatePatchFromChanges" class="com.intellij.openapi.vcs.changes.actions.CreatePatchFromChangesAction"
icon="AllIcons.Vcs.Patch"/>
<action id="ShelvedChanges.ImportPatches" class="com.intellij.openapi.vcs.changes.shelf.ImportIntoShelfAction"/>
@@ -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<String> 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;
}
}
@@ -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<ShelvedChangeList> changeLists = ShelvedChangesViewManager.getShelvedLists(dc);
ShelvedChangeList changeList = ObjectUtils.assertNotNull(ContainerUtil.getFirstItem(changeLists));
ShelvedChangeList changeList = assertNotNull(ContainerUtil.getFirstItem(changeLists));
final List<ShelvedChange> textChanges = changeList.getChanges(project);
final List<ShelvedBinaryFile> binaryChanges = changeList.getBinaryFiles();
final List<MyDiffRequestProducer> 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<ShelvedChange> changesFromFirstList,
@NotNull List<MyDiffRequestProducer> diffRequestProducers) {
@NotNull List<MyDiffRequestProducer> 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;
}
}
});
@@ -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);
}
}