[vcs-log] introduce support for the "--no-merges" option

IDEA-92263

GitOrigin-RevId: 68333cfa5e1b286d22fe7d3089abf385bd25c2a9
This commit is contained in:
Julia Beliaeva
2024-03-22 00:43:23 +01:00
committed by intellij-monorepo-bot
parent b03be48ffb
commit 3abc949823
20 changed files with 270 additions and 55 deletions

View File

@@ -129,6 +129,7 @@ com.intellij.vcs.log.VcsLogFilterCollection
- sf:BRANCH_FILTER:com.intellij.vcs.log.VcsLogFilterCollection$FilterKey
- sf:DATE_FILTER:com.intellij.vcs.log.VcsLogFilterCollection$FilterKey
- sf:HASH_FILTER:com.intellij.vcs.log.VcsLogFilterCollection$FilterKey
- sf:PARENT_FILTER:com.intellij.vcs.log.VcsLogFilterCollection$FilterKey
- sf:RANGE_FILTER:com.intellij.vcs.log.VcsLogFilterCollection$FilterKey
- sf:REVISION_FILTER:com.intellij.vcs.log.VcsLogFilterCollection$FilterKey
- sf:ROOT_FILTER:com.intellij.vcs.log.VcsLogFilterCollection$FilterKey
@@ -177,6 +178,11 @@ com.intellij.vcs.log.VcsLogObjectsFactory
- a:createShortDetails(com.intellij.vcs.log.Hash,java.util.List,J,com.intellij.openapi.vfs.VirtualFile,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,J):com.intellij.vcs.log.VcsShortCommitDetails
- a:createTimedCommit(com.intellij.vcs.log.Hash,java.util.List,J):com.intellij.vcs.log.TimedVcsCommit
- a:createUser(java.lang.String,java.lang.String):com.intellij.vcs.log.VcsUser
com.intellij.vcs.log.VcsLogParentFilter
- com.intellij.vcs.log.VcsLogFilter
- getKey():com.intellij.vcs.log.VcsLogFilterCollection$FilterKey
- a:getMaxParents():I
- a:getMinParents():I
f:com.intellij.vcs.log.VcsLogProperties
- sf:CASE_INSENSITIVE_REGEX:com.intellij.vcs.log.VcsLogProperties$VcsLogProperty
- sf:HAS_COMMITTER:com.intellij.vcs.log.VcsLogProperties$VcsLogProperty
@@ -184,6 +190,7 @@ f:com.intellij.vcs.log.VcsLogProperties
- sf:SUPPORTS_INCREMENTAL_REFRESH:com.intellij.vcs.log.VcsLogProperties$VcsLogProperty
- sf:SUPPORTS_INDEXING:com.intellij.vcs.log.VcsLogProperties$VcsLogProperty
- sf:SUPPORTS_LOG_DIRECTORY_HISTORY:com.intellij.vcs.log.VcsLogProperties$VcsLogProperty
- sf:SUPPORTS_PARENTS_FILTER:com.intellij.vcs.log.VcsLogProperties$VcsLogProperty
- <init>():V
f:com.intellij.vcs.log.VcsLogProperties$VcsLogProperty
- getOrDefault(com.intellij.vcs.log.VcsLogProvider):java.lang.Object

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.vcs.log;
import com.intellij.util.containers.ContainerUtil;
@@ -25,10 +25,12 @@ public interface VcsLogFilterCollection {
FilterKey<VcsLogTextFilter> TEXT_FILTER = FilterKey.create("text");
FilterKey<VcsLogStructureFilter> STRUCTURE_FILTER = FilterKey.create("structure");
FilterKey<VcsLogRootFilter> ROOT_FILTER = FilterKey.create("roots");
FilterKey<VcsLogParentFilter> PARENT_FILTER = FilterKey.create("parent");
Collection<FilterKey<? extends VcsLogFilter>> STANDARD_KEYS = List.of(BRANCH_FILTER, REVISION_FILTER, RANGE_FILTER,
USER_FILTER, HASH_FILTER, DATE_FILTER,
TEXT_FILTER, STRUCTURE_FILTER, ROOT_FILTER);
TEXT_FILTER, STRUCTURE_FILTER, ROOT_FILTER,
PARENT_FILTER);
@Nullable <T extends VcsLogFilter> T get(@NotNull FilterKey<T> key);

View File

@@ -0,0 +1,10 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.vcs.log
interface VcsLogParentFilter : VcsLogFilter {
val minParents: Int
val maxParents: Int
override fun getKey(): VcsLogFilterCollection.FilterKey<*> = VcsLogFilterCollection.PARENT_FILTER
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2020 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.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.vcs.log;
import org.jetbrains.annotations.NotNull;
@@ -30,4 +30,8 @@ public final class VcsLogProperties {
* True if VCS allows incrementally refresh commits in the log. False if full refresh should be performed.
*/
@NotNull public static final VcsLogProperty<Boolean> SUPPORTS_INCREMENTAL_REFRESH = new VcsLogProperty<>(true);
/**
* True if ths {@link VcsLogProvider} implementation supports filtering commits by parents count.
*/
@NotNull public static final VcsLogProperty<Boolean> SUPPORTS_PARENTS_FILTER = new VcsLogProperty<>(true);
}

View File

@@ -1320,7 +1320,6 @@ f:com.intellij.vcs.log.ui.VcsLogActionIds
- sf:CHANGES_BROWSER_TOOLBAR_ACTION_GROUP:java.lang.String
- sf:FILE_HISTORY_TOOLBAR_ACTION_GROUP:java.lang.String
- sf:FILE_HISTORY_TOOLBAR_RIGHT_CORNER_ACTION_GROUP:java.lang.String
- sf:GRAPH_OPTIONS_GROUP:java.lang.String
- sf:HISTORY_POPUP_ACTION_GROUP:java.lang.String
- sf:POPUP_ACTION_GROUP:java.lang.String
- sf:TEXT_FILTER_SETTINGS_ACTION_GROUP:java.lang.String
@@ -1702,13 +1701,6 @@ c:com.intellij.vcs.log.ui.actions.VcsLogCreatePatchActionProvider$Clipboard
c:com.intellij.vcs.log.ui.actions.VcsLogCreatePatchActionProvider$Dialog
- com.intellij.vcs.log.ui.actions.VcsLogCreatePatchActionProvider
- <init>():V
c:com.intellij.vcs.log.ui.actions.VcsLogGraphOptionsChooserGroup
- com.intellij.openapi.actionSystem.DefaultActionGroup
- com.intellij.openapi.project.DumbAware
- <init>():V
- getActionUpdateThread():com.intellij.openapi.actionSystem.ActionUpdateThread
- getChildren(com.intellij.openapi.actionSystem.AnActionEvent):com.intellij.openapi.actionSystem.AnAction[]
- update(com.intellij.openapi.actionSystem.AnActionEvent):V
c:com.intellij.vcs.log.ui.actions.VcsLogToolbarPopupActionGroup
- com.intellij.openapi.actionSystem.DefaultActionGroup
- com.intellij.openapi.project.DumbAware
@@ -1946,7 +1938,6 @@ f:com.intellij.vcs.log.ui.filter.BranchFilterModel
- f:getRangeFilter():com.intellij.vcs.log.VcsLogRangeFilter
- f:getRevisionFilter():com.intellij.vcs.log.VcsLogRevisionFilter
- f:getVisibleRoots():java.util.Collection
- f:onStructureFilterChanged(com.intellij.vcs.log.VcsLogRootFilter,com.intellij.vcs.log.VcsLogStructureFilter):V
- f:setBranchFilter(com.intellij.vcs.log.VcsLogBranchFilter):V
- f:setRangeFilter(com.intellij.vcs.log.VcsLogRangeFilter):V
- f:setRevisionFilter(com.intellij.vcs.log.VcsLogRevisionFilter):V
@@ -2047,6 +2038,19 @@ a:com.intellij.vcs.log.ui.filter.FilterModel$SingleFilterModel
- pa:getFilterValues(com.intellij.vcs.log.VcsLogFilter):java.util.List
- p:saveFilterToProperties(com.intellij.vcs.log.VcsLogFilter):V
- setFilter(com.intellij.vcs.log.VcsLogFilter):V
f:com.intellij.vcs.log.ui.filter.NoMergesFilterAction
- com.intellij.openapi.project.DumbAwareToggleAction
- <init>(com.intellij.vcs.log.ui.filter.ParentFilterModel):V
- getActionUpdateThread():com.intellij.openapi.actionSystem.ActionUpdateThread
- isSelected(com.intellij.openapi.actionSystem.AnActionEvent):Z
- setSelected(com.intellij.openapi.actionSystem.AnActionEvent,Z):V
- update(com.intellij.openapi.actionSystem.AnActionEvent):V
f:com.intellij.vcs.log.ui.filter.ParentFilterModel
- com.intellij.vcs.log.ui.filter.FilterModel$SingleFilterModel
- <init>(com.intellij.vcs.log.impl.MainVcsLogUiProperties,java.util.Map,kotlin.jvm.functions.Function0,com.intellij.vcs.log.VcsLogFilterCollection):V
- b:<init>(com.intellij.vcs.log.impl.MainVcsLogUiProperties,java.util.Map,kotlin.jvm.functions.Function0,com.intellij.vcs.log.VcsLogFilterCollection,I,kotlin.jvm.internal.DefaultConstructorMarker):V
- f:isEnabled():Z
- f:isVisible():Z
c:com.intellij.vcs.log.ui.filter.StructureFilterPopupComponent
- com.intellij.util.ui.FilterComponent
- psf:ALL_ACTION_TEXT:java.util.function.Supplier
@@ -2121,6 +2125,7 @@ c:com.intellij.vcs.log.ui.filter.VcsLogClassicFilterUi
- pf:getBranchFilterModel():com.intellij.vcs.log.ui.filter.BranchFilterModel
- pf:getDateFilterModel():com.intellij.vcs.log.ui.filter.DateFilterModel
- getFilters():com.intellij.vcs.log.VcsLogFilterCollection
- pf:getParentFilterModel():com.intellij.vcs.log.ui.filter.ParentFilterModel
- pf:getStructureFilterModel():com.intellij.vcs.log.ui.filter.FileFilterModel
- getTextFilterComponent():com.intellij.vcs.log.ui.filter.VcsLogTextFilterField
- pf:getTextFilterModel():com.intellij.vcs.log.ui.filter.TextFilterModel
@@ -2144,6 +2149,13 @@ pf:com.intellij.vcs.log.ui.filter.VcsLogClassicFilterUi$MainUiActionComponent
*:com.intellij.vcs.log.ui.filter.VcsLogFilterUiEx$VcsLogFilterListener
- java.util.EventListener
- a:onFiltersChanged():V
c:com.intellij.vcs.log.ui.filter.VcsLogGraphOptionsChooserGroup
- com.intellij.openapi.actionSystem.DefaultActionGroup
- com.intellij.openapi.project.DumbAware
- <init>(com.intellij.vcs.log.ui.filter.ParentFilterModel):V
- getActionUpdateThread():com.intellij.openapi.actionSystem.ActionUpdateThread
- getChildren(com.intellij.openapi.actionSystem.AnActionEvent):com.intellij.openapi.actionSystem.AnAction[]
- update(com.intellij.openapi.actionSystem.AnActionEvent):V
a:com.intellij.vcs.log.ui.filter.VcsLogPopupComponentAction
- com.intellij.openapi.project.DumbAwareAction
- com.intellij.openapi.actionSystem.ex.CustomComponentAction
@@ -2864,6 +2876,8 @@ f:com.intellij.vcs.log.visible.filters.VcsLogFilterObject
- sf:fromDates(java.util.Date,java.util.Date):com.intellij.vcs.log.VcsLogDateFilter
- sf:fromHash(java.lang.String):com.intellij.vcs.log.VcsLogHashFilter
- sf:fromHashes(java.util.Collection):com.intellij.vcs.log.VcsLogHashFilter
- sf:fromParentCount(java.lang.Integer,java.lang.Integer):com.intellij.vcs.log.VcsLogParentFilter
- bs:fromParentCount$default(java.lang.Integer,java.lang.Integer,I,java.lang.Object):com.intellij.vcs.log.VcsLogParentFilter
- sf:fromPaths(java.util.Collection):com.intellij.vcs.log.VcsLogStructureFilter
- sf:fromPattern(java.lang.String,Z,Z):com.intellij.vcs.log.VcsLogTextFilter
- bs:fromPattern$default(java.lang.String,Z,Z,I,java.lang.Object):com.intellij.vcs.log.VcsLogTextFilter
@@ -2878,6 +2892,7 @@ f:com.intellij.vcs.log.visible.filters.VcsLogFilterObject
- sf:fromUserNames(java.util.Collection,com.intellij.vcs.log.data.VcsLogData):com.intellij.vcs.log.VcsLogUserFilter
- sf:fromUserNames(java.util.Collection,java.util.Map,java.util.Set):com.intellij.vcs.log.VcsLogUserFilter
- sf:fromVirtualFiles(java.util.Collection):com.intellij.vcs.log.VcsLogStructureFilter
- sf:noMerges():com.intellij.vcs.log.VcsLogParentFilter
f:com.intellij.vcs.log.visible.filters.VcsLogFiltersKt
- sf:getKeysToSet(com.intellij.vcs.log.VcsLogFilterCollection):java.util.Set
- sf:getPresentation(com.intellij.vcs.log.VcsLogFilterCollection):java.lang.String
@@ -2887,6 +2902,11 @@ f:com.intellij.vcs.log.visible.filters.VcsLogFiltersKt
- sf:without(com.intellij.vcs.log.VcsLogFilterCollection,com.intellij.vcs.log.VcsLogFilterCollection$FilterKey):com.intellij.vcs.log.VcsLogFilterCollection
- sf:without(com.intellij.vcs.log.VcsLogFilterCollection,java.lang.Class):com.intellij.vcs.log.VcsLogFilterCollection
- sf:without(com.intellij.vcs.log.VcsLogFilterCollection,kotlin.jvm.functions.Function1):com.intellij.vcs.log.VcsLogFilterCollection
f:com.intellij.vcs.log.visible.filters.VcsLogParentFilterImplKt
- sf:getHasLowerBound(com.intellij.vcs.log.VcsLogParentFilter):Z
- sf:getHasUpperBound(com.intellij.vcs.log.VcsLogParentFilter):Z
- sf:getMatchesAll(com.intellij.vcs.log.VcsLogParentFilter):Z
- sf:isNoMerges(com.intellij.vcs.log.VcsLogParentFilter):Z
*:com.intellij.vcs.log.visible.filters.VcsLogTextFilterWithMatches
- com.intellij.vcs.log.VcsLogTextFilter
- matches(java.lang.String):Z

View File

@@ -169,9 +169,6 @@
</action>
</group>
<group id="Vcs.Log.GraphOptionsGroup" class="com.intellij.vcs.log.ui.actions.VcsLogGraphOptionsChooserGroup" popup="true"
icon="com.intellij.vcs.log.impl.VcsLogIcons.IntelliSort"/>
<group id="Vcs.Log.BranchActionsGroup" popup="false">
<separator key="action.vcs.log.branches.separator"/>
<reference id="Vcs.Log.CollapseAll"/>

View File

@@ -231,6 +231,7 @@ vcs.log.filter.popup.advertisement.text.or.suffix=or {0}
vcs.log.filter.popup.advertisement.with.key.text=Select one or more values separated with {0}, use {1} to finish
vcs.log.filter.select.folders=Select in Tree\u2026
vcs.log.filter.edit.folders=Select\u2026
vcs.log.filter.no.merges=No Merges
vcs.log.text.filter.action.text=Filter by Text or Hash
vcs.log.branch.filter.action.text=Filter by Branch
vcs.log.user.filter.action.text=Filter by User

View File

@@ -36,7 +36,7 @@ import static com.intellij.vcs.log.ui.table.column.VcsLogColumnUtilKt.getColumns
import static com.intellij.vcs.log.ui.table.column.VcsLogDefaultColumnKt.getDefaultDynamicColumns;
public @NonNls class VcsLogFeaturesCollector extends ProjectUsagesCollector {
private static final EventLogGroup GROUP = new EventLogGroup("vcs.log.ui", 4);
private static final EventLogGroup GROUP = new EventLogGroup("vcs.log.ui", 5);
private static final EventId UI_INITIALIZED = GROUP.registerEvent("uiInitialized");
private static final VarargEventId DETAILS = GROUP.registerVarargEvent("details", EventFields.Enabled);
private static final VarargEventId DIFF_PREVIEW = GROUP.registerVarargEvent("diffPreview", EventFields.Enabled);

View File

@@ -16,7 +16,7 @@ import com.intellij.vcs.log.visible.CommitCountStage
import com.intellij.vcs.log.visible.FilterKind
object VcsLogPerformanceStatisticsCollector : CounterUsagesCollector() {
private val GROUP = EventLogGroup("vcs.log.performance", 5)
private val GROUP = EventLogGroup("vcs.log.performance", 6)
val FILE_HISTORY_COMPUTING = GROUP.registerEvent("file.history.computed",
VcsLogRepoSizeCollector.VCS_FIELD,

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.vcs.log.statistics;
import com.intellij.internal.statistic.eventLog.EventLogGroup;
@@ -18,7 +18,7 @@ import java.util.List;
import java.util.function.Consumer;
public final class VcsLogUsageTriggerCollector extends CounterUsagesCollector {
private static final EventLogGroup GROUP = new EventLogGroup("vcs.log.trigger", 7);
private static final EventLogGroup GROUP = new EventLogGroup("vcs.log.trigger", 8);
private static final StringEventField CONTEXT = EventFields.String("context", List.of("history", "log"));
private static final ClassEventField CLASS = EventFields.Class("class");
public static final BooleanEventField PARENT_COMMIT = EventFields.Boolean("parent_commit");

View File

@@ -9,7 +9,6 @@ public final @NonNls class VcsLogActionIds {
public static final String TOOLBAR_ACTION_GROUP = "Vcs.Log.Toolbar.Internal";
public static final String TOOLBAR_RIGHT_CORNER_ACTION_GROUP = "Vcs.Log.Toolbar.RightCorner";
public static final String TEXT_FILTER_SETTINGS_ACTION_GROUP = "Vcs.Log.TextFilterSettings";
public static final String GRAPH_OPTIONS_GROUP = "Vcs.Log.GraphOptionsGroup";
public static final String BRANCH_ACTIONS_GROUP = "Vcs.Log.BranchActionsGroup";
public static final String FILE_HISTORY_TOOLBAR_ACTION_GROUP = "Vcs.FileHistory.Toolbar";
public static final String FILE_HISTORY_TOOLBAR_RIGHT_CORNER_ACTION_GROUP = "Vcs.FileHistory.Toolbar.RightCorner";

View File

@@ -0,0 +1,31 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.vcs.log.ui.filter
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.project.DumbAwareToggleAction
import com.intellij.vcs.log.VcsLogBundle
import com.intellij.vcs.log.visible.filters.VcsLogFilterObject
class NoMergesFilterAction(private val parentFilterModel: ParentFilterModel) : DumbAwareToggleAction(VcsLogBundle.message("vcs.log.filter.no.merges")) {
override fun isSelected(e: AnActionEvent): Boolean {
return parentFilterModel.getFilter() != null
}
override fun update(e: AnActionEvent) {
e.presentation.isEnabled = parentFilterModel.isEnabled
e.presentation.isVisible = parentFilterModel.isVisible
super.update(e)
}
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT
override fun setSelected(e: AnActionEvent, state: Boolean) {
if (state) {
parentFilterModel.setFilter(VcsLogFilterObject.noMerges())
}
else {
parentFilterModel.setFilter(null)
}
}
}

View File

@@ -0,0 +1,38 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.vcs.log.ui.filter
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.vcs.log.VcsLogFilterCollection
import com.intellij.vcs.log.VcsLogParentFilter
import com.intellij.vcs.log.VcsLogProperties
import com.intellij.vcs.log.VcsLogProvider
import com.intellij.vcs.log.impl.MainVcsLogUiProperties
import com.intellij.vcs.log.visible.filters.VcsLogFilterObject
class ParentFilterModel(uiProperties: MainVcsLogUiProperties,
private val logProviders: Map<VirtualFile, VcsLogProvider>,
private val visibleRootsProvider: () -> Collection<VirtualFile>? = { null }, filters: VcsLogFilterCollection?) :
FilterModel.SingleFilterModel<VcsLogParentFilter>(VcsLogFilterCollection.PARENT_FILTER, uiProperties, filters) {
private fun createParentFilter(values: List<String>): VcsLogParentFilter? {
if (values.size != 2) return null
return VcsLogFilterObject.fromParentCount(values[0].toIntOrNull(), values[1].toIntOrNull())
}
override fun createFilter(values: List<String>): VcsLogParentFilter? {
return createParentFilter(values)
}
override fun getFilterValues(filter: VcsLogParentFilter): List<String> {
return listOf(filter.minParents.toString(), filter.maxParents.toString())
}
val isVisible: Boolean
get() = isSupported(logProviders.keys)
val isEnabled: Boolean
get() = isSupported(visibleRootsProvider() ?: logProviders.keys)
private fun isSupported(roots: Collection<VirtualFile>) =
roots.any { VcsLogProperties.SUPPORTS_PARENTS_FILTER.getOrDefault(logProviders[it]) }
}

View File

@@ -54,6 +54,7 @@ open class VcsLogClassicFilterUi(private val logData: VcsLogData,
protected val dateFilterModel: DateFilterModel
protected val structureFilterModel: FileFilterModel
protected val textFilterModel: TextFilterModel
protected val parentFilterModel: ParentFilterModel
private val textFilterField: VcsLogTextFilterField
@@ -65,12 +66,13 @@ open class VcsLogClassicFilterUi(private val logData: VcsLogData,
dateFilterModel = DateFilterModel(uiProperties, filters)
structureFilterModel = FileFilterModel(logData.logProviders.keys, uiProperties, filters)
textFilterModel = TextFilterModel(uiProperties, filters, parentDisposable)
parentFilterModel = ParentFilterModel(uiProperties, logData.logProviders, ::visibleRoots, filters)
val field = TextFilterField(textFilterModel, parentDisposable)
val toolbar = createTextActionsToolbar(field.textEditor)
textFilterField = MyVcsLogTextFilterField(SearchFieldWithExtension(toolbar.component, field))
val models = arrayOf(branchFilterModel, userFilterModel, dateFilterModel, structureFilterModel, textFilterModel)
val models = arrayOf(branchFilterModel, userFilterModel, dateFilterModel, structureFilterModel, textFilterModel, parentFilterModel)
for (model in models) {
model.addSetFilterListener {
filterConsumer.accept(getFilters())
@@ -140,6 +142,7 @@ open class VcsLogClassicFilterUi(private val logData: VcsLogData,
addAll(structureFilterModel.filtersList)
add(dateFilterModel.getFilter())
add(userFilterModel.getFilter())
add(parentFilterModel.getFilter())
}.filterNotNull()
return VcsLogFilterObject.collection(*filters.toTypedArray())
}
@@ -151,6 +154,7 @@ open class VcsLogClassicFilterUi(private val logData: VcsLogData,
textFilterModel.setFilter(collection)
dateFilterModel.setFilter(collection.get(VcsLogFilterCollection.DATE_FILTER))
userFilterModel.setFilter(collection.get(VcsLogFilterCollection.USER_FILTER))
parentFilterModel.setFilter(collection.get(VcsLogFilterCollection.PARENT_FILTER))
}
protected open fun createBranchComponent(): AnAction? {
@@ -178,7 +182,7 @@ open class VcsLogClassicFilterUi(private val logData: VcsLogData,
}
protected fun createGraphComponent(): AnAction? {
return ActionManager.getInstance().getAction(VcsLogActionIds.GRAPH_OPTIONS_GROUP)
return VcsLogGraphOptionsChooserGroup(parentFilterModel)
}
override fun addFilterListener(listener: VcsLogFilterListener) {

View File

@@ -1,5 +1,5 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.vcs.log.ui.actions;
package com.intellij.vcs.log.ui.filter;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.project.DumbAware;
@@ -8,14 +8,17 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.JBUI;
import com.intellij.vcs.log.VcsLogBundle;
import com.intellij.vcs.log.VcsLogDataKeys;
import com.intellij.vcs.log.VcsLogParentFilter;
import com.intellij.vcs.log.VcsLogUi;
import com.intellij.vcs.log.graph.PermanentGraph;
import com.intellij.vcs.log.impl.MainVcsLogUiProperties;
import com.intellij.vcs.log.impl.VcsLogIcons;
import com.intellij.vcs.log.impl.VcsLogUiProperties;
import com.intellij.vcs.log.ui.VcsLogActionIds;
import com.intellij.vcs.log.ui.VcsLogInternalDataKeys;
import com.intellij.vcs.log.util.BekUtil;
import com.intellij.vcs.log.util.GraphOptionsUtil;
import com.intellij.vcs.log.visible.filters.VcsLogParentFilterImplKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -25,6 +28,16 @@ import java.util.List;
public class VcsLogGraphOptionsChooserGroup extends DefaultActionGroup implements DumbAware {
private final @NotNull ParentFilterModel myParentFilterModel;
public VcsLogGraphOptionsChooserGroup(@NotNull ParentFilterModel parentFilterModel) {
super(VcsLogBundle.message("group.Vcs.Log.GraphOptionsGroup.text"),
VcsLogBundle.message("group.Vcs.Log.GraphOptionsGroup.description"),
VcsLogIcons.IntelliSort);
myParentFilterModel = parentFilterModel;
setPopup(true);
}
@Override
public AnAction @NotNull [] getChildren(@Nullable AnActionEvent e) {
if (e == null) return EMPTY_ARRAY;
@@ -46,6 +59,7 @@ public class VcsLogGraphOptionsChooserGroup extends DefaultActionGroup implement
actions.add(new SelectNonBaseOptionsAction(logUI, properties, PermanentGraph.Options.LinearBek.INSTANCE));
}
actions.add(new SelectNonBaseOptionsAction(logUI, properties, PermanentGraph.Options.FirstParent.INSTANCE));
actions.add(new NoMergesFilterAction(myParentFilterModel));
actions.add(ActionManager.getInstance().getAction(VcsLogActionIds.BRANCH_ACTIONS_GROUP));
@@ -61,7 +75,7 @@ public class VcsLogGraphOptionsChooserGroup extends DefaultActionGroup implement
if (isEnabled) {
Icon icon = getTemplatePresentation().getIcon();
if (icon != null) {
if (!PermanentGraph.Options.Default.equals(properties.get(MainVcsLogUiProperties.GRAPH_OPTIONS))) {
if (hasNonDefaultOptions(properties)) {
e.getPresentation().setIcon(IconManager.getInstance().withIconBadge(icon, JBUI.CurrentTheme.IconBadge.SUCCESS));
}
else {
@@ -76,6 +90,13 @@ public class VcsLogGraphOptionsChooserGroup extends DefaultActionGroup implement
return ActionUpdateThread.EDT;
}
private boolean hasNonDefaultOptions(@NotNull VcsLogUiProperties properties) {
VcsLogParentFilter parentFilter = myParentFilterModel.getFilter();
if (parentFilter != null && !VcsLogParentFilterImplKt.getMatchesAll(parentFilter)) return true;
PermanentGraph.Options options = properties.get(MainVcsLogUiProperties.GRAPH_OPTIONS);
return !PermanentGraph.Options.Default.equals(options);
}
private static @NotNull List<PermanentGraph.SortType> getAvailableSortTypes() {
List<PermanentGraph.SortType> sortTypes = new ArrayList<>(PermanentGraph.SortType.getEntries());
if (!BekUtil.isBekEnabled()) {

View File

@@ -17,6 +17,8 @@ import com.intellij.vcs.log.data.index.IndexDataGetter
import com.intellij.vcs.log.data.index.VcsLogIndex
import com.intellij.vcs.log.graph.PermanentGraph
import com.intellij.vcs.log.graph.VisibleGraph
import com.intellij.vcs.log.graph.api.EdgeFilter
import com.intellij.vcs.log.graph.api.LinearGraph
import com.intellij.vcs.log.graph.api.permanent.PermanentGraphInfo
import com.intellij.vcs.log.graph.impl.facade.LinearGraphController
import com.intellij.vcs.log.graph.impl.facade.PermanentGraphImpl
@@ -34,6 +36,7 @@ import com.intellij.vcs.log.util.VcsLogUtil.FULL_HASH_LENGTH
import com.intellij.vcs.log.visible.filters.*
import io.opentelemetry.api.trace.Span
import io.opentelemetry.api.trace.StatusCode
import it.unimi.dsi.fastutil.ints.IntConsumer
import it.unimi.dsi.fastutil.ints.IntOpenHashSet
import it.unimi.dsi.fastutil.ints.IntSet
import java.util.function.BiConsumer
@@ -75,43 +78,27 @@ class VcsLogFiltererImpl(private val logProviders: Map<VirtualFile, VcsLogProvid
val visibleRoots = VcsLogUtil.getAllVisibleRoots(dataPack.logProviders.keys, filters)
val matchingHeads: Set<Int>?
val commitCandidates: IntSet?
val forceFilterByVcs: Boolean
var commitCandidates: IntSet? = null
var forceFilterByVcs = false
val rangeFilters = allFilters.get(VcsLogFilterCollection.RANGE_FILTER)
if (rangeFilters != null) {
/*
If we have both a range filter and a branch filter (e.g. `183\nmaster..feature`) they should be united: the graph should show both
commits contained in the range, and commits reachable from branches.
val (commits, heads) = filterByRange(dataPack, filters, visibleRoots, rangeFilters)
But the main filtering logic is opposite: matchingHeads + some other filter => makes the intersection of commits.
To overcome this logic for the range filter case, we are not using matchingHeads, but are collecting all commits reachable from
matchingHeads, and unite them with commits belonging to the range.
*/
val branchFilter = filters.get(VcsLogFilterCollection.BRANCH_FILTER)
val revisionFilter = filters.get(VcsLogFilterCollection.REVISION_FILTER)
val explicitMatchingHeads = getMatchingHeads(dataPack.refsModel, visibleRoots, branchFilter, revisionFilter)
val commitsReachableFromHeads = if (explicitMatchingHeads != null)
collectCommitsReachableFromHeads(dataPack, explicitMatchingHeads)
else IntOpenHashSet()
commitCandidates = when (val commitsForRangeFilter = filterByRange(storage, logProviders, dataPack, rangeFilters)) {
is RangeFilterResult.Commits -> IntCollectionUtil.union(listOf(commitsReachableFromHeads, commitsForRangeFilter.commits))
RangeFilterResult.Error, RangeFilterResult.InvalidRange -> null
}
forceFilterByVcs = commitCandidates == null
/*
At the same time, the root filter should intersect with the range filter (and the branch filter),
therefore we take matching heads from the root filter, but use reachable commits set for the branch filter.
*/
matchingHeads = getMatchingHeads(dataPack.refsModel, visibleRoots)
commitCandidates = commits
matchingHeads = heads
forceFilterByVcs = commits == null
}
else {
commitCandidates = null
forceFilterByVcs = false
matchingHeads = getMatchingHeads(dataPack.refsModel, visibleRoots, filters)
}
val parentFilter = allFilters.get(VcsLogFilterCollection.PARENT_FILTER)
if (parentFilter != null && !parentFilter.matchesAll) {
commitCandidates = filterByParent(dataPack, parentFilter, commitCandidates)
forceFilterByVcs = commitCandidates == null
}
try {
val filterResult = filterByDetails(dataPack, filters, commitCount, visibleRoots, matchingHeads, commitCandidates, graphOptions, forceFilterByVcs)
@@ -345,6 +332,57 @@ class VcsLogFiltererImpl(private val logProviders: Map<VirtualFile, VcsLogProvid
return VcsLogFilterObject.fromPatternsList(ArrayList(hashes), false)
}
private fun filterByRange(dataPack: DataPack, filters: VcsLogFilterCollection, visibleRoots: Set<VirtualFile>,
rangeFilter: VcsLogRangeFilter): Pair<IntSet?, Set<Int>> {
/*
If we have both a range filter and a branch filter (e.g. `183\nmaster..feature`) they should be united: the graph should show both
commits contained in the range, and commits reachable from branches.
But the main filtering logic is opposite: matchingHeads + some other filter => makes the intersection of commits.
To overcome this logic for the range filter case, we are not using matchingHeads, but are collecting all commits reachable from
matchingHeads, and unite them with commits belonging to the range.
*/
val branchFilter = filters.get(VcsLogFilterCollection.BRANCH_FILTER)
val revisionFilter = filters.get(VcsLogFilterCollection.REVISION_FILTER)
val explicitMatchingHeads = getMatchingHeads(dataPack.refsModel, visibleRoots, branchFilter, revisionFilter)
val commitsReachableFromHeads = if (explicitMatchingHeads != null)
collectCommitsReachableFromHeads(dataPack, explicitMatchingHeads)
else IntOpenHashSet()
val commits = when (val commitsForRangeFilter = filterByRange(storage, logProviders, dataPack, rangeFilter)) {
is RangeFilterResult.Commits -> IntCollectionUtil.union(listOf(commitsReachableFromHeads, commitsForRangeFilter.commits))
RangeFilterResult.Error, RangeFilterResult.InvalidRange -> null
}
/*
At the same time, the root filter should intersect with the range filter (and the branch filter),
therefore we take matching heads from the root filter, but use reachable commits set for the branch filter.
*/
val heads = getMatchingHeads(dataPack.refsModel, visibleRoots)
return Pair(commits, heads)
}
private fun filterByParent(dataPack: DataPack, parentFilter: VcsLogParentFilter, commitCandidates: IntSet?): IntSet? {
val result = IntOpenHashSet()
@Suppress("UNCHECKED_CAST") val permanentGraph = dataPack.permanentGraph as? PermanentGraphInfo<Int> ?: return null
if (commitCandidates != null) {
commitCandidates.forEach(IntConsumer { commit ->
val nodeId = permanentGraph.permanentCommitsInfo.getNodeId(commit)
if (matches(permanentGraph.linearGraph, nodeId, parentFilter)) {
result.add(commit)
}
})
}
else {
for (nodeId in 0 until permanentGraph.linearGraph.nodesCount()) {
if (matches(permanentGraph.linearGraph, nodeId, parentFilter)) {
result.add(permanentGraph.permanentCommitsInfo.getCommitId(nodeId))
}
}
}
return result
}
fun getMatchingHeads(refs: RefsModel,
roots: Collection<VirtualFile>,
filters: VcsLogFilterCollection): Set<Int>? {
@@ -495,6 +533,11 @@ fun areFiltersAffectedByIndexing(filters: VcsLogFilterCollection, roots: List<Vi
return needsIndex
}
private fun matches(linearGraph: LinearGraph, node: Int, filter: VcsLogParentFilter): Boolean {
val parentsCount = linearGraph.getAdjacentEdges(node, EdgeFilter.NORMAL_DOWN).size
return parentsCount in filter.minParents..filter.maxParents
}
internal fun <T> Collection<T>?.matchesNothing(): Boolean {
return this != null && this.isEmpty()
}

View File

@@ -195,6 +195,16 @@ object VcsLogFilterObject {
return VcsLogRootFilterImpl(roots)
}
@JvmStatic
fun noMerges(): VcsLogParentFilter {
return fromParentCount(maxParents = 1)
}
@JvmStatic
fun fromParentCount(minParents: Int? = null, maxParents: Int? = null): VcsLogParentFilter {
return VcsLogParentFilterImpl(minParents ?: 0, maxParents ?: Int.MAX_VALUE)
}
@JvmStatic
fun collection(vararg filters: VcsLogFilter?): VcsLogFilterCollection {
val filterSet = createFilterSet()

View File

@@ -0,0 +1,21 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.vcs.log.visible.filters
import com.intellij.vcs.log.VcsLogParentFilter
internal class VcsLogParentFilterImpl(override val minParents: Int = 0, override val maxParents: Int = Int.MAX_VALUE) : VcsLogParentFilter {
@Suppress("HardCodedStringLiteral")
override fun getDisplayText(): String {
if (isNoMerges) return "--no-merges"
return buildString {
if (hasLowerBound) append("--min-parents=$minParents")
if (hasUpperBound) append("--max-parents=$maxParents")
}
}
}
val VcsLogParentFilter.hasLowerBound get() = minParents > 0
val VcsLogParentFilter.hasUpperBound get() = maxParents != Int.MAX_VALUE
val VcsLogParentFilter.matchesAll: Boolean get() = minParents <= 0 && maxParents == Int.MAX_VALUE
val VcsLogParentFilter.isNoMerges: Boolean get() = maxParents == 1 && !hasLowerBound

View File

@@ -30,6 +30,7 @@ import com.intellij.vcs.log.util.UserNameRegex;
import com.intellij.vcs.log.util.VcsUserUtil;
import com.intellij.vcs.log.visible.CommitCountStageKt;
import com.intellij.vcs.log.visible.filters.VcsLogFiltersKt;
import com.intellij.vcs.log.visible.filters.VcsLogParentFilterImplKt;
import com.intellij.vcsUtil.VcsFileUtil;
import git4idea.*;
import git4idea.branch.GitBranchUtil;
@@ -479,6 +480,12 @@ public final class GitLogProvider implements VcsLogProvider, VcsIndexableLogProv
filterParameters.add("--first-parent");
}
VcsLogParentFilter parentFilter = filterCollection.get(PARENT_FILTER);
if (parentFilter != null && !VcsLogParentFilterImplKt.getMatchesAll(parentFilter)) {
if (VcsLogParentFilterImplKt.getHasLowerBound(parentFilter)) filterParameters.add("--min-parents=" + parentFilter.getMinParents());
if (VcsLogParentFilterImplKt.getHasUpperBound(parentFilter)) filterParameters.add("--max-parents=" + parentFilter.getMaxParents());
}
// note: structure filter must be the last parameter, because it uses "--" which separates parameters from paths
VcsLogStructureFilter structureFilter = filterCollection.get(STRUCTURE_FILTER);
if (structureFilter != null) {

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.zmlx.hg4idea.log;
import com.intellij.openapi.Disposable;
@@ -317,7 +317,7 @@ public final class HgLogProvider implements VcsLogProvider {
@Override
public @Nullable <T> T getPropertyValue(VcsLogProperties.VcsLogProperty<T> property) {
if (property == VcsLogProperties.CASE_INSENSITIVE_REGEX) {
if (property == VcsLogProperties.CASE_INSENSITIVE_REGEX || property == VcsLogProperties.SUPPORTS_PARENTS_FILTER) {
return (T)Boolean.FALSE;
}
return null;