diff options
Diffstat (limited to 'org.eclipse.jgit')
3 files changed, 143 insertions, 3 deletions
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index 6d43eba6c5..b919140989 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF @@ -70,6 +70,8 @@ Export-Package: org.eclipse.jgit.annotations;version="6.3.0", org.eclipse.jgit.internal;version="6.3.0"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.http.test", + org.eclipse.jgit.internal.diff;version="6.3.0"; + x-friends:="org.eclipse.jgit.test", org.eclipse.jgit.internal.diffmergetool;version="6.3.0"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.pgm.test, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java index 10d77528f6..77967df2e5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java @@ -41,6 +41,7 @@ import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.NoWorkTreeException; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.internal.diff.FilteredRenameDetector; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.MutableObjectId; @@ -1109,9 +1110,10 @@ public class BlameGenerator implements AutoCloseable { treeWalk.setFilter(TreeFilter.ANY_DIFF); treeWalk.reset(parent.getTree(), commit.getTree()); - renameDetector.reset(); - renameDetector.addAll(DiffEntry.scan(treeWalk)); - for (DiffEntry ent : renameDetector.compute()) { + List<DiffEntry> diffs = DiffEntry.scan(treeWalk); + FilteredRenameDetector filteredRenameDetector = new FilteredRenameDetector( + renameDetector); + for (DiffEntry ent : filteredRenameDetector.compute(diffs, path)) { if (isRename(ent) && ent.getNewPath().equals(path.getPath())) return ent; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/diff/FilteredRenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diff/FilteredRenameDetector.java new file mode 100644 index 0000000000..d65624fc6a --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/diff/FilteredRenameDetector.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2022, Simeon Andreev and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.eclipse.jgit.internal.diff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.jgit.diff.DiffEntry; +import org.eclipse.jgit.diff.DiffEntry.ChangeType; +import org.eclipse.jgit.diff.RenameDetector; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.treewalk.filter.PathFilter; + +/** + * Provides rename detection in special cases such as blame, where only a subset + * of the renames detected by {@link RenameDetector} is of interest. + */ +public class FilteredRenameDetector { + + private final RenameDetector renameDetector; + + /** + * @param repository + * The repository in which to check for renames. + */ + public FilteredRenameDetector(Repository repository) { + this(new RenameDetector(repository)); + } + + /** + * @param renameDetector + * The {@link RenameDetector} to use when checking for renames. + */ + public FilteredRenameDetector(RenameDetector renameDetector) { + this.renameDetector = renameDetector; + } + + /** + * @param diffs + * The set of changes to check. + * @param pathFilter + * Filter out changes that didn't affect this path. + * @return The subset of changes that affect only the filtered path. + * @throws IOException + */ + public List<DiffEntry> compute(List<DiffEntry> diffs, + PathFilter pathFilter) throws IOException { + return compute(diffs, Arrays.asList(pathFilter)); + } + + /** + * Tries to avoid computation overhead in {@link RenameDetector#compute()} + * by filtering diffs related to the path filters only. + * <p> + * Note: current implementation only optimizes added or removed diffs, + * further optimization is possible. + * + * @param changes + * The set of changes to check. + * @param pathFilters + * Filter out changes that didn't affect these paths. + * @return The subset of changes that affect only the filtered paths. + * @throws IOException + * @see RenameDetector#compute() + */ + public List<DiffEntry> compute(List<DiffEntry> changes, + List<PathFilter> pathFilters) throws IOException { + + if (pathFilters == null) { + throw new IllegalArgumentException("Must specify path filters"); //$NON-NLS-1$ + } + + Set<String> paths = new HashSet<>(pathFilters.size()); + for (PathFilter pathFilter : pathFilters) { + paths.add(pathFilter.getPath()); + } + + List<DiffEntry> filtered = new ArrayList<>(); + + // For new path: skip ADD's that don't match given paths + for (DiffEntry diff : changes) { + ChangeType changeType = diff.getChangeType(); + if (changeType != ChangeType.ADD + || paths.contains(diff.getNewPath())) { + filtered.add(diff); + } + } + + renameDetector.reset(); + renameDetector.addAll(filtered); + List<DiffEntry> sourceChanges = renameDetector.compute(); + + filtered.clear(); + + // For old path: skip DELETE's that don't match given paths + for (DiffEntry diff : changes) { + ChangeType changeType = diff.getChangeType(); + if (changeType != ChangeType.DELETE + || paths.contains(diff.getOldPath())) { + filtered.add(diff); + } + } + + renameDetector.reset(); + renameDetector.addAll(filtered); + List<DiffEntry> targetChanges = renameDetector.compute(); + + List<DiffEntry> result = new ArrayList<>(); + + for (DiffEntry sourceChange : sourceChanges) { + if (paths.contains(sourceChange.getNewPath())) { + result.add(sourceChange); + } + } + for (DiffEntry targetChange : targetChanges) { + if (paths.contains(targetChange.getOldPath())) { + result.add(targetChange); + } + } + + renameDetector.reset(); + return result; + } +} |