From bd0fdfe97b66159e08ca0c4eb92fc086ce49ce41 Mon Sep 17 00:00:00 2001 From: James Moger Date: Sat, 25 Jun 2011 16:57:03 -0400 Subject: [PATCH] Updated to JGit 1.0.0. --- .classpath | 8 +- src/com/gitblit/Build.java | 16 +- src/com/gitblit/Constants.java | 2 +- src/org/eclipse/jgit/api/BlameCommand.java | 227 ----- .../eclipse/jgit/blame/BlameGenerator.java | 961 ------------------ src/org/eclipse/jgit/blame/BlameResult.java | 356 ------- src/org/eclipse/jgit/blame/Candidate.java | 386 ------- src/org/eclipse/jgit/blame/Region.java | 133 --- src/org/eclipse/jgit/blame/ReverseWalk.java | 113 -- 9 files changed, 13 insertions(+), 2189 deletions(-) delete mode 100644 src/org/eclipse/jgit/api/BlameCommand.java delete mode 100644 src/org/eclipse/jgit/blame/BlameGenerator.java delete mode 100644 src/org/eclipse/jgit/blame/BlameResult.java delete mode 100644 src/org/eclipse/jgit/blame/Candidate.java delete mode 100644 src/org/eclipse/jgit/blame/Region.java delete mode 100644 src/org/eclipse/jgit/blame/ReverseWalk.java diff --git a/.classpath b/.classpath index 73cfac42..93f1a9b8 100644 --- a/.classpath +++ b/.classpath @@ -81,14 +81,14 @@ - + - + - + - + diff --git a/src/com/gitblit/Build.java b/src/com/gitblit/Build.java index b6c485a3..be228af7 100644 --- a/src/com/gitblit/Build.java +++ b/src/com/gitblit/Build.java @@ -385,16 +385,16 @@ public class Build { "3b7c5f3938f202311bdca0bf7ed46bc0118af081"); public static final MavenObject JGIT = new MavenObject("JGit", "org/eclipse/jgit", - "org.eclipse.jgit", "0.12.1", 1318000, 1354000, 2993000, - "fd77699699b9651d2fc31c7ed63af98b14fc1975", - "c8b3d84922c7802cfe6a661e13a002641a78583d", - "5609aa3ce3ac3d52030befd27ddd2941f6c07570"); + "org.eclipse.jgit", "1.0.0.201106090707-r", 1318000, 1354000, 2993000, + "34e70691382d67ee5c84ef207fb8d3784594ba2c", + "78dbd385cf40cb266f4fb2de8651b288a72f4e2d", + "dab55685bb6eee8d07cc87faf0cedaa3f9d04a0d"); public static final MavenObject JGIT_HTTP = new MavenObject("JGit", "org/eclipse/jgit", - "org.eclipse.jgit.http.server", "0.12.1", 68000, 62000, 99000, - "384058ca906dffb8b8708c2db8849c9754359b28", - "7b026658ed8de2eccc2d11d647d43d7c84a56911", - "6c19e37b3caafd70c1b7b024ae1858c725181688"); + "org.eclipse.jgit.http.server", "1.0.0.201106090707-r", 68000, 62000, 99000, + "35e22f7000af95d0c90caaf2012071ef3734ff59", + "4a2368beb1e9db4a6a0d609b7b869f218bf8e7a9", + "3100ce7c40d6968481a12377c59c708cda2d17b5"); public static final MavenObject JSCH = new MavenObject("JSch", "com/jcraft", "jsch", "0.1.44-1", 214000, 211000, 413000, "2e9ae08de5a71bd0e0d3ba2558598181bfa71d4e", diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index d410d35f..4375e422 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -27,7 +27,7 @@ public class Constants { // The build script extracts this exact line so be careful editing it // and only use A-Z a-z 0-9 .-_ in the string. - public static final String JGIT_VERSION = "JGit 0.12.1"; + public static final String JGIT_VERSION = "JGit 1.0.0"; public static final String ADMIN_ROLE = "#admin"; diff --git a/src/org/eclipse/jgit/api/BlameCommand.java b/src/org/eclipse/jgit/api/BlameCommand.java deleted file mode 100644 index 400d94bc..00000000 --- a/src/org/eclipse/jgit/api/BlameCommand.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) 2011, GitHub Inc. - * and other copyright owners as documented in the project's IP log. - * - * This program and the accompanying materials are made available - * under the terms of the Eclipse Distribution License v1.0 which - * accompanies this distribution, is reproduced below, and is - * available at http://www.eclipse.org/org/documents/edl-v10.php - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * - Neither the name of the Eclipse Foundation, Inc. nor the - * names of its contributors may be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.eclipse.jgit.api; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; - -import org.eclipse.jgit.api.errors.JGitInternalException; -import org.eclipse.jgit.blame.BlameGenerator; -import org.eclipse.jgit.blame.BlameResult; -import org.eclipse.jgit.diff.DiffAlgorithm; -import org.eclipse.jgit.diff.RawText; -import org.eclipse.jgit.diff.RawTextComparator; -import org.eclipse.jgit.dircache.DirCache; -import org.eclipse.jgit.lib.AnyObjectId; -import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.Repository; - -/** - * Blame command for building a {@link BlameResult} for a file path. - */ -public class BlameCommand extends GitCommand { - - private String path; - - private DiffAlgorithm diffAlgorithm; - - private RawTextComparator textComparator; - - private ObjectId startCommit; - - private Collection reverseEndCommits; - - private Boolean followFileRenames; - - /** - * @param repo - */ - public BlameCommand(Repository repo) { - super(repo); - } - - /** - * Set file path - * - * @param filePath - * @return this command - */ - public BlameCommand setFilePath(String filePath) { - this.path = filePath; - return this; - } - - /** - * Set diff algorithm - * - * @param diffAlgorithm - * @return this command - */ - public BlameCommand setDiffAlgorithm(DiffAlgorithm diffAlgorithm) { - this.diffAlgorithm = diffAlgorithm; - return this; - } - - /** - * Set raw text comparator - * - * @param textComparator - * @return this command - */ - public BlameCommand setTextComparator(RawTextComparator textComparator) { - this.textComparator = textComparator; - return this; - } - - /** - * Set start commit id - * - * @param commit - * @return this command - */ - public BlameCommand setStartCommit(AnyObjectId commit) { - this.startCommit = commit.toObjectId(); - return this; - } - - /** - * Enable (or disable) following file renames. - *

- * If true renames are followed using the standard FollowFilter behavior - * used by RevWalk (which matches {@code git log --follow} in the C - * implementation). This is not the same as copy/move detection as - * implemented by the C implementation's of {@code git blame -M -C}. - * - * @param follow - * enable following. - * @return {@code this} - */ - public BlameCommand setFollowFileRenames(boolean follow) { - followFileRenames = Boolean.valueOf(follow); - return this; - } - - /** - * Configure the command to compute reverse blame (history of deletes). - * - * @param start - * oldest commit to traverse from. The result file will be loaded - * from this commit's tree. - * @param end - * most recent commit to stop traversal at. Usually an active - * branch tip, tag, or HEAD. - * @return {@code this} - * @throws IOException - * the repository cannot be read. - */ - public BlameCommand reverse(AnyObjectId start, AnyObjectId end) - throws IOException { - return reverse(start, Collections.singleton(end.toObjectId())); - } - - /** - * Configure the generator to compute reverse blame (history of deletes). - * - * @param start - * oldest commit to traverse from. The result file will be loaded - * from this commit's tree. - * @param end - * most recent commits to stop traversal at. Usually an active - * branch tip, tag, or HEAD. - * @return {@code this} - * @throws IOException - * the repository cannot be read. - */ - public BlameCommand reverse(AnyObjectId start, Collection end) - throws IOException { - startCommit = start.toObjectId(); - reverseEndCommits = new ArrayList(end); - return this; - } - - /** - * Generate a list of lines with information about when the lines were - * introduced into the file path. - * - * @return list of lines - */ - public BlameResult call() throws JGitInternalException { - checkCallable(); - BlameGenerator gen = new BlameGenerator(repo, path); - try { - if (diffAlgorithm != null) - gen.setDiffAlgorithm(diffAlgorithm); - if (textComparator != null) - gen.setTextComparator(textComparator); - if (followFileRenames != null) - gen.setFollowFileRenames(followFileRenames.booleanValue()); - - if (reverseEndCommits != null) - gen.reverse(startCommit, reverseEndCommits); - else if (startCommit != null) - gen.push(null, startCommit); - else { - gen.push(null, repo.resolve(Constants.HEAD)); - if (!repo.isBare()) { - DirCache dc = repo.readDirCache(); - int entry = dc.findEntry(path); - if (0 <= entry) - gen.push(null, dc.getEntry(entry).getObjectId()); - - File inTree = new File(repo.getWorkTree(), path); - if (inTree.isFile()) - gen.push(null, new RawText(inTree)); - } - } - return gen.computeBlameResult(); - } catch (IOException e) { - throw new JGitInternalException(e.getMessage(), e); - } finally { - gen.release(); - } - } -} diff --git a/src/org/eclipse/jgit/blame/BlameGenerator.java b/src/org/eclipse/jgit/blame/BlameGenerator.java deleted file mode 100644 index 286f4c1f..00000000 --- a/src/org/eclipse/jgit/blame/BlameGenerator.java +++ /dev/null @@ -1,961 +0,0 @@ -/* - * Copyright (C) 2011, Google Inc. - * and other copyright owners as documented in the project's IP log. - * - * This program and the accompanying materials are made available - * under the terms of the Eclipse Distribution License v1.0 which - * accompanies this distribution, is reproduced below, and is - * available at http://www.eclipse.org/org/documents/edl-v10.php - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * - Neither the name of the Eclipse Foundation, Inc. nor the - * names of its contributors may be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.eclipse.jgit.blame; - -import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; - -import java.io.IOException; -import java.util.Collection; -import java.util.Collections; - -import org.eclipse.jgit.blame.Candidate.BlobCandidate; -import org.eclipse.jgit.blame.Candidate.ReverseCandidate; -import org.eclipse.jgit.blame.ReverseWalk.ReverseCommit; -import org.eclipse.jgit.diff.DiffAlgorithm; -import org.eclipse.jgit.diff.DiffEntry; -import org.eclipse.jgit.diff.DiffEntry.ChangeType; -import org.eclipse.jgit.diff.EditList; -import org.eclipse.jgit.diff.HistogramDiff; -import org.eclipse.jgit.diff.RawText; -import org.eclipse.jgit.diff.RawTextComparator; -import org.eclipse.jgit.diff.RenameDetector; -import org.eclipse.jgit.lib.AnyObjectId; -import org.eclipse.jgit.lib.MutableObjectId; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.ObjectLoader; -import org.eclipse.jgit.lib.ObjectReader; -import org.eclipse.jgit.lib.PersonIdent; -import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.revwalk.RevFlag; -import org.eclipse.jgit.revwalk.RevWalk; -import org.eclipse.jgit.treewalk.TreeWalk; -import org.eclipse.jgit.treewalk.filter.PathFilter; -import org.eclipse.jgit.treewalk.filter.TreeFilter; - -/** - * Generate author information for lines based on introduction to the file. - *

- * Applications that want a simple one-shot computation of blame for a file - * should use {@link #computeBlameResult()} to prepare the entire result in one - * method call. This may block for significant time as the history of the - * repository must be traversed until information is gathered for every line. - *

- * Applications that want more incremental update behavior may use either the - * raw {@link #next()} streaming approach supported by this class, or construct - * a {@link BlameResult} using {@link BlameResult#create(BlameGenerator)} and - * incrementally construct the result with {@link BlameResult#computeNext()}. - *

- * This class is not thread-safe. - *

- * An instance of BlameGenerator can only be used once. To blame multiple files - * the application must create a new BlameGenerator. - *

- * During blame processing there are two files involved: - *

    - *
  • result - The file whose lines are being examined. This is the revision - * the user is trying to view blame/annotation information alongside of.
  • - *
  • source - The file that was blamed with supplying one or more lines of - * data into result. The source may be a different file path (due to copy or - * rename). Source line numbers may differ from result line numbers due to lines - * being added/removed in intermediate revisions.
  • - *
- *

- * The blame algorithm is implemented by initially assigning responsibility for - * all lines of the result to the starting commit. A difference against the - * commit's ancestor is computed, and responsibility is passed to the ancestor - * commit for any lines that are common. The starting commit is blamed only for - * the lines that do not appear in the ancestor, if any. The loop repeats using - * the ancestor, until there are no more lines to acquire information on, or the - * file's creation point is discovered in history. - */ -public class BlameGenerator { - private final Repository repository; - - private final PathFilter resultPath; - - private final MutableObjectId idBuf; - - /** Revision pool used to acquire commits from. */ - private RevWalk revPool; - - /** Indicates the commit has already been processed. */ - private RevFlag SEEN; - - private ObjectReader reader; - - private TreeWalk treeWalk; - - private DiffAlgorithm diffAlgorithm = new HistogramDiff(); - - private RawTextComparator textComparator = RawTextComparator.DEFAULT; - - private RenameDetector renameDetector; - - /** Potential candidates, sorted by commit time descending. */ - private Candidate queue; - - /** Number of lines that still need to be discovered. */ - private int remaining; - - /** Blame is currently assigned to this source. */ - private Candidate currentSource; - - /** - * Create a blame generator for the repository and path - * - * @param repository - * repository to access revision data from. - * @param path - * initial path of the file to start scanning. - */ - public BlameGenerator(Repository repository, String path) { - this.repository = repository; - this.resultPath = PathFilter.create(path); - - idBuf = new MutableObjectId(); - setFollowFileRenames(true); - initRevPool(false); - - remaining = -1; - } - - private void initRevPool(boolean reverse) { - if (queue != null) - throw new IllegalStateException(); - - if (revPool != null) - revPool.release(); - - if (reverse) - revPool = new ReverseWalk(getRepository()); - else - revPool = new RevWalk(getRepository()); - - revPool.setRetainBody(true); - SEEN = revPool.newFlag("SEEN"); - reader = revPool.getObjectReader(); - treeWalk = new TreeWalk(reader); - } - - /** @return repository being scanned for revision history. */ - public Repository getRepository() { - return repository; - } - - /** @return path file path being processed. */ - public String getResultPath() { - return resultPath.getPath(); - } - - /** - * Difference algorithm to use when comparing revisions. - * - * @param algorithm - * @return {@code this} - */ - public BlameGenerator setDiffAlgorithm(DiffAlgorithm algorithm) { - diffAlgorithm = algorithm; - return this; - } - - /** - * Text comparator to use when comparing revisions. - * - * @param comparator - * @return {@code this} - */ - public BlameGenerator setTextComparator(RawTextComparator comparator) { - textComparator = comparator; - return this; - } - - /** - * Enable (or disable) following file renames, on by default. - *

- * If true renames are followed using the standard FollowFilter behavior - * used by RevWalk (which matches {@code git log --follow} in the C - * implementation). This is not the same as copy/move detection as - * implemented by the C implementation's of {@code git blame -M -C}. - * - * @param follow - * enable following. - * @return {@code this} - */ - public BlameGenerator setFollowFileRenames(boolean follow) { - if (follow) - renameDetector = new RenameDetector(getRepository()); - else - renameDetector = null; - return this; - } - - /** - * Obtain the RenameDetector if {@code setFollowFileRenames(true)}. - * - * @return the rename detector, allowing the application to configure its - * settings for rename score and breaking behavior. - */ - public RenameDetector getRenameDetector() { - return renameDetector; - } - - /** - * Push a candidate blob onto the generator's traversal stack. - *

- * Candidates should be pushed in history order from oldest-to-newest. - * Applications should push the starting commit first, then the index - * revision (if the index is interesting), and finally the working tree - * copy (if the working tree is interesting). - * - * @param description - * description of the blob revision, such as "Working Tree". - * @param contents - * contents of the file. - * @return {@code this} - * @throws IOException - * the repository cannot be read. - */ - public BlameGenerator push(String description, byte[] contents) - throws IOException { - return push(description, new RawText(contents)); - } - - /** - * Push a candidate blob onto the generator's traversal stack. - *

- * Candidates should be pushed in history order from oldest-to-newest. - * Applications should push the starting commit first, then the index - * revision (if the index is interesting), and finally the working tree copy - * (if the working tree is interesting). - * - * @param description - * description of the blob revision, such as "Working Tree". - * @param contents - * contents of the file. - * @return {@code this} - * @throws IOException - * the repository cannot be read. - */ - public BlameGenerator push(String description, RawText contents) - throws IOException { - if (description == null) - // XXX description = JGitText.get().blameNotCommittedYet; - description = "blame not committed yet"; - BlobCandidate c = new BlobCandidate(description, resultPath); - c.sourceText = contents; - c.regionList = new Region(0, 0, contents.size()); - remaining = contents.size(); - push(c); - return this; - } - - /** - * Push a candidate object onto the generator's traversal stack. - *

- * Candidates should be pushed in history order from oldest-to-newest. - * Applications should push the starting commit first, then the index - * revision (if the index is interesting), and finally the working tree copy - * (if the working tree is interesting). - * - * @param description - * description of the blob revision, such as "Working Tree". - * @param id - * may be a commit or a blob. - * @return {@code this} - * @throws IOException - * the repository cannot be read. - */ - public BlameGenerator push(String description, AnyObjectId id) - throws IOException { - ObjectLoader ldr = reader.open(id); - if (ldr.getType() == OBJ_BLOB) { - if (description == null) - // XXX description = JGitText.get().blameNotCommittedYet; - description = "blame not committed yet"; - BlobCandidate c = new BlobCandidate(description, resultPath); - c.sourceBlob = id.toObjectId(); - c.sourceText = new RawText(ldr.getCachedBytes(Integer.MAX_VALUE)); - c.regionList = new Region(0, 0, c.sourceText.size()); - remaining = c.sourceText.size(); - push(c); - return this; - } - - RevCommit commit = revPool.parseCommit(id); - if (!find(commit, resultPath)) - return this; - - Candidate c = new Candidate(commit, resultPath); - c.sourceBlob = idBuf.toObjectId(); - c.loadText(reader); - c.regionList = new Region(0, 0, c.sourceText.size()); - remaining = c.sourceText.size(); - push(c); - return this; - } - - /** - * Configure the generator to compute reverse blame (history of deletes). - *

- * This method is expensive as it immediately runs a RevWalk over the - * history spanning the expression {@code start..end} (end being more recent - * than start) and then performs the equivalent operation as - * {@link #push(String, AnyObjectId)} to begin blame traversal from the - * commit named by {@code start} walking forwards through history until - * {@code end} blaming line deletions. - *

- * A reverse blame may produce multiple sources for the same result line, - * each of these is a descendant commit that removed the line, typically - * this occurs when the same deletion appears in multiple side branches such - * as due to a cherry-pick. Applications relying on reverse should use - * {@link BlameResult} as it filters these duplicate sources and only - * remembers the first (oldest) deletion. - * - * @param start - * oldest commit to traverse from. The result file will be loaded - * from this commit's tree. - * @param end - * most recent commit to stop traversal at. Usually an active - * branch tip, tag, or HEAD. - * @return {@code this} - * @throws IOException - * the repository cannot be read. - */ - public BlameGenerator reverse(AnyObjectId start, AnyObjectId end) - throws IOException { - return reverse(start, Collections.singleton(end.toObjectId())); - } - - /** - * Configure the generator to compute reverse blame (history of deletes). - *

- * This method is expensive as it immediately runs a RevWalk over the - * history spanning the expression {@code start..end} (end being more recent - * than start) and then performs the equivalent operation as - * {@link #push(String, AnyObjectId)} to begin blame traversal from the - * commit named by {@code start} walking forwards through history until - * {@code end} blaming line deletions. - *

- * A reverse blame may produce multiple sources for the same result line, - * each of these is a descendant commit that removed the line, typically - * this occurs when the same deletion appears in multiple side branches such - * as due to a cherry-pick. Applications relying on reverse should use - * {@link BlameResult} as it filters these duplicate sources and only - * remembers the first (oldest) deletion. - * - * @param start - * oldest commit to traverse from. The result file will be loaded - * from this commit's tree. - * @param end - * most recent commits to stop traversal at. Usually an active - * branch tip, tag, or HEAD. - * @return {@code this} - * @throws IOException - * the repository cannot be read. - */ - public BlameGenerator reverse(AnyObjectId start, - Collection end) throws IOException { - initRevPool(true); - - ReverseCommit result = (ReverseCommit) revPool.parseCommit(start); - if (!find(result, resultPath)) - return this; - - revPool.markUninteresting(result); - for (ObjectId id : end) - revPool.markStart(revPool.parseCommit(id)); - - while (revPool.next() != null) { - // just pump the queue - } - - ReverseCandidate c = new ReverseCandidate(result, resultPath); - c.sourceBlob = idBuf.toObjectId(); - c.loadText(reader); - c.regionList = new Region(0, 0, c.sourceText.size()); - remaining = c.sourceText.size(); - push(c); - return this; - } - - /** - * Execute the generator in a blocking fashion until all data is ready. - * - * @return the complete result. Null if no file exists for the given path. - * @throws IOException - * the repository cannot be read. - */ - public BlameResult computeBlameResult() throws IOException { - try { - BlameResult r = BlameResult.create(this); - if (r != null) - r.computeAll(); - return r; - } finally { - release(); - } - } - - /** - * Step the blame algorithm one iteration. - * - * @return true if the generator has found a region's source. The getSource* - * and {@link #getResultStart()}, {@link #getResultEnd()} methods - * can be used to inspect the region found. False if there are no - * more regions to describe. - * @throws IOException - * repository cannot be read. - */ - public boolean next() throws IOException { - // If there is a source still pending, produce the next region. - if (currentSource != null) { - Region r = currentSource.regionList; - Region n = r.next; - remaining -= r.length; - if (n != null) { - currentSource.regionList = n; - return true; - } - - if (currentSource.queueNext != null) - return result(currentSource.queueNext); - - currentSource = null; - } - - // If there are no lines remaining, the entire result is done, - // even if there are revisions still available for the path. - if (remaining == 0) - return done(); - - for (;;) { - Candidate n = pop(); - if (n == null) - return done(); - - int pCnt = n.getParentCount(); - if (pCnt == 1) { - if (processOne(n)) - return true; - - } else if (1 < pCnt) { - if (processMerge(n)) - return true; - - } else if (n instanceof ReverseCandidate) { - // Do not generate a tip of a reverse. The region - // survives and should not appear to be deleted. - - } else /* if (pCnt == 0) */{ - // Root commit, with at least one surviving region. - // Assign the remaining blame here. - return result(n); - } - } - } - - private boolean done() { - release(); - return false; - } - - private boolean result(Candidate n) throws IOException { - if (n.sourceCommit != null) - revPool.parseBody(n.sourceCommit); - currentSource = n; - return true; - } - - private boolean reverseResult(Candidate parent, Candidate source) - throws IOException { - // On a reverse blame present the application the parent - // (as this is what did the removals), however the region - // list to enumerate is the source's surviving list. - Candidate res = parent.copy(parent.sourceCommit); - res.regionList = source.regionList; - return result(res); - } - - private Candidate pop() { - Candidate n = queue; - if (n != null) { - queue = n.queueNext; - n.queueNext = null; - } - return n; - } - - private void push(BlobCandidate toInsert) { - Candidate c = queue; - if (c != null) { - c.regionList = null; - toInsert.parent = c; - } - queue = toInsert; - } - - private void push(Candidate toInsert) { - // Mark sources to ensure they get discarded (above) if - // another path to the same commit. - toInsert.add(SEEN); - - // Insert into the queue using descending commit time, so - // the most recent commit will pop next. - int time = toInsert.getTime(); - Candidate n = queue; - if (n == null || time >= n.getTime()) { - toInsert.queueNext = n; - queue = toInsert; - return; - } - - for (Candidate p = n;; p = n) { - n = p.queueNext; - if (n == null || time >= n.getTime()) { - toInsert.queueNext = n; - p.queueNext = toInsert; - return; - } - } - } - - private boolean processOne(Candidate n) throws IOException { - RevCommit parent = n.getParent(0); - if (parent == null) - return split(n.getNextCandidate(0), n); - if (parent.has(SEEN)) - return false; - revPool.parseHeaders(parent); - - if (find(parent, n.sourcePath)) { - if (idBuf.equals(n.sourceBlob)) { - // The common case of the file not being modified in - // a simple string-of-pearls history. Blame parent. - n.sourceCommit = parent; - push(n); - return false; - } - - Candidate next = n.create(parent, n.sourcePath); - next.sourceBlob = idBuf.toObjectId(); - next.loadText(reader); - return split(next, n); - } - - if (n.sourceCommit == null) - return result(n); - - DiffEntry r = findRename(parent, n.sourceCommit, n.sourcePath); - if (r == null) - return result(n); - - if (0 == r.getOldId().prefixCompare(n.sourceBlob)) { - // A 100% rename without any content change can also - // skip directly to the parent. - n.sourceCommit = parent; - n.sourcePath = PathFilter.create(r.getOldPath()); - push(n); - return false; - } - - Candidate next = n.create(parent, PathFilter.create(r.getOldPath())); - next.sourceBlob = r.getOldId().toObjectId(); - next.renameScore = r.getScore(); - next.loadText(reader); - return split(next, n); - } - - private boolean split(Candidate parent, Candidate source) - throws IOException { - EditList editList = diffAlgorithm.diff(textComparator, - parent.sourceText, source.sourceText); - if (editList.isEmpty()) { - // Ignoring whitespace (or some other special comparator) can - // cause non-identical blobs to have an empty edit list. In - // a case like this push the parent alone. - parent.regionList = source.regionList; - push(parent); - return false; - } - - parent.takeBlame(editList, source); - if (parent.regionList != null) - push(parent); - if (source.regionList != null) { - if (source instanceof ReverseCandidate) - return reverseResult(parent, source); - return result(source); - } - return false; - } - - private boolean processMerge(Candidate n) throws IOException { - int pCnt = n.getParentCount(); - - for (int pIdx = 0; pIdx < pCnt; pIdx++) { - RevCommit parent = n.getParent(pIdx); - if (parent.has(SEEN)) - continue; - revPool.parseHeaders(parent); - } - - // If any single parent exactly matches the merge, follow only - // that one parent through history. - ObjectId[] ids = null; - for (int pIdx = 0; pIdx < pCnt; pIdx++) { - RevCommit parent = n.getParent(pIdx); - if (parent.has(SEEN)) - continue; - if (!find(parent, n.sourcePath)) - continue; - if (!(n instanceof ReverseCandidate) && idBuf.equals(n.sourceBlob)) { - n.sourceCommit = parent; - push(n); - return false; - } - if (ids == null) - ids = new ObjectId[pCnt]; - ids[pIdx] = idBuf.toObjectId(); - } - - // If rename detection is enabled, search for any relevant names. - DiffEntry[] renames = null; - if (renameDetector != null) { - renames = new DiffEntry[pCnt]; - for (int pIdx = 0; pIdx < pCnt; pIdx++) { - RevCommit parent = n.getParent(pIdx); - if (parent.has(SEEN)) - continue; - if (ids != null && ids[pIdx] != null) - continue; - - DiffEntry r = findRename(parent, n.sourceCommit, n.sourcePath); - if (r == null) - continue; - - if (n instanceof ReverseCandidate) { - if (ids == null) - ids = new ObjectId[pCnt]; - ids[pCnt] = r.getOldId().toObjectId(); - } else if (0 == r.getOldId().prefixCompare(n.sourceBlob)) { - // A 100% rename without any content change can also - // skip directly to the parent. Note this bypasses an - // earlier parent that had the path (above) but did not - // have an exact content match. For performance reasons - // we choose to follow the one parent over trying to do - // possibly both parents. - n.sourceCommit = parent; - n.sourcePath = PathFilter.create(r.getOldPath()); - push(n); - return false; - } - - renames[pIdx] = r; - } - } - - // Construct the candidate for each parent. - Candidate[] parents = new Candidate[pCnt]; - for (int pIdx = 0; pIdx < pCnt; pIdx++) { - RevCommit parent = n.getParent(pIdx); - if (parent.has(SEEN)) - continue; - - Candidate p; - if (renames != null && renames[pIdx] != null) { - p = n.create(parent, - PathFilter.create(renames[pIdx].getOldPath())); - p.renameScore = renames[pIdx].getScore(); - p.sourceBlob = renames[pIdx].getOldId().toObjectId(); - } else if (ids != null && ids[pIdx] != null) { - p = n.create(parent, n.sourcePath); - p.sourceBlob = ids[pIdx]; - } else { - continue; - } - - EditList editList; - if (n instanceof ReverseCandidate - && p.sourceBlob.equals(n.sourceBlob)) { - // This special case happens on ReverseCandidate forks. - p.sourceText = n.sourceText; - editList = new EditList(0); - } else { - p.loadText(reader); - editList = diffAlgorithm.diff(textComparator, - p.sourceText, n.sourceText); - } - - if (editList.isEmpty()) { - // Ignoring whitespace (or some other special comparator) can - // cause non-identical blobs to have an empty edit list. In - // a case like this push the parent alone. - if (n instanceof ReverseCandidate) { - parents[pIdx] = p; - continue; - } - - p.regionList = n.regionList; - push(p); - return false; - } - - p.takeBlame(editList, n); - - // Only remember this parent candidate if there is at least - // one region that was blamed on the parent. - if (p.regionList != null) { - // Reverse blame requires inverting the regions. This puts - // the regions the parent deleted from us into the parent, - // and retains the common regions to look at other parents - // for deletions. - if (n instanceof ReverseCandidate) { - Region r = p.regionList; - p.regionList = n.regionList; - n.regionList = r; - } - - parents[pIdx] = p; - } - } - - if (n instanceof ReverseCandidate) { - // On a reverse blame report all deletions found in the children, - // and pass on to them a copy of our region list. - Candidate resultHead = null; - Candidate resultTail = null; - - for (int pIdx = 0; pIdx < pCnt; pIdx++) { - Candidate p = parents[pIdx]; - if (p == null) - continue; - - if (p.regionList != null) { - Candidate r = p.copy(p.sourceCommit); - if (resultTail != null) { - resultTail.queueNext = r; - resultTail = r; - } else { - resultHead = r; - resultTail = r; - } - } - - if (n.regionList != null) { - p.regionList = n.regionList.deepCopy(); - push(p); - } - } - - if (resultHead != null) - return result(resultHead); - return false; - } - - // Push any parents that are still candidates. - for (int pIdx = 0; pIdx < pCnt; pIdx++) { - if (parents[pIdx] != null) - push(parents[pIdx]); - } - - if (n.regionList != null) - return result(n); - return false; - } - - /** - * Get the revision blamed for the current region. - *

- * The source commit may be null if the line was blamed to an uncommitted - * revision, such as the working tree copy, or during a reverse blame if the - * line survives to the end revision (e.g. the branch tip). - * - * @return current revision being blamed. - */ - public RevCommit getSourceCommit() { - return currentSource.sourceCommit; - } - - /** @return current author being blamed. */ - public PersonIdent getSourceAuthor() { - return currentSource.getAuthor(); - } - - /** @return current committer being blamed. */ - public PersonIdent getSourceCommitter() { - RevCommit c = getSourceCommit(); - return c != null ? c.getCommitterIdent() : null; - } - - /** @return path of the file being blamed. */ - public String getSourcePath() { - return currentSource.sourcePath.getPath(); - } - - /** @return rename score if a rename occurred in {@link #getSourceCommit}. */ - public int getRenameScore() { - return currentSource.renameScore; - } - - /** - * @return first line of the source data that has been blamed for the - * current region. This is line number of where the region was added - * during {@link #getSourceCommit()} in file - * {@link #getSourcePath()}. - */ - public int getSourceStart() { - return currentSource.regionList.sourceStart; - } - - /** - * @return one past the range of the source data that has been blamed for - * the current region. This is line number of where the region was - * added during {@link #getSourceCommit()} in file - * {@link #getSourcePath()}. - */ - public int getSourceEnd() { - Region r = currentSource.regionList; - return r.sourceStart + r.length; - } - - /** - * @return first line of the result that {@link #getSourceCommit()} has been - * blamed for providing. Line numbers use 0 based indexing. - */ - public int getResultStart() { - return currentSource.regionList.resultStart; - } - - /** - * @return one past the range of the result that {@link #getSourceCommit()} - * has been blamed for providing. Line numbers use 0 based indexing. - * Because a source cannot be blamed for an empty region of the - * result, {@link #getResultEnd()} is always at least one larger - * than {@link #getResultStart()}. - */ - public int getResultEnd() { - Region r = currentSource.regionList; - return r.resultStart + r.length; - } - - /** - * @return number of lines in the current region being blamed to - * {@link #getSourceCommit()}. This is always the value of the - * expression {@code getResultEnd() - getResultStart()}, but also - * {@code getSourceEnd() - getSourceStart()}. - */ - public int getRegionLength() { - return currentSource.regionList.length; - } - - /** - * @return complete contents of the source file blamed for the current - * output region. This is the contents of {@link #getSourcePath()} - * within {@link #getSourceCommit()}. The source contents is - * temporarily available as an artifact of the blame algorithm. Most - * applications will want the result contents for display to users. - */ - public RawText getSourceContents() { - return currentSource.sourceText; - } - - /** - * @return complete file contents of the result file blame is annotating. - * This value is accessible only after being configured and only - * immediately before the first call to {@link #next()}. Returns - * null if the path does not exist. - * @throws IOException - * repository cannot be read. - * @throws IllegalStateException - * {@link #next()} has already been invoked. - */ - public RawText getResultContents() throws IOException { - return queue != null ? queue.sourceText : null; - } - - /** Release the current blame session. */ - public void release() { - revPool.release(); - queue = null; - currentSource = null; - } - - private boolean find(RevCommit commit, PathFilter path) throws IOException { - treeWalk.setFilter(path); - treeWalk.reset(commit.getTree()); - while (treeWalk.next()) { - if (path.isDone(treeWalk)) { - if (treeWalk.getFileMode(0).getObjectType() != OBJ_BLOB) - return false; - treeWalk.getObjectId(idBuf, 0); - return true; - } - - if (treeWalk.isSubtree()) - treeWalk.enterSubtree(); - } - return false; - } - - private DiffEntry findRename(RevCommit parent, RevCommit commit, - PathFilter path) throws IOException { - if (renameDetector == null) - return null; - - treeWalk.setFilter(TreeFilter.ANY_DIFF); - treeWalk.reset(parent.getTree(), commit.getTree()); - renameDetector.addAll(DiffEntry.scan(treeWalk)); - for (DiffEntry ent : renameDetector.compute()) { - if (isRename(ent) && ent.getNewPath().equals(path.getPath())) - return ent; - } - return null; - } - - private static boolean isRename(DiffEntry ent) { - return ent.getChangeType() == ChangeType.RENAME - || ent.getChangeType() == ChangeType.COPY; - } -} diff --git a/src/org/eclipse/jgit/blame/BlameResult.java b/src/org/eclipse/jgit/blame/BlameResult.java deleted file mode 100644 index d7a958fe..00000000 --- a/src/org/eclipse/jgit/blame/BlameResult.java +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (C) 2011, Google Inc. - * and other copyright owners as documented in the project's IP log. - * - * This program and the accompanying materials are made available - * under the terms of the Eclipse Distribution License v1.0 which - * accompanies this distribution, is reproduced below, and is - * available at http://www.eclipse.org/org/documents/edl-v10.php - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * - Neither the name of the Eclipse Foundation, Inc. nor the - * names of its contributors may be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.eclipse.jgit.blame; - -import java.io.IOException; - -import org.eclipse.jgit.diff.RawText; -import org.eclipse.jgit.lib.PersonIdent; -import org.eclipse.jgit.revwalk.RevCommit; - -/** - * Collects line annotations for inspection by applications. - *

- * A result is usually updated incrementally as the BlameGenerator digs back - * further through history. Applications that want to lay annotations down text - * to the original source file in a viewer may find the BlameResult structure an - * easy way to acquire the information, at the expense of keeping tables in - * memory tracking every line of the result file. - *

- * This class is not thread-safe. - *

- * During blame processing there are two files involved: - *

    - *
  • result - The file whose lines are being examined. This is the revision - * the user is trying to view blame/annotation information alongside of.
  • - *
  • source - The file that was blamed with supplying one or more lines of - * data into result. The source may be a different file path (due to copy or - * rename). Source line numbers may differ from result line numbers due to lines - * being added/removed in intermediate revisions.
  • - *
- */ -public class BlameResult { - /** - * Construct a new BlameResult for a generator. - * - * @param gen - * the generator the result will consume records from. - * @return the new result object. null if the generator cannot find the path - * it starts from. - * @throws IOException - * the repository cannot be read. - */ - public static BlameResult create(BlameGenerator gen) throws IOException { - String path = gen.getResultPath(); - RawText contents = gen.getResultContents(); - if (contents == null) { - gen.release(); - return null; - } - return new BlameResult(gen, path, contents); - } - - private final String resultPath; - - private final RevCommit[] sourceCommits; - - private final PersonIdent[] sourceAuthors; - - private final PersonIdent[] sourceCommitters; - - private final String[] sourcePaths; - - /** Warning: these are actually 1-based. */ - private final int[] sourceLines; - - private RawText resultContents; - - private BlameGenerator generator; - - private int lastLength; - - BlameResult(BlameGenerator bg, String path, RawText text) { - generator = bg; - resultPath = path; - resultContents = text; - - int cnt = text.size(); - sourceCommits = new RevCommit[cnt]; - sourceAuthors = new PersonIdent[cnt]; - sourceCommitters = new PersonIdent[cnt]; - sourceLines = new int[cnt]; - sourcePaths = new String[cnt]; - } - - /** @return path of the file this result annotates. */ - public String getResultPath() { - return resultPath; - } - - /** @return contents of the result file, available for display. */ - public RawText getResultContents() { - return resultContents; - } - - /** Throw away the {@link #getResultContents()}. */ - public void discardResultContents() { - resultContents = null; - } - - /** - * Check if the given result line has been annotated yet. - * - * @param idx - * line to read data of, 0 based. - * @return true if the data has been annotated, false otherwise. - */ - public boolean hasSourceData(int idx) { - return sourceLines[idx] != 0; - } - - /** - * Check if the given result line has been annotated yet. - * - * @param start - * first index to examine. - * @param end - * last index to examine. - * @return true if the data has been annotated, false otherwise. - */ - public boolean hasSourceData(int start, int end) { - for (; start < end; start++) - if (sourceLines[start] == 0) - return false; - return true; - } - - /** - * Get the commit that provided the specified line of the result. - *

- * The source commit may be null if the line was blamed to an uncommitted - * revision, such as the working tree copy, or during a reverse blame if the - * line survives to the end revision (e.g. the branch tip). - * - * @param idx - * line to read data of, 0 based. - * @return commit that provided line {@code idx}. May be null. - */ - public RevCommit getSourceCommit(int idx) { - return sourceCommits[idx]; - } - - /** - * Get the author that provided the specified line of the result. - * - * @param idx - * line to read data of, 0 based. - * @return author that provided line {@code idx}. May be null. - */ - public PersonIdent getSourceAuthor(int idx) { - return sourceAuthors[idx]; - } - - /** - * Get the committer that provided the specified line of the result. - * - * @param idx - * line to read data of, 0 based. - * @return committer that provided line {@code idx}. May be null. - */ - public PersonIdent getSourceCommitter(int idx) { - return sourceCommitters[idx]; - } - - /** - * Get the file path that provided the specified line of the result. - * - * @param idx - * line to read data of, 0 based. - * @return source file path that provided line {@code idx}. - */ - public String getSourcePath(int idx) { - return sourcePaths[idx]; - } - - /** - * Get the corresponding line number in the source file. - * - * @param idx - * line to read data of, 0 based. - * @return matching line number in the source file. - */ - public int getSourceLine(int idx) { - return sourceLines[idx] - 1; - } - - /** - * Compute all pending information. - * - * @throws IOException - * the repository cannot be read. - */ - public void computeAll() throws IOException { - BlameGenerator gen = generator; - if (gen == null) - return; - - try { - while (gen.next()) - loadFrom(gen); - } finally { - gen.release(); - generator = null; - } - } - - /** - * Compute the next available segment and return the first index. - *

- * Computes one segment and returns to the caller the first index that is - * available. After return the caller can also inspect {@link #lastLength()} - * to determine how many lines of the result were computed. - * - * @return index that is now available. -1 if no more are available. - * @throws IOException - * the repository cannot be read. - */ - public int computeNext() throws IOException { - BlameGenerator gen = generator; - if (gen == null) - return -1; - - if (gen.next()) { - loadFrom(gen); - lastLength = gen.getRegionLength(); - return gen.getResultStart(); - } else { - gen.release(); - generator = null; - return -1; - } - } - - /** @return length of the last segment found by {@link #computeNext()}. */ - public int lastLength() { - return lastLength; - } - - /** - * Compute until the entire range has been populated. - * - * @param start - * first index to examine. - * @param end - * last index to examine. - * @throws IOException - * the repository cannot be read. - */ - public void computeRange(int start, int end) throws IOException { - BlameGenerator gen = generator; - if (gen == null) - return; - - while (start < end) { - if (hasSourceData(start, end)) - return; - - if (!gen.next()) { - gen.release(); - generator = null; - return; - } - - loadFrom(gen); - - // If the result contains either end of our current range bounds, - // update the bounds to avoid scanning that section during the - // next loop iteration. - - int resLine = gen.getResultStart(); - int resEnd = gen.getResultEnd(); - - if (resLine <= start && start < resEnd) - start = resEnd; - - if (resLine <= end && end < resEnd) - end = resLine; - } - } - - @Override - public String toString() { - StringBuilder r = new StringBuilder(); - r.append("BlameResult: "); - r.append(getResultPath()); - return r.toString(); - } - - private void loadFrom(BlameGenerator gen) { - RevCommit srcCommit = gen.getSourceCommit(); - PersonIdent srcAuthor = gen.getSourceAuthor(); - PersonIdent srcCommitter = gen.getSourceCommitter(); - String srcPath = gen.getSourcePath(); - int srcLine = gen.getSourceStart(); - int resLine = gen.getResultStart(); - int resEnd = gen.getResultEnd(); - - for (; resLine < resEnd; resLine++) { - // Reverse blame can generate multiple results for the same line. - // Favor the first one selected, as this is the oldest and most - // likely to be nearest to the inquiry made by the user. - if (sourceLines[resLine] != 0) - continue; - - sourceCommits[resLine] = srcCommit; - sourceAuthors[resLine] = srcAuthor; - sourceCommitters[resLine] = srcCommitter; - sourcePaths[resLine] = srcPath; - - // Since sourceLines is 1-based to permit hasSourceData to use 0 to - // mean the line has not been annotated yet, pre-increment instead - // of the traditional post-increment when making the assignment. - sourceLines[resLine] = ++srcLine; - } - } -} diff --git a/src/org/eclipse/jgit/blame/Candidate.java b/src/org/eclipse/jgit/blame/Candidate.java deleted file mode 100644 index 5f20ce95..00000000 --- a/src/org/eclipse/jgit/blame/Candidate.java +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Copyright (C) 2011, Google Inc. - * and other copyright owners as documented in the project's IP log. - * - * This program and the accompanying materials are made available - * under the terms of the Eclipse Distribution License v1.0 which - * accompanies this distribution, is reproduced below, and is - * available at http://www.eclipse.org/org/documents/edl-v10.php - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * - Neither the name of the Eclipse Foundation, Inc. nor the - * names of its contributors may be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.eclipse.jgit.blame; - -import java.io.IOException; - -import org.eclipse.jgit.blame.ReverseWalk.ReverseCommit; -import org.eclipse.jgit.diff.Edit; -import org.eclipse.jgit.diff.EditList; -import org.eclipse.jgit.diff.RawText; -import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.ObjectLoader; -import org.eclipse.jgit.lib.ObjectReader; -import org.eclipse.jgit.lib.PersonIdent; -import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.revwalk.RevFlag; -import org.eclipse.jgit.treewalk.filter.PathFilter; - -/** - * A source that may have supplied some (or all) of the result file. - *

- * Candidates are kept in a queue by BlameGenerator, allowing the generator to - * perform a parallel search down the parents of any merges that are discovered - * during the history traversal. Each candidate retains a {@link #regionList} - * describing sections of the result file the candidate has taken responsibility - * for either directly or indirectly through its history. Actual blame from this - * region list will be assigned to the candidate when its ancestor commit(s) are - * themselves converted into Candidate objects and the ancestor's candidate uses - * {@link #takeBlame(EditList, Candidate)} to accept responsibility for sections - * of the result. - */ -class Candidate { - /** Next candidate in the candidate queue. */ - Candidate queueNext; - - /** Commit being considered (or blamed, depending on state). */ - RevCommit sourceCommit; - - /** Path of the candidate file in {@link #sourceCommit}. */ - PathFilter sourcePath; - - /** Unique name of the candidate blob in {@link #sourceCommit}. */ - ObjectId sourceBlob; - - /** Complete contents of the file in {@link #sourceCommit}. */ - RawText sourceText; - - /** - * Chain of regions this candidate may be blamed for. - *

- * This list is always kept sorted by resultStart order, making it simple to - * merge-join with the sorted EditList during blame assignment. - */ - Region regionList; - - /** - * Score assigned to the rename to this candidate. - *

- * Consider the history "A<-B<-C". If the result file S in C was renamed to - * R in B, the rename score for this rename will be held in this field by - * the candidate object for B. By storing the score with B, the application - * can see what the rename score was as it makes the transition from C/S to - * B/R. This may seem backwards since it was C that performed the rename, - * but the application doesn't learn about path R until B. - */ - int renameScore; - - Candidate(RevCommit commit, PathFilter path) { - sourceCommit = commit; - sourcePath = path; - } - - int getParentCount() { - return sourceCommit.getParentCount(); - } - - RevCommit getParent(int idx) { - return sourceCommit.getParent(idx); - } - - Candidate getNextCandidate(@SuppressWarnings("unused") int idx) { - return null; - } - - void add(RevFlag flag) { - sourceCommit.add(flag); - } - - int getTime() { - return sourceCommit.getCommitTime(); - } - - PersonIdent getAuthor() { - return sourceCommit.getAuthorIdent(); - } - - Candidate create(RevCommit commit, PathFilter path) { - return new Candidate(commit, path); - } - - Candidate copy(RevCommit commit) { - Candidate r = create(commit, sourcePath); - r.sourceBlob = sourceBlob; - r.sourceText = sourceText; - r.regionList = regionList; - r.renameScore = renameScore; - return r; - } - - void loadText(ObjectReader reader) throws IOException { - ObjectLoader ldr = reader.open(sourceBlob, Constants.OBJ_BLOB); - sourceText = new RawText(ldr.getCachedBytes(Integer.MAX_VALUE)); - } - - void takeBlame(EditList editList, Candidate child) { - blame(editList, this, child); - } - - private static void blame(EditList editList, Candidate a, Candidate b) { - Region r = b.clearRegionList(); - Region aTail = null; - Region bTail = null; - - for (int eIdx = 0; eIdx < editList.size();) { - // If there are no more regions left, neither side has any - // more responsibility for the result. Remaining edits can - // be safely ignored. - if (r == null) - return; - - Edit e = editList.get(eIdx); - - // Edit ends before the next candidate region. Skip the edit. - if (e.getEndB() <= r.sourceStart) { - eIdx++; - continue; - } - - // Next candidate region starts before the edit. Assign some - // of the blame onto A, but possibly split and also on B. - if (r.sourceStart < e.getBeginB()) { - int d = e.getBeginB() - r.sourceStart; - if (r.length <= d) { - // Pass the blame for this region onto A. - Region next = r.next; - r.sourceStart = e.getBeginA() - d; - aTail = add(aTail, a, r); - r = next; - continue; - } - - // Split the region and assign some to A, some to B. - aTail = add(aTail, a, r.splitFirst(e.getBeginA() - d, d)); - r.slideAndShrink(d); - } - - // At this point e.getBeginB() <= r.sourceStart. - - // An empty edit on the B side isn't relevant to this split, - // as it does not overlap any candidate region. - if (e.getLengthB() == 0) { - eIdx++; - continue; - } - - // If the region ends before the edit, blame on B. - int rEnd = r.sourceStart + r.length; - if (rEnd <= e.getEndB()) { - Region next = r.next; - bTail = add(bTail, b, r); - r = next; - if (rEnd == e.getEndB()) - eIdx++; - continue; - } - - // This region extends beyond the edit. Blame the first - // half of the region on B, and process the rest after. - int len = e.getEndB() - r.sourceStart; - bTail = add(bTail, b, r.splitFirst(r.sourceStart, len)); - r.slideAndShrink(len); - eIdx++; - } - - if (r == null) - return; - - // For any remaining region, pass the blame onto A after shifting - // the source start to account for the difference between the two. - Edit e = editList.get(editList.size() - 1); - int endB = e.getEndB(); - int d = endB - e.getEndA(); - if (aTail == null) - a.regionList = r; - else - aTail.next = r; - do { - if (endB <= r.sourceStart) - r.sourceStart -= d; - r = r.next; - } while (r != null); - } - - private static Region add(Region aTail, Candidate a, Region n) { - // If there is no region on the list, use only this one. - if (aTail == null) { - a.regionList = n; - n.next = null; - return n; - } - - // If the prior region ends exactly where the new region begins - // in both the result and the source, combine these together into - // one contiguous region. This occurs when intermediate commits - // have inserted and deleted lines in the middle of a region. Try - // to report this region as a single region to the application, - // rather than in fragments. - if (aTail.resultStart + aTail.length == n.resultStart - && aTail.sourceStart + aTail.length == n.sourceStart) { - aTail.length += n.length; - return aTail; - } - - // Append the region onto the end of the list. - aTail.next = n; - n.next = null; - return n; - } - - private Region clearRegionList() { - Region r = regionList; - regionList = null; - return r; - } - - @Override - public String toString() { - StringBuilder r = new StringBuilder(); - r.append("Candidate["); - r.append(sourcePath.getPath()); - if (sourceCommit != null) - r.append(" @ ").append(sourceCommit.abbreviate(6).name()); - if (regionList != null) - r.append(" regions:").append(regionList); - r.append("]"); - return r.toString(); - } - - /** - * Special candidate type used for reverse blame. - *

- * Reverse blame inverts the commit history graph to follow from a commit to - * its descendant children, rather than the normal history direction of - * child to parent. These types require a {@link ReverseCommit} which keeps - * children pointers, allowing reverse navigation of history. - */ - static final class ReverseCandidate extends Candidate { - ReverseCandidate(ReverseCommit commit, PathFilter path) { - super(commit, path); - } - - @Override - int getParentCount() { - return ((ReverseCommit) sourceCommit).getChildCount(); - } - - @Override - RevCommit getParent(int idx) { - return ((ReverseCommit) sourceCommit).getChild(idx); - } - - @Override - int getTime() { - // Invert the timestamp so newer dates sort older. - return -sourceCommit.getCommitTime(); - } - - @Override - Candidate create(RevCommit commit, PathFilter path) { - return new ReverseCandidate((ReverseCommit) commit, path); - } - - @Override - public String toString() { - return "Reverse" + super.toString(); - } - } - - /** - * Candidate loaded from a file source, and not a commit. - *

- * The {@link Candidate#sourceCommit} field is always null on this type of - * candidate. Instead history traversal follows the single {@link #parent} - * field to discover the next Candidate. Often this is a normal Candidate - * type that has a valid sourceCommit. - */ - static final class BlobCandidate extends Candidate { - /** - * Next candidate to pass blame onto. - *

- * When computing the differences that this candidate introduced to the - * file content, the parent's sourceText is used as the base. - */ - Candidate parent; - - /** Author name to refer to this blob with. */ - String description; - - BlobCandidate(String name, PathFilter path) { - super(null, path); - description = name; - } - - @Override - int getParentCount() { - return parent != null ? 1 : 0; - } - - @Override - RevCommit getParent(int idx) { - return null; - } - - @Override - Candidate getNextCandidate(int idx) { - return parent; - } - - @Override - void add(RevFlag flag) { - // Do nothing, sourceCommit is null. - } - - @Override - int getTime() { - return Integer.MAX_VALUE; - } - - @Override - PersonIdent getAuthor() { - return new PersonIdent(description, null); - } - } -} diff --git a/src/org/eclipse/jgit/blame/Region.java b/src/org/eclipse/jgit/blame/Region.java deleted file mode 100644 index 9ea346b8..00000000 --- a/src/org/eclipse/jgit/blame/Region.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2011, Google Inc. - * and other copyright owners as documented in the project's IP log. - * - * This program and the accompanying materials are made available - * under the terms of the Eclipse Distribution License v1.0 which - * accompanies this distribution, is reproduced below, and is - * available at http://www.eclipse.org/org/documents/edl-v10.php - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * - Neither the name of the Eclipse Foundation, Inc. nor the - * names of its contributors may be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.eclipse.jgit.blame; - -/** - * Region of the result that still needs to be computed. - *

- * Regions are held in a singly-linked-list by {@link Candidate} using the - * {@link Candidate#regionList} field. The list is kept in sorted order by - * {@link #resultStart}. - */ -class Region { - /** Next entry in the region linked list. */ - Region next; - - /** First position of this region in the result file blame is computing. */ - int resultStart; - - /** First position in the {@link Candidate} that owns this Region. */ - int sourceStart; - - /** Length of the region, always >= 1. */ - int length; - - Region(int rs, int ss, int len) { - resultStart = rs; - sourceStart = ss; - length = len; - } - - /** - * Copy the entire result region, but at a new source position. - * - * @param newSource - * the new source position. - * @return the same result region, but offset for a new source. - */ - Region copy(int newSource) { - return new Region(resultStart, newSource, length); - } - - /** - * Split the region, assigning a new source position to the first half. - * - * @param newSource - * the new source position. - * @param newLen - * length of the new region. - * @return the first half of the region, at the new source. - */ - Region splitFirst(int newSource, int newLen) { - return new Region(resultStart, newSource, newLen); - } - - /** - * Edit this region to remove the first {@code d} elements. - * - * @param d - * number of elements to remove from the start of this region. - */ - void slideAndShrink(int d) { - resultStart += d; - sourceStart += d; - length -= d; - } - - Region deepCopy() { - Region head = new Region(resultStart, sourceStart, length); - Region tail = head; - for (Region n = next; n != null; n = n.next) { - Region q = new Region(n.resultStart, n.sourceStart, n.length); - tail.next = q; - tail = q; - } - return head; - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(); - Region r = this; - do { - if (r != this) - buf.append(','); - buf.append(r.resultStart); - buf.append('-'); - buf.append(r.resultStart + r.length); - r = r.next; - } while (r != null); - return buf.toString(); - } -} diff --git a/src/org/eclipse/jgit/blame/ReverseWalk.java b/src/org/eclipse/jgit/blame/ReverseWalk.java deleted file mode 100644 index 5b59804c..00000000 --- a/src/org/eclipse/jgit/blame/ReverseWalk.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2011, Google Inc. - * and other copyright owners as documented in the project's IP log. - * - * This program and the accompanying materials are made available - * under the terms of the Eclipse Distribution License v1.0 which - * accompanies this distribution, is reproduced below, and is - * available at http://www.eclipse.org/org/documents/edl-v10.php - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * - Neither the name of the Eclipse Foundation, Inc. nor the - * names of its contributors may be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.eclipse.jgit.blame; - -import java.io.IOException; - -import org.eclipse.jgit.errors.IncorrectObjectTypeException; -import org.eclipse.jgit.errors.MissingObjectException; -import org.eclipse.jgit.lib.AnyObjectId; -import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.revwalk.RevWalk; - -final class ReverseWalk extends RevWalk { - ReverseWalk(Repository repo) { - super(repo); - } - - @Override - public ReverseCommit next() throws MissingObjectException, - IncorrectObjectTypeException, IOException { - ReverseCommit c = (ReverseCommit) super.next(); - if (c == null) - return null; - for (int pIdx = 0; pIdx < c.getParentCount(); pIdx++) - ((ReverseCommit) c.getParent(pIdx)).addChild(c); - return c; - } - - @Override - protected RevCommit createCommit(AnyObjectId id) { - return new ReverseCommit(id); - } - - static final class ReverseCommit extends RevCommit { - private static final ReverseCommit[] NO_CHILDREN = {}; - - private ReverseCommit[] children = NO_CHILDREN; - - ReverseCommit(AnyObjectId id) { - super(id); - } - - void addChild(ReverseCommit c) { - // Always put the most recent child onto the front of the list. - // This works correctly because our ReverseWalk parent (above) - // runs in COMMIT_TIME_DESC order. Older commits will be popped - // later and should go in front of the children list so they are - // visited first by BlameGenerator when considering candidates. - - int cnt = children.length; - if (cnt == 0) - children = new ReverseCommit[] { c }; - else if (cnt == 1) - children = new ReverseCommit[] { c, children[0] }; - else { - ReverseCommit[] n = new ReverseCommit[1 + cnt]; - n[0] = c; - System.arraycopy(children, 0, n, 1, cnt); - children = n; - } - } - - int getChildCount() { - return children.length; - } - - ReverseCommit getChild(final int nth) { - return children[nth]; - } - } -} -- 2.39.5