By moving the logic that parses a pack stream from the network (or a bundle) into a type that can be constructed by an ObjectInserter, repository implementations have a chance to inject their own logic for storing object data received into the destination repository. The API isn't completely generic yet, there are still quite a few assumptions that the PackParser subclass is storing the data onto the local filesystem as a single file. But its about the simplest split of IndexPack I can come up with without completely ripping the code apart. Change-Id: I5b167c9cc6d7a7c56d0197c62c0fd0036a83ec6c Signed-off-by: Shawn O. Pearce <spearce@spearce.org> Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>tags/v0.11.1
package org.eclipse.jgit.pgm; | package org.eclipse.jgit.pgm; | ||||
import java.io.BufferedInputStream; | import java.io.BufferedInputStream; | ||||
import java.io.File; | |||||
import org.kohsuke.args4j.Argument; | |||||
import org.kohsuke.args4j.Option; | |||||
import org.eclipse.jgit.lib.CoreConfig; | |||||
import org.eclipse.jgit.lib.ObjectInserter; | |||||
import org.eclipse.jgit.lib.TextProgressMonitor; | import org.eclipse.jgit.lib.TextProgressMonitor; | ||||
import org.eclipse.jgit.storage.file.ObjectDirectoryPackParser; | |||||
import org.eclipse.jgit.transport.PackParser; | |||||
import org.kohsuke.args4j.Option; | |||||
class IndexPack extends TextBuiltin { | class IndexPack extends TextBuiltin { | ||||
@Option(name = "--fix-thin", usage = "usage_fixAThinPackToBeComplete") | @Option(name = "--fix-thin", usage = "usage_fixAThinPackToBeComplete") | ||||
@Option(name = "--index-version", usage = "usage_indexFileFormatToCreate") | @Option(name = "--index-version", usage = "usage_indexFileFormatToCreate") | ||||
private int indexVersion = -1; | private int indexVersion = -1; | ||||
@Argument(index = 0, required = true, metaVar = "metaVar_base") | |||||
private File base; | |||||
@Override | @Override | ||||
protected void run() throws Exception { | protected void run() throws Exception { | ||||
if (indexVersion == -1) | |||||
indexVersion = db.getConfig().get(CoreConfig.KEY) | |||||
.getPackIndexVersion(); | |||||
final BufferedInputStream in; | |||||
final org.eclipse.jgit.transport.IndexPack ip; | |||||
in = new BufferedInputStream(System.in); | |||||
ip = new org.eclipse.jgit.transport.IndexPack(db, in, base); | |||||
ip.setFixThin(fixThin); | |||||
ip.setIndexVersion(indexVersion); | |||||
ip.index(new TextProgressMonitor()); | |||||
BufferedInputStream in = new BufferedInputStream(System.in); | |||||
ObjectInserter inserter = db.newObjectInserter(); | |||||
try { | |||||
PackParser p = inserter.newPackParser(in); | |||||
p.setAllowThin(fixThin); | |||||
if (indexVersion != -1 && p instanceof ObjectDirectoryPackParser) { | |||||
ObjectDirectoryPackParser imp = (ObjectDirectoryPackParser) p; | |||||
imp.setIndexVersion(indexVersion); | |||||
} | |||||
p.parse(new TextProgressMonitor()); | |||||
inserter.flush(); | |||||
} finally { | |||||
inserter.release(); | |||||
} | |||||
} | } | ||||
} | } |
import org.eclipse.jgit.lib.ObjectStream; | import org.eclipse.jgit.lib.ObjectStream; | ||||
import org.eclipse.jgit.revwalk.RevBlob; | import org.eclipse.jgit.revwalk.RevBlob; | ||||
import org.eclipse.jgit.storage.pack.DeltaEncoder; | import org.eclipse.jgit.storage.pack.DeltaEncoder; | ||||
import org.eclipse.jgit.transport.IndexPack; | |||||
import org.eclipse.jgit.transport.PackParser; | |||||
import org.eclipse.jgit.util.IO; | import org.eclipse.jgit.util.IO; | ||||
import org.eclipse.jgit.util.NB; | import org.eclipse.jgit.util.NB; | ||||
import org.eclipse.jgit.util.TemporaryBuffer; | import org.eclipse.jgit.util.TemporaryBuffer; | ||||
deflate(pack, delta3); | deflate(pack, delta3); | ||||
digest(pack); | digest(pack); | ||||
final byte[] raw = pack.toByteArray(); | |||||
IndexPack ip = IndexPack.create(repo, new ByteArrayInputStream(raw)); | |||||
ip.setFixThin(true); | |||||
ip.index(NullProgressMonitor.INSTANCE); | |||||
ip.renameAndOpenPack(); | |||||
PackParser ip = index(pack.toByteArray()); | |||||
ip.setAllowThin(true); | |||||
ip.parse(NullProgressMonitor.INSTANCE); | |||||
assertTrue("has blob", wc.has(id3)); | assertTrue("has blob", wc.has(id3)); | ||||
deflate(pack, delta3); | deflate(pack, delta3); | ||||
digest(pack); | digest(pack); | ||||
final byte[] raw = pack.toByteArray(); | |||||
IndexPack ip = IndexPack.create(repo, new ByteArrayInputStream(raw)); | |||||
ip.setFixThin(true); | |||||
ip.index(NullProgressMonitor.INSTANCE); | |||||
ip.renameAndOpenPack(); | |||||
PackParser ip = index(pack.toByteArray()); | |||||
ip.setAllowThin(true); | |||||
ip.parse(NullProgressMonitor.INSTANCE); | |||||
assertTrue("has blob", wc.has(id3)); | assertTrue("has blob", wc.has(id3)); | ||||
md.update(buf.toByteArray()); | md.update(buf.toByteArray()); | ||||
buf.write(md.digest()); | buf.write(md.digest()); | ||||
} | } | ||||
private ObjectInserter inserter; | |||||
@After | |||||
public void release() { | |||||
if (inserter != null) | |||||
inserter.release(); | |||||
} | |||||
private PackParser index(byte[] raw) throws IOException { | |||||
if (inserter == null) | |||||
inserter = repo.newObjectInserter(); | |||||
return inserter.newPackParser(new ByteArrayInputStream(raw)); | |||||
} | |||||
} | } |
package org.eclipse.jgit.storage.file; | package org.eclipse.jgit.storage.file; | ||||
import static org.junit.Assert.assertEquals; | import static org.junit.Assert.assertEquals; | ||||
import static org.junit.Assert.assertNotNull; | |||||
import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||
import static org.junit.Assert.fail; | import static org.junit.Assert.fail; | ||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileOutputStream; | import java.io.FileOutputStream; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import java.util.Collection; | import java.util.Collection; | ||||
import org.eclipse.jgit.junit.JGitTestUtil; | import org.eclipse.jgit.junit.JGitTestUtil; | ||||
import org.eclipse.jgit.lib.NullProgressMonitor; | import org.eclipse.jgit.lib.NullProgressMonitor; | ||||
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.lib.ObjectInserter; | |||||
import org.eclipse.jgit.lib.SampleDataRepositoryTestCase; | import org.eclipse.jgit.lib.SampleDataRepositoryTestCase; | ||||
import org.eclipse.jgit.lib.TextProgressMonitor; | |||||
import org.eclipse.jgit.revwalk.RevObject; | import org.eclipse.jgit.revwalk.RevObject; | ||||
import org.eclipse.jgit.revwalk.RevWalk; | import org.eclipse.jgit.revwalk.RevWalk; | ||||
import org.eclipse.jgit.storage.file.PackIndex.MutableEntry; | import org.eclipse.jgit.storage.file.PackIndex.MutableEntry; | ||||
import org.eclipse.jgit.storage.pack.PackConfig; | import org.eclipse.jgit.storage.pack.PackConfig; | ||||
import org.eclipse.jgit.storage.pack.PackWriter; | import org.eclipse.jgit.storage.pack.PackWriter; | ||||
import org.eclipse.jgit.transport.IndexPack; | |||||
import org.eclipse.jgit.transport.PackParser; | |||||
import org.junit.After; | import org.junit.After; | ||||
import org.junit.Before; | import org.junit.Before; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
private ByteArrayOutputStream os; | private ByteArrayOutputStream os; | ||||
private File packBase; | |||||
private File packFile; | |||||
private PackFile pack; | |||||
private File indexFile; | |||||
private ObjectInserter inserter; | |||||
private PackFile pack; | |||||
private FileRepository dst; | |||||
@Before | @Before | ||||
public void setUp() throws Exception { | public void setUp() throws Exception { | ||||
super.setUp(); | super.setUp(); | ||||
os = new ByteArrayOutputStream(); | os = new ByteArrayOutputStream(); | ||||
packBase = new File(trash, "tmp_pack"); | |||||
packFile = new File(trash, "tmp_pack.pack"); | |||||
indexFile = new File(trash, "tmp_pack.idx"); | |||||
config = new PackConfig(db); | config = new PackConfig(db); | ||||
dst = createBareRepository(); | |||||
File alt = new File(dst.getObjectDatabase().getDirectory(), "info/alternates"); | |||||
alt.getParentFile().mkdirs(); | |||||
write(alt, db.getObjectDatabase().getDirectory().getAbsolutePath() + "\n"); | |||||
} | } | ||||
@After | @After | ||||
public void tearDown() throws Exception { | public void tearDown() throws Exception { | ||||
if (writer != null) | |||||
if (writer != null) { | |||||
writer.release(); | writer.release(); | ||||
writer = null; | |||||
} | |||||
if (inserter != null) { | |||||
inserter.release(); | |||||
inserter = null; | |||||
} | |||||
super.tearDown(); | super.tearDown(); | ||||
} | } | ||||
config.setIndexVersion(2); | config.setIndexVersion(2); | ||||
writeVerifyPack4(false); | writeVerifyPack4(false); | ||||
File packFile = pack.getPackFile(); | |||||
String name = packFile.getName(); | |||||
String base = name.substring(0, name.lastIndexOf('.')); | |||||
File indexFile = new File(packFile.getParentFile(), base + ".idx"); | |||||
// Validate that IndexPack came up with the right CRC32 value. | // Validate that IndexPack came up with the right CRC32 value. | ||||
final PackIndex idx1 = PackIndex.open(indexFile); | final PackIndex idx1 = PackIndex.open(indexFile); | ||||
assertTrue(idx1 instanceof PackIndexV2); | assertTrue(idx1 instanceof PackIndexV2); | ||||
} | } | ||||
private void verifyOpenPack(final boolean thin) throws IOException { | private void verifyOpenPack(final boolean thin) throws IOException { | ||||
final byte[] packData = os.toByteArray(); | |||||
if (thin) { | if (thin) { | ||||
final InputStream is = new ByteArrayInputStream(os.toByteArray()); | |||||
final IndexPack indexer = new IndexPack(db, is, packBase); | |||||
PackParser p = index(packData); | |||||
try { | try { | ||||
indexer.index(new TextProgressMonitor()); | |||||
p.parse(NullProgressMonitor.INSTANCE); | |||||
fail("indexer should grumble about missing object"); | fail("indexer should grumble about missing object"); | ||||
} catch (IOException x) { | } catch (IOException x) { | ||||
// expected | // expected | ||||
} | } | ||||
} | } | ||||
final InputStream is = new ByteArrayInputStream(os.toByteArray()); | |||||
final IndexPack indexer = new IndexPack(db, is, packBase); | |||||
indexer.setKeepEmpty(true); | |||||
indexer.setFixThin(thin); | |||||
indexer.setIndexVersion(2); | |||||
indexer.index(new TextProgressMonitor()); | |||||
pack = new PackFile(indexFile, packFile); | |||||
ObjectDirectoryPackParser p = (ObjectDirectoryPackParser) index(packData); | |||||
p.setKeepEmpty(true); | |||||
p.setAllowThin(thin); | |||||
p.setIndexVersion(2); | |||||
p.parse(NullProgressMonitor.INSTANCE); | |||||
pack = p.getPackFile(); | |||||
assertNotNull("have PackFile after parsing", pack); | |||||
} | |||||
private PackParser index(final byte[] packData) throws IOException { | |||||
if (inserter == null) | |||||
inserter = dst.newObjectInserter(); | |||||
return inserter.newPackParser(new ByteArrayInputStream(packData)); | |||||
} | } | ||||
private void verifyObjectsOrder(final ObjectId objectsOrder[]) { | private void verifyObjectsOrder(final ObjectId objectsOrder[]) { |
import org.eclipse.jgit.lib.Constants; | import org.eclipse.jgit.lib.Constants; | ||||
import org.eclipse.jgit.lib.NullProgressMonitor; | import org.eclipse.jgit.lib.NullProgressMonitor; | ||||
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.lib.ObjectInserter; | |||||
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
import org.eclipse.jgit.lib.RepositoryTestCase; | import org.eclipse.jgit.lib.RepositoryTestCase; | ||||
import org.eclipse.jgit.lib.TextProgressMonitor; | |||||
import org.eclipse.jgit.revwalk.RevBlob; | import org.eclipse.jgit.revwalk.RevBlob; | ||||
import org.eclipse.jgit.storage.file.ObjectDirectoryPackParser; | |||||
import org.eclipse.jgit.storage.file.PackFile; | import org.eclipse.jgit.storage.file.PackFile; | ||||
import org.eclipse.jgit.util.NB; | import org.eclipse.jgit.util.NB; | ||||
import org.eclipse.jgit.util.TemporaryBuffer; | import org.eclipse.jgit.util.TemporaryBuffer; | ||||
import org.junit.After; | |||||
import org.junit.Test; | import org.junit.Test; | ||||
/** | /** | ||||
* to make sure they contain the expected objects (well we don't test | * to make sure they contain the expected objects (well we don't test | ||||
* for all of them unless the packs are very small). | * for all of them unless the packs are very small). | ||||
*/ | */ | ||||
public class IndexPackTest extends RepositoryTestCase { | |||||
public class PackParserTest extends RepositoryTestCase { | |||||
/** | /** | ||||
* Test indexing one of the test packs in the egit repo. It has deltas. | * Test indexing one of the test packs in the egit repo. It has deltas. | ||||
* | * | ||||
File packFile = JGitTestUtil.getTestResourceFile("pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.pack"); | File packFile = JGitTestUtil.getTestResourceFile("pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.pack"); | ||||
final InputStream is = new FileInputStream(packFile); | final InputStream is = new FileInputStream(packFile); | ||||
try { | try { | ||||
IndexPack pack = new IndexPack(db, is, new File(trash, "tmp_pack1")); | |||||
pack.index(new TextProgressMonitor()); | |||||
PackFile file = new PackFile(new File(trash, "tmp_pack1.idx"), new File(trash, "tmp_pack1.pack")); | |||||
ObjectDirectoryPackParser p = (ObjectDirectoryPackParser) index(is); | |||||
p.parse(NullProgressMonitor.INSTANCE); | |||||
PackFile file = p.getPackFile(); | |||||
assertTrue(file.hasObject(ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"))); | assertTrue(file.hasObject(ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"))); | ||||
assertTrue(file.hasObject(ObjectId.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"))); | assertTrue(file.hasObject(ObjectId.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"))); | ||||
assertTrue(file.hasObject(ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"))); | assertTrue(file.hasObject(ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"))); | ||||
File packFile = JGitTestUtil.getTestResourceFile("pack-df2982f284bbabb6bdb59ee3fcc6eb0983e20371.pack"); | File packFile = JGitTestUtil.getTestResourceFile("pack-df2982f284bbabb6bdb59ee3fcc6eb0983e20371.pack"); | ||||
final InputStream is = new FileInputStream(packFile); | final InputStream is = new FileInputStream(packFile); | ||||
try { | try { | ||||
IndexPack pack = new IndexPack(db, is, new File(trash, "tmp_pack2")); | |||||
pack.index(new TextProgressMonitor()); | |||||
PackFile file = new PackFile(new File(trash, "tmp_pack2.idx"), new File(trash, "tmp_pack2.pack")); | |||||
ObjectDirectoryPackParser p = (ObjectDirectoryPackParser) index(is); | |||||
p.parse(NullProgressMonitor.INSTANCE); | |||||
PackFile file = p.getPackFile(); | |||||
assertTrue(file.hasObject(ObjectId.fromString("02ba32d3649e510002c21651936b7077aa75ffa9"))); | assertTrue(file.hasObject(ObjectId.fromString("02ba32d3649e510002c21651936b7077aa75ffa9"))); | ||||
assertTrue(file.hasObject(ObjectId.fromString("0966a434eb1a025db6b71485ab63a3bfbea520b6"))); | assertTrue(file.hasObject(ObjectId.fromString("0966a434eb1a025db6b71485ab63a3bfbea520b6"))); | ||||
assertTrue(file.hasObject(ObjectId.fromString("09efc7e59a839528ac7bda9fa020dc9101278680"))); | assertTrue(file.hasObject(ObjectId.fromString("09efc7e59a839528ac7bda9fa020dc9101278680"))); | ||||
digest(pack); | digest(pack); | ||||
final byte[] raw = pack.toByteArray(); | |||||
IndexPack ip = IndexPack.create(db, new ByteArrayInputStream(raw)); | |||||
ip.setFixThin(true); | |||||
ip.index(NullProgressMonitor.INSTANCE); | |||||
ip.renameAndOpenPack(); | |||||
PackParser p = index(new ByteArrayInputStream(pack.toByteArray())); | |||||
p.setAllowThin(true); | |||||
p.parse(NullProgressMonitor.INSTANCE); | |||||
} | } | ||||
@Test | @Test | ||||
deflate(pack, data); | deflate(pack, data); | ||||
digest(pack); | digest(pack); | ||||
final byte[] raw = pack.toByteArray(); | |||||
IndexPack ip = IndexPack.create(db, new ByteArrayInputStream(raw)); | |||||
ip.index(NullProgressMonitor.INSTANCE); | |||||
ip.renameAndOpenPack(); | |||||
PackParser p = index(new ByteArrayInputStream(pack.toByteArray())); | |||||
p.setAllowThin(false); | |||||
p.parse(NullProgressMonitor.INSTANCE); | |||||
} | } | ||||
private void packHeader(TemporaryBuffer.Heap tinyPack, int cnt) | private void packHeader(TemporaryBuffer.Heap tinyPack, int cnt) | ||||
md.update(buf.toByteArray()); | md.update(buf.toByteArray()); | ||||
buf.write(md.digest()); | buf.write(md.digest()); | ||||
} | } | ||||
private ObjectInserter inserter; | |||||
@After | |||||
public void release() { | |||||
if (inserter != null) | |||||
inserter.release(); | |||||
} | |||||
private PackParser index(InputStream in) throws IOException { | |||||
if (inserter == null) | |||||
inserter = db.newObjectInserter(); | |||||
return inserter.newPackParser(in); | |||||
} | |||||
} | } |
import org.eclipse.jgit.lib.Constants; | import org.eclipse.jgit.lib.Constants; | ||||
import org.eclipse.jgit.lib.NullProgressMonitor; | import org.eclipse.jgit.lib.NullProgressMonitor; | ||||
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.lib.ObjectInserter; | |||||
import org.eclipse.jgit.lib.ObjectLoader; | import org.eclipse.jgit.lib.ObjectLoader; | ||||
import org.eclipse.jgit.lib.Ref; | import org.eclipse.jgit.lib.Ref; | ||||
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
buf.write(md.digest()); | buf.write(md.digest()); | ||||
} | } | ||||
private ObjectInserter inserter; | |||||
@After | |||||
public void release() { | |||||
if (inserter != null) | |||||
inserter.release(); | |||||
} | |||||
private void openPack(TemporaryBuffer.Heap buf) throws IOException { | private void openPack(TemporaryBuffer.Heap buf) throws IOException { | ||||
if (inserter == null) | |||||
inserter = src.newObjectInserter(); | |||||
final byte[] raw = buf.toByteArray(); | final byte[] raw = buf.toByteArray(); | ||||
IndexPack ip = IndexPack.create(src, new ByteArrayInputStream(raw)); | |||||
ip.setFixThin(true); | |||||
ip.index(PM); | |||||
ip.renameAndOpenPack(); | |||||
PackParser p = inserter.newPackParser(new ByteArrayInputStream(raw)); | |||||
p.setAllowThin(true); | |||||
p.parse(PM); | |||||
} | } | ||||
private static PacketLineIn asPacketLineIn(TemporaryBuffer.Heap buf) | private static PacketLineIn asPacketLineIn(TemporaryBuffer.Heap buf) |
/** | /** | ||||
* @return the preferred pack index file format; 0 for oldest possible. | * @return the preferred pack index file format; 0 for oldest possible. | ||||
* @see org.eclipse.jgit.transport.IndexPack | |||||
*/ | */ | ||||
public int getPackIndexVersion() { | public int getPackIndexVersion() { | ||||
return packIndexVersion; | return packIndexVersion; |
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.security.MessageDigest; | import java.security.MessageDigest; | ||||
import org.eclipse.jgit.transport.PackParser; | |||||
/** | /** | ||||
* Inserts objects into an existing {@code ObjectDatabase}. | * Inserts objects into an existing {@code ObjectDatabase}. | ||||
* <p> | * <p> | ||||
throw new UnsupportedOperationException(); | throw new UnsupportedOperationException(); | ||||
} | } | ||||
@Override | |||||
public PackParser newPackParser(InputStream in) throws IOException { | |||||
throw new UnsupportedOperationException(); | |||||
} | |||||
@Override | @Override | ||||
public void flush() throws IOException { | public void flush() throws IOException { | ||||
// Do nothing. | // Do nothing. | ||||
public abstract ObjectId insert(int objectType, long length, InputStream in) | public abstract ObjectId insert(int objectType, long length, InputStream in) | ||||
throws IOException; | throws IOException; | ||||
/** | |||||
* Initialize a parser to read from a pack formatted stream. | |||||
* | |||||
* @param in | |||||
* the input stream. The stream is not closed by the parser, and | |||||
* must instead be closed by the caller once parsing is complete. | |||||
* @return the pack parser. | |||||
* @throws IOException | |||||
* the parser instance, which can be configured and then used to | |||||
* parse objects into the ObjectDatabase. | |||||
*/ | |||||
public abstract PackParser newPackParser(InputStream in) throws IOException; | |||||
/** | /** | ||||
* Make all inserted objects visible. | * Make all inserted objects visible. | ||||
* <p> | * <p> |
import org.eclipse.jgit.lib.ObjectLoader; | import org.eclipse.jgit.lib.ObjectLoader; | ||||
import org.eclipse.jgit.storage.pack.ObjectToPack; | import org.eclipse.jgit.storage.pack.ObjectToPack; | ||||
import org.eclipse.jgit.storage.pack.PackWriter; | import org.eclipse.jgit.storage.pack.PackWriter; | ||||
import org.eclipse.jgit.util.FS; | |||||
/** | /** | ||||
* The cached instance of an {@link ObjectDirectory}. | * The cached instance of an {@link ObjectDirectory}. | ||||
return wrapped.getConfig(); | return wrapped.getConfig(); | ||||
} | } | ||||
@Override | |||||
FS getFS() { | |||||
return wrapped.getFS(); | |||||
} | |||||
@Override | @Override | ||||
AlternateHandle[] myAlternates() { | AlternateHandle[] myAlternates() { | ||||
if (alts == null) { | if (alts == null) { | ||||
return result; | return result; | ||||
} | } | ||||
@Override | |||||
PackFile openPack(File pack, File idx) throws IOException { | |||||
return wrapped.openPack(pack, idx); | |||||
} | |||||
@Override | @Override | ||||
void selectObjectRepresentation(PackWriter packer, ObjectToPack otp, | void selectObjectRepresentation(PackWriter packer, ObjectToPack otp, | ||||
WindowCursor curs) throws IOException { | WindowCursor curs) throws IOException { |
import org.eclipse.jgit.lib.ObjectReader; | import org.eclipse.jgit.lib.ObjectReader; | ||||
import org.eclipse.jgit.storage.pack.ObjectToPack; | import org.eclipse.jgit.storage.pack.ObjectToPack; | ||||
import org.eclipse.jgit.storage.pack.PackWriter; | import org.eclipse.jgit.storage.pack.PackWriter; | ||||
import org.eclipse.jgit.util.FS; | |||||
abstract class FileObjectDatabase extends ObjectDatabase { | abstract class FileObjectDatabase extends ObjectDatabase { | ||||
static enum InsertLooseObjectResult { | static enum InsertLooseObjectResult { | ||||
abstract Config getConfig(); | abstract Config getConfig(); | ||||
abstract FS getFS(); | |||||
/** | /** | ||||
* Open an object from this database. | * Open an object from this database. | ||||
* <p> | * <p> | ||||
abstract InsertLooseObjectResult insertUnpackedObject(File tmp, | abstract InsertLooseObjectResult insertUnpackedObject(File tmp, | ||||
ObjectId id, boolean createDuplicate) throws IOException; | ObjectId id, boolean createDuplicate) throws IOException; | ||||
abstract PackFile openPack(File pack, File idx) throws IOException; | |||||
abstract FileObjectDatabase newCachedFileObjectDatabase(); | abstract FileObjectDatabase newCachedFileObjectDatabase(); | ||||
static class AlternateHandle { | static class AlternateHandle { |
* path of the pack file to open. | * path of the pack file to open. | ||||
* @param idx | * @param idx | ||||
* path of the corresponding index file. | * path of the corresponding index file. | ||||
* @return the pack that was opened and added to the database. | |||||
* @throws IOException | * @throws IOException | ||||
* index file could not be opened, read, or is not recognized as | * index file could not be opened, read, or is not recognized as | ||||
* a Git pack file index. | * a Git pack file index. | ||||
*/ | */ | ||||
public void openPack(final File pack, final File idx) throws IOException { | |||||
public PackFile openPack(final File pack, final File idx) | |||||
throws IOException { | |||||
final String p = pack.getName(); | final String p = pack.getName(); | ||||
final String i = idx.getName(); | final String i = idx.getName(); | ||||
if (!p.substring(0, 45).equals(i.substring(0, 45))) | if (!p.substring(0, 45).equals(i.substring(0, 45))) | ||||
throw new IOException(MessageFormat.format(JGitText.get().packDoesNotMatchIndex, pack)); | throw new IOException(MessageFormat.format(JGitText.get().packDoesNotMatchIndex, pack)); | ||||
insertPack(new PackFile(idx, pack)); | |||||
PackFile res = new PackFile(idx, pack); | |||||
insertPack(res); | |||||
return res; | |||||
} | } | ||||
@Override | @Override | ||||
return config; | return config; | ||||
} | } | ||||
@Override | |||||
FS getFS() { | |||||
return fs; | |||||
} | |||||
private void insertPack(final PackFile pf) { | private void insertPack(final PackFile pf) { | ||||
PackList o, n; | PackList o, n; | ||||
do { | do { |
import org.eclipse.jgit.lib.Constants; | import org.eclipse.jgit.lib.Constants; | ||||
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.lib.ObjectInserter; | import org.eclipse.jgit.lib.ObjectInserter; | ||||
import org.eclipse.jgit.transport.PackParser; | |||||
import org.eclipse.jgit.util.FileUtils; | import org.eclipse.jgit.util.FileUtils; | ||||
/** Creates loose objects in a {@link ObjectDirectory}. */ | /** Creates loose objects in a {@link ObjectDirectory}. */ | ||||
throw new ObjectWritingException("Unable to create new object: " + dst); | throw new ObjectWritingException("Unable to create new object: " + dst); | ||||
} | } | ||||
@Override | |||||
public PackParser newPackParser(InputStream in) throws IOException { | |||||
return new ObjectDirectoryPackParser(db, in); | |||||
} | |||||
@Override | @Override | ||||
public void flush() throws IOException { | public void flush() throws IOException { | ||||
// Do nothing. Objects are immediately visible. | // Do nothing. Objects are immediately visible. |
/* | |||||
* Copyright (C) 2008-2011, Google Inc. | |||||
* Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg@dewire.com> | |||||
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> | |||||
* 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.storage.file; | |||||
import java.io.File; | |||||
import java.io.FileOutputStream; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.io.RandomAccessFile; | |||||
import java.security.MessageDigest; | |||||
import java.text.MessageFormat; | |||||
import java.util.Arrays; | |||||
import java.util.List; | |||||
import java.util.zip.CRC32; | |||||
import java.util.zip.Deflater; | |||||
import org.eclipse.jgit.JGitText; | |||||
import org.eclipse.jgit.lib.AnyObjectId; | |||||
import org.eclipse.jgit.lib.Constants; | |||||
import org.eclipse.jgit.lib.CoreConfig; | |||||
import org.eclipse.jgit.lib.ObjectId; | |||||
import org.eclipse.jgit.lib.ObjectInserter; | |||||
import org.eclipse.jgit.lib.ProgressMonitor; | |||||
import org.eclipse.jgit.transport.PackParser; | |||||
import org.eclipse.jgit.transport.PackedObjectInfo; | |||||
import org.eclipse.jgit.util.FileUtils; | |||||
import org.eclipse.jgit.util.NB; | |||||
/** | |||||
* Consumes a pack stream and stores as a pack file in {@link ObjectDirectory}. | |||||
* <p> | |||||
* To obtain an instance of a parser, applications should use | |||||
* {@link ObjectInserter#newPackParser(InputStream)}. | |||||
*/ | |||||
public class ObjectDirectoryPackParser extends PackParser { | |||||
private final FileObjectDatabase db; | |||||
/** CRC-32 computation for objects that are appended onto the pack. */ | |||||
private final CRC32 crc; | |||||
/** Running SHA-1 of any base objects appended after {@link #origEnd}. */ | |||||
private final MessageDigest tailDigest; | |||||
/** Preferred format version of the pack-*.idx file to generate. */ | |||||
private int indexVersion; | |||||
/** If true, pack with 0 objects will be stored. Usually these are deleted. */ | |||||
private boolean keepEmpty; | |||||
/** Path of the temporary file holding the pack data. */ | |||||
private File tmpPack; | |||||
/** | |||||
* Path of the index created for the pack, to find objects quickly at read | |||||
* time. | |||||
*/ | |||||
private File tmpIdx; | |||||
/** Read/write handle to {@link #tmpPack} while it is being parsed. */ | |||||
private RandomAccessFile out; | |||||
/** Length of the original pack stream, before missing bases were appended. */ | |||||
private long origEnd; | |||||
/** The original checksum of data up to {@link #origEnd}. */ | |||||
private byte[] origHash; | |||||
/** Current end of the pack file. */ | |||||
private long packEnd; | |||||
/** Checksum of the entire pack file. */ | |||||
private byte[] packHash; | |||||
/** Compresses delta bases when completing a thin pack. */ | |||||
private Deflater def; | |||||
/** The pack that was created, if parsing was successful. */ | |||||
private PackFile newPack; | |||||
ObjectDirectoryPackParser(FileObjectDatabase odb, InputStream src) { | |||||
super(odb, src); | |||||
this.db = odb; | |||||
this.crc = new CRC32(); | |||||
this.tailDigest = Constants.newMessageDigest(); | |||||
indexVersion = db.getConfig().get(CoreConfig.KEY).getPackIndexVersion(); | |||||
} | |||||
/** | |||||
* Set the pack index file format version this instance will create. | |||||
* | |||||
* @param version | |||||
* the version to write. The special version 0 designates the | |||||
* oldest (most compatible) format available for the objects. | |||||
* @see PackIndexWriter | |||||
*/ | |||||
public void setIndexVersion(int version) { | |||||
indexVersion = version; | |||||
} | |||||
/** | |||||
* Configure this index pack instance to keep an empty pack. | |||||
* <p> | |||||
* By default an empty pack (a pack with no objects) is not kept, as doi so | |||||
* is completely pointless. With no objects in the pack there is no d stored | |||||
* by it, so the pack is unnecessary. | |||||
* | |||||
* @param empty | |||||
* true to enable keeping an empty pack. | |||||
*/ | |||||
public void setKeepEmpty(final boolean empty) { | |||||
keepEmpty = empty; | |||||
} | |||||
/** | |||||
* Get the imported {@link PackFile}. | |||||
* <p> | |||||
* This method is supplied only to support testing; applications shouldn't | |||||
* be using it directly to access the imported data. | |||||
* | |||||
* @return the imported PackFile, if parsing was successful. | |||||
*/ | |||||
public PackFile getPackFile() { | |||||
return newPack; | |||||
} | |||||
@Override | |||||
public PackLock parse(ProgressMonitor progress) throws IOException { | |||||
tmpPack = File.createTempFile("incoming_", ".pack", db.getDirectory()); | |||||
tmpIdx = new File(db.getDirectory(), baseName(tmpPack) + ".idx"); | |||||
try { | |||||
out = new RandomAccessFile(tmpPack, "rw"); | |||||
super.parse(progress); | |||||
out.seek(packEnd); | |||||
out.write(packHash); | |||||
out.getChannel().force(true); | |||||
out.close(); | |||||
writeIdx(); | |||||
tmpPack.setReadOnly(); | |||||
tmpIdx.setReadOnly(); | |||||
return renameAndOpenPack(getLockMessage()); | |||||
} finally { | |||||
if (def != null) | |||||
def.end(); | |||||
try { | |||||
if (out != null && out.getChannel().isOpen()) | |||||
out.close(); | |||||
} catch (IOException closeError) { | |||||
// Ignored. We want to delete the file. | |||||
} | |||||
cleanupTemporaryFiles(); | |||||
} | |||||
} | |||||
@Override | |||||
protected void onBeginWholeObject(long streamPosition, int type, | |||||
long inflatedSize) throws IOException { | |||||
crc.reset(); | |||||
} | |||||
@Override | |||||
protected void onEndWholeObject(PackedObjectInfo info) throws IOException { | |||||
info.setCRC((int) crc.getValue()); | |||||
} | |||||
@Override | |||||
protected void onBeginOfsDelta(long streamPosition, | |||||
long baseStreamPosition, long inflatedSize) throws IOException { | |||||
crc.reset(); | |||||
} | |||||
@Override | |||||
protected void onBeginRefDelta(long streamPosition, AnyObjectId baseId, | |||||
long inflatedSize) throws IOException { | |||||
crc.reset(); | |||||
} | |||||
@Override | |||||
protected UnresolvedDelta onEndDelta() throws IOException { | |||||
UnresolvedDelta delta = new UnresolvedDelta(); | |||||
delta.setCRC((int) crc.getValue()); | |||||
return delta; | |||||
} | |||||
@Override | |||||
protected void onObjectHeader(Source src, byte[] raw, int pos, int len) | |||||
throws IOException { | |||||
crc.update(raw, pos, len); | |||||
} | |||||
@Override | |||||
protected void onObjectData(Source src, byte[] raw, int pos, int len) | |||||
throws IOException { | |||||
crc.update(raw, pos, len); | |||||
} | |||||
@Override | |||||
protected void onStoreStream(byte[] raw, int pos, int len) | |||||
throws IOException { | |||||
out.write(raw, pos, len); | |||||
} | |||||
@Override | |||||
protected void onPackFooter(byte[] hash) throws IOException { | |||||
packEnd = out.getFilePointer(); | |||||
origEnd = packEnd; | |||||
origHash = hash; | |||||
packHash = hash; | |||||
} | |||||
@Override | |||||
protected ObjectTypeAndSize seekDatabase(UnresolvedDelta delta, | |||||
ObjectTypeAndSize info) throws IOException { | |||||
out.seek(delta.getOffset()); | |||||
crc.reset(); | |||||
return readObjectHeader(info); | |||||
} | |||||
@Override | |||||
protected ObjectTypeAndSize seekDatabase(PackedObjectInfo obj, | |||||
ObjectTypeAndSize info) throws IOException { | |||||
out.seek(obj.getOffset()); | |||||
crc.reset(); | |||||
return readObjectHeader(info); | |||||
} | |||||
@Override | |||||
protected int readDatabase(byte[] dst, int pos, int cnt) throws IOException { | |||||
return out.read(dst, pos, cnt); | |||||
} | |||||
@Override | |||||
protected boolean checkCRC(int oldCRC) { | |||||
return oldCRC == (int) crc.getValue(); | |||||
} | |||||
private static String baseName(File tmpPack) { | |||||
String name = tmpPack.getName(); | |||||
return name.substring(0, name.lastIndexOf('.')); | |||||
} | |||||
private void cleanupTemporaryFiles() { | |||||
if (tmpIdx != null && !tmpIdx.delete() && tmpIdx.exists()) | |||||
tmpIdx.deleteOnExit(); | |||||
if (tmpPack != null && !tmpPack.delete() && tmpPack.exists()) | |||||
tmpPack.deleteOnExit(); | |||||
} | |||||
@Override | |||||
protected boolean onAppendBase(final int typeCode, final byte[] data, | |||||
final PackedObjectInfo info) throws IOException { | |||||
info.setOffset(packEnd); | |||||
final byte[] buf = buffer(); | |||||
int sz = data.length; | |||||
int len = 0; | |||||
buf[len++] = (byte) ((typeCode << 4) | sz & 15); | |||||
sz >>>= 4; | |||||
while (sz > 0) { | |||||
buf[len - 1] |= 0x80; | |||||
buf[len++] = (byte) (sz & 0x7f); | |||||
sz >>>= 7; | |||||
} | |||||
tailDigest.update(buf, 0, len); | |||||
crc.reset(); | |||||
crc.update(buf, 0, len); | |||||
out.seek(packEnd); | |||||
out.write(buf, 0, len); | |||||
packEnd += len; | |||||
if (def == null) | |||||
def = new Deflater(Deflater.DEFAULT_COMPRESSION, false); | |||||
else | |||||
def.reset(); | |||||
def.setInput(data); | |||||
def.finish(); | |||||
while (!def.finished()) { | |||||
len = def.deflate(buf); | |||||
tailDigest.update(buf, 0, len); | |||||
crc.update(buf, 0, len); | |||||
out.write(buf, 0, len); | |||||
packEnd += len; | |||||
} | |||||
info.setCRC((int) crc.getValue()); | |||||
return true; | |||||
} | |||||
@Override | |||||
protected void onEndThinPack() throws IOException { | |||||
final byte[] tailHash = this.tailDigest.digest(); | |||||
final byte[] buf = buffer(); | |||||
final MessageDigest origDigest = Constants.newMessageDigest(); | |||||
final MessageDigest tailDigest = Constants.newMessageDigest(); | |||||
final MessageDigest packDigest = Constants.newMessageDigest(); | |||||
long origRemaining = origEnd; | |||||
out.seek(0); | |||||
out.readFully(buf, 0, 12); | |||||
origDigest.update(buf, 0, 12); | |||||
origRemaining -= 12; | |||||
NB.encodeInt32(buf, 8, getObjectCount()); | |||||
out.seek(0); | |||||
out.write(buf, 0, 12); | |||||
packDigest.update(buf, 0, 12); | |||||
for (;;) { | |||||
final int n = out.read(buf); | |||||
if (n < 0) | |||||
break; | |||||
if (origRemaining != 0) { | |||||
final int origCnt = (int) Math.min(n, origRemaining); | |||||
origDigest.update(buf, 0, origCnt); | |||||
origRemaining -= origCnt; | |||||
if (origRemaining == 0) | |||||
tailDigest.update(buf, origCnt, n - origCnt); | |||||
} else | |||||
tailDigest.update(buf, 0, n); | |||||
packDigest.update(buf, 0, n); | |||||
} | |||||
if (!Arrays.equals(origDigest.digest(), origHash) | |||||
|| !Arrays.equals(tailDigest.digest(), tailHash)) | |||||
throw new IOException( | |||||
JGitText.get().packCorruptedWhileWritingToFilesystem); | |||||
packHash = packDigest.digest(); | |||||
} | |||||
private void writeIdx() throws IOException { | |||||
List<PackedObjectInfo> list = getSortedObjectList(null /* by ObjectId */); | |||||
final FileOutputStream os = new FileOutputStream(tmpIdx); | |||||
try { | |||||
final PackIndexWriter iw; | |||||
if (indexVersion <= 0) | |||||
iw = PackIndexWriter.createOldestPossible(os, list); | |||||
else | |||||
iw = PackIndexWriter.createVersion(os, indexVersion); | |||||
iw.write(list, packHash); | |||||
os.getChannel().force(true); | |||||
} finally { | |||||
os.close(); | |||||
} | |||||
} | |||||
private PackLock renameAndOpenPack(final String lockMessage) | |||||
throws IOException { | |||||
if (!keepEmpty && getObjectCount() == 0) { | |||||
cleanupTemporaryFiles(); | |||||
return null; | |||||
} | |||||
final MessageDigest d = Constants.newMessageDigest(); | |||||
final byte[] oeBytes = new byte[Constants.OBJECT_ID_LENGTH]; | |||||
for (int i = 0; i < getObjectCount(); i++) { | |||||
final PackedObjectInfo oe = getObject(i); | |||||
oe.copyRawTo(oeBytes, 0); | |||||
d.update(oeBytes); | |||||
} | |||||
final String name = ObjectId.fromRaw(d.digest()).name(); | |||||
final File packDir = new File(db.getDirectory(), "pack"); | |||||
final File finalPack = new File(packDir, "pack-" + name + ".pack"); | |||||
final File finalIdx = new File(packDir, "pack-" + name + ".idx"); | |||||
final PackLock keep = new PackLock(finalPack, db.getFS()); | |||||
if (!packDir.exists() && !packDir.mkdir() && !packDir.exists()) { | |||||
// The objects/pack directory isn't present, and we are unable | |||||
// to create it. There is no way to move this pack in. | |||||
// | |||||
cleanupTemporaryFiles(); | |||||
throw new IOException(MessageFormat.format( | |||||
JGitText.get().cannotCreateDirectory, packDir | |||||
.getAbsolutePath())); | |||||
} | |||||
if (finalPack.exists()) { | |||||
// If the pack is already present we should never replace it. | |||||
// | |||||
cleanupTemporaryFiles(); | |||||
return null; | |||||
} | |||||
if (lockMessage != null) { | |||||
// If we have a reason to create a keep file for this pack, do | |||||
// so, or fail fast and don't put the pack in place. | |||||
// | |||||
try { | |||||
if (!keep.lock(lockMessage)) | |||||
throw new IOException(MessageFormat.format( | |||||
JGitText.get().cannotLockPackIn, finalPack)); | |||||
} catch (IOException e) { | |||||
cleanupTemporaryFiles(); | |||||
throw e; | |||||
} | |||||
} | |||||
if (!tmpPack.renameTo(finalPack)) { | |||||
cleanupTemporaryFiles(); | |||||
keep.unlock(); | |||||
throw new IOException(MessageFormat.format( | |||||
JGitText.get().cannotMovePackTo, finalPack)); | |||||
} | |||||
if (!tmpIdx.renameTo(finalIdx)) { | |||||
cleanupTemporaryFiles(); | |||||
keep.unlock(); | |||||
if (!finalPack.delete()) | |||||
finalPack.deleteOnExit(); | |||||
throw new IOException(MessageFormat.format( | |||||
JGitText.get().cannotMoveIndexTo, finalIdx)); | |||||
} | |||||
try { | |||||
newPack = db.openPack(finalPack, finalIdx); | |||||
} catch (IOException err) { | |||||
keep.unlock(); | |||||
if (finalPack.exists()) | |||||
FileUtils.delete(finalPack); | |||||
if (finalIdx.exists()) | |||||
FileUtils.delete(finalIdx); | |||||
throw err; | |||||
} | |||||
return lockMessage != null ? keep : null; | |||||
} | |||||
} |
import org.eclipse.jgit.lib.Constants; | import org.eclipse.jgit.lib.Constants; | ||||
import org.eclipse.jgit.lib.MutableObjectId; | import org.eclipse.jgit.lib.MutableObjectId; | ||||
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.lib.ObjectInserter; | |||||
import org.eclipse.jgit.lib.ProgressMonitor; | import org.eclipse.jgit.lib.ProgressMonitor; | ||||
import org.eclipse.jgit.lib.Ref; | import org.eclipse.jgit.lib.Ref; | ||||
import org.eclipse.jgit.lib.Config.SectionParser; | import org.eclipse.jgit.lib.Config.SectionParser; | ||||
} | } | ||||
private void receivePack(final ProgressMonitor monitor) throws IOException { | private void receivePack(final ProgressMonitor monitor) throws IOException { | ||||
final IndexPack ip; | |||||
InputStream input = in; | InputStream input = in; | ||||
if (sideband) | if (sideband) | ||||
input = new SideBandInputStream(input, monitor, getMessageWriter()); | input = new SideBandInputStream(input, monitor, getMessageWriter()); | ||||
ip = IndexPack.create(local, input); | |||||
ip.setFixThin(thinPack); | |||||
ip.setObjectChecking(transport.isCheckFetchedObjects()); | |||||
ip.index(monitor); | |||||
packLock = ip.renameAndOpenPack(lockMessage); | |||||
ObjectInserter ins = local.newObjectInserter(); | |||||
try { | |||||
PackParser parser = ins.newPackParser(input); | |||||
parser.setAllowThin(thinPack); | |||||
parser.setObjectChecking(transport.isCheckFetchedObjects()); | |||||
parser.setLockMessage(lockMessage); | |||||
packLock = parser.parse(monitor); | |||||
ins.flush(); | |||||
} finally { | |||||
ins.release(); | |||||
} | |||||
} | } | ||||
private static class CancelledException extends Exception { | private static class CancelledException extends Exception { |
import org.eclipse.jgit.errors.PackProtocolException; | import org.eclipse.jgit.errors.PackProtocolException; | ||||
import org.eclipse.jgit.errors.TransportException; | import org.eclipse.jgit.errors.TransportException; | ||||
import org.eclipse.jgit.lib.Constants; | import org.eclipse.jgit.lib.Constants; | ||||
import org.eclipse.jgit.lib.NullProgressMonitor; | |||||
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.lib.ObjectIdRef; | import org.eclipse.jgit.lib.ObjectIdRef; | ||||
import org.eclipse.jgit.lib.ObjectInserter; | |||||
import org.eclipse.jgit.lib.ProgressMonitor; | import org.eclipse.jgit.lib.ProgressMonitor; | ||||
import org.eclipse.jgit.lib.Ref; | import org.eclipse.jgit.lib.Ref; | ||||
import org.eclipse.jgit.revwalk.RevCommit; | import org.eclipse.jgit.revwalk.RevCommit; | ||||
BundleFetchConnection(Transport transportBundle, final InputStream src) throws TransportException { | BundleFetchConnection(Transport transportBundle, final InputStream src) throws TransportException { | ||||
transport = transportBundle; | transport = transportBundle; | ||||
bin = new BufferedInputStream(src, IndexPack.BUFFER_SIZE); | |||||
bin = new BufferedInputStream(src); | |||||
try { | try { | ||||
switch (readSignature()) { | switch (readSignature()) { | ||||
case 2: | case 2: | ||||
throws TransportException { | throws TransportException { | ||||
verifyPrerequisites(); | verifyPrerequisites(); | ||||
try { | try { | ||||
final IndexPack ip = newIndexPack(); | |||||
ip.index(monitor); | |||||
packLock = ip.renameAndOpenPack(lockMessage); | |||||
ObjectInserter ins = transport.local.newObjectInserter(); | |||||
try { | |||||
PackParser parser = ins.newPackParser(bin); | |||||
parser.setAllowThin(true); | |||||
parser.setObjectChecking(transport.isCheckFetchedObjects()); | |||||
parser.setLockMessage(lockMessage); | |||||
packLock = parser.parse(NullProgressMonitor.INSTANCE); | |||||
ins.flush(); | |||||
} finally { | |||||
ins.release(); | |||||
} | |||||
} catch (IOException err) { | } catch (IOException err) { | ||||
close(); | close(); | ||||
throw new TransportException(transport.uri, err.getMessage(), err); | throw new TransportException(transport.uri, err.getMessage(), err); | ||||
return Collections.<PackLock> emptyList(); | return Collections.<PackLock> emptyList(); | ||||
} | } | ||||
private IndexPack newIndexPack() throws IOException { | |||||
final IndexPack ip = IndexPack.create(transport.local, bin); | |||||
ip.setFixThin(true); | |||||
ip.setObjectChecking(transport.isCheckFetchedObjects()); | |||||
return ip; | |||||
} | |||||
private void verifyPrerequisites() throws TransportException { | private void verifyPrerequisites() throws TransportException { | ||||
if (prereqs.isEmpty()) | if (prereqs.isEmpty()) | ||||
return; | return; |
package org.eclipse.jgit.transport; | package org.eclipse.jgit.transport; | ||||
/** | /** | ||||
* Simple Map<long,Object> helper for {@link IndexPack}. | |||||
* Simple Map<long,Object> helper for {@link PackParser}. | |||||
* | * | ||||
* @param <V> | * @param <V> | ||||
* type of the value instance. | * type of the value instance. |
import org.eclipse.jgit.errors.PackProtocolException; | import org.eclipse.jgit.errors.PackProtocolException; | ||||
import org.eclipse.jgit.errors.UnpackException; | import org.eclipse.jgit.errors.UnpackException; | ||||
import org.eclipse.jgit.lib.Config; | import org.eclipse.jgit.lib.Config; | ||||
import org.eclipse.jgit.lib.Config.SectionParser; | |||||
import org.eclipse.jgit.lib.Constants; | import org.eclipse.jgit.lib.Constants; | ||||
import org.eclipse.jgit.lib.NullProgressMonitor; | import org.eclipse.jgit.lib.NullProgressMonitor; | ||||
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.lib.ObjectIdSubclassMap; | import org.eclipse.jgit.lib.ObjectIdSubclassMap; | ||||
import org.eclipse.jgit.lib.ObjectInserter; | |||||
import org.eclipse.jgit.lib.PersonIdent; | import org.eclipse.jgit.lib.PersonIdent; | ||||
import org.eclipse.jgit.lib.Ref; | import org.eclipse.jgit.lib.Ref; | ||||
import org.eclipse.jgit.lib.RefUpdate; | import org.eclipse.jgit.lib.RefUpdate; | ||||
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
import org.eclipse.jgit.lib.Config.SectionParser; | |||||
import org.eclipse.jgit.revwalk.ObjectWalk; | import org.eclipse.jgit.revwalk.ObjectWalk; | ||||
import org.eclipse.jgit.revwalk.RevBlob; | import org.eclipse.jgit.revwalk.RevBlob; | ||||
import org.eclipse.jgit.revwalk.RevCommit; | import org.eclipse.jgit.revwalk.RevCommit; | ||||
private Writer msgs; | private Writer msgs; | ||||
private IndexPack ip; | |||||
private PackParser parser; | |||||
/** The refs we advertised as existing at the start of the connection. */ | /** The refs we advertised as existing at the start of the connection. */ | ||||
private Map<String, Ref> refs; | private Map<String, Ref> refs; | ||||
receivePack(); | receivePack(); | ||||
if (needCheckConnectivity()) | if (needCheckConnectivity()) | ||||
checkConnectivity(); | checkConnectivity(); | ||||
ip = null; | |||||
parser = null; | |||||
unpackError = null; | unpackError = null; | ||||
} catch (IOException err) { | } catch (IOException err) { | ||||
unpackError = err; | unpackError = err; | ||||
if (timeoutIn != null) | if (timeoutIn != null) | ||||
timeoutIn.setTimeout(10 * timeout * 1000); | timeoutIn.setTimeout(10 * timeout * 1000); | ||||
ip = IndexPack.create(db, rawIn); | |||||
ip.setFixThin(true); | |||||
ip.setNeedNewObjectIds(checkReferencedIsReachable); | |||||
ip.setNeedBaseObjectIds(checkReferencedIsReachable); | |||||
ip.setObjectChecking(isCheckReceivedObjects()); | |||||
ip.index(NullProgressMonitor.INSTANCE); | |||||
String lockMsg = "jgit receive-pack"; | |||||
if (getRefLogIdent() != null) | |||||
lockMsg += " from " + getRefLogIdent().toExternalString(); | |||||
packLock = ip.renameAndOpenPack(lockMsg); | |||||
ObjectInserter ins = db.newObjectInserter(); | |||||
try { | |||||
String lockMsg = "jgit receive-pack"; | |||||
if (getRefLogIdent() != null) | |||||
lockMsg += " from " + getRefLogIdent().toExternalString(); | |||||
parser = ins.newPackParser(rawIn); | |||||
parser.setAllowThin(true); | |||||
parser.setNeedNewObjectIds(checkReferencedIsReachable); | |||||
parser.setNeedBaseObjectIds(checkReferencedIsReachable); | |||||
parser.setObjectChecking(isCheckReceivedObjects()); | |||||
parser.setLockMessage(lockMsg); | |||||
packLock = parser.parse(NullProgressMonitor.INSTANCE); | |||||
ins.flush(); | |||||
} finally { | |||||
ins.release(); | |||||
} | |||||
if (timeoutIn != null) | if (timeoutIn != null) | ||||
timeoutIn.setTimeout(timeout * 1000); | timeoutIn.setTimeout(timeout * 1000); | ||||
ObjectIdSubclassMap<ObjectId> providedObjects = null; | ObjectIdSubclassMap<ObjectId> providedObjects = null; | ||||
if (checkReferencedIsReachable) { | if (checkReferencedIsReachable) { | ||||
baseObjects = ip.getBaseObjectIds(); | |||||
providedObjects = ip.getNewObjectIds(); | |||||
baseObjects = parser.getBaseObjectIds(); | |||||
providedObjects = parser.getNewObjectIds(); | |||||
} | } | ||||
ip = null; | |||||
parser = null; | |||||
final ObjectWalk ow = new ObjectWalk(db); | final ObjectWalk ow = new ObjectWalk(db); | ||||
ow.setRetainBody(false); | ow.setRetainBody(false); |
} | } | ||||
void downloadPack(final ProgressMonitor monitor) throws IOException { | void downloadPack(final ProgressMonitor monitor) throws IOException { | ||||
final WalkRemoteObjectDatabase.FileStream s; | |||||
final IndexPack ip; | |||||
s = connection.open("pack/" + packName); | |||||
ip = IndexPack.create(local, s.in); | |||||
ip.setFixThin(false); | |||||
ip.setObjectChecker(objCheck); | |||||
ip.index(monitor); | |||||
final PackLock keep = ip.renameAndOpenPack(lockMessage); | |||||
if (keep != null) | |||||
packLocks.add(keep); | |||||
String name = "pack/" + packName; | |||||
WalkRemoteObjectDatabase.FileStream s = connection.open(name); | |||||
PackParser parser = inserter.newPackParser(s.in); | |||||
parser.setAllowThin(false); | |||||
parser.setObjectChecker(objCheck); | |||||
parser.setLockMessage(lockMessage); | |||||
PackLock lock = parser.parse(monitor); | |||||
if (lock != null) | |||||
packLocks.add(lock); | |||||
inserter.flush(); | |||||
} | } | ||||
} | } | ||||
} | } |