diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties index dc738d385..e7dce1bf5 100644 --- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties +++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties @@ -168,6 +168,7 @@ usage_listCreateOrDeleteBranches=List, create, or delete branches usage_logAllPretty=format:%H %ct %P' output=log --all '--pretty=format:%H %ct %P' output usage_moveRenameABranch=move/rename a branch usage_nameStatus=show only name and status of files +usage_noRenames=disable rename detection usage_outputFile=Output file usage_path=path usage_performFsckStyleChecksOnReceive=perform fsck style checks on receive diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java index 2be572204..b6650a4ea 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java @@ -46,9 +46,7 @@ package org.eclipse.jgit.pgm; import java.io.BufferedOutputStream; -import java.io.IOException; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.List; import org.eclipse.jgit.diff.DiffEntry; @@ -62,8 +60,6 @@ import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.TextProgressMonitor; import org.eclipse.jgit.pgm.opt.PathTreeFilterHandler; import org.eclipse.jgit.treewalk.AbstractTreeIterator; -import org.eclipse.jgit.treewalk.TreeWalk; -import org.eclipse.jgit.treewalk.filter.AndTreeFilter; import org.eclipse.jgit.treewalk.filter.TreeFilter; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.Option; @@ -74,12 +70,10 @@ class Diff extends TextBuiltin { new BufferedOutputStream(System.out)); @Argument(index = 0, metaVar = "metaVar_treeish", required = true) - void tree_0(final AbstractTreeIterator c) { - trees.add(c); - } + private AbstractTreeIterator oldTree; @Argument(index = 1, metaVar = "metaVar_treeish", required = true) - private final List trees = new ArrayList(); + private AbstractTreeIterator newTree; @Option(name = "--", metaVar = "metaVar_paths", multiValued = true, handler = PathTreeFilterHandler.class) private TreeFilter pathFilter = TreeFilter.ALL; @@ -89,7 +83,12 @@ class Diff extends TextBuiltin { boolean showPatch; @Option(name = "-M", usage = "usage_detectRenames") - private boolean detectRenames; + private Boolean detectRenames; + + @Option(name = "--no-renames", usage = "usage_noRenames") + void noRenames(@SuppressWarnings("unused") boolean on) { + detectRenames = Boolean.FALSE; + } @Option(name = "-l", usage = "usage_renameLimit") private Integer renameLimit; @@ -136,16 +135,27 @@ class Diff extends TextBuiltin { @Override protected void run() throws Exception { - List files = scan(); + diffFmt.setRepository(db); + try { + diffFmt.setProgressMonitor(new TextProgressMonitor()); + diffFmt.setPathFilter(pathFilter); + if (detectRenames != null) + diffFmt.setDetectRenames(detectRenames.booleanValue()); + if (renameLimit != null && diffFmt.isDetectRenames()) { + RenameDetector rd = diffFmt.getRenameDetector(); + rd.setRenameLimit(renameLimit.intValue()); + } - if (showNameAndStatusOnly) { - nameStatus(out, files); - out.flush(); + if (showNameAndStatusOnly) { + nameStatus(out, diffFmt.scan(oldTree, newTree)); + out.flush(); - } else { - diffFmt.setRepository(db); - diffFmt.format(files); - diffFmt.flush(); + } else { + diffFmt.format(oldTree, newTree); + diffFmt.flush(); + } + } finally { + diffFmt.release(); } } @@ -174,23 +184,4 @@ class Diff extends TextBuiltin { } } } - - private List scan() throws IOException { - final TreeWalk walk = new TreeWalk(db); - walk.reset(); - walk.setRecursive(true); - for (final AbstractTreeIterator i : trees) - walk.addTree(i); - walk.setFilter(AndTreeFilter.create(TreeFilter.ANY_DIFF, pathFilter)); - - List files = DiffEntry.scan(walk); - if (detectRenames) { - RenameDetector rd = new RenameDetector(db); - if (renameLimit != null) - rd.setRenameLimit(renameLimit.intValue()); - rd.addAll(files); - files = rd.compute(new TextProgressMonitor()); - } - return files; - } } diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java index d0ae22a21..2b29f7338 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java @@ -51,32 +51,25 @@ import java.text.DateFormat; import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; -import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TimeZone; -import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.diff.DiffFormatter; import org.eclipse.jgit.diff.RawTextIgnoreAllWhitespace; import org.eclipse.jgit.diff.RawTextIgnoreLeadingWhitespace; import org.eclipse.jgit.diff.RawTextIgnoreTrailingWhitespace; import org.eclipse.jgit.diff.RawTextIgnoreWhitespaceChange; import org.eclipse.jgit.diff.RenameDetector; -import org.eclipse.jgit.diff.DiffEntry.ChangeType; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Ref; -import org.eclipse.jgit.revwalk.FollowFilter; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevWalk; -import org.eclipse.jgit.treewalk.TreeWalk; -import org.eclipse.jgit.treewalk.filter.AndTreeFilter; -import org.eclipse.jgit.treewalk.filter.TreeFilter; import org.kohsuke.args4j.Option; @Command(common = true, usage = "usage_viewCommitHistory") @@ -98,7 +91,12 @@ class Log extends RevWalkTextBuiltin { boolean showPatch; @Option(name = "-M", usage = "usage_detectRenames") - private boolean detectRenames; + private Boolean detectRenames; + + @Option(name = "--no-renames", usage = "usage_noRenames") + void noRenames(@SuppressWarnings("unused") boolean on) { + detectRenames = Boolean.FALSE; + } @Option(name = "-l", usage = "usage_renameLimit") private Integer renameLimit; @@ -155,6 +153,24 @@ class Log extends RevWalkTextBuiltin { return ret; } + @Override + protected void run() throws Exception { + diffFmt.setRepository(db); + try { + diffFmt.setPathFilter(pathFilter); + if (detectRenames != null) + diffFmt.setDetectRenames(detectRenames.booleanValue()); + if (renameLimit != null && diffFmt.isDetectRenames()) { + RenameDetector rd = diffFmt.getRenameDetector(); + rd.setRenameLimit(renameLimit.intValue()); + } + + super.run(); + } finally { + diffFmt.release(); + } + } + @Override protected void show(final RevCommit c) throws Exception { out.print(CLIText.get().commitLabel); @@ -196,71 +212,16 @@ class Log extends RevWalkTextBuiltin { } private void showDiff(RevCommit c) throws IOException { - final TreeWalk tw = new TreeWalk(db); - tw.setRecursive(true); - tw.reset(); - tw.addTree(c.getParent(0).getTree()); - tw.addTree(c.getTree()); - tw.setFilter(AndTreeFilter.create(pathFilter, TreeFilter.ANY_DIFF)); - - List files = DiffEntry.scan(tw); - if (pathFilter instanceof FollowFilter && isAdd(files)) { - // The file we are following was added here, find where it - // came from so we can properly show the rename or copy, - // then continue digging backwards. - // - tw.reset(); - tw.addTree(c.getParent(0).getTree()); - tw.addTree(c.getTree()); - tw.setFilter(TreeFilter.ANY_DIFF); - files = updateFollowFilter(detectRenames(DiffEntry.scan(tw))); - - } else if (detectRenames) - files = detectRenames(files); - - if (showNameAndStatusOnly) { - Diff.nameStatus(out, files); - - } else { - diffFmt.setRepository(db); - diffFmt.format(files); + final RevTree a = c.getParent(0).getTree(); + final RevTree b = c.getTree(); + + if (showNameAndStatusOnly) + Diff.nameStatus(out, diffFmt.scan(a, b)); + else { + diffFmt.format(a, b); diffFmt.flush(); } out.println(); - } - - private List detectRenames(List files) - throws IOException { - RenameDetector rd = new RenameDetector(db); - if (renameLimit != null) - rd.setRenameLimit(renameLimit.intValue()); - rd.addAll(files); - return rd.compute(); - } - - private boolean isAdd(List files) { - String oldPath = ((FollowFilter) pathFilter).getPath(); - for (DiffEntry ent : files) { - if (ent.getChangeType() == ChangeType.ADD - && ent.getNewPath().equals(oldPath)) - return true; - } - return false; - } - - private List updateFollowFilter(List files) { - String oldPath = ((FollowFilter) pathFilter).getPath(); - for (DiffEntry ent : files) { - if (isRename(ent) && ent.getNewPath().equals(oldPath)) { - pathFilter = FollowFilter.create(ent.getOldPath()); - return Collections.singletonList(ent); - } - } - return Collections.emptyList(); - } - - private static boolean isRename(DiffEntry ent) { - return ent.getChangeType() == ChangeType.RENAME - || ent.getChangeType() == ChangeType.COPY; + out.flush(); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java index d7a10e4b1..eefbefb07 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java @@ -77,10 +77,17 @@ public class DiffFormatterTest extends RepositoryTestCase { df.setAbbreviationLength(8); } + @Override + public void tearDown() throws Exception { + if (df != null) + df.release(); + super.tearDown(); + } + public void testCreateFileHeader_Add() throws Exception { ObjectId adId = blob("a\nd\n"); DiffEntry ent = DiffEntry.add("FOO", adId); - FileHeader fh = df.createFileHeader(ent); + FileHeader fh = df.toFileHeader(ent); String diffHeader = "diff --git a/FOO b/FOO\n" // + "new file mode " + REGULAR_FILE + "\n" @@ -115,7 +122,7 @@ public class DiffFormatterTest extends RepositoryTestCase { public void testCreateFileHeader_Delete() throws Exception { ObjectId adId = blob("a\nd\n"); DiffEntry ent = DiffEntry.delete("FOO", adId); - FileHeader fh = df.createFileHeader(ent); + FileHeader fh = df.toFileHeader(ent); String diffHeader = "diff --git a/FOO b/FOO\n" // + "deleted file mode " + REGULAR_FILE + "\n" @@ -158,7 +165,7 @@ public class DiffFormatterTest extends RepositoryTestCase { DiffEntry mod = DiffEntry.pair(ChangeType.MODIFY, ad, abcd, 0); - FileHeader fh = df.createFileHeader(mod); + FileHeader fh = df.toFileHeader(mod); assertEquals(diffHeader, RawParseUtils.decode(fh.getBuffer())); assertEquals(0, fh.getStartOffset()); @@ -193,7 +200,7 @@ public class DiffFormatterTest extends RepositoryTestCase { DiffEntry mod = DiffEntry.pair(ChangeType.MODIFY, ad, abcd, 0); - FileHeader fh = df.createFileHeader(mod); + FileHeader fh = df.toFileHeader(mod); assertEquals(diffHeader, RawParseUtils.decode(fh.getBuffer())); assertEquals(FileHeader.PatchType.BINARY, fh.getPatchType()); @@ -218,7 +225,7 @@ public class DiffFormatterTest extends RepositoryTestCase { DiffEntry mod = DiffEntry.pair(ChangeType.MODIFY, ad, abcd, 0); - FileHeader fh = df.createFileHeader(mod); + FileHeader fh = df.toFileHeader(mod); assertEquals(diffHeader, RawParseUtils.decode(fh.getBuffer())); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffConfig.java index 91b7467ae..4b86f55fc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffConfig.java @@ -55,12 +55,28 @@ public class DiffConfig { } }; + private final boolean noPrefix; + + private final boolean renames; + private final int renameLimit; private DiffConfig(final Config rc) { + noPrefix = rc.getBoolean("diff", "noprefix", false); + renames = rc.getBoolean("diff", "renames", false); renameLimit = rc.getInt("diff", "renamelimit", 200); } + /** @return true if the prefix "a/" and "b/" should be suppressed. */ + public boolean isNoPrefix() { + return noPrefix; + } + + /** @return true if rename detection is enabled by default. */ + public boolean isRenameDetectionEnabled() { + return renames; + } + /** @return limit on number of paths to perform inexact rename detection. */ public int getRenameLimit() { return renameLimit; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java index cb145e4b5..3590ef5b4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java @@ -45,6 +45,7 @@ package org.eclipse.jgit.diff; import static org.eclipse.jgit.diff.DiffEntry.ChangeType.ADD; +import static org.eclipse.jgit.diff.DiffEntry.ChangeType.COPY; import static org.eclipse.jgit.diff.DiffEntry.ChangeType.DELETE; import static org.eclipse.jgit.diff.DiffEntry.ChangeType.MODIFY; import static org.eclipse.jgit.diff.DiffEntry.ChangeType.RENAME; @@ -56,6 +57,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Collection; +import java.util.Collections; import java.util.List; import org.eclipse.jgit.JGitText; @@ -65,16 +67,26 @@ import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.LargeObjectException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.lib.AbbreviatedObjectId; +import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.patch.FileHeader; import org.eclipse.jgit.patch.HunkHeader; import org.eclipse.jgit.patch.FileHeader.PatchType; +import org.eclipse.jgit.revwalk.FollowFilter; +import org.eclipse.jgit.revwalk.RevTree; +import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.storage.pack.PackConfig; +import org.eclipse.jgit.treewalk.AbstractTreeIterator; +import org.eclipse.jgit.treewalk.CanonicalTreeParser; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.AndTreeFilter; +import org.eclipse.jgit.treewalk.filter.TreeFilter; import org.eclipse.jgit.util.QuotedString; import org.eclipse.jgit.util.io.DisabledOutputStream; @@ -96,6 +108,8 @@ public class DiffFormatter { private Repository db; + private ObjectReader reader; + private int context = 3; private int abbreviationLength = 7; @@ -108,6 +122,12 @@ public class DiffFormatter { private String newPrefix = "b/"; + private TreeFilter pathFilter = TreeFilter.ALL; + + private RenameDetector renameDetector; + + private ProgressMonitor progressMonitor; + /** * Create a new formatter with a default level of context. * @@ -128,11 +148,25 @@ public class DiffFormatter { /** * Set the repository the formatter can load object contents from. * + * Once a repository has been set, the formatter must be released to ensure + * the internal ObjectReader is able to release its resources. + * * @param repository * source repository holding referenced objects. */ public void setRepository(Repository repository) { + if (reader != null) + reader.release(); + db = repository; + reader = db.newObjectReader(); + + DiffConfig dc = db.getConfig().get(DiffConfig.KEY); + if (dc.isNoPrefix()) { + setOldPrefix(""); + setNewPrefix(""); + } + setDetectRenames(dc.isRenameDetectionEnabled()); } /** @@ -220,6 +254,64 @@ public class DiffFormatter { newPrefix = prefix; } + /** @return true if rename detection is enabled. */ + public boolean isDetectRenames() { + return renameDetector != null; + } + + /** + * Enable or disable rename detection. + * + * Before enabling rename detection the repository must be set with + * {@link #setRepository(Repository)}. Once enabled the detector can be + * configured away from its defaults by obtaining the instance directly from + * {@link #getRenameDetector()} and invoking configuration. + * + * @param on + * if rename detection should be enabled. + */ + public void setDetectRenames(boolean on) { + if (on && renameDetector == null) { + assertHaveRepository(); + renameDetector = new RenameDetector(db); + } else if (!on) + renameDetector = null; + } + + /** @return the rename detector if rename detection is enabled. */ + public RenameDetector getRenameDetector() { + return renameDetector; + } + + /** + * Set the progress monitor for long running rename detection. + * + * @param pm + * progress monitor to receive rename detection status through. + */ + public void setProgressMonitor(ProgressMonitor pm) { + progressMonitor = pm; + } + + /** + * Set the filter to produce only specific paths. + * + * If the filter is an instance of {@link FollowFilter}, the filter path + * will be updated during successive scan or format invocations. The updated + * path can be obtained from {@link #getPathFilter()}. + * + * @param filter + * the tree filter to apply. + */ + public void setPathFilter(TreeFilter filter) { + pathFilter = filter != null ? filter : TreeFilter.ALL; + } + + /** @return the current path filter. */ + public TreeFilter getPathFilter() { + return pathFilter; + } + /** * Flush the underlying output stream of this formatter. * @@ -230,6 +322,208 @@ public class DiffFormatter { out.flush(); } + /** Release the internal ObjectReader state. */ + public void release() { + if (reader != null) + reader.release(); + } + + /** + * Determine the differences between two trees. + * + * No output is created, instead only the file paths that are different are + * returned. Callers may choose to format these paths themselves, or convert + * them into {@link FileHeader} instances with a complete edit list by + * calling {@link #toFileHeader(DiffEntry)}. + * + * @param a + * the old (or previous) side. + * @param b + * the new (or updated) side. + * @return the paths that are different. + * @throws IOException + * trees cannot be read or file contents cannot be read. + */ + public List scan(AnyObjectId a, AnyObjectId b) + throws IOException { + assertHaveRepository(); + + RevWalk rw = new RevWalk(reader); + return scan(rw.parseTree(a), rw.parseTree(b)); + } + + /** + * Determine the differences between two trees. + * + * No output is created, instead only the file paths that are different are + * returned. Callers may choose to format these paths themselves, or convert + * them into {@link FileHeader} instances with a complete edit list by + * calling {@link #toFileHeader(DiffEntry)}. + * + * @param a + * the old (or previous) side. + * @param b + * the new (or updated) side. + * @return the paths that are different. + * @throws IOException + * trees cannot be read or file contents cannot be read. + */ + public List scan(RevTree a, RevTree b) throws IOException { + assertHaveRepository(); + + CanonicalTreeParser aParser = new CanonicalTreeParser(); + CanonicalTreeParser bParser = new CanonicalTreeParser(); + + aParser.reset(reader, a); + bParser.reset(reader, b); + + return scan(aParser, bParser); + } + + /** + * Determine the differences between two trees. + * + * No output is created, instead only the file paths that are different are + * returned. Callers may choose to format these paths themselves, or convert + * them into {@link FileHeader} instances with a complete edit list by + * calling {@link #toFileHeader(DiffEntry)}. + * + * @param a + * the old (or previous) side. + * @param b + * the new (or updated) side. + * @return the paths that are different. + * @throws IOException + * trees cannot be read or file contents cannot be read. + */ + public List scan(AbstractTreeIterator a, AbstractTreeIterator b) + throws IOException { + assertHaveRepository(); + + TreeWalk walk = new TreeWalk(reader); + walk.reset(); + walk.addTree(a); + walk.addTree(b); + walk.setRecursive(true); + + if (pathFilter == TreeFilter.ALL) { + walk.setFilter(TreeFilter.ANY_DIFF); + } else if (pathFilter instanceof FollowFilter) { + walk.setFilter(pathFilter); + } else { + walk.setFilter(AndTreeFilter + .create(pathFilter, TreeFilter.ANY_DIFF)); + } + + List files = DiffEntry.scan(walk); + if (pathFilter instanceof FollowFilter && isAdd(files)) { + // The file we are following was added here, find where it + // came from so we can properly show the rename or copy, + // then continue digging backwards. + // + a.reset(); + b.reset(); + walk.reset(); + walk.addTree(a); + walk.addTree(b); + walk.setFilter(TreeFilter.ANY_DIFF); + + if (renameDetector == null) + setDetectRenames(true); + files = updateFollowFilter(detectRenames(DiffEntry.scan(walk))); + + } else if (renameDetector != null) + files = detectRenames(files); + + return files; + } + + private List detectRenames(List files) + throws IOException { + renameDetector.reset(); + renameDetector.addAll(files); + return renameDetector.compute(reader, progressMonitor); + } + + private boolean isAdd(List files) { + String oldPath = ((FollowFilter) pathFilter).getPath(); + for (DiffEntry ent : files) { + if (ent.getChangeType() == ADD && ent.getNewPath().equals(oldPath)) + return true; + } + return false; + } + + private List updateFollowFilter(List files) { + String oldPath = ((FollowFilter) pathFilter).getPath(); + for (DiffEntry ent : files) { + if (isRename(ent) && ent.getNewPath().equals(oldPath)) { + pathFilter = FollowFilter.create(ent.getOldPath()); + return Collections.singletonList(ent); + } + } + return Collections.emptyList(); + } + + private static boolean isRename(DiffEntry ent) { + return ent.getChangeType() == RENAME || ent.getChangeType() == COPY; + } + + /** + * Format the differences between two trees. + * + * The patch is expressed as instructions to modify {@code a} to make it + * {@code b}. + * + * @param a + * the old (or previous) side. + * @param b + * the new (or updated) side. + * @throws IOException + * trees cannot be read, file contents cannot be read, or the + * patch cannot be output. + */ + public void format(AnyObjectId a, AnyObjectId b) throws IOException { + format(scan(a, b)); + } + + /** + * Format the differences between two trees. + * + * The patch is expressed as instructions to modify {@code a} to make it + * {@code b}. + * + * @param a + * the old (or previous) side. + * @param b + * the new (or updated) side. + * @throws IOException + * trees cannot be read, file contents cannot be read, or the + * patch cannot be output. + */ + public void format(RevTree a, RevTree b) throws IOException { + format(scan(a, b)); + } + + /** + * Format the differences between two trees. + * + * The patch is expressed as instructions to modify {@code a} to make it + * {@code b}. + * + * @param a + * the old (or previous) side. + * @param b + * the new (or updated) side. + * @throws IOException + * trees cannot be read, file contents cannot be read, or the + * patch cannot be output. + */ + public void format(AbstractTreeIterator a, AbstractTreeIterator b) + throws IOException { + format(scan(a, b)); + } + /** * Format a patch script from a list of difference entries. * @@ -272,13 +566,10 @@ public class DiffFormatter { private String format(AbbreviatedObjectId id) { if (id.isComplete() && db != null) { - ObjectReader reader = db.newObjectReader(); try { id = reader.abbreviate(id.toObjectId(), abbreviationLength); } catch (IOException cannotAbbreviate) { // Ignore this. We'll report the full identity. - } finally { - reader.release(); } } return id.name(); @@ -319,22 +610,22 @@ public class DiffFormatter { end = head.getHunks().get(0).getStartOffset(); out.write(head.getBuffer(), start, end - start); if (head.getPatchType() == PatchType.UNIFIED) - formatEdits(a, b, head.toEditList()); + format(head.toEditList(), a, b); } /** * Formats a list of edits in unified diff format * + * @param edits + * some differences which have been calculated between A and B * @param a * the text A which was compared * @param b * the text B which was compared - * @param edits - * some differences which have been calculated between A and B * @throws IOException */ - public void formatEdits(final RawText a, final RawText b, - final EditList edits) throws IOException { + public void format(final EditList edits, final RawText a, final RawText b) + throws IOException { for (int curIdx = 0; curIdx < edits.size();) { Edit curEdit = edits.get(curIdx); final int endIdx = findCombinedEnd(edits, curIdx); @@ -513,7 +804,7 @@ public class DiffFormatter { * @throws MissingObjectException * one of the blobs referenced by the DiffEntry is missing. */ - public FileHeader createFileHeader(DiffEntry ent) throws IOException, + public FileHeader toFileHeader(DiffEntry ent) throws IOException, CorruptObjectException, MissingObjectException { return createFormatResult(ent).header; } @@ -542,24 +833,14 @@ public class DiffFormatter { type = PatchType.UNIFIED; } else { - if (db == null) - throw new IllegalStateException( - JGitText.get().repositoryIsRequired); + assertHaveRepository(); - ObjectReader reader = db.newObjectReader(); - byte[] aRaw, bRaw; - try { - aRaw = open(reader, // - ent.getOldPath(), // - ent.getOldMode(), // - ent.getOldId()); - bRaw = open(reader, // - ent.getNewPath(), // - ent.getNewMode(), // - ent.getNewId()); - } finally { - reader.release(); - } + byte[] aRaw = open(ent.getOldPath(), // + ent.getOldMode(), // + ent.getOldId()); + byte[] bRaw = open(ent.getNewPath(), // + ent.getNewMode(), // + ent.getNewId()); if (aRaw == BINARY || bRaw == BINARY // || RawText.isBinary(aRaw) || RawText.isBinary(bRaw)) { @@ -592,8 +873,13 @@ public class DiffFormatter { return res; } - private byte[] open(ObjectReader reader, String path, FileMode mode, - AbbreviatedObjectId id) throws IOException { + private void assertHaveRepository() { + if (db == null) + throw new IllegalStateException(JGitText.get().repositoryIsRequired); + } + + private byte[] open(String path, FileMode mode, AbbreviatedObjectId id) + throws IOException { if (mode == FileMode.MISSING) return EMPTY; 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 9c1310ab8..bd4a5e238 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java @@ -100,11 +100,11 @@ public class RenameDetector { } }; - private List entries = new ArrayList(); + private List entries; - private List deleted = new ArrayList(); + private List deleted; - private List added = new ArrayList(); + private List added; private boolean done; @@ -137,6 +137,8 @@ public class RenameDetector { DiffConfig cfg = repo.getConfig().get(DiffConfig.KEY); renameLimit = cfg.getRenameLimit(); + + reset(); } /** @@ -304,20 +306,40 @@ public class RenameDetector { * file contents cannot be read from the repository. */ public List compute(ProgressMonitor pm) throws IOException { + if (!done) { + ObjectReader reader = repo.newObjectReader(); + try { + return compute(reader, pm); + } finally { + reader.release(); + } + } + return Collections.unmodifiableList(entries); + } + + /** + * Detect renames in the current file set. + * + * @param reader + * reader to obtain objects from the repository with. + * @param pm + * report progress during the detection phases. + * @return an unmodifiable list of {@link DiffEntry}s representing all files + * that have been changed. + * @throws IOException + * file contents cannot be read from the repository. + */ + public List compute(ObjectReader reader, ProgressMonitor pm) + throws IOException { if (!done) { done = true; if (pm == null) pm = NullProgressMonitor.INSTANCE; - ObjectReader reader = repo.newObjectReader(); - try { breakModifies(reader, pm); findExactRenames(pm); findContentRenames(reader, pm); rejoinModifies(pm); - } finally { - reader.release(); - } entries.addAll(added); added = null; @@ -330,6 +352,14 @@ public class RenameDetector { return Collections.unmodifiableList(entries); } + /** Reset this rename detector for another rename detection pass. */ + public void reset() { + entries = new ArrayList(); + deleted = new ArrayList(); + added = new ArrayList(); + done = false; + } + private void breakModifies(ObjectReader reader, ProgressMonitor pm) throws IOException { if (breakScore <= 0)