summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit')
-rw-r--r--org.eclipse.jgit/META-INF/MANIFEST.MF2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/diff/FilteredRenameDetector.java136
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;
+ }
+}