diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogFilterCollection.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogFilterCollection.java index 65b999f4c4db..365d9968010d 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogFilterCollection.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogFilterCollection.java @@ -45,6 +45,9 @@ public interface VcsLogFilterCollection { @Nullable VcsLogStructureFilter getStructureFilter(); + @Nullable + VcsLogRootFilter getRootFilter(); + /** * Returns true if there are no filters in this collection. */ diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogRootFilter.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogRootFilter.java new file mode 100644 index 000000000000..7720d812ea71 --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogRootFilter.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2014 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.vcs.log; + +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * Tells the log to filter by vcs roots. + */ +public interface VcsLogRootFilter extends VcsLogFilter { + + /** + *

Returns vcs roots that are visible.

+ */ + @NotNull + Collection getRoots(); +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogStructureFilter.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogStructureFilter.java index bdfd0d1701a4..44adc30a925e 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogStructureFilter.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogStructureFilter.java @@ -26,14 +26,11 @@ import java.util.Collection; public interface VcsLogStructureFilter extends VcsLogDetailsFilter { /** - *

Returns files from the given VCS root, which are affected by matching commits, and folders containing such files.

+ *

Returns files which are affected by matching commits, and folders containing such files.

* *

That is: the commit A (made in the given VCS root) modifying file f.txt matches this filter, * if this method returns a set which includes a folder containing f.txt, or the file f.txt itself.

*/ @NotNull - Collection getFiles(@NotNull VirtualFile root); - - Collection getRoots(); - + Collection getFiles(); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/VcsLogRootFilterImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/VcsLogRootFilterImpl.java new file mode 100644 index 000000000000..82abf2fc3f32 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/VcsLogRootFilterImpl.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2014 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.vcs.log; + +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +public class VcsLogRootFilterImpl implements VcsLogRootFilter { + @NotNull private final Collection myRoots; + + public VcsLogRootFilterImpl(@NotNull Collection roots) { + myRoots = roots; + } + + @NotNull + @Override + public Collection getRoots() { + return myRoots; + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogFileFilterUtil.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogFileFilterUtil.java new file mode 100644 index 000000000000..d73ae9c935a0 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogFileFilterUtil.java @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2014 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.vcs.log.data; + +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.HashSet; +import com.intellij.util.containers.MultiMap; +import com.intellij.vcs.log.VcsLogRootFilter; +import com.intellij.vcs.log.VcsLogStructureFilter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Set; + +public class VcsLogFileFilterUtil { + @NotNull + static Pair, MultiMap> collectRoots(@NotNull Collection files, + @NotNull Set roots) { + Set selectedRoots = new HashSet(); + MultiMap selectedFiles = new MultiMap(); + + for (VirtualFile file : files) { + if (roots.contains(file)) { + selectedRoots.add(file); + } + else { + VirtualFile candidateAncestorRoot = null; + for (VirtualFile root : roots) { + if (VfsUtilCore.isAncestor(root, file, false)) { + if (candidateAncestorRoot == null || VfsUtilCore.isAncestor(candidateAncestorRoot, root, false)) { + candidateAncestorRoot = root; + } + } + else if (VfsUtilCore.isAncestor(file, root, false)) { + selectedRoots.add(root); + } + } + + if (candidateAncestorRoot != null) { + selectedFiles.putValue(candidateAncestorRoot, file); + } + } + } + + return Pair.create(selectedRoots, selectedFiles); + } + + @NotNull + public static Pair, MultiMap> collectRootsAndFiles(@NotNull Set roots, + @Nullable VcsLogRootFilter rootFilter, + @Nullable VcsLogStructureFilter structureFilter) { + if (rootFilter == null && structureFilter == null) return Pair.create(roots, MultiMap.create()); + + if (structureFilter == null) { + return Pair.create((Set)new HashSet(rootFilter.getRoots()), MultiMap.create()); + } + Pair, MultiMap> selectedRootsAndFiles = collectRoots(structureFilter.getFiles(), roots); + if (rootFilter == null) { + return selectedRootsAndFiles; + } + return Pair.create(ContainerUtil.union(new HashSet(rootFilter.getRoots()), selectedRootsAndFiles.first), + selectedRootsAndFiles.second); + } + + @Nullable + public static Set collectRoots(@NotNull Set roots, + @Nullable VcsLogRootFilter rootFilter, + @Nullable VcsLogStructureFilter structureFilter) { + if (rootFilter == null && structureFilter == null) return null; + + Pair, MultiMap> rootsAndFiles = collectRootsAndFiles(roots, rootFilter, structureFilter); + return ContainerUtil.union(rootsAndFiles.first, rootsAndFiles.second.keySet()); + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogFiltererImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogFiltererImpl.java index 2248cb7b31e7..1f106c001754 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogFiltererImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogFiltererImpl.java @@ -55,7 +55,7 @@ public class VcsLogFiltererImpl implements VcsLogFilterer { @NotNull final PermanentGraph.SortType initialSortType, @NotNull final Consumer visiblePackConsumer) { myVisiblePackBuilder = new VisiblePackBuilder(providers, hashMap, topCommitsDetailsCache, detailsGetter); - myFilters = new VcsLogFilterCollectionImpl(null, null, null, null, null, null); + myFilters = new VcsLogFilterCollectionImpl(null, null, null, null, null, null, null); mySortType = initialSortType; myTaskController = new SingleTaskController(visiblePackConsumer) { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogStructureFilterImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogStructureFilterImpl.java index 39fe1c8dc1cb..8c14a22b4895 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogStructureFilterImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogStructureFilterImpl.java @@ -32,38 +32,28 @@ import java.util.HashSet; import java.util.Set; public class VcsLogStructureFilterImpl implements VcsLogDetailsFilter, VcsLogStructureFilter { - @NotNull private final Set myRoots; - @NotNull private final MultiMap myRootToFiles; + @NotNull private final Set myFiles; - public VcsLogStructureFilterImpl(@NotNull Set roots, - @NotNull MultiMap rootToFiles) { - myRoots = roots; - myRootToFiles = rootToFiles; + public VcsLogStructureFilterImpl(@NotNull Set files) { + myFiles = files; } @NotNull @Override - public Collection getFiles(@NotNull VirtualFile root) { - return myRootToFiles.get(root); - } - - @Override - public Collection getRoots() { - return ContainerUtil.union(myRoots, myRootToFiles.keySet()); + public Collection getFiles() { + return myFiles; } @Override public boolean matches(@NotNull VcsCommitMetadata details) { - if (myRoots.contains(details.getRoot())) return true; - if ((details instanceof VcsFullCommitDetails)) { for (Change change : ((VcsFullCommitDetails)details).getChanges()) { ContentRevision before = change.getBeforeRevision(); - if (before != null && matches(before.getFile().getPath(), myRootToFiles.get(details.getRoot()))) { + if (before != null && matches(before.getFile().getPath())) { return true; } ContentRevision after = change.getAfterRevision(); - if (after != null && matches(after.getFile().getPath(), myRootToFiles.get(details.getRoot()))) { + if (after != null && matches(after.getFile().getPath())) { return true; } } @@ -74,47 +64,12 @@ public class VcsLogStructureFilterImpl implements VcsLogDetailsFilter, VcsLogStr } } - private boolean matches(@NotNull final String path, @NotNull Collection files) { - return ContainerUtil.find(files, new Condition() { + private boolean matches(@NotNull final String path) { + return ContainerUtil.find(myFiles, new Condition() { @Override public boolean value(VirtualFile file) { return FileUtil.isAncestor(file.getPath(), path, false); } }) != null; } - - @NotNull - public static VcsLogStructureFilterImpl build(@NotNull Collection files, - @NotNull VcsLogDataPack dataPack) { - Set roots = dataPack.getLogProviders().keySet(); - - Set selectedRoots = new HashSet(); - MultiMap selectedFiles = new MultiMap(); - - for (VirtualFile file : files) { - if (roots.contains(file)) { - // no need in details filter - selectedRoots.add(file); - } - else { - VirtualFile candidateAncestorRoot = null; - for (VirtualFile root : roots) { - if (VfsUtilCore.isAncestor(root, file, false)) { - if (candidateAncestorRoot == null || VfsUtilCore.isAncestor(candidateAncestorRoot, root, false)) { - candidateAncestorRoot = root; - } - } - else if (VfsUtilCore.isAncestor(file, root, false)) { - selectedRoots.add(root); - } - } - - if (candidateAncestorRoot != null) { - selectedFiles.putValue(candidateAncestorRoot, file); - } - } - } - - return new VcsLogStructureFilterImpl(selectedRoots, selectedFiles); - } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VisiblePackBuilder.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VisiblePackBuilder.java index 31489526f59f..e2c2a9ce332d 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VisiblePackBuilder.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VisiblePackBuilder.java @@ -24,11 +24,13 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashSet; +import com.intellij.util.containers.MultiMap; import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.*; import com.intellij.vcs.log.graph.GraphCommit; import com.intellij.vcs.log.graph.PermanentGraph; import com.intellij.vcs.log.graph.VisibleGraph; +import com.intellij.vcs.log.impl.VcsLogFilterCollectionImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -63,7 +65,7 @@ class VisiblePackBuilder { return Pair.create(applyHashFilter(dataPack, hashFilter.getHashes(), sortType), commitCount); } - Set matchingHeads = getMatchingHeads(dataPack.getRefs(), filters); + Set matchingHeads = getMatchingHeads(dataPack.getRefs(), dataPack.getLogProviders().keySet(), filters); List detailsFilters = filters.getDetailsFilters(); List matchingCommits = null; boolean canRequestMore = false; @@ -105,8 +107,8 @@ class VisiblePackBuilder { } private VisiblePack applyHashFilter(@NotNull DataPack dataPack, - @NotNull Collection hashes, - @NotNull PermanentGraph.SortType sortType) { + @NotNull Collection hashes, + @NotNull PermanentGraph.SortType sortType) { final Set indices = ContainerUtil.map2SetNotNull(hashes, new Function() { @Override public Integer fun(String partOfHash) { @@ -124,13 +126,39 @@ class VisiblePackBuilder { } @Nullable - private Set getMatchingHeads(@NotNull VcsLogRefs refs, @NotNull VcsLogFilterCollection filters) { + private Set getMatchingHeads(@NotNull VcsLogRefs refs, Set roots, @NotNull VcsLogFilterCollection filters) { VcsLogBranchFilter branchFilter = filters.getBranchFilter(); - if (branchFilter == null) { - return null; + VcsLogRootFilter rootFilter = filters.getRootFilter(); + VcsLogStructureFilter structureFilter = filters.getStructureFilter(); + + if (branchFilter == null && rootFilter == null && structureFilter == null) return null; + + Set filteredByFile = null; + Set filteredByBranch = null; + + if (branchFilter != null) { + filteredByBranch = getMatchingHeads(refs, branchFilter); + } + if (rootFilter != null) { + filteredByFile = getMatchingHeads(refs, rootFilter); + } + if (structureFilter != null) { + if (filteredByFile == null) { + filteredByFile = getMatchingHeads(refs, roots, structureFilter); + } + else { + filteredByFile = ContainerUtil.union(filteredByFile, getMatchingHeads(refs, roots, structureFilter)); + } } - final Collection branchNames = new HashSet(branchFilter.getBranchNames()); + if (filteredByBranch == null) return filteredByFile; + if (filteredByFile == null) return filteredByBranch; + + return new HashSet(ContainerUtil.intersection(filteredByBranch, filteredByFile)); + } + + private Set getMatchingHeads(@NotNull VcsLogRefs refs, @NotNull VcsLogBranchFilter filter) { + final Collection branchNames = new HashSet(filter.getBranchNames()); return new HashSet(ContainerUtil.mapNotNull(refs.getBranches(), new Function() { @Override public Integer fun(VcsRef ref) { @@ -142,6 +170,28 @@ class VisiblePackBuilder { })); } + private Set getMatchingHeads(@NotNull VcsLogRefs refs, @NotNull VcsLogRootFilter filter) { + Collection roots = filter.getRoots(); + return getMatchingHeads(refs, roots); + } + + private Set getMatchingHeads(@NotNull VcsLogRefs refs, @NotNull Collection roots) { + Set result = new HashSet(); + for (VcsRef branch : refs.getBranches()) { + if (roots.contains(branch.getRoot())) { + result.add(myHashMap.getCommitIndex(branch.getCommitHash())); + } + } + return result; + } + + private Set getMatchingHeads(@NotNull VcsLogRefs refs, + @NotNull Set vcsRoots, + @NotNull VcsLogStructureFilter filter) { + Pair, MultiMap> roots = VcsLogFileFilterUtil.collectRoots(filter.getFiles(), vcsRoots); + return getMatchingHeads(refs, ContainerUtil.union(roots.first, roots.second.keySet())); + } + @NotNull private List filterInMemory(@NotNull PermanentGraph permanentGraph, @NotNull List detailsFilters, @@ -203,17 +253,28 @@ class VisiblePackBuilder { private static List getFilteredDetailsFromTheVcs(@NotNull Map providers, @NotNull VcsLogFilterCollection filterCollection, int maxCount) throws VcsException { + VcsLogRootFilter rootFilter = filterCollection.getRootFilter(); + VcsLogStructureFilter structureFilter = filterCollection.getStructureFilter(); + + Pair, MultiMap> selectedRootsAndFiles = + VcsLogFileFilterUtil.collectRootsAndFiles(providers.keySet(), rootFilter, structureFilter); + Collection> logs = ContainerUtil.newArrayList(); for (Map.Entry entry : providers.entrySet()) { VirtualFile root = entry.getKey(); - if (filterCollection.getStructureFilter() != null && !filterCollection.getStructureFilter().getRoots().contains(root) - || filterCollection.getUserFilter() != null && filterCollection.getUserFilter().getUserNames(root).isEmpty()) { + if (!selectedRootsAndFiles.first.contains(root) || + filterCollection.getUserFilter() != null && filterCollection.getUserFilter().getUserNames(root).isEmpty()) { // there is a structure or user filter, but it doesn't match this root continue; } - List matchingCommits = entry.getValue().getCommitsMatchingFilter(root, filterCollection, maxCount); + VcsLogFilterCollection rootSpecificCollection = filterCollection; + if (rootSpecificCollection.getStructureFilter() != null) { + rootSpecificCollection = replaceStructureFilter(filterCollection, new HashSet(selectedRootsAndFiles.second.get(root))); + } + + List matchingCommits = entry.getValue().getCommitsMatchingFilter(root, rootSpecificCollection, maxCount); logs.add(matchingCommits); } @@ -226,6 +287,15 @@ class VisiblePackBuilder { }); } + @NotNull + private static VcsLogFilterCollection replaceStructureFilter(@NotNull VcsLogFilterCollection filterCollection, + @NotNull Set files) { + return new VcsLogFilterCollectionImpl(filterCollection.getBranchFilter(), filterCollection.getUserFilter(), + filterCollection.getHashFilter(), filterCollection.getDateFilter(), + filterCollection.getTextFilter(), new VcsLogStructureFilterImpl(files), + filterCollection.getRootFilter()); + } + @Nullable private Condition getFilterFromCommits(@Nullable List filteredCommits) { if (filteredCommits == null) { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogFilterCollectionImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogFilterCollectionImpl.java index 3fbb7cb7b82f..d973face890d 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogFilterCollectionImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogFilterCollectionImpl.java @@ -31,19 +31,22 @@ public class VcsLogFilterCollectionImpl implements VcsLogFilterCollection { @Nullable private final VcsLogDateFilter myDateFilter; @Nullable private final VcsLogTextFilter myTextFilter; @Nullable private final VcsLogStructureFilter myStructureFilter; + @Nullable private final VcsLogRootFilter myRootFilter; public VcsLogFilterCollectionImpl(@Nullable VcsLogBranchFilter branchFilter, @Nullable VcsLogUserFilter userFilter, @Nullable VcsLogHashFilter hashFilter, @Nullable VcsLogDateFilter dateFilter, @Nullable VcsLogTextFilter textFilter, - @Nullable VcsLogStructureFilter structureFilter) { + @Nullable VcsLogStructureFilter structureFilter, + @Nullable VcsLogRootFilter rootFilter) { myBranchFilter = branchFilter; myUserFilter = userFilter; myHashFilter = hashFilter; myDateFilter = dateFilter; myTextFilter = textFilter; myStructureFilter = structureFilter; + myRootFilter = rootFilter; } @Nullable @@ -82,6 +85,13 @@ public class VcsLogFilterCollectionImpl implements VcsLogFilterCollection { return myStructureFilter; } + @Nullable + @Override + public VcsLogRootFilter getRootFilter() { + return myRootFilter; + } + + @Override public boolean isEmpty() { return myBranchFilter == null && getDetailsFilters().isEmpty(); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/FilterModel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/FilterModel.java index 88c8a8d0011c..a6ebaf6ad5b4 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/FilterModel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/FilterModel.java @@ -47,6 +47,11 @@ class FilterModel { return myFilter; } + @NotNull + public Computable getDataPackProvider() { + return myDataPackProvider; + } + @NotNull VcsLogDataPack getDataPack() { return myDataPackProvider.compute(); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/StructureFilterPopupComponent.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/StructureFilterPopupComponent.java index 2b849e1de8fe..2b72113d2de4 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/StructureFilterPopupComponent.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/StructureFilterPopupComponent.java @@ -23,8 +23,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; -import com.intellij.vcs.log.VcsLogDataPack; -import com.intellij.vcs.log.VcsLogStructureFilter; +import com.intellij.vcs.log.*; import com.intellij.vcs.log.data.VcsLogStructureFilterImpl; import com.intellij.vcs.log.ui.VcsStructureChooser; import org.jetbrains.annotations.NotNull; @@ -32,18 +31,17 @@ import org.jetbrains.annotations.Nullable; import java.util.*; -class StructureFilterPopupComponent extends FilterPopupComponent { - +class StructureFilterPopupComponent extends FilterPopupComponent { private static final int FILTER_LABEL_LENGTH = 20; - public StructureFilterPopupComponent(@NotNull FilterModel filterModel) { + public StructureFilterPopupComponent(@NotNull FilterModel filterModel) { super("Structure", filterModel); } @NotNull @Override - protected String getText(@NotNull VcsLogStructureFilter filter) { - Collection files = getAllFiles(myFilterModel.getDataPack(), filter); + protected String getText(@NotNull VcsLogFileFilter filter) { + Collection files = getAllFiles(filter); if (files.size() == 0) { return ALL; } @@ -58,20 +56,15 @@ class StructureFilterPopupComponent extends FilterPopupComponent getAllFiles(@NotNull VcsLogDataPack dataPack, @NotNull VcsLogStructureFilter filter) { + private static Collection getAllFiles(@NotNull VcsLogFileFilter filter) { Collection result = ContainerUtil.newArrayList(); - for (VirtualFile root : dataPack.getLogProviders().keySet()) { - Collection files = filter.getFiles(root); - result.addAll(files); - if (files.isEmpty() && filter.getRoots().contains(root)) { - result.add(root); - } - } + if (filter.getRootFilter() != null) result.addAll(filter.getRootFilter().getRoots()); + if (filter.getStructureFilter() != null) result.addAll(filter.getStructureFilter().getFiles()); return result; } @@ -90,8 +83,9 @@ class StructureFilterPopupComponent extends FilterPopupComponent roots = myFilterModel.getDataPack().getLogProviders().keySet(); + VcsLogFileFilter previousFilter = myFilterModel.getFilter(); + VcsLogRootFilter rootFilter = previousFilter != null ? previousFilter.getRootFilter() : null; + Collection visibleRoots; - VcsLogStructureFilter previousFilter = myFilterModel.getFilter(); - if (previousFilter == null) { + if (rootFilter == null) { if (visible) { visibleRoots = roots; } @@ -113,14 +109,13 @@ class StructureFilterPopupComponent extends FilterPopupComponent(myFilterModel.getFilter().getRoots()), Collections.singleton(root)); + visibleRoots = ContainerUtil.union(new HashSet(rootFilter.getRoots()), Collections.singleton(root)); } else { - visibleRoots = ContainerUtil.subtract(myFilterModel.getFilter().getRoots(), Collections.singleton(root)); + visibleRoots = ContainerUtil.subtract(rootFilter.getRoots(), Collections.singleton(root)); } } - myFilterModel.setFilter(VcsLogStructureFilterImpl.build(visibleRoots, myFilterModel.getDataPack())); - // todo if there are some non-roots in structure filter they will get lost + myFilterModel.setFilter(new VcsLogFileFilter(previousFilter != null ? previousFilter.getStructureFilter() : null, new VcsLogRootFilterImpl(visibleRoots))); } @NotNull @@ -169,12 +164,13 @@ class StructureFilterPopupComponent extends FilterPopupComponent files = filter == null ? Collections.emptySet() : getAllFiles(dataPack, filter); + VcsLogFileFilter filter = myFilterModel.getFilter(); + VcsLogRootFilter rootFilter = filter == null ? null : filter.getRootFilter(); + Collection files = filter == null ? Collections.emptySet() : getAllFiles(filter); VcsStructureChooser chooser = new VcsStructureChooser(project, "Select Files or Folders to Filter", files, new ArrayList(dataPack.getLogProviders().keySet())); if (chooser.showAndGet()) { - myFilterModel.setFilter(VcsLogStructureFilterImpl.build(chooser.getSelectedFiles(), dataPack)); + myFilterModel.setFilter(new VcsLogFileFilter(new VcsLogStructureFilterImpl(new HashSet(chooser.getSelectedFiles())), rootFilter)); } } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogClassicFilterUi.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogClassicFilterUi.java index 771811222de9..937ca7ff12b3 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogClassicFilterUi.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogClassicFilterUi.java @@ -31,11 +31,10 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.DocumentAdapter; import com.intellij.ui.SearchTextFieldWithStoredHistory; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.*; -import com.intellij.vcs.log.data.VcsLogDataHolder; -import com.intellij.vcs.log.data.VcsLogStructureFilterImpl; -import com.intellij.vcs.log.data.VcsLogUiProperties; +import com.intellij.vcs.log.data.*; import com.intellij.vcs.log.impl.VcsLogFilterCollectionImpl; import com.intellij.vcs.log.impl.VcsLogHashFilterImpl; import com.intellij.vcs.log.ui.VcsLogUiImpl; @@ -49,7 +48,9 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** */ @@ -68,7 +69,7 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { @NotNull private final BranchFilterModel myBranchFilterModel; @NotNull private final FilterModel myUserFilterModel; @NotNull private final FilterModel myDateFilterModel; - @NotNull private final FilterModel myStructureFilterModel; + @NotNull private final FilterModel myStructureFilterModel; @NotNull private final TextFilterModel myTextFilterModel; public VcsLogClassicFilterUi(@NotNull VcsLogUiImpl ui, @@ -90,7 +91,7 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { myBranchFilterModel = new BranchFilterModel(dataPackGetter); myUserFilterModel = new FilterModel(dataPackGetter); myDateFilterModel = new FilterModel(dataPackGetter); - myStructureFilterModel = new FilterModel(dataPackGetter); + myStructureFilterModel = new FilterModel(dataPackGetter); myTextFilterModel = new TextFilterModel(dataPackGetter); updateUiOnFilterChange(); @@ -103,7 +104,7 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { @Override public void run() { myUi.applyFiltersAndUpdateUi(); - myBranchFilterModel.onStructureFilterChanged(myStructureFilterModel.getFilter()); + myBranchFilterModel.onStructureFilterChanged(new HashSet(myLogDataHolder.getRoots()), myStructureFilterModel.getFilter()); } }); } @@ -157,7 +158,8 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { filtersFromText.second, myDateFilterModel.getFilter(), filtersFromText.first, - myStructureFilterModel.getFilter()); + myStructureFilterModel.getFilter() == null ? null : myStructureFilterModel.getFilter().getStructureFilter(), + myStructureFilterModel.getFilter() == null ? null : myStructureFilterModel.getFilter().getRootFilter()); } @NotNull @@ -281,11 +283,11 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { super(provider); } - public void onStructureFilterChanged(@Nullable VcsLogStructureFilter filter) { + public void onStructureFilterChanged(@NotNull Set roots, @Nullable VcsLogFileFilter filter) { if (filter == null) { myVisibleRoots = null; } else { - myVisibleRoots = filter.getRoots(); + myVisibleRoots = VcsLogFileFilterUtil.collectRoots(roots, filter.getRootFilter(), filter.getStructureFilter()); } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogFileFilter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogFileFilter.java new file mode 100644 index 000000000000..b4b5411c4c7c --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogFileFilter.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2014 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.vcs.log.ui.filter; + +import com.intellij.vcs.log.VcsLogFilter; +import com.intellij.vcs.log.VcsLogRootFilter; +import com.intellij.vcs.log.VcsLogStructureFilter; +import org.jetbrains.annotations.Nullable; + +public class VcsLogFileFilter implements VcsLogFilter { + @Nullable private final VcsLogStructureFilter myStructureFilter; + @Nullable private final VcsLogRootFilter myRootFilter; + + public VcsLogFileFilter(@Nullable VcsLogStructureFilter structureFilter, @Nullable VcsLogRootFilter rootFilter) { + myStructureFilter = structureFilter; + myRootFilter = rootFilter; + } + + @Nullable + public VcsLogStructureFilter getStructureFilter() { + return myStructureFilter; + } + + @Nullable + public VcsLogRootFilter getRootFilter() { + return myRootFilter; + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/BranchesPanel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/BranchesPanel.java index ac6765b8dbf9..7762b2fd4ea0 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/BranchesPanel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/BranchesPanel.java @@ -158,9 +158,9 @@ public class BranchesPanel extends JPanel { } public void onFiltersChange(@NotNull VcsLogFilterCollection filters) { - VcsLogStructureFilter structureFilter = filters.getStructureFilter(); - if (structureFilter != null) { - myRoots = structureFilter.getRoots(); + VcsLogRootFilter rootFilter = filters.getRootFilter(); + if (rootFilter != null) { + myRoots = rootFilter.getRoots(); } else { myRoots = null; diff --git a/plugins/git4idea/src/git4idea/log/GitBekParentFixer.java b/plugins/git4idea/src/git4idea/log/GitBekParentFixer.java index 4bcfade3cfa8..7778b23e2327 100644 --- a/plugins/git4idea/src/git4idea/log/GitBekParentFixer.java +++ b/plugins/git4idea/src/git4idea/log/GitBekParentFixer.java @@ -136,6 +136,12 @@ class GitBekParentFixer { return null; } + @Nullable + @Override + public VcsLogRootFilter getRootFilter() { + return null; + } + @Override public boolean isEmpty() { return false; diff --git a/plugins/git4idea/src/git4idea/log/GitLogProvider.java b/plugins/git4idea/src/git4idea/log/GitLogProvider.java index 1abb59c5476f..88ab9a892f53 100644 --- a/plugins/git4idea/src/git4idea/log/GitLogProvider.java +++ b/plugins/git4idea/src/git4idea/log/GitLogProvider.java @@ -440,7 +440,7 @@ public class GitLogProvider implements VcsLogProvider { // note: structure filter must be the last parameter, because it uses "--" which separates parameters from paths if (filterCollection.getStructureFilter() != null) { - Collection files = filterCollection.getStructureFilter().getFiles(root); + Collection files = filterCollection.getStructureFilter().getFiles(); if (!files.isEmpty()) { filterParameters.add("--simplify-merges"); filterParameters.add("--"); diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java index 842e415a8476..5f9e2cea2071 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java @@ -233,7 +233,7 @@ public class HgLogProvider implements VcsLogProvider { } if (filterCollection.getStructureFilter() != null) { - for (VirtualFile file : filterCollection.getStructureFilter().getFiles(root)) { + for (VirtualFile file : filterCollection.getStructureFilter().getFiles()) { filterParameters.add(file.getPath()); } }