@@ -5,6 +5,7 @@
#include "parse-options.h"
#include "progress.h"
#include "ref-filter.h"
+#include "strbuf.h"
#include "strvec.h"
#include "trace2.h"
@@ -27,10 +28,16 @@ static struct survey_refs_wanted default_ref_options = {
.want_all_refs = 1,
};
+enum survey_format {
+ SURVEY_PLAINTEXT = 0,
+ SURVEY_JSON = 1,
+};
+
struct survey_opts {
int verbose;
int show_progress;
struct survey_refs_wanted refs;
+ enum survey_format format;
};
struct survey_report_ref_summary {
@@ -78,6 +85,161 @@ static void clear_survey_context(struct survey_context *ctx)
strvec_clear(&ctx->refs);
}
+struct survey_table {
+ const char *table_name;
+ struct strvec header;
+ struct strvec *rows;
+ size_t rows_nr;
+ size_t rows_alloc;
+};
+
+#define SURVEY_TABLE_INIT { \
+ .header = STRVEC_INIT, \
+}
+
+static void clear_table(struct survey_table *table)
+{
+ strvec_clear(&table->header);
+ for (size_t i = 0; i < table->rows_nr; i++)
+ strvec_clear(&table->rows[i]);
+ free(table->rows);
+}
+
+static void insert_table_rowv(struct survey_table *table, ...)
+{
+ va_list ap;
+ char *arg;
+ ALLOC_GROW(table->rows, table->rows_nr + 1, table->rows_alloc);
+
+ memset(&table->rows[table->rows_nr], 0, sizeof(struct strvec));
+
+ va_start(ap, table);
+ while ((arg = va_arg(ap, char *)))
+ strvec_push(&table->rows[table->rows_nr], arg);
+ va_end(ap);
+
+ table->rows_nr++;
+}
+
+static void print_table_title(const char *name, size_t *widths, size_t nr)
+{
+ static struct strbuf lines = STRBUF_INIT;
+ size_t width = 0;
+ strbuf_setlen(&lines, 0);
+
+ strbuf_addch(&lines, ' ');
+ strbuf_addstr(&lines, name);
+ strbuf_addch(&lines, '\n');
+
+ for (size_t i = 0; i < nr; i++) {
+ if (i)
+ width += 3;
+ width += widths[i];
+ }
+ strbuf_addchars(&lines, '=', width);
+ printf("%s\n", lines.buf);
+}
+
+static void print_row_plaintext(struct strvec *row, size_t *widths)
+{
+ static struct strbuf line = STRBUF_INIT;
+ strbuf_setlen(&line, 0);
+
+ for (size_t i = 0; i < row->nr; i++) {
+ const char *str = row->v[i];
+ size_t len = strlen(str);
+ if (i)
+ strbuf_add(&line, " | ", 3);
+ strbuf_addchars(&line, ' ', widths[i] - len);
+ strbuf_add(&line, str, len);
+ }
+ printf("%s\n", line.buf);
+}
+
+static void print_divider_plaintext(size_t *widths, size_t nr)
+{
+ static struct strbuf line = STRBUF_INIT;
+ strbuf_setlen(&line, 0);
+
+ for (size_t i = 0; i < nr; i++) {
+ if (i)
+ strbuf_add(&line, "-+-", 3);
+ strbuf_addchars(&line, '-', widths[i]);
+ }
+ printf("%s\n", line.buf);
+}
+
+static void print_table_plaintext(struct survey_table *table)
+{
+ size_t *column_widths;
+ size_t columns_nr = table->header.nr;
+ CALLOC_ARRAY(column_widths, columns_nr);
+
+ for (size_t i = 0; i < columns_nr; i++) {
+ column_widths[i] = strlen(table->header.v[i]);
+
+ for (size_t j = 0; j < table->rows_nr; j++) {
+ size_t rowlen = strlen(table->rows[j].v[i]);
+ if (column_widths[i] < rowlen)
+ column_widths[i] = rowlen;
+ }
+ }
+
+ print_table_title(table->table_name, column_widths, columns_nr);
+ print_row_plaintext(&table->header, column_widths);
+ print_divider_plaintext(column_widths, columns_nr);
+
+ for (size_t j = 0; j < table->rows_nr; j++)
+ print_row_plaintext(&table->rows[j], column_widths);
+}
+
+static void survey_report_plaintext_refs(struct survey_context *ctx)
+{
+ struct survey_report_ref_summary *refs = &ctx->report.refs;
+ struct survey_table table = SURVEY_TABLE_INIT;
+
+ table.table_name = _("REFERENCES SUMMARY");
+
+ strvec_push(&table.header, _("Ref Type"));
+ strvec_push(&table.header, _("Count"));
+
+ if (ctx->opts.refs.want_all_refs || ctx->opts.refs.want_branches) {
+ char *fmt = xstrfmt("%"PRIuMAX"", refs->branches_nr);
+ insert_table_rowv(&table, _("Branches"), fmt, NULL);
+ free(fmt);
+ }
+
+ if (ctx->opts.refs.want_all_refs || ctx->opts.refs.want_remotes) {
+ char *fmt = xstrfmt("%"PRIuMAX"", refs->remote_refs_nr);
+ insert_table_rowv(&table, _("Remote refs"), fmt, NULL);
+ free(fmt);
+ }
+
+ if (ctx->opts.refs.want_all_refs || ctx->opts.refs.want_tags) {
+ char *fmt = xstrfmt("%"PRIuMAX"", refs->tags_nr);
+ insert_table_rowv(&table, _("Tags (all)"), fmt, NULL);
+ free(fmt);
+ fmt = xstrfmt("%"PRIuMAX"", refs->tags_annotated_nr);
+ insert_table_rowv(&table, _("Tags (annotated)"), fmt, NULL);
+ free(fmt);
+ }
+
+ print_table_plaintext(&table);
+ clear_table(&table);
+}
+
+static void survey_report_plaintext(struct survey_context *ctx)
+{
+ printf("GIT SURVEY for \"%s\"\n", ctx->repo->worktree);
+ printf("-----------------------------------------------------\n");
+ survey_report_plaintext_refs(ctx);
+}
+
+static void survey_report_json(struct survey_context *ctx)
+{
+ /* TODO. */
+}
+
/*
* After parsing the command line arguments, figure out which refs we
* should scan.
@@ -312,6 +474,19 @@ int cmd_survey(int argc, const char **argv, const char *prefix)
survey_phase_refs(&ctx);
+ switch (ctx.opts.format) {
+ case SURVEY_PLAINTEXT:
+ survey_report_plaintext(&ctx);
+ break;
+
+ case SURVEY_JSON:
+ survey_report_json(&ctx);
+ break;
+
+ default:
+ BUG("Undefined format");
+ }
+
clear_survey_context(&ctx);
return 0;
}
@@ -21,7 +21,22 @@ test_expect_success 'creat a semi-interesting repo' '
test_expect_success 'git survey (default)' '
git survey >out 2>err &&
- test_line_count = 0 err
+ test_line_count = 0 err &&
+
+ cat >expect <<-EOF &&
+ GIT SURVEY for "$(pwd)"
+ -----------------------------------------------------
+ REFERENCES SUMMARY
+ ========================
+ Ref Type | Count
+ -----------------+------
+ Branches | 1
+ Remote refs | 0
+ Tags (all) | 0
+ Tags (annotated) | 0
+ EOF
+
+ test_cmp expect out
'
test_done