[vcs-log] add an option to show only affected files in the changes browser

This commit is contained in:
Julia Beliaeva
2019-02-05 22:24:13 +03:00
parent 6530efbcf3
commit 23cdce0e15
8 changed files with 88 additions and 12 deletions
@@ -85,6 +85,7 @@
<action class="com.intellij.vcs.log.ui.actions.ShowDetailsAction" id="Vcs.Log.ShowDetailsAction"/>
<action class="com.intellij.vcs.log.ui.actions.ShowDiffPreviewAction" id="Vcs.Log.ShowDiffPreview"/>
<action class="com.intellij.vcs.log.ui.actions.ShowChangesFromParentsAction" id="Vcs.Log.ShowChangesFromParents"/>
<action class="com.intellij.vcs.log.ui.actions.ShowOnlyAffectedChangesAction" id="Vcs.Log.ShowOnlyAffectedChanges"/>
<action class="com.intellij.vcs.log.ui.actions.ShowRootsColumnAction" id="Vcs.Log.ShowRootsColumnAction"/>
<action class="com.intellij.vcs.log.ui.actions.FocusTextFilterAction" id="Vcs.Log.FocusTextFilter">
<keyboard-shortcut first-keystroke="control L" keymap="$default"/>
@@ -154,6 +155,7 @@
</group>
<group id="Vcs.Log.ChangesBrowser.Toolbar">
<reference id="Vcs.RepositoryChangesBrowserToolbar"/>
<reference id="Vcs.Log.ShowOnlyAffectedChanges"/>
<separator/>
<reference id="ChangesView.GroupBy"/>
<separator/>
@@ -58,7 +58,7 @@ public class FileHistoryUtil {
return Collections.emptyList();
}
static boolean affectsFile(@NotNull Change change, @NotNull FilePath file, boolean isDeleted) {
public static boolean affectsFile(@NotNull Change change, @NotNull FilePath file, boolean isDeleted) {
ContentRevision revision = isDeleted ? change.getBeforeRevision() : change.getAfterRevision();
if (revision == null) return false;
return file.equals(revision.getFile());
@@ -8,10 +8,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.*;
import com.intellij.vcs.log.data.VcsLogData;
import com.intellij.vcs.log.impl.HashImpl;
import com.intellij.vcs.log.impl.VcsLogContentUtil;
import com.intellij.vcs.log.impl.VcsLogManager;
import com.intellij.vcs.log.impl.VcsProjectLog;
import com.intellij.vcs.log.impl.*;
import com.intellij.vcs.log.ui.AbstractVcsLogUi;
import com.intellij.vcs.log.ui.VcsLogUiImpl;
import com.intellij.vcs.log.util.VcsLogUtil;
@@ -94,6 +91,7 @@ public class VcsLogFileHistoryProviderImpl implements VcsLogFileHistoryProvider
if (firstTime) {
VcsLogFilterCollection filters = createFilters(path, hash, root);
ui = VcsProjectLog.getInstance(project).getTabsManager().openAnotherLogTab(logManager, filters);
ui.getProperties().set(MainVcsLogUiProperties.SHOW_ONLY_AFFECTED_CHANGES, true);
}
consumer.accept(ui, firstTime);
}
@@ -33,6 +33,7 @@ public interface MainVcsLogUiProperties extends VcsLogUiProperties {
VcsLogUiProperty<Boolean> TEXT_FILTER_MATCH_CASE = new VcsLogUiProperty<>("TextFilter.MatchCase");
VcsLogUiProperty<Boolean> TEXT_FILTER_REGEX = new VcsLogUiProperty<>("TextFilter.Regex");
VcsLogUiProperty<Boolean> SHOW_CHANGES_FROM_PARENTS = new VcsLogUiProperty<>("Changes.ShowChangesFromParents");
VcsLogUiProperty<Boolean> SHOW_ONLY_AFFECTED_CHANGES = new VcsLogUiProperty<>("Changes.ShowOnlyAffected");
void addRecentlyFilteredGroup(@NotNull String filterName, @NotNull Collection<String> values);
@@ -33,6 +33,7 @@ public abstract class VcsLogUiPropertiesImpl<S extends VcsLogUiPropertiesImpl.St
MainVcsLogUiProperties.SHOW_LONG_EDGES,
MainVcsLogUiProperties.BEK_SORT_TYPE,
CommonUiProperties.SHOW_ROOT_NAMES,
MainVcsLogUiProperties.SHOW_ONLY_AFFECTED_CHANGES,
MainVcsLogUiProperties.TEXT_FILTER_MATCH_CASE,
MainVcsLogUiProperties.TEXT_FILTER_REGEX,
CommonUiProperties.COLUMN_ORDER);
@@ -48,6 +49,7 @@ public abstract class VcsLogUiPropertiesImpl<S extends VcsLogUiPropertiesImpl.St
public boolean LONG_EDGES_VISIBLE = false;
public int BEK_SORT_TYPE = 0;
public boolean SHOW_ROOT_NAMES = false;
public boolean SHOW_ONLY_AFFECTED_CHANGES = false;
@Deprecated
public Deque<UserGroup> RECENTLY_FILTERED_USER_GROUPS = new ArrayDeque<>();
@Deprecated
@@ -80,6 +82,9 @@ public abstract class VcsLogUiPropertiesImpl<S extends VcsLogUiPropertiesImpl.St
else if (CommonUiProperties.SHOW_ROOT_NAMES.equals(property)) {
return (T)Boolean.valueOf(getState().SHOW_ROOT_NAMES);
}
else if (SHOW_ONLY_AFFECTED_CHANGES.equals(property)) {
return (T)Boolean.valueOf(getState().SHOW_ONLY_AFFECTED_CHANGES);
}
else if (BEK_SORT_TYPE.equals(property)) {
return (T)PermanentGraph.SortType.values()[getState().BEK_SORT_TYPE];
}
@@ -124,6 +129,9 @@ public abstract class VcsLogUiPropertiesImpl<S extends VcsLogUiPropertiesImpl.St
else if (CommonUiProperties.SHOW_ROOT_NAMES.equals(property)) {
getState().SHOW_ROOT_NAMES = (Boolean)value;
}
else if (SHOW_ONLY_AFFECTED_CHANGES.equals(property)) {
getState().SHOW_ONLY_AFFECTED_CHANGES = (Boolean)value;
}
else if (BEK_SORT_TYPE.equals(property)) {
getState().BEK_SORT_TYPE = ((PermanentGraph.SortType)value).ordinal();
}
@@ -0,0 +1,31 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.vcs.log.ui.actions;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.vcs.log.impl.MainVcsLogUiProperties;
import com.intellij.vcs.log.impl.VcsLogUiProperties;
import org.jetbrains.annotations.NotNull;
public class ShowOnlyAffectedChangesAction extends BooleanPropertyToggleAction {
public ShowOnlyAffectedChangesAction() {
super("Show Only Affected Changes", "Show only changes that affect files that were selected in the \"Paths\" menu",
AllIcons.Nodes.Folder);
}
@Override
protected VcsLogUiProperties.VcsLogUiProperty<Boolean> getProperty() {
return MainVcsLogUiProperties.SHOW_ONLY_AFFECTED_CHANGES;
}
@Override
public void update(@NotNull AnActionEvent e) {
if (!Registry.is("vcs.folder.history.in.log")) {
e.getPresentation().setEnabledAndVisible(false);
} else {
super.update(e);
}
}
}
@@ -169,6 +169,8 @@ public class MainFrame extends JPanel implements DataProvider, Disposable {
public void updateDataPack(@NotNull VisiblePack dataPack, boolean permGraphChanged) {
myFilterUi.updateDataPack(dataPack);
myGraphTable.updateDataPack(dataPack, permGraphChanged);
VcsLogStructureFilter structureFilter = dataPack.getFilters().get(VcsLogFilterCollection.STRUCTURE_FILTER);
myChangesBrowser.setAffectedPaths(structureFilter != null ? structureFilter.getFiles() : null);
}
@NotNull
@@ -20,6 +20,7 @@ import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.SideBorder;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.components.panels.Wrapper;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
@@ -30,6 +31,7 @@ import com.intellij.vcs.log.VcsShortCommitDetails;
import com.intellij.vcs.log.data.LoadingDetails;
import com.intellij.vcs.log.data.index.IndexedDetails;
import com.intellij.vcs.log.history.FileHistoryKt;
import com.intellij.vcs.log.history.FileHistoryUtil;
import com.intellij.vcs.log.impl.MainVcsLogUiProperties;
import com.intellij.vcs.log.impl.MergedChange;
import com.intellij.vcs.log.impl.MergedChangeDiffRequestProvider;
@@ -51,6 +53,7 @@ import static com.intellij.diff.util.DiffUserDataKeysEx.*;
import static com.intellij.util.ObjectUtils.notNull;
import static com.intellij.util.containers.ContainerUtil.getFirstItem;
import static com.intellij.vcs.log.impl.MainVcsLogUiProperties.SHOW_CHANGES_FROM_PARENTS;
import static com.intellij.vcs.log.impl.MainVcsLogUiProperties.SHOW_ONLY_AFFECTED_CHANGES;
/**
* Change browser for commits in the Log. For merge commits, can display changes to commits parents in separate groups.
@@ -66,6 +69,7 @@ public class VcsLogChangesBrowser extends ChangesBrowserBase implements Disposab
@NotNull private final Set<VirtualFile> myRoots = ContainerUtil.newHashSet();
@NotNull private final List<Change> myChanges = ContainerUtil.newArrayList();
@NotNull private final Map<CommitId, Set<Change>> myChangesToParents = ContainerUtil.newHashMap();
@Nullable private Collection<FilePath> myAffectedPaths;
@NotNull private final Wrapper myToolbarWrapper;
@Nullable private Runnable myModelUpdateListener;
@@ -81,7 +85,7 @@ public class VcsLogChangesBrowser extends ChangesBrowserBase implements Disposab
myListener = new VcsLogUiProperties.PropertiesChangeListener() {
@Override
public <T> void onPropertyChanged(@NotNull VcsLogUiProperties.VcsLogUiProperty<T> property) {
if (SHOW_CHANGES_FROM_PARENTS.equals(property)) {
if (SHOW_CHANGES_FROM_PARENTS.equals(property) || SHOW_ONLY_AFFECTED_CHANGES.equals(property)) {
myViewer.rebuildTree();
}
}
@@ -150,6 +154,11 @@ public class VcsLogChangesBrowser extends ChangesBrowserBase implements Disposab
if (myModelUpdateListener != null) myModelUpdateListener.run();
}
public void setAffectedPaths(@Nullable Collection<FilePath> paths) {
myAffectedPaths = paths;
myViewer.rebuildTree();
}
public void setSelectedDetails(@NotNull List<? extends VcsFullCommitDetails> detailsList) {
myChanges.clear();
myChangesToParents.clear();
@@ -192,15 +201,21 @@ public class VcsLogChangesBrowser extends ChangesBrowserBase implements Disposab
@NotNull
@Override
protected DefaultTreeModel buildTreeModel() {
MyTreeModelBuilder builder = new MyTreeModelBuilder();
builder.setChanges(myChanges, null);
Collection<Change> changes = collectAffectedChanges(myChanges);
Map<CommitId, Collection<Change>> changesToParents = ContainerUtil.newHashMap();
for (Map.Entry<CommitId, Set<Change>> entry : myChangesToParents.entrySet()) {
changesToParents.put(entry.getKey(), collectAffectedChanges(entry.getValue()));
}
if (isShowChangesFromParents() && !myChangesToParents.isEmpty()) {
if (myChanges.isEmpty()) {
MyTreeModelBuilder builder = new MyTreeModelBuilder();
builder.setChanges(changes, null);
if (isShowChangesFromParents() && !changesToParents.isEmpty()) {
if (changes.isEmpty()) {
builder.addEmptyTextNode("No merged conflicts");
}
for (CommitId commitId : myChangesToParents.keySet()) {
Collection<Change> changesFromParent = myChangesToParents.get(commitId);
for (CommitId commitId : changesToParents.keySet()) {
Collection<Change> changesFromParent = changesToParents.get(commitId);
if (!changesFromParent.isEmpty()) {
builder.addChangesFromParentNode(changesFromParent, commitId);
}
@@ -210,11 +225,30 @@ public class VcsLogChangesBrowser extends ChangesBrowserBase implements Disposab
return builder.build();
}
@NotNull
private Collection<Change> collectAffectedChanges(@NotNull Collection<Change> changes) {
if (!isShowOnlyAffected() || myAffectedPaths == null) return changes;
return ContainerUtil.filter(changes, change -> ContainerUtil.or(myAffectedPaths, filePath -> {
if (filePath.isDirectory()) {
return FileHistoryUtil.affectsDirectory(change, filePath);
}
else {
return FileHistoryUtil.affectsFile(change, filePath, false) ||
FileHistoryUtil.affectsFile(change, filePath, true);
}
}));
}
private boolean isShowChangesFromParents() {
return myUiProperties.exists(SHOW_CHANGES_FROM_PARENTS) &&
myUiProperties.get(SHOW_CHANGES_FROM_PARENTS);
}
private boolean isShowOnlyAffected() {
return myUiProperties.exists(SHOW_ONLY_AFFECTED_CHANGES) &&
myUiProperties.get(SHOW_ONLY_AFFECTED_CHANGES);
}
@NotNull
public List<Change> getDirectChanges() {
return myChanges;