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>
stable-0.9
Shawn O. Pearce 15 years ago
parent
commit
41c04bbb28
  1. 74
      org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java

74
org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java

@ -48,7 +48,6 @@ import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.security.MessageDigest;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -63,7 +62,6 @@ import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.CompoundException; import org.eclipse.jgit.errors.CompoundException;
import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.ObjectWritingException;
import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants; 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.ObjectChecker;
import org.eclipse.jgit.lib.ObjectDirectory; import org.eclipse.jgit.lib.ObjectDirectory;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PackIndex; import org.eclipse.jgit.lib.PackIndex;
import org.eclipse.jgit.lib.PackLock; import org.eclipse.jgit.lib.PackLock;
import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.ProgressMonitor;
@ -166,8 +165,6 @@ class WalkFetchConnection extends BaseFetchConnection {
private final MutableObjectId idBuffer = new MutableObjectId(); private final MutableObjectId idBuffer = new MutableObjectId();
private final MessageDigest objectDigest = Constants.newMessageDigest();
/** /**
* Errors received while trying to obtain an object. * Errors received while trying to obtain an object.
* <p> * <p>
@ -181,10 +178,14 @@ class WalkFetchConnection extends BaseFetchConnection {
private final List<PackLock> packLocks; private final List<PackLock> packLocks;
/** Inserter to write objects onto {@link #local}. */
private final ObjectInserter inserter;
WalkFetchConnection(final WalkTransport t, final WalkRemoteObjectDatabase w) { WalkFetchConnection(final WalkTransport t, final WalkRemoteObjectDatabase w) {
Transport wt = (Transport)t; Transport wt = (Transport)t;
local = wt.local; local = wt.local;
objCheck = wt.isCheckFetchedObjects() ? new ObjectChecker() : null; objCheck = wt.isCheckFetchedObjects() ? new ObjectChecker() : null;
inserter = local.newObjectInserter();
remotes = new ArrayList<WalkRemoteObjectDatabase>(); remotes = new ArrayList<WalkRemoteObjectDatabase>();
remotes.add(w); remotes.add(w);
@ -241,6 +242,7 @@ class WalkFetchConnection extends BaseFetchConnection {
@Override @Override
public void close() { public void close() {
inserter.release();
for (final RemotePack p : unfetchedPacks) { for (final RemotePack p : unfetchedPacks) {
if (p.tmpIdx != null) if (p.tmpIdx != null)
p.tmpIdx.delete(); p.tmpIdx.delete();
@ -559,8 +561,7 @@ class WalkFetchConnection extends BaseFetchConnection {
throws TransportException { throws TransportException {
try { try {
final byte[] compressed = remote.open(looseName).toArray(); final byte[] compressed = remote.open(looseName).toArray();
verifyLooseObject(id, compressed); verifyAndInsertLooseObject(id, compressed);
saveLooseObject(id, compressed);
return true; return true;
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
// Not available in a loose format from this alternate? // 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) private void verifyAndInsertLooseObject(final AnyObjectId id,
throws IOException { final byte[] compressed) throws IOException {
final UnpackedObjectLoader uol; final UnpackedObjectLoader uol;
try { try {
uol = new UnpackedObjectLoader(compressed); uol = new UnpackedObjectLoader(compressed);
@ -596,62 +597,23 @@ class WalkFetchConnection extends BaseFetchConnection {
throw e; throw e;
} }
objectDigest.reset(); final int type = uol.getType();
objectDigest.update(Constants.encodedTypeString(uol.getType())); final byte[] raw = uol.getCachedBytes();
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));
}
if (objCheck != null) { if (objCheck != null) {
try { try {
objCheck.check(uol.getType(), uol.getCachedBytes()); objCheck.check(type, raw);
} catch (CorruptObjectException e) { } catch (CorruptObjectException e) {
throw new TransportException(MessageFormat.format(JGitText.get().transportExceptionInvalid 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) ObjectId act = inserter.insert(type, raw);
throws IOException, ObjectWritingException { if (!AnyObjectId.equals(id, act)) {
final File tmp; throw new TransportException(MessageFormat.format(JGitText.get().incorrectHashFor
, id.name(), act.name(), Constants.typeString(type), compressed.length));
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;
} }
inserter.flush();
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()));
} }
private Collection<WalkRemoteObjectDatabase> expandOneAlternate( private Collection<WalkRemoteObjectDatabase> expandOneAlternate(

Loading…
Cancel
Save