Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java152
1 files changed, 132 insertions, 20 deletions
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 a3203e349f..fedd7cda15 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
@@ -55,6 +55,7 @@ import java.util.List;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectReader;
@@ -99,7 +100,7 @@ public class RenameDetector {
}
};
- private final List<DiffEntry> entries = new ArrayList<DiffEntry>();
+ private List<DiffEntry> entries = new ArrayList<DiffEntry>();
private List<DiffEntry> deleted = new ArrayList<DiffEntry>();
@@ -112,6 +113,13 @@ public class RenameDetector {
/** Similarity score required to pair an add/delete as a rename. */
private int renameScore = 60;
+ /**
+ * Similarity score required to keep modified file pairs together. Any
+ * modified file pairs with a similarity score below this will be broken
+ * apart.
+ */
+ private int breakScore = -1;
+
/** Limit in the number of files to consider for renames. */
private int renameLimit;
@@ -159,6 +167,29 @@ public class RenameDetector {
renameScore = score;
}
+ /**
+ * @return the similarity score required to keep modified file pairs
+ * together. Any modify pairs that score below this will be broken
+ * apart into separate add/deletes. Values less than or equal to
+ * zero indicate that no modifies will be broken apart. Values over
+ * 100 cause all modify pairs to be broken.
+ */
+ public int getBreakScore() {
+ return breakScore;
+ }
+
+ /**
+ * @param breakScore
+ * the similarity score required to keep modified file pairs
+ * together. Any modify pairs that score below this will be
+ * broken apart into separate add/deletes. Values less than or
+ * equal to zero indicate that no modifies will be broken apart.
+ * Values over 100 cause all modify pairs to be broken.
+ */
+ public void setBreakScore(int breakScore) {
+ this.breakScore = breakScore;
+ }
+
/** @return limit on number of paths to perform inexact rename detection. */
public int getRenameLimit() {
return renameLimit;
@@ -219,10 +250,13 @@ public class RenameDetector {
break;
case MODIFY:
- if (sameType(entry.getOldMode(), entry.getNewMode()))
+ if (sameType(entry.getOldMode(), entry.getNewMode())) {
entries.add(entry);
- else
- entries.addAll(DiffEntry.breakModify(entry));
+ } else {
+ List<DiffEntry> tmp = DiffEntry.breakModify(entry);
+ deleted.add(tmp.get(0));
+ added.add(tmp.get(1));
+ }
break;
case COPY:
@@ -275,8 +309,15 @@ public class RenameDetector {
if (pm == null)
pm = NullProgressMonitor.INSTANCE;
- findExactRenames(pm);
- findContentRenames(pm);
+ ObjectReader reader = repo.newObjectReader();
+ try {
+ breakModifies(reader, pm);
+ findExactRenames(pm);
+ findContentRenames(reader, pm);
+ rejoinModifies(pm);
+ } finally {
+ reader.release();
+ }
entries.addAll(added);
added = null;
@@ -289,25 +330,96 @@ public class RenameDetector {
return Collections.unmodifiableList(entries);
}
- private void findContentRenames(ProgressMonitor pm) throws IOException {
+ private void breakModifies(ObjectReader reader, ProgressMonitor pm)
+ throws IOException {
+ if (breakScore <= 0)
+ return;
+
+ ArrayList<DiffEntry> newEntries = new ArrayList<DiffEntry>(entries.size());
+
+ pm.beginTask(JGitText.get().renamesBreakingModifies, entries.size());
+
+ for (int i = 0; i < entries.size(); i++) {
+ DiffEntry e = entries.get(i);
+ if (e.getChangeType() == ChangeType.MODIFY) {
+ int score = calculateModifyScore(reader, e);
+ if (score < breakScore) {
+ List<DiffEntry> tmp = DiffEntry.breakModify(e);
+ DiffEntry del = tmp.get(0);
+ del.score = score;
+ deleted.add(del);
+ added.add(tmp.get(1));
+ } else {
+ newEntries.add(e);
+ }
+ } else {
+ newEntries.add(e);
+ }
+ pm.update(1);
+ }
+
+ entries = newEntries;
+ }
+
+ private void rejoinModifies(ProgressMonitor pm) {
+ HashMap<String, DiffEntry> nameMap = new HashMap<String, DiffEntry>();
+ ArrayList<DiffEntry> newAdded = new ArrayList<DiffEntry>(added.size());
+
+ pm.beginTask(JGitText.get().renamesRejoiningModifies, added.size()
+ + deleted.size());
+
+ for (DiffEntry src : deleted) {
+ nameMap.put(src.oldName, src);
+ pm.update(1);
+ }
+
+ for (DiffEntry dst : added) {
+ DiffEntry src = nameMap.remove(dst.newName);
+ if (src != null) {
+ if (sameType(src.oldMode, dst.newMode)) {
+ entries.add(DiffEntry.pair(ChangeType.MODIFY, src, dst,
+ src.score));
+ } else {
+ nameMap.put(src.oldName, src);
+ newAdded.add(dst);
+ }
+ } else {
+ newAdded.add(dst);
+ }
+ pm.update(1);
+ }
+
+ added = newAdded;
+ deleted = new ArrayList<DiffEntry>(nameMap.values());
+ }
+
+ private int calculateModifyScore(ObjectReader reader, DiffEntry d)
+ throws IOException {
+ SimilarityIndex src = new SimilarityIndex();
+ src.hash(reader.open(d.oldId.toObjectId(), Constants.OBJ_BLOB));
+ src.sort();
+
+ SimilarityIndex dst = new SimilarityIndex();
+ dst.hash(reader.open(d.newId.toObjectId(), Constants.OBJ_BLOB));
+ dst.sort();
+ return src.score(dst, 100);
+ }
+
+ private void findContentRenames(ObjectReader reader, ProgressMonitor pm)
+ throws IOException {
int cnt = Math.max(added.size(), deleted.size());
if (cnt == 0)
return;
if (getRenameLimit() == 0 || cnt <= getRenameLimit()) {
- ObjectReader reader = repo.newObjectReader();
- try {
- SimilarityRenameDetector d;
-
- d = new SimilarityRenameDetector(reader, deleted, added);
- d.setRenameScore(getRenameScore());
- d.compute(pm);
- deleted = d.getLeftOverSources();
- added = d.getLeftOverDestinations();
- entries.addAll(d.getMatches());
- } finally {
- reader.release();
- }
+ SimilarityRenameDetector d;
+
+ d = new SimilarityRenameDetector(reader, deleted, added);
+ d.setRenameScore(getRenameScore());
+ d.compute(pm);
+ deleted = d.getLeftOverSources();
+ added = d.getLeftOverDestinations();
+ entries.addAll(d.getMatches());
} else {
overRenameLimit = true;
}

Back to the top