Use ObjectInserter for loose objects in WalkFetchConnection

Rather than relying on the repository's ability to give us the
local file path for a loose object, just pass its inflated form to
the ObjectInserter for the repository.  We have to recompress it,
which may slow down fetches, but this is the slow dumb protocol.
The extra cost to do the compression locally isn't going to be a
major bottleneck.

This nicely removes the nasty part about computing the object
identity by hand, allowing us to instead rely upon the inserter's
internal computation.  Unfortunately it means we might store a loose
object whose SHA-1 doesn't match the expected SHA-1, such as if the
remote repository was corrupted.  This is fairly harmless, as the
incorrectly named object will now be stored under its proper name,
and will eventually be garbage collected, as its not referenced by
the local repository.

We have to flush the inserter after the object is stored because
we aren't sure if we need to read the object later, or not.

Change-Id: Idb1e2b1af1433a23f8c3fd55aeb20575e6047ef0
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
This commit is contained in:
Shawn O. Pearce 2010-06-23 18:26:40 -07:00
parent 5cfc29b491
commit 41c04bbb28
1 changed files with 18 additions and 56 deletions

View File

@ -48,7 +48,6 @@
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
@ -63,7 +62,6 @@
import org.eclipse.jgit.errors.CompoundException;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.ObjectWritingException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
@ -72,6 +70,7 @@
import org.eclipse.jgit.lib.ObjectChecker;
import org.eclipse.jgit.lib.ObjectDirectory;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PackIndex;
import org.eclipse.jgit.lib.PackLock;
import org.eclipse.jgit.lib.ProgressMonitor;
@ -166,8 +165,6 @@ class WalkFetchConnection extends BaseFetchConnection {
private final MutableObjectId idBuffer = new MutableObjectId();
private final MessageDigest objectDigest = Constants.newMessageDigest();
/**
* Errors received while trying to obtain an object.
* <p>
@ -181,10 +178,14 @@ class WalkFetchConnection extends BaseFetchConnection {
private final List<PackLock> packLocks;
/** Inserter to write objects onto {@link #local}. */
private final ObjectInserter inserter;
WalkFetchConnection(final WalkTransport t, final WalkRemoteObjectDatabase w) {
Transport wt = (Transport)t;
local = wt.local;
objCheck = wt.isCheckFetchedObjects() ? new ObjectChecker() : null;
inserter = local.newObjectInserter();
remotes = new ArrayList<WalkRemoteObjectDatabase>();
remotes.add(w);
@ -241,6 +242,7 @@ public void setPackLockMessage(final String message) {
@Override
public void close() {
inserter.release();
for (final RemotePack p : unfetchedPacks) {
if (p.tmpIdx != null)
p.tmpIdx.delete();
@ -559,8 +561,7 @@ private boolean downloadLooseObject(final AnyObjectId id,
throws TransportException {
try {
final byte[] compressed = remote.open(looseName).toArray();
verifyLooseObject(id, compressed);
saveLooseObject(id, compressed);
verifyAndInsertLooseObject(id, compressed);
return true;
} catch (FileNotFoundException e) {
// Not available in a loose format from this alternate?
@ -573,8 +574,8 @@ private boolean downloadLooseObject(final AnyObjectId id,
}
}
private void verifyLooseObject(final AnyObjectId id, final byte[] compressed)
throws IOException {
private void verifyAndInsertLooseObject(final AnyObjectId id,
final byte[] compressed) throws IOException {
final UnpackedObjectLoader uol;
try {
uol = new UnpackedObjectLoader(compressed);
@ -596,62 +597,23 @@ private void verifyLooseObject(final AnyObjectId id, final byte[] compressed)
throw e;
}
objectDigest.reset();
objectDigest.update(Constants.encodedTypeString(uol.getType()));
objectDigest.update((byte) ' ');
objectDigest.update(Constants.encodeASCII(uol.getSize()));
objectDigest.update((byte) 0);
objectDigest.update(uol.getCachedBytes());
idBuffer.fromRaw(objectDigest.digest(), 0);
if (!AnyObjectId.equals(id, idBuffer)) {
throw new TransportException(MessageFormat.format(JGitText.get().incorrectHashFor
, id.name(), idBuffer.name(), Constants.typeString(uol.getType()), compressed.length));
}
final int type = uol.getType();
final byte[] raw = uol.getCachedBytes();
if (objCheck != null) {
try {
objCheck.check(uol.getType(), uol.getCachedBytes());
objCheck.check(type, raw);
} catch (CorruptObjectException e) {
throw new TransportException(MessageFormat.format(JGitText.get().transportExceptionInvalid
, Constants.typeString(uol.getType()), id.name(), e.getMessage()));
, Constants.typeString(type), id.name(), e.getMessage()));
}
}
}
private void saveLooseObject(final AnyObjectId id, final byte[] compressed)
throws IOException, ObjectWritingException {
final File tmp;
tmp = File.createTempFile("noz", null, local.getObjectsDirectory());
try {
final FileOutputStream out = new FileOutputStream(tmp);
try {
out.write(compressed);
} finally {
out.close();
}
tmp.setReadOnly();
} catch (IOException e) {
tmp.delete();
throw e;
ObjectId act = inserter.insert(type, raw);
if (!AnyObjectId.equals(id, act)) {
throw new TransportException(MessageFormat.format(JGitText.get().incorrectHashFor
, id.name(), act.name(), Constants.typeString(type), compressed.length));
}
final File o = local.toFile(id);
if (tmp.renameTo(o))
return;
// Maybe the directory doesn't exist yet as the object
// directories are always lazily created. Note that we
// try the rename first as the directory likely does exist.
//
o.getParentFile().mkdir();
if (tmp.renameTo(o))
return;
tmp.delete();
if (local.hasObject(id))
return;
throw new ObjectWritingException(MessageFormat.format(JGitText.get().unableToStore, id.name()));
inserter.flush();
}
private Collection<WalkRemoteObjectDatabase> expandOneAlternate(