Skip to content

Commit 8b2f027

Browse files
committed
Merge branch 'jk/upload-pack-skip-hash-check'
The server side that responds to "git fetch" and "git clone" request has been optimized by allowing it to send objects in its object store without recomputing and validating the object names. * jk/upload-pack-skip-hash-check: t1060: check partial clone of misnamed blob parse_object(): check commit-graph when skip_hash set upload-pack: skip parse-object re-hashing of "want" objects parse_object(): allow skipping hash check
2 parents e0574c4 + 945ed00 commit 8b2f027

File tree

6 files changed

+56
-20
lines changed

6 files changed

+56
-20
lines changed

object.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,8 +263,11 @@ struct object *parse_object_or_die(const struct object_id *oid,
263263
die(_("unable to parse object: %s"), name ? name : oid_to_hex(oid));
264264
}
265265

266-
struct object *parse_object(struct repository *r, const struct object_id *oid)
266+
struct object *parse_object_with_flags(struct repository *r,
267+
const struct object_id *oid,
268+
enum parse_object_flags flags)
267269
{
270+
int skip_hash = !!(flags & PARSE_OBJECT_SKIP_HASH_CHECK);
268271
unsigned long size;
269272
enum object_type type;
270273
int eaten;
@@ -276,10 +279,16 @@ struct object *parse_object(struct repository *r, const struct object_id *oid)
276279
if (obj && obj->parsed)
277280
return obj;
278281

282+
if (skip_hash) {
283+
struct commit *commit = lookup_commit_in_graph(r, repl);
284+
if (commit)
285+
return &commit->object;
286+
}
287+
279288
if ((obj && obj->type == OBJ_BLOB && repo_has_object_file(r, oid)) ||
280289
(!obj && repo_has_object_file(r, oid) &&
281290
oid_object_info(r, oid, NULL) == OBJ_BLOB)) {
282-
if (stream_object_signature(r, repl) < 0) {
291+
if (!skip_hash && stream_object_signature(r, repl) < 0) {
283292
error(_("hash mismatch %s"), oid_to_hex(oid));
284293
return NULL;
285294
}
@@ -289,7 +298,8 @@ struct object *parse_object(struct repository *r, const struct object_id *oid)
289298

290299
buffer = repo_read_object_file(r, oid, &type, &size);
291300
if (buffer) {
292-
if (check_object_signature(r, repl, buffer, size, type) < 0) {
301+
if (!skip_hash &&
302+
check_object_signature(r, repl, buffer, size, type) < 0) {
293303
free(buffer);
294304
error(_("hash mismatch %s"), oid_to_hex(repl));
295305
return NULL;
@@ -304,6 +314,11 @@ struct object *parse_object(struct repository *r, const struct object_id *oid)
304314
return NULL;
305315
}
306316

317+
struct object *parse_object(struct repository *r, const struct object_id *oid)
318+
{
319+
return parse_object_with_flags(r, oid, 0);
320+
}
321+
307322
struct object_list *object_list_insert(struct object *item,
308323
struct object_list **list_p)
309324
{

object.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,13 @@ void *object_as_type(struct object *obj, enum object_type type, int quiet);
128128
*
129129
* Returns NULL if the object is missing or corrupt.
130130
*/
131+
enum parse_object_flags {
132+
PARSE_OBJECT_SKIP_HASH_CHECK = 1 << 0,
133+
};
131134
struct object *parse_object(struct repository *r, const struct object_id *oid);
135+
struct object *parse_object_with_flags(struct repository *r,
136+
const struct object_id *oid,
137+
enum parse_object_flags flags);
132138

133139
/*
134140
* Like parse_object, but will die() instead of returning NULL. If the

revision.c

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -373,18 +373,10 @@ static struct object *get_reference(struct rev_info *revs, const char *name,
373373
unsigned int flags)
374374
{
375375
struct object *object;
376-
struct commit *commit;
377376

378-
/*
379-
* If the repository has commit graphs, we try to opportunistically
380-
* look up the object ID in those graphs. Like this, we can avoid
381-
* parsing commit data from disk.
382-
*/
383-
commit = lookup_commit_in_graph(revs->repo, oid);
384-
if (commit)
385-
object = &commit->object;
386-
else
387-
object = parse_object(revs->repo, oid);
377+
object = parse_object_with_flags(revs->repo, oid,
378+
revs->verify_objects ? 0 :
379+
PARSE_OBJECT_SKIP_HASH_CHECK);
388380

389381
if (!object) {
390382
if (revs->ignore_missing)

t/t1060-object-corruption.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,11 @@ test_expect_success 'internal tree objects are not "missing"' '
139139
)
140140
'
141141

142+
test_expect_success 'partial clone of corrupted repository' '
143+
test_config -C misnamed uploadpack.allowFilter true &&
144+
git clone --no-local --no-checkout --filter=blob:none \
145+
misnamed corrupt-partial && \
146+
test_must_fail git -C corrupt-partial checkout --force
147+
'
148+
142149
test_done

t/t1450-fsck.sh

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,26 @@ test_expect_success 'rev-list --verify-objects with bad sha1' '
507507
test_i18ngrep -q "error: hash mismatch $(dirname $new)$(test_oid ff_2)" out
508508
'
509509

510+
# An actual bit corruption is more likely than swapped commits, but
511+
# this provides an easy way to have commits which don't match their purported
512+
# hashes, but which aren't so broken we can't read them at all.
513+
test_expect_success 'rev-list --verify-objects notices swapped commits' '
514+
git init swapped-commits &&
515+
(
516+
cd swapped-commits &&
517+
test_commit one &&
518+
test_commit two &&
519+
one_oid=$(git rev-parse HEAD) &&
520+
two_oid=$(git rev-parse HEAD^) &&
521+
one=.git/objects/$(test_oid_to_path $one_oid) &&
522+
two=.git/objects/$(test_oid_to_path $two_oid) &&
523+
mv $one tmp &&
524+
mv $two $one &&
525+
mv tmp $two &&
526+
test_must_fail git rev-list --verify-objects HEAD
527+
)
528+
'
529+
510530
test_expect_success 'force fsck to ignore double author' '
511531
git cat-file commit HEAD >basis &&
512532
sed "s/^author .*/&,&/" <basis | tr , \\n >multiple-authors &&

upload-pack.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,18 +1409,14 @@ static int parse_want(struct packet_writer *writer, const char *line,
14091409
const char *arg;
14101410
if (skip_prefix(line, "want ", &arg)) {
14111411
struct object_id oid;
1412-
struct commit *commit;
14131412
struct object *o;
14141413

14151414
if (get_oid_hex(arg, &oid))
14161415
die("git upload-pack: protocol error, "
14171416
"expected to get oid, not '%s'", line);
14181417

1419-
commit = lookup_commit_in_graph(the_repository, &oid);
1420-
if (commit)
1421-
o = &commit->object;
1422-
else
1423-
o = parse_object(the_repository, &oid);
1418+
o = parse_object_with_flags(the_repository, &oid,
1419+
PARSE_OBJECT_SKIP_HASH_CHECK);
14241420

14251421
if (!o) {
14261422
packet_writer_error(writer,

0 commit comments

Comments
 (0)