Skip to content

Commit acdb1e1

Browse files
committed
Merge branch 'mt/checkout-count-fix'
"git checkout" miscounted the paths it updated, which has been corrected. source: <[email protected]> * mt/checkout-count-fix: checkout: fix two bugs on the final count of updated entries checkout: show bug about failed entries being included in final report checkout: document bug where delayed checkout counts entries twice
2 parents f0f9a03 + 611c778 commit acdb1e1

10 files changed

+113
-24
lines changed

builtin/checkout.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ static int checkout_worktree(const struct checkout_opts *opts,
417417
mem_pool_discard(&ce_mem_pool, should_validate_cache_entries());
418418
remove_marked_cache_entries(&the_index, 1);
419419
remove_scheduled_dirs();
420-
errs |= finish_delayed_checkout(&state, &nr_checkouts, opts->show_progress);
420+
errs |= finish_delayed_checkout(&state, opts->show_progress);
421421

422422
if (opts->count_checkout_paths) {
423423
if (nr_unmerged)

convert.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,11 @@ struct delayed_checkout {
5353
enum ce_delay_state state;
5454
/* List of filter drivers that signaled delayed blobs. */
5555
struct string_list filters;
56-
/* List of delayed blobs identified by their path. */
56+
/*
57+
* List of delayed blobs identified by their path. The `util` member
58+
* holds a counter pointer which must be incremented when/if the
59+
* associated blob gets checked out.
60+
*/
5761
struct string_list paths;
5862
};
5963

entry.c

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,11 @@ static int remove_available_paths(struct string_list_item *item, void *cb_data)
157157

158158
available = string_list_lookup(available_paths, item->string);
159159
if (available)
160-
available->util = (void *)item->string;
160+
available->util = item->util;
161161
return !available;
162162
}
163163

164-
int finish_delayed_checkout(struct checkout *state, int *nr_checkouts,
165-
int show_progress)
164+
int finish_delayed_checkout(struct checkout *state, int show_progress)
166165
{
167166
int errs = 0;
168167
unsigned processed_paths = 0;
@@ -227,7 +226,7 @@ int finish_delayed_checkout(struct checkout *state, int *nr_checkouts,
227226
strlen(path->string), 0);
228227
if (ce) {
229228
display_progress(progress, ++processed_paths);
230-
errs |= checkout_entry(ce, state, NULL, nr_checkouts);
229+
errs |= checkout_entry(ce, state, NULL, path->util);
231230
filtered_bytes += ce->ce_stat_data.sd_size;
232231
display_throughput(progress, filtered_bytes);
233232
} else
@@ -266,7 +265,8 @@ void update_ce_after_write(const struct checkout *state, struct cache_entry *ce,
266265

267266
/* Note: ca is used (and required) iff the entry refers to a regular file. */
268267
static int write_entry(struct cache_entry *ce, char *path, struct conv_attrs *ca,
269-
const struct checkout *state, int to_tempfile)
268+
const struct checkout *state, int to_tempfile,
269+
int *nr_checkouts)
270270
{
271271
unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT;
272272
struct delayed_checkout *dco = state->delayed_checkout;
@@ -279,6 +279,7 @@ static int write_entry(struct cache_entry *ce, char *path, struct conv_attrs *ca
279279
struct stat st;
280280
const struct submodule *sub;
281281
struct checkout_metadata meta;
282+
static int scratch_nr_checkouts;
282283

283284
clone_checkout_metadata(&meta, &state->meta, &ce->oid);
284285

@@ -333,9 +334,15 @@ static int write_entry(struct cache_entry *ce, char *path, struct conv_attrs *ca
333334
ret = async_convert_to_working_tree_ca(ca, ce->name,
334335
new_blob, size,
335336
&buf, &meta, dco);
336-
if (ret && string_list_has_string(&dco->paths, ce->name)) {
337-
free(new_blob);
338-
goto delayed;
337+
if (ret) {
338+
struct string_list_item *item =
339+
string_list_lookup(&dco->paths, ce->name);
340+
if (item) {
341+
item->util = nr_checkouts ? nr_checkouts
342+
: &scratch_nr_checkouts;
343+
free(new_blob);
344+
goto delayed;
345+
}
339346
}
340347
} else {
341348
ret = convert_to_working_tree_ca(ca, ce->name, new_blob,
@@ -392,6 +399,8 @@ static int write_entry(struct cache_entry *ce, char *path, struct conv_attrs *ca
392399
ce->name);
393400
update_ce_after_write(state, ce , &st);
394401
}
402+
if (nr_checkouts)
403+
(*nr_checkouts)++;
395404
delayed:
396405
return 0;
397406
}
@@ -476,7 +485,7 @@ int checkout_entry_ca(struct cache_entry *ce, struct conv_attrs *ca,
476485
convert_attrs(state->istate, &ca_buf, ce->name);
477486
ca = &ca_buf;
478487
}
479-
return write_entry(ce, topath, ca, state, 1);
488+
return write_entry(ce, topath, ca, state, 1, nr_checkouts);
480489
}
481490

482491
strbuf_reset(&path);
@@ -540,18 +549,15 @@ int checkout_entry_ca(struct cache_entry *ce, struct conv_attrs *ca,
540549

541550
create_directories(path.buf, path.len, state);
542551

543-
if (nr_checkouts)
544-
(*nr_checkouts)++;
545-
546552
if (S_ISREG(ce->ce_mode) && !ca) {
547553
convert_attrs(state->istate, &ca_buf, ce->name);
548554
ca = &ca_buf;
549555
}
550556

551-
if (!enqueue_checkout(ce, ca))
557+
if (!enqueue_checkout(ce, ca, nr_checkouts))
552558
return 0;
553559

554-
return write_entry(ce, path.buf, ca, state, 0);
560+
return write_entry(ce, path.buf, ca, state, 0, nr_checkouts);
555561
}
556562

557563
void unlink_entry(const struct cache_entry *ce)

entry.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ static inline int checkout_entry(struct cache_entry *ce,
4343
}
4444

4545
void enable_delayed_checkout(struct checkout *state);
46-
int finish_delayed_checkout(struct checkout *state, int *nr_checkouts,
47-
int show_progress);
46+
int finish_delayed_checkout(struct checkout *state, int show_progress);
4847

4948
/*
5049
* Unlink the last component and schedule the leading directories for

parallel-checkout.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ static int is_eligible_for_parallel_checkout(const struct cache_entry *ce,
143143
}
144144
}
145145

146-
int enqueue_checkout(struct cache_entry *ce, struct conv_attrs *ca)
146+
int enqueue_checkout(struct cache_entry *ce, struct conv_attrs *ca,
147+
int *checkout_counter)
147148
{
148149
struct parallel_checkout_item *pc_item;
149150

@@ -159,6 +160,7 @@ int enqueue_checkout(struct cache_entry *ce, struct conv_attrs *ca)
159160
memcpy(&pc_item->ca, ca, sizeof(pc_item->ca));
160161
pc_item->status = PC_ITEM_PENDING;
161162
pc_item->id = parallel_checkout.nr;
163+
pc_item->checkout_counter = checkout_counter;
162164
parallel_checkout.nr++;
163165

164166
return 0;
@@ -200,7 +202,8 @@ static int handle_results(struct checkout *state)
200202

201203
switch(pc_item->status) {
202204
case PC_ITEM_WRITTEN:
203-
/* Already handled */
205+
if (pc_item->checkout_counter)
206+
(*pc_item->checkout_counter)++;
204207
break;
205208
case PC_ITEM_COLLIDED:
206209
/*
@@ -225,7 +228,8 @@ static int handle_results(struct checkout *state)
225228
* add any extra overhead.
226229
*/
227230
ret |= checkout_entry_ca(pc_item->ce, &pc_item->ca,
228-
state, NULL, NULL);
231+
state, NULL,
232+
pc_item->checkout_counter);
229233
advance_progress_meter();
230234
break;
231235
case PC_ITEM_PENDING:

parallel-checkout.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ void init_parallel_checkout(void);
3131
* entry is not eligible for parallel checkout. Otherwise, enqueue the entry
3232
* for later write and return 0.
3333
*/
34-
int enqueue_checkout(struct cache_entry *ce, struct conv_attrs *ca);
34+
int enqueue_checkout(struct cache_entry *ce, struct conv_attrs *ca,
35+
int *checkout_counter);
3536
size_t pc_queue_size(void);
3637

3738
/*
@@ -68,6 +69,7 @@ struct parallel_checkout_item {
6869
struct cache_entry *ce;
6970
struct conv_attrs ca;
7071
size_t id; /* position in parallel_checkout.items[] of main process */
72+
int *checkout_counter;
7173

7274
/* Output fields, sent from workers. */
7375
enum pc_item_status status;

t/lib-parallel-checkout.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ test_checkout_workers () {
2525

2626
local trace_file=trace-test-checkout-workers &&
2727
rm -f "$trace_file" &&
28-
GIT_TRACE2="$(pwd)/$trace_file" "$@" 2>&8 &&
28+
(
29+
GIT_TRACE2="$(pwd)/$trace_file" &&
30+
export GIT_TRACE2 &&
31+
"$@" 2>&8
32+
) &&
2933

3034
local workers="$(grep "child_start\[..*\] git checkout--worker" "$trace_file" | wc -l)" &&
3135
test $workers -eq $expected_workers &&

t/t0021-conversion.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,4 +1132,26 @@ do
11321132
'
11331133
done
11341134

1135+
test_expect_success PERL 'delayed checkout correctly reports the number of updated entries' '
1136+
rm -rf repo &&
1137+
git init repo &&
1138+
(
1139+
cd repo &&
1140+
git config filter.delay.process "../rot13-filter.pl delayed.log clean smudge delay" &&
1141+
git config filter.delay.required true &&
1142+
1143+
echo "*.a filter=delay" >.gitattributes &&
1144+
echo a >test-delay10.a &&
1145+
echo a >test-delay11.a &&
1146+
git add . &&
1147+
git commit -m files &&
1148+
1149+
rm *.a &&
1150+
git checkout . 2>err &&
1151+
grep "IN: smudge test-delay10.a .* \\[DELAYED\\]" delayed.log &&
1152+
grep "IN: smudge test-delay11.a .* \\[DELAYED\\]" delayed.log &&
1153+
grep "Updated 2 paths from the index" err
1154+
)
1155+
'
1156+
11351157
test_done

t/t2080-parallel-checkout-basics.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,4 +226,52 @@ test_expect_success SYMLINKS 'parallel checkout checks for symlinks in leading d
226226
)
227227
'
228228

229+
# This test is here (and not in e.g. t2022-checkout-paths.sh), because we
230+
# check the final report including sequential, parallel, and delayed entries
231+
# all at the same time. So we must have finer control of the parallel checkout
232+
# variables.
233+
test_expect_success PERL '"git checkout ." report should not include failed entries' '
234+
write_script rot13-filter.pl "$PERL_PATH" \
235+
<"$TEST_DIRECTORY"/t0021/rot13-filter.pl &&
236+
237+
test_config_global filter.delay.process \
238+
"\"$(pwd)/rot13-filter.pl\" --always-delay delayed.log clean smudge delay" &&
239+
test_config_global filter.delay.required true &&
240+
test_config_global filter.cat.clean cat &&
241+
test_config_global filter.cat.smudge cat &&
242+
test_config_global filter.cat.required true &&
243+
244+
set_checkout_config 2 0 &&
245+
git init failed_entries &&
246+
(
247+
cd failed_entries &&
248+
cat >.gitattributes <<-EOF &&
249+
*delay* filter=delay
250+
parallel-ineligible* filter=cat
251+
EOF
252+
echo a >missing-delay.a &&
253+
echo a >parallel-ineligible.a &&
254+
echo a >parallel-eligible.a &&
255+
echo b >success-delay.b &&
256+
echo b >parallel-ineligible.b &&
257+
echo b >parallel-eligible.b &&
258+
git add -A &&
259+
git commit -m files &&
260+
261+
a_blob="$(git rev-parse :parallel-ineligible.a)" &&
262+
rm .git/objects/$(test_oid_to_path $a_blob) &&
263+
rm *.a *.b &&
264+
265+
test_checkout_workers 2 test_must_fail git checkout . 2>err &&
266+
267+
# All *.b entries should succeed and all *.a entries should fail:
268+
# - missing-delay.a: the delay filter will drop this path
269+
# - parallel-*.a: the blob will be missing
270+
#
271+
grep "Updated 3 paths from the index" err &&
272+
test_stdout_line_count = 3 ls *.b &&
273+
! ls *.a
274+
)
275+
'
276+
229277
test_done

unpack-trees.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ static int check_updates(struct unpack_trees_options *o,
487487
errs |= run_parallel_checkout(&state, pc_workers, pc_threshold,
488488
progress, &cnt);
489489
stop_progress(&progress);
490-
errs |= finish_delayed_checkout(&state, NULL, o->verbose_update);
490+
errs |= finish_delayed_checkout(&state, o->verbose_update);
491491
git_attr_set_direction(GIT_ATTR_CHECKIN);
492492

493493
if (o->clone)

0 commit comments

Comments
 (0)