diff --git a/cache.h b/cache.h
--- a/cache.h
+++ b/cache.h
@@ -159,7 +159,7 @@ extern int write_sha1_file(void *buf, un
extern int check_sha1_signature(unsigned char *sha1, void *buf, unsigned long size, const char *type);
extern int sha1_delta_base(const unsigned char *, unsigned char *);
-
+extern int sha1_file_size(const unsigned char *, unsigned long *);
/* Read a tree into the cache */
extern int read_tree(void *buffer, unsigned long size, int stage);
diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -333,7 +333,6 @@ int diff_populate_filespec(struct diff_f
close(fd);
}
else {
- /* We cannot do size only for SHA1 blobs */
char type[20];
struct sha1_size_cache *e;
@@ -343,11 +342,13 @@ int diff_populate_filespec(struct diff_f
s->size = e->size;
return 0;
}
+ if (!sha1_file_size(s->sha1, &s->size))
+ locate_size_cache(s->sha1, s->size);
+ }
+ else {
+ s->data = read_sha1_file(s->sha1, type, &s->size);
+ s->should_free = 1;
}
- s->data = read_sha1_file(s->sha1, type, &s->size);
- s->should_free = 1;
- if (s->data && size_only)
- locate_size_cache(s->sha1, s->size);
}
return 0;
}diff --git a/sha1_file.c b/sha1_file.c
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -387,6 +387,84 @@ int sha1_delta_base(const unsigned char
return status;
}
+int sha1_file_size(const unsigned char *sha1, unsigned long *sizep)
+{
+ unsigned long mapsize, size;
+ void *map;
+ char type[20];
+ char buffer[200];
+ z_stream stream;
+ int ret, bytes, status;
+
+ map = map_sha1_file(sha1, &mapsize);
+ if (!map)
+ return 0;
+ ret = unpack_sha1_header(&stream, map, mapsize, buffer,
+ sizeof(buffer));
+ status = -1;
+
+ if (ret < Z_OK || sscanf(buffer, "%10s %lu", type, &size) != 2)
+ goto out;
+ if (strcmp(type, "delta")) {
+ *sizep = size;
+ status = 0;
+ goto out;
+ }
+
+ /* We are dealing with delta object. Inflated, the first 20
+ * bytes hold the base object SHA1, and delta data follows
+ * immediately after it.
+ */
+ bytes = strlen(buffer) + 1;
+ if (size < bytes + 6 + 20)
+ goto out; /* the smallest delta size is 6 bytes */
+
+ memmove(buffer, buffer + bytes, stream.total_out - bytes);
+ bytes = stream.total_out - bytes;
+ if (bytes < sizeof(buffer) && ret == Z_OK) {
+ stream.next_out = buffer + bytes;
+ stream.avail_out = sizeof(buffer) - bytes;
+ while (inflate(&stream, Z_FINISH) == Z_OK)
+ ; /* nothing */
+ }
+
+ /* We have read initial part of the delta, which starts at
+ * buffer+20. Borrow code from patch-delta to read the
+ * result size.
+ */
+ {
+ const unsigned char *data = buffer + 20;
+ unsigned char cmd;
+ int i;
+
+ /* Skip over the source size; we are not interested in
+ * it and we cannot verify it because we do not want
+ * to read the base object.
+ */
+ cmd = *data++;
+ while (cmd) {
+ if (cmd & 1)
+ data++;
+ cmd >>= 1;
+ }
+ /* Read the result size */
+ size = i = 0;
+ cmd = *data++;
+ while (cmd) {
+ if (cmd & 1)
+ size |= *data++ << i;
+ i += 8;
+ cmd >>= 1;
+ }
+ *sizep = size;
+ }
+ status = 0;
+ out:
+ inflateEnd(&stream);
+ munmap(map, mapsize);
+ return status;
+}
+
void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size)
{
unsigned long mapsize;
------------