Browse Source

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>
tags/v0.9.1
Shawn O. Pearce 14 years ago
parent
commit
41c04bbb28

+ 18
- 56
org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java View File

@@ -48,7 +48,6 @@ import java.io.File;
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.JGitText;
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.MutableObjectId;
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 @@ class WalkFetchConnection extends BaseFetchConnection {

@Override
public void close() {
inserter.release();
for (final RemotePack p : unfetchedPacks) {
if (p.tmpIdx != null)
p.tmpIdx.delete();
@@ -559,8 +561,7 @@ class WalkFetchConnection extends BaseFetchConnection {
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 @@ class WalkFetchConnection extends BaseFetchConnection {
}
}

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 @@ class WalkFetchConnection extends BaseFetchConnection {
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(

Loading…
Cancel
Save