diff options
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.java | 152 |
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; } |