aboutsummaryrefslogtreecommitdiffstats
path: root/object-file.c
diff options
context:
space:
mode:
Diffstat (limited to 'object-file.c')
-rw-r--r--object-file.c657
1 files changed, 497 insertions, 160 deletions
diff --git a/object-file.c b/object-file.c
index 8d87720dd5..726e41a047 100644
--- a/object-file.c
+++ b/object-file.c
@@ -6,9 +6,12 @@
* This handles basic git object files - packing, unpacking,
* creation etc.
*/
+
+#define USE_THE_REPOSITORY_VARIABLE
+#define DISABLE_SIGN_COMPARE_WARNINGS
+
#include "git-compat-util.h"
#include "abspath.h"
-#include "alloc.h"
#include "config.h"
#include "convert.h"
#include "environment.h"
@@ -16,24 +19,16 @@
#include "hex.h"
#include "string-list.h"
#include "lockfile.h"
-#include "delta.h"
#include "pack.h"
-#include "blob.h"
#include "commit.h"
#include "run-command.h"
-#include "tag.h"
-#include "tree.h"
-#include "tree-walk.h"
#include "refs.h"
-#include "pack-revindex.h"
-#include "hash-lookup.h"
#include "bulk-checkin.h"
#include "repository.h"
#include "replace-object.h"
#include "streaming.h"
#include "dir.h"
#include "list.h"
-#include "mergesort.h"
#include "quote.h"
#include "packfile.h"
#include "object-file.h"
@@ -44,36 +39,24 @@
#include "setup.h"
#include "submodule.h"
#include "fsck.h"
-#include "wrapper.h"
+#include "loose.h"
+#include "object-file-convert.h"
/* The maximum size for an object header. */
#define MAX_HEADER_LEN 32
-
-#define EMPTY_TREE_SHA1_BIN_LITERAL \
- "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \
- "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04"
-#define EMPTY_TREE_SHA256_BIN_LITERAL \
- "\x6e\xf1\x9b\x41\x22\x5c\x53\x69\xf1\xc1" \
- "\x04\xd4\x5d\x8d\x85\xef\xa9\xb0\x57\xb5" \
- "\x3b\x14\xb4\xb9\xb9\x39\xdd\x74\xde\xcc" \
- "\x53\x21"
-
-#define EMPTY_BLOB_SHA1_BIN_LITERAL \
- "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \
- "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91"
-#define EMPTY_BLOB_SHA256_BIN_LITERAL \
- "\x47\x3a\x0f\x4c\x3b\xe8\xa9\x36\x81\xa2" \
- "\x67\xe3\xb1\xe9\xa7\xdc\xda\x11\x85\x43" \
- "\x6f\xe1\x41\xf7\x74\x91\x20\xa3\x03\x72" \
- "\x18\x13"
-
static const struct object_id empty_tree_oid = {
- .hash = EMPTY_TREE_SHA1_BIN_LITERAL,
+ .hash = {
+ 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60,
+ 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04
+ },
.algo = GIT_HASH_SHA1,
};
static const struct object_id empty_blob_oid = {
- .hash = EMPTY_BLOB_SHA1_BIN_LITERAL,
+ .hash = {
+ 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1, 0xd6, 0x43, 0x4b, 0x8b,
+ 0x29, 0xae, 0x77, 0x5a, 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91
+ },
.algo = GIT_HASH_SHA1,
};
static const struct object_id null_oid_sha1 = {
@@ -81,11 +64,21 @@ static const struct object_id null_oid_sha1 = {
.algo = GIT_HASH_SHA1,
};
static const struct object_id empty_tree_oid_sha256 = {
- .hash = EMPTY_TREE_SHA256_BIN_LITERAL,
+ .hash = {
+ 0x6e, 0xf1, 0x9b, 0x41, 0x22, 0x5c, 0x53, 0x69, 0xf1, 0xc1,
+ 0x04, 0xd4, 0x5d, 0x8d, 0x85, 0xef, 0xa9, 0xb0, 0x57, 0xb5,
+ 0x3b, 0x14, 0xb4, 0xb9, 0xb9, 0x39, 0xdd, 0x74, 0xde, 0xcc,
+ 0x53, 0x21
+ },
.algo = GIT_HASH_SHA256,
};
static const struct object_id empty_blob_oid_sha256 = {
- .hash = EMPTY_BLOB_SHA256_BIN_LITERAL,
+ .hash = {
+ 0x47, 0x3a, 0x0f, 0x4c, 0x3b, 0xe8, 0xa9, 0x36, 0x81, 0xa2,
+ 0x67, 0xe3, 0xb1, 0xe9, 0xa7, 0xdc, 0xda, 0x11, 0x85, 0x43,
+ 0x6f, 0xe1, 0x41, 0xf7, 0x74, 0x91, 0x20, 0xa3, 0x03, 0x72,
+ 0x18, 0x13
+ },
.algo = GIT_HASH_SHA256,
};
static const struct object_id null_oid_sha256 = {
@@ -93,57 +86,90 @@ static const struct object_id null_oid_sha256 = {
.algo = GIT_HASH_SHA256,
};
-static void git_hash_sha1_init(git_hash_ctx *ctx)
+static void git_hash_sha1_init(struct git_hash_ctx *ctx)
{
- git_SHA1_Init(&ctx->sha1);
+ ctx->algop = &hash_algos[GIT_HASH_SHA1];
+ git_SHA1_Init(&ctx->state.sha1);
}
-static void git_hash_sha1_clone(git_hash_ctx *dst, const git_hash_ctx *src)
+static void git_hash_sha1_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src)
{
- git_SHA1_Clone(&dst->sha1, &src->sha1);
+ dst->algop = src->algop;
+ git_SHA1_Clone(&dst->state.sha1, &src->state.sha1);
}
-static void git_hash_sha1_update(git_hash_ctx *ctx, const void *data, size_t len)
+static void git_hash_sha1_update(struct git_hash_ctx *ctx, const void *data, size_t len)
{
- git_SHA1_Update(&ctx->sha1, data, len);
+ git_SHA1_Update(&ctx->state.sha1, data, len);
}
-static void git_hash_sha1_final(unsigned char *hash, git_hash_ctx *ctx)
+static void git_hash_sha1_final(unsigned char *hash, struct git_hash_ctx *ctx)
{
- git_SHA1_Final(hash, &ctx->sha1);
+ git_SHA1_Final(hash, &ctx->state.sha1);
}
-static void git_hash_sha1_final_oid(struct object_id *oid, git_hash_ctx *ctx)
+static void git_hash_sha1_final_oid(struct object_id *oid, struct git_hash_ctx *ctx)
{
- git_SHA1_Final(oid->hash, &ctx->sha1);
+ git_SHA1_Final(oid->hash, &ctx->state.sha1);
memset(oid->hash + GIT_SHA1_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA1_RAWSZ);
oid->algo = GIT_HASH_SHA1;
}
+static void git_hash_sha1_init_unsafe(struct git_hash_ctx *ctx)
+{
+ ctx->algop = unsafe_hash_algo(&hash_algos[GIT_HASH_SHA1]);
+ git_SHA1_Init_unsafe(&ctx->state.sha1_unsafe);
+}
+
+static void git_hash_sha1_clone_unsafe(struct git_hash_ctx *dst, const struct git_hash_ctx *src)
+{
+ dst->algop = src->algop;
+ git_SHA1_Clone_unsafe(&dst->state.sha1_unsafe, &src->state.sha1_unsafe);
+}
+
+static void git_hash_sha1_update_unsafe(struct git_hash_ctx *ctx, const void *data,
+ size_t len)
+{
+ git_SHA1_Update_unsafe(&ctx->state.sha1_unsafe, data, len);
+}
+
+static void git_hash_sha1_final_unsafe(unsigned char *hash, struct git_hash_ctx *ctx)
+{
+ git_SHA1_Final_unsafe(hash, &ctx->state.sha1_unsafe);
+}
-static void git_hash_sha256_init(git_hash_ctx *ctx)
+static void git_hash_sha1_final_oid_unsafe(struct object_id *oid, struct git_hash_ctx *ctx)
{
- git_SHA256_Init(&ctx->sha256);
+ git_SHA1_Final_unsafe(oid->hash, &ctx->state.sha1_unsafe);
+ memset(oid->hash + GIT_SHA1_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA1_RAWSZ);
+ oid->algo = GIT_HASH_SHA1;
}
-static void git_hash_sha256_clone(git_hash_ctx *dst, const git_hash_ctx *src)
+static void git_hash_sha256_init(struct git_hash_ctx *ctx)
{
- git_SHA256_Clone(&dst->sha256, &src->sha256);
+ ctx->algop = unsafe_hash_algo(&hash_algos[GIT_HASH_SHA256]);
+ git_SHA256_Init(&ctx->state.sha256);
}
-static void git_hash_sha256_update(git_hash_ctx *ctx, const void *data, size_t len)
+static void git_hash_sha256_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src)
{
- git_SHA256_Update(&ctx->sha256, data, len);
+ dst->algop = src->algop;
+ git_SHA256_Clone(&dst->state.sha256, &src->state.sha256);
}
-static void git_hash_sha256_final(unsigned char *hash, git_hash_ctx *ctx)
+static void git_hash_sha256_update(struct git_hash_ctx *ctx, const void *data, size_t len)
{
- git_SHA256_Final(hash, &ctx->sha256);
+ git_SHA256_Update(&ctx->state.sha256, data, len);
}
-static void git_hash_sha256_final_oid(struct object_id *oid, git_hash_ctx *ctx)
+static void git_hash_sha256_final(unsigned char *hash, struct git_hash_ctx *ctx)
{
- git_SHA256_Final(oid->hash, &ctx->sha256);
+ git_SHA256_Final(hash, &ctx->state.sha256);
+}
+
+static void git_hash_sha256_final_oid(struct object_id *oid, struct git_hash_ctx *ctx)
+{
+ git_SHA256_Final(oid->hash, &ctx->state.sha256);
/*
* This currently does nothing, so the compiler should optimize it out,
* but keep it in case we extend the hash size again.
@@ -152,18 +178,18 @@ static void git_hash_sha256_final_oid(struct object_id *oid, git_hash_ctx *ctx)
oid->algo = GIT_HASH_SHA256;
}
-static void git_hash_unknown_init(git_hash_ctx *ctx UNUSED)
+static void git_hash_unknown_init(struct git_hash_ctx *ctx UNUSED)
{
BUG("trying to init unknown hash");
}
-static void git_hash_unknown_clone(git_hash_ctx *dst UNUSED,
- const git_hash_ctx *src UNUSED)
+static void git_hash_unknown_clone(struct git_hash_ctx *dst UNUSED,
+ const struct git_hash_ctx *src UNUSED)
{
BUG("trying to clone unknown hash");
}
-static void git_hash_unknown_update(git_hash_ctx *ctx UNUSED,
+static void git_hash_unknown_update(struct git_hash_ctx *ctx UNUSED,
const void *data UNUSED,
size_t len UNUSED)
{
@@ -171,17 +197,33 @@ static void git_hash_unknown_update(git_hash_ctx *ctx UNUSED,
}
static void git_hash_unknown_final(unsigned char *hash UNUSED,
- git_hash_ctx *ctx UNUSED)
+ struct git_hash_ctx *ctx UNUSED)
{
BUG("trying to finalize unknown hash");
}
static void git_hash_unknown_final_oid(struct object_id *oid UNUSED,
- git_hash_ctx *ctx UNUSED)
+ struct git_hash_ctx *ctx UNUSED)
{
BUG("trying to finalize unknown hash");
}
+static const struct git_hash_algo sha1_unsafe_algo = {
+ .name = "sha1",
+ .format_id = GIT_SHA1_FORMAT_ID,
+ .rawsz = GIT_SHA1_RAWSZ,
+ .hexsz = GIT_SHA1_HEXSZ,
+ .blksz = GIT_SHA1_BLKSZ,
+ .init_fn = git_hash_sha1_init_unsafe,
+ .clone_fn = git_hash_sha1_clone_unsafe,
+ .update_fn = git_hash_sha1_update_unsafe,
+ .final_fn = git_hash_sha1_final_unsafe,
+ .final_oid_fn = git_hash_sha1_final_oid_unsafe,
+ .empty_tree = &empty_tree_oid,
+ .empty_blob = &empty_blob_oid,
+ .null_oid = &null_oid_sha1,
+};
+
const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
{
.name = NULL,
@@ -209,6 +251,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
.update_fn = git_hash_sha1_update,
.final_fn = git_hash_sha1_final,
.final_oid_fn = git_hash_sha1_final_oid,
+ .unsafe = &sha1_unsafe_algo,
.empty_tree = &empty_tree_oid,
.empty_blob = &empty_blob_oid,
.null_oid = &null_oid_sha1,
@@ -235,16 +278,10 @@ const struct object_id *null_oid(void)
return the_hash_algo->null_oid;
}
-const char *empty_tree_oid_hex(void)
+const char *empty_tree_oid_hex(const struct git_hash_algo *algop)
{
static char buf[GIT_MAX_HEXSZ + 1];
- return oid_to_hex_r(buf, the_hash_algo->empty_tree);
-}
-
-const char *empty_blob_oid_hex(void)
-{
- static char buf[GIT_MAX_HEXSZ + 1];
- return oid_to_hex_r(buf, the_hash_algo->empty_blob);
+ return oid_to_hex_r(buf, algop->empty_tree);
}
int hash_algo_by_name(const char *name)
@@ -276,36 +313,43 @@ int hash_algo_by_length(int len)
return GIT_HASH_UNKNOWN;
}
+const struct git_hash_algo *unsafe_hash_algo(const struct git_hash_algo *algop)
+{
+ /* If we have a faster "unsafe" implementation, use that. */
+ if (algop->unsafe)
+ return algop->unsafe;
+ /* Otherwise use the default one. */
+ return algop;
+}
+
/*
* This is meant to hold a *small* number of objects that you would
* want repo_read_object_file() to be able to return, but yet you do not want
* to write them into the object store (e.g. a browse-only
* application).
*/
-static struct cached_object {
+static struct cached_object_entry {
struct object_id oid;
- enum object_type type;
- void *buf;
- unsigned long size;
+ struct cached_object {
+ enum object_type type;
+ const void *buf;
+ unsigned long size;
+ } value;
} *cached_objects;
static int cached_object_nr, cached_object_alloc;
-static struct cached_object empty_tree = {
- .oid = {
- .hash = EMPTY_TREE_SHA1_BIN_LITERAL,
- },
- .type = OBJ_TREE,
- .buf = "",
-};
-
-static struct cached_object *find_cached_object(const struct object_id *oid)
+static const struct cached_object *find_cached_object(const struct object_id *oid)
{
+ static const struct cached_object empty_tree = {
+ .type = OBJ_TREE,
+ .buf = "",
+ };
int i;
- struct cached_object *co = cached_objects;
+ const struct cached_object_entry *co = cached_objects;
for (i = 0; i < cached_object_nr; i++, co++) {
if (oideq(&co->oid, oid))
- return co;
+ return &co->value;
}
if (oideq(oid, the_hash_algo->empty_tree))
return &empty_tree;
@@ -350,7 +394,7 @@ int mkdir_in_gitdir(const char *path)
}
strbuf_release(&sb);
}
- return adjust_shared_perm(path);
+ return adjust_shared_perm(the_repository, path);
}
static enum scld_error safe_create_leading_directories_1(char *path, int share)
@@ -399,7 +443,7 @@ static enum scld_error safe_create_leading_directories_1(char *path, int share)
ret = SCLD_VANISHED;
else
ret = SCLD_FAILED;
- } else if (share && adjust_shared_perm(path)) {
+ } else if (share && adjust_shared_perm(the_repository, path)) {
ret = SCLD_PERMS;
}
*slash = slash_character;
@@ -430,6 +474,39 @@ enum scld_error safe_create_leading_directories_const(const char *path)
return result;
}
+int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)
+{
+ int fd;
+ /*
+ * we let the umask do its job, don't try to be more
+ * restrictive except to remove write permission.
+ */
+ int mode = 0444;
+ repo_git_path_replace(the_repository, temp_filename, "objects/%s", pattern);
+ fd = git_mkstemp_mode(temp_filename->buf, mode);
+ if (0 <= fd)
+ return fd;
+
+ /* slow path */
+ /* some mkstemp implementations erase temp_filename on failure */
+ repo_git_path_replace(the_repository, temp_filename, "objects/%s", pattern);
+ safe_create_leading_directories(temp_filename->buf);
+ return xmkstemp_mode(temp_filename->buf, mode);
+}
+
+int odb_pack_keep(const char *name)
+{
+ int fd;
+
+ fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
+ if (0 <= fd)
+ return fd;
+
+ /* slow path */
+ safe_create_leading_directories_const(name);
+ return open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
+}
+
static void fill_loose_path(struct strbuf *buf, const struct object_id *oid)
{
int i;
@@ -646,7 +723,7 @@ static void read_info_alternates(struct repository *r,
void add_to_alternates_file(const char *reference)
{
struct lock_file lock = LOCK_INIT;
- char *alts = git_pathdup("objects/info/alternates");
+ char *alts = repo_git_path(the_repository, "objects/info/alternates");
FILE *in, *out;
int found = 0;
@@ -1094,9 +1171,11 @@ int check_object_signature(struct repository *r, const struct object_id *oid,
void *buf, unsigned long size,
enum object_type type)
{
+ const struct git_hash_algo *algo =
+ oid->algo ? &hash_algos[oid->algo] : r->hash_algo;
struct object_id real_oid;
- hash_object_file(r->hash_algo, buf, size, type, &real_oid);
+ hash_object_file(algo, buf, size, type, &real_oid);
return !oideq(oid, &real_oid) ? -1 : 0;
}
@@ -1107,7 +1186,7 @@ int stream_object_signature(struct repository *r, const struct object_id *oid)
unsigned long size;
enum object_type obj_type;
struct git_istream *st;
- git_hash_ctx c;
+ struct git_hash_ctx c;
char hdr[MAX_HEADER_LEN];
int hdrlen;
@@ -1120,7 +1199,7 @@ int stream_object_signature(struct repository *r, const struct object_id *oid)
/* Sha1.. */
r->hash_algo->init_fn(&c);
- r->hash_algo->update_fn(&c, hdr, hdrlen);
+ git_hash_update(&c, hdr, hdrlen);
for (;;) {
char buf[1024 * 16];
ssize_t readlen = read_istream(st, buf, sizeof(buf));
@@ -1131,9 +1210,9 @@ int stream_object_signature(struct repository *r, const struct object_id *oid)
}
if (!readlen)
break;
- r->hash_algo->update_fn(&c, buf, readlen);
+ git_hash_update(&c, buf, readlen);
}
- r->hash_algo->final_oid_fn(&real_oid, &c);
+ git_hash_final_oid(&real_oid, &c);
close_istream(st);
return !oideq(oid, &real_oid) ? -1 : 0;
}
@@ -1452,7 +1531,7 @@ static int loose_object_info(struct repository *r,
int allow_unknown = flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE;
if (oi->delta_base_oid)
- oidclr(oi->delta_base_oid);
+ oidclr(oi->delta_base_oid, the_repository->hash_algo);
/*
* If we don't care about type or size, then we don't
@@ -1561,7 +1640,7 @@ static int do_oid_object_info_extended(struct repository *r,
struct object_info *oi, unsigned flags)
{
static struct object_info blank_oi = OBJECT_INFO_INIT;
- struct cached_object *co;
+ const struct cached_object *co;
struct pack_entry e;
int rtype;
const struct object_id *real = oid;
@@ -1586,7 +1665,7 @@ static int do_oid_object_info_extended(struct repository *r,
if (oi->disk_sizep)
*(oi->disk_sizep) = 0;
if (oi->delta_base_oid)
- oidclr(oi->delta_base_oid);
+ oidclr(oi->delta_base_oid, the_repository->hash_algo);
if (oi->type_name)
strbuf_addstr(oi->type_name, type_name(co->type));
if (oi->contentp)
@@ -1662,10 +1741,101 @@ static int do_oid_object_info_extended(struct repository *r,
return 0;
}
+static int oid_object_info_convert(struct repository *r,
+ const struct object_id *input_oid,
+ struct object_info *input_oi, unsigned flags)
+{
+ const struct git_hash_algo *input_algo = &hash_algos[input_oid->algo];
+ int do_die = flags & OBJECT_INFO_DIE_IF_CORRUPT;
+ struct strbuf type_name = STRBUF_INIT;
+ struct object_id oid, delta_base_oid;
+ struct object_info new_oi, *oi;
+ unsigned long size;
+ void *content;
+ int ret;
+
+ if (repo_oid_to_algop(r, input_oid, the_hash_algo, &oid)) {
+ if (do_die)
+ die(_("missing mapping of %s to %s"),
+ oid_to_hex(input_oid), the_hash_algo->name);
+ return -1;
+ }
+
+ /* Is new_oi needed? */
+ oi = input_oi;
+ if (input_oi && (input_oi->delta_base_oid || input_oi->sizep ||
+ input_oi->contentp)) {
+ new_oi = *input_oi;
+ /* Does delta_base_oid need to be converted? */
+ if (input_oi->delta_base_oid)
+ new_oi.delta_base_oid = &delta_base_oid;
+ /* Will the attributes differ when converted? */
+ if (input_oi->sizep || input_oi->contentp) {
+ new_oi.contentp = &content;
+ new_oi.sizep = &size;
+ new_oi.type_name = &type_name;
+ }
+ oi = &new_oi;
+ }
+
+ ret = oid_object_info_extended(r, &oid, oi, flags);
+ if (ret)
+ return -1;
+ if (oi == input_oi)
+ return ret;
+
+ if (new_oi.contentp) {
+ struct strbuf outbuf = STRBUF_INIT;
+ enum object_type type;
+
+ type = type_from_string_gently(type_name.buf, type_name.len,
+ !do_die);
+ if (type == -1)
+ return -1;
+ if (type != OBJ_BLOB) {
+ ret = convert_object_file(&outbuf,
+ the_hash_algo, input_algo,
+ content, size, type, !do_die);
+ free(content);
+ if (ret == -1)
+ return -1;
+ size = outbuf.len;
+ content = strbuf_detach(&outbuf, NULL);
+ }
+ if (input_oi->sizep)
+ *input_oi->sizep = size;
+ if (input_oi->contentp)
+ *input_oi->contentp = content;
+ else
+ free(content);
+ if (input_oi->type_name)
+ *input_oi->type_name = type_name;
+ else
+ strbuf_release(&type_name);
+ }
+ if (new_oi.delta_base_oid == &delta_base_oid) {
+ if (repo_oid_to_algop(r, &delta_base_oid, input_algo,
+ input_oi->delta_base_oid)) {
+ if (do_die)
+ die(_("missing mapping of %s to %s"),
+ oid_to_hex(&delta_base_oid),
+ input_algo->name);
+ return -1;
+ }
+ }
+ input_oi->whence = new_oi.whence;
+ input_oi->u = new_oi.u;
+ return ret;
+}
+
int oid_object_info_extended(struct repository *r, const struct object_id *oid,
struct object_info *oi, unsigned flags)
{
int ret;
+
+ if (oid->algo && (hash_algo_by_ptr(r->hash_algo) != oid->algo))
+ return oid_object_info_convert(r, oid, oi, flags);
+
obj_read_lock();
ret = do_oid_object_info_extended(r, oid, oi, flags);
obj_read_unlock();
@@ -1692,7 +1862,8 @@ int oid_object_info(struct repository *r,
int pretend_object_file(void *buf, unsigned long len, enum object_type type,
struct object_id *oid)
{
- struct cached_object *co;
+ struct cached_object_entry *co;
+ char *co_buf;
hash_object_file(the_hash_algo, buf, len, type, oid);
if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
@@ -1700,10 +1871,11 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
return 0;
ALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);
co = &cached_objects[cached_object_nr++];
- co->size = len;
- co->type = type;
- co->buf = xmalloc(len);
- memcpy(co->buf, buf, len);
+ co->value.size = len;
+ co->value.type = type;
+ co_buf = xmalloc(len);
+ memcpy(co_buf, buf, len);
+ co->value.buf = co_buf;
oidcpy(&co->oid, oid);
return 0;
}
@@ -1779,15 +1951,15 @@ void *read_object_with_reference(struct repository *r,
}
}
-static void hash_object_body(const struct git_hash_algo *algo, git_hash_ctx *c,
+static void hash_object_body(const struct git_hash_algo *algo, struct git_hash_ctx *c,
const void *buf, unsigned long len,
struct object_id *oid,
char *hdr, int *hdrlen)
{
algo->init_fn(c);
- algo->update_fn(c, hdr, *hdrlen);
- algo->update_fn(c, buf, len);
- algo->final_oid_fn(oid, c);
+ git_hash_update(c, hdr, *hdrlen);
+ git_hash_update(c, buf, len);
+ git_hash_final_oid(oid, c);
}
static void write_object_file_prepare(const struct git_hash_algo *algo,
@@ -1795,7 +1967,7 @@ static void write_object_file_prepare(const struct git_hash_algo *algo,
enum object_type type, struct object_id *oid,
char *hdr, int *hdrlen)
{
- git_hash_ctx c;
+ struct git_hash_ctx c;
/* Generate the header */
*hdrlen = format_object_header(hdr, *hdrlen, type, len);
@@ -1809,23 +1981,91 @@ static void write_object_file_prepare_literally(const struct git_hash_algo *algo
const char *type, struct object_id *oid,
char *hdr, int *hdrlen)
{
- git_hash_ctx c;
+ struct git_hash_ctx c;
*hdrlen = format_object_header_literally(hdr, *hdrlen, type, len);
hash_object_body(algo, &c, buf, len, oid, hdr, hdrlen);
}
+#define CHECK_COLLISION_DEST_VANISHED -2
+
+static int check_collision(const char *source, const char *dest)
+{
+ char buf_source[4096], buf_dest[4096];
+ int fd_source = -1, fd_dest = -1;
+ int ret = 0;
+
+ fd_source = open(source, O_RDONLY);
+ if (fd_source < 0) {
+ ret = error_errno(_("unable to open %s"), source);
+ goto out;
+ }
+
+ fd_dest = open(dest, O_RDONLY);
+ if (fd_dest < 0) {
+ if (errno != ENOENT)
+ ret = error_errno(_("unable to open %s"), dest);
+ else
+ ret = CHECK_COLLISION_DEST_VANISHED;
+ goto out;
+ }
+
+ while (1) {
+ ssize_t sz_a, sz_b;
+
+ sz_a = read_in_full(fd_source, buf_source, sizeof(buf_source));
+ if (sz_a < 0) {
+ ret = error_errno(_("unable to read %s"), source);
+ goto out;
+ }
+
+ sz_b = read_in_full(fd_dest, buf_dest, sizeof(buf_dest));
+ if (sz_b < 0) {
+ ret = error_errno(_("unable to read %s"), dest);
+ goto out;
+ }
+
+ if (sz_a != sz_b || memcmp(buf_source, buf_dest, sz_a)) {
+ ret = error(_("files '%s' and '%s' differ in contents"),
+ source, dest);
+ goto out;
+ }
+
+ if (sz_a < sizeof(buf_source))
+ break;
+ }
+
+out:
+ if (fd_source > -1)
+ close(fd_source);
+ if (fd_dest > -1)
+ close(fd_dest);
+ return ret;
+}
+
/*
* Move the just written object into its final resting place.
*/
int finalize_object_file(const char *tmpfile, const char *filename)
{
- int ret = 0;
+ return finalize_object_file_flags(tmpfile, filename, 0);
+}
+
+int finalize_object_file_flags(const char *tmpfile, const char *filename,
+ enum finalize_object_file_flags flags)
+{
+ unsigned retries = 0;
+ int ret;
+
+retry:
+ ret = 0;
if (object_creation_mode == OBJECT_CREATION_USES_RENAMES)
goto try_rename;
else if (link(tmpfile, filename))
ret = errno;
+ else
+ unlink_or_warn(tmpfile);
/*
* Coda hack - coda doesn't like cross-directory links,
@@ -1839,21 +2079,39 @@ int finalize_object_file(const char *tmpfile, const char *filename)
* left to unlink.
*/
if (ret && ret != EEXIST) {
+ struct stat st;
+
try_rename:
- if (!rename(tmpfile, filename))
+ if (!stat(filename, &st))
+ ret = EEXIST;
+ else if (!rename(tmpfile, filename))
goto out;
- ret = errno;
+ else
+ ret = errno;
}
- unlink_or_warn(tmpfile);
if (ret) {
if (ret != EEXIST) {
+ int saved_errno = errno;
+ unlink_or_warn(tmpfile);
+ errno = saved_errno;
return error_errno(_("unable to write file %s"), filename);
}
- /* FIXME!!! Collision check here ? */
+ if (!(flags & FOF_SKIP_COLLISION_CHECK)) {
+ ret = check_collision(tmpfile, filename);
+ if (ret == CHECK_COLLISION_DEST_VANISHED) {
+ if (retries++ > 5)
+ return error(_("unable to write repeatedly vanishing file %s"),
+ filename);
+ goto retry;
+ }
+ else if (ret)
+ return -1;
+ }
+ unlink_or_warn(tmpfile);
}
out:
- if (adjust_shared_perm(filename))
+ if (adjust_shared_perm(the_repository, filename))
return error(_("unable to set permission to '%s'"), filename);
return 0;
}
@@ -1929,7 +2187,7 @@ static int create_tmpfile(struct strbuf *tmp, const char *filename)
strbuf_add(tmp, filename, dirlen - 1);
if (mkdir(tmp->buf, 0777) && errno != EEXIST)
return -1;
- if (adjust_shared_perm(tmp->buf))
+ if (adjust_shared_perm(the_repository, tmp->buf))
return -1;
/* Try again */
@@ -1954,9 +2212,12 @@ static int start_loose_object_common(struct strbuf *tmp_file,
const char *filename, unsigned flags,
git_zstream *stream,
unsigned char *buf, size_t buflen,
- git_hash_ctx *c,
+ struct git_hash_ctx *c, struct git_hash_ctx *compat_c,
char *hdr, int hdrlen)
{
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *algo = repo->hash_algo;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
int fd;
fd = create_tmpfile(tmp_file, filename);
@@ -1966,7 +2227,7 @@ static int start_loose_object_common(struct strbuf *tmp_file,
else if (errno == EACCES)
return error(_("insufficient permission for adding "
"an object to repository database %s"),
- get_object_directory());
+ repo_get_object_directory(the_repository));
else
return error_errno(
_("unable to create temporary file"));
@@ -1976,14 +2237,18 @@ static int start_loose_object_common(struct strbuf *tmp_file,
git_deflate_init(stream, zlib_compression_level);
stream->next_out = buf;
stream->avail_out = buflen;
- the_hash_algo->init_fn(c);
+ algo->init_fn(c);
+ if (compat && compat_c)
+ compat->init_fn(compat_c);
/* Start to feed header to zlib stream */
stream->next_in = (unsigned char *)hdr;
stream->avail_in = hdrlen;
while (git_deflate(stream, 0) == Z_OK)
; /* nothing */
- the_hash_algo->update_fn(c, hdr, hdrlen);
+ git_hash_update(c, hdr, hdrlen);
+ if (compat && compat_c)
+ git_hash_update(compat_c, hdr, hdrlen);
return fd;
}
@@ -1992,16 +2257,20 @@ static int start_loose_object_common(struct strbuf *tmp_file,
* Common steps for the inner git_deflate() loop for writing loose
* objects. Returns what git_deflate() returns.
*/
-static int write_loose_object_common(git_hash_ctx *c,
+static int write_loose_object_common(struct git_hash_ctx *c, struct git_hash_ctx *compat_c,
git_zstream *stream, const int flush,
unsigned char *in0, const int fd,
unsigned char *compressed,
const size_t compressed_len)
{
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
int ret;
ret = git_deflate(stream, flush ? Z_FINISH : 0);
- the_hash_algo->update_fn(c, in0, stream->next_in - in0);
+ git_hash_update(c, in0, stream->next_in - in0);
+ if (compat && compat_c)
+ git_hash_update(compat_c, in0, stream->next_in - in0);
if (write_in_full(fd, compressed, stream->next_out - compressed) < 0)
die_errno(_("unable to write loose object file"));
stream->next_out = compressed;
@@ -2016,15 +2285,20 @@ static int write_loose_object_common(git_hash_ctx *c,
* - End the compression of zlib stream.
* - Get the calculated oid to "oid".
*/
-static int end_loose_object_common(git_hash_ctx *c, git_zstream *stream,
- struct object_id *oid)
+static int end_loose_object_common(struct git_hash_ctx *c, struct git_hash_ctx *compat_c,
+ git_zstream *stream, struct object_id *oid,
+ struct object_id *compat_oid)
{
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
int ret;
ret = git_deflate_end_gently(stream);
if (ret != Z_OK)
return ret;
- the_hash_algo->final_oid_fn(oid, c);
+ git_hash_final_oid(oid, c);
+ if (compat && compat_c)
+ git_hash_final_oid(compat_oid, compat_c);
return Z_OK;
}
@@ -2036,7 +2310,7 @@ static int write_loose_object(const struct object_id *oid, char *hdr,
int fd, ret;
unsigned char compressed[4096];
git_zstream stream;
- git_hash_ctx c;
+ struct git_hash_ctx c;
struct object_id parano_oid;
static struct strbuf tmp_file = STRBUF_INIT;
static struct strbuf filename = STRBUF_INIT;
@@ -2048,7 +2322,7 @@ static int write_loose_object(const struct object_id *oid, char *hdr,
fd = start_loose_object_common(&tmp_file, filename.buf, flags,
&stream, compressed, sizeof(compressed),
- &c, hdr, hdrlen);
+ &c, NULL, hdr, hdrlen);
if (fd < 0)
return -1;
@@ -2058,14 +2332,14 @@ static int write_loose_object(const struct object_id *oid, char *hdr,
do {
unsigned char *in0 = stream.next_in;
- ret = write_loose_object_common(&c, &stream, 1, in0, fd,
+ ret = write_loose_object_common(&c, NULL, &stream, 1, in0, fd,
compressed, sizeof(compressed));
} while (ret == Z_OK);
if (ret != Z_STREAM_END)
die(_("unable to deflate new object %s (%d)"), oid_to_hex(oid),
ret);
- ret = end_loose_object_common(&c, &stream, &parano_oid);
+ ret = end_loose_object_common(&c, NULL, &stream, &parano_oid, NULL);
if (ret != Z_OK)
die(_("deflateEnd on object %s failed (%d)"), oid_to_hex(oid),
ret);
@@ -2084,7 +2358,8 @@ static int write_loose_object(const struct object_id *oid, char *hdr,
warning_errno(_("failed utime() on %s"), tmp_file.buf);
}
- return finalize_object_file(tmp_file.buf, filename.buf);
+ return finalize_object_file_flags(tmp_file.buf, filename.buf,
+ FOF_SKIP_COLLISION_CHECK);
}
static int freshen_loose_object(const struct object_id *oid)
@@ -2110,10 +2385,12 @@ static int freshen_packed_object(const struct object_id *oid)
int stream_loose_object(struct input_stream *in_stream, size_t len,
struct object_id *oid)
{
+ const struct git_hash_algo *compat = the_repository->compat_hash_algo;
+ struct object_id compat_oid;
int fd, ret, err = 0, flush = 0;
unsigned char compressed[4096];
git_zstream stream;
- git_hash_ctx c;
+ struct git_hash_ctx c, compat_c;
struct strbuf tmp_file = STRBUF_INIT;
struct strbuf filename = STRBUF_INIT;
int dirlen;
@@ -2124,7 +2401,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
prepare_loose_object_bulk_checkin();
/* Since oid is not determined, save tmp file to odb path. */
- strbuf_addf(&filename, "%s/", get_object_directory());
+ strbuf_addf(&filename, "%s/", repo_get_object_directory(the_repository));
hdrlen = format_object_header(hdr, sizeof(hdr), OBJ_BLOB, len);
/*
@@ -2137,7 +2414,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
*/
fd = start_loose_object_common(&tmp_file, filename.buf, 0,
&stream, compressed, sizeof(compressed),
- &c, hdr, hdrlen);
+ &c, &compat_c, hdr, hdrlen);
if (fd < 0) {
err = -1;
goto cleanup;
@@ -2155,7 +2432,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
if (in_stream->is_finished)
flush = 1;
}
- ret = write_loose_object_common(&c, &stream, flush, in0, fd,
+ ret = write_loose_object_common(&c, &compat_c, &stream, flush, in0, fd,
compressed, sizeof(compressed));
/*
* Unlike write_loose_object(), we do not have the entire
@@ -2171,14 +2448,14 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
/*
* Common steps for write_loose_object and stream_loose_object to
- * end writing loose oject:
+ * end writing loose object:
*
* - End the compression of zlib stream.
* - Get the calculated oid.
*/
if (ret != Z_STREAM_END)
die(_("unable to stream deflate new object (%d)"), ret);
- ret = end_loose_object_common(&c, &stream, oid);
+ ret = end_loose_object_common(&c, &compat_c, &stream, oid, &compat_oid);
if (ret != Z_OK)
die(_("deflateEnd on stream object failed (%d)"), ret);
close_loose_object(fd, tmp_file.buf);
@@ -2204,7 +2481,10 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
strbuf_release(&dir);
}
- err = finalize_object_file(tmp_file.buf, filename.buf);
+ err = finalize_object_file_flags(tmp_file.buf, filename.buf,
+ FOF_SKIP_COLLISION_CHECK);
+ if (!err && compat)
+ err = repo_add_loose_object_map(the_repository, oid, &compat_oid);
cleanup:
strbuf_release(&tmp_file);
strbuf_release(&filename);
@@ -2213,19 +2493,42 @@ cleanup:
int write_object_file_flags(const void *buf, unsigned long len,
enum object_type type, struct object_id *oid,
- unsigned flags)
+ struct object_id *compat_oid_in, unsigned flags)
{
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *algo = repo->hash_algo;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
+ struct object_id compat_oid;
char hdr[MAX_HEADER_LEN];
int hdrlen = sizeof(hdr);
+ /* Generate compat_oid */
+ if (compat) {
+ if (compat_oid_in)
+ oidcpy(&compat_oid, compat_oid_in);
+ else if (type == OBJ_BLOB)
+ hash_object_file(compat, buf, len, type, &compat_oid);
+ else {
+ struct strbuf converted = STRBUF_INIT;
+ convert_object_file(&converted, algo, compat,
+ buf, len, type, 0);
+ hash_object_file(compat, converted.buf, converted.len,
+ type, &compat_oid);
+ strbuf_release(&converted);
+ }
+ }
+
/* Normally if we have it in the pack then we do not bother writing
* it out into .git/objects/??/?{38} file.
*/
- write_object_file_prepare(the_hash_algo, buf, len, type, oid, hdr,
- &hdrlen);
+ write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen);
if (freshen_packed_object(oid) || freshen_loose_object(oid))
return 0;
- return write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags);
+ if (write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags))
+ return -1;
+ if (compat)
+ return repo_add_loose_object_map(repo, oid, &compat_oid);
+ return 0;
}
int write_object_file_literally(const void *buf, unsigned long len,
@@ -2233,7 +2536,27 @@ int write_object_file_literally(const void *buf, unsigned long len,
unsigned flags)
{
char *header;
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *algo = repo->hash_algo;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
+ struct object_id compat_oid;
int hdrlen, status = 0;
+ int compat_type = -1;
+
+ if (compat) {
+ compat_type = type_from_string_gently(type, -1, 1);
+ if (compat_type == OBJ_BLOB)
+ hash_object_file(compat, buf, len, compat_type,
+ &compat_oid);
+ else if (compat_type != -1) {
+ struct strbuf converted = STRBUF_INIT;
+ convert_object_file(&converted, algo, compat,
+ buf, len, compat_type, 0);
+ hash_object_file(compat, converted.buf, converted.len,
+ compat_type, &compat_oid);
+ strbuf_release(&converted);
+ }
+ }
/* type string, SP, %lu of the length plus NUL must fit this */
hdrlen = strlen(type) + MAX_HEADER_LEN;
@@ -2246,6 +2569,8 @@ int write_object_file_literally(const void *buf, unsigned long len,
if (freshen_packed_object(oid) || freshen_loose_object(oid))
goto cleanup;
status = write_loose_object(oid, header, hdrlen, buf, len, 0, 0);
+ if (compat_type != -1)
+ return repo_add_loose_object_map(repo, oid, &compat_oid);
cleanup:
free(header);
@@ -2254,9 +2579,12 @@ cleanup:
int force_object_loose(const struct object_id *oid, time_t mtime)
{
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
void *buf;
unsigned long len;
struct object_info oi = OBJECT_INFO_INIT;
+ struct object_id compat_oid;
enum object_type type;
char hdr[MAX_HEADER_LEN];
int hdrlen;
@@ -2269,8 +2597,15 @@ int force_object_loose(const struct object_id *oid, time_t mtime)
oi.contentp = &buf;
if (oid_object_info_extended(the_repository, oid, &oi, 0))
return error(_("cannot read object for %s"), oid_to_hex(oid));
+ if (compat) {
+ if (repo_oid_to_algop(repo, oid, compat, &compat_oid))
+ return error(_("cannot map object %s to %s"),
+ oid_to_hex(oid), compat->name);
+ }
hdrlen = format_object_header(hdr, sizeof(hdr), type, len);
ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime, 0);
+ if (!ret && compat)
+ ret = repo_add_loose_object_map(the_repository, oid, &compat_oid);
free(buf);
return ret;
@@ -2308,24 +2643,24 @@ int repo_has_object_file(struct repository *r,
* report the minimal fsck error here, and rely on the caller to
* give more context.
*/
-static int hash_format_check_report(struct fsck_options *opts,
- const struct object_id *oid,
- enum object_type object_type,
- enum fsck_msg_type msg_type,
- enum fsck_msg_id msg_id,
- const char *message)
+static int hash_format_check_report(struct fsck_options *opts UNUSED,
+ void *fsck_report UNUSED,
+ enum fsck_msg_type msg_type UNUSED,
+ enum fsck_msg_id msg_id UNUSED,
+ const char *message)
{
error(_("object fails fsck: %s"), message);
return 1;
}
static int index_mem(struct index_state *istate,
- struct object_id *oid, void *buf, size_t size,
+ struct object_id *oid,
+ const void *buf, size_t size,
enum object_type type,
const char *path, unsigned flags)
{
+ struct strbuf nbuf = STRBUF_INIT;
int ret = 0;
- int re_allocated = 0;
int write_object = flags & HASH_WRITE_OBJECT;
if (!type)
@@ -2335,11 +2670,10 @@ static int index_mem(struct index_state *istate,
* Convert blobs to git internal format
*/
if ((type == OBJ_BLOB) && path) {
- struct strbuf nbuf = STRBUF_INIT;
if (convert_to_git(istate, path, buf, size, &nbuf,
get_conv_flags(flags))) {
- buf = strbuf_detach(&nbuf, &size);
- re_allocated = 1;
+ buf = nbuf.buf;
+ size = nbuf.len;
}
}
if (flags & HASH_FORMAT_CHECK) {
@@ -2356,8 +2690,8 @@ static int index_mem(struct index_state *istate,
ret = write_object_file(buf, size, type, oid);
else
hash_object_file(the_hash_algo, buf, size, type, oid);
- if (re_allocated)
- free(buf);
+
+ strbuf_release(&nbuf);
return ret;
}
@@ -2448,11 +2782,11 @@ static int index_core(struct index_state *istate,
* binary blobs, they generally do not want to get any conversion, and
* callers should avoid this code path when filters are requested.
*/
-static int index_stream(struct object_id *oid, int fd, size_t size,
- enum object_type type, const char *path,
- unsigned flags)
+static int index_blob_stream(struct object_id *oid, int fd, size_t size,
+ const char *path,
+ unsigned flags)
{
- return index_bulk_checkin(oid, fd, size, type, path, flags);
+ return index_blob_bulk_checkin(oid, fd, size, path, flags);
}
int index_fd(struct index_state *istate, struct object_id *oid,
@@ -2474,8 +2808,8 @@ int index_fd(struct index_state *istate, struct object_id *oid,
ret = index_core(istate, oid, fd, xsize_t(st->st_size),
type, path, flags);
else
- ret = index_stream(oid, fd, xsize_t(st->st_size), type, path,
- flags);
+ ret = index_blob_stream(oid, fd, xsize_t(st->st_size), path,
+ flags);
close(fd);
return ret;
}
@@ -2507,7 +2841,7 @@ int index_path(struct index_state *istate, struct object_id *oid,
strbuf_release(&sb);
break;
case S_IFDIR:
- return resolve_gitlink_ref(path, "HEAD", oid);
+ return repo_resolve_gitlink_ref(the_repository, path, "HEAD", oid);
default:
return error(_("%s: unsupported file type"), path);
}
@@ -2581,6 +2915,8 @@ int for_each_file_in_obj_subdir(unsigned int subdir_nr,
!hex_to_bytes(oid.hash + 1, de->d_name,
the_hash_algo->rawsz - 1)) {
oid_set_algo(&oid, the_hash_algo);
+ memset(oid.hash + the_hash_algo->rawsz, 0,
+ GIT_MAX_RAWSZ - the_hash_algo->rawsz);
if (obj_cb) {
r = obj_cb(&oid, path->buf, data);
if (r)
@@ -2714,14 +3050,14 @@ static int check_stream_oid(git_zstream *stream,
const char *path,
const struct object_id *expected_oid)
{
- git_hash_ctx c;
+ struct git_hash_ctx c;
struct object_id real_oid;
unsigned char buf[4096];
unsigned long total_read;
int status = Z_OK;
the_hash_algo->init_fn(&c);
- the_hash_algo->update_fn(&c, hdr, stream->total_out);
+ git_hash_update(&c, hdr, stream->total_out);
/*
* We already read some bytes into hdr, but the ones up to the NUL
@@ -2741,7 +3077,7 @@ static int check_stream_oid(git_zstream *stream,
if (size - total_read < stream->avail_out)
stream->avail_out = size - total_read;
status = git_inflate(stream, Z_FINISH);
- the_hash_algo->update_fn(&c, buf, stream->next_out - buf);
+ git_hash_update(&c, buf, stream->next_out - buf);
total_read += stream->next_out - buf;
}
git_inflate_end(stream);
@@ -2756,7 +3092,7 @@ static int check_stream_oid(git_zstream *stream,
return -1;
}
- the_hash_algo->final_oid_fn(&real_oid, &c);
+ git_hash_final_oid(&real_oid, &c);
if (!oideq(expected_oid, &real_oid)) {
error(_("hash mismatch for %s (expected %s)"), path,
oid_to_hex(expected_oid));
@@ -2791,6 +3127,7 @@ int read_loose_object(const char *path,
if (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr),
NULL) != ULHR_OK) {
error(_("unable to unpack header of %s"), path);
+ git_inflate_end(&stream);
goto out;
}