|
4 | 4 | #include "environment.h" |
5 | 5 | #include "parse-options.h" |
6 | 6 | #include "quote.h" |
| 7 | +#include "ref-filter.h" |
7 | 8 | #include "refs.h" |
8 | 9 | #include "strbuf.h" |
| 10 | +#include "string-list.h" |
9 | 11 | #include "shallow.h" |
| 12 | +#include "utf8.h" |
10 | 13 |
|
11 | 14 | static const char *const repo_usage[] = { |
12 | 15 | "git repo info [--format=(keyvalue|nul)] [-z] [<key>...]", |
| 16 | + "git repo structure", |
13 | 17 | NULL |
14 | 18 | }; |
15 | 19 |
|
@@ -156,12 +160,208 @@ static int cmd_repo_info(int argc, const char **argv, const char *prefix, |
156 | 160 | return print_fields(argc, argv, repo, format); |
157 | 161 | } |
158 | 162 |
|
| 163 | +struct ref_stats { |
| 164 | + size_t branches; |
| 165 | + size_t remotes; |
| 166 | + size_t tags; |
| 167 | + size_t others; |
| 168 | +}; |
| 169 | + |
| 170 | +struct stats_table { |
| 171 | + struct string_list rows; |
| 172 | + |
| 173 | + int name_col_width; |
| 174 | + int value_col_width; |
| 175 | +}; |
| 176 | + |
| 177 | +/* |
| 178 | + * Holds column data that gets stored for each row. |
| 179 | + */ |
| 180 | +struct stats_table_entry { |
| 181 | + char *value; |
| 182 | +}; |
| 183 | + |
| 184 | +static void stats_table_vaddf(struct stats_table *table, |
| 185 | + struct stats_table_entry *entry, |
| 186 | + const char *format, va_list ap) |
| 187 | +{ |
| 188 | + struct strbuf buf = STRBUF_INIT; |
| 189 | + struct string_list_item *item; |
| 190 | + char *formatted_name; |
| 191 | + int name_width; |
| 192 | + |
| 193 | + strbuf_vaddf(&buf, format, ap); |
| 194 | + formatted_name = strbuf_detach(&buf, NULL); |
| 195 | + name_width = utf8_strwidth(formatted_name); |
| 196 | + |
| 197 | + item = string_list_append_nodup(&table->rows, formatted_name); |
| 198 | + item->util = entry; |
| 199 | + |
| 200 | + if (name_width > table->name_col_width) |
| 201 | + table->name_col_width = name_width; |
| 202 | + if (entry) { |
| 203 | + int value_width = utf8_strwidth(entry->value); |
| 204 | + if (value_width > table->value_col_width) |
| 205 | + table->value_col_width = value_width; |
| 206 | + } |
| 207 | +} |
| 208 | + |
| 209 | +static void stats_table_addf(struct stats_table *table, const char *format, ...) |
| 210 | +{ |
| 211 | + va_list ap; |
| 212 | + |
| 213 | + va_start(ap, format); |
| 214 | + stats_table_vaddf(table, NULL, format, ap); |
| 215 | + va_end(ap); |
| 216 | +} |
| 217 | + |
| 218 | +static void stats_table_count_addf(struct stats_table *table, size_t value, |
| 219 | + const char *format, ...) |
| 220 | +{ |
| 221 | + struct stats_table_entry *entry; |
| 222 | + va_list ap; |
| 223 | + |
| 224 | + CALLOC_ARRAY(entry, 1); |
| 225 | + entry->value = xstrfmt("%" PRIuMAX, (uintmax_t)value); |
| 226 | + |
| 227 | + va_start(ap, format); |
| 228 | + stats_table_vaddf(table, entry, format, ap); |
| 229 | + va_end(ap); |
| 230 | +} |
| 231 | + |
| 232 | +static inline size_t get_total_reference_count(struct ref_stats *stats) |
| 233 | +{ |
| 234 | + return stats->branches + stats->remotes + stats->tags + stats->others; |
| 235 | +} |
| 236 | + |
| 237 | +static void stats_table_setup_structure(struct stats_table *table, |
| 238 | + struct ref_stats *refs) |
| 239 | +{ |
| 240 | + size_t ref_total; |
| 241 | + |
| 242 | + ref_total = get_total_reference_count(refs); |
| 243 | + stats_table_addf(table, "* %s", _("References")); |
| 244 | + stats_table_count_addf(table, ref_total, " * %s", _("Count")); |
| 245 | + stats_table_count_addf(table, refs->branches, " * %s", _("Branches")); |
| 246 | + stats_table_count_addf(table, refs->tags, " * %s", _("Tags")); |
| 247 | + stats_table_count_addf(table, refs->remotes, " * %s", _("Remotes")); |
| 248 | + stats_table_count_addf(table, refs->others, " * %s", _("Others")); |
| 249 | +} |
| 250 | + |
| 251 | +static void stats_table_print_structure(const struct stats_table *table) |
| 252 | +{ |
| 253 | + const char *name_col_title = _("Repository structure"); |
| 254 | + const char *value_col_title = _("Value"); |
| 255 | + int name_col_width = utf8_strwidth(name_col_title); |
| 256 | + int value_col_width = utf8_strwidth(value_col_title); |
| 257 | + struct string_list_item *item; |
| 258 | + |
| 259 | + if (table->name_col_width > name_col_width) |
| 260 | + name_col_width = table->name_col_width; |
| 261 | + if (table->value_col_width > value_col_width) |
| 262 | + value_col_width = table->value_col_width; |
| 263 | + |
| 264 | + printf("| %-*s | %-*s |\n", name_col_width, name_col_title, |
| 265 | + value_col_width, value_col_title); |
| 266 | + printf("| "); |
| 267 | + for (int i = 0; i < name_col_width; i++) |
| 268 | + putchar('-'); |
| 269 | + printf(" | "); |
| 270 | + for (int i = 0; i < value_col_width; i++) |
| 271 | + putchar('-'); |
| 272 | + printf(" |\n"); |
| 273 | + |
| 274 | + for_each_string_list_item(item, &table->rows) { |
| 275 | + struct stats_table_entry *entry = item->util; |
| 276 | + const char *value = ""; |
| 277 | + |
| 278 | + if (entry) { |
| 279 | + struct stats_table_entry *entry = item->util; |
| 280 | + value = entry->value; |
| 281 | + } |
| 282 | + |
| 283 | + printf("| %-*s | %*s |\n", name_col_width, item->string, |
| 284 | + value_col_width, value); |
| 285 | + } |
| 286 | +} |
| 287 | + |
| 288 | +static void stats_table_clear(struct stats_table *table) |
| 289 | +{ |
| 290 | + struct stats_table_entry *entry; |
| 291 | + struct string_list_item *item; |
| 292 | + |
| 293 | + for_each_string_list_item(item, &table->rows) { |
| 294 | + entry = item->util; |
| 295 | + if (entry) |
| 296 | + free(entry->value); |
| 297 | + } |
| 298 | + |
| 299 | + string_list_clear(&table->rows, 1); |
| 300 | +} |
| 301 | + |
| 302 | +static int count_references(const char *refname, |
| 303 | + const char *referent UNUSED, |
| 304 | + const struct object_id *oid UNUSED, |
| 305 | + int flags UNUSED, void *cb_data) |
| 306 | +{ |
| 307 | + struct ref_stats *stats = cb_data; |
| 308 | + |
| 309 | + switch (ref_kind_from_refname(refname)) { |
| 310 | + case FILTER_REFS_BRANCHES: |
| 311 | + stats->branches++; |
| 312 | + break; |
| 313 | + case FILTER_REFS_REMOTES: |
| 314 | + stats->remotes++; |
| 315 | + break; |
| 316 | + case FILTER_REFS_TAGS: |
| 317 | + stats->tags++; |
| 318 | + break; |
| 319 | + case FILTER_REFS_OTHERS: |
| 320 | + stats->others++; |
| 321 | + break; |
| 322 | + default: |
| 323 | + BUG("unexpected reference type"); |
| 324 | + } |
| 325 | + |
| 326 | + return 0; |
| 327 | +} |
| 328 | + |
| 329 | +static void structure_count_references(struct ref_stats *stats, |
| 330 | + struct repository *repo) |
| 331 | +{ |
| 332 | + refs_for_each_ref(get_main_ref_store(repo), count_references, &stats); |
| 333 | +} |
| 334 | + |
| 335 | +static int cmd_repo_structure(int argc, const char **argv, const char *prefix, |
| 336 | + struct repository *repo) |
| 337 | +{ |
| 338 | + struct stats_table table = { |
| 339 | + .rows = STRING_LIST_INIT_DUP, |
| 340 | + }; |
| 341 | + struct ref_stats stats = { 0 }; |
| 342 | + struct option options[] = { 0 }; |
| 343 | + |
| 344 | + argc = parse_options(argc, argv, prefix, options, repo_usage, 0); |
| 345 | + if (argc) |
| 346 | + usage(_("too many arguments")); |
| 347 | + |
| 348 | + structure_count_references(&stats, repo); |
| 349 | + |
| 350 | + stats_table_setup_structure(&table, &stats); |
| 351 | + stats_table_print_structure(&table); |
| 352 | + |
| 353 | + stats_table_clear(&table); |
| 354 | + |
| 355 | + return 0; |
| 356 | +} |
| 357 | + |
159 | 358 | int cmd_repo(int argc, const char **argv, const char *prefix, |
160 | 359 | struct repository *repo) |
161 | 360 | { |
162 | 361 | parse_opt_subcommand_fn *fn = NULL; |
163 | 362 | struct option options[] = { |
164 | 363 | OPT_SUBCOMMAND("info", &fn, cmd_repo_info), |
| 364 | + OPT_SUBCOMMAND("structure", &fn, cmd_repo_structure), |
165 | 365 | OPT_END() |
166 | 366 | }; |
167 | 367 |
|
|
0 commit comments