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