diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java index 6203feda4..2ea3cd7eb 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java @@ -542,6 +542,43 @@ public void testBreakModify_RejoinIfUnpaired() throws Exception { assertEquals(0, modify.score); } + @Test + public void testExactRename_LargeFile() throws Exception { + ObjectId aId = blob("blah\nblah\nfoo"); // size = 14 + + DiffEntry a = DiffEntry.add(PATH_A, aId); + DiffEntry b = DiffEntry.delete(PATH_Q, aId); + + rd.add(a); + rd.add(b); + + // Exact renames are identified for large files + rd.setBigFileThreshold(10); + List entries = rd.compute(); + assertEquals(1, entries.size()); + assertRename(b, a, 100, entries.get(0)); + } + + @Test + public void testInexactRename_LargeFile() throws Exception { + ObjectId aId = blob("blah\nblah\nfoo"); // size = 14 + ObjectId bId = blob("bla\nblah\nfoo"); // size = 13 + + DiffEntry a = DiffEntry.add(PATH_A, aId); + DiffEntry b = DiffEntry.delete(PATH_Q, bId); + + rd.add(a); + rd.add(b); + + rd.setBigFileThreshold(10); + + // Inexact renames are not detected for large files + List entries = rd.compute(); + assertEquals(2, entries.size()); + assertAdd(PATH_A, aId, FileMode.REGULAR_FILE, entries.get(0)); + assertDelete(PATH_Q, bId, FileMode.REGULAR_FILE, entries.get(1)); + } + @Test public void testSetRenameScore_IllegalArgs() throws Exception { try { @@ -634,4 +671,15 @@ private static void assertAdd(String newName, ObjectId newId, assertEquals(AbbreviatedObjectId.fromObjectId(newId), add.newId); assertEquals(newMode, add.newMode); } + + private static void assertDelete(String oldName, ObjectId oldId, + FileMode oldMode, DiffEntry delete) { + assertEquals(DiffEntry.DEV_NULL, delete.newPath); + assertEquals(DiffEntry.A_ZERO, delete.newId); + assertEquals(FileMode.MISSING, delete.newMode); + assertEquals(ChangeType.DELETE, delete.changeType); + assertEquals(oldName, delete.oldPath); + assertEquals(AbbreviatedObjectId.fromObjectId(oldId), delete.oldId); + assertEquals(oldMode, delete.oldMode); + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java index 80e1b1829..75784c255 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java @@ -12,6 +12,7 @@ import static org.eclipse.jgit.diff.DiffEntry.Side.NEW; import static org.eclipse.jgit.diff.DiffEntry.Side.OLD; +import static org.eclipse.jgit.storage.pack.PackConfig.DEFAULT_BIG_FILE_THRESHOLD; import java.io.IOException; import java.util.ArrayList; @@ -97,6 +98,12 @@ private int sortOf(ChangeType changeType) { /** Limit in the number of files to consider for renames. */ private int renameLimit; + /** + * File size threshold (in bytes) for detecting renames. Files larger + * than this size will not be processed for renames. + */ + private int bigFileThreshold = DEFAULT_BIG_FILE_THRESHOLD; + /** Set if the number of adds or deletes was over the limit. */ private boolean overRenameLimit; @@ -208,6 +215,26 @@ public void setRenameLimit(int limit) { renameLimit = limit; } + /** + * Get file size threshold for detecting renames. Files larger + * than this size will not be processed for rename detection. + * + * @return threshold in bytes of the file size. + * @since 5.12 + */ + public int getBigFileThreshold() { return bigFileThreshold; } + + /** + * Set the file size threshold for detecting renames. Files larger than this + * threshold will be skipped during rename detection computation. + * + * @param threshold file size threshold in bytes. + * @since 5.12 + */ + public void setBigFileThreshold(int threshold) { + this.bigFileThreshold = threshold; + } + /** * Check if the detector is over the rename limit. *

@@ -493,6 +520,7 @@ private void findContentRenames(ContentSource.Pair reader, d = new SimilarityRenameDetector(reader, deleted, added); d.setRenameScore(getRenameScore()); + d.setBigFileThreshold(getBigFileThreshold()); d.compute(pm); overRenameLimit |= d.isTableOverflow(); deleted = d.getLeftOverSources(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java index 74a11a024..082f31d17 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java @@ -12,6 +12,7 @@ import static org.eclipse.jgit.diff.DiffEntry.Side.NEW; import static org.eclipse.jgit.diff.DiffEntry.Side.OLD; +import static org.eclipse.jgit.storage.pack.PackConfig.DEFAULT_BIG_FILE_THRESHOLD; import java.io.IOException; import java.util.ArrayList; @@ -80,6 +81,12 @@ class SimilarityRenameDetector { /** Score a pair must exceed to be considered a rename. */ private int renameScore = 60; + /** + * File size threshold (in bytes) for detecting renames. Files larger + * than this size will not be processed for renames. + */ + private int bigFileThreshold = DEFAULT_BIG_FILE_THRESHOLD; + /** Set if any {@link SimilarityIndex.TableFullException} occurs. */ private boolean tableOverflow; @@ -96,6 +103,10 @@ void setRenameScore(int score) { renameScore = score; } + void setBigFileThreshold(int threshold) { + bigFileThreshold = threshold; + } + void compute(ProgressMonitor pm) throws IOException, CancelledException { if (pm == null) pm = NullProgressMonitor.INSTANCE; @@ -253,6 +264,11 @@ private int buildMatrix(ProgressMonitor pm) continue; } + if (max > bigFileThreshold) { + pm.update(1); + continue; + } + if (s == null) { try { s = hash(OLD, srcEnt);