builtin/repo: add progress meter for structure stats
When using the structure subcommand for git-repo(1), evaluating a repository may take some time depending on its shape. Add a progress meter to provide feedback to the user about what is happening. The progress meter is enabled by default when the command is executed from a tty. It can also be explicitly enabled/disabled via the --[no-]progress option. Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Justin Tobler committed
Oct 21, 2025 at 13:26 UTC
16a93c03c7824a40b034a6ee1cb1c68c8ef48682
2 files changed
+60
-6
builtin/repo.c
+40
-6
@@ -4,6 +4,7 @@
4
#include "environment.h"
5
#include "parse-options.h"
6
#include "path-walk.h"
7
+#include "progress.h"
8
#include "quote.h"
9
#include "ref-filter.h"
10
#include "refs.h"
@@ -362,6 +363,7 @@ static void structure_keyvalue_print(struct repo_structure *stats,
363
struct count_references_data {
364
struct ref_stats *stats;
365
struct rev_info *revs;
366
+ struct progress *progress;
367
};
368
369
static int count_references(const char *refname,
@@ -371,6 +373,7 @@ static int count_references(const char *refname,
373
{
374
struct count_references_data *data = cb_data;
375
struct ref_stats *stats = data->stats;
376
+ size_t ref_count;
377
378
switch (ref_kind_from_refname(refname)) {
379
case FILTER_REFS_BRANCHES:
@@ -395,26 +398,41 @@ static int count_references(const char *refname,
398
*/
399
add_pending_oid(data->revs, NULL, oid, 0);
400
401
+ ref_count = get_total_reference_count(stats);
402
+ display_progress(data->progress, ref_count);
403
+
404
return 0;
405
}
406
407
static void structure_count_references(struct ref_stats *stats,
408
struct rev_info *revs,
403
- struct repository *repo)
409
+ struct repository *repo,
410
+ int show_progress)
411
{
412
struct count_references_data data = {
413
.stats = stats,
414
.revs = revs,
415
};
416
417
+ if (show_progress)
418
+ data.progress = start_delayed_progress(repo,
419
+ _("Counting references"), 0);
420
+
421
refs_for_each_ref(get_main_ref_store(repo), count_references, &data);
422
+ stop_progress(&data.progress);
423
}
424
425
+struct count_objects_data {
426
+ struct object_stats *stats;
427
+ struct progress *progress;
428
+};
429
430
static int count_objects(const char *path UNUSED, struct oid_array *oids,
431
enum object_type type, void *cb_data)
432
{
417
- struct object_stats *stats = cb_data;
433
+ struct count_objects_data *data = cb_data;
434
+ struct object_stats *stats = data->stats;
435
+ size_t object_count;
436
437
switch (type) {
438
case OBJ_TAG:
@@ -433,20 +451,31 @@ static int count_objects(const char *path UNUSED, struct oid_array *oids,
451
BUG("invalid object type");
452
}
453
454
+ object_count = get_total_object_count(stats);
455
+ display_progress(data->progress, object_count);
456
+
457
return 0;
458
}
459
460
static void structure_count_objects(struct object_stats *stats,
440
- struct rev_info *revs)
461
+ struct rev_info *revs,
462
+ struct repository *repo, int show_progress)
463
{
464
struct path_walk_info info = PATH_WALK_INFO_INIT;
465
+ struct count_objects_data data = {
466
+ .stats = stats,
467
+ };
468
469
info.revs = revs;
470
info.path_fn = count_objects;
446
- info.path_fn_data = stats;
471
+ info.path_fn_data = &data;
472
+
473
+ if (show_progress)
474
+ data.progress = start_delayed_progress(repo, _("Counting objects"), 0);
475
476
walk_objects_by_path(&info);
477
path_walk_info_clear(&info);
478
+ stop_progress(&data.progress);
479
}
480
481
static int cmd_repo_structure(int argc, const char **argv, const char *prefix,
@@ -458,10 +487,12 @@ static int cmd_repo_structure(int argc, const char **argv, const char *prefix,
487
enum output_format format = FORMAT_TABLE;
488
struct repo_structure stats = { 0 };
489
struct rev_info revs;
490
+ int show_progress = -1;
491
struct option options[] = {
492
OPT_CALLBACK_F(0, "format", &format, N_("format"),
493
N_("output format"),
494
PARSE_OPT_NONEG, parse_format_cb),
495
+ OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
496
OPT_END()
497
};
498
@@ -471,8 +502,11 @@ static int cmd_repo_structure(int argc, const char **argv, const char *prefix,
502
503
repo_init_revisions(repo, &revs, prefix);
504
474
- structure_count_references(&stats.refs, &revs, repo);
475
- structure_count_objects(&stats.objects, &revs);
505
+ if (show_progress < 0)
506
+ show_progress = isatty(2);
507
+
508
+ structure_count_references(&stats.refs, &revs, repo, show_progress);
509
+ structure_count_objects(&stats.objects, &revs, repo, show_progress);
510
511
switch (format) {
512
case FORMAT_TABLE:
t/t1901-repo-structure.sh
+20
@@ -106,4 +106,24 @@ test_expect_success 'keyvalue and nul format' '
106
)
107
'
108
109
+test_expect_success 'progress meter option' '
110
+ test_when_finished "rm -rf repo" &&
111
+ git init repo &&
112
+ (
113
+ cd repo &&
114
+ test_commit foo &&
115
+
116
+ GIT_PROGRESS_DELAY=0 git repo structure --progress >out 2>err &&
117
+
118
+ test_file_not_empty out &&
119
+ test_grep "Counting references: 2, done." err &&
120
+ test_grep "Counting objects: 3, done." err &&
121
+
122
+ GIT_PROGRESS_DELAY=0 git repo structure --no-progress >out 2>err &&
123
+
124
+ test_file_not_empty out &&
125
+ test_line_count = 0 err
126
+ )
127
+'
128
+
129
test_done