Skip to content

Commit 7c0fe33

Browse files
matvoregitster
authored andcommitted
rev-list: handle missing tree objects properly
Previously, we assumed only blob objects could be missing. This patch makes rev-list handle missing trees like missing blobs. The --missing=* and --exclude-promisor-objects flags now work for trees as they already do for blobs. This is demonstrated in t6112. Signed-off-by: Matthew DeVore <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f1d02da commit 7c0fe33

File tree

6 files changed

+110
-7
lines changed

6 files changed

+110
-7
lines changed

builtin/rev-list.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "list-objects.h"
77
#include "list-objects-filter.h"
88
#include "list-objects-filter-options.h"
9+
#include "object.h"
910
#include "object-store.h"
1011
#include "pack.h"
1112
#include "pack-bitmap.h"
@@ -209,7 +210,8 @@ static inline void finish_object__ma(struct object *obj)
209210
*/
210211
switch (arg_missing_action) {
211212
case MA_ERROR:
212-
die("missing blob object '%s'", oid_to_hex(&obj->oid));
213+
die("missing %s object '%s'",
214+
type_name(obj->type), oid_to_hex(&obj->oid));
213215
return;
214216

215217
case MA_ALLOW_ANY:
@@ -222,8 +224,8 @@ static inline void finish_object__ma(struct object *obj)
222224
case MA_ALLOW_PROMISOR:
223225
if (is_promisor_object(&obj->oid))
224226
return;
225-
die("unexpected missing blob object '%s'",
226-
oid_to_hex(&obj->oid));
227+
die("unexpected missing %s object '%s'",
228+
type_name(obj->type), oid_to_hex(&obj->oid));
227229
return;
228230

229231
default:
@@ -235,7 +237,7 @@ static inline void finish_object__ma(struct object *obj)
235237
static int finish_object(struct object *obj, const char *name, void *cb_data)
236238
{
237239
struct rev_list_info *info = cb_data;
238-
if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid)) {
240+
if (!has_object_file(&obj->oid)) {
239241
finish_object__ma(obj);
240242
return 1;
241243
}
@@ -373,6 +375,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
373375
init_revisions(&revs, prefix);
374376
revs.abbrev = DEFAULT_ABBREV;
375377
revs.commit_format = CMIT_FMT_UNSPECIFIED;
378+
revs.do_not_die_on_missing_tree = 1;
376379

377380
/*
378381
* Scan the argument list before invoking setup_revisions(), so that we

list-objects.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,17 @@ static void process_tree(struct traversal_context *ctx,
143143
struct rev_info *revs = ctx->revs;
144144
int baselen = base->len;
145145
enum list_objects_filter_result r = LOFR_MARK_SEEN | LOFR_DO_SHOW;
146+
int failed_parse;
146147

147148
if (!revs->tree_objects)
148149
return;
149150
if (!obj)
150151
die("bad tree object");
151152
if (obj->flags & (UNINTERESTING | SEEN))
152153
return;
153-
if (parse_tree_gently(tree, 1) < 0) {
154+
155+
failed_parse = parse_tree_gently(tree, 1);
156+
if (failed_parse) {
154157
if (revs->ignore_missing_links)
155158
return;
156159

@@ -163,7 +166,8 @@ static void process_tree(struct traversal_context *ctx,
163166
is_promisor_object(&obj->oid))
164167
return;
165168

166-
die("bad tree object %s", oid_to_hex(&obj->oid));
169+
if (!revs->do_not_die_on_missing_tree)
170+
die("bad tree object %s", oid_to_hex(&obj->oid));
167171
}
168172

169173
strbuf_addstr(base, name);
@@ -178,7 +182,8 @@ static void process_tree(struct traversal_context *ctx,
178182
if (base->len)
179183
strbuf_addch(base, '/');
180184

181-
process_tree_contents(ctx, tree, base);
185+
if (!failed_parse)
186+
process_tree_contents(ctx, tree, base);
182187

183188
if (!(obj->flags & USER_GIVEN) && ctx->filter_fn) {
184189
r = ctx->filter_fn(LOFS_END_TREE, obj,

revision.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,21 @@ struct rev_info {
125125
line_level_traverse:1,
126126
tree_blobs_in_commit_order:1,
127127

128+
/*
129+
* Blobs are shown without regard for their existence.
130+
* But not so for trees: unless exclude_promisor_objects
131+
* is set and the tree in question is a promisor object;
132+
* OR ignore_missing_links is set, the revision walker
133+
* dies with a "bad tree object HASH" message when
134+
* encountering a missing tree. For callers that can
135+
* handle missing trees and want them to be filterable
136+
* and showable, set this to true. The revision walker
137+
* will filter and show such a missing tree as usual,
138+
* but will not attempt to recurse into this tree
139+
* object.
140+
*/
141+
do_not_die_on_missing_tree:1,
142+
128143
/* for internal use only */
129144
exclude_promisor_objects:1;
130145

t/t0410-partial-clone.sh

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,51 @@ test_expect_success 'rev-list stops traversal at missing and promised commit' '
186186
! grep $FOO out
187187
'
188188

189+
test_expect_success 'missing tree objects with --missing=allow-promisor and --exclude-promisor-objects' '
190+
rm -rf repo &&
191+
test_create_repo repo &&
192+
test_commit -C repo foo &&
193+
test_commit -C repo bar &&
194+
test_commit -C repo baz &&
195+
196+
promise_and_delete $(git -C repo rev-parse bar^{tree}) &&
197+
promise_and_delete $(git -C repo rev-parse foo^{tree}) &&
198+
199+
git -C repo config core.repositoryformatversion 1 &&
200+
git -C repo config extensions.partialclone "arbitrary string" &&
201+
202+
git -C repo rev-list --missing=allow-promisor --objects HEAD >objs 2>rev_list_err &&
203+
test_must_be_empty rev_list_err &&
204+
# 3 commits, 3 blobs, and 1 tree
205+
test_line_count = 7 objs &&
206+
207+
# Do the same for --exclude-promisor-objects, but with all trees gone.
208+
promise_and_delete $(git -C repo rev-parse baz^{tree}) &&
209+
git -C repo rev-list --exclude-promisor-objects --objects HEAD >objs 2>rev_list_err &&
210+
test_must_be_empty rev_list_err &&
211+
# 3 commits, no blobs or trees
212+
test_line_count = 3 objs
213+
'
214+
215+
test_expect_success 'missing non-root tree object and rev-list' '
216+
rm -rf repo &&
217+
test_create_repo repo &&
218+
mkdir repo/dir &&
219+
echo foo >repo/dir/foo &&
220+
git -C repo add dir/foo &&
221+
git -C repo commit -m "commit dir/foo" &&
222+
223+
promise_and_delete $(git -C repo rev-parse HEAD:dir) &&
224+
225+
git -C repo config core.repositoryformatversion 1 &&
226+
git -C repo config extensions.partialclone "arbitrary string" &&
227+
228+
git -C repo rev-list --missing=allow-any --objects HEAD >objs 2>rev_list_err &&
229+
test_must_be_empty rev_list_err &&
230+
# 1 commit and 1 tree
231+
test_line_count = 2 objs
232+
'
233+
189234
test_expect_success 'rev-list stops traversal at missing and promised tree' '
190235
rm -rf repo &&
191236
test_create_repo repo &&

t/t5317-pack-objects-filter-objects.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,19 @@ test_expect_success 'verify normal and blob:none packfiles have same commits/tre
5959
test_cmp observed expected
6060
'
6161

62+
test_expect_success 'get an error for missing tree object' '
63+
git init r5 &&
64+
echo foo >r5/foo &&
65+
git -C r5 add foo &&
66+
git -C r5 commit -m "foo" &&
67+
del=$(git -C r5 rev-parse HEAD^{tree} | sed "s|..|&/|") &&
68+
rm r5/.git/objects/$del &&
69+
test_must_fail git -C r5 pack-objects --rev --stdout 2>bad_tree <<-EOF &&
70+
HEAD
71+
EOF
72+
grep -q "bad tree object" bad_tree
73+
'
74+
6275
# Test blob:limit=<n>[kmg] filter.
6376
# We boundary test around the size parameter. The filter is strictly less than
6477
# the value, so size 500 and 1000 should have the same results, but 1001 should

t/t6112-rev-list-filters-objects.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,28 @@ test_expect_success 'verify sparse:oid=oid-ish omits top-level files' '
196196
test_cmp observed expected
197197
'
198198

199+
test_expect_success 'rev-list W/ --missing=print and --missing=allow-any for trees' '
200+
TREE=$(git -C r3 rev-parse HEAD:dir1) &&
201+
202+
# Create a spare repo because we will be deleting objects from this one.
203+
git clone r3 r3.b &&
204+
205+
rm r3.b/.git/objects/$(echo $TREE | sed "s|^..|&/|") &&
206+
207+
git -C r3.b rev-list --quiet --missing=print --objects HEAD \
208+
>missing_objs 2>rev_list_err &&
209+
echo "?$TREE" >expected &&
210+
test_cmp expected missing_objs &&
211+
212+
# do not complain when a missing tree cannot be parsed
213+
test_must_be_empty rev_list_err &&
214+
215+
git -C r3.b rev-list --missing=allow-any --objects HEAD \
216+
>objs 2>rev_list_err &&
217+
! grep $TREE objs &&
218+
test_must_be_empty rev_list_err
219+
'
220+
199221
# Delete some loose objects and use rev-list, but WITHOUT any filtering.
200222
# This models previously omitted objects that we did not receive.
201223

0 commit comments

Comments
 (0)