Raw
1 #define USE_THE_REPOSITORY_VARIABLE
2
3 #include "builtin.h"
4 #include "commit.h"
5 #include "environment.h"
6 #include "hash.h"
7 #include "hex.h"
8 #include "odb.h"
9 #include "parse-options.h"
10 #include "path.h"
11 #include "path-walk.h"
12 #include "progress.h"
13 #include "quote.h"
14 #include "ref-filter.h"
15 #include "refs.h"
16 #include "revision.h"
17 #include "setup.h"
18 #include "strbuf.h"
19 #include "string-list.h"
20 #include "shallow.h"
21 #include "tree.h"
22 #include "tree-walk.h"
23 #include "utf8.h"
24
25 #define REPO_INFO_USAGE \
26 "git repo info [--format=(lines|nul) | -z] [--all | <key>...]", \
27 "git repo info --keys [--format=(lines|nul) | -z]"
28
29 #define REPO_STRUCTURE_USAGE \
30 "git repo structure [--format=(table|lines|nul) | -z]"
31
32 static const char *const repo_usage[] = {
33 REPO_INFO_USAGE,
34 REPO_STRUCTURE_USAGE,
35 NULL,
36 };
37
38 static const char *const repo_info_usage[] = {
39 REPO_INFO_USAGE,
40 NULL,
41 };
42
43 static const char *const repo_structure_usage[] = {
44 REPO_STRUCTURE_USAGE,
45 NULL,
46 };
47
48 typedef int get_value_fn(struct repository *repo, struct strbuf *buf);
49
50 enum output_format {
51 FORMAT_TABLE,
52 FORMAT_NEWLINE_TERMINATED,
53 FORMAT_NUL_TERMINATED,
54 };
55
56 struct repo_info_field {
57 const char *key;
58 get_value_fn *get_value;
59 };
60
61 static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)
62 {
63 strbuf_addstr(buf, is_bare_repository(the_repository) ? "true" : "false");
64 return 0;
65 }
66
67 static int get_layout_shallow(struct repository *repo, struct strbuf *buf)
68 {
69 strbuf_addstr(buf,
70 is_repository_shallow(repo) ? "true" : "false");
71 return 0;
72 }
73
74 static int get_object_format(struct repository *repo, struct strbuf *buf)
75 {
76 strbuf_addstr(buf, repo->hash_algo->name);
77 return 0;
78 }
79
80 static int get_path_commondir_absolute(struct repository *repo, struct strbuf *buf)
81 {
82 const char *common_dir = repo_get_common_dir(repo);
83
84 if (!common_dir)
85 return error(_("unable to get common directory"));
86
87 format_path(buf, common_dir, repo->prefix, PATH_FORMAT_CANONICAL);
88 return 0;
89 }
90
91 static int get_path_commondir_relative(struct repository *repo, struct strbuf *buf)
92 {
93 const char *common_dir = repo_get_common_dir(repo);
94
95 if (!common_dir)
96 return error(_("unable to get common directory"));
97
98 format_path(buf, common_dir, repo->prefix, PATH_FORMAT_RELATIVE);
99 return 0;
100 }
101
102 static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)
103 {
104 const char *git_dir = repo_get_git_dir(repo);
105
106 if (!git_dir)
107 return error(_("unable to get git directory"));
108
109 format_path(buf, git_dir, repo->prefix, PATH_FORMAT_CANONICAL);
110 return 0;
111 }
112
113 static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
114 {
115 const char *git_dir = repo_get_git_dir(repo);
116
117 if (!git_dir)
118 return error(_("unable to get git directory"));
119
120 format_path(buf, git_dir, repo->prefix, PATH_FORMAT_RELATIVE);
121 return 0;
122 }
123
124 static int get_references_format(struct repository *repo, struct strbuf *buf)
125 {
126 strbuf_addstr(buf,
127 ref_storage_format_to_name(repo->ref_storage_format));
128 return 0;
129 }
130
131 /* repo_info_field keys must be in lexicographical order */
132 static const struct repo_info_field repo_info_field[] = {
133 { "layout.bare", get_layout_bare },
134 { "layout.shallow", get_layout_shallow },
135 { "object.format", get_object_format },
136 { "path.commondir.absolute", get_path_commondir_absolute },
137 { "path.commondir.relative", get_path_commondir_relative },
138 { "path.gitdir.absolute", get_path_gitdir_absolute },
139 { "path.gitdir.relative", get_path_gitdir_relative },
140 { "references.format", get_references_format },
141 };
142
143 static int repo_info_field_cmp(const void *va, const void *vb)
144 {
145 const struct repo_info_field *a = va;
146 const struct repo_info_field *b = vb;
147
148 return strcmp(a->key, b->key);
149 }
150
151 static const struct repo_info_field *get_repo_info_field(const char *key)
152 {
153 const struct repo_info_field search_key = { key, NULL };
154 const struct repo_info_field *found = bsearch(&search_key,
155 repo_info_field,
156 ARRAY_SIZE(repo_info_field),
157 sizeof(*found),
158 repo_info_field_cmp);
159
160 return found;
161 }
162
163 static void print_field(enum output_format format, const char *key,
164 const char *value)
165 {
166 switch (format) {
167 case FORMAT_NEWLINE_TERMINATED:
168 printf("%s=", key);
169 quote_c_style(value, NULL, stdout, 0);
170 putchar('\n');
171 break;
172 case FORMAT_NUL_TERMINATED:
173 printf("%s\n%s%c", key, value, '\0');
174 break;
175 default:
176 BUG("not a valid output format: %d", format);
177 }
178 }
179
180 static int print_fields(int argc, const char **argv,
181 struct repository *repo,
182 enum output_format format)
183 {
184 int ret = 0;
185 struct strbuf valbuf = STRBUF_INIT;
186
187 for (int i = 0; i < argc; i++) {
188 const char *key = argv[i];
189 const struct repo_info_field *field = get_repo_info_field(key);
190
191 if (!field) {
192 ret = error(_("key '%s' not found"), key);
193 continue;
194 }
195
196 strbuf_reset(&valbuf);
197 field->get_value(repo, &valbuf);
198 print_field(format, key, valbuf.buf);
199 }
200
201 strbuf_release(&valbuf);
202 return ret;
203 }
204
205 static int print_all_fields(struct repository *repo,
206 enum output_format format)
207 {
208 struct strbuf valbuf = STRBUF_INIT;
209
210 for (size_t i = 0; i < ARRAY_SIZE(repo_info_field); i++) {
211 const struct repo_info_field *field = &repo_info_field[i];
212
213 strbuf_reset(&valbuf);
214 field->get_value(repo, &valbuf);
215 print_field(format, field->key, valbuf.buf);
216 }
217
218 strbuf_release(&valbuf);
219 return 0;
220 }
221
222 static int print_keys(enum output_format format)
223 {
224 char sep;
225
226 switch (format) {
227 case FORMAT_NEWLINE_TERMINATED:
228 sep = '\n';
229 break;
230 case FORMAT_NUL_TERMINATED:
231 sep = '\0';
232 break;
233 default:
234 die(_("--keys can only be used with --format=lines or --format=nul"));
235 }
236
237 for (size_t i = 0; i < ARRAY_SIZE(repo_info_field); i++) {
238 const struct repo_info_field *field = &repo_info_field[i];
239 printf("%s%c", field->key, sep);
240 }
241
242 return 0;
243 }
244
245 static int parse_format_cb(const struct option *opt,
246 const char *arg, int unset UNUSED)
247 {
248 enum output_format *format = opt->value;
249
250 if (opt->short_name == 'z')
251 *format = FORMAT_NUL_TERMINATED;
252 else if (!strcmp(arg, "nul"))
253 *format = FORMAT_NUL_TERMINATED;
254 else if (!strcmp(arg, "lines"))
255 *format = FORMAT_NEWLINE_TERMINATED;
256 else if (!strcmp(arg, "table"))
257 *format = FORMAT_TABLE;
258 else
259 die(_("invalid format '%s'"), arg);
260
261 return 0;
262 }
263
264 static int cmd_repo_info(int argc, const char **argv, const char *prefix,
265 struct repository *repo)
266 {
267 enum output_format format = FORMAT_NEWLINE_TERMINATED;
268 int all_keys = 0;
269 int show_keys = 0;
270 struct option options[] = {
271 OPT_CALLBACK_F(0, "format", &format, N_("format"),
272 N_("output format"),
273 PARSE_OPT_NONEG, parse_format_cb),
274 OPT_CALLBACK_F('z', NULL, &format, NULL,
275 N_("synonym for --format=nul"),
276 PARSE_OPT_NONEG | PARSE_OPT_NOARG,
277 parse_format_cb),
278 OPT_BOOL(0, "all", &all_keys, N_("print all keys/values")),
279 OPT_BOOL(0, "keys", &show_keys, N_("show keys")),
280 OPT_END()
281 };
282
283 argc = parse_options(argc, argv, prefix, options, repo_info_usage, 0);
284
285 if (show_keys && (all_keys || argc))
286 die(_("--keys cannot be used with a <key> or --all"));
287
288 if (show_keys)
289 return print_keys(format);
290
291 if (format != FORMAT_NEWLINE_TERMINATED && format != FORMAT_NUL_TERMINATED)
292 die(_("unsupported output format"));
293
294 if (all_keys && argc)
295 die(_("--all and <key> cannot be used together"));
296
297 if (all_keys)
298 return print_all_fields(repo, format);
299 else
300 return print_fields(argc, argv, repo, format);
301 }
302
303 struct object_data {
304 struct object_id oid;
305 size_t value;
306 };
307
308 struct largest_objects {
309 struct object_data tag_size;
310 struct object_data commit_size;
311 struct object_data tree_size;
312 struct object_data blob_size;
313
314 struct object_data parent_count;
315 struct object_data tree_entries;
316 };
317
318 struct ref_stats {
319 size_t branches;
320 size_t remotes;
321 size_t tags;
322 size_t others;
323 };
324
325 struct object_values {
326 size_t tags;
327 size_t commits;
328 size_t trees;
329 size_t blobs;
330 };
331
332 struct object_stats {
333 struct object_values type_counts;
334 struct object_values inflated_sizes;
335 struct object_values disk_sizes;
336 struct largest_objects largest;
337 };
338
339 struct repo_structure {
340 struct ref_stats refs;
341 struct object_stats objects;
342 };
343
344 struct stats_table {
345 struct string_list rows;
346 struct string_list annotations;
347
348 int name_col_width;
349 int value_col_width;
350 int unit_col_width;
351 };
352
353 /*
354 * Holds column data that gets stored for each row.
355 */
356 struct stats_table_entry {
357 char *value;
358 const char *unit;
359 size_t index;
360 struct object_id *oid;
361 };
362
363 static void stats_table_vaddf(struct stats_table *table,
364 struct stats_table_entry *entry,
365 const char *format, va_list ap)
366 {
367 struct strbuf buf = STRBUF_INIT;
368 struct string_list_item *item;
369 char *formatted_name;
370 int name_width;
371
372 strbuf_vaddf(&buf, format, ap);
373 formatted_name = strbuf_detach(&buf, NULL);
374 name_width = utf8_strwidth(formatted_name);
375
376 item = string_list_append_nodup(&table->rows, formatted_name);
377 item->util = entry;
378
379 if (name_width > table->name_col_width)
380 table->name_col_width = name_width;
381 if (!entry)
382 return;
383 if (entry->oid) {
384 entry->index = table->annotations.nr + 1;
385 strbuf_addf(&buf, "[%" PRIuMAX "] %s", (uintmax_t)entry->index,
386 oid_to_hex(entry->oid));
387 string_list_append_nodup(&table->annotations, strbuf_detach(&buf, NULL));
388 }
389 if (entry->value) {
390 int value_width = utf8_strwidth(entry->value);
391 if (value_width > table->value_col_width)
392 table->value_col_width = value_width;
393 }
394 if (entry->unit) {
395 int unit_width = utf8_strwidth(entry->unit);
396 if (unit_width > table->unit_col_width)
397 table->unit_col_width = unit_width;
398 }
399
400 strbuf_release(&buf);
401 }
402
403 static void stats_table_addf(struct stats_table *table, const char *format, ...)
404 {
405 va_list ap;
406
407 va_start(ap, format);
408 stats_table_vaddf(table, NULL, format, ap);
409 va_end(ap);
410 }
411
412 static void stats_table_count_addf(struct stats_table *table, size_t value,
413 const char *format, ...)
414 {
415 struct stats_table_entry *entry;
416 va_list ap;
417
418 CALLOC_ARRAY(entry, 1);
419 humanise_count(value, &entry->value, &entry->unit);
420
421 va_start(ap, format);
422 stats_table_vaddf(table, entry, format, ap);
423 va_end(ap);
424 }
425
426 static void stats_table_object_count_addf(struct stats_table *table,
427 struct object_id *oid, size_t value,
428 const char *format, ...)
429 {
430 struct stats_table_entry *entry;
431 va_list ap;
432
433 CALLOC_ARRAY(entry, 1);
434 humanise_count(value, &entry->value, &entry->unit);
435
436 /*
437 * A NULL OID should not have a table annotation.
438 */
439 if (!is_null_oid(oid))
440 entry->oid = oid;
441
442 va_start(ap, format);
443 stats_table_vaddf(table, entry, format, ap);
444 va_end(ap);
445 }
446
447 static void stats_table_size_addf(struct stats_table *table, size_t value,
448 const char *format, ...)
449 {
450 struct stats_table_entry *entry;
451 va_list ap;
452
453 CALLOC_ARRAY(entry, 1);
454 humanise_bytes(value, &entry->value, &entry->unit, HUMANISE_COMPACT);
455
456 va_start(ap, format);
457 stats_table_vaddf(table, entry, format, ap);
458 va_end(ap);
459 }
460
461 static void stats_table_object_size_addf(struct stats_table *table,
462 struct object_id *oid, size_t value,
463 const char *format, ...)
464 {
465 struct stats_table_entry *entry;
466 va_list ap;
467
468 CALLOC_ARRAY(entry, 1);
469 humanise_bytes(value, &entry->value, &entry->unit, HUMANISE_COMPACT);
470
471 /*
472 * A NULL OID should not have a table annotation.
473 */
474 if (!is_null_oid(oid))
475 entry->oid = oid;
476
477 va_start(ap, format);
478 stats_table_vaddf(table, entry, format, ap);
479 va_end(ap);
480 }
481
482 static inline size_t get_total_reference_count(struct ref_stats *stats)
483 {
484 return stats->branches + stats->remotes + stats->tags + stats->others;
485 }
486
487 static inline size_t get_total_object_values(struct object_values *values)
488 {
489 return values->tags + values->commits + values->trees + values->blobs;
490 }
491
492 static void stats_table_setup_structure(struct stats_table *table,
493 struct repo_structure *stats)
494 {
495 struct object_stats *objects = &stats->objects;
496 struct ref_stats *refs = &stats->refs;
497 size_t inflated_object_total;
498 size_t object_count_total;
499 size_t disk_object_total;
500 size_t ref_total;
501
502 ref_total = get_total_reference_count(refs);
503 stats_table_addf(table, "* %s", _("References"));
504 stats_table_count_addf(table, ref_total, " * %s", _("Count"));
505 stats_table_count_addf(table, refs->branches, " * %s", _("Branches"));
506 stats_table_count_addf(table, refs->tags, " * %s", _("Tags"));
507 stats_table_count_addf(table, refs->remotes, " * %s", _("Remotes"));
508 stats_table_count_addf(table, refs->others, " * %s", _("Others"));
509
510 object_count_total = get_total_object_values(&objects->type_counts);
511 stats_table_addf(table, "");
512 stats_table_addf(table, "* %s", _("Reachable objects"));
513 stats_table_count_addf(table, object_count_total, " * %s", _("Count"));
514 stats_table_count_addf(table, objects->type_counts.commits,
515 " * %s", _("Commits"));
516 stats_table_count_addf(table, objects->type_counts.trees,
517 " * %s", _("Trees"));
518 stats_table_count_addf(table, objects->type_counts.blobs,
519 " * %s", _("Blobs"));
520 stats_table_count_addf(table, objects->type_counts.tags,
521 " * %s", _("Tags"));
522
523 inflated_object_total = get_total_object_values(&objects->inflated_sizes);
524 stats_table_size_addf(table, inflated_object_total,
525 " * %s", _("Inflated size"));
526 stats_table_size_addf(table, objects->inflated_sizes.commits,
527 " * %s", _("Commits"));
528 stats_table_size_addf(table, objects->inflated_sizes.trees,
529 " * %s", _("Trees"));
530 stats_table_size_addf(table, objects->inflated_sizes.blobs,
531 " * %s", _("Blobs"));
532 stats_table_size_addf(table, objects->inflated_sizes.tags,
533 " * %s", _("Tags"));
534
535 disk_object_total = get_total_object_values(&objects->disk_sizes);
536 stats_table_size_addf(table, disk_object_total,
537 " * %s", _("Disk size"));
538 stats_table_size_addf(table, objects->disk_sizes.commits,
539 " * %s", _("Commits"));
540 stats_table_size_addf(table, objects->disk_sizes.trees,
541 " * %s", _("Trees"));
542 stats_table_size_addf(table, objects->disk_sizes.blobs,
543 " * %s", _("Blobs"));
544 stats_table_size_addf(table, objects->disk_sizes.tags,
545 " * %s", _("Tags"));
546
547 stats_table_addf(table, "");
548 stats_table_addf(table, "* %s", _("Largest objects"));
549 stats_table_addf(table, " * %s", _("Commits"));
550 stats_table_object_size_addf(table,
551 &objects->largest.commit_size.oid,
552 objects->largest.commit_size.value,
553 " * %s", _("Maximum size"));
554 stats_table_object_count_addf(table,
555 &objects->largest.parent_count.oid,
556 objects->largest.parent_count.value,
557 " * %s", _("Maximum parents"));
558 stats_table_addf(table, " * %s", _("Trees"));
559 stats_table_object_size_addf(table,
560 &objects->largest.tree_size.oid,
561 objects->largest.tree_size.value,
562 " * %s", _("Maximum size"));
563 stats_table_object_count_addf(table,
564 &objects->largest.tree_entries.oid,
565 objects->largest.tree_entries.value,
566 " * %s", _("Maximum entries"));
567 stats_table_addf(table, " * %s", _("Blobs"));
568 stats_table_object_size_addf(table,
569 &objects->largest.blob_size.oid,
570 objects->largest.blob_size.value,
571 " * %s", _("Maximum size"));
572 stats_table_addf(table, " * %s", _("Tags"));
573 stats_table_object_size_addf(table,
574 &objects->largest.tag_size.oid,
575 objects->largest.tag_size.value,
576 " * %s", _("Maximum size"));
577 }
578
579 #define INDEX_WIDTH 4
580
581 static void stats_table_print_structure(const struct stats_table *table)
582 {
583 const char *name_col_title = _("Repository structure");
584 const char *value_col_title = _("Value");
585 int title_name_width = utf8_strwidth(name_col_title);
586 int title_value_width = utf8_strwidth(value_col_title);
587 int name_col_width = table->name_col_width;
588 int value_col_width = table->value_col_width;
589 int unit_col_width = table->unit_col_width;
590 struct string_list_item *item;
591 struct strbuf buf = STRBUF_INIT;
592
593 if (title_name_width > name_col_width)
594 name_col_width = title_name_width;
595 if (title_value_width > value_col_width + unit_col_width + 1)
596 value_col_width = title_value_width - unit_col_width;
597
598 strbuf_addstr(&buf, "| ");
599 strbuf_utf8_align(&buf, ALIGN_LEFT, name_col_width + INDEX_WIDTH,
600 name_col_title);
601 strbuf_addstr(&buf, " | ");
602 strbuf_utf8_align(&buf, ALIGN_LEFT,
603 value_col_width + unit_col_width + 1, value_col_title);
604 strbuf_addstr(&buf, " |");
605 printf("%s\n", buf.buf);
606
607 printf("| ");
608 for (int i = 0; i < name_col_width + INDEX_WIDTH; i++)
609 putchar('-');
610 printf(" | ");
611 for (int i = 0; i < value_col_width + unit_col_width + 1; i++)
612 putchar('-');
613 printf(" |\n");
614
615 for_each_string_list_item(item, &table->rows) {
616 struct stats_table_entry *entry = item->util;
617 const char *value = "";
618 const char *unit = "";
619
620 if (entry) {
621 value = entry->value;
622 if (entry->unit)
623 unit = entry->unit;
624 }
625
626 strbuf_reset(&buf);
627 strbuf_addstr(&buf, "| ");
628 strbuf_utf8_align(&buf, ALIGN_LEFT, name_col_width, item->string);
629
630 if (entry && entry->oid)
631 strbuf_addf(&buf, " [%" PRIuMAX "]",
632 (uintmax_t)entry->index);
633 else
634 strbuf_addchars(&buf, ' ', INDEX_WIDTH);
635
636 strbuf_addstr(&buf, " | ");
637 strbuf_utf8_align(&buf, ALIGN_RIGHT, value_col_width, value);
638 strbuf_addch(&buf, ' ');
639 strbuf_utf8_align(&buf, ALIGN_LEFT, unit_col_width, unit);
640 strbuf_addstr(&buf, " |");
641 printf("%s\n", buf.buf);
642 }
643
644 if (table->annotations.nr) {
645 printf("\n");
646 for_each_string_list_item(item, &table->annotations)
647 printf("%s\n", item->string);
648 }
649
650 strbuf_release(&buf);
651 }
652
653 static void stats_table_clear(struct stats_table *table)
654 {
655 struct stats_table_entry *entry;
656 struct string_list_item *item;
657
658 for_each_string_list_item(item, &table->rows) {
659 entry = item->util;
660 if (entry)
661 free(entry->value);
662 }
663
664 string_list_clear(&table->rows, 1);
665 string_list_clear(&table->annotations, 1);
666 }
667
668 static inline void print_keyvalue(const char *key, char key_delim, size_t value,
669 char value_delim)
670 {
671 printf("%s%c%" PRIuMAX "%c", key, key_delim, (uintmax_t)value,
672 value_delim);
673 }
674
675 static void print_object_data(const char *key, char key_delim,
676 struct object_data *data, char value_delim)
677 {
678 print_keyvalue(key, key_delim, data->value, value_delim);
679 printf("%s_oid%c%s%c", key, key_delim, oid_to_hex(&data->oid),
680 value_delim);
681 }
682
683 static void structure_keyvalue_print(struct repo_structure *stats,
684 char key_delim, char value_delim)
685 {
686 print_keyvalue("references.branches.count", key_delim,
687 stats->refs.branches, value_delim);
688 print_keyvalue("references.tags.count", key_delim,
689 stats->refs.tags, value_delim);
690 print_keyvalue("references.remotes.count", key_delim,
691 stats->refs.remotes, value_delim);
692 print_keyvalue("references.others.count", key_delim,
693 stats->refs.others, value_delim);
694
695 print_keyvalue("objects.commits.count", key_delim,
696 stats->objects.type_counts.commits, value_delim);
697 print_keyvalue("objects.trees.count", key_delim,
698 stats->objects.type_counts.trees, value_delim);
699 print_keyvalue("objects.blobs.count", key_delim,
700 stats->objects.type_counts.blobs, value_delim);
701 print_keyvalue("objects.tags.count", key_delim,
702 stats->objects.type_counts.tags, value_delim);
703
704 print_keyvalue("objects.commits.inflated_size", key_delim,
705 stats->objects.inflated_sizes.commits, value_delim);
706 print_keyvalue("objects.trees.inflated_size", key_delim,
707 stats->objects.inflated_sizes.trees, value_delim);
708 print_keyvalue("objects.blobs.inflated_size", key_delim,
709 stats->objects.inflated_sizes.blobs, value_delim);
710 print_keyvalue("objects.tags.inflated_size", key_delim,
711 stats->objects.inflated_sizes.tags, value_delim);
712
713 print_keyvalue("objects.commits.disk_size", key_delim,
714 stats->objects.disk_sizes.commits, value_delim);
715 print_keyvalue("objects.trees.disk_size", key_delim,
716 stats->objects.disk_sizes.trees, value_delim);
717 print_keyvalue("objects.blobs.disk_size", key_delim,
718 stats->objects.disk_sizes.blobs, value_delim);
719 print_keyvalue("objects.tags.disk_size", key_delim,
720 stats->objects.disk_sizes.tags, value_delim);
721
722 print_object_data("objects.commits.max_size", key_delim,
723 &stats->objects.largest.commit_size, value_delim);
724 print_object_data("objects.trees.max_size", key_delim,
725 &stats->objects.largest.tree_size, value_delim);
726 print_object_data("objects.blobs.max_size", key_delim,
727 &stats->objects.largest.blob_size, value_delim);
728 print_object_data("objects.tags.max_size", key_delim,
729 &stats->objects.largest.tag_size, value_delim);
730
731 print_object_data("objects.commits.max_parents", key_delim,
732 &stats->objects.largest.parent_count, value_delim);
733 print_object_data("objects.trees.max_entries", key_delim,
734 &stats->objects.largest.tree_entries, value_delim);
735
736 fflush(stdout);
737 }
738
739 struct count_references_data {
740 struct ref_stats *stats;
741 struct rev_info *revs;
742 struct progress *progress;
743 };
744
745 static int count_references(const struct reference *ref, void *cb_data)
746 {
747 struct count_references_data *data = cb_data;
748 struct ref_stats *stats = data->stats;
749 size_t ref_count;
750
751 switch (ref_kind_from_refname(ref->name)) {
752 case FILTER_REFS_BRANCHES:
753 stats->branches++;
754 break;
755 case FILTER_REFS_REMOTES:
756 stats->remotes++;
757 break;
758 case FILTER_REFS_TAGS:
759 stats->tags++;
760 break;
761 case FILTER_REFS_OTHERS:
762 stats->others++;
763 break;
764 default:
765 BUG("unexpected reference type");
766 }
767
768 /*
769 * While iterating through references for counting, also add OIDs in
770 * preparation for the path walk.
771 */
772 add_pending_oid(data->revs, NULL, ref->oid, 0);
773
774 ref_count = get_total_reference_count(stats);
775 display_progress(data->progress, ref_count);
776
777 return 0;
778 }
779
780 static void structure_count_references(struct ref_stats *stats,
781 struct rev_info *revs,
782 struct repository *repo,
783 int show_progress)
784 {
785 struct count_references_data data = {
786 .stats = stats,
787 .revs = revs,
788 };
789
790 if (show_progress)
791 data.progress = start_delayed_progress(repo,
792 _("Counting references"), 0);
793
794 refs_for_each_ref(get_main_ref_store(repo), count_references, &data);
795 stop_progress(&data.progress);
796 }
797
798 struct count_objects_data {
799 struct object_database *odb;
800 struct object_stats *stats;
801 struct progress *progress;
802 };
803
804 static void check_largest(struct object_data *data, struct object_id *oid,
805 size_t value)
806 {
807 if (value > data->value || is_null_oid(&data->oid)) {
808 oidcpy(&data->oid, oid);
809 data->value = value;
810 }
811 }
812
813 static size_t count_tree_entries(struct object *obj)
814 {
815 struct tree *t = object_as_type(obj, OBJ_TREE, 0);
816 struct name_entry entry;
817 struct tree_desc desc;
818 size_t count = 0;
819
820 init_tree_desc(&desc, &t->object.oid, t->buffer, t->size);
821 while (tree_entry(&desc, &entry))
822 count++;
823
824 return count;
825 }
826
827 static int count_objects(const char *path UNUSED, struct oid_array *oids,
828 enum object_type type, void *cb_data)
829 {
830 struct count_objects_data *data = cb_data;
831 struct object_stats *stats = data->stats;
832 size_t object_count;
833
834 for (size_t i = 0; i < oids->nr; i++) {
835 struct object_info oi = OBJECT_INFO_INIT;
836 unsigned long inflated;
837 size_t inflated_st = 0;
838 struct commit *commit;
839 struct object *obj;
840 void *content;
841 off_t disk;
842 int eaten;
843
844 oi.sizep = &inflated_st;
845 oi.disk_sizep = &disk;
846 oi.contentp = &content;
847
848 if (odb_read_object_info_extended(data->odb, &oids->oid[i], &oi,
849 OBJECT_INFO_SKIP_FETCH_OBJECT |
850 OBJECT_INFO_QUICK) < 0)
851 continue;
852 inflated = cast_size_t_to_ulong(inflated_st);
853
854 obj = parse_object_buffer(the_repository, &oids->oid[i], type,
855 inflated, content, &eaten);
856
857 switch (type) {
858 case OBJ_TAG:
859 stats->type_counts.tags++;
860 stats->inflated_sizes.tags += inflated;
861 stats->disk_sizes.tags += disk;
862 check_largest(&stats->largest.tag_size, &oids->oid[i],
863 inflated);
864 break;
865 case OBJ_COMMIT:
866 commit = object_as_type(obj, OBJ_COMMIT, 0);
867 stats->type_counts.commits++;
868 stats->inflated_sizes.commits += inflated;
869 stats->disk_sizes.commits += disk;
870 check_largest(&stats->largest.commit_size, &oids->oid[i],
871 inflated);
872 check_largest(&stats->largest.parent_count, &oids->oid[i],
873 commit_list_count(commit->parents));
874 break;
875 case OBJ_TREE:
876 stats->type_counts.trees++;
877 stats->inflated_sizes.trees += inflated;
878 stats->disk_sizes.trees += disk;
879 check_largest(&stats->largest.tree_size, &oids->oid[i],
880 inflated);
881 check_largest(&stats->largest.tree_entries, &oids->oid[i],
882 count_tree_entries(obj));
883 break;
884 case OBJ_BLOB:
885 stats->type_counts.blobs++;
886 stats->inflated_sizes.blobs += inflated;
887 stats->disk_sizes.blobs += disk;
888 check_largest(&stats->largest.blob_size, &oids->oid[i],
889 inflated);
890 break;
891 default:
892 BUG("invalid object type");
893 }
894
895 if (!eaten)
896 free(content);
897 }
898
899 object_count = get_total_object_values(&stats->type_counts);
900 display_progress(data->progress, object_count);
901
902 return 0;
903 }
904
905 static void structure_count_objects(struct object_stats *stats,
906 struct rev_info *revs,
907 struct repository *repo, int show_progress)
908 {
909 struct path_walk_info info = PATH_WALK_INFO_INIT;
910 struct count_objects_data data = {
911 .odb = repo->objects,
912 .stats = stats,
913 };
914
915 info.revs = revs;
916 info.path_fn = count_objects;
917 info.path_fn_data = &data;
918
919 if (show_progress)
920 data.progress = start_delayed_progress(repo, _("Counting objects"), 0);
921
922 walk_objects_by_path(&info);
923 path_walk_info_clear(&info);
924 stop_progress(&data.progress);
925 }
926
927 static int cmd_repo_structure(int argc, const char **argv, const char *prefix,
928 struct repository *repo)
929 {
930 struct stats_table table = {
931 .rows = STRING_LIST_INIT_DUP,
932 .annotations = STRING_LIST_INIT_DUP,
933 };
934 enum output_format format = FORMAT_TABLE;
935 struct repo_structure stats = { 0 };
936 struct rev_info revs;
937 int show_progress = -1;
938 struct option options[] = {
939 OPT_CALLBACK_F(0, "format", &format, N_("format"),
940 N_("output format"),
941 PARSE_OPT_NONEG, parse_format_cb),
942 OPT_CALLBACK_F('z', NULL, &format, NULL,
943 N_("synonym for --format=nul"),
944 PARSE_OPT_NONEG | PARSE_OPT_NOARG,
945 parse_format_cb),
946 OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
947 OPT_END()
948 };
949
950 argc = parse_options(argc, argv, prefix, options, repo_structure_usage, 0);
951 if (argc)
952 usage(_("too many arguments"));
953
954 repo_init_revisions(repo, &revs, prefix);
955
956 if (show_progress < 0)
957 show_progress = isatty(2);
958
959 structure_count_references(&stats.refs, &revs, repo, show_progress);
960 structure_count_objects(&stats.objects, &revs, repo, show_progress);
961
962 switch (format) {
963 case FORMAT_TABLE:
964 stats_table_setup_structure(&table, &stats);
965 stats_table_print_structure(&table);
966 break;
967 case FORMAT_NEWLINE_TERMINATED:
968 structure_keyvalue_print(&stats, '=', '\n');
969 break;
970 case FORMAT_NUL_TERMINATED:
971 structure_keyvalue_print(&stats, '\n', '\0');
972 break;
973 default:
974 BUG("invalid output format");
975 }
976
977 stats_table_clear(&table);
978 release_revisions(&revs);
979
980 return 0;
981 }
982
983 int cmd_repo(int argc, const char **argv, const char *prefix,
984 struct repository *repo)
985 {
986 parse_opt_subcommand_fn *fn = NULL;
987 struct option options[] = {
988 OPT_SUBCOMMAND("info", &fn, cmd_repo_info),
989 OPT_SUBCOMMAND("structure", &fn, cmd_repo_structure),
990 OPT_END()
991 };
992
993 argc = parse_options(argc, argv, prefix, options, repo_usage, 0);
994
995 return fn(argc, argv, prefix, repo);
996 }