summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jgit.pgm/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin1
-rw-r--r--org.eclipse.jgit.pgm/jgit.sh1
-rw-r--r--org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties17
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java350
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java1
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevWalkTextBuiltin.java8
-rw-r--r--org.eclipse.jgit.storage.dht.test/tst/org/eclipse/jgit/storage/dht/ChunkKeyTest.java9
-rw-r--r--org.eclipse.jgit.storage.dht.test/tst/org/eclipse/jgit/storage/dht/ObjectIndexKeyTest.java6
-rw-r--r--org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/ChunkKey.java16
-rw-r--r--org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/DhtCachedPack.java15
-rw-r--r--org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/DhtReader.java18
-rw-r--r--org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/DhtReaderOptions.java121
-rw-r--r--org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/ObjectIndexKey.java16
-rw-r--r--org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/OpenQueue.java10
-rw-r--r--org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/PackChunk.java2
-rw-r--r--org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/Prefetcher.java4
-rw-r--r--org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/RecentChunks.java68
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java30
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java37
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/PullResult.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java47
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java144
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java28
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java481
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndex.java80
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java19
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java17
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java23
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java55
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java28
40 files changed, 1372 insertions, 358 deletions
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index a0a5ed0382..76b135508a 100644
--- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
@@ -9,6 +9,7 @@ Bundle-RequiredExecutionEnvironment: J2SE-1.5
Import-Package: org.eclipse.jgit.api;version="[1.1.0,1.2.0)",
org.eclipse.jgit.api.errors;version="[1.1.0,1.2.0)",
org.eclipse.jgit.awtui;version="[1.1.0,1.2.0)",
+ org.eclipse.jgit.blame;version="[1.1.0,1.2.0)",
org.eclipse.jgit.diff;version="[1.1.0,1.2.0)",
org.eclipse.jgit.dircache;version="[1.1.0,1.2.0)",
org.eclipse.jgit.errors;version="[1.1.0,1.2.0)",
diff --git a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
index 1586528c6c..6562423f89 100644
--- a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
+++ b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
@@ -1,5 +1,6 @@
org.eclipse.jgit.pgm.Add
org.eclipse.jgit.pgm.AmazonS3Client
+org.eclipse.jgit.pgm.Blame
org.eclipse.jgit.pgm.Branch
org.eclipse.jgit.pgm.Checkout
org.eclipse.jgit.pgm.Clone
diff --git a/org.eclipse.jgit.pgm/jgit.sh b/org.eclipse.jgit.pgm/jgit.sh
index 9ff59d6122..f18e85fe45 100644
--- a/org.eclipse.jgit.pgm/jgit.sh
+++ b/org.eclipse.jgit.pgm/jgit.sh
@@ -52,6 +52,7 @@ done
use_pager=
case "$cmd" in
+blame) use_pager=1 ;;
diff) use_pager=1 ;;
log) use_pager=1 ;;
esac
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 98fbd7fbc4..1c95fd5f97 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
@@ -51,6 +51,7 @@ failedToLockTag=Failed to lock tag {0}: {1}
fatalError=fatal: {0}
fatalErrorTagExists=fatal: tag '{0}' exists
fatalThisProgramWillDestroyTheRepository=fatal: This program will destroy the repository\nfatal:\nfatal:\nfatal: {0}\nfatal:\nfatal: To continue, add {1} to the command line\nfatal:
+fileIsRequired=argument file is required
forcedUpdate=forced update
fromURI=From {0}
initializedEmptyGitRepositoryIn=Initialized empty Git repository in {0}
@@ -65,6 +66,8 @@ metaVar_KEY=KEY
metaVar_arg=ARG
metaVar_author=AUTHOR
metaVar_base=base
+metaVar_blameL=START,END
+metaVar_blameReverse=START..END
metaVar_bucket=BUCKET
metaVar_command=command
metaVar_commandDetail=DETAIL
@@ -93,6 +96,7 @@ metaVar_ref=REF
metaVar_refs=REFS
metaVar_refspec=refspec
metaVar_remoteName=name
+metaVar_revision=REVISION
metaVar_seconds=SECONDS
metaVar_service=SERVICE
metaVar_treeish=tree-ish
@@ -132,6 +136,7 @@ timeInMilliSeconds={0} ms
tooManyRefsGiven=Too many refs given
unknownMergeStratey=unknown merge strategy {0} specified
unsupportedOperation=Unsupported operation: {0}
+usage_Blame=Show what revision and author last modified each line
usage_CommandLineClientForamazonsS3Service=Command line client for Amazon's S3 service
usage_CommitAuthor=Override the author name used in the commit. You can use the standard A U Thor <author@example.com> format.
usage_CommitMessage=Use the given <msg> as the commit message
@@ -152,11 +157,22 @@ usage_ServerSideBackendForJgitPush=Server side backend for 'jgit push'
usage_ShowDiffs=Show diffs
usage_StopTrackingAFile=Stop tracking a file
usage_UpdateRemoteRepositoryFromLocalRefs=Update remote repository from local refs
+usage_abbrevCommits=abbreviate commits to N + 1 digits
usage_abortConnectionIfNoActivity=abort connection if no activity
usage_actOnRemoteTrackingBranches=act on remote-tracking branches
usage_addFileContentsToTheIndex=Add file contents to the index
usage_alterTheDetailShown=alter the detail shown
usage_approveDestructionOfRepository=approve destruction of repository
+usage_blameLongRevision=show long revision
+usage_blameRange=annotate only the given range
+usage_blameRawTimestamp=show raw timestamp
+usage_blameReverse=show origin of deletions instead of insertions
+usage_blameShowBlankBoundary=show blank SHA-1 for boundary commits
+usage_blameShowEmail=show author email instead of name
+usage_blameShowRoot=do not treat root commits as boundaries
+usage_blameShowSourceLine=show source line number
+usage_blameShowSourcePath=show source filename
+usage_blameSuppressAuthor=do not show author name and timestamp
usage_beMoreVerbose=be more verbose
usage_beVerbose=be verbose
usage_cached=compare against index
@@ -188,6 +204,7 @@ usage_forceCreateBranchEvenExists=force create branch even exists
usage_forceReplacingAnExistingTag=force replacing an existing tag
usage_hostnameOrIpToListenOn=hostname (or ip) to listen on
usage_indexFileFormatToCreate=index file format to create
+usage_ignoreWhitespace=ignore all whitespace
usage_inputOutputFile=Input/output file
usage_listBothRemoteTrackingAndLocalBranches=list both remote-tracking and local branches
usage_listCreateOrDeleteBranches=List, create, or delete branches
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
new file mode 100644
index 0000000000..162f433ffa
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2011, Google Inc.
+ * Copyright (C) 2009, Christian Halstrick <christian.halstrick@sap.com>
+ * Copyright (C) 2009, Johannes E. Schindelin
+ * Copyright (C) 2009, Johannes Schindelin <johannes.schindelin@gmx.de>
+ * 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.pgm;
+
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.eclipse.jgit.blame.BlameGenerator;
+import org.eclipse.jgit.blame.BlameResult;
+import org.eclipse.jgit.diff.RawText;
+import org.eclipse.jgit.diff.RawTextComparator;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.lib.Constants;
+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.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
+
+@Command(common = false, usage = "usage_Blame")
+class Blame extends TextBuiltin {
+ private RawTextComparator comparator = RawTextComparator.DEFAULT;
+
+ @Option(name = "-w", usage = "usage_ignoreWhitespace")
+ void ignoreAllSpace(@SuppressWarnings("unused") boolean on) {
+ comparator = RawTextComparator.WS_IGNORE_ALL;
+ }
+
+ @Option(name = "--abbrev", metaVar = "metaVar_n", usage = "usage_abbrevCommits")
+ private int abbrev;
+
+ @Option(name = "-l", usage = "usage_blameLongRevision")
+ private boolean showLongRevision;
+
+ @Option(name = "-t", usage = "usage_blameRawTimestamp")
+ private boolean showRawTimestamp;
+
+ @Option(name = "-b", usage = "usage_blameShowBlankBoundary")
+ private boolean showBlankBoundary;
+
+ @Option(name = "-s", usage = "usage_blameSuppressAuthor")
+ private boolean noAuthor;
+
+ @Option(name = "--show-email", aliases = { "-e" }, usage = "usage_blameShowEmail")
+ private boolean showAuthorEmail;
+
+ @Option(name = "--show-name", aliases = { "-f" }, usage = "usage_blameShowSourcePath")
+ private boolean showSourcePath;
+
+ @Option(name = "--show-number", aliases = { "-n" }, usage = "usage_blameShowSourceLine")
+ private boolean showSourceLine;
+
+ @Option(name = "--root", usage = "usage_blameShowRoot")
+ private boolean root;
+
+ @Option(name = "-L", metaVar = "metaVar_blameL", usage = "usage_blameRange")
+ private String rangeString;
+
+ @Option(name = "--reverse", metaVar = "metaVar_blameReverse", usage = "usage_blameReverse")
+ private List<RevCommit> reverseRange = new ArrayList<RevCommit>(2);
+
+ @Argument(index = 0, required = false, metaVar = "metaVar_revision")
+ private String revision;
+
+ @Argument(index = 1, required = false, metaVar = "metaVar_file")
+ private String file;
+
+ private ObjectReader reader;
+
+ private final Map<RevCommit, String> abbreviatedCommits = new HashMap<RevCommit, String>();
+
+ private SimpleDateFormat dateFmt;
+
+ private int begin;
+
+ private int end;
+
+ private BlameResult blame;
+
+ @Override
+ protected void run() throws Exception {
+ if (file == null) {
+ if (revision == null)
+ throw die(CLIText.get().fileIsRequired);
+ file = revision;
+ revision = null;
+ }
+
+ if (abbrev == 0)
+ abbrev = db.getConfig().getInt("core", "abbrev", 7);
+ if (!showBlankBoundary)
+ root = db.getConfig().getBoolean("blame", "blankboundary", false);
+ if (!root)
+ root = db.getConfig().getBoolean("blame", "showroot", false);
+
+ if (showRawTimestamp)
+ dateFmt = new SimpleDateFormat("ZZZZ");
+ else
+ dateFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ZZZZ");
+
+ BlameGenerator generator = new BlameGenerator(db, file);
+ reader = db.newObjectReader();
+ try {
+ generator.setTextComparator(comparator);
+
+ if (!reverseRange.isEmpty()) {
+ RevCommit rangeStart = null;
+ List<RevCommit> rangeEnd = new ArrayList<RevCommit>(2);
+ for (RevCommit c : reverseRange) {
+ if (c.has(RevFlag.UNINTERESTING))
+ rangeStart = c;
+ else
+ rangeEnd.add(c);
+ }
+ generator.reverse(rangeStart, rangeEnd);
+ } else if (revision != null) {
+ generator.push(null, db.resolve(revision + "^{commit}"));
+ } else {
+ generator.push(null, db.resolve(Constants.HEAD));
+ if (!db.isBare()) {
+ DirCache dc = db.readDirCache();
+ int entry = dc.findEntry(file);
+ if (0 <= entry)
+ generator.push(null, dc.getEntry(entry).getObjectId());
+
+ File inTree = new File(db.getWorkTree(), file);
+ if (inTree.isFile())
+ generator.push(null, new RawText(inTree));
+ }
+ }
+
+ blame = BlameResult.create(generator);
+ begin = 0;
+ end = blame.getResultContents().size();
+ if (rangeString != null)
+ parseLineRangeOption();
+ blame.computeRange(begin, end);
+
+ int authorWidth = 8;
+ int dateWidth = 8;
+ int pathWidth = 1;
+ int maxSourceLine = 1;
+ for (int line = begin; line < end; line++) {
+ authorWidth = Math.max(authorWidth, author(line).length());
+ dateWidth = Math.max(dateWidth, date(line).length());
+ pathWidth = Math.max(pathWidth, path(line).length());
+ maxSourceLine = Math.max(maxSourceLine, blame.getSourceLine(line));
+ }
+
+ String pathFmt = MessageFormat.format(" %{0}s", pathWidth);
+ String numFmt = MessageFormat.format(" %{0}d",
+ 1 + (int) Math.log10(maxSourceLine + 1));
+ String lineFmt = MessageFormat.format(" %{0}d) ",
+ 1 + (int) Math.log10(end + 1));
+ String authorFmt = MessageFormat.format(" (%-{0}s %{1}s",
+ authorWidth, dateWidth);
+
+ for (int line = begin; line < end; line++) {
+ out.print(abbreviate(blame.getSourceCommit(line)));
+ if (showSourcePath)
+ out.format(pathFmt, path(line));
+ if (showSourceLine)
+ out.format(numFmt, blame.getSourceLine(line) + 1);
+ if (!noAuthor)
+ out.format(authorFmt, author(line), date(line));
+ out.format(lineFmt, line + 1);
+ out.flush();
+ blame.getResultContents().writeLine(System.out, line);
+ out.print('\n');
+ }
+ } finally {
+ generator.release();
+ reader.release();
+ }
+ }
+
+ private void parseLineRangeOption() {
+ String beginStr, endStr;
+ if (rangeString.startsWith("/")) {
+ int c = rangeString.indexOf("/,", 1);
+ if (c < 0) {
+ beginStr = rangeString;
+ endStr = String.valueOf(end);
+ } else {
+ beginStr = rangeString.substring(0, c);
+ endStr = rangeString.substring(c + 2);
+ }
+
+ } else {
+ int c = rangeString.indexOf(',');
+ if (c < 0) {
+ beginStr = rangeString;
+ endStr = String.valueOf(end);
+ } else if (c == 0) {
+ beginStr = "0";
+ endStr = rangeString.substring(1);
+ } else {
+ beginStr = rangeString.substring(0, c);
+ endStr = rangeString.substring(c + 1);
+ }
+ }
+
+ if (beginStr.equals(""))
+ begin = 0;
+ else if (beginStr.startsWith("/"))
+ begin = findLine(0, beginStr);
+ else
+ begin = Math.max(0, Integer.parseInt(beginStr) - 1);
+
+ if (endStr.equals(""))
+ end = blame.getResultContents().size();
+ else if (endStr.startsWith("/"))
+ end = findLine(begin, endStr);
+ else if (endStr.startsWith("-"))
+ end = begin + Integer.parseInt(endStr);
+ else if (endStr.startsWith("+"))
+ end = begin + Integer.parseInt(endStr.substring(1));
+ else
+ end = Math.max(0, Integer.parseInt(endStr) - 1);
+ }
+
+ private int findLine(int b, String regex) {
+ String re = regex.substring(1, regex.length() - 1);
+ if (!re.startsWith("^"))
+ re = ".*" + re;
+ if (!re.endsWith("$"))
+ re = re + ".*";
+ Pattern p = Pattern.compile(re);
+ RawText text = blame.getResultContents();
+ for (int line = b; line < text.size(); line++) {
+ if (p.matcher(text.getString(line)).matches())
+ return line;
+ }
+ return b;
+ }
+
+ private String path(int line) {
+ String p = blame.getSourcePath(line);
+ return p != null ? p : "";
+ }
+
+ private String author(int line) {
+ PersonIdent author = blame.getSourceAuthor(line);
+ if (author == null)
+ return "";
+ String name = showAuthorEmail ? author.getEmailAddress() : author
+ .getName();
+ return name != null ? name : "";
+ }
+
+ private String date(int line) {
+ if (blame.getSourceCommit(line) == null)
+ return "";
+
+ PersonIdent author = blame.getSourceAuthor(line);
+ if (author == null)
+ return "";
+
+ dateFmt.setTimeZone(author.getTimeZone());
+ if (!showRawTimestamp)
+ return dateFmt.format(author.getWhen());
+ return String.format("%d %s", author.getWhen().getTime() / 1000L,
+ dateFmt.format(author.getWhen()));
+ }
+
+ private String abbreviate(RevCommit commit) throws IOException {
+ String r = abbreviatedCommits.get(commit);
+ if (r != null)
+ return r;
+
+ if (showBlankBoundary && commit.getParentCount() == 0)
+ commit = null;
+
+ if (commit == null) {
+ int len = showLongRevision ? OBJECT_ID_STRING_LENGTH : (abbrev + 1);
+ StringBuilder b = new StringBuilder(len);
+ for (int i = 0; i < len; i++)
+ b.append(' ');
+ r = b.toString();
+
+ } else if (!root && commit.getParentCount() == 0) {
+ if (showLongRevision)
+ r = "^" + commit.name().substring(0, OBJECT_ID_STRING_LENGTH - 1);
+ else
+ r = "^" + reader.abbreviate(commit, abbrev).name();
+ } else {
+ if (showLongRevision)
+ r = commit.name();
+ else
+ r = reader.abbreviate(commit, abbrev + 1).name();
+ }
+
+ abbreviatedCommits.put(commit, r);
+ return r;
+ }
+}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java
index d82ff499f9..e1c26adf4c 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java
@@ -104,6 +104,7 @@ public class CLIText extends TranslationBundle {
/***/ public String fatalError;
/***/ public String fatalErrorTagExists;
/***/ public String fatalThisProgramWillDestroyTheRepository;
+ /***/ public String fileIsRequired;
/***/ public String forcedUpdate;
/***/ public String fromURI;
/***/ public String initializedEmptyGitRepositoryIn;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevWalkTextBuiltin.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevWalkTextBuiltin.java
index 474e974cd1..715cb71b43 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevWalkTextBuiltin.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevWalkTextBuiltin.java
@@ -199,9 +199,11 @@ abstract class RevWalkTextBuiltin extends TextBuiltin {
}
protected RevWalk createWalk() {
- if (argWalk == null)
- argWalk = objects ? new ObjectWalk(db) : new RevWalk(db);
- return argWalk;
+ if (objects)
+ return new ObjectWalk(db);
+ if (argWalk != null)
+ return argWalk;
+ return new RevWalk(db);
}
protected int walkLoop() throws Exception {
diff --git a/org.eclipse.jgit.storage.dht.test/tst/org/eclipse/jgit/storage/dht/ChunkKeyTest.java b/org.eclipse.jgit.storage.dht.test/tst/org/eclipse/jgit/storage/dht/ChunkKeyTest.java
index 63cbf520c0..9219663667 100644
--- a/org.eclipse.jgit.storage.dht.test/tst/org/eclipse/jgit/storage/dht/ChunkKeyTest.java
+++ b/org.eclipse.jgit.storage.dht.test/tst/org/eclipse/jgit/storage/dht/ChunkKeyTest.java
@@ -43,7 +43,8 @@
package org.eclipse.jgit.storage.dht;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
@@ -59,19 +60,19 @@ public class ChunkKeyTest {
ChunkKey key1 = ChunkKey.create(repo1, id);
assertEquals(repo1.asInt(), key1.getRepositoryId());
assertEquals(id, key1.getChunkHash());
- assertEquals("3e.41234567.3e64b928d51b3a28e89cfe2a3f0eeae35ef07839",
+ assertEquals("41234567.3e64b928d51b3a28e89cfe2a3f0eeae35ef07839",
key1.asString());
ChunkKey key2 = ChunkKey.fromBytes(key1.asBytes());
assertEquals(repo1.asInt(), key2.getRepositoryId());
assertEquals(id, key2.getChunkHash());
- assertEquals("3e.41234567.3e64b928d51b3a28e89cfe2a3f0eeae35ef07839",
+ assertEquals("41234567.3e64b928d51b3a28e89cfe2a3f0eeae35ef07839",
key2.asString());
ChunkKey key3 = ChunkKey.fromString(key1.asString());
assertEquals(repo1.asInt(), key3.getRepositoryId());
assertEquals(id, key3.getChunkHash());
- assertEquals("3e.41234567.3e64b928d51b3a28e89cfe2a3f0eeae35ef07839",
+ assertEquals("41234567.3e64b928d51b3a28e89cfe2a3f0eeae35ef07839",
key3.asString());
assertEquals(key1, key2);
diff --git a/org.eclipse.jgit.storage.dht.test/tst/org/eclipse/jgit/storage/dht/ObjectIndexKeyTest.java b/org.eclipse.jgit.storage.dht.test/tst/org/eclipse/jgit/storage/dht/ObjectIndexKeyTest.java
index ab3b423ede..d3419bd140 100644
--- a/org.eclipse.jgit.storage.dht.test/tst/org/eclipse/jgit/storage/dht/ObjectIndexKeyTest.java
+++ b/org.eclipse.jgit.storage.dht.test/tst/org/eclipse/jgit/storage/dht/ObjectIndexKeyTest.java
@@ -58,19 +58,19 @@ public class ObjectIndexKeyTest {
ObjectIndexKey key1 = ObjectIndexKey.create(repo, id);
assertEquals(repo.asInt(), key1.getRepositoryId());
assertEquals(key1, id);
- assertEquals("3e.41234567.3e64b928d51b3a28e89cfe2a3f0eeae35ef07839",
+ assertEquals("41234567.3e64b928d51b3a28e89cfe2a3f0eeae35ef07839",
key1.asString());
ObjectIndexKey key2 = ObjectIndexKey.fromBytes(key1.asBytes());
assertEquals(repo.asInt(), key2.getRepositoryId());
assertEquals(key2, id);
- assertEquals("3e.41234567.3e64b928d51b3a28e89cfe2a3f0eeae35ef07839",
+ assertEquals("41234567.3e64b928d51b3a28e89cfe2a3f0eeae35ef07839",
key2.asString());
ObjectIndexKey key3 = ObjectIndexKey.fromString(key1.asString());
assertEquals(repo.asInt(), key3.getRepositoryId());
assertEquals(key3, id);
- assertEquals("3e.41234567.3e64b928d51b3a28e89cfe2a3f0eeae35ef07839",
+ assertEquals("41234567.3e64b928d51b3a28e89cfe2a3f0eeae35ef07839",
key3.asString());
}
}
diff --git a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/ChunkKey.java b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/ChunkKey.java
index 272b5ea173..11a151f6ba 100644
--- a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/ChunkKey.java
+++ b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/ChunkKey.java
@@ -54,7 +54,7 @@ import org.eclipse.jgit.lib.ObjectId;
/** Unique identifier of a {@link PackChunk} in the DHT. */
public final class ChunkKey implements RowKey {
- static final int KEYLEN = 52;
+ static final int KEYLEN = 49;
/**
* @param repo
@@ -84,8 +84,8 @@ public final class ChunkKey implements RowKey {
throw new IllegalArgumentException(MessageFormat.format(
DhtText.get().invalidChunkKey, decode(key, ptr, ptr + len)));
- int repo = parse32(key, ptr + 3);
- ObjectId chunk = ObjectId.fromString(key, ptr + 12);
+ int repo = parse32(key, ptr);
+ ObjectId chunk = ObjectId.fromString(key, ptr + 9);
return new ChunkKey(repo, chunk);
}
@@ -122,13 +122,9 @@ public final class ChunkKey implements RowKey {
public byte[] asBytes() {
byte[] r = new byte[KEYLEN];
- chunk.copyTo(r, 12);
- format32(r, 3, repo);
- // bucket is the leading 2 digits of the SHA-1.
- r[11] = '.';
- r[2] = '.';
- r[1] = r[12 + 1];
- r[0] = r[12 + 0];
+ format32(r, 0, repo);
+ r[8] = '.';
+ chunk.copyTo(r, 9);
return r;
}
diff --git a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/DhtCachedPack.java b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/DhtCachedPack.java
index 39a76463fb..0fd253bfbf 100644
--- a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/DhtCachedPack.java
+++ b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/DhtCachedPack.java
@@ -125,9 +125,18 @@ public class DhtCachedPack extends CachedPack {
throws IOException {
if (keyList == null)
init();
- Prefetcher p = new Prefetcher(ctx, 0);
- p.push(Arrays.asList(keyList));
- copyPack(out, p, validate);
+
+ // Clear the recent chunks because all of the reader's
+ // chunk limit should be made available for prefetch.
+ int cacheLimit = ctx.getOptions().getChunkLimit();
+ ctx.getRecentChunks().setMaxBytes(0);
+ try {
+ Prefetcher p = new Prefetcher(ctx, 0, cacheLimit);
+ p.push(Arrays.asList(keyList));
+ copyPack(out, p, validate);
+ } finally {
+ ctx.getRecentChunks().setMaxBytes(cacheLimit);
+ }
}
private void copyPack(PackOutputStream out, Prefetcher prefetcher,
diff --git a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/DhtReader.java b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/DhtReader.java
index f9288b9e2e..330b5c0734 100644
--- a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/DhtReader.java
+++ b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/DhtReader.java
@@ -156,6 +156,10 @@ public class DhtReader extends ObjectReader implements ObjectReuseAsIs {
return recentInfo;
}
+ RecentChunks getRecentChunks() {
+ return recentChunks;
+ }
+
DeltaBaseCache getDeltaBaseCache() {
return deltaBaseCache;
}
@@ -242,7 +246,7 @@ public class DhtReader extends ObjectReader implements ObjectReuseAsIs {
// configured as push might invoke our own methods that may
// try to call back into the active prefetcher.
//
- Prefetcher p = new Prefetcher(this, OBJ_COMMIT);
+ Prefetcher p = prefetch(OBJ_COMMIT, readerOptions.getWalkCommitsPrefetchRatio());
p.push(this, roots);
prefetcher = p;
}
@@ -256,7 +260,7 @@ public class DhtReader extends ObjectReader implements ObjectReuseAsIs {
// configured as push might invoke our own methods that may
// try to call back into the active prefetcher.
//
- Prefetcher p = new Prefetcher(this, OBJ_TREE);
+ Prefetcher p = prefetch(OBJ_TREE, readerOptions.getWalkTreesPrefetchRatio());
p.push(this, min.getTree(), max.getTree());
prefetcher = p;
}
@@ -391,14 +395,22 @@ public class DhtReader extends ObjectReader implements ObjectReuseAsIs {
new RepresentationSelector(packer, this, monitor).select(itr);
}
+ private Prefetcher prefetch(final int type, final int ratio) {
+ int limit = readerOptions.getChunkLimit();
+ int prefetchLimit = (int) (limit * (ratio / 100.0));
+ recentChunks.setMaxBytes(limit - prefetchLimit);
+ return new Prefetcher(this, type, prefetchLimit);
+ }
+
private void endPrefetch() {
+ recentChunks.setMaxBytes(getOptions().getChunkLimit());
prefetcher = null;
}
@SuppressWarnings("unchecked")
public void writeObjects(PackOutputStream out, List<ObjectToPack> objects)
throws IOException {
- prefetcher = new Prefetcher(this, 0);
+ prefetcher = prefetch(0, readerOptions.getWriteObjectsPrefetchRatio());
try {
List itr = objects;
new ObjectWriter(this, prefetcher).plan(itr);
diff --git a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/DhtReaderOptions.java b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/DhtReaderOptions.java
index 0890e39ad0..db3f51028f 100644
--- a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/DhtReaderOptions.java
+++ b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/DhtReaderOptions.java
@@ -57,7 +57,15 @@ public class DhtReaderOptions {
private boolean prefetchFollowEdgeHints;
- private int prefetchLimit;
+ private int chunkLimit;
+
+ private int openQueuePrefetchRatio;
+
+ private int walkCommitsPrefetchRatio;
+
+ private int walkTreesPrefetchRatio;
+
+ private int writeObjectsPrefetchRatio;
private int objectIndexConcurrentBatches;
@@ -69,15 +77,18 @@ public class DhtReaderOptions {
private int recentInfoCacheSize;
- private int recentChunkCacheSize;
-
private boolean trackFirstChunkLoad;
/** Create a default reader configuration. */
public DhtReaderOptions() {
setTimeout(Timeout.seconds(5));
setPrefetchFollowEdgeHints(true);
- setPrefetchLimit(5 * MiB);
+
+ setChunkLimit(5 * MiB);
+ setOpenQueuePrefetchRatio(20 /* percent */);
+ setWalkCommitsPrefetchRatio(20 /* percent */);
+ setWalkTreesPrefetchRatio(20 /* percent */);
+ setWriteObjectsPrefetchRatio(90 /* percent */);
setObjectIndexConcurrentBatches(2);
setObjectIndexBatchSize(512);
@@ -86,7 +97,6 @@ public class DhtReaderOptions {
setDeltaBaseCacheLimit(10 * MiB);
setRecentInfoCacheSize(4096);
- setRecentChunkCacheSize(4);
}
/** @return default timeout to wait on long operations before aborting. */
@@ -125,19 +135,83 @@ public class DhtReaderOptions {
return this;
}
- /** @return number of bytes to load during prefetching. */
- public int getPrefetchLimit() {
- return prefetchLimit;
+ /** @return number of bytes to hold within a DhtReader. */
+ public int getChunkLimit() {
+ return chunkLimit;
}
/**
- * Set the number of bytes the prefetcher should hold onto.
+ * Set the number of bytes hold within a DhtReader.
*
* @param maxBytes
* @return {@code this}
*/
- public DhtReaderOptions setPrefetchLimit(int maxBytes) {
- prefetchLimit = Math.max(1024, maxBytes);
+ public DhtReaderOptions setChunkLimit(int maxBytes) {
+ chunkLimit = Math.max(1024, maxBytes);
+ return this;
+ }
+
+ /** @return percentage of {@link #getChunkLimit()} used for prefetch, 0..100. */
+ public int getOpenQueuePrefetchRatio() {
+ return openQueuePrefetchRatio;
+ }
+
+ /**
+ * Set the prefetch ratio used by the open object queue.
+ *
+ * @param ratio 0..100.
+ * @return {@code this}
+ */
+ public DhtReaderOptions setOpenQueuePrefetchRatio(int ratio) {
+ openQueuePrefetchRatio = Math.max(0, Math.min(ratio, 100));
+ return this;
+ }
+
+ /** @return percentage of {@link #getChunkLimit()} used for prefetch, 0..100. */
+ public int getWalkCommitsPrefetchRatio() {
+ return walkCommitsPrefetchRatio;
+ }
+
+ /**
+ * Set the prefetch ratio used by the open object queue.
+ *
+ * @param ratio 0..100.
+ * @return {@code this}
+ */
+ public DhtReaderOptions setWalkCommitsPrefetchRatio(int ratio) {
+ walkCommitsPrefetchRatio = Math.max(0, Math.min(ratio, 100));
+ return this;
+ }
+
+ /** @return percentage of {@link #getChunkLimit()} used for prefetch, 0..100. */
+ public int getWalkTreesPrefetchRatio() {
+ return walkTreesPrefetchRatio;
+ }
+
+ /**
+ * Set the prefetch ratio used by the open object queue.
+ *
+ * @param ratio 0..100.
+ * @return {@code this}
+ */
+ public DhtReaderOptions setWalkTreesPrefetchRatio(int ratio) {
+ walkTreesPrefetchRatio = Math.max(0, Math.min(ratio, 100));
+ return this;
+ }
+
+ /** @return percentage of {@link #getChunkLimit()} used for prefetch, 0..100. */
+ public int getWriteObjectsPrefetchRatio() {
+ return writeObjectsPrefetchRatio;
+ }
+
+ /**
+ * Set the prefetch ratio used by the open object queue.
+ *
+ * @param ratio 0..100.
+ * @return {@code this}
+ */
+ public DhtReaderOptions setWriteObjectsPrefetchRatio(int ratio) {
+ writeObjectsPrefetchRatio = Math.max(0, Math.min(ratio, 100));
return this;
}
@@ -226,24 +300,6 @@ public class DhtReaderOptions {
return this;
}
- /** @return number of recent chunks to hold onto per-reader. */
- public int getRecentChunkCacheSize() {
- return recentChunkCacheSize;
- }
-
- /**
- * Set the number of chunks each reader holds onto for recently used access.
- *
- * @param chunkCnt
- * the number of chunks each reader retains of recently used
- * chunks to smooth out access.
- * @return {@code this}
- */
- public DhtReaderOptions setRecentChunkCacheSize(int chunkCnt) {
- recentChunkCacheSize = Math.max(0, chunkCnt);
- return this;
- }
-
/**
* @return true if {@link DhtReader.Statistics} includes the stack trace for
* the first time a chunk is loaded. Supports debugging DHT code.
@@ -277,7 +333,11 @@ public class DhtReaderOptions {
public DhtReaderOptions fromConfig(Config rc) {
setTimeout(Timeout.getTimeout(rc, "core", "dht", "timeout", getTimeout()));
setPrefetchFollowEdgeHints(rc.getBoolean("core", "dht", "prefetchFollowEdgeHints", isPrefetchFollowEdgeHints()));
- setPrefetchLimit(rc.getInt("core", "dht", "prefetchLimit", getPrefetchLimit()));
+ setChunkLimit(rc.getInt("core", "dht", "chunkLimit", getChunkLimit()));
+ setOpenQueuePrefetchRatio(rc.getInt("core", "dht", "openQueuePrefetchRatio", getOpenQueuePrefetchRatio()));
+ setWalkCommitsPrefetchRatio(rc.getInt("core", "dht", "walkCommitsPrefetchRatio", getWalkCommitsPrefetchRatio()));
+ setWalkTreesPrefetchRatio(rc.getInt("core", "dht", "walkTreesPrefetchRatio", getWalkTreesPrefetchRatio()));
+ setWriteObjectsPrefetchRatio(rc.getInt("core", "dht", "writeObjectsPrefetchRatio", getWriteObjectsPrefetchRatio()));
setObjectIndexConcurrentBatches(rc.getInt("core", "dht", "objectIndexConcurrentBatches", getObjectIndexConcurrentBatches()));
setObjectIndexBatchSize(rc.getInt("core", "dht", "objectIndexBatchSize", getObjectIndexBatchSize()));
@@ -286,7 +346,6 @@ public class DhtReaderOptions {
setDeltaBaseCacheLimit(rc.getInt("core", "dht", "deltaBaseCacheLimit", getDeltaBaseCacheLimit()));
setRecentInfoCacheSize(rc.getInt("core", "dht", "recentInfoCacheSize", getRecentInfoCacheSize()));
- setRecentChunkCacheSize(rc.getInt("core", "dht", "recentChunkCacheSize", getRecentChunkCacheSize()));
setTrackFirstChunkLoad(rc.getBoolean("core", "dht", "debugTrackFirstChunkLoad", isTrackFirstChunkLoad()));
return this;
diff --git a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/ObjectIndexKey.java b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/ObjectIndexKey.java
index b38fdcec22..ab8f8352b0 100644
--- a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/ObjectIndexKey.java
+++ b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/ObjectIndexKey.java
@@ -55,7 +55,7 @@ import org.eclipse.jgit.lib.ObjectId;
/** Identifies an ObjectId in the DHT. */
public final class ObjectIndexKey extends ObjectId implements RowKey {
- private static final int KEYLEN = 52;
+ private static final int KEYLEN = 49;
/**
* @param repo
@@ -75,8 +75,8 @@ public final class ObjectIndexKey extends ObjectId implements RowKey {
throw new IllegalArgumentException(MessageFormat.format(
DhtText.get().invalidChunkKey, decode(key)));
- int repo = parse32(key, 3);
- ObjectId id = ObjectId.fromString(key, 12);
+ int repo = parse32(key, 0);
+ ObjectId id = ObjectId.fromString(key, 9);
return new ObjectIndexKey(repo, id);
}
@@ -106,13 +106,9 @@ public final class ObjectIndexKey extends ObjectId implements RowKey {
public byte[] asBytes() {
byte[] r = new byte[KEYLEN];
- copyTo(r, 12);
- format32(r, 3, repo);
- // bucket is the leading 2 digits of the SHA-1.
- r[11] = '.';
- r[2] = '.';
- r[1] = r[12 + 1];
- r[0] = r[12 + 0];
+ format32(r, 0, repo);
+ r[8] = '.';
+ copyTo(r, 9);
return r;
}
diff --git a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/OpenQueue.java b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/OpenQueue.java
index e47f2b2cb4..32b22340d1 100644
--- a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/OpenQueue.java
+++ b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/OpenQueue.java
@@ -159,6 +159,7 @@ final class OpenQueue<T extends ObjectId> extends QueueObjectLookup<T>
@Override
public void release() {
+ reader.getRecentChunks().setMaxBytes(reader.getOptions().getChunkLimit());
prefetcher = null;
currChunk = null;
}
@@ -173,8 +174,13 @@ final class OpenQueue<T extends ObjectId> extends QueueObjectLookup<T>
list = new ArrayList<ObjectWithInfo<T>>();
byChunk.put(chunkKey, list);
- if (prefetcher == null)
- prefetcher = new Prefetcher(reader, 0);
+ if (prefetcher == null) {
+ int limit = reader.getOptions().getChunkLimit();
+ int ratio = reader.getOptions().getOpenQueuePrefetchRatio();
+ int prefetchLimit = (int) (limit * (ratio / 100.0));
+ reader.getRecentChunks().setMaxBytes(limit - prefetchLimit);
+ prefetcher = new Prefetcher(reader, 0, prefetchLimit);
+ }
prefetcher.push(chunkKey);
}
list.add(c);
diff --git a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/PackChunk.java b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/PackChunk.java
index c0684022f3..66d3d3386b 100644
--- a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/PackChunk.java
+++ b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/PackChunk.java
@@ -291,7 +291,7 @@ public final class PackChunk {
}
int findOffset(RepositoryKey repo, AnyObjectId objId) {
- if (key.getRepositoryId() == repo.asInt())
+ if (key.getRepositoryId() == repo.asInt() && index != null)
return index.findOffset(objId);
return -1;
}
diff --git a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/Prefetcher.java b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/Prefetcher.java
index 743f1f5944..fef2b4f29d 100644
--- a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/Prefetcher.java
+++ b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/Prefetcher.java
@@ -104,7 +104,7 @@ class Prefetcher implements StreamingCallback<Collection<PackChunk.Members>> {
private DhtException error;
- Prefetcher(DhtReader reader, int objectType) {
+ Prefetcher(DhtReader reader, int objectType, int prefetchLimitInBytes) {
this.db = reader.getDatabase();
this.stats = reader.getStatistics();
this.objectType = objectType;
@@ -113,7 +113,7 @@ class Prefetcher implements StreamingCallback<Collection<PackChunk.Members>> {
this.queue = new LinkedList<ChunkKey>();
this.followEdgeHints = reader.getOptions().isPrefetchFollowEdgeHints();
this.averageChunkSize = reader.getInserterOptions().getChunkSize();
- this.highWaterMark = reader.getOptions().getPrefetchLimit();
+ this.highWaterMark = prefetchLimitInBytes;
int lwm = (highWaterMark / averageChunkSize) - 4;
if (lwm <= 0)
diff --git a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/RecentChunks.java b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/RecentChunks.java
index f75e3bdc82..22608ee1b3 100644
--- a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/RecentChunks.java
+++ b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/RecentChunks.java
@@ -44,6 +44,7 @@
package org.eclipse.jgit.storage.dht;
import java.io.IOException;
+import java.util.HashMap;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
@@ -55,9 +56,11 @@ final class RecentChunks {
private final DhtReader.Statistics stats;
- private final int maxSize;
+ private final HashMap<ChunkKey, Node> byKey;
- private int curSize;
+ private int maxBytes;
+
+ private int curBytes;
private Node lruHead;
@@ -66,38 +69,56 @@ final class RecentChunks {
RecentChunks(DhtReader reader) {
this.reader = reader;
this.stats = reader.getStatistics();
- this.maxSize = reader.getOptions().getRecentChunkCacheSize();
+ this.byKey = new HashMap<ChunkKey, Node>();
+ this.maxBytes = reader.getOptions().getChunkLimit();
+ }
+
+ void setMaxBytes(int newMax) {
+ maxBytes = Math.max(0, newMax);
+ if (0 < maxBytes)
+ prune();
+ else
+ clear();
}
PackChunk get(ChunkKey key) {
- for (Node n = lruHead; n != null; n = n.next) {
- if (key.equals(n.chunk.getChunkKey())) {
- hit(n);
- stats.recentChunks_Hits++;
- return n.chunk;
- }
+ Node n = byKey.get(key);
+ if (n != null) {
+ hit(n);
+ stats.recentChunks_Hits++;
+ return n.chunk;
}
stats.recentChunks_Miss++;
return null;
}
void put(PackChunk chunk) {
- for (Node n = lruHead; n != null; n = n.next) {
- if (n.chunk == chunk) {
- hit(n);
- return;
- }
+ Node n = byKey.get(chunk.getChunkKey());
+ if (n != null && n.chunk == chunk) {
+ hit(n);
+ return;
}
- Node n;
- if (curSize < maxSize) {
- n = new Node();
- curSize++;
- } else {
- n = lruTail;
- }
+ curBytes += chunk.getTotalSize();
+ prune();
+
+ n = new Node();
n.chunk = chunk;
- hit(n);
+ byKey.put(chunk.getChunkKey(), n);
+ first(n);
+ }
+
+ private void prune() {
+ while (maxBytes < curBytes) {
+ Node n = lruTail;
+ if (n == null)
+ break;
+
+ PackChunk c = n.chunk;
+ curBytes -= c.getTotalSize();
+ byKey.remove(c.getChunkKey());
+ remove(n);
+ }
}
ObjectLoader open(RepositoryKey repo, AnyObjectId objId, int typeHint)
@@ -164,9 +185,10 @@ final class RecentChunks {
}
void clear() {
- curSize = 0;
+ curBytes = 0;
lruHead = null;
lruTail = null;
+ byKey.clear();
}
private void hit(Node n) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java
index dc2ccb97fb..3ca4f589db 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java
@@ -61,6 +61,9 @@ import java.io.File;
import java.io.IOException;
import java.util.Map;
+import org.eclipse.jgit.events.ListenerHandle;
+import org.eclipse.jgit.events.RefsChangedEvent;
+import org.eclipse.jgit.events.RefsChangedListener;
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.AnyObjectId;
@@ -466,6 +469,33 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
}
@Test
+ public void testGetRefs_LooseSorting_Bug_348834() throws IOException {
+ Map<String, Ref> refs;
+
+ writeLooseRef("refs/heads/my/a+b", A);
+ writeLooseRef("refs/heads/my/a/b/c", B);
+
+ final int[] count = new int[1];
+
+ ListenerHandle listener = Repository.getGlobalListenerList()
+ .addRefsChangedListener(new RefsChangedListener() {
+
+ public void onRefsChanged(RefsChangedEvent event) {
+ count[0]++;
+ }
+ });
+
+ refs = refdir.getRefs(RefDatabase.ALL);
+ refs = refdir.getRefs(RefDatabase.ALL);
+ listener.remove();
+ assertEquals(1, count[0]); // Bug 348834 multiple RefsChangedEvents
+ assertEquals(2, refs.size());
+ assertEquals(A, refs.get("refs/heads/my/a+b").getObjectId());
+ assertEquals(B, refs.get("refs/heads/my/a/b/c").getObjectId());
+
+ }
+
+ @Test
public void testGetRefs_TagsOnly_AllPacked() throws IOException {
Map<String, Ref> tags;
Ref a;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
index f3a2e53602..8caff06292 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
@@ -66,6 +66,7 @@ import org.eclipse.jgit.errors.CheckoutConflictException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
@@ -266,9 +267,14 @@ public class CheckoutCommand extends GitCommand<Ref> {
}
File workTree = repo.getWorkTree();
- for (String file : files)
- DirCacheCheckout.checkoutEntry(repo, new File(workTree, file),
- dc.getEntry(file));
+ ObjectReader r = repo.getObjectDatabase().newReader();
+ try {
+ for (String file : files)
+ DirCacheCheckout.checkoutEntry(repo, new File(workTree,
+ file), dc.getEntry(file), r);
+ } finally {
+ r.release();
+ }
} finally {
dc.unlock();
revWalk.release();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
index cfbcbf1b6d..29dd6e90c3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -301,8 +301,13 @@ public class CloneCommand implements Callable<Git> {
}
/**
+ * The remote name used to keep track of the upstream repository for the
+ * clone operation. If no remote name is set, the default value of
+ * <code>Constants.DEFAULT_REMOTE_NAME</code> will be used.
+ *
+ * @see Constants#DEFAULT_REMOTE_NAME
* @param remote
- * the branch to keep track of in the origin repository
+ * name that keeps track of the upstream repository
* @return this instance
*/
public CloneCommand setRemote(String remote) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
index 1ec8685f7e..86e4e78c8c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
@@ -70,37 +70,72 @@ public class MergeResult {
public String toString() {
return "Fast-forward";
}
+
+ @Override
+ public boolean isSuccessful() {
+ return true;
+ }
},
/** */
ALREADY_UP_TO_DATE {
public String toString() {
return "Already-up-to-date";
}
+
+ @Override
+ public boolean isSuccessful() {
+ return true;
+ }
},
/** */
FAILED {
public String toString() {
return "Failed";
}
+
+ @Override
+ public boolean isSuccessful() {
+ return false;
+ }
},
/** */
MERGED {
public String toString() {
return "Merged";
}
+
+ @Override
+ public boolean isSuccessful() {
+ return true;
+ }
},
/** */
CONFLICTING {
public String toString() {
return "Conflicting";
}
+
+ @Override
+ public boolean isSuccessful() {
+ return false;
+ }
},
/** */
NOT_SUPPORTED {
public String toString() {
return "Not-yet-supported";
}
- }
+
+ @Override
+ public boolean isSuccessful() {
+ return false;
+ }
+ };
+
+ /**
+ * @return whether the status indicates a successful result
+ */
+ public abstract boolean isSuccessful();
}
private ObjectId[] mergedCommits;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
index 8eae4027a1..25dd52822a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
@@ -194,7 +194,8 @@ public class PullCommand extends GitCommand<PullResult> {
String remoteUri;
FetchResult fetchRes;
if (isRemote) {
- remoteUri = repoConfig.getString("remote", remote,
+ remoteUri = repoConfig.getString(
+ ConfigConstants.CONFIG_REMOTE_SECTION, remote,
ConfigConstants.CONFIG_KEY_URL);
if (remoteUri == null) {
String missingKey = ConfigConstants.CONFIG_REMOTE_SECTION + DOT
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullResult.java
index 40ed137ee0..49327fc9b9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullResult.java
@@ -101,6 +101,17 @@ public class PullResult {
return this.fetchedFrom;
}
+ /**
+ * @return whether the pull was successful
+ */
+ public boolean isSuccessful() {
+ if (mergeResult != null)
+ return mergeResult.getMergeStatus().isSuccessful();
+ else if (rebaseResult != null)
+ return rebaseResult.getStatus().isSuccessful();
+ return true;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java
index af070d6535..07cd8eb20a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java
@@ -59,27 +59,62 @@ public class RebaseResult {
/**
* Rebase was successful, HEAD points to the new commit
*/
- OK,
+ OK {
+ @Override
+ public boolean isSuccessful() {
+ return true;
+ }
+ },
/**
* Aborted; the original HEAD was restored
*/
- ABORTED,
+ ABORTED {
+ @Override
+ public boolean isSuccessful() {
+ return false;
+ }
+ },
/**
* Stopped due to a conflict; must either abort or resolve or skip
*/
- STOPPED,
+ STOPPED {
+ @Override
+ public boolean isSuccessful() {
+ return false;
+ }
+ },
/**
* Failed; the original HEAD was restored
*/
- FAILED,
+ FAILED {
+ @Override
+ public boolean isSuccessful() {
+ return false;
+ }
+ },
/**
* Already up-to-date
*/
- UP_TO_DATE,
+ UP_TO_DATE {
+ @Override
+ public boolean isSuccessful() {
+ return true;
+ }
+ },
/**
* Fast-forward, HEAD points to the new commit
*/
- FAST_FORWARD;
+ FAST_FORWARD {
+ @Override
+ public boolean isSuccessful() {
+ return true;
+ }
+ };
+
+ /**
+ * @return whether the status indicates a successful result
+ */
+ public abstract boolean isSuccessful();
}
static final RebaseResult OK_RESULT = new RebaseResult(Status.OK);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index 79f2297b26..8559483292 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -60,6 +60,7 @@ import org.eclipse.jgit.errors.MissingObjectException;
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.Repository;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
@@ -386,64 +387,68 @@ public class DirCacheCheckout {
MissingObjectException, IncorrectObjectTypeException,
CheckoutConflictException, IndexWriteException {
toBeDeleted.clear();
- if (headCommitTree != null)
- preScanTwoTrees();
- else
- prescanOneTree();
- if (!conflicts.isEmpty()) {
- if (failOnConflict) {
- dc.unlock();
- throw new CheckoutConflictException(conflicts.toArray(new String[conflicts.size()]));
- } else
- cleanUpConflicts();
- }
+ ObjectReader objectReader = repo.getObjectDatabase().newReader();
+ try {
+ if (headCommitTree != null)
+ preScanTwoTrees();
+ else
+ prescanOneTree();
- // update our index
- builder.finish();
-
- File file=null;
- String last = "";
- // when deleting files process them in the opposite order as they have
- // been reported. This ensures the files are deleted before we delete
- // their parent folders
- for (int i = removed.size() - 1; i >= 0; i--) {
- String r = removed.get(i);
- file = new File(repo.getWorkTree(), r);
- if (!file.delete() && file.exists())
- toBeDeleted.add(r);
- else {
- if (!isSamePrefix(r, last))
- removeEmptyParents(new File(repo.getWorkTree(), last));
- last = r;
+ if (!conflicts.isEmpty()) {
+ if (failOnConflict) {
+ dc.unlock();
+ throw new CheckoutConflictException(conflicts.toArray(new String[conflicts.size()]));
+ } else
+ cleanUpConflicts();
}
- }
- if (file != null)
- removeEmptyParents(file);
- for (String path : updated.keySet()) {
- // ... create/overwrite this file ...
- file = new File(repo.getWorkTree(), path);
- if (!file.getParentFile().mkdirs()) {
- // ignore
+ // update our index
+ builder.finish();
+
+ File file = null;
+ String last = "";
+ // when deleting files process them in the opposite order as they have
+ // been reported. This ensures the files are deleted before we delete
+ // their parent folders
+ for (int i = removed.size() - 1; i >= 0; i--) {
+ String r = removed.get(i);
+ file = new File(repo.getWorkTree(), r);
+ if (!file.delete() && file.exists())
+ toBeDeleted.add(r);
+ else {
+ if (!isSamePrefix(r, last))
+ removeEmptyParents(new File(repo.getWorkTree(), last));
+ last = r;
+ }
}
+ if (file != null)
+ removeEmptyParents(file);
+
+ for (String path : updated.keySet()) {
+ // ... create/overwrite this file ...
+ file = new File(repo.getWorkTree(), path);
+ if (!file.getParentFile().mkdirs()) {
+ // ignore
+ }
- DirCacheEntry entry = dc.getEntry(path);
-
- // submodules are handled with separate operations
- if (FileMode.GITLINK.equals(entry.getRawMode()))
- continue;
+ DirCacheEntry entry = dc.getEntry(path);
- checkoutEntry(repo, file, entry);
- }
+ // submodules are handled with separate operations
+ if (FileMode.GITLINK.equals(entry.getRawMode()))
+ continue;
+ checkoutEntry(repo, file, entry, objectReader);
+ }
- // commit the index builder - a new index is persisted
- if (!builder.commit()) {
- dc.unlock();
- throw new IndexWriteException();
+ // commit the index builder - a new index is persisted
+ if (!builder.commit()) {
+ dc.unlock();
+ throw new IndexWriteException();
+ }
+ } finally {
+ objectReader.release();
}
-
return toBeDeleted.size() == 0;
}
@@ -848,22 +853,61 @@ public class DirCacheCheckout {
* Updates the file in the working tree with content and mode from an entry
* in the index. The new content is first written to a new temporary file in
* the same directory as the real file. Then that new file is renamed to the
+ * final filename. Use this method only for checkout of a single entry.
+ * Otherwise use
+ * {@code checkoutEntry(Repository, File f, DirCacheEntry, ObjectReader)}
+ * instead which allows to reuse one {@code ObjectReader} for multiple
+ * entries.
+ *
+ * <p>
+ * TODO: this method works directly on File IO, we may need another
+ * abstraction (like WorkingTreeIterator). This way we could tell e.g.
+ * Eclipse that Files in the workspace got changed
+ * </p>
+ *
+ * @param repository
+ * @param f
+ * the file to be modified. The parent directory for this file
+ * has to exist already
+ * @param entry
+ * the entry containing new mode and content
+ * @throws IOException
+ */
+ public static void checkoutEntry(final Repository repository, File f,
+ DirCacheEntry entry) throws IOException {
+ ObjectReader or = repository.newObjectReader();
+ try {
+ checkoutEntry(repository, f, entry, repository.newObjectReader());
+ } finally {
+ or.release();
+ }
+ }
+
+ /**
+ * Updates the file in the working tree with content and mode from an entry
+ * in the index. The new content is first written to a new temporary file in
+ * the same directory as the real file. Then that new file is renamed to the
* final filename.
*
+ * <p>
* TODO: this method works directly on File IO, we may need another
* abstraction (like WorkingTreeIterator). This way we could tell e.g.
* Eclipse that Files in the workspace got changed
+ * </p>
+ *
* @param repo
* @param f
* the file to be modified. The parent directory for this file
* has to exist already
* @param entry
* the entry containing new mode and content
+ * @param or
+ * object reader to use for checkout
* @throws IOException
*/
public static void checkoutEntry(final Repository repo, File f,
- DirCacheEntry entry) throws IOException {
- ObjectLoader ol = repo.open(entry.getObjectId());
+ DirCacheEntry entry, ObjectReader or) throws IOException {
+ ObjectLoader ol = or.open(entry.getObjectId());
File parentDir = f.getParentFile();
File tmpFile = File.createTempFile("._" + f.getName(), null, parentDir);
FileOutputStream channel = new FileOutputStream(tmpFile);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java
index 59354ddb70..3db720a927 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java
@@ -60,6 +60,16 @@ public class PackInvalidException extends IOException {
* path of the invalid pack file.
*/
public PackInvalidException(final File path) {
- super(MessageFormat.format(JGitText.get().packFileInvalid, path.getAbsolutePath()));
+ this(path.getAbsolutePath());
+ }
+
+ /**
+ * Construct a pack invalid error.
+ *
+ * @param path
+ * path of the invalid pack file.
+ */
+ public PackInvalidException(final String path) {
+ super(MessageFormat.format(JGitText.get().packFileInvalid, path));
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
index aab8a8e619..b51a954ca9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -1137,7 +1137,7 @@ public abstract class Repository {
fos.close();
}
} else {
- FileUtils.delete(mergeMsgFile);
+ FileUtils.delete(mergeMsgFile, FileUtils.SKIP_MISSING);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
index d347b59a0b..b763568951 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -238,19 +238,23 @@ public class ResolveMerger extends ThreeWayMerger {
}
private void checkout() throws NoWorkTreeException, IOException {
- for (Map.Entry<String, DirCacheEntry> entry : toBeCheckedOut.entrySet()) {
- File f = new File(db.getWorkTree(), entry.getKey());
- if (entry.getValue() != null) {
- createDir(f.getParentFile());
- DirCacheCheckout.checkoutEntry(db,
- f,
- entry.getValue());
- } else {
- if (!f.delete())
- failingPaths.put(entry.getKey(),
- MergeFailureReason.COULD_NOT_DELETE);
+ ObjectReader r = db.getObjectDatabase().newReader();
+ try {
+ for (Map.Entry<String, DirCacheEntry> entry : toBeCheckedOut
+ .entrySet()) {
+ File f = new File(db.getWorkTree(), entry.getKey());
+ if (entry.getValue() != null) {
+ createDir(f.getParentFile());
+ DirCacheCheckout.checkoutEntry(db, f, entry.getValue(), r);
+ } else {
+ if (!f.delete())
+ failingPaths.put(entry.getKey(),
+ MergeFailureReason.COULD_NOT_DELETE);
+ }
+ modifiedFiles.add(entry.getKey());
}
- modifiedFiles.add(entry.getKey());
+ } finally {
+ r.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
index 5d93126a96..9aeb44d58f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
@@ -43,6 +43,10 @@
package org.eclipse.jgit.revwalk;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
+import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
+
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
@@ -51,13 +55,12 @@ import java.util.List;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.LargeObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.treewalk.CanonicalTreeParser;
+import org.eclipse.jgit.util.RawParseUtils;
/**
* Specialized subclass of RevWalk to include trees, blobs and tags.
@@ -76,6 +79,13 @@ import org.eclipse.jgit.treewalk.CanonicalTreeParser;
* commits that are returned first.
*/
public class ObjectWalk extends RevWalk {
+ private static final int ID_SZ = 20;
+ private static final int TYPE_SHIFT = 12;
+ private static final int TYPE_TREE = 0040000 >>> TYPE_SHIFT;
+ private static final int TYPE_SYMLINK = 0120000 >>> TYPE_SHIFT;
+ private static final int TYPE_FILE = 0100000 >>> TYPE_SHIFT;
+ private static final int TYPE_GITLINK = 0160000 >>> TYPE_SHIFT;
+
/**
* Indicates a non-RevCommit is in {@link #pendingObjects}.
* <p>
@@ -85,22 +95,24 @@ public class ObjectWalk extends RevWalk {
*/
private static final int IN_PENDING = RevWalk.REWRITE;
- private static final byte[] EMPTY_PATH = {};
-
- private CanonicalTreeParser treeWalk;
-
private List<RevObject> rootObjects;
private BlockObjQueue pendingObjects;
- private RevTree currentTree;
-
- private RevObject last;
-
private RevCommit firstCommit;
private RevCommit lastCommit;
+ private TreeVisit freeVisit;
+
+ private TreeVisit currVisit;
+
+ private byte[] pathBuf;
+
+ private int pathLen;
+
+ private boolean boundary;
+
/**
* Create a new revision and object walker for a given repository.
*
@@ -123,7 +135,7 @@ public class ObjectWalk extends RevWalk {
super(or);
rootObjects = new ArrayList<RevObject>();
pendingObjects = new BlockObjQueue();
- treeWalk = new CanonicalTreeParser();
+ pathBuf = new byte[256];
}
/**
@@ -213,7 +225,7 @@ public class ObjectWalk extends RevWalk {
IncorrectObjectTypeException, IOException {
while (o instanceof RevTag) {
o.flags |= UNINTERESTING;
- if (hasRevSort(RevSort.BOUNDARY))
+ if (boundary)
addObject(o);
o = ((RevTag) o).getObject();
parseHeaders(o);
@@ -226,9 +238,20 @@ public class ObjectWalk extends RevWalk {
else
o.flags |= UNINTERESTING;
- if (o.getType() != Constants.OBJ_COMMIT && hasRevSort(RevSort.BOUNDARY)) {
+ if (o.getType() != OBJ_COMMIT && boundary)
addObject(o);
- }
+ }
+
+ @Override
+ public void sort(RevSort s) {
+ super.sort(s);
+ boundary = hasRevSort(RevSort.BOUNDARY);
+ }
+
+ @Override
+ public void sort(RevSort s, boolean use) {
+ super.sort(s, use);
+ boundary = hasRevSort(RevSort.BOUNDARY);
}
@Override
@@ -236,11 +259,14 @@ public class ObjectWalk extends RevWalk {
IncorrectObjectTypeException, IOException {
for (;;) {
final RevCommit r = super.next();
- if (r == null)
+ if (r == null) {
+ if (firstCommit != null)
+ reader.walkAdviceBeginTrees(this, firstCommit, lastCommit);
return null;
+ }
if ((r.flags & UNINTERESTING) != 0) {
markTreeUninteresting(r.getTree());
- if (hasRevSort(RevSort.BOUNDARY))
+ if (boundary)
return r;
continue;
}
@@ -268,85 +294,180 @@ public class ObjectWalk extends RevWalk {
*/
public RevObject nextObject() throws MissingObjectException,
IncorrectObjectTypeException, IOException {
- if (last != null)
- treeWalk = last instanceof RevTree ? enter(last) : treeWalk.next();
-
- while (!treeWalk.eof()) {
- final FileMode mode = treeWalk.getEntryFileMode();
- switch (mode.getObjectType()) {
- case Constants.OBJ_BLOB: {
- treeWalk.getEntryObjectId(idBuffer);
- final RevBlob o = lookupBlob(idBuffer);
- if ((o.flags & SEEN) != 0)
- break;
- o.flags |= SEEN;
- if (shouldSkipObject(o))
- break;
- last = o;
- return o;
- }
- case Constants.OBJ_TREE: {
- treeWalk.getEntryObjectId(idBuffer);
- final RevTree o = lookupTree(idBuffer);
- if ((o.flags & SEEN) != 0)
- break;
- o.flags |= SEEN;
- if (shouldSkipObject(o))
- break;
- last = o;
- return o;
- }
- default:
- if (FileMode.GITLINK.equals(mode))
- break;
- treeWalk.getEntryObjectId(idBuffer);
- throw new CorruptObjectException(MessageFormat.format(JGitText.get().corruptObjectInvalidMode3
- , mode , idBuffer.name() , treeWalk.getEntryPathString() , currentTree.name()));
- }
+ pathLen = 0;
+
+ TreeVisit tv = currVisit;
+ while (tv != null) {
+ byte[] buf = tv.buf;
+ for (int ptr = tv.ptr; ptr < buf.length;) {
+ int startPtr = ptr;
+ ptr = findObjectId(buf, ptr);
+ idBuffer.fromRaw(buf, ptr);
+ ptr += ID_SZ;
+
+ RevObject obj = objects.get(idBuffer);
+ if (obj != null && (obj.flags & SEEN) != 0)
+ continue;
- treeWalk = treeWalk.next();
- }
+ int mode = parseMode(buf, startPtr, ptr, tv);
+ int flags;
+ switch (mode >>> TYPE_SHIFT) {
+ case TYPE_FILE:
+ case TYPE_SYMLINK:
+ if (obj == null) {
+ obj = new RevBlob(idBuffer);
+ obj.flags = SEEN;
+ objects.add(obj);
+ return obj;
+ }
+ if (!(obj instanceof RevBlob))
+ throw new IncorrectObjectTypeException(obj, OBJ_BLOB);
+ obj.flags = flags = obj.flags | SEEN;
+ if ((flags & UNINTERESTING) == 0)
+ return obj;
+ if (boundary)
+ return obj;
+ continue;
- if (firstCommit != null) {
- reader.walkAdviceBeginTrees(this, firstCommit, lastCommit);
- firstCommit = null;
- lastCommit = null;
+ case TYPE_TREE:
+ if (obj == null) {
+ obj = new RevTree(idBuffer);
+ obj.flags = SEEN;
+ objects.add(obj);
+ return enterTree(obj);
+ }
+ if (!(obj instanceof RevTree))
+ throw new IncorrectObjectTypeException(obj, OBJ_TREE);
+ obj.flags = flags = obj.flags | SEEN;
+ if ((flags & UNINTERESTING) == 0)
+ return enterTree(obj);
+ if (boundary)
+ return enterTree(obj);
+ continue;
+
+ case TYPE_GITLINK:
+ continue;
+
+ default:
+ throw new CorruptObjectException(MessageFormat.format(
+ JGitText.get().corruptObjectInvalidMode3,
+ String.format("%o", mode), idBuffer.name(),
+ RawParseUtils.decode(buf, tv.namePtr, tv.nameEnd),
+ tv.obj));
+ }
+ }
+
+ currVisit = tv.parent;
+ releaseTreeVisit(tv);
+ tv = currVisit;
}
- last = null;
for (;;) {
- final RevObject o = pendingObjects.next();
+ RevObject o = pendingObjects.next();
if (o == null) {
reader.walkAdviceEnd();
return null;
}
- if ((o.flags & SEEN) != 0)
- continue;
- o.flags |= SEEN;
- if (shouldSkipObject(o))
+ int flags = o.flags;
+ if ((flags & SEEN) != 0)
continue;
- if (o instanceof RevTree) {
- currentTree = (RevTree) o;
- treeWalk = treeWalk.resetRoot(reader, currentTree);
+ flags |= SEEN;
+ o.flags = flags;
+ if ((flags & UNINTERESTING) == 0 | boundary) {
+ if (o instanceof RevTree) {
+ tv = newTreeVisit(o);
+ tv.parent = null;
+ currVisit = tv;
+ }
+ return o;
}
- return o;
}
}
- private CanonicalTreeParser enter(RevObject tree) throws IOException {
- CanonicalTreeParser p = treeWalk.createSubtreeIterator0(reader, tree);
- if (p.eof()) {
- // We can't tolerate the subtree being an empty tree, as
- // that will break us out early before we visit all names.
- // If it is, advance to the parent's next record.
- //
- return treeWalk.next();
+ private RevObject enterTree(RevObject obj) throws MissingObjectException,
+ IncorrectObjectTypeException, IOException {
+ TreeVisit tv = newTreeVisit(obj);
+ tv.parent = currVisit;
+ currVisit = tv;
+ return obj;
+ }
+
+ private static int findObjectId(byte[] buf, int ptr) {
+ // Skip over the mode and name until the NUL before the ObjectId
+ // can be located. Skip the NUL as the function returns.
+ for (;;) {
+ if (buf[++ptr] == 0) return ++ptr;
+ if (buf[++ptr] == 0) return ++ptr;
+ if (buf[++ptr] == 0) return ++ptr;
+ if (buf[++ptr] == 0) return ++ptr;
+
+ if (buf[++ptr] == 0) return ++ptr;
+ if (buf[++ptr] == 0) return ++ptr;
+ if (buf[++ptr] == 0) return ++ptr;
+ if (buf[++ptr] == 0) return ++ptr;
+
+ if (buf[++ptr] == 0) return ++ptr;
+ if (buf[++ptr] == 0) return ++ptr;
+ if (buf[++ptr] == 0) return ++ptr;
+ if (buf[++ptr] == 0) return ++ptr;
+
+ if (buf[++ptr] == 0) return ++ptr;
+ if (buf[++ptr] == 0) return ++ptr;
+ if (buf[++ptr] == 0) return ++ptr;
+ if (buf[++ptr] == 0) return ++ptr;
}
- return p;
}
- private final boolean shouldSkipObject(final RevObject o) {
- return (o.flags & UNINTERESTING) != 0 && !hasRevSort(RevSort.BOUNDARY);
+ private static int parseMode(byte[] buf, int startPtr, int recEndPtr, TreeVisit tv) {
+ int mode = buf[startPtr] - '0';
+ for (;;) {
+ byte c = buf[++startPtr];
+ if (' ' == c)
+ break;
+ mode <<= 3;
+ mode += c - '0';
+
+ c = buf[++startPtr];
+ if (' ' == c)
+ break;
+ mode <<= 3;
+ mode += c - '0';
+
+ c = buf[++startPtr];
+ if (' ' == c)
+ break;
+ mode <<= 3;
+ mode += c - '0';
+
+ c = buf[++startPtr];
+ if (' ' == c)
+ break;
+ mode <<= 3;
+ mode += c - '0';
+
+ c = buf[++startPtr];
+ if (' ' == c)
+ break;
+ mode <<= 3;
+ mode += c - '0';
+
+ c = buf[++startPtr];
+ if (' ' == c)
+ break;
+ mode <<= 3;
+ mode += c - '0';
+
+ c = buf[++startPtr];
+ if (' ' == c)
+ break;
+ mode <<= 3;
+ mode += c - '0';
+ }
+
+ tv.ptr = recEndPtr;
+ tv.namePtr = startPtr + 1;
+ tv.nameEnd = recEndPtr - (ID_SZ + 1);
+ return mode;
}
/**
@@ -383,7 +504,7 @@ public class ObjectWalk extends RevWalk {
if (o == null)
break;
if (o instanceof RevBlob && !reader.has(o))
- throw new MissingObjectException(o, Constants.TYPE_BLOB);
+ throw new MissingObjectException(o, OBJ_BLOB);
}
}
@@ -401,7 +522,12 @@ public class ObjectWalk extends RevWalk {
* has no path, such as for annotated tags or root level trees.
*/
public String getPathString() {
- return last != null ? treeWalk.getEntryPathString() : null;
+ if (pathLen == 0) {
+ pathLen = updatePathBuf(currVisit);
+ if (pathLen == 0)
+ return null;
+ }
+ return RawParseUtils.decode(pathBuf, 0, pathLen);
}
/**
@@ -413,28 +539,104 @@ public class ObjectWalk extends RevWalk {
* @return path hash code; any integer may be returned.
*/
public int getPathHashCode() {
- return last != null ? treeWalk.getEntryPathHashCode() : 0;
+ TreeVisit tv = currVisit;
+ if (tv == null)
+ return 0;
+
+ int nameEnd = tv.nameEnd;
+ if (nameEnd == 0) {
+ // When nameEnd == 0 the subtree is itself the current path
+ // being visited. The name hash must be obtained from its
+ // parent tree. If there is no parent, this is a root tree with
+ // a hash code of 0.
+ tv = tv.parent;
+ if (tv == null)
+ return 0;
+ nameEnd = tv.nameEnd;
+ }
+
+ byte[] buf;
+ int ptr;
+
+ if (16 <= (nameEnd - tv.namePtr)) {
+ buf = tv.buf;
+ ptr = nameEnd - 16;
+ } else {
+ nameEnd = pathLen;
+ if (nameEnd == 0) {
+ nameEnd = updatePathBuf(currVisit);
+ pathLen = nameEnd;
+ }
+ buf = pathBuf;
+ ptr = Math.max(0, nameEnd - 16);
+ }
+
+ int hash = 0;
+ for (; ptr < nameEnd; ptr++) {
+ byte c = buf[ptr];
+ if (c != ' ')
+ hash = (hash >>> 2) + (c << 24);
+ }
+ return hash;
}
/** @return the internal buffer holding the current path. */
public byte[] getPathBuffer() {
- return last != null ? treeWalk.getEntryPathBuffer() : EMPTY_PATH;
+ if (pathLen == 0)
+ pathLen = updatePathBuf(currVisit);
+ return pathBuf;
}
/** @return length of the path in {@link #getPathBuffer()}. */
public int getPathLength() {
- return last != null ? treeWalk.getEntryPathLength() : 0;
+ if (pathLen == 0)
+ pathLen = updatePathBuf(currVisit);
+ return pathLen;
+ }
+
+ private int updatePathBuf(TreeVisit tv) {
+ if (tv == null)
+ return 0;
+
+ // If nameEnd == 0 this tree has not yet contributed an entry.
+ // Update only for the parent, which if null will be empty.
+ int nameEnd = tv.nameEnd;
+ if (nameEnd == 0)
+ return updatePathBuf(tv.parent);
+
+ int ptr = tv.pathLen;
+ if (ptr == 0) {
+ ptr = updatePathBuf(tv.parent);
+ if (ptr == pathBuf.length)
+ growPathBuf(ptr);
+ if (ptr != 0)
+ pathBuf[ptr++] = '/';
+ tv.pathLen = ptr;
+ }
+
+ int namePtr = tv.namePtr;
+ int nameLen = nameEnd - namePtr;
+ int end = ptr + nameLen;
+ while (pathBuf.length < end)
+ growPathBuf(ptr);
+ System.arraycopy(tv.buf, namePtr, pathBuf, ptr, nameLen);
+ return end;
+ }
+
+ private void growPathBuf(int ptr) {
+ byte[] newBuf = new byte[pathBuf.length << 1];
+ System.arraycopy(pathBuf, 0, newBuf, 0, ptr);
+ pathBuf = newBuf;
}
@Override
public void dispose() {
super.dispose();
pendingObjects = new BlockObjQueue();
- treeWalk = new CanonicalTreeParser();
- currentTree = null;
- last = null;
firstCommit = null;
lastCommit = null;
+ currVisit = null;
+ freeVisit = null;
}
@Override
@@ -446,11 +648,10 @@ public class ObjectWalk extends RevWalk {
rootObjects = new ArrayList<RevObject>();
pendingObjects = new BlockObjQueue();
- treeWalk = new CanonicalTreeParser();
- currentTree = null;
- last = null;
firstCommit = null;
lastCommit = null;
+ currVisit = null;
+ freeVisit = null;
}
private void addObject(final RevObject o) {
@@ -468,36 +669,90 @@ public class ObjectWalk extends RevWalk {
return;
tree.flags |= UNINTERESTING;
- treeWalk = treeWalk.resetRoot(reader, tree);
- while (!treeWalk.eof()) {
- final FileMode mode = treeWalk.getEntryFileMode();
- final int sType = mode.getObjectType();
+ byte[] raw = reader.open(tree, OBJ_TREE).getCachedBytes();
+ for (int ptr = 0; ptr < raw.length;) {
+ byte c = raw[ptr];
+ int mode = c - '0';
+ for (;;) {
+ c = raw[++ptr];
+ if (' ' == c)
+ break;
+ mode <<= 3;
+ mode += c - '0';
+ }
+ while (raw[++ptr] != 0) {
+ // Skip entry name.
+ }
+ ptr++; // Skip NUL after entry name.
- switch (sType) {
- case Constants.OBJ_BLOB: {
- treeWalk.getEntryObjectId(idBuffer);
+ switch (mode >>> TYPE_SHIFT) {
+ case TYPE_FILE:
+ case TYPE_SYMLINK:
+ idBuffer.fromRaw(raw, ptr);
lookupBlob(idBuffer).flags |= UNINTERESTING;
break;
- }
- case Constants.OBJ_TREE: {
- treeWalk.getEntryObjectId(idBuffer);
- final RevTree t = lookupTree(idBuffer);
- if ((t.flags & UNINTERESTING) == 0) {
- t.flags |= UNINTERESTING;
- treeWalk = treeWalk.createSubtreeIterator0(reader, t);
- continue;
- }
+
+ case TYPE_TREE:
+ idBuffer.fromRaw(raw, ptr);
+ markTreeUninteresting(lookupTree(idBuffer));
break;
- }
+
+ case TYPE_GITLINK:
+ break;
+
default:
- if (FileMode.GITLINK.equals(mode))
- break;
- treeWalk.getEntryObjectId(idBuffer);
- throw new CorruptObjectException(MessageFormat.format(JGitText.get().corruptObjectInvalidMode3
- , mode , idBuffer.name() , treeWalk.getEntryPathString() , tree));
+ idBuffer.fromRaw(raw, ptr);
+ throw new CorruptObjectException(MessageFormat.format(JGitText
+ .get().corruptObjectInvalidMode3, String.format("%o",
+ mode), idBuffer.name(), "", tree));
}
+ ptr += ID_SZ;
+ }
+ }
- treeWalk = treeWalk.next();
+ private TreeVisit newTreeVisit(RevObject obj) throws LargeObjectException,
+ MissingObjectException, IncorrectObjectTypeException, IOException {
+ TreeVisit tv = freeVisit;
+ if (tv != null) {
+ freeVisit = tv.parent;
+ tv.ptr = 0;
+ tv.namePtr = 0;
+ tv.nameEnd = 0;
+ tv.pathLen = 0;
+ } else {
+ tv = new TreeVisit();
}
+ tv.obj = obj;
+ tv.buf = reader.open(obj, OBJ_TREE).getCachedBytes();
+ return tv;
+ }
+
+ private void releaseTreeVisit(TreeVisit tv) {
+ tv.buf = null;
+ tv.parent = freeVisit;
+ freeVisit = tv;
+ }
+
+ private static class TreeVisit {
+ /** Parent tree visit that entered this tree, null if root tree. */
+ TreeVisit parent;
+
+ /** The RevTree currently being iterated through. */
+ RevObject obj;
+
+ /** Canonical encoding of the tree named by {@link #obj}. */
+ byte[] buf;
+
+ /** Index of next entry to parse in {@link #buf}. */
+ int ptr;
+
+ /** Start of the current name entry in {@link #buf}. */
+ int namePtr;
+
+ /** One past end of name, {@code nameEnd - namePtr} is the length. */
+ int nameEnd;
+
+ /** Number of bytes in the path leading up to this tree. */
+ int pathLen;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
index a617dd1dc5..cbf075e72a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -170,7 +170,7 @@ public class RevWalk implements Iterable<RevCommit> {
final MutableObjectId idBuffer;
- private ObjectIdOwnerMap<RevObject> objects;
+ ObjectIdOwnerMap<RevObject> objects;
private int freeFlags = APP_FLAGS;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndex.java
index fc1b748f5e..9b43b39ea9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndex.java
@@ -48,11 +48,13 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.text.MessageFormat;
import java.util.Iterator;
import java.util.Set;
import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.AnyObjectId;
@@ -91,18 +93,7 @@ public abstract class PackIndex implements Iterable<PackIndex.MutableEntry> {
public static PackIndex open(final File idxFile) throws IOException {
final FileInputStream fd = new FileInputStream(idxFile);
try {
- final byte[] hdr = new byte[8];
- IO.readFully(fd, hdr, 0, hdr.length);
- if (isTOC(hdr)) {
- final int v = NB.decodeInt32(hdr, 4);
- switch (v) {
- case 2:
- return new PackIndexV2(fd);
- default:
- throw new IOException(MessageFormat.format(JGitText.get().unsupportedPackIndexVersion, v));
- }
- }
- return new PackIndexV1(fd, hdr);
+ return read(fd);
} catch (IOException ioe) {
final String path = idxFile.getAbsolutePath();
final IOException err;
@@ -118,6 +109,39 @@ public abstract class PackIndex implements Iterable<PackIndex.MutableEntry> {
}
}
+ /**
+ * Read an existing pack index file from a buffered stream.
+ * <p>
+ * The format of the file will be automatically detected and a proper access
+ * implementation for that format will be constructed and returned to the
+ * caller. The file may or may not be held open by the returned instance.
+ *
+ * @param fd
+ * stream to read the index file from. The stream must be
+ * buffered as some small IOs are performed against the stream.
+ * The caller is responsible for closing the stream.
+ * @return a copy of the index in-memory.
+ * @throws IOException
+ * the stream cannot be read.
+ * @throws CorruptObjectException
+ * the stream does not contain a valid pack index.
+ */
+ public static PackIndex read(InputStream fd) throws IOException,
+ CorruptObjectException {
+ final byte[] hdr = new byte[8];
+ IO.readFully(fd, hdr, 0, hdr.length);
+ if (isTOC(hdr)) {
+ final int v = NB.decodeInt32(hdr, 4);
+ switch (v) {
+ case 2:
+ return new PackIndexV2(fd);
+ default:
+ throw new IOException(MessageFormat.format(JGitText.get().unsupportedPackIndexVersion, v));
+ }
+ }
+ return new PackIndexV1(fd, hdr);
+ }
+
private static boolean isTOC(final byte[] h) {
final byte[] toc = PackIndexWriter.TOC;
for (int i = 0; i < toc.length; i++)
@@ -159,7 +183,7 @@ public abstract class PackIndex implements Iterable<PackIndex.MutableEntry> {
* @return number of objects in this index, and likewise in the associated
* pack that this index was generated from.
*/
- abstract long getObjectCount();
+ public abstract long getObjectCount();
/**
* Obtain the total number of objects needing 64 bit offsets.
@@ -167,7 +191,7 @@ public abstract class PackIndex implements Iterable<PackIndex.MutableEntry> {
* @return number of objects in this index using a 64 bit offset; that is an
* object positioned after the 2 GB position within the file.
*/
- abstract long getOffset64Count();
+ public abstract long getOffset64Count();
/**
* Get ObjectId for the n-th object entry returned by {@link #iterator()}.
@@ -188,7 +212,7 @@ public abstract class PackIndex implements Iterable<PackIndex.MutableEntry> {
* {@link MutableEntry} is 0, the second is 1, etc.
* @return the ObjectId for the corresponding entry.
*/
- abstract ObjectId getObjectId(long nthPosition);
+ public abstract ObjectId getObjectId(long nthPosition);
/**
* Get ObjectId for the n-th object entry returned by {@link #iterator()}.
@@ -210,7 +234,7 @@ public abstract class PackIndex implements Iterable<PackIndex.MutableEntry> {
* etc. Positions past 2**31-1 are negative, but still valid.
* @return the ObjectId for the corresponding entry.
*/
- final ObjectId getObjectId(final int nthPosition) {
+ public final ObjectId getObjectId(final int nthPosition) {
if (nthPosition >= 0)
return getObjectId((long) nthPosition);
final int u31 = nthPosition >>> 1;
@@ -227,7 +251,7 @@ public abstract class PackIndex implements Iterable<PackIndex.MutableEntry> {
* object does not exist in this index and is thus not stored in the
* associated pack.
*/
- abstract long findOffset(AnyObjectId objId);
+ public abstract long findOffset(AnyObjectId objId);
/**
* Retrieve stored CRC32 checksum of the requested object raw-data
@@ -241,17 +265,31 @@ public abstract class PackIndex implements Iterable<PackIndex.MutableEntry> {
* @throws UnsupportedOperationException
* when this index doesn't support CRC32 checksum
*/
- abstract long findCRC32(AnyObjectId objId) throws MissingObjectException,
- UnsupportedOperationException;
+ public abstract long findCRC32(AnyObjectId objId)
+ throws MissingObjectException, UnsupportedOperationException;
/**
* Check whether this index supports (has) CRC32 checksums for objects.
*
* @return true if CRC32 is stored, false otherwise
*/
- abstract boolean hasCRC32Support();
+ public abstract boolean hasCRC32Support();
- abstract void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
+ /**
+ * Find objects matching the prefix abbreviation.
+ *
+ * @param matches
+ * set to add any located ObjectIds to. This is an output
+ * parameter.
+ * @param id
+ * prefix to search for.
+ * @param matchLimit
+ * maximum number of results to return. At most this many
+ * ObjectIds should be added to matches before returning.
+ * @throws IOException
+ * the index cannot be read.
+ */
+ public abstract void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
int matchLimit) throws IOException;
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java
index 1c682f17db..4071fb84d1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java
@@ -98,12 +98,13 @@ class PackIndexV1 extends PackIndex {
IO.readFully(fd, packChecksum, 0, packChecksum.length);
}
- long getObjectCount() {
+ @Override
+ public long getObjectCount() {
return objectCnt;
}
@Override
- long getOffset64Count() {
+ public long getOffset64Count() {
long n64 = 0;
for (final MutableEntry e : this) {
if (e.getOffset() >= Integer.MAX_VALUE)
@@ -113,7 +114,7 @@ class PackIndexV1 extends PackIndex {
}
@Override
- ObjectId getObjectId(final long nthPosition) {
+ public ObjectId getObjectId(final long nthPosition) {
int levelOne = Arrays.binarySearch(idxHeader, nthPosition + 1);
long base;
if (levelOne >= 0) {
@@ -135,7 +136,8 @@ class PackIndexV1 extends PackIndex {
return ObjectId.fromRaw(idxdata[levelOne], dataIdx);
}
- long findOffset(final AnyObjectId objId) {
+ @Override
+ public long findOffset(final AnyObjectId objId) {
final int levelOne = objId.getFirstByte();
byte[] data = idxdata[levelOne];
if (data == null)
@@ -161,22 +163,23 @@ class PackIndexV1 extends PackIndex {
}
@Override
- long findCRC32(AnyObjectId objId) {
+ public long findCRC32(AnyObjectId objId) {
throw new UnsupportedOperationException();
}
@Override
- boolean hasCRC32Support() {
+ public boolean hasCRC32Support() {
return false;
}
+ @Override
public Iterator<MutableEntry> iterator() {
return new IndexV1Iterator();
}
@Override
- void resolve(Set<ObjectId> matches, AbbreviatedObjectId id, int matchLimit)
- throws IOException {
+ public void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
+ int matchLimit) throws IOException {
byte[] data = idxdata[id.getFirstByte()];
if (data == null)
return;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java
index abc8767666..44a4044ac3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java
@@ -161,17 +161,17 @@ class PackIndexV2 extends PackIndex {
}
@Override
- long getObjectCount() {
+ public long getObjectCount() {
return objectCnt;
}
@Override
- long getOffset64Count() {
+ public long getOffset64Count() {
return offset64.length / 8;
}
@Override
- ObjectId getObjectId(final long nthPosition) {
+ public ObjectId getObjectId(final long nthPosition) {
int levelOne = Arrays.binarySearch(fanoutTable, nthPosition + 1);
long base;
if (levelOne >= 0) {
@@ -194,7 +194,7 @@ class PackIndexV2 extends PackIndex {
}
@Override
- long findOffset(final AnyObjectId objId) {
+ public long findOffset(final AnyObjectId objId) {
final int levelOne = objId.getFirstByte();
final int levelTwo = binarySearchLevelTwo(objId, levelOne);
if (levelTwo == -1)
@@ -206,7 +206,7 @@ class PackIndexV2 extends PackIndex {
}
@Override
- long findCRC32(AnyObjectId objId) throws MissingObjectException {
+ public long findCRC32(AnyObjectId objId) throws MissingObjectException {
final int levelOne = objId.getFirstByte();
final int levelTwo = binarySearchLevelTwo(objId, levelOne);
if (levelTwo == -1)
@@ -215,17 +215,18 @@ class PackIndexV2 extends PackIndex {
}
@Override
- boolean hasCRC32Support() {
+ public boolean hasCRC32Support() {
return true;
}
+ @Override
public Iterator<MutableEntry> iterator() {
return new EntriesIteratorV2();
}
@Override
- void resolve(Set<ObjectId> matches, AbbreviatedObjectId id, int matchLimit)
- throws IOException {
+ public void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
+ int matchLimit) throws IOException {
int[] data = names[id.getFirstByte()];
int max = offset32[id.getFirstByte()].length >>> 2;
int high = max;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java
index 21ebd1ca9c..7671cd6f1c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java
@@ -56,6 +56,9 @@ import org.eclipse.jgit.util.NB;
* @see PackIndexV2
*/
class PackIndexWriterV2 extends PackIndexWriter {
+ private static final int MAX_OFFSET_32 = 0x7fffffff;
+ private static final int IS_OFFSET_64 = 0x80000000;
+
PackIndexWriterV2(final OutputStream dst) {
super(dst);
}
@@ -87,10 +90,10 @@ class PackIndexWriterV2 extends PackIndexWriter {
int o64 = 0;
for (final PackedObjectInfo oe : entries) {
final long o = oe.getOffset();
- if (o < Integer.MAX_VALUE)
+ if (o <= MAX_OFFSET_32)
NB.encodeInt32(tmp, 0, (int) o);
else
- NB.encodeInt32(tmp, 0, (1 << 31) | o64++);
+ NB.encodeInt32(tmp, 0, IS_OFFSET_64 | o64++);
out.write(tmp, 0, 4);
}
}
@@ -98,7 +101,7 @@ class PackIndexWriterV2 extends PackIndexWriter {
private void writeOffset64() throws IOException {
for (final PackedObjectInfo oe : entries) {
final long o = oe.getOffset();
- if (o > Integer.MAX_VALUE) {
+ if (MAX_OFFSET_32 < o) {
NB.encodeInt64(tmp, 0, o);
out.write(tmp, 0, 8);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java
index 96abaeefd3..ccd621c935 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java
@@ -61,7 +61,7 @@ import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
* @see PackIndex
* @see PackFile
*/
-class PackReverseIndex {
+public class PackReverseIndex {
/** Index we were created from, and that has our ObjectId data. */
private final PackIndex index;
@@ -88,7 +88,7 @@ class PackReverseIndex {
* @param packIndex
* forward index - entries to (reverse) index.
*/
- PackReverseIndex(final PackIndex packIndex) {
+ public PackReverseIndex(final PackIndex packIndex) {
index = packIndex;
final long cnt = index.getObjectCount();
@@ -135,7 +135,7 @@ class PackReverseIndex {
* start offset of object to find.
* @return object id for this offset, or null if no object was found.
*/
- ObjectId findObject(final long offset) {
+ public ObjectId findObject(final long offset) {
if (offset <= Integer.MAX_VALUE) {
final int i32 = Arrays.binarySearch(offsets32, (int) offset);
if (i32 < 0)
@@ -164,7 +164,7 @@ class PackReverseIndex {
* @throws CorruptObjectException
* when there is no object with the provided offset.
*/
- long findNextOffset(final long offset, final long maxOffset)
+ public long findNextOffset(final long offset, final long maxOffset)
throws CorruptObjectException {
if (offset <= Integer.MAX_VALUE) {
final int i32 = Arrays.binarySearch(offsets32, (int) offset);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java
index 0590f905ec..3917ff4716 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java
@@ -385,10 +385,17 @@ public class RefDirectory extends RefDatabase {
if (entries == null) // not a directory or an I/O error
return false;
if (0 < entries.length) {
+ for (int i = 0; i < entries.length; ++i) {
+ String e = entries[i];
+ File f = new File(dir, e);
+ if (f.isDirectory())
+ entries[i] += '/';
+ }
Arrays.sort(entries);
for (String name : entries) {
- File e = new File(dir, name);
- if (!scanTree(prefix + name + '/', e))
+ if (name.charAt(name.length() - 1) == '/')
+ scanTree(prefix + name, new File(dir, name));
+ else
scanOne(prefix + name);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
index 7206bb8b0d..91275dc098 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
@@ -190,6 +190,29 @@ public class PackConfig {
}
/**
+ * Copy an existing configuration to a new instance.
+ *
+ * @param cfg
+ * the source configuration to copy from.
+ */
+ public PackConfig(PackConfig cfg) {
+ this.compressionLevel = cfg.compressionLevel;
+ this.reuseDeltas = cfg.reuseDeltas;
+ this.reuseObjects = cfg.reuseObjects;
+ this.deltaBaseAsOffset = cfg.deltaBaseAsOffset;
+ this.deltaCompress = cfg.deltaCompress;
+ this.maxDeltaDepth = cfg.maxDeltaDepth;
+ this.deltaSearchWindowSize = cfg.deltaSearchWindowSize;
+ this.deltaSearchMemoryLimit = cfg.deltaSearchMemoryLimit;
+ this.deltaCacheSize = cfg.deltaCacheSize;
+ this.deltaCacheLimit = cfg.deltaCacheLimit;
+ this.bigFileThreshold = cfg.bigFileThreshold;
+ this.threads = cfg.threads;
+ this.executor = cfg.executor;
+ this.indexVersion = cfg.indexVersion;
+ }
+
+ /**
* Check whether to reuse deltas existing in repository.
*
* Default setting: {@value #DEFAULT_REUSE_DELTAS}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java
index 8a912aeb6b..8c3d52afcd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java
@@ -80,6 +80,7 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.AsyncObjectSizeQueue;
+import org.eclipse.jgit.lib.BatchingProgressMonitor;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
@@ -640,10 +641,21 @@ public class PackWriter {
if (writeMonitor == null)
writeMonitor = NullProgressMonitor.INSTANCE;
- if (reuseSupport != null && (
+ boolean needSearchForReuse = reuseSupport != null && (
reuseDeltas
|| config.isReuseObjects()
- || !cachedPacks.isEmpty()))
+ || !cachedPacks.isEmpty());
+
+ if (compressMonitor instanceof BatchingProgressMonitor) {
+ long delay = 1000;
+ if (needSearchForReuse && config.isDeltaCompress())
+ delay = 500;
+ ((BatchingProgressMonitor) compressMonitor).setDelayStart(
+ delay,
+ TimeUnit.MILLISECONDS);
+ }
+
+ if (needSearchForReuse)
searchForReuse(compressMonitor);
if (config.isDeltaCompress())
searchForDeltas(compressMonitor);
@@ -897,10 +909,10 @@ public class PackWriter {
private int findObjectsNeedingDelta(ObjectToPack[] list, int cnt, int type) {
for (ObjectToPack otp : objectsLists[type]) {
- if (otp.isReuseAsIs()) // already reusing a representation
- continue;
if (otp.isDoNotDelta()) // delta is disabled for this path
continue;
+ if (otp.isDeltaRepresentation()) // already reusing a delta
+ continue;
otp.setWeight(0);
list[cnt++] = otp;
}
@@ -1425,20 +1437,29 @@ public class PackWriter {
}
commits = null;
- BaseSearch bases = new BaseSearch(countingMonitor, baseTrees, //
- objectsMap, edgeObjects, reader);
- RevObject o;
- while ((o = walker.nextObject()) != null) {
- if (o.has(RevFlag.UNINTERESTING))
- continue;
-
- int pathHash = walker.getPathHashCode();
- byte[] pathBuf = walker.getPathBuffer();
- int pathLen = walker.getPathLength();
+ if (thin && !baseTrees.isEmpty()) {
+ BaseSearch bases = new BaseSearch(countingMonitor, baseTrees, //
+ objectsMap, edgeObjects, reader);
+ RevObject o;
+ while ((o = walker.nextObject()) != null) {
+ if (o.has(RevFlag.UNINTERESTING))
+ continue;
- bases.addBase(o.getType(), pathBuf, pathLen, pathHash);
- addObject(o, pathHash);
- countingMonitor.update(1);
+ int pathHash = walker.getPathHashCode();
+ byte[] pathBuf = walker.getPathBuffer();
+ int pathLen = walker.getPathLength();
+ bases.addBase(o.getType(), pathBuf, pathLen, pathHash);
+ addObject(o, pathHash);
+ countingMonitor.update(1);
+ }
+ } else {
+ RevObject o;
+ while ((o = walker.nextObject()) != null) {
+ if (o.has(RevFlag.UNINTERESTING))
+ continue;
+ addObject(o, walker.getPathHashCode());
+ countingMonitor.update(1);
+ }
}
for (CachedPack pack : cachedPacks)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
index 4bbe3a0048..1b30e859ec 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
@@ -54,6 +54,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
@@ -61,6 +62,7 @@ import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.BatchingProgressMonitor;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.InflaterCache;
import org.eclipse.jgit.lib.MutableObjectId;
@@ -478,6 +480,12 @@ public abstract class PackParser {
if (!deferredCheckBlobs.isEmpty())
doDeferredCheckBlobs();
if (deltaCount > 0) {
+ if (resolving instanceof BatchingProgressMonitor) {
+ ((BatchingProgressMonitor) resolving).setDelayStart(
+ 1000,
+ TimeUnit.MILLISECONDS);
+ }
+ resolving.beginTask(JGitText.get().resolvingDeltas, deltaCount);
resolveDeltas(resolving);
if (entryCount < objectCount) {
if (!isAllowThin()) {
@@ -494,6 +502,7 @@ public abstract class PackParser {
(objectCount - entryCount)));
}
}
+ resolving.endTask();
}
packDigest = null;
@@ -511,7 +520,6 @@ public abstract class PackParser {
inflater.release();
} finally {
inflater = null;
- objectDatabase.close();
}
}
return null; // By default there is no locking.
@@ -519,20 +527,17 @@ public abstract class PackParser {
private void resolveDeltas(final ProgressMonitor progress)
throws IOException {
- progress.beginTask(JGitText.get().resolvingDeltas, deltaCount);
final int last = entryCount;
for (int i = 0; i < last; i++) {
- final int before = entryCount;
- resolveDeltas(entries[i]);
- progress.update(entryCount - before);
+ resolveDeltas(entries[i], progress);
if (progress.isCancelled())
throw new IOException(
JGitText.get().downloadCancelledDuringIndexing);
}
- progress.endTask();
}
- private void resolveDeltas(final PackedObjectInfo oe) throws IOException {
+ private void resolveDeltas(final PackedObjectInfo oe,
+ ProgressMonitor progress) throws IOException {
UnresolvedDelta children = firstChildOf(oe);
if (children == null)
return;
@@ -560,12 +565,14 @@ public abstract class PackParser {
.getOffset()));
}
- resolveDeltas(visit.next(), info.type, info);
+ resolveDeltas(visit.next(), info.type, info, progress);
}
private void resolveDeltas(DeltaVisit visit, final int type,
- ObjectTypeAndSize info) throws IOException {
+ ObjectTypeAndSize info, ProgressMonitor progress)
+ throws IOException {
do {
+ progress.update(1);
info = openDatabase(visit.delta, info);
switch (info.type) {
case Constants.OBJ_OFS_DELTA:
@@ -750,7 +757,8 @@ public abstract class PackParser {
entries[entryCount++] = oe;
visit.nextChild = firstChildOf(oe);
- resolveDeltas(visit.next(), typeCode, new ObjectTypeAndSize());
+ resolveDeltas(visit.next(), typeCode,
+ new ObjectTypeAndSize(), progress);
if (progress.isCancelled())
throw new IOException(