Skip to content

Commit 0d8393e

Browse files
derrickstoleedscho
authored andcommitted
survey: start pretty printing data in table form
When 'git survey' provides information to the user, this will be presented in one of two formats: plaintext and JSON. The JSON implementation will be delayed until the functionality is complete for the plaintext format. The most important parts of the plaintext format are headers specifying the different sections of the report and tables providing concreted data. Create a custom table data structure that allows specifying a list of strings for the row values. When printing the table, check each column for the maximum width so we can create a table of the correct size from the start. The table structure is designed to be flexible to the different kinds of output that will be implemented in future changes. Signed-off-by: Derrick Stolee <[email protected]>
1 parent 7d894d8 commit 0d8393e

File tree

3 files changed

+181
-1
lines changed

3 files changed

+181
-1
lines changed

Documentation/git-survey.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ OUTPUT
6565
By default, `git survey` will print information about the repository in a
6666
human-readable format that includes overviews and tables.
6767

68+
References Summary
69+
~~~~~~~~~~~~~~~~~~
70+
71+
The references summary includes a count of each kind of reference,
72+
including branches, remote refs, and tags (split by "all" and
73+
"annotated").
74+
6875
GIT
6976
---
7077
Part of the linkgit:git[1] suite

builtin/survey.c

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "parse-options.h"
88
#include "progress.h"
99
#include "ref-filter.h"
10+
#include "strbuf.h"
1011
#include "strvec.h"
1112
#include "trace2.h"
1213

@@ -80,6 +81,160 @@ static void clear_survey_context(struct survey_context *ctx)
8081
strvec_clear(&ctx->refs);
8182
}
8283

84+
struct survey_table {
85+
const char *table_name;
86+
struct strvec header;
87+
struct strvec *rows;
88+
size_t rows_nr;
89+
size_t rows_alloc;
90+
};
91+
92+
#define SURVEY_TABLE_INIT { \
93+
.header = STRVEC_INIT, \
94+
}
95+
96+
static void clear_table(struct survey_table *table)
97+
{
98+
strvec_clear(&table->header);
99+
for (size_t i = 0; i < table->rows_nr; i++)
100+
strvec_clear(&table->rows[i]);
101+
free(table->rows);
102+
}
103+
104+
static void insert_table_rowv(struct survey_table *table, ...)
105+
{
106+
va_list ap;
107+
char *arg;
108+
ALLOC_GROW(table->rows, table->rows_nr + 1, table->rows_alloc);
109+
110+
memset(&table->rows[table->rows_nr], 0, sizeof(struct strvec));
111+
112+
va_start(ap, table);
113+
while ((arg = va_arg(ap, char *)))
114+
strvec_push(&table->rows[table->rows_nr], arg);
115+
va_end(ap);
116+
117+
table->rows_nr++;
118+
}
119+
120+
#define SECTION_SEGMENT "========================================"
121+
#define SECTION_SEGMENT_LEN 40
122+
static const char *section_line = SECTION_SEGMENT
123+
SECTION_SEGMENT
124+
SECTION_SEGMENT
125+
SECTION_SEGMENT;
126+
static const size_t section_len = 4 * SECTION_SEGMENT_LEN;
127+
128+
static void print_table_title(const char *name, size_t *widths, size_t nr)
129+
{
130+
size_t width = 3 * (nr - 1);
131+
132+
for (size_t i = 0; i < nr; i++)
133+
width += widths[i];
134+
135+
if (width > section_len)
136+
width = section_len;
137+
138+
printf("\n%s\n%.*s\n", name, (int)width, section_line);
139+
}
140+
141+
static void print_row_plaintext(struct strvec *row, size_t *widths)
142+
{
143+
static struct strbuf line = STRBUF_INIT;
144+
strbuf_setlen(&line, 0);
145+
146+
for (size_t i = 0; i < row->nr; i++) {
147+
const char *str = row->v[i];
148+
size_t len = strlen(str);
149+
if (i)
150+
strbuf_add(&line, " | ", 3);
151+
strbuf_addchars(&line, ' ', widths[i] - len);
152+
strbuf_add(&line, str, len);
153+
}
154+
printf("%s\n", line.buf);
155+
}
156+
157+
static void print_divider_plaintext(size_t *widths, size_t nr)
158+
{
159+
static struct strbuf line = STRBUF_INIT;
160+
strbuf_setlen(&line, 0);
161+
162+
for (size_t i = 0; i < nr; i++) {
163+
if (i)
164+
strbuf_add(&line, "-+-", 3);
165+
strbuf_addchars(&line, '-', widths[i]);
166+
}
167+
printf("%s\n", line.buf);
168+
}
169+
170+
static void print_table_plaintext(struct survey_table *table)
171+
{
172+
size_t *column_widths;
173+
size_t columns_nr = table->header.nr;
174+
CALLOC_ARRAY(column_widths, columns_nr);
175+
176+
for (size_t i = 0; i < columns_nr; i++) {
177+
column_widths[i] = strlen(table->header.v[i]);
178+
179+
for (size_t j = 0; j < table->rows_nr; j++) {
180+
size_t rowlen = strlen(table->rows[j].v[i]);
181+
if (column_widths[i] < rowlen)
182+
column_widths[i] = rowlen;
183+
}
184+
}
185+
186+
print_table_title(table->table_name, column_widths, columns_nr);
187+
print_row_plaintext(&table->header, column_widths);
188+
print_divider_plaintext(column_widths, columns_nr);
189+
190+
for (size_t j = 0; j < table->rows_nr; j++)
191+
print_row_plaintext(&table->rows[j], column_widths);
192+
193+
free(column_widths);
194+
}
195+
196+
static void survey_report_plaintext_refs(struct survey_context *ctx)
197+
{
198+
struct survey_report_ref_summary *refs = &ctx->report.refs;
199+
struct survey_table table = SURVEY_TABLE_INIT;
200+
201+
table.table_name = _("REFERENCES SUMMARY");
202+
203+
strvec_push(&table.header, _("Ref Type"));
204+
strvec_push(&table.header, _("Count"));
205+
206+
if (ctx->opts.refs.want_all_refs || ctx->opts.refs.want_branches) {
207+
char *fmt = xstrfmt("%"PRIuMAX"", (uintmax_t)refs->branches_nr);
208+
insert_table_rowv(&table, _("Branches"), fmt, NULL);
209+
free(fmt);
210+
}
211+
212+
if (ctx->opts.refs.want_all_refs || ctx->opts.refs.want_remotes) {
213+
char *fmt = xstrfmt("%"PRIuMAX"", (uintmax_t)refs->remote_refs_nr);
214+
insert_table_rowv(&table, _("Remote refs"), fmt, NULL);
215+
free(fmt);
216+
}
217+
218+
if (ctx->opts.refs.want_all_refs || ctx->opts.refs.want_tags) {
219+
char *fmt = xstrfmt("%"PRIuMAX"", (uintmax_t)refs->tags_nr);
220+
insert_table_rowv(&table, _("Tags (all)"), fmt, NULL);
221+
free(fmt);
222+
fmt = xstrfmt("%"PRIuMAX"", (uintmax_t)refs->tags_annotated_nr);
223+
insert_table_rowv(&table, _("Tags (annotated)"), fmt, NULL);
224+
free(fmt);
225+
}
226+
227+
print_table_plaintext(&table);
228+
clear_table(&table);
229+
}
230+
231+
static void survey_report_plaintext(struct survey_context *ctx)
232+
{
233+
printf("GIT SURVEY for \"%s\"\n", ctx->repo->worktree);
234+
printf("-----------------------------------------------------\n");
235+
survey_report_plaintext_refs(ctx);
236+
}
237+
83238
/*
84239
* After parsing the command line arguments, figure out which refs we
85240
* should scan.
@@ -317,6 +472,8 @@ int cmd_survey(int argc, const char **argv, const char *prefix, struct repositor
317472

318473
survey_phase_refs(&ctx);
319474

475+
survey_report_plaintext(&ctx);
476+
320477
clear_survey_context(&ctx);
321478
return 0;
322479
}

t/t8100-git-survey.sh

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,23 @@ test_expect_success 'create a semi-interesting repo' '
2121

2222
test_expect_success 'git survey (default)' '
2323
git survey >out 2>err &&
24-
test_line_count = 0 err
24+
test_line_count = 0 err &&
25+
26+
tr , " " >expect <<-EOF &&
27+
GIT SURVEY for "$(pwd)"
28+
-----------------------------------------------------
29+
30+
REFERENCES SUMMARY
31+
========================
32+
, Ref Type | Count
33+
-----------------+------
34+
, Branches | 1
35+
Remote refs | 0
36+
Tags (all) | 0
37+
Tags (annotated) | 0
38+
EOF
39+
40+
test_cmp expect out
2541
'
2642

2743
test_done

0 commit comments

Comments
 (0)