From 67edd3eda77fb98673065501a2cf7c9d39db668c Mon Sep 17 00:00:00 2001 From: Marc Strapetz Date: Mon, 29 Oct 2012 16:34:33 +0100 Subject: [PATCH] RevWalk support for shallow clones StartGenerator now processes .git/shallow to have the RevWalk stop for shallow commits. See RevWalkShallowTest for tests. Bug: 394543 CQ: 6908 Change-Id: Ia5af1dab3fe9c7888f44eeecab1e1bcf2e8e48fe Signed-off-by: Chris Aniszczyk --- .../eclipse/jgit/revwalk/FooterLineTest.java | 42 ++-- .../jgit/revwalk/RevWalkShallowTest.java | 195 ++++++++++++++++++ .../eclipse/jgit/internal/JGitText.properties | 1 + .../org/eclipse/jgit/internal/JGitText.java | 1 + .../src/org/eclipse/jgit/lib/Constants.java | 3 + .../org/eclipse/jgit/lib/ObjectReader.java | 9 + .../org/eclipse/jgit/revwalk/RevCommit.java | 18 +- .../src/org/eclipse/jgit/revwalk/RevWalk.java | 17 ++ .../eclipse/jgit/storage/dfs/DfsReader.java | 6 + .../storage/file/CachedObjectDirectory.java | 5 + .../jgit/storage/file/FileObjectDatabase.java | 2 + .../jgit/storage/file/FileRepository.java | 3 +- .../jgit/storage/file/ObjectDirectory.java | 38 +++- .../jgit/storage/file/WindowCursor.java | 6 + 14 files changed, 318 insertions(+), 28 deletions(-) create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkShallowTest.java diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java index 7f440df22..1720b26bb 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java @@ -48,7 +48,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; - +import java.io.IOException; import java.util.List; import org.eclipse.jgit.lib.Constants; @@ -58,7 +58,7 @@ import org.junit.Test; public class FooterLineTest extends RepositoryTestCase { @Test - public void testNoFooters_EmptyBody() { + public void testNoFooters_EmptyBody() throws IOException { final RevCommit commit = parse(""); final List footers = commit.getFooterLines(); assertNotNull(footers); @@ -66,7 +66,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testNoFooters_NewlineOnlyBody1() { + public void testNoFooters_NewlineOnlyBody1() throws IOException { final RevCommit commit = parse("\n"); final List footers = commit.getFooterLines(); assertNotNull(footers); @@ -74,7 +74,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testNoFooters_NewlineOnlyBody5() { + public void testNoFooters_NewlineOnlyBody5() throws IOException { final RevCommit commit = parse("\n\n\n\n\n"); final List footers = commit.getFooterLines(); assertNotNull(footers); @@ -82,7 +82,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testNoFooters_OneLineBodyNoLF() { + public void testNoFooters_OneLineBodyNoLF() throws IOException { final RevCommit commit = parse("this is a commit"); final List footers = commit.getFooterLines(); assertNotNull(footers); @@ -90,7 +90,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testNoFooters_OneLineBodyWithLF() { + public void testNoFooters_OneLineBodyWithLF() throws IOException { final RevCommit commit = parse("this is a commit\n"); final List footers = commit.getFooterLines(); assertNotNull(footers); @@ -98,7 +98,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testNoFooters_ShortBodyNoLF() { + public void testNoFooters_ShortBodyNoLF() throws IOException { final RevCommit commit = parse("subject\n\nbody of commit"); final List footers = commit.getFooterLines(); assertNotNull(footers); @@ -106,7 +106,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testNoFooters_ShortBodyWithLF() { + public void testNoFooters_ShortBodyWithLF() throws IOException { final RevCommit commit = parse("subject\n\nbody of commit\n"); final List footers = commit.getFooterLines(); assertNotNull(footers); @@ -114,7 +114,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testSignedOffBy_OneUserNoLF() { + public void testSignedOffBy_OneUserNoLF() throws IOException { final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n" + "Signed-off-by: A. U. Thor "); final List footers = commit.getFooterLines(); @@ -130,7 +130,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testSignedOffBy_OneUserWithLF() { + public void testSignedOffBy_OneUserWithLF() throws IOException { final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n" + "Signed-off-by: A. U. Thor \n"); final List footers = commit.getFooterLines(); @@ -146,7 +146,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testSignedOffBy_IgnoreWhitespace() { + public void testSignedOffBy_IgnoreWhitespace() throws IOException { // We only ignore leading whitespace on the value, trailing // is assumed part of the value. // @@ -165,7 +165,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testEmptyValueNoLF() { + public void testEmptyValueNoLF() throws IOException { final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n" + "Signed-off-by:"); final List footers = commit.getFooterLines(); @@ -181,7 +181,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testEmptyValueWithLF() { + public void testEmptyValueWithLF() throws IOException { final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n" + "Signed-off-by:\n"); final List footers = commit.getFooterLines(); @@ -197,7 +197,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testShortKey() { + public void testShortKey() throws IOException { final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n" + "K:V\n"); final List footers = commit.getFooterLines(); @@ -213,7 +213,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testNonDelimtedEmail() { + public void testNonDelimtedEmail() throws IOException { final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n" + "Acked-by: re@example.com\n"); final List footers = commit.getFooterLines(); @@ -229,7 +229,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testNotEmail() { + public void testNotEmail() throws IOException { final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n" + "Acked-by: Main Tain Er\n"); final List footers = commit.getFooterLines(); @@ -245,7 +245,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testSignedOffBy_ManyUsers() { + public void testSignedOffBy_ManyUsers() throws IOException { final RevCommit commit = parse("subject\n\nbody of commit\n" + "Not-A-Footer-Line: this line must not be read as a footer\n" + "\n" // paragraph break, now footers appear in final block @@ -281,7 +281,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testSignedOffBy_SkipNonFooter() { + public void testSignedOffBy_SkipNonFooter() throws IOException { final RevCommit commit = parse("subject\n\nbody of commit\n" + "Not-A-Footer-Line: this line must not be read as a footer\n" + "\n" // paragraph break, now footers appear in final block @@ -314,7 +314,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testFilterFootersIgnoreCase() { + public void testFilterFootersIgnoreCase() throws IOException { final RevCommit commit = parse("subject\n\nbody of commit\n" + "Not-A-Footer-Line: this line must not be read as a footer\n" + "\n" // paragraph break, now footers appear in final block @@ -332,7 +332,7 @@ public class FooterLineTest extends RepositoryTestCase { } @Test - public void testMatchesBugId() { + public void testMatchesBugId() throws IOException { final RevCommit commit = parse("this is a commit subject for test\n" + "\n" // paragraph break, now footers appear in final block + "Simple-Bug-Id: 42\n"); @@ -352,7 +352,7 @@ public class FooterLineTest extends RepositoryTestCase { assertFalse("not CC", line.matches(FooterKey.CC)); } - private RevCommit parse(final String msg) { + private RevCommit parse(final String msg) throws IOException { final StringBuilder buf = new StringBuilder(); buf.append("tree " + ObjectId.zeroId().name() + "\n"); buf.append("author A. U. Thor 1 +0000\n"); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkShallowTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkShallowTest.java new file mode 100644 index 000000000..479d9d304 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkShallowTest.java @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2012, Marc Strapetz + * 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.revwalk; + +import java.io.File; +import java.io.IOException; + +import org.eclipse.jgit.junit.JGitTestUtil; +import org.eclipse.jgit.lib.*; +import org.junit.*; + +import static org.junit.Assert.*; + +public class RevWalkShallowTest extends RevWalkTestCase { + + // Accessing ============================================================== + + @Test + public void testDepth1() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + final RevCommit d = commit(c); + + createShallowFile(d); + + rw.reset(); + markStart(d); + assertCommit(d, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testDepth2() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + final RevCommit d = commit(c); + + createShallowFile(c); + + rw.reset(); + markStart(d); + assertCommit(d, rw.next()); + assertCommit(c, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testDepth3() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + final RevCommit d = commit(c); + + createShallowFile(b); + + rw.reset(); + markStart(d); + assertCommit(d, rw.next()); + assertCommit(c, rw.next()); + assertCommit(b, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testMergeCommitOneParentShallow() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + final RevCommit d = commit(b); + final RevCommit e = commit(d); + final RevCommit merge = commit(c, e); + + createShallowFile(e); + + rw.reset(); + markStart(merge); + assertCommit(merge, rw.next()); + assertCommit(e, rw.next()); + assertCommit(c, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testMergeCommitEntirelyShallow() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + final RevCommit d = commit(b); + final RevCommit e = commit(d); + final RevCommit merge = commit(c, e); + + createShallowFile(c, e); + + rw.reset(); + markStart(merge); + assertCommit(merge, rw.next()); + assertCommit(e, rw.next()); + assertCommit(c, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testObjectDirectorySnapshot() throws Exception { + RevCommit a = commit(); + RevCommit b = commit(a); + RevCommit c = commit(b); + RevCommit d = commit(c); + + createShallowFile(d); + + rw.reset(); + markStart(d); + assertCommit(d, rw.next()); + assertNull(rw.next()); + + rw = createRevWalk(); + a = rw.lookupCommit(a); + b = rw.lookupCommit(b); + c = rw.lookupCommit(c); + d = rw.lookupCommit(d); + + rw.reset(); + markStart(d); + assertCommit(d, rw.next()); + assertNull(rw.next()); + + createShallowFile(c); + + rw = createRevWalk(); + a = rw.lookupCommit(a); + b = rw.lookupCommit(b); + c = rw.lookupCommit(c); + d = rw.lookupCommit(d); + + rw.reset(); + markStart(d); + assertCommit(d, rw.next()); + assertCommit(c, rw.next()); + assertNull(rw.next()); + } + + private void createShallowFile(ObjectId... shallowCommits) + throws IOException { + final StringBuilder builder = new StringBuilder(); + for (ObjectId commit : shallowCommits) + builder.append(commit.getName() + "\n"); + JGitTestUtil.write(new File(rw.repository.getDirectory(), "shallow"), + builder.toString()); + } +} diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index e7b1fe895..327026788 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -423,6 +423,7 @@ sequenceTooLargeForDiffAlgorithm=Sequence too large for difference algorithm. serviceNotEnabledNoName=Service not enabled serviceNotPermitted={0} not permitted serviceNotPermittedNoName=Service not permitted +shallowCommitsAlreadyInitialized=Shallow commits have already been initialized shortCompressedStreamAt=Short compressed stream at {0} shortReadOfBlock=Short read of block. shortReadOfOptionalDIRCExtensionExpectedAnotherBytes=Short read of optional DIRC extension {0}; expected another {1} bytes within the section. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index 48963253c..7caeba8be 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -484,6 +484,7 @@ public class JGitText extends TranslationBundle { /***/ public String serviceNotEnabledNoName; /***/ public String serviceNotPermitted; /***/ public String serviceNotPermittedNoName; + /***/ public String shallowCommitsAlreadyInitialized; /***/ public String shortCompressedStreamAt; /***/ public String shortReadOfBlock; /***/ public String shortReadOfOptionalDIRCExtensionExpectedAnotherBytes; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java index d5be3157d..e66e3a835 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java @@ -340,6 +340,9 @@ public final class Constants { /** Name of the submodules file */ public static final String DOT_GIT_MODULES = ".gitmodules"; + /** Name of the .git/shallow file */ + public static final String SHALLOW = "shallow"; + /** * Create a new digest function for objects. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java index cd3706bbe..ee4f3d9e0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java @@ -48,6 +48,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.Set; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; @@ -250,6 +251,14 @@ public abstract class ObjectReader { throws MissingObjectException, IncorrectObjectTypeException, IOException; + /** + * Returns IDs for those commits which should be considered as shallow. + * + * @return IDs of shallow commits + * @throws IOException + */ + public abstract Set getShallowCommits() throws IOException; + /** * Asynchronous object opening. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java index 05c173888..636301441 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java @@ -80,7 +80,11 @@ public class RevCommit extends RevObject { * available to the caller. */ public static RevCommit parse(byte[] raw) { - return parse(new RevWalk((ObjectReader) null), raw); + try { + return parse(new RevWalk((ObjectReader) null), raw); + } catch (IOException ex) { + throw new RuntimeException(ex); + } } /** @@ -88,7 +92,7 @@ public class RevCommit extends RevObject { * * This method inserts the commit directly into the caller supplied revision * pool, making it appear as though the commit exists in the repository, - * even if it doesn't. The repository under the pool is not affected. + * even if it doesn't. The repository under the pool is not affected. * * @param rw * the revision pool to allocate the commit within. The commit's @@ -97,8 +101,10 @@ public class RevCommit extends RevObject { * the canonical formatted commit to be parsed. * @return the parsed commit, in an isolated revision pool that is not * available to the caller. + * @throws IOException + * in case of RevWalk initialization fails */ - public static RevCommit parse(RevWalk rw, byte[] raw) { + public static RevCommit parse(RevWalk rw, byte[] raw) throws IOException { ObjectInserter.Formatter fmt = new ObjectInserter.Formatter(); boolean retain = rw.isRetainBody(); rw.setRetainBody(true); @@ -146,7 +152,11 @@ public class RevCommit extends RevObject { } } - void parseCanonical(final RevWalk walk, final byte[] raw) { + void parseCanonical(final RevWalk walk, final byte[] raw) + throws IOException { + if (!walk.shallowCommitsInitialized) + walk.initializeShallowCommits(); + final MutableObjectId idBuffer = walk.idBuffer; idBuffer.fromString(raw, 5); tree = walk.lookupTree(idBuffer); 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 93428f985..8e07048e2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java @@ -192,6 +192,8 @@ public class RevWalk implements Iterable { private boolean retainBody; + boolean shallowCommitsInitialized; + /** * Create a new revision walker for a given repository. * @@ -1209,6 +1211,7 @@ public class RevWalk implements Iterable { roots.clear(); queue = new DateRevQueue(); pending = new StartGenerator(this); + shallowCommitsInitialized = false; } /** @@ -1312,4 +1315,18 @@ public class RevWalk implements Iterable { if (carry != 0) RevCommit.carryFlags(c, carry); } + + void initializeShallowCommits() throws IOException { + if (shallowCommitsInitialized) + throw new IllegalStateException( + JGitText.get().shallowCommitsAlreadyInitialized); + + shallowCommitsInitialized = true; + + if (reader == null) + return; + + for (ObjectId id : reader.getShallowCommits()) + lookupCommit(id).parents = RevCommit.NO_PARENTS; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReader.java index 6a76258ed..b5244d043 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReader.java @@ -61,6 +61,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.zip.DataFormatException; import java.util.zip.Inflater; @@ -192,6 +193,11 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs { throw new MissingObjectException(objectId.copy(), typeHint); } + @Override + public Set getShallowCommits() { + return Collections.emptySet(); + } + private static final Comparator> FOUND_OBJECT_SORT = new Comparator>() { public int compare(FoundObject a, FoundObject b) { int cmp = a.packIndex - b.packIndex; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java index 8e87b08ee..37ab0e086 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java @@ -141,6 +141,11 @@ class CachedObjectDirectory extends FileObjectDatabase { return wrapped.getFS(); } + @Override + Set getShallowCommits() throws IOException { + return wrapped.getShallowCommits(); + } + @Override Collection getCachedPacks() throws IOException { return wrapped.getCachedPacks(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java index f28facbec..ffbd2edfb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java @@ -137,6 +137,8 @@ abstract class FileObjectDatabase extends ObjectDatabase { abstract FS getFS(); + abstract Set getShallowCommits() throws IOException; + /** * Open an object from this database. *

diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java index 33a51269d..2393e607e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java @@ -178,7 +178,8 @@ public class FileRepository extends Repository { objectDatabase = new ObjectDirectory(repoConfig, // options.getObjectDirectory(), // options.getAlternateObjectDirectories(), // - getFS()); + getFS(), // + new File(getDirectory(), Constants.SHALLOW)); if (objectDatabase.exists()) { final long repositoryFormatVersion = getConfig().getLong( diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java index eab6ed6e7..7b5d3a63c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java @@ -127,6 +127,12 @@ public class ObjectDirectory extends FileObjectDatabase { private final UnpackedObjectCache unpackedObjectCache; + private final File shallowFile; + + private FileSnapshot shallowFileSnapshot = FileSnapshot.DIRTY; + + private Set shallowCommitsIds; + /** * Initialize a reference to an on-disk object directory. * @@ -139,11 +145,14 @@ public class ObjectDirectory extends FileObjectDatabase { * @param fs * the file system abstraction which will be necessary to perform * certain file system operations. + * @param shallowFile + * file which contains IDs of shallow commits, null if shallow + * commits handling should be turned off * @throws IOException * an alternate object cannot be opened. */ public ObjectDirectory(final Config cfg, final File dir, - File[] alternatePaths, FS fs) throws IOException { + File[] alternatePaths, FS fs, File shallowFile) throws IOException { config = cfg; objects = dir; infoDirectory = new File(objects, "info"); @@ -154,6 +163,7 @@ public class ObjectDirectory extends FileObjectDatabase { cachedPacks = new AtomicReference(); unpackedObjectCache = new UnpackedObjectCache(); this.fs = fs; + this.shallowFile = shallowFile; alternates = new AtomicReference(); if (alternatePaths != null) { @@ -614,6 +624,30 @@ public class ObjectDirectory extends FileObjectDatabase { return fs; } + @Override + Set getShallowCommits() throws IOException { + if (shallowFile == null || !shallowFile.isFile()) + return Collections.emptySet(); + + if (shallowFileSnapshot == null + || shallowFileSnapshot.isModified(shallowFile)) { + shallowCommitsIds = new HashSet(); + + final BufferedReader reader = open(shallowFile); + try { + String line; + while ((line = reader.readLine()) != null) + shallowCommitsIds.add(ObjectId.fromString(line)); + } finally { + reader.close(); + } + + shallowFileSnapshot = FileSnapshot.save(shallowFile); + } + + return shallowCommitsIds; + } + private void insertPack(final PackFile pf) { PackList o, n; do { @@ -829,7 +863,7 @@ public class ObjectDirectory extends FileObjectDatabase { return new AlternateRepository(db); } - ObjectDirectory db = new ObjectDirectory(config, objdir, null, fs); + ObjectDirectory db = new ObjectDirectory(config, objdir, null, fs, null); return new AlternateHandle(db); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java index 0e9970fcc..5555a3c40 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java @@ -52,6 +52,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.zip.DataFormatException; import java.util.zip.Inflater; @@ -130,6 +131,11 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs { return ldr; } + @Override + public Set getShallowCommits() throws IOException { + return db.getShallowCommits(); + } + public long getObjectSize(AnyObjectId objectId, int typeHint) throws MissingObjectException, IncorrectObjectTypeException, IOException {