Skip to content

Commit 98333d5

Browse files
committed
status: add --json output format to git status
Add a new --json flag to 'git status' that outputs repository state in a structured JSON format. This enables reliable machine parsing of status information for tools and automation. The JSON output includes: - Branch information (name, detached state, ahead/behind counts) - Staged files array - Unstaged files array - Untracked files array - Ignored files array (with --ignored flag) Implementation details: - Add STATUS_FORMAT_JSON to wt_status_format enum - Add JSON output option to git status and git commit - Implement JSON formatting helpers for arrays and branch info - Structure output for easy parsing and future extensibility Example: $ git status --json { "branch": { "current": "main", "detached": false }, "staged": ["file1.txt"], "unstaged": ["file2.txt"], "untracked": ["file3.txt"] } This provides a robust alternative to parsing traditional output formats, making it easier to build reliable tools and automation around Git status information. Signed-off-by: Tachera Sasi <[email protected]> Signed-off-by: tacherasasi <[email protected]>
1 parent cb3b403 commit 98333d5

File tree

3 files changed

+107
-1
lines changed

3 files changed

+107
-1
lines changed

builtin/commit.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1540,6 +1540,9 @@ struct repository *repo UNUSED)
15401540
OPT_SET_INT(0, "long", &status_format,
15411541
N_("show status in long format (default)"),
15421542
STATUS_FORMAT_LONG),
1543+
OPT_SET_INT(0, "json", &status_format,
1544+
N_("show status in JSON format"),
1545+
STATUS_FORMAT_JSON),
15431546
OPT_BOOL('z', "null", &s.null_termination,
15441547
N_("terminate entries with NUL")),
15451548
{
@@ -1603,7 +1606,8 @@ struct repository *repo UNUSED)
16031606
prefix, argv);
16041607

16051608
if (status_format != STATUS_FORMAT_PORCELAIN &&
1606-
status_format != STATUS_FORMAT_PORCELAIN_V2)
1609+
status_format != STATUS_FORMAT_PORCELAIN_V2 &&
1610+
status_format != STATUS_FORMAT_JSON)
16071611
progress_flag = REFRESH_PROGRESS;
16081612
repo_read_index(the_repository);
16091613
refresh_index(the_repository->index,
@@ -1735,6 +1739,9 @@ int cmd_commit(int argc,
17351739
OPT_SET_INT(0, "long", &status_format,
17361740
N_("show status in long format (default)"),
17371741
STATUS_FORMAT_LONG),
1742+
OPT_SET_INT(0, "json", &status_format,
1743+
N_("show status in JSON format"),
1744+
STATUS_FORMAT_JSON),
17381745
OPT_BOOL('z', "null", &s.null_termination,
17391746
N_("terminate entries with NUL")),
17401747
OPT_BOOL(0, "amend", &amend, N_("amend previous commit")),

wt-status.c

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2564,6 +2564,101 @@ static void wt_porcelain_v2_print(struct wt_status *s)
25642564
}
25652565
}
25662566

2567+
2568+
static void wt_json_print_string_array(struct wt_status *s, const char *name, struct string_list *list)
2569+
{
2570+
int i;
2571+
fprintf(s->fp, " \"%s\": [", name);
2572+
for (i = 0; i < list->nr; i++) {
2573+
if (i > 0)
2574+
fprintf(s->fp, ", ");
2575+
fprintf(s->fp, "\"%s\"", list->items[i].string);
2576+
}
2577+
fprintf(s->fp, "]");
2578+
}
2579+
2580+
static void wt_json_print_change_array(struct wt_status *s, const char *name, int change_type)
2581+
{
2582+
int i;
2583+
struct string_list files = STRING_LIST_INIT_DUP;
2584+
2585+
for (i = 0; i < s->change.nr; i++) {
2586+
struct wt_status_change_data *d;
2587+
struct string_list_item *it;
2588+
it = &(s->change.items[i]);
2589+
d = it->util;
2590+
2591+
if ((change_type == WT_STATUS_UPDATED && d->index_status &&
2592+
d->index_status != DIFF_STATUS_UNMERGED) ||
2593+
(change_type == WT_STATUS_CHANGED && d->worktree_status &&
2594+
d->worktree_status != DIFF_STATUS_UNMERGED)) {
2595+
string_list_append(&files, it->string);
2596+
}
2597+
}
2598+
2599+
wt_json_print_string_array(s, name, &files);
2600+
string_list_clear(&files, 0);
2601+
}
2602+
2603+
static void wt_json_print_branch_info(struct wt_status *s)
2604+
{
2605+
struct branch *branch;
2606+
const char *branch_name;
2607+
int ahead = 0, behind = 0;
2608+
2609+
fprintf(s->fp, " \"branch\": {\n");
2610+
2611+
if (s->branch && !s->is_initial) {
2612+
if (!strcmp(s->branch, "HEAD")) {
2613+
fprintf(s->fp, " \"current\": \"HEAD\",\n");
2614+
fprintf(s->fp, " \"detached\": true");
2615+
} else {
2616+
if (skip_prefix(s->branch, "refs/heads/", &branch_name)) {
2617+
fprintf(s->fp, " \"current\": \"%s\",\n", branch_name);
2618+
fprintf(s->fp, " \"detached\": false");
2619+
2620+
branch = branch_get(branch_name);
2621+
if (branch && branch->merge && branch->merge[0] && branch->merge[0]->dst) {
2622+
if (!stat_tracking_info(branch, &ahead, &behind, NULL, 0, 0)) {
2623+
fprintf(s->fp, ",\n \"ahead\": %d,\n \"behind\": %d", ahead, behind);
2624+
}
2625+
}
2626+
} else {
2627+
fprintf(s->fp, " \"current\": \"%s\",\n", s->branch);
2628+
fprintf(s->fp, " \"detached\": false");
2629+
}
2630+
}
2631+
} else {
2632+
fprintf(s->fp, " \"current\": null,\n");
2633+
fprintf(s->fp, " \"detached\": false");
2634+
}
2635+
2636+
fprintf(s->fp, "\n }");
2637+
}
2638+
2639+
static void wt_json_status_print(struct wt_status *s)
2640+
{
2641+
fprintf(s->fp, "{\n");
2642+
2643+
wt_json_print_branch_info(s);
2644+
fprintf(s->fp, ",\n");
2645+
2646+
wt_json_print_change_array(s, "staged", WT_STATUS_UPDATED);
2647+
fprintf(s->fp, ",\n");
2648+
2649+
wt_json_print_change_array(s, "unstaged", WT_STATUS_CHANGED);
2650+
fprintf(s->fp, ",\n");
2651+
2652+
wt_json_print_string_array(s, "untracked", &s->untracked);
2653+
2654+
if (s->ignored.nr > 0) {
2655+
fprintf(s->fp, ",\n");
2656+
wt_json_print_string_array(s, "ignored", &s->ignored);
2657+
}
2658+
2659+
fprintf(s->fp, "\n}\n");
2660+
}
2661+
25672662
void wt_status_print(struct wt_status *s)
25682663
{
25692664
trace2_data_intmax("status", s->repo, "count/changed", s->change.nr);
@@ -2583,6 +2678,9 @@ void wt_status_print(struct wt_status *s)
25832678
case STATUS_FORMAT_PORCELAIN_V2:
25842679
wt_porcelain_v2_print(s);
25852680
break;
2681+
case STATUS_FORMAT_JSON:
2682+
wt_json_status_print(s);
2683+
break;
25862684
case STATUS_FORMAT_UNSPECIFIED:
25872685
BUG("finalize_deferred_config() should have been called");
25882686
break;

wt-status.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ enum wt_status_format {
7474
STATUS_FORMAT_SHORT,
7575
STATUS_FORMAT_PORCELAIN,
7676
STATUS_FORMAT_PORCELAIN_V2,
77+
STATUS_FORMAT_JSON,
7778

7879
STATUS_FORMAT_UNSPECIFIED
7980
};

0 commit comments

Comments
 (0)