{"thread":{"id":"60408","subject":"[RFC PATCH 0/5] Introduce -t, --table for status/add commands","startedAt":"2023-10-20T18:39:55Z","lastAt":"2024-01-06T07:06:44Z","messageCount":62,"participants":["Jacob Stopak","Dragan Simic","Junio C Hamano","Oswald Buddenhagen"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"483584","messageId":"20231020183947.463882-1-jacob@initialcommit.io","threadId":"60408","inReplyTo":null,"subject":"[RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-20T18:39:42Z","receivedAt":"2023-10-20T18:39:55Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"This is a proposal / proof-of-concept for a new table-based output\nformat for the git status command, and for dry runs (-n) of the git add\ncommand. This could be extended to create visual dry runs for other\nother commands like rm, mv, restore, stash, commit, and clean.\n\nFor some context, earlier this year I released a tool called Git-Sim\n(https://github.com/initialcommit-com/git-sim) which allows users to do\nvisual dry runs of many Git commands, which are rendered as high quality\noutput image files. Simulating commands like status, add, rm, mv, restore,\nstash, and commit creates a table with 3 columns to represent the way file\nchanges \"move around\" as a result of the command being simulated.\n\nI've gotten positive feedback from users about this visual approach to\nsimulating git commands, which is more intuitive than pure terminal text\nfor both newer users to understand how git works and for visual people.\n\nAs a result, I was thinking of ways to integrate these types of visual\nformats directly into Git. A table-based output format with colored\nhighlighting for the commands mentioned above is low hanging fruit.\n\nTeach 'git status' the new -t, --table flag, which displays the status\noutput in a 3-column table format, preserving terminology and color\ncoding from the default git status \"long output\" format (note that the\ncolumn headers are shortened here for the small width of this email, and\nalso I just realized that the tables below might not look right on the\nmailing list due to the differing character width, but it looks correct\nin the terminal so please test there it's more fun anyway :D):\n\n$ git status -t\n-------------------------------------------------------------------------\n|    Untracked files    | Changes n...or commit | Changes t...committed |\n-------------------------------------------------------------------------\n|         poiu          |                       |                       |\n|     status-table/     |                       |                       |\n|                       |                       |         asdf          |\n|                       |        table.c        |                       |\n|                       |      wt-status.c      |                       |\n-------------------------------------------------------------------------\n\nTeach 'git add' the new -t, --table flag to be used ONLY in combination\nwith the '-n' flag for dry runs. Instead of simply printing out the\nadded filenames, the full status table format is displayed, along with\narrows that visually show how the added files are being moved around:\n\n$ git add -nt poiu wt-status.c\n-------------------------------------------------------------------------\n|    Untracked files    | Changes n...or commit | Changes t...committed |\n-------------------------------------------------------------------------\n|         poiu -----------------------------------------> poiu          |\n|     status-table/     |                       |                       |\n|                       |                       |         asdf          |\n|                       |        table.c        |                       |\n|                       |      wt-status.c ----------> wt-status.c      |\n-------------------------------------------------------------------------\n\nOther notes:\n\n* The width of the table and columns are dynamically set based on the\n  width of the terminal.\n\n* Long paths are shortened to include the maximum number of characters\n  from both ends of the path that will fit, with a '...' in the middle.\n\n* Color coding matches the default output of 'git status', with\n  untracked files and working dir mods in red, and staged changes in\n  green. If needed, arrows are drawn in cyan.\n\nAs stated above, the dry run version of the table format can be applied\nto various other commands like rm, mv, restore, stash, commit, and clean\nwhich all move file changes around in a way that can be represented in\nthe table format. New columns may need to be added or arrows reversed\nto show changes moving in various directions. Note that some of these\ncommands don't appear to have a dry run (-n) option yet, so it could be\nadded for consistency (if not already in use) and for use with the new\ntable format.\n\nSince this is an RFC patch series, I probably did some illegal and dumb\nthings in my code changes just to get it into a demo-able state. I am a\nbit wary of having made changes to files like \"read-cache.c\" and\n\"read-cache-ll.h\" to pass in the wt_status info, and there are probably\nbetters ways to do some other things too.\n\nFeedback on both the new format itself and the implementation is very\nmuch appreciated!\n\nJacob Stopak (5):\n  status: introduce -t, --table flag\n  status: handle long paths with -t, --table flag\n  status: add advice arg for -t, --table flag\n  add: add -t, --table flag for visual dry runs\n  add: set unique color for -t, --table arrows\n\n Makefile         |   1 +\n builtin/add.c    |  46 +++++++--\n builtin/commit.c |   4 +-\n read-cache-ll.h  |   9 +-\n read-cache.c     |  32 ++++++-\n table.c          | 245 +++++++++++++++++++++++++++++++++++++++++++++++\n table.h          |   6 ++\n wt-status.c      |  74 +++++++++-----\n wt-status.h      |   3 +\n 9 files changed, 378 insertions(+), 42 deletions(-)\n create mode 100644 table.c\n create mode 100644 table.h\n\n-- \n2.42.0.402.gbe8243af7b.dirty\n\n"},{"id":"483585","messageId":"20231020183947.463882-2-jacob@initialcommit.io","threadId":"60408","inReplyTo":"20231020183947.463882-1-jacob@initialcommit.io","subject":"[RFC PATCH 1/5] status: introduce -t, --table flag","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-20T18:39:43Z","receivedAt":"2023-10-20T18:39:56Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"Signed-off-by: Jacob Stopak <jacob@initialcommit.io>\n---\n Makefile         |   1 +\n builtin/commit.c |   2 +\n table.c          | 117 +++++++++++++++++++++++++++++++++++++++++++++++\n table.h          |   6 +++\n wt-status.c      |  72 +++++++++++++++++++----------\n wt-status.h      |   1 +\n 6 files changed, 174 insertions(+), 25 deletions(-)\n create mode 100644 table.c\n create mode 100644 table.h\n\ndiff --git a/Makefile b/Makefile\nindex 9c6a2f125f..a7399ca8f0 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1155,6 +1155,7 @@ LIB_OBJS += submodule-config.o\n LIB_OBJS += submodule.o\n LIB_OBJS += symlinks.o\n LIB_OBJS += tag.o\n+LIB_OBJS += table.o\n LIB_OBJS += tempfile.o\n LIB_OBJS += thread-utils.o\n LIB_OBJS += tmp-objdir.o\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 7da5f92448..4338896dbf 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1539,6 +1539,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \t\tOPT_CALLBACK_F('M', \"find-renames\", &rename_score_arg,\n \t\t  N_(\"n\"), N_(\"detect renames, optionally set similarity index\"),\n \t\t  PARSE_OPT_OPTARG | PARSE_OPT_NONEG, opt_parse_rename_score),\n+\t\tOPT_SET_INT('t', \"table\", &status_format,\n+\t\t\t    N_(\"show status in table format\"), STATUS_FORMAT_TABLE),\n \t\tOPT_END(),\n \t};\n \ndiff --git a/table.c b/table.c\nnew file mode 100644\nindex 0000000000..54cf9e4d07\n--- /dev/null\n+++ b/table.c\n@@ -0,0 +1,117 @@\n+#define USE_THE_INDEX_VARIABLE\n+#include \"builtin.h\"\n+#include \"gettext.h\"\n+#include \"strbuf.h\"\n+#include \"wt-status.h\"\n+#include \"config.h\"\n+#include \"string-list.h\"\n+#include \"sys/ioctl.h\"\n+\n+static const char *color(int slot, struct wt_status *s)\n+{\n+\tconst char *c = \"\";\n+\tif (want_color(s->use_color))\n+\t\tc = s->color_palette[slot];\n+\tif (slot == WT_STATUS_ONBRANCH && color_is_nil(c))\n+\t\tc = s->color_palette[WT_STATUS_HEADER];\n+\treturn c;\n+}\n+\n+static void build_table_border(struct strbuf *buf, int cols)\n+{\n+\tstrbuf_reset(buf);\n+\tstrbuf_addchars(buf, '-', cols);\n+}\n+\n+static void build_table_entry(struct strbuf *buf, char *entry, int cols)\n+{\n+\tstrbuf_reset(buf);\n+\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2);\n+\tstrbuf_addstr(buf, entry);\n+\n+\t/* Bump right padding if entry length is odd */\n+\tif (!(strlen(entry) % 2))\n+\t\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2 + 1);\n+\telse\n+\t\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2);\n+}\n+\n+static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct wt_status *s)\n+{\n+\tprintf(_(\"|\"));\n+\tcolor_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), \"%s\", buf1->buf);\n+\tprintf(_(\"|\"));\n+\tcolor_fprintf(s->fp, color(WT_STATUS_CHANGED, s), \"%s\", buf2->buf);\n+\tprintf(_(\"|\"));\n+\tcolor_fprintf(s->fp, color(WT_STATUS_UPDATED, s), \"%s\", buf3->buf);\n+\tprintf(_(\"|\\n\"));\n+}\n+\n+void build_and_draw_status_table(struct wt_status *s)\n+{\n+\tstruct winsize w;\n+\tint cols;\n+\tstruct strbuf table_border = STRBUF_INIT;\n+\tstruct strbuf table_col_entry_1 = STRBUF_INIT;\n+\tstruct strbuf table_col_entry_2 = STRBUF_INIT;\n+\tstruct strbuf table_col_entry_3 = STRBUF_INIT;\n+\tstruct string_list_item *item;\n+\n+\t/* Get terminal width */\n+\tioctl(STDOUT_FILENO, TIOCGWINSZ, &w);\n+\tcols = w.ws_col;\n+\n+\t/* Ensure table is divisible into 3 even columns */\n+\twhile (((cols - 1) % 3) > 0 || !(cols % 2)) {\n+\t\tcols -= 1;\n+\t}\n+\n+\tbuild_table_border(&table_border, cols);\n+\tbuild_table_entry(&table_col_entry_1, \"Untracked files\", cols);\n+\tbuild_table_entry(&table_col_entry_2, \"Changes not staged for commit\", cols);\n+\tbuild_table_entry(&table_col_entry_3, \"Changes to be committed\", cols);\n+\n+\t/* Draw table header */\n+\tprintf(_(\"%s\\n\"), table_border.buf);\n+\tprintf(_(\"|%s|%s|%s|\\n\"), table_col_entry_1.buf, table_col_entry_2.buf, table_col_entry_3.buf);\n+\tprintf(_(\"%s\\n\"), table_border.buf);\n+\n+\t/* Draw table body */\n+\tfor_each_string_list_item(item, &s->untracked) {\n+\t\tbuild_table_entry(&table_col_entry_1, item->string, cols);\n+\t\tbuild_table_entry(&table_col_entry_2, \"\", cols);\n+\t\tbuild_table_entry(&table_col_entry_3, \"\", cols);\n+\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\t}\n+\n+\tfor_each_string_list_item(item, &s->change) {\n+\t\tstruct wt_status_change_data *d = item->util;\n+\t\tif (d->worktree_status && d->index_status) {\n+\t\t\tbuild_table_entry(&table_col_entry_1, \"\", cols);\n+\t\t\tbuild_table_entry(&table_col_entry_2, item->string, cols);\n+\t\t\tbuild_table_entry(&table_col_entry_3, item->string, cols);\n+\t\t} else if (d->worktree_status) {\n+\t\t\tbuild_table_entry(&table_col_entry_1, \"\", cols);\n+\t\t\tbuild_table_entry(&table_col_entry_2, item->string, cols);\n+\t\t\tbuild_table_entry(&table_col_entry_3, \"\", cols);\n+\t\t} else if (d->index_status) {\n+\t\t\tbuild_table_entry(&table_col_entry_1, \"\", cols);\n+\t\t\tbuild_table_entry(&table_col_entry_2, \"\", cols);\n+\t\t\tbuild_table_entry(&table_col_entry_3, item->string, cols);\n+\t\t}\n+\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\t}\n+\t\n+\tif (!s->untracked.nr && !s->change.nr) {\n+\t\tbuild_table_entry(&table_col_entry_1, \"-\", cols);\n+\t\tbuild_table_entry(&table_col_entry_2, \"-\", cols);\n+\t\tbuild_table_entry(&table_col_entry_3, \"-\", cols);\n+\t\tprintf(_(\"|%s|%s|%s|\\n\"), table_col_entry_1.buf, table_col_entry_2.buf, table_col_entry_3.buf);\n+\t}\n+\n+\tprintf(_(\"%s\\n\"), table_border.buf);\n+\tstrbuf_release(&table_border);\n+\tstrbuf_release(&table_col_entry_1);\n+\tstrbuf_release(&table_col_entry_2);\n+\tstrbuf_release(&table_col_entry_3);\n+}\ndiff --git a/table.h b/table.h\nnew file mode 100644\nindex 0000000000..30e0d5509b\n--- /dev/null\n+++ b/table.h\n@@ -0,0 +1,6 @@\n+#ifndef TABLE_H\n+#define TABLE_H\n+\n+void build_and_draw_status_table(struct wt_status *s);\n+\n+#endif /* TABLE_H */\ndiff --git a/wt-status.c b/wt-status.c\nindex 9f45bf6949..24b56ea559 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -31,6 +31,7 @@\n #include \"lockfile.h\"\n #include \"sequencer.h\"\n #include \"fsmonitor-settings.h\"\n+#include \"table.h\"\n \n #define AB_DELAY_WARNING_IN_MS (2 * 1000)\n #define UF_DELAY_WARNING_IN_MS (2 * 1000)\n@@ -1833,39 +1834,46 @@ static void wt_longstatus_print_state(struct wt_status *s)\n \t\tshow_sparse_checkout_in_use(s, state_color);\n }\n \n-static void wt_longstatus_print(struct wt_status *s)\n+static void wt_longstatus_print_onwhat(struct wt_status *s, const char *branch_name)\n {\n+\tconst char *on_what = _(\"On branch \");\n \tconst char *branch_color = color(WT_STATUS_ONBRANCH, s);\n \tconst char *branch_status_color = color(WT_STATUS_HEADER, s);\n+\n+\tif (!strcmp(branch_name, \"HEAD\")) {\n+\t\tbranch_status_color = color(WT_STATUS_NOBRANCH, s);\n+\t\tif (s->state.rebase_in_progress ||\n+\t\t    s->state.rebase_interactive_in_progress) {\n+\t\t\tif (s->state.rebase_interactive_in_progress)\n+\t\t\t\ton_what = _(\"interactive rebase in progress; onto \");\n+\t\t\telse\n+\t\t\t\ton_what = _(\"rebase in progress; onto \");\n+\t\t\tbranch_name = s->state.onto;\n+\t\t} else if (s->state.detached_from) {\n+\t\t\tbranch_name = s->state.detached_from;\n+\t\t\tif (s->state.detached_at)\n+\t\t\t\ton_what = _(\"HEAD detached at \");\n+\t\t\telse\n+\t\t\t\ton_what = _(\"HEAD detached from \");\n+\t\t} else {\n+\t\t\tbranch_name = \"\";\n+\t\t\ton_what = _(\"Not currently on any branch.\");\n+\t\t}\n+\t} else\n+\t\tskip_prefix(branch_name, \"refs/heads/\", &branch_name);\n+\n+\tstatus_printf_more(s, branch_status_color, \"%s\", on_what);\n+\tstatus_printf_more(s, branch_color, \"%s\\n\", branch_name);\n+}\n+\n+static void wt_longstatus_print(struct wt_status *s)\n+{\n \tenum fsmonitor_mode fsm_mode = fsm_settings__get_mode(s->repo);\n \n \tif (s->branch) {\n-\t\tconst char *on_what = _(\"On branch \");\n \t\tconst char *branch_name = s->branch;\n-\t\tif (!strcmp(branch_name, \"HEAD\")) {\n-\t\t\tbranch_status_color = color(WT_STATUS_NOBRANCH, s);\n-\t\t\tif (s->state.rebase_in_progress ||\n-\t\t\t    s->state.rebase_interactive_in_progress) {\n-\t\t\t\tif (s->state.rebase_interactive_in_progress)\n-\t\t\t\t\ton_what = _(\"interactive rebase in progress; onto \");\n-\t\t\t\telse\n-\t\t\t\t\ton_what = _(\"rebase in progress; onto \");\n-\t\t\t\tbranch_name = s->state.onto;\n-\t\t\t} else if (s->state.detached_from) {\n-\t\t\t\tbranch_name = s->state.detached_from;\n-\t\t\t\tif (s->state.detached_at)\n-\t\t\t\t\ton_what = _(\"HEAD detached at \");\n-\t\t\t\telse\n-\t\t\t\t\ton_what = _(\"HEAD detached from \");\n-\t\t\t} else {\n-\t\t\t\tbranch_name = \"\";\n-\t\t\t\ton_what = _(\"Not currently on any branch.\");\n-\t\t\t}\n-\t\t} else\n-\t\t\tskip_prefix(branch_name, \"refs/heads/\", &branch_name);\n \t\tstatus_printf(s, color(WT_STATUS_HEADER, s), \"%s\", \"\");\n-\t\tstatus_printf_more(s, branch_status_color, \"%s\", on_what);\n-\t\tstatus_printf_more(s, branch_color, \"%s\\n\", branch_name);\n+\t\twt_longstatus_print_onwhat(s, branch_name);\n \t\tif (!s->is_initial)\n \t\t\twt_longstatus_print_tracking(s);\n \t}\n@@ -2133,6 +2141,17 @@ static void wt_shortstatus_print(struct wt_status *s)\n \t\twt_shortstatus_other(it, s, \"!!\");\n }\n \n+static void wt_tablestatus_print(struct wt_status *s)\n+{\n+\tif (s->show_branch) {\n+\t\tconst char *branch_name = s->branch;\n+\t\twt_longstatus_print_onwhat(s, branch_name);\n+\t\twt_longstatus_print_tracking(s);\n+\t}\n+\n+\tbuild_and_draw_status_table(s);\n+}\n+\n static void wt_porcelain_print(struct wt_status *s)\n {\n \ts->use_color = 0;\n@@ -2560,6 +2579,9 @@ void wt_status_print(struct wt_status *s)\n \tcase STATUS_FORMAT_LONG:\n \t\twt_longstatus_print(s);\n \t\tbreak;\n+\tcase STATUS_FORMAT_TABLE:\n+\t\twt_tablestatus_print(s);\n+\t\tbreak;\n \t}\n \n \ttrace2_region_leave(\"status\", \"print\", s->repo);\ndiff --git a/wt-status.h b/wt-status.h\nindex ab9cc9d8f0..70a3b7a2e4 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -73,6 +73,7 @@ enum wt_status_format {\n \tSTATUS_FORMAT_SHORT,\n \tSTATUS_FORMAT_PORCELAIN,\n \tSTATUS_FORMAT_PORCELAIN_V2,\n+\tSTATUS_FORMAT_TABLE,\n \n \tSTATUS_FORMAT_UNSPECIFIED\n };\n-- \n2.42.0.402.gbe8243af7b.dirty\n\n"},{"id":"483586","messageId":"20231020183947.463882-3-jacob@initialcommit.io","threadId":"60408","inReplyTo":"20231020183947.463882-1-jacob@initialcommit.io","subject":"[RFC PATCH 2/5] status: handle long paths with -t, --table flag","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-20T18:39:44Z","receivedAt":"2023-10-20T18:39:56Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"Signed-off-by: Jacob Stopak <jacob@initialcommit.io>\n---\n table.c | 39 ++++++++++++++++++++++++++++++++++-----\n 1 file changed, 34 insertions(+), 5 deletions(-)\n\ndiff --git a/table.c b/table.c\nindex 54cf9e4d07..87b6df8c66 100644\n--- a/table.c\n+++ b/table.c\n@@ -25,15 +25,44 @@ static void build_table_border(struct strbuf *buf, int cols)\n \n static void build_table_entry(struct strbuf *buf, char *entry, int cols)\n {\n+\tint len = strlen(entry);\n+\tsize_t col_width = (cols / 3) - 5; /* subtract for padding */\n+\n \tstrbuf_reset(buf);\n-\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2);\n+\n+\t/* Trim equally from both sides if it doesn't fit in column */\n+\tif (len > col_width) {\n+\t\tstruct strbuf start_buf = STRBUF_INIT;\n+\t\tstruct strbuf end_buf = STRBUF_INIT;\n+\t\tstruct strbuf entry_buf = STRBUF_INIT;\n+\n+\t\tstrbuf_addstr(&start_buf, entry);\n+\t\tstrbuf_addstr(&end_buf, entry);\n+\n+\t\tstrbuf_remove(&start_buf, col_width / 2, len - col_width / 2);\n+\t\tstrbuf_remove(&end_buf, 0, len - col_width / 2);\n+\n+\t\tstrbuf_addstr(&entry_buf, start_buf.buf);\n+\t\tstrbuf_addstr(&entry_buf, \"...\");\n+\t\tstrbuf_addstr(&entry_buf, end_buf.buf);\n+\n+\t\tentry = strbuf_detach(&entry_buf, &col_width);\n+\t\tlen = strlen(entry);\n+\n+\t\tstrbuf_release(&start_buf);\n+\t\tstrbuf_release(&end_buf);\n+\t\tstrbuf_release(&entry_buf);\n+\t}\n+\n+\tstrbuf_addchars(buf, ' ', (cols / 3 - len - 1) / 2); /* left padding */\n \tstrbuf_addstr(buf, entry);\n \n-\t/* Bump right padding if entry length is odd */\n-\tif (!(strlen(entry) % 2))\n-\t\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2 + 1);\n+\t/* right padding */\n+\tif (!(len % 2))\n+\t\t/* Bump right padding if entry length is odd */\n+\t\tstrbuf_addchars(buf, ' ', (cols / 3 - len - 1) / 2 + 1);\n \telse\n-\t\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2);\n+\t\tstrbuf_addchars(buf, ' ', (cols / 3 - len - 1) / 2);\n }\n \n static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct wt_status *s)\n-- \n2.42.0.402.gbe8243af7b.dirty\n\n"},{"id":"483587","messageId":"20231020183947.463882-4-jacob@initialcommit.io","threadId":"60408","inReplyTo":"20231020183947.463882-1-jacob@initialcommit.io","subject":"[RFC PATCH 3/5] status: add advice arg for -t, --table flag","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-20T18:39:45Z","receivedAt":"2023-10-20T18:39:57Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"Signed-off-by: Jacob Stopak <jacob@initialcommit.io>\n---\n table.c     | 19 +++++++++++++++++--\n table.h     |  2 +-\n wt-status.c |  2 +-\n 3 files changed, 19 insertions(+), 4 deletions(-)\n\ndiff --git a/table.c b/table.c\nindex 87b6df8c66..73751339da 100644\n--- a/table.c\n+++ b/table.c\n@@ -76,7 +76,7 @@ static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, stru\n \tprintf(_(\"|\\n\"));\n }\n \n-void build_and_draw_status_table(struct wt_status *s)\n+void build_and_draw_status_table(struct wt_status *s, int add_advice)\n {\n \tstruct winsize w;\n \tint cols;\n@@ -95,14 +95,29 @@ void build_and_draw_status_table(struct wt_status *s)\n \t\tcols -= 1;\n \t}\n \n+\t/* Draw table header */\n \tbuild_table_border(&table_border, cols);\n \tbuild_table_entry(&table_col_entry_1, \"Untracked files\", cols);\n \tbuild_table_entry(&table_col_entry_2, \"Changes not staged for commit\", cols);\n \tbuild_table_entry(&table_col_entry_3, \"Changes to be committed\", cols);\n \n-\t/* Draw table header */\n \tprintf(_(\"%s\\n\"), table_border.buf);\n \tprintf(_(\"|%s|%s|%s|\\n\"), table_col_entry_1.buf, table_col_entry_2.buf, table_col_entry_3.buf);\n+\n+\tif (add_advice) {\n+\t\tbuild_table_entry(&table_col_entry_1, \"(stage: git add <file>)\", cols);\n+\t\tbuild_table_entry(&table_col_entry_2, \"(stage: git add <file>)\", cols);\n+\t\tbuild_table_entry(&table_col_entry_3, \"(unstage: git restore --staged <file>)\", cols);\n+\n+\t\tprintf(_(\"|%s|%s|%s|\\n\"), table_col_entry_1.buf, table_col_entry_2.buf, table_col_entry_3.buf);\n+\n+\t\tbuild_table_entry(&table_col_entry_1, \"\", cols);\n+\t\tbuild_table_entry(&table_col_entry_2, \"(discard: git restore --staged <file>)\", cols);\n+\t\tbuild_table_entry(&table_col_entry_3, \"\", cols);\n+\n+\t\tprintf(_(\"|%s|%s|%s|\\n\"), table_col_entry_1.buf, table_col_entry_2.buf, table_col_entry_3.buf);\n+\t}\n+\n \tprintf(_(\"%s\\n\"), table_border.buf);\n \n \t/* Draw table body */\ndiff --git a/table.h b/table.h\nindex 30e0d5509b..6017923bf9 100644\n--- a/table.h\n+++ b/table.h\n@@ -1,6 +1,6 @@\n #ifndef TABLE_H\n #define TABLE_H\n \n-void build_and_draw_status_table(struct wt_status *s);\n+void build_and_draw_status_table(struct wt_status *s, int i);\n \n #endif /* TABLE_H */\ndiff --git a/wt-status.c b/wt-status.c\nindex 24b56ea559..62731859fe 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -2149,7 +2149,7 @@ static void wt_tablestatus_print(struct wt_status *s)\n \t\twt_longstatus_print_tracking(s);\n \t}\n \n-\tbuild_and_draw_status_table(s);\n+\tbuild_and_draw_status_table(s, 0);\n }\n \n static void wt_porcelain_print(struct wt_status *s)\n-- \n2.42.0.402.gbe8243af7b.dirty\n\n"},{"id":"483588","messageId":"20231020183947.463882-6-jacob@initialcommit.io","threadId":"60408","inReplyTo":"20231020183947.463882-1-jacob@initialcommit.io","subject":"[RFC PATCH 5/5] add: set unique color for -t, --table arrows","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-20T18:39:47Z","receivedAt":"2023-10-20T18:39:59Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"Signed-off-by: Jacob Stopak <jacob@initialcommit.io>\n---\n table.c     | 62 +++++++++++++++++++++++++++++++----------------------\n wt-status.c |  1 +\n wt-status.h |  1 +\n 3 files changed, 38 insertions(+), 26 deletions(-)\n\ndiff --git a/table.c b/table.c\nindex a6fc660fec..390b2e2dd9 100644\n--- a/table.c\n+++ b/table.c\n@@ -5,6 +5,7 @@\n #include \"wt-status.h\"\n #include \"config.h\"\n #include \"string-list.h\"\n+#include \"color.h\"\n #include \"sys/ioctl.h\"\n \n static const char *color(int slot, struct wt_status *s)\n@@ -65,52 +66,51 @@ static void build_table_entry(struct strbuf *buf, char *entry, int cols)\n \t\tstrbuf_addchars(buf, ' ', (cols / 3 - len - 1) / 2);\n }\n \n-static void add_arrow_to_entry(struct strbuf *buf, int add_after_entry)\n+static void build_arrow(struct strbuf *buf, struct strbuf* arrow, int add_after_entry)\n {\n \tstruct strbuf empty = STRBUF_INIT;\n \tstruct strbuf trimmed = STRBUF_INIT;\n-\tstruct strbuf holder = STRBUF_INIT;\n \tint len = strlen(buf->buf);\n \n+\tstrbuf_reset(arrow);\n \tstrbuf_addstr(&trimmed, buf->buf);\n \tstrbuf_trim(&trimmed);\n \n \tif (!strbuf_cmp(&trimmed, &empty) && !add_after_entry) {\n \t\tstrbuf_reset(buf);\n-\t\tstrbuf_addchars(buf, '-', len + 1);\n+\t\tstrbuf_addchars(arrow, '-', len + 1);\n \t} else if (add_after_entry) {\n \t\tstrbuf_rtrim(buf);\n-\t\tstrbuf_addchars(buf, ' ', 1);\n-\t\tstrbuf_addchars(buf, '-', len - strlen(buf->buf) + 1);\n+\t\tstrbuf_addchars(arrow, ' ', 1);\n+\t\tstrbuf_addchars(arrow, '-', len - strlen(buf->buf) + 1);\n \t} else if (!add_after_entry) {\n \t\tstrbuf_ltrim(buf);\n-\t\tstrbuf_addchars(&holder, '-', len - strlen(buf->buf) - 2);\n-\t\tstrbuf_addchars(&holder, '>', 1);\n-\t\tstrbuf_addchars(&holder, ' ', 1);\n-\t\tstrbuf_addstr(&holder, buf->buf);\n-\t\tstrbuf_reset(buf);\n-\t\tstrbuf_addstr(buf, holder.buf);\n+\t\tstrbuf_addchars(arrow, '-', len - strlen(buf->buf) - 3);\n+\t\tstrbuf_addchars(arrow, '>', 1);\n+\t\tstrbuf_addchars(arrow, ' ', 1);\n \t}\n }\n \n-static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct wt_status *s, int hide_pipe)\n+static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct strbuf *arrow1, struct strbuf *arrow2, struct strbuf *arrow3, struct wt_status *s, int hide_pipe)\n {\n \tprintf(_(\"|\"));\n \tcolor_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), \"%s\", buf1->buf);\n+\tif (strlen(arrow1->buf) > 0)\n+\t\tcolor_fprintf(s->fp, color(WT_STATUS_ARROW, s), \"%s\", arrow1->buf);\n \tif (hide_pipe != 1 && hide_pipe != 3)\n \t\tprintf(_(\"|\"));\n \tcolor_fprintf(s->fp, color(WT_STATUS_CHANGED, s), \"%s\", buf2->buf);\n+\tif (strlen(arrow2->buf) > 0)\n+\t\tcolor_fprintf(s->fp, color(WT_STATUS_ARROW, s), \"%s\", arrow2->buf);\n \tif (hide_pipe != 2 && hide_pipe != 3)\n \t\tprintf(_(\"|\"));\n+\tif (strlen(arrow3->buf) > 0) {\n+\t\tcolor_fprintf(s->fp, color(WT_STATUS_ARROW, s), \"%s\", arrow3->buf);\n+\t}\n \tcolor_fprintf(s->fp, color(WT_STATUS_UPDATED, s), \"%s\", buf3->buf);\n \tprintf(_(\"|\\n\"));\n }\n \n-static void print_table_body_line_(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct wt_status *s)\n-{\n-\tprint_table_body_line(buf1, buf2, buf3, s, 0);\n-}\n-\n void build_and_draw_status_table(struct wt_status *s, int advice)\n {\n \tstruct winsize w;\n@@ -119,6 +119,9 @@ void build_and_draw_status_table(struct wt_status *s, int advice)\n \tstruct strbuf table_col_entry_1 = STRBUF_INIT;\n \tstruct strbuf table_col_entry_2 = STRBUF_INIT;\n \tstruct strbuf table_col_entry_3 = STRBUF_INIT;\n+\tstruct strbuf arrow_1 = STRBUF_INIT;\n+\tstruct strbuf arrow_2 = STRBUF_INIT;\n+\tstruct strbuf arrow_3 = STRBUF_INIT;\n \tstruct string_list_item *item, *item2;\n \n \t/* Get terminal width */\n@@ -170,17 +173,21 @@ void build_and_draw_status_table(struct wt_status *s, int advice)\n \t\t\tstrbuf_addstr(&buf_2, item2->string);\n \t\t\tif (!strbuf_cmp(&buf_1, &buf_2)) {\n \t\t\t\tbuild_table_entry(&table_col_entry_3, buf_1.buf, cols);\n-\t\t\t\tadd_arrow_to_entry(&table_col_entry_1, 1);\n-\t\t\t\tadd_arrow_to_entry(&table_col_entry_2, 0);\n-\t\t\t\tadd_arrow_to_entry(&table_col_entry_3, 0);\n+\t\t\t\tbuild_arrow(&table_col_entry_1, &arrow_1, 1);\n+\t\t\t\tbuild_arrow(&table_col_entry_2, &arrow_2, 0);\n+\t\t\t\tbuild_arrow(&table_col_entry_3, &arrow_3, 0);\n \t\t\t\tis_arrow = 1;\n \t\t\t}\n \t\t}\n \n \t\tif (!is_arrow)\n-\t\t\tprint_table_body_line_(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\t\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, &arrow_1, &arrow_2, &arrow_3, s, 0);\n \t\telse\n-\t\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s, 3);\n+\t\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, &arrow_1, &arrow_2, &arrow_3, s, 3);\n+\n+\t\tstrbuf_reset(&arrow_1);\n+\t\tstrbuf_reset(&arrow_2);\n+\t\tstrbuf_reset(&arrow_3);\n \t}\n \n \tfor_each_string_list_item(item, &s->change) {\n@@ -203,8 +210,8 @@ void build_and_draw_status_table(struct wt_status *s, int advice)\n \t\t\t\tstrbuf_addstr(&buf_2, item2->string);\n \t\t\t\tif (!strbuf_cmp(&buf_1, &buf_2)) {\n \t\t\t\t\tbuild_table_entry(&table_col_entry_3, buf_1.buf, cols);\n-\t\t\t\t\tadd_arrow_to_entry(&table_col_entry_2, 1);\n-\t\t\t\t\tadd_arrow_to_entry(&table_col_entry_3, 0);\n+\t\t\t\t\tbuild_arrow(&table_col_entry_2, &arrow_2, 1);\n+\t\t\t\t\tbuild_arrow(&table_col_entry_3, &arrow_3, 0);\n \t\t\t\t\tis_arrow = 1;\n \t\t\t\t}\n \t\t\t}\n@@ -215,9 +222,12 @@ void build_and_draw_status_table(struct wt_status *s, int advice)\n \t\t}\n \n \t\tif (!is_arrow)\n-\t\t\tprint_table_body_line_(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\t\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, &arrow_1, &arrow_2, &arrow_3, s, 0);\n \t\telse\n-\t\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s, 2);\n+\t\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, &arrow_1, &arrow_2, &arrow_3, s, 2);\n+\t\tstrbuf_reset(&arrow_1);\n+\t\tstrbuf_reset(&arrow_2);\n+\t\tstrbuf_reset(&arrow_3);\n \t}\n \t\n \tif (!s->untracked.nr && !s->change.nr) {\ndiff --git a/wt-status.c b/wt-status.c\nindex 975cfc01a5..fe38260baa 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -49,6 +49,7 @@ static char default_wt_status_colors[][COLOR_MAXLEN] = {\n \tGIT_COLOR_GREEN,  /* WT_STATUS_LOCAL_BRANCH */\n \tGIT_COLOR_RED,    /* WT_STATUS_REMOTE_BRANCH */\n \tGIT_COLOR_NIL,    /* WT_STATUS_ONBRANCH */\n+\tGIT_COLOR_CYAN,   /* WT_STATUS_ARROW */\n };\n \n static const char *color(int slot, struct wt_status *s)\ndiff --git a/wt-status.h b/wt-status.h\nindex 5d29c058c1..0517f81e1b 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -19,6 +19,7 @@ enum color_wt_status {\n \tWT_STATUS_LOCAL_BRANCH,\n \tWT_STATUS_REMOTE_BRANCH,\n \tWT_STATUS_ONBRANCH,\n+\tWT_STATUS_ARROW,\n \tWT_STATUS_MAXSLOT\n };\n \n-- \n2.42.0.402.gbe8243af7b.dirty\n\n"},{"id":"483589","messageId":"20231020183947.463882-5-jacob@initialcommit.io","threadId":"60408","inReplyTo":"20231020183947.463882-1-jacob@initialcommit.io","subject":"[RFC PATCH 4/5] add: add -t, --table flag for visual dry runs","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-20T18:39:46Z","receivedAt":"2023-10-20T18:39:59Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"Signed-off-by: Jacob Stopak <jacob@initialcommit.io>\n---\n builtin/add.c    | 46 ++++++++++++++++++------\n builtin/commit.c |  2 +-\n read-cache-ll.h  |  9 ++++-\n read-cache.c     | 32 ++++++++++++++---\n table.c          | 92 +++++++++++++++++++++++++++++++++++++++++++-----\n wt-status.c      |  1 +\n wt-status.h      |  1 +\n 7 files changed, 157 insertions(+), 26 deletions(-)\n\ndiff --git a/builtin/add.c b/builtin/add.c\nindex c27254a5cd..35ea1deda5 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -27,6 +27,7 @@\n #include \"strvec.h\"\n #include \"submodule.h\"\n #include \"add-interactive.h\"\n+#include \"wt-status.h\"\n \n static const char * const builtin_add_usage[] = {\n \tN_(\"git add [<options>] [--] <pathspec>...\"),\n@@ -221,7 +222,7 @@ N_(\"The following paths are ignored by one of your .gitignore files:\\n\");\n \n static int verbose, show_only, ignored_too, refresh_only;\n static int ignore_add_errors, intent_to_add, ignore_missing;\n-static int warn_on_embedded_repo = 1;\n+static int table_format, warn_on_embedded_repo = 1;\n \n #define ADDREMOVE_DEFAULT 1\n static int addremove = ADDREMOVE_DEFAULT;\n@@ -264,6 +265,8 @@ static struct option builtin_add_options[] = {\n \t\t\tN_(\"warn when adding an embedded repository\")),\n \tOPT_PATHSPEC_FROM_FILE(&pathspec_from_file),\n \tOPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),\n+\tOPT_SET_INT('t', \"table\", &table_format,\n+\t\t    N_(\"show status in table format\"), STATUS_FORMAT_TABLE),\n \tOPT_END(),\n };\n \n@@ -322,7 +325,7 @@ static void check_embedded_repo(const char *path)\n \tstrbuf_release(&name);\n }\n \n-static int add_files(struct dir_struct *dir, int flags)\n+static int add_files(struct dir_struct *dir, int flags, struct wt_status *status)\n {\n \tint i, exit_status = 0;\n \tstruct string_list matched_sparse_paths = STRING_LIST_INIT_NODUP;\n@@ -345,7 +348,7 @@ static int add_files(struct dir_struct *dir, int flags)\n \t\t\t\t\t   dir->entries[i]->name);\n \t\t\tcontinue;\n \t\t}\n-\t\tif (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {\n+\t\tif (add_file_to_index_with_status(&the_index, dir->entries[i]->name, flags, status)) {\n \t\t\tif (!ignore_add_errors)\n \t\t\t\tdie(_(\"adding files failed\"));\n \t\t\texit_status = 1;\n@@ -374,6 +377,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \tint require_pathspec;\n \tchar *seen = NULL;\n \tstruct lock_file lock_file = LOCK_INIT;\n+\tstruct wt_status status;\n+\tunsigned int progress_flag = 0;\n \n \tgit_config(add_config, NULL);\n \n@@ -459,7 +464,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \t\t (intent_to_add ? ADD_CACHE_INTENT : 0) |\n \t\t (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |\n \t\t (!(addremove || take_worktree_changes)\n-\t\t  ? ADD_CACHE_IGNORE_REMOVAL : 0));\n+\t\t  ? ADD_CACHE_IGNORE_REMOVAL : 0) |\n+\t\t (table_format ? ADD_CACHE_FORMAT_TABLE : 0));\n \n \tif (repo_read_index_preload(the_repository, &pathspec, 0) < 0)\n \t\tdie(_(\"index file corrupt\"));\n@@ -551,15 +557,35 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \n \tbegin_odb_transaction();\n \n-\tif (add_renormalize)\n+\tif (show_only && table_format) {\n+\t\t/* Prepare index and populate status */\n+\t\twt_status_prepare(the_repository, &status);\n+\t\tgit_config(git_default_config, &status);\n+\t\trepo_read_index(the_repository);\n+\t\trefresh_index(&the_index,\n+\t\t\t      REFRESH_QUIET|REFRESH_UNMERGED|progress_flag,\n+\t\t\t      &status.pathspec, NULL, NULL);\n+\t\tstatus.status_format = STATUS_FORMAT_TABLE;\n+\t\tstatus.show_branch = 0;\n+\t}\n+\n+\tif (add_renormalize) {\n \t\texit_status |= renormalize_tracked_files(&pathspec, flags);\n-\telse\n-\t\texit_status |= add_files_to_cache(the_repository, prefix,\n+\t} else {\n+\t\texit_status |= add_files_to_cache_with_status(the_repository, prefix,\n \t\t\t\t\t\t  &pathspec, include_sparse,\n-\t\t\t\t\t\t  flags);\n+\t\t\t\t\t\t  flags, &status);\n+\t}\n \n-\tif (add_new_files)\n-\t\texit_status |= add_files(&dir, flags);\n+\tif (add_new_files) {\n+\t\texit_status |= add_files(&dir, flags, &status);\n+\t}\n+\n+\tif (show_only && table_format) {\n+\t\twt_status_collect(&status);\n+\t\twt_status_print(&status);\n+\t\twt_status_collect_free_buffers(&status);\n+\t}\n \n \tif (chmod_arg && pathspec.nr)\n \t\texit_status |= chmod_pathspec(&pathspec, chmod_arg[0], show_only);\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 4338896dbf..33b15ef96e 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -310,7 +310,7 @@ static void add_remove_files(struct string_list *list)\n \t\t\tcontinue;\n \n \t\tif (!lstat(p->string, &st)) {\n-\t\t\tif (add_to_index(&the_index, p->string, &st, 0))\n+\t\t\tif (add_file_to_index(&the_index, p->string, 0))\n \t\t\t\tdie(_(\"updating files failed\"));\n \t\t} else\n \t\t\tremove_file_from_index(&the_index, p->string);\ndiff --git a/read-cache-ll.h b/read-cache-ll.h\nindex 9a1a7edc5a..b1cee2c7ee 100644\n--- a/read-cache-ll.h\n+++ b/read-cache-ll.h\n@@ -4,6 +4,7 @@\n #include \"hash-ll.h\"\n #include \"hashmap.h\"\n #include \"statinfo.h\"\n+#include \"wt-status.h\"\n \n /*\n  * Basic data structures for the directory cache\n@@ -395,6 +396,7 @@ int remove_file_from_index(struct index_state *, const char *path);\n #define ADD_CACHE_IGNORE_ERRORS\t4\n #define ADD_CACHE_IGNORE_REMOVAL 8\n #define ADD_CACHE_INTENT 16\n+#define ADD_CACHE_FORMAT_TABLE 32\n /*\n  * These two are used to add the contents of the file at path\n  * to the index, marking the working tree up-to-date by storing\n@@ -404,7 +406,8 @@ int remove_file_from_index(struct index_state *, const char *path);\n  * the latter will do necessary lstat(2) internally before\n  * calling the former.\n  */\n-int add_to_index(struct index_state *, const char *path, struct stat *, int flags);\n+int add_to_index(struct index_state *, const char *path, struct stat *, int flags, struct wt_status *status);\n+int add_file_to_index_with_status(struct index_state *, const char *path, int flags, struct wt_status *status);\n int add_file_to_index(struct index_state *, const char *path, int flags);\n \n int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);\n@@ -475,6 +478,10 @@ int add_files_to_cache(struct repository *repo, const char *prefix,\n \t\t       const struct pathspec *pathspec, int include_sparse,\n \t\t       int flags);\n \n+int add_files_to_cache_with_status(struct repository *repo, const char *prefix,\n+\t\t       const struct pathspec *pathspec, int include_sparse,\n+\t\t       int flags, struct wt_status *status);\n+\n void overlay_tree_on_index(struct index_state *istate,\n \t\t\t   const char *tree_name, const char *prefix);\n \ndiff --git a/read-cache.c b/read-cache.c\nindex 080bd39713..e777cdb210 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -45,6 +45,8 @@\n #include \"csum-file.h\"\n #include \"promisor-remote.h\"\n #include \"hook.h\"\n+#include \"wt-status.h\"\n+#include \"string-list.h\"\n \n /* Mask for the name length in ce_flags in the on-disk index */\n \n@@ -664,7 +666,7 @@ void set_object_name_for_intent_to_add_entry(struct cache_entry *ce)\n \toidcpy(&ce->oid, &oid);\n }\n \n-int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags)\n+int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags, struct wt_status *status)\n {\n \tint namelen, was_same;\n \tmode_t st_mode = st->st_mode;\n@@ -672,6 +674,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,\n \tunsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE|CE_MATCH_RACY_IS_DIRTY;\n \tint verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND);\n \tint pretend = flags & ADD_CACHE_PRETEND;\n+\tint table = flags & ADD_CACHE_FORMAT_TABLE;\n \tint intent_only = flags & ADD_CACHE_INTENT;\n \tint add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|\n \t\t\t  (intent_only ? ADD_CACHE_NEW_ONLY : 0));\n@@ -760,17 +763,26 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,\n \t\tdiscard_cache_entry(ce);\n \t\treturn error(_(\"unable to add '%s' to index\"), path);\n \t}\n-\tif (verbose && !was_same)\n+\tif (verbose && !was_same && !table)\n \t\tprintf(\"add '%s'\\n\", path);\n+\tif (table && pretend && !was_same) {\n+\t\tstring_list_insert(&status->dry_run_added, path);\n+\t}\n \treturn 0;\n }\n \n-int add_file_to_index(struct index_state *istate, const char *path, int flags)\n+int add_file_to_index_with_status(struct index_state *istate, const char *path, int flags, struct wt_status *status)\n {\n \tstruct stat st;\n \tif (lstat(path, &st))\n \t\tdie_errno(_(\"unable to stat '%s'\"), path);\n-\treturn add_to_index(istate, path, &st, flags);\n+\treturn add_to_index(istate, path, &st, flags, status);\n+}\n+\n+int add_file_to_index(struct index_state *istate, const char *path, int flags)\n+{\n+\tstruct wt_status status;\n+\treturn add_file_to_index_with_status(istate, path, flags, &status);\n }\n \n struct cache_entry *make_empty_cache_entry(struct index_state *istate, size_t len)\n@@ -3872,6 +3884,7 @@ struct update_callback_data {\n \tint include_sparse;\n \tint flags;\n \tint add_errors;\n+\tstruct wt_status *status;\n };\n \n static int fix_unmerged_status(struct diff_filepair *p,\n@@ -3914,7 +3927,7 @@ static void update_callback(struct diff_queue_struct *q,\n \t\t\tdie(_(\"unexpected diff status %c\"), p->status);\n \t\tcase DIFF_STATUS_MODIFIED:\n \t\tcase DIFF_STATUS_TYPE_CHANGED:\n-\t\t\tif (add_file_to_index(data->index, path, data->flags)) {\n+\t\t\tif (add_file_to_index_with_status(data->index, path, data->flags, data->status)) {\n \t\t\t\tif (!(data->flags & ADD_CACHE_IGNORE_ERRORS))\n \t\t\t\t\tdie(_(\"updating files failed\"));\n \t\t\t\tdata->add_errors++;\n@@ -3935,6 +3948,14 @@ static void update_callback(struct diff_queue_struct *q,\n int add_files_to_cache(struct repository *repo, const char *prefix,\n \t\t       const struct pathspec *pathspec, int include_sparse,\n \t\t       int flags)\n+{\n+\tstruct wt_status status;\n+\treturn add_files_to_cache_with_status(repo, prefix, pathspec, include_sparse, flags, &status);\n+}\n+\n+int add_files_to_cache_with_status(struct repository *repo, const char *prefix,\n+\t\t       const struct pathspec *pathspec, int include_sparse,\n+\t\t       int flags, struct wt_status *status)\n {\n \tstruct update_callback_data data;\n \tstruct rev_info rev;\n@@ -3943,6 +3964,7 @@ int add_files_to_cache(struct repository *repo, const char *prefix,\n \tdata.index = repo->index;\n \tdata.include_sparse = include_sparse;\n \tdata.flags = flags;\n+\tdata.status = status;\n \n \trepo_init_revisions(repo, &rev, prefix);\n \tsetup_revisions(0, NULL, &rev, NULL);\ndiff --git a/table.c b/table.c\nindex 73751339da..a6fc660fec 100644\n--- a/table.c\n+++ b/table.c\n@@ -65,18 +65,53 @@ static void build_table_entry(struct strbuf *buf, char *entry, int cols)\n \t\tstrbuf_addchars(buf, ' ', (cols / 3 - len - 1) / 2);\n }\n \n-static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct wt_status *s)\n+static void add_arrow_to_entry(struct strbuf *buf, int add_after_entry)\n+{\n+\tstruct strbuf empty = STRBUF_INIT;\n+\tstruct strbuf trimmed = STRBUF_INIT;\n+\tstruct strbuf holder = STRBUF_INIT;\n+\tint len = strlen(buf->buf);\n+\n+\tstrbuf_addstr(&trimmed, buf->buf);\n+\tstrbuf_trim(&trimmed);\n+\n+\tif (!strbuf_cmp(&trimmed, &empty) && !add_after_entry) {\n+\t\tstrbuf_reset(buf);\n+\t\tstrbuf_addchars(buf, '-', len + 1);\n+\t} else if (add_after_entry) {\n+\t\tstrbuf_rtrim(buf);\n+\t\tstrbuf_addchars(buf, ' ', 1);\n+\t\tstrbuf_addchars(buf, '-', len - strlen(buf->buf) + 1);\n+\t} else if (!add_after_entry) {\n+\t\tstrbuf_ltrim(buf);\n+\t\tstrbuf_addchars(&holder, '-', len - strlen(buf->buf) - 2);\n+\t\tstrbuf_addchars(&holder, '>', 1);\n+\t\tstrbuf_addchars(&holder, ' ', 1);\n+\t\tstrbuf_addstr(&holder, buf->buf);\n+\t\tstrbuf_reset(buf);\n+\t\tstrbuf_addstr(buf, holder.buf);\n+\t}\n+}\n+\n+static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct wt_status *s, int hide_pipe)\n {\n \tprintf(_(\"|\"));\n \tcolor_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), \"%s\", buf1->buf);\n-\tprintf(_(\"|\"));\n+\tif (hide_pipe != 1 && hide_pipe != 3)\n+\t\tprintf(_(\"|\"));\n \tcolor_fprintf(s->fp, color(WT_STATUS_CHANGED, s), \"%s\", buf2->buf);\n-\tprintf(_(\"|\"));\n+\tif (hide_pipe != 2 && hide_pipe != 3)\n+\t\tprintf(_(\"|\"));\n \tcolor_fprintf(s->fp, color(WT_STATUS_UPDATED, s), \"%s\", buf3->buf);\n \tprintf(_(\"|\\n\"));\n }\n \n-void build_and_draw_status_table(struct wt_status *s, int add_advice)\n+static void print_table_body_line_(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct wt_status *s)\n+{\n+\tprint_table_body_line(buf1, buf2, buf3, s, 0);\n+}\n+\n+void build_and_draw_status_table(struct wt_status *s, int advice)\n {\n \tstruct winsize w;\n \tint cols;\n@@ -84,7 +119,7 @@ void build_and_draw_status_table(struct wt_status *s, int add_advice)\n \tstruct strbuf table_col_entry_1 = STRBUF_INIT;\n \tstruct strbuf table_col_entry_2 = STRBUF_INIT;\n \tstruct strbuf table_col_entry_3 = STRBUF_INIT;\n-\tstruct string_list_item *item;\n+\tstruct string_list_item *item, *item2;\n \n \t/* Get terminal width */\n \tioctl(STDOUT_FILENO, TIOCGWINSZ, &w);\n@@ -104,7 +139,7 @@ void build_and_draw_status_table(struct wt_status *s, int add_advice)\n \tprintf(_(\"%s\\n\"), table_border.buf);\n \tprintf(_(\"|%s|%s|%s|\\n\"), table_col_entry_1.buf, table_col_entry_2.buf, table_col_entry_3.buf);\n \n-\tif (add_advice) {\n+\tif (advice) {\n \t\tbuild_table_entry(&table_col_entry_1, \"(stage: git add <file>)\", cols);\n \t\tbuild_table_entry(&table_col_entry_2, \"(stage: git add <file>)\", cols);\n \t\tbuild_table_entry(&table_col_entry_3, \"(unstage: git restore --staged <file>)\", cols);\n@@ -122,14 +157,38 @@ void build_and_draw_status_table(struct wt_status *s, int add_advice)\n \n \t/* Draw table body */\n \tfor_each_string_list_item(item, &s->untracked) {\n-\t\tbuild_table_entry(&table_col_entry_1, item->string, cols);\n+\t\tstruct strbuf buf_1 = STRBUF_INIT;\n+\t\tstruct strbuf buf_2 = STRBUF_INIT;\n+\t\tint is_arrow = 0;\n+\t\tstrbuf_addstr(&buf_1, item->string);\n+\t\tbuild_table_entry(&table_col_entry_1, buf_1.buf, cols);\n \t\tbuild_table_entry(&table_col_entry_2, \"\", cols);\n \t\tbuild_table_entry(&table_col_entry_3, \"\", cols);\n-\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\n+\t\tfor_each_string_list_item(item2, &s->dry_run_added) {\n+\t\t\tstrbuf_reset(&buf_2);\n+\t\t\tstrbuf_addstr(&buf_2, item2->string);\n+\t\t\tif (!strbuf_cmp(&buf_1, &buf_2)) {\n+\t\t\t\tbuild_table_entry(&table_col_entry_3, buf_1.buf, cols);\n+\t\t\t\tadd_arrow_to_entry(&table_col_entry_1, 1);\n+\t\t\t\tadd_arrow_to_entry(&table_col_entry_2, 0);\n+\t\t\t\tadd_arrow_to_entry(&table_col_entry_3, 0);\n+\t\t\t\tis_arrow = 1;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (!is_arrow)\n+\t\t\tprint_table_body_line_(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\t\telse\n+\t\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s, 3);\n \t}\n \n \tfor_each_string_list_item(item, &s->change) {\n \t\tstruct wt_status_change_data *d = item->util;\n+\t\tstruct strbuf buf_1 = STRBUF_INIT;\n+\t\tstruct strbuf buf_2 = STRBUF_INIT;\n+\t\tint is_arrow = 0;\n+\t\tstrbuf_addstr(&buf_1, item->string);\n \t\tif (d->worktree_status && d->index_status) {\n \t\t\tbuild_table_entry(&table_col_entry_1, \"\", cols);\n \t\t\tbuild_table_entry(&table_col_entry_2, item->string, cols);\n@@ -138,12 +197,27 @@ void build_and_draw_status_table(struct wt_status *s, int add_advice)\n \t\t\tbuild_table_entry(&table_col_entry_1, \"\", cols);\n \t\t\tbuild_table_entry(&table_col_entry_2, item->string, cols);\n \t\t\tbuild_table_entry(&table_col_entry_3, \"\", cols);\n+\n+\t\t\tfor_each_string_list_item(item2, &s->dry_run_added) {\n+\t\t\t\tstrbuf_reset(&buf_2);\n+\t\t\t\tstrbuf_addstr(&buf_2, item2->string);\n+\t\t\t\tif (!strbuf_cmp(&buf_1, &buf_2)) {\n+\t\t\t\t\tbuild_table_entry(&table_col_entry_3, buf_1.buf, cols);\n+\t\t\t\t\tadd_arrow_to_entry(&table_col_entry_2, 1);\n+\t\t\t\t\tadd_arrow_to_entry(&table_col_entry_3, 0);\n+\t\t\t\t\tis_arrow = 1;\n+\t\t\t\t}\n+\t\t\t}\n \t\t} else if (d->index_status) {\n \t\t\tbuild_table_entry(&table_col_entry_1, \"\", cols);\n \t\t\tbuild_table_entry(&table_col_entry_2, \"\", cols);\n \t\t\tbuild_table_entry(&table_col_entry_3, item->string, cols);\n \t\t}\n-\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\n+\t\tif (!is_arrow)\n+\t\t\tprint_table_body_line_(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\t\telse\n+\t\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s, 2);\n \t}\n \t\n \tif (!s->untracked.nr && !s->change.nr) {\ndiff --git a/wt-status.c b/wt-status.c\nindex 62731859fe..975cfc01a5 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -153,6 +153,7 @@ void wt_status_prepare(struct repository *r, struct wt_status *s)\n \ts->change.strdup_strings = 1;\n \ts->untracked.strdup_strings = 1;\n \ts->ignored.strdup_strings = 1;\n+\ts->dry_run_added.strdup_strings = 1;\n \ts->show_branch = -1;  /* unspecified */\n \ts->show_stash = 0;\n \ts->ahead_behind_flags = AHEAD_BEHIND_UNSPECIFIED;\ndiff --git a/wt-status.h b/wt-status.h\nindex 70a3b7a2e4..5d29c058c1 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -142,6 +142,7 @@ struct wt_status {\n \tstruct string_list change;\n \tstruct string_list untracked;\n \tstruct string_list ignored;\n+\tstruct string_list dry_run_added;\n \tuint32_t untracked_in_ms;\n };\n \n-- \n2.42.0.402.gbe8243af7b.dirty\n\n"},{"id":"483591","messageId":"fd26df85661d554ced9d8e0445f75952@manjaro.org","threadId":"60408","inReplyTo":"20231020183947.463882-1-jacob@initialcommit.io","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-20T18:48:12Z","receivedAt":"2023-10-20T18:48:16Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-20 20:39, Jacob Stopak wrote:\n> This is a proposal / proof-of-concept for a new table-based output\n> format for the git status command, and for dry runs (-n) of the git add\n> command. This could be extended to create visual dry runs for other\n> other commands like rm, mv, restore, stash, commit, and clean.\n\nHuh, please don't get me wrong, but based on the examples provided \nbelow, I really think that's only wasted screen estate, providing little \nor no help in understanding the performed operations.\n\nI appreciate your effort, but IMHO it makes little sense from the \nusability standpoint.\n\n> For some context, earlier this year I released a tool called Git-Sim\n> (https://github.com/initialcommit-com/git-sim) which allows users to do\n> visual dry runs of many Git commands, which are rendered as high \n> quality\n> output image files. Simulating commands like status, add, rm, mv, \n> restore,\n> stash, and commit creates a table with 3 columns to represent the way \n> file\n> changes \"move around\" as a result of the command being simulated.\n> \n> I've gotten positive feedback from users about this visual approach to\n> simulating git commands, which is more intuitive than pure terminal \n> text\n> for both newer users to understand how git works and for visual people.\n> \n> As a result, I was thinking of ways to integrate these types of visual\n> formats directly into Git. A table-based output format with colored\n> highlighting for the commands mentioned above is low hanging fruit.\n> \n> Teach 'git status' the new -t, --table flag, which displays the status\n> output in a 3-column table format, preserving terminology and color\n> coding from the default git status \"long output\" format (note that the\n> column headers are shortened here for the small width of this email, \n> and\n> also I just realized that the tables below might not look right on the\n> mailing list due to the differing character width, but it looks correct\n> in the terminal so please test there it's more fun anyway :D):\n> \n> $ git status -t\n> -------------------------------------------------------------------------\n> |    Untracked files    | Changes n...or commit | Changes t...committed \n> |\n> -------------------------------------------------------------------------\n> |         poiu          |                       |                       \n> |\n> |     status-table/     |                       |                       \n> |\n> |                       |                       |         asdf          \n> |\n> |                       |        table.c        |                       \n> |\n> |                       |      wt-status.c      |                       \n> |\n> -------------------------------------------------------------------------\n> \n> Teach 'git add' the new -t, --table flag to be used ONLY in combination\n> with the '-n' flag for dry runs. Instead of simply printing out the\n> added filenames, the full status table format is displayed, along with\n> arrows that visually show how the added files are being moved around:\n> \n> $ git add -nt poiu wt-status.c\n> -------------------------------------------------------------------------\n> |    Untracked files    | Changes n...or commit | Changes t...committed \n> |\n> -------------------------------------------------------------------------\n> |         poiu -----------------------------------------> poiu          \n> |\n> |     status-table/     |                       |                       \n> |\n> |                       |                       |         asdf          \n> |\n> |                       |        table.c        |                       \n> |\n> |                       |      wt-status.c ----------> wt-status.c      \n> |\n> -------------------------------------------------------------------------\n> \n> Other notes:\n> \n> * The width of the table and columns are dynamically set based on the\n>   width of the terminal.\n> \n> * Long paths are shortened to include the maximum number of characters\n>   from both ends of the path that will fit, with a '...' in the middle.\n> \n> * Color coding matches the default output of 'git status', with\n>   untracked files and working dir mods in red, and staged changes in\n>   green. If needed, arrows are drawn in cyan.\n> \n> As stated above, the dry run version of the table format can be applied\n> to various other commands like rm, mv, restore, stash, commit, and \n> clean\n> which all move file changes around in a way that can be represented in\n> the table format. New columns may need to be added or arrows reversed\n> to show changes moving in various directions. Note that some of these\n> commands don't appear to have a dry run (-n) option yet, so it could be\n> added for consistency (if not already in use) and for use with the new\n> table format.\n> \n> Since this is an RFC patch series, I probably did some illegal and dumb\n> things in my code changes just to get it into a demo-able state. I am a\n> bit wary of having made changes to files like \"read-cache.c\" and\n> \"read-cache-ll.h\" to pass in the wt_status info, and there are probably\n> betters ways to do some other things too.\n> \n> Feedback on both the new format itself and the implementation is very\n> much appreciated!\n> \n> Jacob Stopak (5):\n>   status: introduce -t, --table flag\n>   status: handle long paths with -t, --table flag\n>   status: add advice arg for -t, --table flag\n>   add: add -t, --table flag for visual dry runs\n>   add: set unique color for -t, --table arrows\n> \n>  Makefile         |   1 +\n>  builtin/add.c    |  46 +++++++--\n>  builtin/commit.c |   4 +-\n>  read-cache-ll.h  |   9 +-\n>  read-cache.c     |  32 ++++++-\n>  table.c          | 245 +++++++++++++++++++++++++++++++++++++++++++++++\n>  table.h          |   6 ++\n>  wt-status.c      |  74 +++++++++-----\n>  wt-status.h      |   3 +\n>  9 files changed, 378 insertions(+), 42 deletions(-)\n>  create mode 100644 table.c\n>  create mode 100644 table.h\n"},{"id":"483605","messageId":"ZTL1wJIIK/5YWQK5.jacob@initialcommit.io","threadId":"60408","inReplyTo":"fd26df85661d554ced9d8e0445f75952@manjaro.org","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-20T21:48:48Z","receivedAt":"2023-10-20T21:48:58Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"On Fri, Oct 20, 2023 at 08:48:12PM +0200, Dragan Simic wrote:\n> On 2023-10-20 20:39, Jacob Stopak wrote:\n> > This is a proposal / proof-of-concept for a new table-based output\n> > format for the git status command, and for dry runs (-n) of the git add\n> > command. This could be extended to create visual dry runs for other\n> > other commands like rm, mv, restore, stash, commit, and clean.\n> \n> Huh, please don't get me wrong, but based on the examples provided below, I\n> really think that's only wasted screen estate, providing little or no help\n> in understanding the performed operations.\n> \n> I appreciate your effort, but IMHO it makes little sense from the usability\n> standpoint.\n> \n\nThanks for the quick (and honest ;) reply - I appreciate it and no offense\ntaken! But let me try to expand on my reasoning a bit.\n\nI agree with you that Git users who are already comfortable with Git,\nthe command-line, and their workflows would be unlikely to use this in\ntheir day to day work.\n\nThe main benefits of this format are for beginners and folks who\nare still learning Git to use it as needed:\n\n  * To beginners, the concepts of working directory and \"staging area\"\n    can be very abstract. By representing these concepts as table columns\n    on the screen, (a format that 99% of humans are used to interpreting),\n    they become more tangible and intuitive to new users.\n\n  * In Git, changes fly around all over the place, in all sorts of\n    directions. Even small hints at this movement can be very helpful to\n    understand what the heck is going on. The table format (esp with\n    arrows used in the 'git add' version) highlights the \"flow\" of\n    changes through the workflow in a way that the current default format\n    doesn't. The current dry runs just show the filenames being added\n    without context of _where_ they come from and where they are going.\n    Not to mention many commands don't even have dry runs. This might\n    sound like a small thing, but to a newbie having that extra level of\n    confirmation and understanding can make a big difference.\n\n  * Git doesn't exactly have a reputation as a user-friendly tool, and\n    much of that stems from the difficulty of learning Git. So we should\n    try to make it more approachable to normal humans. This format\n    (esp if applied to a wide variety of commands as dry runs) would\n    provide a rudimentary visual output that is more intuitive to users.\n\n  * This flag doesn't change any default behavior, it can easily be\n    tossed on for newbie use (either when teaching a newbie or when the\n    newbie is practicing on their own). Given this usage, the screen\n    realestate is not really a concern. I.e. this would be used\n    specifically when needed for the extra info/clarity it provides,\n    not to be efficient with the terminal space.\n\nThat's my perspective anyway, but of course the point of this is to\npropose it to the community and hear the response, so even if it's\nnot included it's still a good experience :D.\n"},{"id":"483608","messageId":"d3bbe53c3b910f891c80465ea0c3f53f@manjaro.org","threadId":"60408","inReplyTo":"ZTL1wJIIK/5YWQK5.jacob@initialcommit.io","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-20T23:02:46Z","receivedAt":"2023-10-20T23:02:57Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-20 23:48, Jacob Stopak wrote:\n> On Fri, Oct 20, 2023 at 08:48:12PM +0200, Dragan Simic wrote:\n>> On 2023-10-20 20:39, Jacob Stopak wrote:\n>> > This is a proposal / proof-of-concept for a new table-based output\n>> > format for the git status command, and for dry runs (-n) of the git add\n>> > command. This could be extended to create visual dry runs for other\n>> > other commands like rm, mv, restore, stash, commit, and clean.\n>> \n>> Huh, please don't get me wrong, but based on the examples provided \n>> below, I\n>> really think that's only wasted screen estate, providing little or no \n>> help\n>> in understanding the performed operations.\n>> \n>> I appreciate your effort, but IMHO it makes little sense from the \n>> usability\n>> standpoint.\n>> \n> Thanks for the quick (and honest ;) reply - I appreciate it and no \n> offense\n> taken! But let me try to expand on my reasoning a bit.\n\nThank you!\n\n> I agree with you that Git users who are already comfortable with Git,\n> the command-line, and their workflows would be unlikely to use this in\n> their day to day work.\n> \n> The main benefits of this format are for beginners and folks who\n> are still learning Git to use it as needed:\n\nOh, I always do my best to put myself in the shoes of the targeted \naudience.  Maybe I sometimes fail at that, I don't know, but that's why \nwe're here to discuss it further.\n\n>   * To beginners, the concepts of working directory and \"staging area\"\n>     can be very abstract. By representing these concepts as table \n> columns\n>     on the screen, (a format that 99% of humans are used to \n> interpreting),\n>     they become more tangible and intuitive to new users.\n\nFrankly, based on my rather broad experience, there are two primary \ncategories of the beginners in the world of version control software \n(VCS), be it git or any other product:\n\n1) People who are forced to use some VCS at work, and they actually \ndon't give a damn about it.\n2) True enthusiasts who love what they do, and who love expanding their \nknowledge.\n\nFor the first category, nothing helps.  For the second category, a \nnicely written tutorial is all they needed to start with, aided later \nwith the man pages, Stack Exchange, and perhaps some textbook.\n\n>   * In Git, changes fly around all over the place, in all sorts of\n>     directions. Even small hints at this movement can be very helpful \n> to\n>     understand what the heck is going on. The table format (esp with\n>     arrows used in the 'git add' version) highlights the \"flow\" of\n>     changes through the workflow in a way that the current default \n> format\n>     doesn't. The current dry runs just show the filenames being added\n>     without context of _where_ they come from and where they are going.\n>     Not to mention many commands don't even have dry runs. This might\n>     sound like a small thing, but to a newbie having that extra level \n> of\n>     confirmation and understanding can make a big difference.\n\nPlease don't get me wrong, I understand your reasoning, but again, it \nall comes down to the two categories described above.  IMHO, the second \ncategory will likely start turning off the default hints sooner than \nturning the table formatting on.  The first category will choose some \nGUI anyway.\n\n>   * Git doesn't exactly have a reputation as a user-friendly tool, and\n>     much of that stems from the difficulty of learning Git. So we \n> should\n>     try to make it more approachable to normal humans. This format\n>     (esp if applied to a wide variety of commands as dry runs) would\n>     provide a rudimentary visual output that is more intuitive to \n> users.\n\nNo pain, no gain.  That's the ancient mantra, but IMHO it still applies \nvery well to many things, and of course not to the first category \nmentioned above.  Nothing applies to that category.\n\n>   * This flag doesn't change any default behavior, it can easily be\n>     tossed on for newbie use (either when teaching a newbie or when the\n>     newbie is practicing on their own). Given this usage, the screen\n>     realestate is not really a concern. I.e. this would be used\n>     specifically when needed for the extra info/clarity it provides,\n>     not to be efficient with the terminal space.\n\nAs I already assumed above, the targeted audience will likely start \nturning the default hints off, rather than turning the table formatting \non.  Maybe I'm wrong there, who knows.\n\n> That's my perspective anyway, but of course the point of this is to\n> propose it to the community and hear the response, so even if it's\n> not included it's still a good experience :D.\n\nLet's hear more thoughts from other people, of course.\n"},{"id":"483610","messageId":"xmqqwmvgoovg.fsf@gitster.g","threadId":"60408","inReplyTo":"d3bbe53c3b910f891c80465ea0c3f53f@manjaro.org","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-10-20T23:28:19Z","receivedAt":"2023-10-20T23:28:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Dragan Simic <dsimic@manjaro.org> writes:\n\n> Please don't get me wrong, I understand your reasoning, but again, it\n> all comes down to the two categories described above.  IMHO, the\n> second category will likely start turning off the default hints sooner\n> than turning the table formatting on.  The first category will choose\n> some GUI anyway.\n\nYou are not alone in feeling the impedance mismatch between the\nintended audience the patch(es) try to help (pointy-clicky GUI\nusers) and the codebase the patch(es) modify (perhaps spartan\ncommand line interface).  I did wonder why this is not made as a\npart of sugarcoating the command line interface with some GUI that\nshows what could be added, what has been added, and the stuff in its\n\"git status\" equivalent.\n\n"},{"id":"483628","messageId":"ZTS4jfsU6oS6LW0u.jacob@initialcommit.io","threadId":"60408","inReplyTo":"d3bbe53c3b910f891c80465ea0c3f53f@manjaro.org","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-22T05:52:13Z","receivedAt":"2023-10-22T05:52:20Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"> Frankly, based on my rather broad experience, there are two primary\n> categories of the beginners in the world of version control software (VCS),\n> be it git or any other product:\n> \n> 1) People who are forced to use some VCS at work, and they actually don't\n> give a damn about it.\n> 2) True enthusiasts who love what they do, and who love expanding their\n> knowledge.\n>\n> For the first category, nothing helps.  \n\nInteresting categorization I didn't think of splitting users that way. I\nguess for group 1 that's true, if they are shown a GUI and can run 3\ncommands that can do what they need, that's all they will ever use.\n\n> For the second category, a nicely\n> written tutorial is all they needed to start with, aided later with the man\n> pages, Stack Exchange, and perhaps some textbook.\n\nThis is the exact way I learned Git and became comfortable and eventually\nconfident using it. Reflecting on that, I really only started to become\ntruly confident after understanding the core underlying concepts (maybe\nthis is obvious / true for anything). And it's always easy once you get it.\n\nHowever, there is one main benefit of a feature like this, that none of\nthe other options (man pages, stack exchange, a textbook) can provide:\n\nSince the tool (Git) has access to and knows the exact state of your local\nenvironment, it can provide instant feedback that takes into account that\ncontext. That is immeasurably more helpful than trying to figure out how\nto best ask Google your question, and then piecing together your problem\nwith a similar one some lost soul ran into 10 years ago.\n\n> Please don't get me wrong, I understand your reasoning, but again, it all\n> comes down to the two categories described above.  IMHO, the second category\n> will likely start turning off the default hints sooner than turning the\n> table formatting on.  The first category will choose some GUI anyway.\n\nThe default hints are an intersting consideration. I've found them handy\nfor commands that I use infrequently, and also when I find myself in a\nscenario that is not a part of my usual workflow.\n\nAnd the hint feature does show that Git has some \"helper\" features to\nhold the user's hand at least a little bit.\n\n> No pain, no gain.  That's the ancient mantra, but IMHO it still applies very\n> well to many things, and of course not to the first category mentioned\n> above.  Nothing applies to that category.\n\nSomehow I do feel some sense of satisfaction at the countless times I've\nI've been stuck on some menial issue only to find out it had a stupid\nsolution I overlooked. It's also just kind of funny in hindsight.\n\nRegardless of a table formatting feature in Git, there is still plenty of\nsweet sweet pain to be had with software dev in general.\n\nBut in the moment I always do appreciate being able to get past a\nroadblock as quickly as possible. I would want a tool I design to be\nknown for avoiding pain rather than causing it.\n\n> As I already assumed above, the targeted audience will likely start turning\n> the default hints off, rather than turning the table formatting on.  Maybe\n> I'm wrong there, who knows.\n\nEven if the hints are off (presumably because the user felt confident\nenough or annoyed enough to turn them off), sometimes people just run\ninto a situation they need an extra bit of clarification on.\n"},{"id":"483629","messageId":"ZTS7YsxSE8UA+n4G.jacob@initialcommit.io","threadId":"60408","inReplyTo":"xmqqwmvgoovg.fsf@gitster.g","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-22T06:04:18Z","receivedAt":"2023-10-22T06:04:24Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"On Fri, Oct 20, 2023 at 04:28:19PM -0700, Junio C Hamano wrote:\n> \n> You are not alone in feeling the impedance mismatch between the\n> intended audience the patch(es) try to help (pointy-clicky GUI\n> users) \n\nI'm sure there's overlap with \"pointy-clicky GUI users\" but my point\nisn't to directly cater to them. I find it intersting to think about\nhow visual (and ok fine even gui) tools can be used as bridge tools\nthat can be discarded one the important concepts are solidified, and\nmaybe resurrected in a moment of stupidity or strife.\n\nIt's like yes use the crutch if you need it, but then do it the real\nway once you get it.\n\nAnd altho this is a visual helper feature, it keeps the user within the\nterminal, close to the Git cli and may help some subset stay there.\n\n> and the codebase the patch(es) modify (perhaps spartan\n> command line interface).\n\nGit does have some comforting features doesn't it? For example the\nhints, as well as the nice pretty colored --graph option for log. I'm\nsure I'm missing some others? Isn't there a file called pretty.c?! :D\n\n> I did wonder why this is not made as a\n> part of sugarcoating the command line interface with some GUI that\n> shows what could be added, what has been added, and the stuff in its\n> \"git status\" equivalent.\n\nI'm working on a couple of tools (ok fine basically guis) that include\nthis feature.\n\nThe reason to bring it up here is that a common feedback I got on my\nexisting tool was \"add something like this into git\", so I was curious\nabout what was possible to do from the Git cli.\n\nThis would obviously be the best way for the feature to reach the widest\npossible audience and get the most use. Any standalone tool I create\nwould get a teensy fraction of the use that a feature in Git itself would\nget, so I figured I'd give it a whirl.\n"},{"id":"483630","messageId":"5fac8607a3c270e06fd610551d7403c7@manjaro.org","threadId":"60408","inReplyTo":"ZTS4jfsU6oS6LW0u.jacob@initialcommit.io","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-22T06:38:19Z","receivedAt":"2023-10-22T06:38:24Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-22 07:52, Jacob Stopak wrote:\n>> Frankly, based on my rather broad experience, there are two primary\n>> categories of the beginners in the world of version control software \n>> (VCS),\n>> be it git or any other product:\n>> \n>> 1) People who are forced to use some VCS at work, and they actually \n>> don't\n>> give a damn about it.\n>> 2) True enthusiasts who love what they do, and who love expanding \n>> their\n>> knowledge.\n>> \n>> For the first category, nothing helps.\n> \n> Interesting categorization I didn't think of splitting users that way. \n> I\n> guess for group 1 that's true, if they are shown a GUI and can run 3\n> commands that can do what they need, that's all they will ever use.\n\nCoincidentally, yesterday I was demonstrated for the first time in my \nlife the way VS Code works with git, by a member of the first category \nof users.  It's dumbed down to the extreme, hiding pretty much all the \ndetails and git specifics, which is exactly what the first category \nwants.  Now I understand why the VS Code is so much popular.\n\nThe second category, myself included, tends to be genuinely disgusted by \nsuch an approach that makes everything nearly sterile.  But I do \nunderstand why many users simply love it that way.  Maybe I digress.\n\n>> For the second category, a nicely\n>> written tutorial is all they needed to start with, aided later with \n>> the man\n>> pages, Stack Exchange, and perhaps some textbook.\n> \n> This is the exact way I learned Git and became comfortable and \n> eventually\n> confident using it. Reflecting on that, I really only started to become\n> truly confident after understanding the core underlying concepts (maybe\n> this is obvious / true for anything). And it's always easy once you get \n> it.\n\nI agree, understanding the internals of some project or product, with \nmany or all of its outer layers peeled back, is often the only way to \nreally get to know it.\n\n> However, there is one main benefit of a feature like this, that none of\n> the other options (man pages, stack exchange, a textbook) can provide:\n> \n> Since the tool (Git) has access to and knows the exact state of your \n> local\n> environment, it can provide instant feedback that takes into account \n> that\n> context. That is immeasurably more helpful than trying to figure out \n> how\n> to best ask Google your question, and then piecing together your \n> problem\n> with a similar one some lost soul ran into 10 years ago.\n\nTrue, but I still think that having git put its thoughts into tables is \nactually not helpful.  To be precise, it actually might be helpful, but \nonly to the first category of users, who will never reach it.  I mean, \nnever say never, but in this case I'm pretty sure it's safe to say it.  \nUnfortunately.\n\n>> Please don't get me wrong, I understand your reasoning, but again, it \n>> all\n>> comes down to the two categories described above.  IMHO, the second \n>> category\n>> will likely start turning off the default hints sooner than turning \n>> the\n>> table formatting on.  The first category will choose some GUI anyway.\n> \n> The default hints are an intersting consideration. I've found them \n> handy\n> for commands that I use infrequently, and also when I find myself in a\n> scenario that is not a part of my usual workflow.\n\nThe built-in hints are useful without doubt, and in fact I still have at \nleast a dozen of them left enabled.\n\n> And the hint feature does show that Git has some \"helper\" features to\n> hold the user's hand at least a little bit.\n\nIMHO, git strikes a very good balance between holding the user's hand \nand leaving them on their own.  For the second category of users, of \ncourse.\n\n>> No pain, no gain.  That's the ancient mantra, but IMHO it still \n>> applies very\n>> well to many things, and of course not to the first category mentioned\n>> above.  Nothing applies to that category.\n> \n> Somehow I do feel some sense of satisfaction at the countless times \n> I've\n> I've been stuck on some menial issue only to find out it had a stupid\n> solution I overlooked. It's also just kind of funny in hindsight.\n> \n> Regardless of a table formatting feature in Git, there is still plenty \n> of\n> sweet sweet pain to be had with software dev in general.\n> \n> But in the moment I always do appreciate being able to get past a\n> roadblock as quickly as possible. I would want a tool I design to be\n> known for avoiding pain rather than causing it.\n\nI agree, software in general shouldn't cause people pain, it should make \npeople's lives better.  However, many people expect software these days \nto be some kind of pain killer, which it simply can't be unless dumbed \ndown to the extreme.  If you ask any doctor what results from taking \npain killers for an extended period of time, they'll answer you that \nstronger pain killers will usually become needed.\n"},{"id":"483631","messageId":"d6c06aec7313b8078382e16065d04282@manjaro.org","threadId":"60408","inReplyTo":"ZTS7YsxSE8UA+n4G.jacob@initialcommit.io","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-22T06:52:36Z","receivedAt":"2023-10-22T06:52:40Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-22 08:04, Jacob Stopak wrote:\n> On Fri, Oct 20, 2023 at 04:28:19PM -0700, Junio C Hamano wrote:\n>> \n>> You are not alone in feeling the impedance mismatch between the\n>> intended audience the patch(es) try to help (pointy-clicky GUI\n>> users)\n> \n> I'm sure there's overlap with \"pointy-clicky GUI users\" but my point\n> isn't to directly cater to them. I find it intersting to think about\n> how visual (and ok fine even gui) tools can be used as bridge tools\n> that can be discarded one the important concepts are solidified, and\n> maybe resurrected in a moment of stupidity or strife.\n> \n> It's like yes use the crutch if you need it, but then do it the real\n> way once you get it.\n\nQuite frankly, that would be like starting to learn how to drive a car \nby playing GTA 5, or whichever version of GTA it currently popular.  I \ndon't think that would work out well for the vast majority of student \ndrivers.\n\n> And altho this is a visual helper feature, it keeps the user within the\n> terminal, close to the Git cli and may help some subset stay there.\n\nEven if that would work out for some people, it would require the \nformatting into tables to be the default for git, which frankly I'd \nnever support.\n"},{"id":"483633","messageId":"ZTT5uI5Hm1+n0Agx@ugly","threadId":"60408","inReplyTo":"5fac8607a3c270e06fd610551d7403c7@manjaro.org","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Oswald Buddenhagen","fromEmail":"oswald.buddenhagen@gmx.de","sentAt":"2023-10-22T10:30:16Z","receivedAt":"2023-10-22T10:30:20Z","isPatch":true,"sender":{"key":"oswald.buddenhagen@gmx.de","avatar":"https://avatars.githubusercontent.com/u/812380?v=4"},"body":"On Sun, Oct 22, 2023 at 08:38:19AM +0200, Dragan Simic wrote:\n>True, but I still think that having git put its thoughts into tables is \n>actually not helpful.\n>\ni'm not convinced that the proposed feature specifically would have \nhelped me, either (i found the index a rather obvious concept once i \nknew that it's there), but i'm making a general argument here. so:\n\n>To be precise, it actually might be helpful, but only to the first \n>category of users, who will never reach it.  I mean, never say never, \n>but in this case I'm pretty sure it's safe to say it.  \n>\nwell, and i think that you're wrong about that.\nyour categorization is simply wrong, because it assumes an incorrect \nstatic model.\n\nwhile for the last decade i've been as much of a git expert as one can \nreasonably be without being literally obsessed with it or having written \nmuch of it, i absolutely *did* start out in your first category (as in, \nit was forced upon me, while i couldn't have cared less about the \nspecifics - p4 was working well enough (or so i thought)). and i hated \nthis stupid git (it was 2009, and it was much more of a pita for noobs \nthan it is now). i certainly could have used more sensible \nvisualizations at every step - on the command line, because that's where \ni mostly \"live\".\n\nthe second major error in the thinking is that \"expert\" and \"gui user\" \nare mutually exclusive categories. while i do most things on the command \nline, i would never voluntarily use \"add -p\" - why should i inflict that \npain upon me, when i can simply use git-gui to do the job in a much more \nvisual and freely navigable way? the same goes for \"log --graph\" vs.  \ngitk, and git's \"blame\" function vs. qt creator's (or git-gui's, but i \ndon't use it for that).\n\nregards\n"},{"id":"483634","messageId":"58a6a25a7b2eb82c21d9b87143033cef@manjaro.org","threadId":"60408","inReplyTo":"ZTT5uI5Hm1+n0Agx@ugly","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-22T12:55:05Z","receivedAt":"2023-10-22T12:55:09Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-22 12:30, Oswald Buddenhagen wrote:\n> On Sun, Oct 22, 2023 at 08:38:19AM +0200, Dragan Simic wrote:\n>> True, but I still think that having git put its thoughts into tables\n>> is actually not helpful.\n> \n> i'm not convinced that the proposed feature specifically would have\n> helped me, either (i found the index a rather obvious concept once i\n> knew that it's there), but i'm making a general argument here. so:\n> \n>> To be precise, it actually might be helpful, but only to the first\n>> category of users, who will never reach it.  I mean, never say never,\n>> but in this case I'm pretty sure it's safe to say it.\n> \n> well, and i think that you're wrong about that.\n> your categorization is simply wrong, because it assumes an incorrect\n> static model.\n> \n> while for the last decade i've been as much of a git expert as one can\n> reasonably be without being literally obsessed with it or having\n> written much of it, i absolutely *did* start out in your first\n> category (as in, it was forced upon me, while i couldn't have cared\n> less about the specifics - p4 was working well enough (or so i\n> thought)). and i hated this stupid git (it was 2009, and it was much\n> more of a pita for noobs than it is now). i certainly could have used\n> more sensible visualizations at every step - on the command line,\n> because that's where i mostly \"live\".\n\nOh, that's awesome and I'm really happy to be wrong with my broad \nclassification of VCS users.  However, I still need to be convinced \nfurther, and I'd assign your example as an exception to the rules, \nespecially because you migrated to git from another VCS, which you \nliked, and because you use the command line a lot.\n\nFull disclosure, I used Subversion for many years and I loved it.  I \nknew it very well and it did all I needed for me and the team I worked \nwith.  Then git came and I really didn't like it, because it was touted \nto be \"the best thing ever\".  After using git for a while, I can firmly \nsay that git is awesome, but that it also is a total overkill for many \nprojects that need a VCS, for which choosing Subversion would be a much \nbatter choice.  Why, you'll ask?  Because Subversion is many times \nsimpler, and because many projects actually don't need a distributed \nVCS.\n\n> the second major error in the thinking is that \"expert\" and \"gui user\"\n> are mutually exclusive categories. while i do most things on the\n> command line, i would never voluntarily use \"add -p\" - why should i\n> inflict that pain upon me, when i can simply use git-gui to do the job\n> in a much more visual and freely navigable way? the same goes for \"log\n> --graph\" vs.  gitk, and git's \"blame\" function vs. qt creator's (or\n> git-gui's, but i don't use it for that).\n\nI also ask myself why would I use git-gui or any other GUI utility?  To \nme, clicking on something that represents a file is often simply wrong.  \nThough, I understand that many people prefer GUI utilities and I respect \nthat, everyone is free to do anything, but I also expect others to \nrespect my own preferences.\n"},{"id":"483635","messageId":"ZTVEv3SMhhjeA6z5.jacob@initialcommit.io","threadId":"60408","inReplyTo":"ZTT5uI5Hm1+n0Agx@ugly","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-22T15:50:23Z","receivedAt":"2023-10-22T15:50:29Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"On Sun, Oct 22, 2023 at 12:30:16PM +0200, Oswald Buddenhagen wrote:\n> On Sun, Oct 22, 2023 at 08:38:19AM +0200, Dragan Simic wrote:\n> > True, but I still think that having git put its thoughts into tables is\n> > actually not helpful.\n> > \n> i'm not convinced that the proposed feature specifically would have helped\n> me, either (i found the index a rather obvious concept once i knew that it's\n> there), but i'm making a general argument here. so:\n> \n\nThank you for the input! One point I'd like to add is that although the\ncurrent proposal patch series only implements the table option for the\nstatus and add commands, it could be applied to many others as dry runs,\nas mentioned in my cover letter, some of which touch on concepts besides\nthe index. Examples would be git stash and git clean. It can take a while\nbefore all these commands feel natural, which was my hope for having this\noptional helper when the user simply could use a bit more clarity.\n\n> > To be precise, it actually might be helpful, but only to the first\n> > category of users, who will never reach it.  I mean, never say never,\n> > but in this case I'm pretty sure it's safe to say it.\n> > \n> well, and i think that you're wrong about that.\n> your categorization is simply wrong, because it assumes an incorrect static\n> model.\n> \n> while for the last decade i've been as much of a git expert as one can\n> reasonably be without being literally obsessed with it or having written\n> much of it, i absolutely *did* start out in your first category (as in, it\n> was forced upon me, while i couldn't have cared less about the specifics -\n> p4 was working well enough (or so i thought)). and i hated this stupid git\n> (it was 2009, and it was much more of a pita for noobs than it is now). i\n> certainly could have used more sensible visualizations at every step - on\n> the command line, because that's where i mostly \"live\".\n> \n\n:D. I feel similar and as mentioned in a reply above, the main benefit\nto getting direct feedback on in the cli is that it can provide guidance\nbased on the exact context the user is in, instead of an external\nresource which can in most cases only suggest a more general or\ntangential guidance / solution.\n"},{"id":"483661","messageId":"ZTZQZhtTxvGT/s81@ugly","threadId":"60408","inReplyTo":"58a6a25a7b2eb82c21d9b87143033cef@manjaro.org","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Oswald Buddenhagen","fromEmail":"oswald.buddenhagen@gmx.de","sentAt":"2023-10-23T10:52:22Z","receivedAt":"2023-10-23T10:52:28Z","isPatch":true,"sender":{"key":"oswald.buddenhagen@gmx.de","avatar":"https://avatars.githubusercontent.com/u/812380?v=4"},"body":"On Sun, Oct 22, 2023 at 02:55:05PM +0200, Dragan Simic wrote:\n>Oh, that's awesome and I'm really happy to be wrong with my broad \n>classification of VCS users.  However, I still need to be convinced \n>further, and I'd assign your example as an exception to the rules, \n>\ni don't see myself as exceptional at all in that regard.\nin fact, your second user group seems like unicorns, and the first like \na disparaging attitude from an elitist. in reality, users lie on a \nspectrum of willingness to engage with the details of the tools they \nuse, and that willingness is circumstantial. a tool that is forthcoming \nwith information has a higher chance of being actively engaged.\n\n> especially because you migrated to git from another VCS,\n>\nthat isn't all that uncommon, esp. in the older cohorts. and unless git \nachieves a total monopoly, it will remain a regular occurence.\n\nbut i don't see how that advances your argument anyway.\n\n> which you liked,\n>\ni didn't say that.\n\n> and because you use the command line a lot.\n>\nin my experience, this isn't uncommon for users of \"discrete\" vcs'es at \nall, even if they aren't too interested in the details. they just copy \n\"magic incantations\" from stackoverflow, etc. - disgusting, right? ;-)\n\n>After using git for a while, I can firmly say that git is awesome, but \n>that it also is a total overkill for many projects that need a VCS, for \n>which choosing Subversion would be a much batter choice.  Why, you'll \n>ask?  Because Subversion is many times simpler, and because many \n>projects actually don't need a distributed VCS.\n>\nthat line of reasoning seems mostly bogus to me. every project can \nbenefit from using a dvcs - reviewing and polishing the history prior to \npublication leads to higher quality (primarily of the history, but such \npolishing often also transpires to the code itself), so encouraging it \nis a useful default (of course interested users can just use git-svn, \nbut that's a bigger step than just having a closer look at what was \nshoved in their faces).\n\ngit is indeed pretty much by definition many times harder than svn, \nsimply because it splits the process of submission into three stages.  \nhowever, this is not a _useful_ definition, as everyone can remember to \nuse `git commit -a && git push` instead of `svn commit`. the real \ncomplexity comes from all the interesting things one can do because of \nthe split stages, but that's optional (well, until you need to pull and \nyou get a merge conflict - unlike with svn, git leaves the repo in a \n\"weird\" state, and the poor communication of that was in fact the major \nsource of frustration for me when i started).\n\n>I also ask myself why would I use git-gui or any other GUI utility?  To \n>me, clicking on something that represents a file is often simply wrong.  \n>\nthat makes you an outlier. most people find point-and-click interaction \nrather intuitive and significantly more efficient than encoding their \nintent into character sequences.\n\nalso, i said \"add -p\". selecting hunks (and even single lines) plays in \na wholly different league than whole files.\n\n>Though, I understand that many people prefer GUI utilities and I \n>respect that, everyone is free to do anything, but I also expect others \n>to respect my own preferences.\n>\nwhere did anyone even suggest disrespecting your preferences?\n\nyou should however consider whether your preferences are a good default \nfor the wider audience, even within the context of the command line.\n\ni for one think that it would be a perfectly valid experiment to go \nall-in and beyond with jacob's proposal - _and make it the default_ \n(when the output is a tty). more advanced users who feel annoyed would \nbe expected to opt out of it via configuration, as they are for the \nadvice messages. because it's really the same idea, only thought bigger.\n\nregards\n"},{"id":"483677","messageId":"bc55e29274da0d8059a8cd4383aa1b22@manjaro.org","threadId":"60408","inReplyTo":"ZTZQZhtTxvGT/s81@ugly","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-23T14:34:15Z","receivedAt":"2023-10-23T14:34:20Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-23 12:52, Oswald Buddenhagen wrote:\n> On Sun, Oct 22, 2023 at 02:55:05PM +0200, Dragan Simic wrote:\n>> Oh, that's awesome and I'm really happy to be wrong with my broad\n>> classification of VCS users.  However, I still need to be convinced\n>> further, and I'd assign your example as an exception to the rules,\n> \n> i don't see myself as exceptional at all in that regard.\n> in fact, your second user group seems like unicorns, and the first\n> like a disparaging attitude from an elitist. in reality, users lie on\n> a spectrum of willingness to engage with the details of the tools they\n> use, and that willingness is circumstantial. a tool that is\n> forthcoming with information has a higher chance of being actively\n> engaged.\n\nActually, I see myself as some kind of a slave worker who just keeps \ntyping on his keyboard and helps the elite (i.e. the \"normal people\") to \nenjoy their lives.  In other words, my viewpoint is totally opposite of \nhow you perceived it.\n\n>> and because you use the command line a lot.\n>> \n> in my experience, this isn't uncommon for users of \"discrete\" vcs'es\n> at all, even if they aren't too interested in the details. they just\n> copy \"magic incantations\" from stackoverflow, etc. - disgusting,\n> right? ;-)\n\nGood point.  Various commands are often simply copied and pasted with \nlittle understanding.  I guess that makes people content, as one of \ntheir life choices.  I can respect that.\n\n>> I also ask myself why would I use git-gui or any other GUI utility?\n>> To me, clicking on something that represents a file is often simply\n>> wrong.\n> \n> that makes you an outlier. most people find point-and-click\n> interaction rather intuitive and significantly more efficient than\n> encoding their intent into character sequences.\n\nWell, I guess I'm different.  As I already wrote above, I see myself as \nsome kind of a \"slave worker\" who helps in enabling others (i.e. the \n\"elite\") to do whatever they want.\n\n> you should however consider whether your preferences are a good\n> default for the wider audience, even within the context of the command\n> line.\n\nActually, some of my own preferences for my environment, when it comes \nto the git configuration, are not the defaults.\n\n> i for one think that it would be a perfectly valid experiment to go\n> all-in and beyond with jacob's proposal - _and make it the default_\n> (when the output is a tty). more advanced users who feel annoyed would\n> be expected to opt out of it via configuration, as they are for the\n> advice messages. because it's really the same idea, only thought\n> bigger.\n\nI'd never support that, FWIW.\n"},{"id":"483688","messageId":"ZTatzlzCkPOW3Rn7.jacob@initialcommit.io","threadId":"60408","inReplyTo":"bc55e29274da0d8059a8cd4383aa1b22@manjaro.org","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-23T17:30:54Z","receivedAt":"2023-10-23T17:31:22Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"On Mon, Oct 23, 2023 at 04:34:15PM +0200, Dragan Simic wrote:\n> On 2023-10-23 12:52, Oswald Buddenhagen wrote:\n> > On Sun, Oct 22, 2023 at 02:55:05PM +0200, Dragan Simic wrote:\n> > > Oh, that's awesome and I'm really happy to be wrong with my broad\n> > > classification of VCS users.  However, I still need to be convinced\n> > > further, and I'd assign your example as an exception to the rules,\n> > \n> > i don't see myself as exceptional at all in that regard.\n> > in fact, your second user group seems like unicorns, and the first\n> > like a disparaging attitude from an elitist. in reality, users lie on\n> > a spectrum of willingness to engage with the details of the tools they\n> > use, and that willingness is circumstantial. a tool that is\n> > forthcoming with information has a higher chance of being actively\n> > engaged.\n\nJust a note here, in my initial reply I was thinking of writing something\nsimilar about how in reality users of a tool as ubiquitous as Git would\nform a continuous spectrum in terms of their usage habits and trying to\nneatly plop them into 2 categories by speculating on their motives is\nan oversimplification to the point where it might not be so helpful\nevaluating whether an option like this would make sense to implement.\n\nTo me the bigger question is much simpler:\n\n\"Would this feature improve the Git experience for a signficant number\nof users?\"\n\nI have some evidence to support the claim that it would.\n\nMy Git-Sim tool does essentially what this proposal suggests and it has\nabout 31,000 installs since I released it early this year. Granted this\nis a drop in the bucket in the grand scheme of things, but it still shows\nthat there is demand for such a thing.\n\nGit-Sim is a visual dry-run tool for Git that creates images simulating\nwhat the corresponding Git command will do, without actually making any\nchange to the underlying repo state. Another important aspect is that\ncommand syntax mimics Git's exactly - so to simulate any Git command, like:\n\n$ git add asdf.txt qwer.txt\n\nYou would just replace the executable name and run:\n\n$ git-sim add asdf.txt qwer.txt\n\nand it will show you in an image exactly what will happen.\n\nThis is important because even simulating the command requires the user\nto know and use the Git CLI syntax for the command. It keeps them on the\ncommand line to do all of their actual work, unlike other true \"pointy\nclicky GUI's\" I've seen which expect the user to do all their work in the\nGUI. In fact this tool and feature expect no pointing or clicking at all.\n\nThe purpose of this is that users actually do all their work in the CLI\nand learn to use Git as intuitively as possible, the way the \"spartan\"\nCLI folks use it, the way it is meant to be used.\n\nThe reason to include a format like this in Git instead of just in my\ntool is simply to reach a wider audience and benefit more people. Of\ncourse it also appears much more trustworthy when a feature is part of\nthe native tool itself instead of some external thing.\n\n> > i for one think that it would be a perfectly valid experiment to go\n> > all-in and beyond with jacob's proposal - _and make it the default_\n> > (when the output is a tty). more advanced users who feel annoyed would\n> > be expected to opt out of it via configuration, as they are for the\n> > advice messages. because it's really the same idea, only thought\n> > bigger.\n> \n> I'd never support that, FWIW.\n\nFWIW, I'd _never suggest_ that. I very much value Git's current usage\nand wouldn't dream to make this the default. This proposal is for an\noptional flag to help users who would benefit from it, nothing more,\nnothing less. Speculating on user motives to classify them into 2 broad\ncategories in order to prove the feature isn't helpful misses the point\nthat there is a (relatively large IMO) subset of users who would benefit\nfrom it.\n\nAs an optional flag, experienced users wouldn't bat an eyelash, and the\ntype of users who installed my tool could use the flag on and off until\nthey feel confident enough to drop it. But it is always there in case\nthey need a refresher.\n"},{"id":"483693","messageId":"fd619b5f21e24529e5bc608cb0b9876f@manjaro.org","threadId":"60408","inReplyTo":"ZTatzlzCkPOW3Rn7.jacob@initialcommit.io","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-23T17:59:57Z","receivedAt":"2023-10-23T18:00:01Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-23 19:30, Jacob Stopak wrote:\n> On Mon, Oct 23, 2023 at 04:34:15PM +0200, Dragan Simic wrote:\n>> On 2023-10-23 12:52, Oswald Buddenhagen wrote:\n>>> i for one think that it would be a perfectly valid experiment to go\n>>> all-in and beyond with jacob's proposal - _and make it the default_\n>>> (when the output is a tty). more advanced users who feel annoyed \n>>> would\n>>> be expected to opt out of it via configuration, as they are for the\n>>> advice messages. because it's really the same idea, only thought\n>>> bigger.\n>> \n>> I'd never support that, FWIW.\n> \n> FWIW, I'd _never suggest_ that. I very much value Git's current usage\n> and wouldn't dream to make this the default. This proposal is for an\n> optional flag to help users who would benefit from it, nothing more,\n> nothing less. Speculating on user motives to classify them into 2 broad\n> categories in order to prove the feature isn't helpful misses the point\n> that there is a (relatively large IMO) subset of users who would \n> benefit\n> from it.\n> \n> As an optional flag, experienced users wouldn't bat an eyelash, and the\n> type of users who installed my tool could use the flag on and off until\n> they feel confident enough to drop it. But it is always there in case\n> they need a refresher.\n\nPerhaps my broad classification of git users wasn't that great, and I'm \nactually happy for being wrong there.\n\nJust to clarify, in general I'd support the inclusion of the table \noutput formatting, but I'd need to test it in detail before saying a \ndefinite yes.  It would also need to be more feature-complete, and the \noriginal author of the patches should commit to maintaining it as the \nnew git feature.  Having different options available can't hurt, IMHO.\n\nHowever, having that as the default is something I'll never support.  \nAll this is just my opinion.\n"},{"id":"483695","messageId":"ZTa4iqe0lqn/Yg5L@ugly","threadId":"60408","inReplyTo":"ZTatzlzCkPOW3Rn7.jacob@initialcommit.io","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Oswald Buddenhagen","fromEmail":"oswald.buddenhagen@gmx.de","sentAt":"2023-10-23T18:16:42Z","receivedAt":"2023-10-23T18:16:46Z","isPatch":true,"sender":{"key":"oswald.buddenhagen@gmx.de","avatar":"https://avatars.githubusercontent.com/u/812380?v=4"},"body":"On Mon, Oct 23, 2023 at 10:30:54AM -0700, Jacob Stopak wrote:\n>On Mon, Oct 23, 2023 at 04:34:15PM +0200, Dragan Simic wrote:\n>> On 2023-10-23 12:52, Oswald Buddenhagen wrote:\n>> > i for one think that it would be a perfectly valid experiment to go\n>> > all-in and beyond with jacob's proposal - _and make it the default_\n>> \n>> I'd never support that, FWIW.\n>\n>FWIW, I'd _never suggest_ that.\n>\nwhy, though?\ndoing that would extend the feature's reach about two orders of \nmagnitude among newbies, which is where it matters most.\n\n>I very much value Git's current usage and wouldn't dream to make this \n>the default.\n>\nmaking the default output format somewhat more verbose wouldn't really \n\"change the usage\", though. and being able to permanently get rid of it \nwith a single command should alleviate any _reasonable_ concerns about \nhabit disruption.\n\nregards\n"},{"id":"483706","messageId":"xmqqfs21noxx.fsf@gitster.g","threadId":"60408","inReplyTo":"ZTatzlzCkPOW3Rn7.jacob@initialcommit.io","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-10-23T19:01:14Z","receivedAt":"2023-10-23T19:01:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Stopak <jacob@initialcommit.io> writes:\n\n> Git-Sim is a visual dry-run tool for Git that creates images simulating\n> what the corresponding Git command will do, without actually making any\n> change to the underlying repo state. Another important aspect is that\n> command syntax mimics Git's exactly - so to simulate any Git command, like:\n\nAh, OK, now I see where your \"--table\" is coming from ;-).\n\"git-sim\" was exactly what I thought about when I saw it, and I did\nnot know that \"--table\" came from the same set of brain cells.\n\nOne thing that nobody seems to have raised that disturbs me is that\neven though there may be educational value in having such a\n\"feature\", having to carry the extra code to implement in Git incurs\nextra cost.  I was reasonably happy when I saw that \"git-sim\" was\ndone as a totally separate entity exactly for this reason.\n\nTHanks.\n"},{"id":"483708","messageId":"18c7b1bea07d5f3878f4466b8e133da1@manjaro.org","threadId":"60408","inReplyTo":"xmqqfs21noxx.fsf@gitster.g","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-23T19:04:49Z","receivedAt":"2023-10-23T19:04:53Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-23 21:01, Junio C Hamano wrote:\n> Jacob Stopak <jacob@initialcommit.io> writes:\n> \n>> Git-Sim is a visual dry-run tool for Git that creates images \n>> simulating\n>> what the corresponding Git command will do, without actually making \n>> any\n>> change to the underlying repo state. Another important aspect is that\n>> command syntax mimics Git's exactly - so to simulate any Git command, \n>> like:\n> \n> Ah, OK, now I see where your \"--table\" is coming from ;-).\n> \"git-sim\" was exactly what I thought about when I saw it, and I did\n> not know that \"--table\" came from the same set of brain cells.\n> \n> One thing that nobody seems to have raised that disturbs me is that\n> even though there may be educational value in having such a\n> \"feature\", having to carry the extra code to implement in Git incurs\n> extra cost.  I was reasonably happy when I saw that \"git-sim\" was\n> done as a totally separate entity exactly for this reason.\n\nThat's exactly why I already wrote that the original author of the table \npatches, if those would become accepted, should commit in advance to \nmaintaining that as the new git feature.\n"},{"id":"483712","messageId":"ZTbJiIkIyXwWK8JP.jacob@initialcommit.io","threadId":"60408","inReplyTo":"ZTa4iqe0lqn/Yg5L@ugly","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-23T19:29:12Z","receivedAt":"2023-10-23T19:29:17Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"On Mon, Oct 23, 2023 at 08:16:42PM +0200, Oswald Buddenhagen wrote:\n> On Mon, Oct 23, 2023 at 10:30:54AM -0700, Jacob Stopak wrote:\n> > On Mon, Oct 23, 2023 at 04:34:15PM +0200, Dragan Simic wrote:\n> > > On 2023-10-23 12:52, Oswald Buddenhagen wrote:\n> > > > i for one think that it would be a perfectly valid experiment to go\n> > > > all-in and beyond with jacob's proposal - _and make it the default_\n> > > \n> > > I'd never support that, FWIW.\n> > \n> > FWIW, I'd _never suggest_ that.\n> > \n> why, though?\n> doing that would extend the feature's reach about two orders of magnitude\n> among newbies, which is where it matters most.\n\nTo be honest it never even popped into my head to contemplate that and\nhow the user experience might be impacted by making it the default. I\nassume the big distinction is \"would it help more users than it would\nannoy\"?\n\nI always saw this feature as a helper to be invoked when the user is in need\nas opposed to a default, similar to the -n dry run option on some commands.\n\nFor brand new users, I can see what you mean since they would likely not\nknow the --table format exists unless being instructed by someone else.\nIt would be kindof a shame for this capability to exist but not be taken\nadvantage of by the folks who need it most - the newbies running \"git\nstatus\" literally for the very first time.\n\nBut the main drawback is that although the --table for status does provide\nsome visual clarity and tangibility, the status command doesn't actually\n_do_ anything, so the arrows showing how changes move around don't apply.\n\nThose arrows showing how things move only really apply to \"simulating\"\n(dry runs) for specific commands like add, restore, rm, commit, stash,\netc, so making the --table proposal a default status output would still\nmiss those scenarios.\n\nHowever, now that I'm thinking about it maybe it could somehow be included\nin the Hints feature? I honestly don't know exactly when the hints are\ncurrently invoked or how much detail they go into, but what just popped\ninto my head is kindof a \"universal dry run\" option, which would show the\nuser the --table format hint when they invoke an applicable command, and\nprompt them if they actually want to run it.\n\n> \n> > I very much value Git's current usage and wouldn't dream to make this\n> > the default.\n> > \n> making the default output format somewhat more verbose wouldn't really\n> \"change the usage\", though. and being able to permanently get rid of it with\n> a single command should alleviate any _reasonable_ concerns about habit\n> disruption.\n> \n> regards\n\nIt's a good point too that people surprised or annoyed or disgusted by the\nchange of a longstanding status output format could just disable the\nconfiguration with a single config command...\n"},{"id":"483720","messageId":"ZTbVY7Nf+DTYqHky@ugly","threadId":"60408","inReplyTo":"ZTbJiIkIyXwWK8JP.jacob@initialcommit.io","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Oswald Buddenhagen","fromEmail":"oswald.buddenhagen@gmx.de","sentAt":"2023-10-23T20:19:47Z","receivedAt":"2023-10-23T20:19:51Z","isPatch":true,"sender":{"key":"oswald.buddenhagen@gmx.de","avatar":"https://avatars.githubusercontent.com/u/812380?v=4"},"body":"On Mon, Oct 23, 2023 at 12:29:12PM -0700, Jacob Stopak wrote:\n>Those arrows showing how things move only really apply to \"simulating\"\n>(dry runs) for specific commands like add, restore, rm, commit, stash,\n>etc, so making the --table proposal a default status output would still\n>miss those scenarios.\n>\nyou're too focused on the status quo of your own tool. :-)\nthere is really nothing that would speak against the real commands \nreporting what they just *actually did*. this would seem rather helpful \nfor noobs and other insecure users.\nif one really wanted, \"you can also use this with --dry-run\" could be \npart of the hint that would say how to turn off the extra verbosity (or \njust the hint itself, if one likes the verbosity).\n\none could even go one step further and put at least the destructive \ncommands into interactive/confirmation mode by default. but that's \nprobably a bridge too far, as it would be potentially habit-forming in a \nbad way.\n\nregards\n"},{"id":"483723","messageId":"206fcee1a8004aca813debc358b183d4@manjaro.org","threadId":"60408","inReplyTo":"ZTbJiIkIyXwWK8JP.jacob@initialcommit.io","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-23T20:29:48Z","receivedAt":"2023-10-23T20:29:51Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-23 21:29, Jacob Stopak wrote:\n> However, now that I'm thinking about it maybe it could somehow be \n> included\n> in the Hints feature? I honestly don't know exactly when the hints are\n> currently invoked or how much detail they go into, but what just popped\n> into my head is kindof a \"universal dry run\" option, which would show \n> the\n> user the --table format hint when they invoke an applicable command, \n> and\n> prompt them if they actually want to run it.\n\nThat's a good idea, having the tables and dry runs mentioned in a \nwell-placed hint that could, of course, be disabled through git \nconfiguration.\n"},{"id":"483724","messageId":"ZTbb7bHkFOOyBT6+@ugly","threadId":"60408","inReplyTo":"18c7b1bea07d5f3878f4466b8e133da1@manjaro.org","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Oswald Buddenhagen","fromEmail":"oswald.buddenhagen@gmx.de","sentAt":"2023-10-23T20:47:41Z","receivedAt":"2023-10-23T20:47:45Z","isPatch":true,"sender":{"key":"oswald.buddenhagen@gmx.de","avatar":"https://avatars.githubusercontent.com/u/812380?v=4"},"body":">On 2023-10-23 21:01, Junio C Hamano wrote:\n>> One thing that nobody seems to have raised that disturbs me is that\n>> even though there may be educational value in having such a\n>> \"feature\", having to carry the extra code to implement in Git incurs\n>> extra cost.\n>\nthat's the case for literally every feature. you're just noticing it  \nhere, because from your expert perspective it seems redundant with \npre-existing functionality. so assuming that you actually care about \nnewbie UX, you are at least not convinced that it would be a significant \nhelp.\n\ni'm not totally convinced either, which is why i wrote of \"valid \nexperiment\" upthread. but i don't think that measuring the actual impact \nis all that interesting unless the feature turns out to be actually \nexcessively costly in the long run.\n\nOn Mon, Oct 23, 2023 at 09:04:49PM +0200, Dragan Simic wrote:\n>That's exactly why I already wrote that the original author of the table \n>patches, if those would become accepted, should commit in advance to \n>maintaining that as the new git feature.\n>\nthat's seems just a wee bit unfair (the bar isn't put that high for \nother features), and it's not realistically enforcable anyway.\n\nregards\n"},{"id":"483725","messageId":"d55eaf41f55bdff0b5ae734e5d7e6724@manjaro.org","threadId":"60408","inReplyTo":"ZTbVY7Nf+DTYqHky@ugly","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-23T20:51:31Z","receivedAt":"2023-10-23T20:51:34Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-23 22:19, Oswald Buddenhagen wrote:\n> On Mon, Oct 23, 2023 at 12:29:12PM -0700, Jacob Stopak wrote:\n>> Those arrows showing how things move only really apply to \"simulating\"\n>> (dry runs) for specific commands like add, restore, rm, commit, stash,\n>> etc, so making the --table proposal a default status output would \n>> still\n>> miss those scenarios.\n>> \n> you're too focused on the status quo of your own tool. :-)\n> there is really nothing that would speak against the real commands\n> reporting what they just *actually did*. this would seem rather\n> helpful for noobs and other insecure users.\n> if one really wanted, \"you can also use this with --dry-run\" could be\n> part of the hint that would say how to turn off the extra verbosity\n> (or just the hint itself, if one likes the verbosity).\n\nThe hint should be about how to turn the tables and verbosity on, not \nhow to get rid of it.\n\n> one could even go one step further and put at least the destructive\n> commands into interactive/confirmation mode by default. but that's\n> probably a bridge too far, as it would be potentially habit-forming in\n> a bad way.\n> \n> regards\n"},{"id":"483726","messageId":"c8e437eacdb883f612ae44e43c95602f@manjaro.org","threadId":"60408","inReplyTo":"ZTbb7bHkFOOyBT6+@ugly","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-23T20:59:08Z","receivedAt":"2023-10-23T20:59:12Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"> On Mon, Oct 23, 2023 at 09:04:49PM +0200, Dragan Simic wrote:\n>> That's exactly why I already wrote that the original author of\n>> the table patches, if those would become accepted, should commit\n>> in advance to maintaining that as the new git feature.\n>> \n> that's seems just a wee bit unfair (the bar isn't put that high for\n> other features), and it's not realistically enforcable anyway.\n\nWell, it's a bit disruptive new feature, which is also quite extensive \nand can rather easily become obsolete if not maintained in the long run. \n  Perhaps we should hear Jacob's thoughts about that as well.\n"},{"id":"483727","messageId":"ZTbhqzRLFS6/99lb.jacob@initialcommit.io","threadId":"60408","inReplyTo":"xmqqfs21noxx.fsf@gitster.g","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-23T21:12:11Z","receivedAt":"2023-10-23T21:12:16Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"On Mon, Oct 23, 2023 at 12:01:14PM -0700, Junio C Hamano wrote:\n> Ah, OK, now I see where your \"--table\" is coming from ;-).\n> \"git-sim\" was exactly what I thought about when I saw it, and I did\n> not know that \"--table\" came from the same set of brain cells.\n\nHaha the set is not quite the same I'm sure I've lost many over the\ncourse of this year. But the survivors are doing their best.\n\n> One thing that nobody seems to have raised that disturbs me is that\n> even though there may be educational value in having such a\n> \"feature\", having to carry the extra code to implement in Git incurs\n> extra cost.  I was reasonably happy when I saw that \"git-sim\" was\n> done as a totally separate entity exactly for this reason.\n\nErm, not to get too sappy here but I'd love to maintain anything related\nto this that gets implemented, in whatever form that turns out to be.\n\nI already spend way too much of my free time working on Git-related\nthings so making this contribution to Git itself would mean a lot to me.\n\nStarting Git-Sim as a separate entity made sense to me because:\n\n  * I had no idea whether anyone wanted something like this, so it\n    would have made for a pretty weak argument to the community. (It\n    turned out way more users were interested than I thought).\n\n  * It's written in Python and relies on a dependency library called\n    Manim, so I thought it wouldn't make any sense to try and wedge\n    that into the Git codebase.\n\n  * The output is presentation quality images / video animations, which\n    is unlike anything I've seen outputted by any Git command. (I didn't\n    explore what it would take to do something similar in C).\n\nThe main downsides to Git-Sim as a separate tool are:\n\n  * The main downside is lack of reach. Not being Git-native means only\n    a tiny fraction of Git users will ever know it exists.\n\n  * There is technically no guarantee that a simulated output actually\n    corresponds to what the command will do, as highlighted by this\n    comment on Hacker News: \"Next HN post - \"I destroyed my repo - but\n    it WORKED in Git-Sim!\"\n\n  * As Git changes over time, Git-Sim is destined to be a step behind.\n\n  * Certain commands (like the networked commands fetch/push/pull/etc)\n    are not easy to simulate without doing horrible things like cloning\n    a new copy of the repo behind the scenes, running the desired\n    operation on it, and checking the result. I assume things like this\n    would be a lot easier to do within Git.\n"},{"id":"483728","messageId":"ZTbiU+SwvGi9972S@ugly","threadId":"60408","inReplyTo":"d55eaf41f55bdff0b5ae734e5d7e6724@manjaro.org","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Oswald Buddenhagen","fromEmail":"oswald.buddenhagen@gmx.de","sentAt":"2023-10-23T21:14:59Z","receivedAt":"2023-10-23T21:15:02Z","isPatch":true,"sender":{"key":"oswald.buddenhagen@gmx.de","avatar":"https://avatars.githubusercontent.com/u/812380?v=4"},"body":"On Mon, Oct 23, 2023 at 10:51:31PM +0200, Dragan Simic wrote:\n>The hint should be about how to turn the tables and verbosity on, not \n>how to get rid of it.\n>\nbut that's just backwards. a noob shouldn't be bothered with putting the \ntool into noob-friendly mode, neither actually nor \"just\" \npsychologically. switching to \"expert\" mode should be the conscious \neffort. and making it opt-in wouldn't even save the experts any \nannoyance, as they need to opt out from the hint anyway.\n\nregards\n"},{"id":"483729","messageId":"2d392c7a7a00bd2c66efb17a2dbc86d7@manjaro.org","threadId":"60408","inReplyTo":"ZTbiU+SwvGi9972S@ugly","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-23T21:19:18Z","receivedAt":"2023-10-23T21:19:23Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-23 23:14, Oswald Buddenhagen wrote:\n> On Mon, Oct 23, 2023 at 10:51:31PM +0200, Dragan Simic wrote:\n>> The hint should be about how to turn the tables and verbosity on, not \n>> how to get rid of it.\n> \n> but that's just backwards. a noob shouldn't be bothered with putting\n> the tool into noob-friendly mode, neither actually nor \"just\"\n> psychologically. switching to \"expert\" mode should be the conscious\n> effort. and making it opt-in wouldn't even save the experts any\n> annoyance, as they need to opt out from the hint anyway.\n\nLet's focus on non-noobs as well, which could go nuts by having some \nstrange tables displayed after updating git.  For the beginners, having \nsomething configured is already inevitable, e.g. their credentials, so \nthey could also turn on the tables if they wanted so.\n"},{"id":"483730","messageId":"ZTbkUUbmp56fgc+a.jacob@initialcommit.io","threadId":"60408","inReplyTo":"c8e437eacdb883f612ae44e43c95602f@manjaro.org","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-23T21:23:29Z","receivedAt":"2023-10-23T21:23:34Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"On Mon, Oct 23, 2023 at 10:59:08PM +0200, Dragan Simic wrote:\n> > On Mon, Oct 23, 2023 at 09:04:49PM +0200, Dragan Simic wrote:\n> > > That's exactly why I already wrote that the original author of\n> > > the table patches, if those would become accepted, should commit\n> > > in advance to maintaining that as the new git feature.\n> > > \n> > that's seems just a wee bit unfair (the bar isn't put that high for\n> > other features), and it's not realistically enforcable anyway.\n> \n> Well, it's a bit disruptive new feature, which is also quite extensive and\n> can rather easily become obsolete if not maintained in the long run.\n> Perhaps we should hear Jacob's thoughts about that as well.\n\nI would love to work on implementing this and maintaining it! I spend\ntoo much time doing Git things for no reason anyway so why not? :D\n\nBut... I could die at the hands of an angry user and then you would have\nto take over, Dragan...\n"},{"id":"483731","messageId":"8c661ea94ba42f60eb87d1d6d4530947@manjaro.org","threadId":"60408","inReplyTo":"ZTbkUUbmp56fgc+a.jacob@initialcommit.io","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-23T21:26:45Z","receivedAt":"2023-10-23T21:26:48Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-23 23:23, Jacob Stopak wrote:\n> On Mon, Oct 23, 2023 at 10:59:08PM +0200, Dragan Simic wrote:\n>> > On Mon, Oct 23, 2023 at 09:04:49PM +0200, Dragan Simic wrote:\n>> > > That's exactly why I already wrote that the original author of\n>> > > the table patches, if those would become accepted, should commit\n>> > > in advance to maintaining that as the new git feature.\n>> > >\n>> > that's seems just a wee bit unfair (the bar isn't put that high for\n>> > other features), and it's not realistically enforcable anyway.\n>> \n>> Well, it's a bit disruptive new feature, which is also quite extensive \n>> and\n>> can rather easily become obsolete if not maintained in the long run.\n>> Perhaps we should hear Jacob's thoughts about that as well.\n> \n> I would love to work on implementing this and maintaining it! I spend\n> too much time doing Git things for no reason anyway so why not? :D\n\nAwesome!  Quite frankly, I expected to hear that. :)\n\n> But... I could die at the hands of an angry user and then you would \n> have\n> to take over, Dragan...\n\nOr I could instead try to help you fending off angry git users? :)\n"},{"id":"483748","messageId":"ZTb/HeILRHnZjaz6.jacob@initialcommit.io","threadId":"60408","inReplyTo":"ZTbVY7Nf+DTYqHky@ugly","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-23T23:17:49Z","receivedAt":"2023-10-23T23:17:54Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"On Mon, Oct 23, 2023 at 10:19:47PM +0200, Oswald Buddenhagen wrote:\n> On Mon, Oct 23, 2023 at 12:29:12PM -0700, Jacob Stopak wrote:\n> > Those arrows showing how things move only really apply to \"simulating\"\n> > (dry runs) for specific commands like add, restore, rm, commit, stash,\n> > etc, so making the --table proposal a default status output would still\n> > miss those scenarios.\n> > \n> you're too focused on the status quo of your own tool. :-)\n\nHa it's possible. I created git-sim with a very specific use case in mind\nso you're right it's probably worth rethinking that while taking into account\nGit's other functionality that wasn't in the picture with an external tool.\n\n> there is really nothing that would speak against the real commands reporting\n> what they just *actually did*. this would seem rather helpful for noobs and\n> other insecure users.\n\nThat's true, it would be just as easy to report the results of a command\n(and even easier in some cases) than forecasting the result in a dry-run.\nThe question is how to decide which one? Do you report the results of\ncertain commands by default while hints are enabled? And only as a dry run\nwhen the --dry-run / -n flag is added? That actually would make sense as\nit wouldn't add \"special\" behavior to the dry run. The dry run would just\nreport the exact same default output as the normal command, but omit the action.\n\n> if one really wanted, \"you can also use this with --dry-run\" could be part\n> of the hint that would say how to turn off the extra verbosity (or just the\n> hint itself, if one likes the verbosity).\n\nInteresting. So many ways to think about how to optimize the user\ninteraction...\n\n> one could even go one step further and put at least the destructive commands\n> into interactive/confirmation mode by default. but that's probably a bridge\n> too far, as it would be potentially habit-forming in a bad way.\n\nThat would be an interesting add on to our discussion above. So as a\nthought experiment, let's pretend there are no restrictions from\ntraditional users, we could:\n\n  1) Enable verbose results output by default to certain commands, which\n     could include a visual table-based output where applicable, and a\n     hint to disable it. (Are hints currently command-specific? Or even\n     scenario-specific within a command? Or is it all or nothing?)\n\n  2) Include verbose/table results on dry runs, and add dry run flags onto\n     commands that should seem to have it for consistency. For example\n     \"git add\" has a dry run flag but \"git restore\" (the hinted inverse\n     operation) does not. I assume on dry runs hints wouldn't be used to\n     communicate anything.\n\n  3) Well, I'll admit about once every 3 months I run \"git stash --all\"\n     when I really meant \"git stash --include-untracked\" and proceed to\n     lose a small part of my soul. This would be saved by a simple\n     confirmation. I find that the stuff like \"git reset --hard\" doesn't\n     bother me anymore since I know when to be careful with it and what\n     things I can get back and how to do it. But I find the nasty ones\n     are the things that sound like what you want but end up doing\n     something bad. Not sure there is a way to isolate those though...\n\n     I'm rambling now. But maybe for interactivity, at the very least it\n     could be added to dry runs, like a \"Here's what would happen, want\n     to run it now?\" I got this feedback a few times for git-sim as well.\n"},{"id":"483754","messageId":"62164acf4a787042dbb6e5abe212559b@manjaro.org","threadId":"60408","inReplyTo":"ZTb/HeILRHnZjaz6.jacob@initialcommit.io","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-24T01:10:25Z","receivedAt":"2023-10-24T01:10:29Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-24 01:17, Jacob Stopak wrote:\n> That's true, it would be just as easy to report the results of a \n> command\n> (and even easier in some cases) than forecasting the result in a \n> dry-run.\n> The question is how to decide which one? Do you report the results of\n> certain commands by default while hints are enabled? And only as a dry \n> run\n> when the --dry-run / -n flag is added? That actually would make sense \n> as\n> it wouldn't add \"special\" behavior to the dry run. The dry run would \n> just\n> report the exact same default output as the normal command, but omit \n> the action.\n\nIMHO, it would be the best to simply implement support for \n\"<command>.verbose = table\" in the git configuration, similarly to how \nwe already have \"commit.verbose = true\".  That way tables could be \nenabled per command, according to the user's preferences, regardless of \nperforming dry runs or not.\n\nThe new hint would be placed somewhere, which should be decided \nseparately, but having the hint enabled or disabled wouldn't affect \nanything else.  Just like with the other hints.\n"},{"id":"483755","messageId":"xmqqil6w6al3.fsf@gitster.g","threadId":"60408","inReplyTo":"62164acf4a787042dbb6e5abe212559b@manjaro.org","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-10-24T02:03:20Z","receivedAt":"2023-10-24T02:03:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Dragan Simic <dsimic@manjaro.org> writes:\n\n> IMHO, it would be the best to simply implement support for\n> \"<command>.verbose = table\" in the git configuration, similarly to how\n> we already have \"commit.verbose = true\".  That way tables could be\n> enabled per command, according to the user's preferences, regardless\n> of performing dry runs or not.\n\nI think the \"use more verbose report format to help relatively\ninexperienced folks, in exchange for spending more screen real\nestate\" is a good direction to think about this thing.\n\nI am not personally interested in adding such support all that much\nmyself, but one piece of advice I can offer those who are interested\nis not to be too deeply attached to the word \"table\".\n\nIt may be that \"git status\" (not \"status -s\" [*] but the current\nversion for human consumption) shows \"paths with changes to be\ncommitted (i.e. changes added to the index exist)\" and \"paths with\nchanges you could add to the index (i.e. changes yet to be added to\nthe index exist)\" in a separate list, and Jacob may have found that\nit gives a more understandable view into the states of each path if\nthe output is turned 90-degrees and the changes are shown in a\ntabular form.  But \"table\" in this example is merely an\nimplementation detail of one presentation that is easier to\nunderstand for \"git status\", and calling it \"table\" and making it a\nword in the vocabulary of <command>.verbose is like a tail wagging\nthe dog.  We want to convey to the users that the option is about\n\"with extra verbosity, the user is buying a bit more clarity\", not\nnecessarily \"use tabular form no matter what\".\n\nFor some of the commands, tabular presentation might not even be the\npresentation form that is the easiest to understand to novices.  For\nexample, I just pushed out today's integration result to some of my\nrepositories, and \"git push\" output looks like so:\n\nTo github.com:gitster/git.git\n + 5cb4030332...7dc6f5ada8 ak/color-decorate-symbols -> ak/colo...\n + a71066b71b...c80a646458 jch -> jch (forced update)\n + 89a1ffc6a4...416cdf7260 seen -> seen (forced update)\n + 7ff160463b...2c610ca9ff tb/merge-tree-write-pack -> tb/merge...\n   2e3b7b2460..57243409ad  refs/notes/amlog -> refs/notes/amlog\n\nThis is already \"tabular\" and gives enough information to tell me\nwhich ones did not get updated (e.g., I do not see 'next' there) and\nwhich ones are forced ('jch' and 'seen' are usually forced and I'll\nnotice that I may have made mistakes if they are not).  But a\nhypothetical presentation that is easier for novices to read may\nshow \"git log --oneline --graph old...new\" (or some abbreviated form\nof it) between the old and new tips of the affected branches.  At\nthat point, calling the improved output as \"table\" would make little\nsense.\n\nFor commands that Jacob found it easier to explain in tabular form,\nlike \"git add\", it is very possible that two years down the road,\nanother Jacob comes around and proposes a different presentation\nthat is even easier for novices to understand, and it may not be a\ntabular form.\n\nSo be very careful when choosing what to call this new thing, and\navoid naming it after the implementation details (e.g., in what\nparticular shape the data gets presented) that may turn out not to\nbe the most important part of the concept.\n\n[Footnote]\n\n * FWIW, \"git status -s\" is a tabular presentation.  Maybe we can\n   add a more verbose form of \"-s\" and be done with it for the\n   command?\n"},{"id":"483756","messageId":"8d45763bb4fa4c7d1e1f69dfaf93e647@manjaro.org","threadId":"60408","inReplyTo":"xmqqil6w6al3.fsf@gitster.g","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-24T02:21:16Z","receivedAt":"2023-10-24T02:21:21Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-24 04:03, Junio C Hamano wrote:\n> I think the \"use more verbose report format to help relatively\n> inexperienced folks, in exchange for spending more screen real\n> estate\" is a good direction to think about this thing.\n> \n> I am not personally interested in adding such support all that much\n> myself, but one piece of advice I can offer those who are interested\n> is not to be too deeply attached to the word \"table\".\n> \n> ... snip ...\n> \n> So be very careful when choosing what to call this new thing, and\n> avoid naming it after the implementation details (e.g., in what\n> particular shape the data gets presented) that may turn out not to\n> be the most important part of the concept.\n\nTotally agreed, \"table\" simply sneaked in and remained here as the term. \n  Perhaps \"<command>.verbose = extra\" or something similar would be a \ngood choice.\n\n> [Footnote]\n> \n>  * FWIW, \"git status -s\" is a tabular presentation.  Maybe we can\n>    add a more verbose form of \"-s\" and be done with it for the\n>    command?\n\nThat's also an option.\n"},{"id":"483938","messageId":"20231026224615.675172-1-jacob@initialcommit.io","threadId":"60408","inReplyTo":"20231020183947.463882-1-jacob@initialcommit.io","subject":"[RFC PATCH v2 0/6] Noobify format for status, add, restore","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-26T22:46:09Z","receivedAt":"2023-10-26T22:46:41Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"Take into account reviewer feedback by doing several things differently:\n\n  * Rename this feature (for now) as \"noob format mode\" (or just \"noob\n    mode\") instead of the original \"--table\" verbiage. As pointed out,\n    this no longer ties the name of the setting to it's proposed\n    implementation detail as a table. Noob mode is not necessarily the\n    right name, just a placeholder for now. Unless people like it :D\n\n  * Instead of manually having to invoke the -t, --table every time this\n    format is to be used, set the config option \"status.noob\" to true.\n    Although this is logically tied to the status command, there are many\n    commands that produce status output, (and this series adds more), so\n    assume that if the user wants to see the status this way, that it\n    should be enabled whenever the status info is displayed.\n\n  * When running \"git add\" and \"git restore\" while noob mode is enabled,\n    perform the add/restore function as usual, but display the table\n    formatted output with arrows showing how file changes moved around.\n    Displaying the output in this understandable format after each\n    command execution allows the noob to immediately see what they did.\n    Although this series only implements for status, add, and restore,\n    this output format would make sense in other commands like rm, mv,\n    commit, clean, and stash.\n\n  * Works consistently with commands that already have a --dry-run\n    (-n) option. The dry run shows the exact same output, but\n    doesn't actually do the thing.\n\n  * If `advice.statusHints` is true, add a table footer with status hints.\n    Shorten these hints so that they are still clear but better fit into a\n    table. Make the hint text yellow to distinguish them. The hints only\n    appear when explicitly running \"git status\", which helps the user\n    answer the question \"what can I do next?\". Hints are omitted in\n    \"impact\" commands like add and restore. Having hints here distracts\n    from the file change moves being showed in the table by arrows.\n\nTODO:\n\n  * \"git status\" outputs myriad other information depending on the state\n    of the repo, like branch info, merge conflicts, rebase info, bisect,\n    etc. Need to think about how to convey that info with the new setting.\n\n  * Some commands (like stash) might need more than 3 table columns to\n    display everything clearly.\n\n  * For destructive commands, think about adding a prompt describing the\n    effect, so the user can confirm before the action is taken.\n\n  * Fix horrible things in the patch series code.\n\n  * Probably other things.\n\nPlay around with it! It's fun!\n\nJacob Stopak (6):\n  status: add noob format from status.noob config\n  status: handle long paths in noob format\n  add: implement noob mode\n  add: set unique color for noob mode arrows\n  restore: implement noob mode\n  status: add advice status hints as table footer\n\n Makefile           |   2 +\n builtin/add.c      |  47 +++++--\n builtin/checkout.c |  46 +++++--\n builtin/commit.c   | 157 +----------------------\n commit.c           |   2 +\n noob.c             | 198 +++++++++++++++++++++++++++++\n noob.h             |  21 ++++\n read-cache-ll.h    |  10 +-\n read-cache.c       |  41 +++++-\n table.c            | 301 +++++++++++++++++++++++++++++++++++++++++++++\n table.h            |   6 +\n wt-status.c        |  75 +++++++----\n wt-status.h        |   6 +\n 13 files changed, 708 insertions(+), 204 deletions(-)\n create mode 100644 noob.c\n create mode 100644 noob.h\n create mode 100644 table.c\n create mode 100644 table.h\n\n-- \n2.42.0.404.g2bcc23f3db\n\n"},{"id":"483939","messageId":"20231026224615.675172-2-jacob@initialcommit.io","threadId":"60408","inReplyTo":"20231026224615.675172-1-jacob@initialcommit.io","subject":"[RFC PATCH v2 1/6] status: add noob format from status.noob config","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-26T22:46:10Z","receivedAt":"2023-10-26T22:46:43Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"Signed-off-by: Jacob Stopak <jacob@initialcommit.io>\n---\n Makefile         |   1 +\n builtin/commit.c |   7 +++\n table.c          | 117 +++++++++++++++++++++++++++++++++++++++++++++++\n table.h          |   6 +++\n wt-status.c      |  72 +++++++++++++++++++----------\n wt-status.h      |   1 +\n 6 files changed, 179 insertions(+), 25 deletions(-)\n create mode 100644 table.c\n create mode 100644 table.h\n\ndiff --git a/Makefile b/Makefile\nindex 9c6a2f125f..a7399ca8f0 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1155,6 +1155,7 @@ LIB_OBJS += submodule-config.o\n LIB_OBJS += submodule.o\n LIB_OBJS += symlinks.o\n LIB_OBJS += tag.o\n+LIB_OBJS += table.o\n LIB_OBJS += tempfile.o\n LIB_OBJS += thread-utils.o\n LIB_OBJS += tmp-objdir.o\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 7da5f92448..880c42f5b7 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1430,6 +1430,13 @@ static int git_status_config(const char *k, const char *v,\n \t\t\tstatus_deferred_config.status_format = STATUS_FORMAT_NONE;\n \t\treturn 0;\n \t}\n+\tif (!strcmp(k, \"status.noob\")) {\n+\t\tif (git_config_bool(k, v))\n+\t\t\tstatus_deferred_config.status_format = STATUS_FORMAT_NOOB;\n+\t\telse\n+\t\t\tstatus_deferred_config.status_format = STATUS_FORMAT_NONE;\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(k, \"status.branch\")) {\n \t\tstatus_deferred_config.show_branch = git_config_bool(k, v);\n \t\treturn 0;\ndiff --git a/table.c b/table.c\nnew file mode 100644\nindex 0000000000..15600e117f\n--- /dev/null\n+++ b/table.c\n@@ -0,0 +1,117 @@\n+#define USE_THE_INDEX_VARIABLE\n+#include \"builtin.h\"\n+#include \"gettext.h\"\n+#include \"strbuf.h\"\n+#include \"wt-status.h\"\n+#include \"config.h\"\n+#include \"string-list.h\"\n+#include \"sys/ioctl.h\"\n+\n+static const char *color(int slot, struct wt_status *s)\n+{\n+\tconst char *c = \"\";\n+\tif (want_color(s->use_color))\n+\t\tc = s->color_palette[slot];\n+\tif (slot == WT_STATUS_ONBRANCH && color_is_nil(c))\n+\t\tc = s->color_palette[WT_STATUS_HEADER];\n+\treturn c;\n+}\n+\n+static void build_table_border(struct strbuf *buf, int cols)\n+{\n+\tstrbuf_reset(buf);\n+\tstrbuf_addchars(buf, '-', cols);\n+}\n+\n+static void build_table_entry(struct strbuf *buf, char *entry, int cols)\n+{\n+\tstrbuf_reset(buf);\n+\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2);\n+\tstrbuf_addstr(buf, entry);\n+\n+\t/* Bump right padding if entry length is odd */\n+\tif (!(strlen(entry) % 2))\n+\t\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2 + 1);\n+\telse\n+\t\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2);\n+}\n+\n+static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct wt_status *s)\n+{\n+\tprintf(_(\"|\"));\n+\tcolor_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), \"%s\", buf1->buf);\n+\tprintf(_(\"|\"));\n+\tcolor_fprintf(s->fp, color(WT_STATUS_CHANGED, s), \"%s\", buf2->buf);\n+\tprintf(_(\"|\"));\n+\tcolor_fprintf(s->fp, color(WT_STATUS_UPDATED, s), \"%s\", buf3->buf);\n+\tprintf(_(\"|\\n\"));\n+}\n+\n+void print_noob_status(struct wt_status *s)\n+{\n+\tstruct winsize w;\n+\tint cols;\n+\tstruct strbuf table_border = STRBUF_INIT;\n+\tstruct strbuf table_col_entry_1 = STRBUF_INIT;\n+\tstruct strbuf table_col_entry_2 = STRBUF_INIT;\n+\tstruct strbuf table_col_entry_3 = STRBUF_INIT;\n+\tstruct string_list_item *item;\n+\n+\t/* Get terminal width */\n+\tioctl(STDOUT_FILENO, TIOCGWINSZ, &w);\n+\tcols = w.ws_col;\n+\n+\t/* Ensure table is divisible into 3 even columns */\n+\twhile (((cols - 1) % 3) > 0 || !(cols % 2)) {\n+\t\tcols -= 1;\n+\t}\n+\n+\tbuild_table_border(&table_border, cols);\n+\tbuild_table_entry(&table_col_entry_1, \"Untracked files\", cols);\n+\tbuild_table_entry(&table_col_entry_2, \"Unstaged changes\", cols);\n+\tbuild_table_entry(&table_col_entry_3, \"Staging area\", cols);\n+\n+\t/* Draw table header */\n+\tprintf(_(\"%s\\n\"), table_border.buf);\n+\tprintf(_(\"|%s|%s|%s|\\n\"), table_col_entry_1.buf, table_col_entry_2.buf, table_col_entry_3.buf);\n+\tprintf(_(\"%s\\n\"), table_border.buf);\n+\n+\t/* Draw table body */\n+\tfor_each_string_list_item(item, &s->untracked) {\n+\t\tbuild_table_entry(&table_col_entry_1, item->string, cols);\n+\t\tbuild_table_entry(&table_col_entry_2, \"\", cols);\n+\t\tbuild_table_entry(&table_col_entry_3, \"\", cols);\n+\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\t}\n+\n+\tfor_each_string_list_item(item, &s->change) {\n+\t\tstruct wt_status_change_data *d = item->util;\n+\t\tif (d->worktree_status && d->index_status) {\n+\t\t\tbuild_table_entry(&table_col_entry_1, \"\", cols);\n+\t\t\tbuild_table_entry(&table_col_entry_2, item->string, cols);\n+\t\t\tbuild_table_entry(&table_col_entry_3, item->string, cols);\n+\t\t} else if (d->worktree_status) {\n+\t\t\tbuild_table_entry(&table_col_entry_1, \"\", cols);\n+\t\t\tbuild_table_entry(&table_col_entry_2, item->string, cols);\n+\t\t\tbuild_table_entry(&table_col_entry_3, \"\", cols);\n+\t\t} else if (d->index_status) {\n+\t\t\tbuild_table_entry(&table_col_entry_1, \"\", cols);\n+\t\t\tbuild_table_entry(&table_col_entry_2, \"\", cols);\n+\t\t\tbuild_table_entry(&table_col_entry_3, item->string, cols);\n+\t\t}\n+\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\t}\n+\t\n+\tif (!s->untracked.nr && !s->change.nr) {\n+\t\tbuild_table_entry(&table_col_entry_1, \"-\", cols);\n+\t\tbuild_table_entry(&table_col_entry_2, \"-\", cols);\n+\t\tbuild_table_entry(&table_col_entry_3, \"-\", cols);\n+\t\tprintf(_(\"|%s|%s|%s|\\n\"), table_col_entry_1.buf, table_col_entry_2.buf, table_col_entry_3.buf);\n+\t}\n+\n+\tprintf(_(\"%s\\n\"), table_border.buf);\n+\tstrbuf_release(&table_border);\n+\tstrbuf_release(&table_col_entry_1);\n+\tstrbuf_release(&table_col_entry_2);\n+\tstrbuf_release(&table_col_entry_3);\n+}\ndiff --git a/table.h b/table.h\nnew file mode 100644\nindex 0000000000..c9e8c386de\n--- /dev/null\n+++ b/table.h\n@@ -0,0 +1,6 @@\n+#ifndef TABLE_H\n+#define TABLE_H\n+\n+void print_noob_status(struct wt_status *s);\n+\n+#endif /* TABLE_H */\ndiff --git a/wt-status.c b/wt-status.c\nindex 9f45bf6949..712807aa8f 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -31,6 +31,7 @@\n #include \"lockfile.h\"\n #include \"sequencer.h\"\n #include \"fsmonitor-settings.h\"\n+#include \"table.h\"\n \n #define AB_DELAY_WARNING_IN_MS (2 * 1000)\n #define UF_DELAY_WARNING_IN_MS (2 * 1000)\n@@ -1833,39 +1834,46 @@ static void wt_longstatus_print_state(struct wt_status *s)\n \t\tshow_sparse_checkout_in_use(s, state_color);\n }\n \n-static void wt_longstatus_print(struct wt_status *s)\n+static void wt_longstatus_print_onwhat(struct wt_status *s, const char *branch_name)\n {\n+\tconst char *on_what = _(\"On branch \");\n \tconst char *branch_color = color(WT_STATUS_ONBRANCH, s);\n \tconst char *branch_status_color = color(WT_STATUS_HEADER, s);\n+\n+\tif (!strcmp(branch_name, \"HEAD\")) {\n+\t\tbranch_status_color = color(WT_STATUS_NOBRANCH, s);\n+\t\tif (s->state.rebase_in_progress ||\n+\t\t    s->state.rebase_interactive_in_progress) {\n+\t\t\tif (s->state.rebase_interactive_in_progress)\n+\t\t\t\ton_what = _(\"interactive rebase in progress; onto \");\n+\t\t\telse\n+\t\t\t\ton_what = _(\"rebase in progress; onto \");\n+\t\t\tbranch_name = s->state.onto;\n+\t\t} else if (s->state.detached_from) {\n+\t\t\tbranch_name = s->state.detached_from;\n+\t\t\tif (s->state.detached_at)\n+\t\t\t\ton_what = _(\"HEAD detached at \");\n+\t\t\telse\n+\t\t\t\ton_what = _(\"HEAD detached from \");\n+\t\t} else {\n+\t\t\tbranch_name = \"\";\n+\t\t\ton_what = _(\"Not currently on any branch.\");\n+\t\t}\n+\t} else\n+\t\tskip_prefix(branch_name, \"refs/heads/\", &branch_name);\n+\n+\tstatus_printf_more(s, branch_status_color, \"%s\", on_what);\n+\tstatus_printf_more(s, branch_color, \"%s\\n\", branch_name);\n+}\n+\n+static void wt_longstatus_print(struct wt_status *s)\n+{\n \tenum fsmonitor_mode fsm_mode = fsm_settings__get_mode(s->repo);\n \n \tif (s->branch) {\n-\t\tconst char *on_what = _(\"On branch \");\n \t\tconst char *branch_name = s->branch;\n-\t\tif (!strcmp(branch_name, \"HEAD\")) {\n-\t\t\tbranch_status_color = color(WT_STATUS_NOBRANCH, s);\n-\t\t\tif (s->state.rebase_in_progress ||\n-\t\t\t    s->state.rebase_interactive_in_progress) {\n-\t\t\t\tif (s->state.rebase_interactive_in_progress)\n-\t\t\t\t\ton_what = _(\"interactive rebase in progress; onto \");\n-\t\t\t\telse\n-\t\t\t\t\ton_what = _(\"rebase in progress; onto \");\n-\t\t\t\tbranch_name = s->state.onto;\n-\t\t\t} else if (s->state.detached_from) {\n-\t\t\t\tbranch_name = s->state.detached_from;\n-\t\t\t\tif (s->state.detached_at)\n-\t\t\t\t\ton_what = _(\"HEAD detached at \");\n-\t\t\t\telse\n-\t\t\t\t\ton_what = _(\"HEAD detached from \");\n-\t\t\t} else {\n-\t\t\t\tbranch_name = \"\";\n-\t\t\t\ton_what = _(\"Not currently on any branch.\");\n-\t\t\t}\n-\t\t} else\n-\t\t\tskip_prefix(branch_name, \"refs/heads/\", &branch_name);\n \t\tstatus_printf(s, color(WT_STATUS_HEADER, s), \"%s\", \"\");\n-\t\tstatus_printf_more(s, branch_status_color, \"%s\", on_what);\n-\t\tstatus_printf_more(s, branch_color, \"%s\\n\", branch_name);\n+\t\twt_longstatus_print_onwhat(s, branch_name);\n \t\tif (!s->is_initial)\n \t\t\twt_longstatus_print_tracking(s);\n \t}\n@@ -2133,6 +2141,17 @@ static void wt_shortstatus_print(struct wt_status *s)\n \t\twt_shortstatus_other(it, s, \"!!\");\n }\n \n+static void wt_noobstatus_print(struct wt_status *s)\n+{\n+\tif (s->show_branch) {\n+\t\tconst char *branch_name = s->branch;\n+\t\twt_longstatus_print_onwhat(s, branch_name);\n+\t\twt_longstatus_print_tracking(s);\n+\t}\n+\n+\tprint_noob_status(s);\n+}\n+\n static void wt_porcelain_print(struct wt_status *s)\n {\n \ts->use_color = 0;\n@@ -2560,6 +2579,9 @@ void wt_status_print(struct wt_status *s)\n \tcase STATUS_FORMAT_LONG:\n \t\twt_longstatus_print(s);\n \t\tbreak;\n+\tcase STATUS_FORMAT_NOOB:\n+\t\twt_noobstatus_print(s);\n+\t\tbreak;\n \t}\n \n \ttrace2_region_leave(\"status\", \"print\", s->repo);\ndiff --git a/wt-status.h b/wt-status.h\nindex ab9cc9d8f0..3f08f0d72b 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -73,6 +73,7 @@ enum wt_status_format {\n \tSTATUS_FORMAT_SHORT,\n \tSTATUS_FORMAT_PORCELAIN,\n \tSTATUS_FORMAT_PORCELAIN_V2,\n+\tSTATUS_FORMAT_NOOB,\n \n \tSTATUS_FORMAT_UNSPECIFIED\n };\n-- \n2.42.0.404.g2bcc23f3db\n\n"},{"id":"483940","messageId":"20231026224615.675172-3-jacob@initialcommit.io","threadId":"60408","inReplyTo":"20231026224615.675172-1-jacob@initialcommit.io","subject":"[RFC PATCH v2 2/6] status: handle long paths in noob format","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-26T22:46:11Z","receivedAt":"2023-10-26T22:46:44Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"Signed-off-by: Jacob Stopak <jacob@initialcommit.io>\n---\n table.c     | 58 ++++++++++++++++++++++++++++++++++++++++++++++-------\n table.h     |  2 +-\n wt-status.c |  2 +-\n 3 files changed, 53 insertions(+), 9 deletions(-)\n\ndiff --git a/table.c b/table.c\nindex 15600e117f..d085f2a098 100644\n--- a/table.c\n+++ b/table.c\n@@ -25,15 +25,44 @@ static void build_table_border(struct strbuf *buf, int cols)\n \n static void build_table_entry(struct strbuf *buf, char *entry, int cols)\n {\n+\tint len = strlen(entry);\n+\tsize_t col_width = (cols / 3) - 5; /* subtract for padding */\n+\n \tstrbuf_reset(buf);\n-\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2);\n+\n+\t/* Trim equally from both sides if it doesn't fit in column */\n+\tif (len > col_width) {\n+\t\tstruct strbuf start_buf = STRBUF_INIT;\n+\t\tstruct strbuf end_buf = STRBUF_INIT;\n+\t\tstruct strbuf entry_buf = STRBUF_INIT;\n+\n+\t\tstrbuf_addstr(&start_buf, entry);\n+\t\tstrbuf_addstr(&end_buf, entry);\n+\n+\t\tstrbuf_remove(&start_buf, col_width / 2, len - col_width / 2);\n+\t\tstrbuf_remove(&end_buf, 0, len - col_width / 2);\n+\n+\t\tstrbuf_addstr(&entry_buf, start_buf.buf);\n+\t\tstrbuf_addstr(&entry_buf, \"...\");\n+\t\tstrbuf_addstr(&entry_buf, end_buf.buf);\n+\n+\t\tentry = strbuf_detach(&entry_buf, &col_width);\n+\t\tlen = strlen(entry);\n+\n+\t\tstrbuf_release(&start_buf);\n+\t\tstrbuf_release(&end_buf);\n+\t\tstrbuf_release(&entry_buf);\n+\t}\n+\n+\tstrbuf_addchars(buf, ' ', (cols / 3 - len - 1) / 2); /* left padding */\n \tstrbuf_addstr(buf, entry);\n \n-\t/* Bump right padding if entry length is odd */\n-\tif (!(strlen(entry) % 2))\n-\t\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2 + 1);\n+\t/* right padding */\n+\tif (!(len % 2))\n+\t\t/* Bump right padding if entry length is odd */\n+\t\tstrbuf_addchars(buf, ' ', (cols / 3 - len - 1) / 2 + 1);\n \telse\n-\t\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2);\n+\t\tstrbuf_addchars(buf, ' ', (cols / 3 - len - 1) / 2);\n }\n \n static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct wt_status *s)\n@@ -47,7 +76,7 @@ static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, stru\n \tprintf(_(\"|\\n\"));\n }\n \n-void print_noob_status(struct wt_status *s)\n+void print_noob_status(struct wt_status *s, int add_advice)\n {\n \tstruct winsize w;\n \tint cols;\n@@ -66,14 +95,29 @@ void print_noob_status(struct wt_status *s)\n \t\tcols -= 1;\n \t}\n \n+\t/* Draw table header */\n \tbuild_table_border(&table_border, cols);\n \tbuild_table_entry(&table_col_entry_1, \"Untracked files\", cols);\n \tbuild_table_entry(&table_col_entry_2, \"Unstaged changes\", cols);\n \tbuild_table_entry(&table_col_entry_3, \"Staging area\", cols);\n \n-\t/* Draw table header */\n \tprintf(_(\"%s\\n\"), table_border.buf);\n \tprintf(_(\"|%s|%s|%s|\\n\"), table_col_entry_1.buf, table_col_entry_2.buf, table_col_entry_3.buf);\n+\n+\tif (add_advice) {\n+\t\tbuild_table_entry(&table_col_entry_1, \"(stage: git add <file>)\", cols);\n+\t\tbuild_table_entry(&table_col_entry_2, \"(stage: git add <file>)\", cols);\n+\t\tbuild_table_entry(&table_col_entry_3, \"(unstage: git restore --staged <file>)\", cols);\n+\n+\t\tprintf(_(\"|%s|%s|%s|\\n\"), table_col_entry_1.buf, table_col_entry_2.buf, table_col_entry_3.buf);\n+\n+\t\tbuild_table_entry(&table_col_entry_1, \"\", cols);\n+\t\tbuild_table_entry(&table_col_entry_2, \"(discard: git restore --staged <file>)\", cols);\n+\t\tbuild_table_entry(&table_col_entry_3, \"\", cols);\n+\n+\t\tprintf(_(\"|%s|%s|%s|\\n\"), table_col_entry_1.buf, table_col_entry_2.buf, table_col_entry_3.buf);\n+\t}\n+\n \tprintf(_(\"%s\\n\"), table_border.buf);\n \n \t/* Draw table body */\ndiff --git a/table.h b/table.h\nindex c9e8c386de..5dff7162a4 100644\n--- a/table.h\n+++ b/table.h\n@@ -1,6 +1,6 @@\n #ifndef TABLE_H\n #define TABLE_H\n \n-void print_noob_status(struct wt_status *s);\n+void print_noob_status(struct wt_status *s, int i);\n \n #endif /* TABLE_H */\ndiff --git a/wt-status.c b/wt-status.c\nindex 712807aa8f..b5899dcc98 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -2149,7 +2149,7 @@ static void wt_noobstatus_print(struct wt_status *s)\n \t\twt_longstatus_print_tracking(s);\n \t}\n \n-\tprint_noob_status(s);\n+\tprint_noob_status(s, 0);\n }\n \n static void wt_porcelain_print(struct wt_status *s)\n-- \n2.42.0.404.g2bcc23f3db\n\n"},{"id":"483941","messageId":"20231026224615.675172-5-jacob@initialcommit.io","threadId":"60408","inReplyTo":"20231026224615.675172-1-jacob@initialcommit.io","subject":"[RFC PATCH v2 4/6] add: set unique color for noob mode arrows","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-26T22:46:13Z","receivedAt":"2023-10-26T22:46:47Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"Signed-off-by: Jacob Stopak <jacob@initialcommit.io>\n---\n table.c     | 64 +++++++++++++++++++++++++++++++----------------------\n wt-status.c |  1 +\n wt-status.h |  1 +\n 3 files changed, 39 insertions(+), 27 deletions(-)\n\ndiff --git a/table.c b/table.c\nindex 527e38c07d..d29b311440 100644\n--- a/table.c\n+++ b/table.c\n@@ -5,6 +5,7 @@\n #include \"wt-status.h\"\n #include \"config.h\"\n #include \"string-list.h\"\n+#include \"color.h\"\n #include \"sys/ioctl.h\"\n \n static const char *color(int slot, struct wt_status *s)\n@@ -26,7 +27,7 @@ static void build_table_border(struct strbuf *buf, int cols)\n static void build_table_entry(struct strbuf *buf, char *entry, int cols)\n {\n \tint len = strlen(entry);\n-\tsize_t col_width = (cols / 3) - 5; /* subtract for padding */\n+\tsize_t col_width = (cols / 3) - 9; /* subtract for padding */\n \n \tstrbuf_reset(buf);\n \n@@ -65,52 +66,51 @@ static void build_table_entry(struct strbuf *buf, char *entry, int cols)\n \t\tstrbuf_addchars(buf, ' ', (cols / 3 - len - 1) / 2);\n }\n \n-static void add_arrow_to_entry(struct strbuf *buf, int add_after_entry)\n+static void build_arrow(struct strbuf *buf, struct strbuf* arrow, int add_after_entry)\n {\n \tstruct strbuf empty = STRBUF_INIT;\n \tstruct strbuf trimmed = STRBUF_INIT;\n-\tstruct strbuf holder = STRBUF_INIT;\n \tint len = strlen(buf->buf);\n \n+\tstrbuf_reset(arrow);\n \tstrbuf_addstr(&trimmed, buf->buf);\n \tstrbuf_trim(&trimmed);\n \n \tif (!strbuf_cmp(&trimmed, &empty) && !add_after_entry) {\n \t\tstrbuf_reset(buf);\n-\t\tstrbuf_addchars(buf, '-', len + 1);\n+\t\tstrbuf_addchars(arrow, '-', len + 1);\n \t} else if (add_after_entry) {\n \t\tstrbuf_rtrim(buf);\n-\t\tstrbuf_addchars(buf, ' ', 1);\n-\t\tstrbuf_addchars(buf, '-', len - strlen(buf->buf) + 1);\n+\t\tstrbuf_addchars(arrow, ' ', 1);\n+\t\tstrbuf_addchars(arrow, '-', len - strlen(buf->buf) + 1);\n \t} else if (!add_after_entry) {\n \t\tstrbuf_ltrim(buf);\n-\t\tstrbuf_addchars(&holder, '-', len - strlen(buf->buf) - 2);\n-\t\tstrbuf_addchars(&holder, '>', 1);\n-\t\tstrbuf_addchars(&holder, ' ', 1);\n-\t\tstrbuf_addstr(&holder, buf->buf);\n-\t\tstrbuf_reset(buf);\n-\t\tstrbuf_addstr(buf, holder.buf);\n+\t\tstrbuf_addchars(arrow, '-', len - strlen(buf->buf) - 3);\n+\t\tstrbuf_addchars(arrow, '>', 1);\n+\t\tstrbuf_addchars(arrow, ' ', 1);\n \t}\n }\n \n-static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct wt_status *s, int hide_pipe)\n+static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct strbuf *arrow1, struct strbuf *arrow2, struct strbuf *arrow3, struct wt_status *s, int hide_pipe)\n {\n \tprintf(_(\"|\"));\n \tcolor_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), \"%s\", buf1->buf);\n+\tif (strlen(arrow1->buf) > 0)\n+\t\tcolor_fprintf(s->fp, color(WT_STATUS_ARROW, s), \"%s\", arrow1->buf);\n \tif (hide_pipe != 1 && hide_pipe != 3)\n \t\tprintf(_(\"|\"));\n \tcolor_fprintf(s->fp, color(WT_STATUS_CHANGED, s), \"%s\", buf2->buf);\n+\tif (strlen(arrow2->buf) > 0)\n+\t\tcolor_fprintf(s->fp, color(WT_STATUS_ARROW, s), \"%s\", arrow2->buf);\n \tif (hide_pipe != 2 && hide_pipe != 3)\n \t\tprintf(_(\"|\"));\n+\tif (strlen(arrow3->buf) > 0) {\n+\t\tcolor_fprintf(s->fp, color(WT_STATUS_ARROW, s), \"%s\", arrow3->buf);\n+\t}\n \tcolor_fprintf(s->fp, color(WT_STATUS_UPDATED, s), \"%s\", buf3->buf);\n \tprintf(_(\"|\\n\"));\n }\n \n-static void print_table_body_line_(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct wt_status *s)\n-{\n-\tprint_table_body_line(buf1, buf2, buf3, s, 0);\n-}\n-\n void print_noob_status(struct wt_status *s, int advice)\n {\n \tstruct winsize w;\n@@ -119,6 +119,9 @@ void print_noob_status(struct wt_status *s, int advice)\n \tstruct strbuf table_col_entry_1 = STRBUF_INIT;\n \tstruct strbuf table_col_entry_2 = STRBUF_INIT;\n \tstruct strbuf table_col_entry_3 = STRBUF_INIT;\n+\tstruct strbuf arrow_1 = STRBUF_INIT;\n+\tstruct strbuf arrow_2 = STRBUF_INIT;\n+\tstruct strbuf arrow_3 = STRBUF_INIT;\n \tstruct string_list_item *item, *item2;\n \n \t/* Get terminal width */\n@@ -170,17 +173,21 @@ void print_noob_status(struct wt_status *s, int advice)\n \t\t\tstrbuf_addstr(&buf_2, item2->string);\n \t\t\tif (!strbuf_cmp(&buf_1, &buf_2)) {\n \t\t\t\tbuild_table_entry(&table_col_entry_3, buf_1.buf, cols);\n-\t\t\t\tadd_arrow_to_entry(&table_col_entry_1, 1);\n-\t\t\t\tadd_arrow_to_entry(&table_col_entry_2, 0);\n-\t\t\t\tadd_arrow_to_entry(&table_col_entry_3, 0);\n+\t\t\t\tbuild_arrow(&table_col_entry_1, &arrow_1, 1);\n+\t\t\t\tbuild_arrow(&table_col_entry_2, &arrow_2, 0);\n+\t\t\t\tbuild_arrow(&table_col_entry_3, &arrow_3, 0);\n \t\t\t\tis_arrow = 1;\n \t\t\t}\n \t\t}\n \n \t\tif (!is_arrow)\n-\t\t\tprint_table_body_line_(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\t\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, &arrow_1, &arrow_2, &arrow_3, s, 0);\n \t\telse\n-\t\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s, 3);\n+\t\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, &arrow_1, &arrow_2, &arrow_3, s, 3);\n+\n+\t\tstrbuf_reset(&arrow_1);\n+\t\tstrbuf_reset(&arrow_2);\n+\t\tstrbuf_reset(&arrow_3);\n \t}\n \n \tfor_each_string_list_item(item, &s->change) {\n@@ -203,8 +210,8 @@ void print_noob_status(struct wt_status *s, int advice)\n \t\t\t\tstrbuf_addstr(&buf_2, item2->string);\n \t\t\t\tif (!strbuf_cmp(&buf_1, &buf_2)) {\n \t\t\t\t\tbuild_table_entry(&table_col_entry_3, buf_1.buf, cols);\n-\t\t\t\t\tadd_arrow_to_entry(&table_col_entry_2, 1);\n-\t\t\t\t\tadd_arrow_to_entry(&table_col_entry_3, 0);\n+\t\t\t\t\tbuild_arrow(&table_col_entry_2, &arrow_2, 1);\n+\t\t\t\t\tbuild_arrow(&table_col_entry_3, &arrow_3, 0);\n \t\t\t\t\tis_arrow = 1;\n \t\t\t\t}\n \t\t\t}\n@@ -215,9 +222,12 @@ void print_noob_status(struct wt_status *s, int advice)\n \t\t}\n \n \t\tif (!is_arrow)\n-\t\t\tprint_table_body_line_(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\t\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, &arrow_1, &arrow_2, &arrow_3, s, 0);\n \t\telse\n-\t\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s, 2);\n+\t\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, &arrow_1, &arrow_2, &arrow_3, s, 2);\n+\t\tstrbuf_reset(&arrow_1);\n+\t\tstrbuf_reset(&arrow_2);\n+\t\tstrbuf_reset(&arrow_3);\n \t}\n \t\n \tif (!s->untracked.nr && !s->change.nr) {\ndiff --git a/wt-status.c b/wt-status.c\nindex 969f79f441..1332d07dba 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -49,6 +49,7 @@ static char default_wt_status_colors[][COLOR_MAXLEN] = {\n \tGIT_COLOR_GREEN,  /* WT_STATUS_LOCAL_BRANCH */\n \tGIT_COLOR_RED,    /* WT_STATUS_REMOTE_BRANCH */\n \tGIT_COLOR_NIL,    /* WT_STATUS_ONBRANCH */\n+\tGIT_COLOR_CYAN,   /* WT_STATUS_ARROW */\n };\n \n static const char *color(int slot, struct wt_status *s)\ndiff --git a/wt-status.h b/wt-status.h\nindex 64551f3a75..7b883fd476 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -19,6 +19,7 @@ enum color_wt_status {\n \tWT_STATUS_LOCAL_BRANCH,\n \tWT_STATUS_REMOTE_BRANCH,\n \tWT_STATUS_ONBRANCH,\n+\tWT_STATUS_ARROW,\n \tWT_STATUS_MAXSLOT\n };\n \n-- \n2.42.0.404.g2bcc23f3db\n\n"},{"id":"483942","messageId":"20231026224615.675172-4-jacob@initialcommit.io","threadId":"60408","inReplyTo":"20231026224615.675172-1-jacob@initialcommit.io","subject":"[RFC PATCH v2 3/6] add: implement noob mode","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-26T22:46:12Z","receivedAt":"2023-10-26T22:46:47Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"Signed-off-by: Jacob Stopak <jacob@initialcommit.io>\n---\n Makefile         |   1 +\n builtin/add.c    |  47 ++++++++---\n builtin/commit.c | 163 +-------------------------------------\n commit.c         |   2 +\n noob.c           | 198 +++++++++++++++++++++++++++++++++++++++++++++++\n noob.h           |  21 +++++\n read-cache-ll.h  |   9 ++-\n read-cache.c     |  32 ++++++--\n table.c          |  92 +++++++++++++++++++---\n wt-status.c      |   1 +\n wt-status.h      |   1 +\n 11 files changed, 381 insertions(+), 186 deletions(-)\n create mode 100644 noob.c\n create mode 100644 noob.h\n\ndiff --git a/Makefile b/Makefile\nindex a7399ca8f0..78acfaf14d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1070,6 +1070,7 @@ LIB_OBJS += name-hash.o\n LIB_OBJS += negotiator/default.o\n LIB_OBJS += negotiator/noop.o\n LIB_OBJS += negotiator/skipping.o\n+LIB_OBJS += noob.o\n LIB_OBJS += notes-cache.o\n LIB_OBJS += notes-merge.o\n LIB_OBJS += notes-utils.o\ndiff --git a/builtin/add.c b/builtin/add.c\nindex c27254a5cd..dbb99d179e 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -27,6 +27,9 @@\n #include \"strvec.h\"\n #include \"submodule.h\"\n #include \"add-interactive.h\"\n+#include \"wt-status.h\"\n+#include \"commit.h\"\n+#include \"noob.h\"\n \n static const char * const builtin_add_usage[] = {\n \tN_(\"git add [<options>] [--] <pathspec>...\"),\n@@ -322,7 +325,7 @@ static void check_embedded_repo(const char *path)\n \tstrbuf_release(&name);\n }\n \n-static int add_files(struct dir_struct *dir, int flags)\n+static int add_files(struct dir_struct *dir, int flags, struct wt_status *status)\n {\n \tint i, exit_status = 0;\n \tstruct string_list matched_sparse_paths = STRING_LIST_INIT_NODUP;\n@@ -345,7 +348,7 @@ static int add_files(struct dir_struct *dir, int flags)\n \t\t\t\t\t   dir->entries[i]->name);\n \t\t\tcontinue;\n \t\t}\n-\t\tif (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {\n+\t\tif (add_file_to_index_with_status(&the_index, dir->entries[i]->name, flags, status)) {\n \t\t\tif (!ignore_add_errors)\n \t\t\t\tdie(_(\"adding files failed\"));\n \t\t\texit_status = 1;\n@@ -374,8 +377,14 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \tint require_pathspec;\n \tchar *seen = NULL;\n \tstruct lock_file lock_file = LOCK_INIT;\n-\n+\tstruct wt_status status;\n+\tunsigned int progress_flag = 0;\n+\t\n+\twt_status_prepare(the_repository, &status);\n \tgit_config(add_config, NULL);\n+\tgit_config(git_status_config, &status);\n+\tfinalize_deferred_config(&status);\n+\tstatus.status_format = status_format;\n \n \targc = parse_options(argc, argv, prefix, builtin_add_options,\n \t\t\t  builtin_add_usage, PARSE_OPT_KEEP_ARGV0);\n@@ -459,7 +468,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \t\t (intent_to_add ? ADD_CACHE_INTENT : 0) |\n \t\t (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |\n \t\t (!(addremove || take_worktree_changes)\n-\t\t  ? ADD_CACHE_IGNORE_REMOVAL : 0));\n+\t\t  ? ADD_CACHE_IGNORE_REMOVAL : 0) |\n+\t\t (status.status_format == STATUS_FORMAT_NOOB ? ADD_CACHE_FORMAT_NOOB : 0));\n \n \tif (repo_read_index_preload(the_repository, &pathspec, 0) < 0)\n \t\tdie(_(\"index file corrupt\"));\n@@ -551,15 +561,32 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \n \tbegin_odb_transaction();\n \n-\tif (add_renormalize)\n+\tif (status.status_format == STATUS_FORMAT_NOOB) {\n+\t\t/* Read index and populate status */\n+\t\trepo_read_index(the_repository);\n+\t\trefresh_index(&the_index,\n+\t\t\t      REFRESH_QUIET|REFRESH_UNMERGED|progress_flag,\n+\t\t\t      &status.pathspec, NULL, NULL);\n+\t\tstatus.show_branch = 0;\n+\t\twt_status_collect(&status);\n+\t}\n+\n+\tif (add_renormalize) {\n \t\texit_status |= renormalize_tracked_files(&pathspec, flags);\n-\telse\n-\t\texit_status |= add_files_to_cache(the_repository, prefix,\n+\t} else {\n+\t\texit_status |= add_files_to_cache_with_status(the_repository, prefix,\n \t\t\t\t\t\t  &pathspec, include_sparse,\n-\t\t\t\t\t\t  flags);\n+\t\t\t\t\t\t  flags, &status);\n+\t}\n \n-\tif (add_new_files)\n-\t\texit_status |= add_files(&dir, flags);\n+\tif (add_new_files) {\n+\t\texit_status |= add_files(&dir, flags, &status);\n+\t}\n+\n+\tif (status.status_format == STATUS_FORMAT_NOOB) {\n+\t\twt_status_print(&status);\n+\t\twt_status_collect_free_buffers(&status);\n+\t}\n \n \tif (chmod_arg && pathspec.nr)\n \t\texit_status |= chmod_pathspec(&pathspec, chmod_arg[0], show_only);\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 880c42f5b7..3f816c117d 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -46,6 +46,7 @@\n #include \"commit-reach.h\"\n #include \"commit-graph.h\"\n #include \"pretty.h\"\n+#include \"noob.h\"\n \n static const char * const builtin_commit_usage[] = {\n \tN_(\"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\\n\"\n@@ -148,8 +149,6 @@ static int use_editor = 1, include_status = 1;\n static int have_option_m;\n static struct strbuf message = STRBUF_INIT;\n \n-static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;\n-\n static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)\n {\n \tBUG_ON_OPT_NEG(unset);\n@@ -310,7 +309,7 @@ static void add_remove_files(struct string_list *list)\n \t\t\tcontinue;\n \n \t\tif (!lstat(p->string, &st)) {\n-\t\t\tif (add_to_index(&the_index, p->string, &st, 0))\n+\t\t\tif (add_file_to_index(&the_index, p->string, 0))\n \t\t\t\tdie(_(\"updating files failed\"));\n \t\t} else\n \t\t\tremove_file_from_index(&the_index, p->string);\n@@ -1196,59 +1195,6 @@ static const char *read_commit_message(const char *name)\n \treturn repo_logmsg_reencode(the_repository, commit, NULL, out_enc);\n }\n \n-/*\n- * Enumerate what needs to be propagated when --porcelain\n- * is not in effect here.\n- */\n-static struct status_deferred_config {\n-\tenum wt_status_format status_format;\n-\tint show_branch;\n-\tenum ahead_behind_flags ahead_behind;\n-} status_deferred_config = {\n-\tSTATUS_FORMAT_UNSPECIFIED,\n-\t-1, /* unspecified */\n-\tAHEAD_BEHIND_UNSPECIFIED,\n-};\n-\n-static void finalize_deferred_config(struct wt_status *s)\n-{\n-\tint use_deferred_config = (status_format != STATUS_FORMAT_PORCELAIN &&\n-\t\t\t\t   status_format != STATUS_FORMAT_PORCELAIN_V2 &&\n-\t\t\t\t   !s->null_termination);\n-\n-\tif (s->null_termination) {\n-\t\tif (status_format == STATUS_FORMAT_NONE ||\n-\t\t    status_format == STATUS_FORMAT_UNSPECIFIED)\n-\t\t\tstatus_format = STATUS_FORMAT_PORCELAIN;\n-\t\telse if (status_format == STATUS_FORMAT_LONG)\n-\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--long\", \"-z\");\n-\t}\n-\n-\tif (use_deferred_config && status_format == STATUS_FORMAT_UNSPECIFIED)\n-\t\tstatus_format = status_deferred_config.status_format;\n-\tif (status_format == STATUS_FORMAT_UNSPECIFIED)\n-\t\tstatus_format = STATUS_FORMAT_NONE;\n-\n-\tif (use_deferred_config && s->show_branch < 0)\n-\t\ts->show_branch = status_deferred_config.show_branch;\n-\tif (s->show_branch < 0)\n-\t\ts->show_branch = 0;\n-\n-\t/*\n-\t * If the user did not give a \"--[no]-ahead-behind\" command\n-\t * line argument *AND* we will print in a human-readable format\n-\t * (short, long etc.) then we inherit from the status.aheadbehind\n-\t * config setting.  In all other cases (and porcelain V[12] formats\n-\t * in particular), we inherit _FULL for backwards compatibility.\n-\t */\n-\tif (use_deferred_config &&\n-\t    s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)\n-\t\ts->ahead_behind_flags = status_deferred_config.ahead_behind;\n-\n-\tif (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)\n-\t\ts->ahead_behind_flags = AHEAD_BEHIND_FULL;\n-}\n-\n static void check_fixup_reword_options(int argc, const char *argv[]) {\n \tif (whence != FROM_COMMIT) {\n \t\tif (whence == FROM_MERGE)\n@@ -1399,111 +1345,6 @@ static int dry_run_commit(const char **argv, const char *prefix,\n \n define_list_config_array_extra(color_status_slots, {\"added\"});\n \n-static int parse_status_slot(const char *slot)\n-{\n-\tif (!strcasecmp(slot, \"added\"))\n-\t\treturn WT_STATUS_UPDATED;\n-\n-\treturn LOOKUP_CONFIG(color_status_slots, slot);\n-}\n-\n-static int git_status_config(const char *k, const char *v,\n-\t\t\t     const struct config_context *ctx, void *cb)\n-{\n-\tstruct wt_status *s = cb;\n-\tconst char *slot_name;\n-\n-\tif (starts_with(k, \"column.\"))\n-\t\treturn git_column_config(k, v, \"status\", &s->colopts);\n-\tif (!strcmp(k, \"status.submodulesummary\")) {\n-\t\tint is_bool;\n-\t\ts->submodule_summary = git_config_bool_or_int(k, v, ctx->kvi,\n-\t\t\t\t\t\t\t      &is_bool);\n-\t\tif (is_bool && s->submodule_summary)\n-\t\t\ts->submodule_summary = -1;\n-\t\treturn 0;\n-\t}\n-\tif (!strcmp(k, \"status.short\")) {\n-\t\tif (git_config_bool(k, v))\n-\t\t\tstatus_deferred_config.status_format = STATUS_FORMAT_SHORT;\n-\t\telse\n-\t\t\tstatus_deferred_config.status_format = STATUS_FORMAT_NONE;\n-\t\treturn 0;\n-\t}\n-\tif (!strcmp(k, \"status.noob\")) {\n-\t\tif (git_config_bool(k, v))\n-\t\t\tstatus_deferred_config.status_format = STATUS_FORMAT_NOOB;\n-\t\telse\n-\t\t\tstatus_deferred_config.status_format = STATUS_FORMAT_NONE;\n-\t\treturn 0;\n-\t}\n-\tif (!strcmp(k, \"status.branch\")) {\n-\t\tstatus_deferred_config.show_branch = git_config_bool(k, v);\n-\t\treturn 0;\n-\t}\n-\tif (!strcmp(k, \"status.aheadbehind\")) {\n-\t\tstatus_deferred_config.ahead_behind = git_config_bool(k, v);\n-\t\treturn 0;\n-\t}\n-\tif (!strcmp(k, \"status.showstash\")) {\n-\t\ts->show_stash = git_config_bool(k, v);\n-\t\treturn 0;\n-\t}\n-\tif (!strcmp(k, \"status.color\") || !strcmp(k, \"color.status\")) {\n-\t\ts->use_color = git_config_colorbool(k, v);\n-\t\treturn 0;\n-\t}\n-\tif (!strcmp(k, \"status.displaycommentprefix\")) {\n-\t\ts->display_comment_prefix = git_config_bool(k, v);\n-\t\treturn 0;\n-\t}\n-\tif (skip_prefix(k, \"status.color.\", &slot_name) ||\n-\t    skip_prefix(k, \"color.status.\", &slot_name)) {\n-\t\tint slot = parse_status_slot(slot_name);\n-\t\tif (slot < 0)\n-\t\t\treturn 0;\n-\t\tif (!v)\n-\t\t\treturn config_error_nonbool(k);\n-\t\treturn color_parse(v, s->color_palette[slot]);\n-\t}\n-\tif (!strcmp(k, \"status.relativepaths\")) {\n-\t\ts->relative_paths = git_config_bool(k, v);\n-\t\treturn 0;\n-\t}\n-\tif (!strcmp(k, \"status.showuntrackedfiles\")) {\n-\t\tif (!v)\n-\t\t\treturn config_error_nonbool(k);\n-\t\telse if (!strcmp(v, \"no\"))\n-\t\t\ts->show_untracked_files = SHOW_NO_UNTRACKED_FILES;\n-\t\telse if (!strcmp(v, \"normal\"))\n-\t\t\ts->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;\n-\t\telse if (!strcmp(v, \"all\"))\n-\t\t\ts->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;\n-\t\telse\n-\t\t\treturn error(_(\"Invalid untracked files mode '%s'\"), v);\n-\t\treturn 0;\n-\t}\n-\tif (!strcmp(k, \"diff.renamelimit\")) {\n-\t\tif (s->rename_limit == -1)\n-\t\t\ts->rename_limit = git_config_int(k, v, ctx->kvi);\n-\t\treturn 0;\n-\t}\n-\tif (!strcmp(k, \"status.renamelimit\")) {\n-\t\ts->rename_limit = git_config_int(k, v, ctx->kvi);\n-\t\treturn 0;\n-\t}\n-\tif (!strcmp(k, \"diff.renames\")) {\n-\t\tif (s->detect_rename == -1)\n-\t\t\ts->detect_rename = git_config_rename(k, v);\n-\t\treturn 0;\n-\t}\n-\tif (!strcmp(k, \"status.renames\")) {\n-\t\ts->detect_rename = git_config_rename(k, v);\n-\t\treturn 0;\n-\t}\n-\treturn git_diff_ui_config(k, v, ctx, NULL);\n-}\n-\n int cmd_status(int argc, const char **argv, const char *prefix)\n {\n \tstatic int no_renames = -1;\ndiff --git a/commit.c b/commit.c\nindex b3223478bc..c08faf48fd 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -28,6 +28,8 @@\n #include \"shallow.h\"\n #include \"tree.h\"\n #include \"hook.h\"\n+#include \"column.h\"\n+#include \"config.h\"\n \n static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);\n \ndiff --git a/noob.c b/noob.c\nnew file mode 100644\nindex 0000000000..680d461698\n--- /dev/null\n+++ b/noob.c\n@@ -0,0 +1,198 @@\n+#include \"git-compat-util.h\"\n+#include \"tag.h\"\n+#include \"commit.h\"\n+#include \"commit-graph.h\"\n+#include \"environment.h\"\n+#include \"gettext.h\"\n+#include \"hex.h\"\n+#include \"repository.h\"\n+#include \"object-name.h\"\n+#include \"object-store-ll.h\"\n+#include \"pkt-line.h\"\n+#include \"utf8.h\"\n+#include \"diff.h\"\n+#include \"revision.h\"\n+#include \"notes.h\"\n+#include \"alloc.h\"\n+#include \"gpg-interface.h\"\n+#include \"mergesort.h\"\n+#include \"commit-slab.h\"\n+#include \"prio-queue.h\"\n+#include \"hash-lookup.h\"\n+#include \"wt-status.h\"\n+#include \"advice.h\"\n+#include \"refs.h\"\n+#include \"commit-reach.h\"\n+#include \"run-command.h\"\n+#include \"setup.h\"\n+#include \"shallow.h\"\n+#include \"tree.h\"\n+#include \"hook.h\"\n+#include \"column.h\"\n+#include \"config.h\"\n+#include \"noob.h\"\n+\n+static const char *color_status_slots[] = {\n+\t[WT_STATUS_HEADER]\t  = \"header\",\n+\t[WT_STATUS_UPDATED]\t  = \"updated\",\n+\t[WT_STATUS_CHANGED]\t  = \"changed\",\n+\t[WT_STATUS_UNTRACKED]\t  = \"untracked\",\n+\t[WT_STATUS_NOBRANCH]\t  = \"noBranch\",\n+\t[WT_STATUS_UNMERGED]\t  = \"unmerged\",\n+\t[WT_STATUS_LOCAL_BRANCH]  = \"localBranch\",\n+\t[WT_STATUS_REMOTE_BRANCH] = \"remoteBranch\",\n+\t[WT_STATUS_ONBRANCH]\t  = \"branch\",\n+};\n+\n+enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;\n+\n+struct status_deferred_config status_deferred_config = {\n+\tSTATUS_FORMAT_UNSPECIFIED,\n+\t-1, /* unspecified */\n+\tAHEAD_BEHIND_UNSPECIFIED,\n+};\n+\n+int parse_status_slot(const char *slot)\n+{\n+\tif (!strcasecmp(slot, \"added\"))\n+\t\treturn WT_STATUS_UPDATED;\n+\n+\treturn LOOKUP_CONFIG(color_status_slots, slot);\n+}\n+\n+int git_status_config(const char *k, const char *v,\n+\t\t      const struct config_context *ctx, void *cb)\n+{\n+\tstruct wt_status *s = cb;\n+\tconst char *slot_name;\n+\n+\tif (starts_with(k, \"column.\"))\n+\t\treturn git_column_config(k, v, \"status\", &s->colopts);\n+\tif (!strcmp(k, \"status.submodulesummary\")) {\n+\t\tint is_bool;\n+\t\ts->submodule_summary = git_config_bool_or_int(k, v, ctx->kvi,\n+\t\t\t\t\t\t\t      &is_bool);\n+\t\tif (is_bool && s->submodule_summary)\n+\t\t\ts->submodule_summary = -1;\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(k, \"status.short\")) {\n+\t\tif (git_config_bool(k, v))\n+\t\t\tstatus_deferred_config.status_format = STATUS_FORMAT_SHORT;\n+\t\telse\n+\t\t\tstatus_deferred_config.status_format = STATUS_FORMAT_NONE;\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(k, \"status.noob\")) {\n+\t\tif (git_config_bool(k, v))\n+\t\t\tstatus_deferred_config.status_format = STATUS_FORMAT_NOOB;\n+\t\telse\n+\t\t\tstatus_deferred_config.status_format = STATUS_FORMAT_NONE;\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(k, \"status.branch\")) {\n+\t\tstatus_deferred_config.show_branch = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(k, \"status.aheadbehind\")) {\n+\t\tstatus_deferred_config.ahead_behind = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(k, \"status.showstash\")) {\n+\t\ts->show_stash = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(k, \"status.color\") || !strcmp(k, \"color.status\")) {\n+\t\ts->use_color = git_config_colorbool(k, v);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(k, \"status.displaycommentprefix\")) {\n+\t\ts->display_comment_prefix = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n+\tif (skip_prefix(k, \"status.color.\", &slot_name) ||\n+\t\tskip_prefix(k, \"color.status.\", &slot_name)) {\n+\t\tint slot = parse_status_slot(slot_name);\n+\t\tif (slot < 0)\n+\t\t       return 0;\n+\t\tif (!v)\n+\t\t       return config_error_nonbool(k);\n+\t\treturn color_parse(v, s->color_palette[slot]);\n+\t}\n+\tif (!strcmp(k, \"status.relativepaths\")) {\n+\t\ts->relative_paths = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(k, \"status.showuntrackedfiles\")) {\n+\t\tif (!v)\n+\t\t       return config_error_nonbool(k);\n+\t\telse if (!strcmp(v, \"no\"))\n+\t\t       s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;\n+\t\telse if (!strcmp(v, \"normal\"))\n+\t\t       s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;\n+\t\telse if (!strcmp(v, \"all\"))\n+\t\t       s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;\n+\t\telse\n+\t\t       return error(_(\"Invalid untracked files mode '%s'\"), v);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(k, \"diff.renamelimit\")) {\n+\t\tif (s->rename_limit == -1)\n+\t\t       s->rename_limit = git_config_int(k, v, ctx->kvi);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(k, \"status.renamelimit\")) {\n+\t\ts->rename_limit = git_config_int(k, v, ctx->kvi);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(k, \"diff.renames\")) {\n+\t\tif (s->detect_rename == -1)\n+\t\t       s->detect_rename = git_config_rename(k, v);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(k, \"status.renames\")) {\n+\t\ts->detect_rename = git_config_rename(k, v);\n+\t\treturn 0;\n+\t}\n+\treturn git_diff_ui_config(k, v, ctx, NULL);\n+}\n+\n+void finalize_deferred_config(struct wt_status *s)\n+{\n+\tint use_deferred_config = (status_format != STATUS_FORMAT_PORCELAIN &&\n+\t\t\t\t   status_format != STATUS_FORMAT_PORCELAIN_V2 &&\n+\t\t\t\t   !s->null_termination);\n+\n+\tif (s->null_termination) {\n+\t\tif (status_format == STATUS_FORMAT_NONE ||\n+\t\t    status_format == STATUS_FORMAT_UNSPECIFIED)\n+\t\t\tstatus_format = STATUS_FORMAT_PORCELAIN;\n+\t\telse if (status_format == STATUS_FORMAT_LONG)\n+\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--long\", \"-z\");\n+\t}\n+\n+\tif (use_deferred_config && status_format == STATUS_FORMAT_UNSPECIFIED) {\n+\t\tstatus_format = status_deferred_config.status_format;\n+\t}\n+\tif (status_format == STATUS_FORMAT_UNSPECIFIED)\n+\t\tstatus_format = STATUS_FORMAT_NONE;\n+\n+\tif (use_deferred_config && s->show_branch < 0)\n+\t\ts->show_branch = status_deferred_config.show_branch;\n+\tif (s->show_branch < 0)\n+\t\ts->show_branch = 0;\n+\n+\t/*\n+\t * If the user did not give a \"--[no]-ahead-behind\" command\n+\t * line argument *AND* we will print in a human-readable format\n+\t * (short, long etc.) then we inherit from the status.aheadbehind\n+\t * config setting.  In all other cases (and porcelain V[12] formats\n+\t * in particular), we inherit _FULL for backwards compatibility.\n+\t */\n+\tif (use_deferred_config &&\n+\t    s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)\n+\t\ts->ahead_behind_flags = status_deferred_config.ahead_behind;\n+\n+\tif (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)\n+\t\ts->ahead_behind_flags = AHEAD_BEHIND_FULL;\n+}\ndiff --git a/noob.h b/noob.h\nnew file mode 100644\nindex 0000000000..d5bc073594\n--- /dev/null\n+++ b/noob.h\n@@ -0,0 +1,21 @@\n+#ifndef NOOB_H\n+#define NOOB_H\n+\n+struct status_deferred_config {\n+\tenum wt_status_format status_format;\n+\tint show_branch;\n+\tenum ahead_behind_flags ahead_behind;\n+};\n+\n+extern enum wt_status_format status_format;\n+\n+extern struct status_deferred_config status_deferred_config;\n+\n+int git_status_config(const char *k, const char *v,\n+\t\t      const struct config_context *ctx, void *cb);\n+\n+int parse_status_slot(const char *slot);\n+\n+void finalize_deferred_config(struct wt_status *s);\n+\n+#endif /* NOOB_H */\ndiff --git a/read-cache-ll.h b/read-cache-ll.h\nindex 9a1a7edc5a..302a075714 100644\n--- a/read-cache-ll.h\n+++ b/read-cache-ll.h\n@@ -4,6 +4,7 @@\n #include \"hash-ll.h\"\n #include \"hashmap.h\"\n #include \"statinfo.h\"\n+#include \"wt-status.h\"\n \n /*\n  * Basic data structures for the directory cache\n@@ -395,6 +396,7 @@ int remove_file_from_index(struct index_state *, const char *path);\n #define ADD_CACHE_IGNORE_ERRORS\t4\n #define ADD_CACHE_IGNORE_REMOVAL 8\n #define ADD_CACHE_INTENT 16\n+#define ADD_CACHE_FORMAT_NOOB 32\n /*\n  * These two are used to add the contents of the file at path\n  * to the index, marking the working tree up-to-date by storing\n@@ -404,7 +406,8 @@ int remove_file_from_index(struct index_state *, const char *path);\n  * the latter will do necessary lstat(2) internally before\n  * calling the former.\n  */\n-int add_to_index(struct index_state *, const char *path, struct stat *, int flags);\n+int add_to_index(struct index_state *, const char *path, struct stat *, int flags, struct wt_status *status);\n+int add_file_to_index_with_status(struct index_state *, const char *path, int flags, struct wt_status *status);\n int add_file_to_index(struct index_state *, const char *path, int flags);\n \n int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);\n@@ -475,6 +478,10 @@ int add_files_to_cache(struct repository *repo, const char *prefix,\n \t\t       const struct pathspec *pathspec, int include_sparse,\n \t\t       int flags);\n \n+int add_files_to_cache_with_status(struct repository *repo, const char *prefix,\n+\t\t       const struct pathspec *pathspec, int include_sparse,\n+\t\t       int flags, struct wt_status *status);\n+\n void overlay_tree_on_index(struct index_state *istate,\n \t\t\t   const char *tree_name, const char *prefix);\n \ndiff --git a/read-cache.c b/read-cache.c\nindex 080bd39713..319415430a 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -45,6 +45,8 @@\n #include \"csum-file.h\"\n #include \"promisor-remote.h\"\n #include \"hook.h\"\n+#include \"wt-status.h\"\n+#include \"string-list.h\"\n \n /* Mask for the name length in ce_flags in the on-disk index */\n \n@@ -664,7 +666,7 @@ void set_object_name_for_intent_to_add_entry(struct cache_entry *ce)\n \toidcpy(&ce->oid, &oid);\n }\n \n-int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags)\n+int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags, struct wt_status *status)\n {\n \tint namelen, was_same;\n \tmode_t st_mode = st->st_mode;\n@@ -672,6 +674,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,\n \tunsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE|CE_MATCH_RACY_IS_DIRTY;\n \tint verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND);\n \tint pretend = flags & ADD_CACHE_PRETEND;\n+\tint noob = flags & ADD_CACHE_FORMAT_NOOB;\n \tint intent_only = flags & ADD_CACHE_INTENT;\n \tint add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|\n \t\t\t  (intent_only ? ADD_CACHE_NEW_ONLY : 0));\n@@ -760,17 +763,26 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,\n \t\tdiscard_cache_entry(ce);\n \t\treturn error(_(\"unable to add '%s' to index\"), path);\n \t}\n-\tif (verbose && !was_same)\n+\tif (verbose && !was_same && !noob)\n \t\tprintf(\"add '%s'\\n\", path);\n+\tif (noob && !was_same) {\n+\t\tstring_list_insert(&status->added, path);\n+\t}\n \treturn 0;\n }\n \n-int add_file_to_index(struct index_state *istate, const char *path, int flags)\n+int add_file_to_index_with_status(struct index_state *istate, const char *path, int flags, struct wt_status *status)\n {\n \tstruct stat st;\n \tif (lstat(path, &st))\n \t\tdie_errno(_(\"unable to stat '%s'\"), path);\n-\treturn add_to_index(istate, path, &st, flags);\n+\treturn add_to_index(istate, path, &st, flags, status);\n+}\n+\n+int add_file_to_index(struct index_state *istate, const char *path, int flags)\n+{\n+\tstruct wt_status status;\n+\treturn add_file_to_index_with_status(istate, path, flags, &status);\n }\n \n struct cache_entry *make_empty_cache_entry(struct index_state *istate, size_t len)\n@@ -3872,6 +3884,7 @@ struct update_callback_data {\n \tint include_sparse;\n \tint flags;\n \tint add_errors;\n+\tstruct wt_status *status;\n };\n \n static int fix_unmerged_status(struct diff_filepair *p,\n@@ -3914,7 +3927,7 @@ static void update_callback(struct diff_queue_struct *q,\n \t\t\tdie(_(\"unexpected diff status %c\"), p->status);\n \t\tcase DIFF_STATUS_MODIFIED:\n \t\tcase DIFF_STATUS_TYPE_CHANGED:\n-\t\t\tif (add_file_to_index(data->index, path, data->flags)) {\n+\t\t\tif (add_file_to_index_with_status(data->index, path, data->flags, data->status)) {\n \t\t\t\tif (!(data->flags & ADD_CACHE_IGNORE_ERRORS))\n \t\t\t\t\tdie(_(\"updating files failed\"));\n \t\t\t\tdata->add_errors++;\n@@ -3935,6 +3948,14 @@ static void update_callback(struct diff_queue_struct *q,\n int add_files_to_cache(struct repository *repo, const char *prefix,\n \t\t       const struct pathspec *pathspec, int include_sparse,\n \t\t       int flags)\n+{\n+\tstruct wt_status status;\n+\treturn add_files_to_cache_with_status(repo, prefix, pathspec, include_sparse, flags, &status);\n+}\n+\n+int add_files_to_cache_with_status(struct repository *repo, const char *prefix,\n+\t\t       const struct pathspec *pathspec, int include_sparse,\n+\t\t       int flags, struct wt_status *status)\n {\n \tstruct update_callback_data data;\n \tstruct rev_info rev;\n@@ -3943,6 +3964,7 @@ int add_files_to_cache(struct repository *repo, const char *prefix,\n \tdata.index = repo->index;\n \tdata.include_sparse = include_sparse;\n \tdata.flags = flags;\n+\tdata.status = status;\n \n \trepo_init_revisions(repo, &rev, prefix);\n \tsetup_revisions(0, NULL, &rev, NULL);\ndiff --git a/table.c b/table.c\nindex d085f2a098..527e38c07d 100644\n--- a/table.c\n+++ b/table.c\n@@ -65,18 +65,53 @@ static void build_table_entry(struct strbuf *buf, char *entry, int cols)\n \t\tstrbuf_addchars(buf, ' ', (cols / 3 - len - 1) / 2);\n }\n \n-static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct wt_status *s)\n+static void add_arrow_to_entry(struct strbuf *buf, int add_after_entry)\n+{\n+\tstruct strbuf empty = STRBUF_INIT;\n+\tstruct strbuf trimmed = STRBUF_INIT;\n+\tstruct strbuf holder = STRBUF_INIT;\n+\tint len = strlen(buf->buf);\n+\n+\tstrbuf_addstr(&trimmed, buf->buf);\n+\tstrbuf_trim(&trimmed);\n+\n+\tif (!strbuf_cmp(&trimmed, &empty) && !add_after_entry) {\n+\t\tstrbuf_reset(buf);\n+\t\tstrbuf_addchars(buf, '-', len + 1);\n+\t} else if (add_after_entry) {\n+\t\tstrbuf_rtrim(buf);\n+\t\tstrbuf_addchars(buf, ' ', 1);\n+\t\tstrbuf_addchars(buf, '-', len - strlen(buf->buf) + 1);\n+\t} else if (!add_after_entry) {\n+\t\tstrbuf_ltrim(buf);\n+\t\tstrbuf_addchars(&holder, '-', len - strlen(buf->buf) - 2);\n+\t\tstrbuf_addchars(&holder, '>', 1);\n+\t\tstrbuf_addchars(&holder, ' ', 1);\n+\t\tstrbuf_addstr(&holder, buf->buf);\n+\t\tstrbuf_reset(buf);\n+\t\tstrbuf_addstr(buf, holder.buf);\n+\t}\n+}\n+\n+static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct wt_status *s, int hide_pipe)\n {\n \tprintf(_(\"|\"));\n \tcolor_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), \"%s\", buf1->buf);\n-\tprintf(_(\"|\"));\n+\tif (hide_pipe != 1 && hide_pipe != 3)\n+\t\tprintf(_(\"|\"));\n \tcolor_fprintf(s->fp, color(WT_STATUS_CHANGED, s), \"%s\", buf2->buf);\n-\tprintf(_(\"|\"));\n+\tif (hide_pipe != 2 && hide_pipe != 3)\n+\t\tprintf(_(\"|\"));\n \tcolor_fprintf(s->fp, color(WT_STATUS_UPDATED, s), \"%s\", buf3->buf);\n \tprintf(_(\"|\\n\"));\n }\n \n-void print_noob_status(struct wt_status *s, int add_advice)\n+static void print_table_body_line_(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct wt_status *s)\n+{\n+\tprint_table_body_line(buf1, buf2, buf3, s, 0);\n+}\n+\n+void print_noob_status(struct wt_status *s, int advice)\n {\n \tstruct winsize w;\n \tint cols;\n@@ -84,7 +119,7 @@ void print_noob_status(struct wt_status *s, int add_advice)\n \tstruct strbuf table_col_entry_1 = STRBUF_INIT;\n \tstruct strbuf table_col_entry_2 = STRBUF_INIT;\n \tstruct strbuf table_col_entry_3 = STRBUF_INIT;\n-\tstruct string_list_item *item;\n+\tstruct string_list_item *item, *item2;\n \n \t/* Get terminal width */\n \tioctl(STDOUT_FILENO, TIOCGWINSZ, &w);\n@@ -104,7 +139,7 @@ void print_noob_status(struct wt_status *s, int add_advice)\n \tprintf(_(\"%s\\n\"), table_border.buf);\n \tprintf(_(\"|%s|%s|%s|\\n\"), table_col_entry_1.buf, table_col_entry_2.buf, table_col_entry_3.buf);\n \n-\tif (add_advice) {\n+\tif (advice) {\n \t\tbuild_table_entry(&table_col_entry_1, \"(stage: git add <file>)\", cols);\n \t\tbuild_table_entry(&table_col_entry_2, \"(stage: git add <file>)\", cols);\n \t\tbuild_table_entry(&table_col_entry_3, \"(unstage: git restore --staged <file>)\", cols);\n@@ -122,14 +157,38 @@ void print_noob_status(struct wt_status *s, int add_advice)\n \n \t/* Draw table body */\n \tfor_each_string_list_item(item, &s->untracked) {\n-\t\tbuild_table_entry(&table_col_entry_1, item->string, cols);\n+\t\tstruct strbuf buf_1 = STRBUF_INIT;\n+\t\tstruct strbuf buf_2 = STRBUF_INIT;\n+\t\tint is_arrow = 0;\n+\t\tstrbuf_addstr(&buf_1, item->string);\n+\t\tbuild_table_entry(&table_col_entry_1, buf_1.buf, cols);\n \t\tbuild_table_entry(&table_col_entry_2, \"\", cols);\n \t\tbuild_table_entry(&table_col_entry_3, \"\", cols);\n-\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\n+\t\tfor_each_string_list_item(item2, &s->added) {\n+\t\t\tstrbuf_reset(&buf_2);\n+\t\t\tstrbuf_addstr(&buf_2, item2->string);\n+\t\t\tif (!strbuf_cmp(&buf_1, &buf_2)) {\n+\t\t\t\tbuild_table_entry(&table_col_entry_3, buf_1.buf, cols);\n+\t\t\t\tadd_arrow_to_entry(&table_col_entry_1, 1);\n+\t\t\t\tadd_arrow_to_entry(&table_col_entry_2, 0);\n+\t\t\t\tadd_arrow_to_entry(&table_col_entry_3, 0);\n+\t\t\t\tis_arrow = 1;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (!is_arrow)\n+\t\t\tprint_table_body_line_(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\t\telse\n+\t\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s, 3);\n \t}\n \n \tfor_each_string_list_item(item, &s->change) {\n \t\tstruct wt_status_change_data *d = item->util;\n+\t\tstruct strbuf buf_1 = STRBUF_INIT;\n+\t\tstruct strbuf buf_2 = STRBUF_INIT;\n+\t\tint is_arrow = 0;\n+\t\tstrbuf_addstr(&buf_1, item->string);\n \t\tif (d->worktree_status && d->index_status) {\n \t\t\tbuild_table_entry(&table_col_entry_1, \"\", cols);\n \t\t\tbuild_table_entry(&table_col_entry_2, item->string, cols);\n@@ -138,12 +197,27 @@ void print_noob_status(struct wt_status *s, int add_advice)\n \t\t\tbuild_table_entry(&table_col_entry_1, \"\", cols);\n \t\t\tbuild_table_entry(&table_col_entry_2, item->string, cols);\n \t\t\tbuild_table_entry(&table_col_entry_3, \"\", cols);\n+\n+\t\t\tfor_each_string_list_item(item2, &s->added) {\n+\t\t\t\tstrbuf_reset(&buf_2);\n+\t\t\t\tstrbuf_addstr(&buf_2, item2->string);\n+\t\t\t\tif (!strbuf_cmp(&buf_1, &buf_2)) {\n+\t\t\t\t\tbuild_table_entry(&table_col_entry_3, buf_1.buf, cols);\n+\t\t\t\t\tadd_arrow_to_entry(&table_col_entry_2, 1);\n+\t\t\t\t\tadd_arrow_to_entry(&table_col_entry_3, 0);\n+\t\t\t\t\tis_arrow = 1;\n+\t\t\t\t}\n+\t\t\t}\n \t\t} else if (d->index_status) {\n \t\t\tbuild_table_entry(&table_col_entry_1, \"\", cols);\n \t\t\tbuild_table_entry(&table_col_entry_2, \"\", cols);\n \t\t\tbuild_table_entry(&table_col_entry_3, item->string, cols);\n \t\t}\n-\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\n+\t\tif (!is_arrow)\n+\t\t\tprint_table_body_line_(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\t\telse\n+\t\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s, 2);\n \t}\n \t\n \tif (!s->untracked.nr && !s->change.nr) {\ndiff --git a/wt-status.c b/wt-status.c\nindex b5899dcc98..969f79f441 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -153,6 +153,7 @@ void wt_status_prepare(struct repository *r, struct wt_status *s)\n \ts->change.strdup_strings = 1;\n \ts->untracked.strdup_strings = 1;\n \ts->ignored.strdup_strings = 1;\n+\ts->added.strdup_strings = 1;\n \ts->show_branch = -1;  /* unspecified */\n \ts->show_stash = 0;\n \ts->ahead_behind_flags = AHEAD_BEHIND_UNSPECIFIED;\ndiff --git a/wt-status.h b/wt-status.h\nindex 3f08f0d72b..64551f3a75 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -142,6 +142,7 @@ struct wt_status {\n \tstruct string_list change;\n \tstruct string_list untracked;\n \tstruct string_list ignored;\n+\tstruct string_list added;\n \tuint32_t untracked_in_ms;\n };\n \n-- \n2.42.0.404.g2bcc23f3db\n\n"},{"id":"483943","messageId":"20231026224615.675172-6-jacob@initialcommit.io","threadId":"60408","inReplyTo":"20231026224615.675172-1-jacob@initialcommit.io","subject":"[RFC PATCH v2 5/6] restore: implement noob mode","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-26T22:46:14Z","receivedAt":"2023-10-26T22:46:49Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"Signed-off-by: Jacob Stopak <jacob@initialcommit.io>\n---\n builtin/checkout.c | 46 ++++++++++++++++++++++++++++-------\n read-cache-ll.h    |  1 +\n read-cache.c       |  9 ++++++-\n table.c            | 60 +++++++++++++++++++++++++++++++++++++++-------\n wt-status.h        |  1 +\n 5 files changed, 100 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f02434bc15..afc414b0b1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -41,6 +41,7 @@\n #include \"entry.h\"\n #include \"parallel-checkout.h\"\n #include \"add-interactive.h\"\n+#include \"noob.h\"\n \n static const char * const checkout_usage[] = {\n \tN_(\"git checkout [<options>] <branch>\"),\n@@ -456,7 +457,8 @@ static int checkout_worktree(const struct checkout_opts *opts,\n }\n \n static int checkout_paths(const struct checkout_opts *opts,\n-\t\t\t  const struct branch_info *new_branch_info)\n+\t\t\t  const struct branch_info *new_branch_info,\n+\t\t\t  struct wt_status *status)\n {\n \tint pos;\n \tstatic char *ps_matched;\n@@ -598,8 +600,10 @@ static int checkout_paths(const struct checkout_opts *opts,\n \tfor (pos = 0; pos < the_index.cache_nr; pos++) {\n \t\tconst struct cache_entry *ce = the_index.cache[pos];\n \t\tif (ce->ce_flags & CE_MATCHED) {\n-\t\t\tif (!ce_stage(ce))\n+\t\t\tif (!ce_stage(ce)) {\n+\t\t\t\tstring_list_insert(&status->restored, ce->name);\n \t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (opts->ignore_unmerged) {\n \t\t\t\tif (!opts->quiet)\n \t\t\t\t\twarning(_(\"path '%s' is unmerged\"), ce->name);\n@@ -621,7 +625,7 @@ static int checkout_paths(const struct checkout_opts *opts,\n \tif (opts->checkout_worktree)\n \t\terrs |= checkout_worktree(opts, new_branch_info);\n \telse\n-\t\tremove_marked_cache_entries(&the_index, 1);\n+\t\tremove_marked_cache_entries_with_status(&the_index, 1, status);\n \n \t/*\n \t * Allow updating the index when checking out from the index.\n@@ -1668,7 +1672,8 @@ static char cb_option = 'b';\n static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\t\t struct checkout_opts *opts, struct option *options,\n \t\t\t const char * const usagestr[],\n-\t\t\t struct branch_info *new_branch_info)\n+\t\t\t struct branch_info *new_branch_info,\n+\t\t\t struct wt_status *status)\n {\n \tint parseopt_flags = 0;\n \n@@ -1865,7 +1870,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t}\n \n \tif (opts->patch_mode || opts->pathspec.nr)\n-\t\treturn checkout_paths(opts, new_branch_info);\n+\t\treturn checkout_paths(opts, new_branch_info, status);\n \telse\n \t\treturn checkout_branch(opts, new_branch_info);\n }\n@@ -1887,6 +1892,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t};\n \tint ret;\n \tstruct branch_info new_branch_info = { 0 };\n+\tstruct wt_status status;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n@@ -1917,7 +1923,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \toptions = add_checkout_path_options(&opts, options);\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n-\t\t\t    options, checkout_usage, &new_branch_info);\n+\t\t\t    options, checkout_usage,\n+\t\t\t    &new_branch_info, &status);\n \tbranch_info_release(&new_branch_info);\n \tclear_pathspec(&opts.pathspec);\n \tfree(opts.pathspec_from_file);\n@@ -1942,6 +1949,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \t};\n \tint ret;\n \tstruct branch_info new_branch_info = { 0 };\n+\tstruct wt_status status;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n@@ -1961,7 +1969,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)\n \tcb_option = 'c';\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n-\t\t\t    options, switch_branch_usage, &new_branch_info);\n+\t\t\t    options, switch_branch_usage,\n+\t\t\t    &new_branch_info, &status);\n \tbranch_info_release(&new_branch_info);\n \tFREE_AND_NULL(options);\n \treturn ret;\n@@ -1985,6 +1994,13 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \t};\n \tint ret;\n \tstruct branch_info new_branch_info = { 0 };\n+\tstruct wt_status status;\n+\tunsigned int progress_flag = 0;\n+\n+\twt_status_prepare(the_repository, &status);\n+\tgit_config(git_status_config, &status);\n+\tfinalize_deferred_config(&status);\n+\tstatus.status_format = status_format;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.accept_ref = 0;\n@@ -2000,7 +2016,21 @@ int cmd_restore(int argc, const char **argv, const char *prefix)\n \toptions = add_checkout_path_options(&opts, options);\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n-\t\t\t    options, restore_usage, &new_branch_info);\n+\t\t\t    options, restore_usage,\n+\t\t\t    &new_branch_info, &status);\n+\n+\tif (status.status_format == STATUS_FORMAT_NOOB) {\n+\t\t/* Read index and populate status */\n+\t\trepo_read_index(the_repository);\n+\t\trefresh_index(&the_index,\n+\t\t\t      REFRESH_QUIET|REFRESH_UNMERGED|progress_flag,\n+\t\t\t      &status.pathspec, NULL, NULL);\n+\t\tstatus.show_branch = 0;\n+\t\twt_status_collect(&status);\n+\t\twt_status_print(&status);\n+\t\twt_status_collect_free_buffers(&status);\n+\t}\n+\n \tbranch_info_release(&new_branch_info);\n \tFREE_AND_NULL(options);\n \treturn ret;\ndiff --git a/read-cache-ll.h b/read-cache-ll.h\nindex 302a075714..8bdc157196 100644\n--- a/read-cache-ll.h\n+++ b/read-cache-ll.h\n@@ -389,6 +389,7 @@ void rename_index_entry_at(struct index_state *, int pos, const char *new_name);\n /* Remove entry, return true if there are more entries to go. */\n int remove_index_entry_at(struct index_state *, int pos);\n \n+void remove_marked_cache_entries_with_status(struct index_state *istate, int invalidate, struct wt_status *status);\n void remove_marked_cache_entries(struct index_state *istate, int invalidate);\n int remove_file_from_index(struct index_state *, const char *path);\n #define ADD_CACHE_VERBOSE 1\ndiff --git a/read-cache.c b/read-cache.c\nindex 319415430a..1c1a3290c0 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -558,7 +558,7 @@ int remove_index_entry_at(struct index_state *istate, int pos)\n  * CE_REMOVE is set in ce_flags.  This is much more effective than\n  * calling remove_index_entry_at() for each entry to be removed.\n  */\n-void remove_marked_cache_entries(struct index_state *istate, int invalidate)\n+void remove_marked_cache_entries_with_status(struct index_state *istate, int invalidate, struct wt_status *status)\n {\n \tstruct cache_entry **ce_array = istate->cache;\n \tunsigned int i, j;\n@@ -570,6 +570,7 @@ void remove_marked_cache_entries(struct index_state *istate, int invalidate)\n \t\t\t\t\t\t\t   ce_array[i]->name);\n \t\t\t\tuntracked_cache_remove_from_index(istate,\n \t\t\t\t\t\t\t\t  ce_array[i]->name);\n+\t\t\t\tstring_list_insert(&status->restored, ce_array[i]->name);\n \t\t\t}\n \t\t\tremove_name_hash(istate, ce_array[i]);\n \t\t\tsave_or_free_index_entry(istate, ce_array[i]);\n@@ -583,6 +584,12 @@ void remove_marked_cache_entries(struct index_state *istate, int invalidate)\n \tistate->cache_nr = j;\n }\n \n+void remove_marked_cache_entries(struct index_state *istate, int invalidate)\n+{\n+\tstruct wt_status status;\n+\tremove_marked_cache_entries_with_status(istate, invalidate, &status);\n+}\n+\n int remove_file_from_index(struct index_state *istate, const char *path)\n {\n \tint pos = index_name_pos(istate, path, strlen(path));\ndiff --git a/table.c b/table.c\nindex d29b311440..3602def17a 100644\n--- a/table.c\n+++ b/table.c\n@@ -66,7 +66,7 @@ static void build_table_entry(struct strbuf *buf, char *entry, int cols)\n \t\tstrbuf_addchars(buf, ' ', (cols / 3 - len - 1) / 2);\n }\n \n-static void build_arrow(struct strbuf *buf, struct strbuf* arrow, int add_after_entry)\n+static void build_arrow_(struct strbuf *buf, struct strbuf* arrow, int add_after_entry, int reversed)\n {\n \tstruct strbuf empty = STRBUF_INIT;\n \tstruct strbuf trimmed = STRBUF_INIT;\n@@ -80,17 +80,38 @@ static void build_arrow(struct strbuf *buf, struct strbuf* arrow, int add_after_\n \t\tstrbuf_reset(buf);\n \t\tstrbuf_addchars(arrow, '-', len + 1);\n \t} else if (add_after_entry) {\n-\t\tstrbuf_rtrim(buf);\n-\t\tstrbuf_addchars(arrow, ' ', 1);\n-\t\tstrbuf_addchars(arrow, '-', len - strlen(buf->buf) + 1);\n+\t\tif (!reversed) {\n+\t\t\tstrbuf_rtrim(buf);\n+\t\t\tstrbuf_addchars(arrow, ' ', 1);\n+\t\t\tstrbuf_addchars(arrow, '-', len - strlen(buf->buf) + 1);\n+\t\t} else {\n+\t\t\tstrbuf_rtrim(buf);\n+\t\t\tstrbuf_addchars(arrow, ' ', 1);\n+\t\t\tstrbuf_addchars(arrow, '<', 1);\n+\t\t\tstrbuf_addchars(arrow, '-', len - strlen(buf->buf) - 3);\n+\t\t}\n \t} else if (!add_after_entry) {\n-\t\tstrbuf_ltrim(buf);\n-\t\tstrbuf_addchars(arrow, '-', len - strlen(buf->buf) - 3);\n-\t\tstrbuf_addchars(arrow, '>', 1);\n-\t\tstrbuf_addchars(arrow, ' ', 1);\n+\t\tif (!reversed) {\n+\t\t\tstrbuf_ltrim(buf);\n+\t\t\tstrbuf_addchars(arrow, '-', len - strlen(buf->buf) - 3);\n+\t\t\tstrbuf_addchars(arrow, '>', 1);\n+\t\t\tstrbuf_addchars(arrow, ' ', 1);\n+\t\t} else {\n+\t\t\tstrbuf_ltrim(buf);\n+\t\t\tstrbuf_addchars(arrow, '-', len - strlen(buf->buf) + 1);\n+\t\t\tstrbuf_addchars(arrow, ' ', 1);\n+\t\t}\n \t}\n }\n \n+static void build_arrow(struct strbuf *buf, struct strbuf* arrow, int add_after_entry) {\n+\tbuild_arrow_(buf, arrow, add_after_entry, 0);\n+}\n+\n+static void build_reversed_arrow(struct strbuf *buf, struct strbuf* arrow, int add_after_entry) {\n+\tbuild_arrow_(buf, arrow, add_after_entry, 1);\n+}\n+\n static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct strbuf *arrow1, struct strbuf *arrow2, struct strbuf *arrow3, struct wt_status *s, int hide_pipe)\n {\n \tprintf(_(\"|\"));\n@@ -180,6 +201,18 @@ void print_noob_status(struct wt_status *s, int advice)\n \t\t\t}\n \t\t}\n \n+\t\tfor_each_string_list_item(item2, &s->restored) {\n+\t\t\tstrbuf_reset(&buf_2);\n+\t\t\tstrbuf_addstr(&buf_2, item2->string);\n+\t\t\tif (!strbuf_cmp(&buf_1, &buf_2)) {\n+\t\t\t\tbuild_table_entry(&table_col_entry_3, buf_1.buf, cols);\n+\t\t\t\tbuild_reversed_arrow(&table_col_entry_1, &arrow_1, 1);\n+\t\t\t\tbuild_reversed_arrow(&table_col_entry_2, &arrow_2, 0);\n+\t\t\t\tbuild_reversed_arrow(&table_col_entry_3, &arrow_3, 0);\n+\t\t\t\tis_arrow = 1;\n+\t\t\t}\n+\t\t}\n+\n \t\tif (!is_arrow)\n \t\t\tprint_table_body_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, &arrow_1, &arrow_2, &arrow_3, s, 0);\n \t\telse\n@@ -215,6 +248,17 @@ void print_noob_status(struct wt_status *s, int advice)\n \t\t\t\t\tis_arrow = 1;\n \t\t\t\t}\n \t\t\t}\n+\n+\t\t\tfor_each_string_list_item(item2, &s->restored) {\n+\t\t\t\tstrbuf_reset(&buf_2);\n+\t\t\t\tstrbuf_addstr(&buf_2, item2->string);\n+\t\t\t\tif (!strbuf_cmp(&buf_1, &buf_2)) {\n+\t\t\t\t\tbuild_table_entry(&table_col_entry_3, buf_1.buf, cols);\n+\t\t\t\t\tbuild_reversed_arrow(&table_col_entry_2, &arrow_2, 1);\n+\t\t\t\t\tbuild_reversed_arrow(&table_col_entry_3, &arrow_3, 0);\n+\t\t\t\t\tis_arrow = 1;\n+\t\t\t\t}\n+\t\t\t}\n \t\t} else if (d->index_status) {\n \t\t\tbuild_table_entry(&table_col_entry_1, \"\", cols);\n \t\t\tbuild_table_entry(&table_col_entry_2, \"\", cols);\ndiff --git a/wt-status.h b/wt-status.h\nindex 7b883fd476..c6bce8f74a 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -144,6 +144,7 @@ struct wt_status {\n \tstruct string_list untracked;\n \tstruct string_list ignored;\n \tstruct string_list added;\n+\tstruct string_list restored;\n \tuint32_t untracked_in_ms;\n };\n \n-- \n2.42.0.404.g2bcc23f3db\n\n"},{"id":"483944","messageId":"20231026224615.675172-7-jacob@initialcommit.io","threadId":"60408","inReplyTo":"20231026224615.675172-1-jacob@initialcommit.io","subject":"[RFC PATCH v2 6/6] status: add advice status hints as table footer","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-26T22:46:15Z","receivedAt":"2023-10-26T22:46:49Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"Signed-off-by: Jacob Stopak <jacob@initialcommit.io>\n---\n builtin/commit.c |  1 +\n table.c          | 42 +++++++++++++++++++++++++++---------------\n table.h          |  2 +-\n wt-status.c      |  3 ++-\n wt-status.h      |  2 ++\n 5 files changed, 33 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 3f816c117d..b97943e642 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1434,6 +1434,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \ts.ignore_submodule_arg = ignore_submodule_arg;\n \ts.status_format = status_format;\n \ts.verbose = verbose;\n+\ts.is_cmd_status = 1;\n \tif (no_renames != -1)\n \t\ts.detect_rename = !no_renames;\n \tif ((intptr_t)rename_score_arg != -1) {\ndiff --git a/table.c b/table.c\nindex 3602def17a..36719e3d09 100644\n--- a/table.c\n+++ b/table.c\n@@ -6,6 +6,7 @@\n #include \"config.h\"\n #include \"string-list.h\"\n #include \"color.h\"\n+#include \"advice.h\"\n #include \"sys/ioctl.h\"\n \n static const char *color(int slot, struct wt_status *s)\n@@ -132,7 +133,18 @@ static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, stru\n \tprintf(_(\"|\\n\"));\n }\n \n-void print_noob_status(struct wt_status *s, int advice)\n+static void print_table_hint_line(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct wt_status *s)\n+{\n+\tprintf(_(\"|\"));\n+\tcolor_fprintf(s->fp, color(WT_STATUS_HINT, s), \"%s\", buf1->buf);\n+\tprintf(_(\"|\"));\n+\tcolor_fprintf(s->fp, color(WT_STATUS_HINT, s), \"%s\", buf2->buf);\n+\tprintf(_(\"|\"));\n+\tcolor_fprintf(s->fp, color(WT_STATUS_HINT, s), \"%s\", buf3->buf);\n+\tprintf(_(\"|\\n\"));\n+}\n+\n+void print_noob_status(struct wt_status *s)\n {\n \tstruct winsize w;\n \tint cols;\n@@ -163,20 +175,6 @@ void print_noob_status(struct wt_status *s, int advice)\n \tprintf(_(\"%s\\n\"), table_border.buf);\n \tprintf(_(\"|%s|%s|%s|\\n\"), table_col_entry_1.buf, table_col_entry_2.buf, table_col_entry_3.buf);\n \n-\tif (advice) {\n-\t\tbuild_table_entry(&table_col_entry_1, \"(stage: git add <file>)\", cols);\n-\t\tbuild_table_entry(&table_col_entry_2, \"(stage: git add <file>)\", cols);\n-\t\tbuild_table_entry(&table_col_entry_3, \"(unstage: git restore --staged <file>)\", cols);\n-\n-\t\tprintf(_(\"|%s|%s|%s|\\n\"), table_col_entry_1.buf, table_col_entry_2.buf, table_col_entry_3.buf);\n-\n-\t\tbuild_table_entry(&table_col_entry_1, \"\", cols);\n-\t\tbuild_table_entry(&table_col_entry_2, \"(discard: git restore --staged <file>)\", cols);\n-\t\tbuild_table_entry(&table_col_entry_3, \"\", cols);\n-\n-\t\tprintf(_(\"|%s|%s|%s|\\n\"), table_col_entry_1.buf, table_col_entry_2.buf, table_col_entry_3.buf);\n-\t}\n-\n \tprintf(_(\"%s\\n\"), table_border.buf);\n \n \t/* Draw table body */\n@@ -282,6 +280,20 @@ void print_noob_status(struct wt_status *s, int advice)\n \t}\n \n \tprintf(_(\"%s\\n\"), table_border.buf);\n+\n+\tif (s->is_cmd_status && advice_enabled(ADVICE_STATUS_HINTS)) {\n+\t\tbuild_table_entry(&table_col_entry_1, \"stage: git add ...\", cols);\n+\t\tbuild_table_entry(&table_col_entry_2, \"stage: git add ...\", cols);\n+\t\tbuild_table_entry(&table_col_entry_3, \"unstage: git restore --staged ...\", cols);\n+\t\tprint_table_hint_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\n+\t\tbuild_table_entry(&table_col_entry_1, \"\", cols);\n+\t\tbuild_table_entry(&table_col_entry_2, \"discard: git restore --staged ...\", cols);\n+\t\tbuild_table_entry(&table_col_entry_3, \"\", cols);\n+\t\tprint_table_hint_line(&table_col_entry_1, &table_col_entry_2, &table_col_entry_3, s);\n+\t\tprintf(_(\"%s\\n\"), table_border.buf);\n+\t}\n+\n \tstrbuf_release(&table_border);\n \tstrbuf_release(&table_col_entry_1);\n \tstrbuf_release(&table_col_entry_2);\ndiff --git a/table.h b/table.h\nindex 5dff7162a4..c9e8c386de 100644\n--- a/table.h\n+++ b/table.h\n@@ -1,6 +1,6 @@\n #ifndef TABLE_H\n #define TABLE_H\n \n-void print_noob_status(struct wt_status *s, int i);\n+void print_noob_status(struct wt_status *s);\n \n #endif /* TABLE_H */\ndiff --git a/wt-status.c b/wt-status.c\nindex 1332d07dba..288817dcf7 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -50,6 +50,7 @@ static char default_wt_status_colors[][COLOR_MAXLEN] = {\n \tGIT_COLOR_RED,    /* WT_STATUS_REMOTE_BRANCH */\n \tGIT_COLOR_NIL,    /* WT_STATUS_ONBRANCH */\n \tGIT_COLOR_CYAN,   /* WT_STATUS_ARROW */\n+\tGIT_COLOR_YELLOW, /* WT_STATUS_HINT */\n };\n \n static const char *color(int slot, struct wt_status *s)\n@@ -2151,7 +2152,7 @@ static void wt_noobstatus_print(struct wt_status *s)\n \t\twt_longstatus_print_tracking(s);\n \t}\n \n-\tprint_noob_status(s, 0);\n+\tprint_noob_status(s);\n }\n \n static void wt_porcelain_print(struct wt_status *s)\ndiff --git a/wt-status.h b/wt-status.h\nindex c6bce8f74a..0a14b4b064 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -20,6 +20,7 @@ enum color_wt_status {\n \tWT_STATUS_REMOTE_BRANCH,\n \tWT_STATUS_ONBRANCH,\n \tWT_STATUS_ARROW,\n+\tWT_STATUS_HINT,\n \tWT_STATUS_MAXSLOT\n };\n \n@@ -146,6 +147,7 @@ struct wt_status {\n \tstruct string_list added;\n \tstruct string_list restored;\n \tuint32_t untracked_in_ms;\n+\tint is_cmd_status;\n };\n \n size_t wt_status_locate_end(const char *s, size_t len);\n-- \n2.42.0.404.g2bcc23f3db\n\n"},{"id":"483990","messageId":"ca47d328c280e4b4c13bfa6dd9958a57@manjaro.org","threadId":"60408","inReplyTo":"20231026224615.675172-1-jacob@initialcommit.io","subject":"Re: [RFC PATCH v2 0/6] Noobify format for status, add, restore","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-27T13:32:40Z","receivedAt":"2023-10-27T13:32:46Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-27 00:46, Jacob Stopak wrote:\n> Take into account reviewer feedback by doing several things \n> differently:\n> \n>   * Rename this feature (for now) as \"noob format mode\" (or just \"noob\n>     mode\") instead of the original \"--table\" verbiage. As pointed out,\n>     this no longer ties the name of the setting to it's proposed\n>     implementation detail as a table. Noob mode is not necessarily the\n>     right name, just a placeholder for now. Unless people like it :D\n> \n>   * Instead of manually having to invoke the -t, --table every time \n> this\n>     format is to be used, set the config option \"status.noob\" to true.\n>     Although this is logically tied to the status command, there are \n> many\n>     commands that produce status output, (and this series adds more), \n> so\n>     assume that if the user wants to see the status this way, that it\n>     should be enabled whenever the status info is displayed.\n\nHow would \"status.noob\" relate to and coexist with possible future \nconfiguration options named \"<command>.verbose\", which would be somewhat \nsimilar to the currently existing \"commit.verbose\" option?  IOW, perhaps \nit would be better to have per-command options \"<command>.verbose = \nnoob\" or, even better, \"<command>.verbose = extended\", to make it all \nmore future-proof and more granular.\n\n>   * When running \"git add\" and \"git restore\" while noob mode is \n> enabled,\n>     perform the add/restore function as usual, but display the table\n>     formatted output with arrows showing how file changes moved around.\n>     Displaying the output in this understandable format after each\n>     command execution allows the noob to immediately see what they did.\n>     Although this series only implements for status, add, and restore,\n>     this output format would make sense in other commands like rm, mv,\n>     commit, clean, and stash.\n> \n>   * Works consistently with commands that already have a --dry-run\n>     (-n) option. The dry run shows the exact same output, but\n>     doesn't actually do the thing.\n> \n>   * If `advice.statusHints` is true, add a table footer with status \n> hints.\n>     Shorten these hints so that they are still clear but better fit \n> into a\n>     table. Make the hint text yellow to distinguish them. The hints \n> only\n>     appear when explicitly running \"git status\", which helps the user\n>     answer the question \"what can I do next?\". Hints are omitted in\n>     \"impact\" commands like add and restore. Having hints here distracts\n>     from the file change moves being showed in the table by arrows.\n> \n> TODO:\n> \n>   * \"git status\" outputs myriad other information depending on the \n> state\n>     of the repo, like branch info, merge conflicts, rebase info, \n> bisect,\n>     etc. Need to think about how to convey that info with the new \n> setting.\n> \n>   * Some commands (like stash) might need more than 3 table columns to\n>     display everything clearly.\n> \n>   * For destructive commands, think about adding a prompt describing \n> the\n>     effect, so the user can confirm before the action is taken.\n> \n>   * Fix horrible things in the patch series code.\n> \n>   * Probably other things.\n> \n> Play around with it! It's fun!\n> \n> Jacob Stopak (6):\n>   status: add noob format from status.noob config\n>   status: handle long paths in noob format\n>   add: implement noob mode\n>   add: set unique color for noob mode arrows\n>   restore: implement noob mode\n>   status: add advice status hints as table footer\n> \n>  Makefile           |   2 +\n>  builtin/add.c      |  47 +++++--\n>  builtin/checkout.c |  46 +++++--\n>  builtin/commit.c   | 157 +----------------------\n>  commit.c           |   2 +\n>  noob.c             | 198 +++++++++++++++++++++++++++++\n>  noob.h             |  21 ++++\n>  read-cache-ll.h    |  10 +-\n>  read-cache.c       |  41 +++++-\n>  table.c            | 301 +++++++++++++++++++++++++++++++++++++++++++++\n>  table.h            |   6 +\n>  wt-status.c        |  75 +++++++----\n>  wt-status.h        |   6 +\n>  13 files changed, 708 insertions(+), 204 deletions(-)\n>  create mode 100644 noob.c\n>  create mode 100644 noob.h\n>  create mode 100644 table.c\n>  create mode 100644 table.h\n"},{"id":"483999","messageId":"ZTvvz6/GFdwagVa+.jacob@initialcommit.io","threadId":"60408","inReplyTo":"ca47d328c280e4b4c13bfa6dd9958a57@manjaro.org","subject":"Re: [RFC PATCH v2 0/6] Noobify format for status, add, restore","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-27T17:13:51Z","receivedAt":"2023-10-27T17:13:58Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"On Fri, Oct 27, 2023 at 03:32:40PM +0200, Dragan Simic wrote:\n> On 2023-10-27 00:46, Jacob Stopak wrote:\n> > Take into account reviewer feedback by doing several things differently:\n> > \n> >   * Rename this feature (for now) as \"noob format mode\" (or just \"noob\n> >     mode\") instead of the original \"--table\" verbiage. As pointed out,\n> >     this no longer ties the name of the setting to it's proposed\n> >     implementation detail as a table. Noob mode is not necessarily the\n> >     right name, just a placeholder for now. Unless people like it :D\n> > \n> >   * Instead of manually having to invoke the -t, --table every time this\n> >     format is to be used, set the config option \"status.noob\" to true.\n> >     Although this is logically tied to the status command, there are\n> > many\n> >     commands that produce status output, (and this series adds more), so\n> >     assume that if the user wants to see the status this way, that it\n> >     should be enabled whenever the status info is displayed.\n> \n> How would \"status.noob\" relate to and coexist with possible future\n> configuration options named \"<command>.verbose\", which would be somewhat\n> similar to the currently existing \"commit.verbose\" option?  IOW, perhaps it\n> would be better to have per-command options \"<command>.verbose = noob\" or,\n> even better, \"<command>.verbose = extended\", to make it all more\n> future-proof and more granular.\n\nHmm, do there currently exist other <command>.verbose config settings\nbesides for commit? From what I can tell from \"git help config\", the\ncommit.verbose setting is the only one I see, and it just adds the diff\ninfo into the editor if the user runs git commit without the -m flag, but\notherwise there seems to be no extra verbosity outputted.\n\nI noticed that git add and git mv have \"verbose\" (-v, --verbose) cli flags\nwhich just output the name of the file being added or renamed, and that\ncertain other commands like git branch has a verbose output which includes\nthe branch head commit hash and message in the output, so I guess this one\nis actually kindof verbose in that it outputs more than the non-empty\ndefault output.\n\nSo it seems like currently \"verbose\" is used for various things among the\ncommand set, sometimes meaning \"add something into the template if one is\nused\" or \"add some tiny output to a command that has no default output\"\n(which still seems more \"--shy\" than \"--verbose\" :P) or \"add some\nadditional output to a command that already has some sparse output\".\n\nAnother thing is that commands like status have multiple flags that can be\nused to specify the output format, such as --short, --long, --porcelain,\netc, but only --short seems to be configurable as a git config setting.\nIs there a reason (besides backward compatibility I guess) that these\naren't rolled into a single thing like --format=<type>? This seems like\nit would be the easiest way to future proof for new formats like\n--format=verbose, --format=noob, --format=extended, etc.\n\nFrom a noob's perspective though, does adding a config setting for each\ncommand really make sense? I'm kindof envisioning this setting now as a\n\"mode\" that is either enabled for all commands it affects or for none.\nAnd it's highly unlikely a newish user would individually discover which\ncommands this \"extended\" format is available for, and run \"git config\n<command>.verbose = extended\" for every one. I mean we could do that\nin case there are folks who only want it for specific commands, but to\nfulfill it's purpose I think there should definetely be some general way\nto enable the setting for all commands that have it.\n"},{"id":"484010","messageId":"9b93115810ca269c87ec08f72fdc9c12@manjaro.org","threadId":"60408","inReplyTo":"ZTvvz6/GFdwagVa+.jacob@initialcommit.io","subject":"Re: [RFC PATCH v2 0/6] Noobify format for status, add, restore","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-28T00:06:49Z","receivedAt":"2023-10-28T00:06:55Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-27 19:13, Jacob Stopak wrote:\n> On Fri, Oct 27, 2023 at 03:32:40PM +0200, Dragan Simic wrote:\n>> On 2023-10-27 00:46, Jacob Stopak wrote:\n>> > Take into account reviewer feedback by doing several things differently:\n>> >\n>> >   * Rename this feature (for now) as \"noob format mode\" (or just \"noob\n>> >     mode\") instead of the original \"--table\" verbiage. As pointed out,\n>> >     this no longer ties the name of the setting to it's proposed\n>> >     implementation detail as a table. Noob mode is not necessarily the\n>> >     right name, just a placeholder for now. Unless people like it :D\n>> >\n>> >   * Instead of manually having to invoke the -t, --table every time this\n>> >     format is to be used, set the config option \"status.noob\" to true.\n>> >     Although this is logically tied to the status command, there are\n>> > many\n>> >     commands that produce status output, (and this series adds more), so\n>> >     assume that if the user wants to see the status this way, that it\n>> >     should be enabled whenever the status info is displayed.\n>> \n>> How would \"status.noob\" relate to and coexist with possible future\n>> configuration options named \"<command>.verbose\", which would be \n>> somewhat\n>> similar to the currently existing \"commit.verbose\" option?  IOW, \n>> perhaps it\n>> would be better to have per-command options \"<command>.verbose = noob\" \n>> or,\n>> even better, \"<command>.verbose = extended\", to make it all more\n>> future-proof and more granular.\n> \n> Hmm, do there currently exist other <command>.verbose config settings\n> besides for commit? From what I can tell from \"git help config\", the\n> commit.verbose setting is the only one I see, and it just adds the diff\n> info into the editor if the user runs git commit without the -m flag, \n> but\n> otherwise there seems to be no extra verbosity outputted.\n\nThey currently don't exist, but that's something I've planned to \nimplement, e.g. to \"add.verbose\" as a new configuration option.  It \nshould be usable, while not being messy or intrusive as a new feature.\n\n> I noticed that git add and git mv have \"verbose\" (-v, --verbose) cli \n> flags\n> which just output the name of the file being added or renamed, and that\n> certain other commands like git branch has a verbose output which \n> includes\n> the branch head commit hash and message in the output, so I guess this \n> one\n> is actually kindof verbose in that it outputs more than the non-empty\n> default output.\n\nYes, those are the basic per-command verbosity modes or levels, as I \ncall them.  The way I see it, your patches would add new, extended \nper-command verbosity levels.\n\n> So it seems like currently \"verbose\" is used for various things among \n> the\n> command set, sometimes meaning \"add something into the template if one \n> is\n> used\" or \"add some tiny output to a command that has no default output\"\n> (which still seems more \"--shy\" than \"--verbose\" :P) or \"add some\n> additional output to a command that already has some sparse output\".\n\nYes, that's the basic verbosity, as I named it above.\n\n> Another thing is that commands like status have multiple flags that can \n> be\n> used to specify the output format, such as --short, --long, \n> --porcelain,\n> etc, but only --short seems to be configurable as a git config setting.\n> Is there a reason (besides backward compatibility I guess) that these\n> aren't rolled into a single thing like --format=<type>? This seems like\n> it would be the easiest way to future proof for new formats like\n> --format=verbose, --format=noob, --format=extended, etc.\n\nThat's a good question, but I'd need to go through the commit history to \nbe able to provide some kind of an explanation.  It could also be all \npacked into \"status.verbose\" as a new configuration option.\n\n> From a noob's perspective though, does adding a config setting for each\n> command really make sense? I'm kindof envisioning this setting now as a\n> \"mode\" that is either enabled for all commands it affects or for none.\n> And it's highly unlikely a newish user would individually discover \n> which\n> commands this \"extended\" format is available for, and run \"git config\n> <command>.verbose = extended\" for every one. I mean we could do that\n> in case there are folks who only want it for specific commands, but to\n> fulfill it's purpose I think there should definetely be some general \n> way\n> to enable the setting for all commands that have it.\n\nQuite frankly, we shouldn't expect that all users are noobs, and as a \nresult dumb everything down just to make them as comfortable as \npossible.  On the other hand, perhaps not everyone would like to have \nextended verbosity enabled for all commands, just as not everyone uses \n\"-v\" for all commands.\n"},{"id":"484014","messageId":"ZTx3fIGpdGl4JpaV.jacob@initialcommit.io","threadId":"60408","inReplyTo":"9b93115810ca269c87ec08f72fdc9c12@manjaro.org","subject":"Re: [RFC PATCH v2 0/6] Noobify format for status, add, restore","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-28T02:52:44Z","receivedAt":"2023-10-28T02:52:50Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"> They currently don't exist, but that's something I've planned to implement,\n> e.g. to \"add.verbose\" as a new configuration option.  It should be usable,\n> while not being messy or intrusive as a new feature.\n\n\"git add\" already has the -v, --verbose flag available for the command\nitself like:\n\n$ git add -v foo.txt\nadd 'foo.txt'\n\nBut like you said the config option add.verbose doesn't seem to exist yet.\n\nSo I assume an \"add.verbose\" config option would just always print that\nwithout having to specify the -v, --verbose flag when running the command?\n\nBasically what I'm asking is if commands that already have a --verbose flag\nwould just get a config setting that does the existing thing by default?\n\n> Yes, those are the basic per-command verbosity modes or levels, as I call\n> them.  The way I see it, your patches would add new, extended per-command\n> verbosity levels.\n\nOk, I see.\n\n> > So it seems like currently \"verbose\" is used for various things among\n> > the command set...\n> Yes, that's the basic verbosity, as I named it above.\n\nWould it make sense to try to define a more consistent type of output or\nformat style for \"verbosity\" across different commands? As it stands it\nseems each command treats verbosity in its own way which makes it hard to\ninterpret exactly what it will do each time...\n\n> > Another thing is that commands like status have multiple flags that can\n> > be\n> > used to specify the output format, such as --short, --long, --porcelain,\n> > etc, but only --short seems to be configurable as a git config setting.\n> > Is there a reason (besides backward compatibility I guess) that these\n> > aren't rolled into a single thing like --format=<type>? This seems like\n> > it would be the easiest way to future proof for new formats like\n> > --format=verbose, --format=noob, --format=extended, etc.\n> \n> That's a good question, but I'd need to go through the commit history to be\n> able to provide some kind of an explanation.  It could also be all packed\n> into \"status.verbose\" as a new configuration option.\n\nOk so it sounds like you prefer to use \"verbose\" as the setting key?\nI guess at this point that might make more sense since commit.verbose\nalready exists, and existing options could be packed into it like you\nsaid instead of just true or false.\n\nAnd then my thing here would just be called \"command.verbose = extended\"?\n\n> > From a noob's perspective though, does adding a config setting for each\n> > command really make sense? I'm kindof envisioning this setting now as a\n> > \"mode\" that is either enabled for all commands it affects or for none.\n> > And it's highly unlikely a newish user would individually discover which\n> > commands this \"extended\" format is available for, and run \"git config\n> > <command>.verbose = extended\" for every one. I mean we could do that\n> > in case there are folks who only want it for specific commands, but to\n> > fulfill it's purpose I think there should definetely be some general way\n> > to enable the setting for all commands that have it.\n> \n> Quite frankly, we shouldn't expect that all users are noobs, and as a result\n> dumb everything down just to make them as comfortable as possible.  On the\n> other hand, perhaps not everyone would like to have extended verbosity\n> enabled for all commands, just as not everyone uses \"-v\" for all commands.\n\nI agree with this, and I think it's important to cater to both newbies and\nexperienced users alike. That's why I said I never dreamed of making this\nnew format the default.\n\nAnd it's true that some users might only want the extended (or any format)\nfor specific commands. I think a happy medium then is to have the command-\nspecific settings like you mention, plus one toplevel option that enables a\nspecific type of output format among all commands (and overrides the\ncommand-specific settings), so that the user can choose which they prefer.\n\nAny thoughts on what the section in the config for a more general setting\nlike this might be named? If \"status.verbose = extended\" would already be\ntaken specifically for the status command, what terminology could we use\nto mean something like \"global.verbose = extended\" or \"global.extended =\ntrue\"? Although the former seems better to me since other format values\ncould be implemented, like \"global.verbose = standard\"...\n"},{"id":"484016","messageId":"2a0ba4c8e96cb7d2ea66dd1e78cdd39c@manjaro.org","threadId":"60408","inReplyTo":"ZTx3fIGpdGl4JpaV.jacob@initialcommit.io","subject":"Re: [RFC PATCH v2 0/6] Noobify format for status, add, restore","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-28T05:55:31Z","receivedAt":"2023-10-28T05:55:36Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-28 04:52, Jacob Stopak wrote:\n>> They currently don't exist, but that's something I've planned to \n>> implement,\n>> e.g. to \"add.verbose\" as a new configuration option.  It should be \n>> usable,\n>> while not being messy or intrusive as a new feature.\n> \n> \"git add\" already has the -v, --verbose flag available for the command\n> itself like:\n> \n> $ git add -v foo.txt\n> add 'foo.txt'\n> \n> But like you said the config option add.verbose doesn't seem to exist \n> yet.\n> \n> So I assume an \"add.verbose\" config option would just always print that\n> without having to specify the -v, --verbose flag when running the \n> command?\n\nYes, that's how I see it.  Setting \"add.verbose\" to \"true\", to be \nprecise, or to \"basic\", which I'll explain a bit further later in my \nresponse.\n\n> Basically what I'm asking is if commands that already have a --verbose \n> flag\n> would just get a config setting that does the existing thing by \n> default?\n\nWell, not by default.  The default values would remain \"false\", so \nnothing jumps out of nowhere.\n\n>>> So it seems like currently \"verbose\" is used for various things among\n>>> the command set...\n>> \n>> Yes, that's the basic verbosity, as I named it above.\n> \n> Would it make sense to try to define a more consistent type of output \n> or\n> format style for \"verbosity\" across different commands? As it stands it\n> seems each command treats verbosity in its own way which makes it hard \n> to\n> interpret exactly what it will do each time...\n\nWe'd have to follow the already established behavior of the commands, \nand there are the man pages to describe what's going on with the \nverbosity for each command.  In other words, nothing would get changed, \njust some more knobs would be added, for those who prefer to have the \nadditional verbosity enabled.\n\n>>> Another thing is that commands like status have multiple flags that \n>>> can be\n>>> used to specify the output format, such as --short, --long, \n>>> --porcelain,\n>>> etc, but only --short seems to be configurable as a git config \n>>> setting.\n>>> Is there a reason (besides backward compatibility I guess) that these\n>>> aren't rolled into a single thing like --format=<type>? This seems \n>>> like\n>>> it would be the easiest way to future proof for new formats like\n>>> --format=verbose, --format=noob, --format=extended, etc.\n>> \n>> That's a good question, but I'd need to go through the commit history \n>> to be\n>> able to provide some kind of an explanation.  It could also be all \n>> packed\n>> into \"status.verbose\" as a new configuration option.\n> \n> Ok so it sounds like you prefer to use \"verbose\" as the setting key?\n> I guess at this point that might make more sense since commit.verbose\n> already exists, and existing options could be packed into it like you\n> said instead of just true or false.\n\nIt looks like a logical choice to me.\n\n> And then my thing here would just be called \"command.verbose = \n> extended\"?\n\nYes, that's what I propose.  It also looks like a logical choice to me, \nand it would leave space for some possible later changes to the \n\"<command>.verbose = extended\" verbosity, without tying it to the \ntables.  We'd also leave some space that way for even maybe an \nadditional level of verbosity, be it \"<command>.verbose = simple\", \n\"<command>.verbose = graphical\" or whatever.\n\nPerhaps this scheme should also support \"<command>.verbose = basic\", \nwhich would be an alias for \"<command>.verbose = true\", for additional \nclarity.\n\n>>> From a noob's perspective though, does adding a config setting for \n>>> each\n>>> command really make sense? I'm kindof envisioning this setting now as \n>>> a\n>>> \"mode\" that is either enabled for all commands it affects or for \n>>> none.\n>>> And it's highly unlikely a newish user would individually discover \n>>> which\n>>> commands this \"extended\" format is available for, and run \"git config\n>>> <command>.verbose = extended\" for every one. I mean we could do that\n>>> in case there are folks who only want it for specific commands, but \n>>> to\n>>> fulfill it's purpose I think there should definetely be some general \n>>> way\n>>> to enable the setting for all commands that have it.\n>> \n>> Quite frankly, we shouldn't expect that all users are noobs, and as a \n>> result\n>> dumb everything down just to make them as comfortable as possible.  On \n>> the\n>> other hand, perhaps not everyone would like to have extended verbosity\n>> enabled for all commands, just as not everyone uses \"-v\" for all \n>> commands.\n> \n> I agree with this, and I think it's important to cater to both newbies \n> and\n> experienced users alike. That's why I said I never dreamed of making \n> this\n> new format the default.\n\nPerhaps it would also be good to nudge the newbies a bit by requesting \nthem to enable the extended verbosity for each command by hand.  That \nway they would both learn a bit about the way git configuration works, \nwhich they ultimately can't escape from, and they would be excited to \nlearn new git commands.  Or I at least hope so. :)\n\n> And it's true that some users might only want the extended (or any \n> format)\n> for specific commands. I think a happy medium then is to have the \n> command-\n> specific settings like you mention, plus one toplevel option that \n> enables a\n> specific type of output format among all commands (and overrides the\n> command-specific settings), so that the user can choose which they \n> prefer.\n\nThat's something we can consider as an additional configuration option.  \nThat way, users could also enable the basic verbosity for all commands, \nwhich may also be usable.\n\n> Any thoughts on what the section in the config for a more general \n> setting\n> like this might be named? If \"status.verbose = extended\" would already \n> be\n> taken specifically for the status command, what terminology could we \n> use\n> to mean something like \"global.verbose = extended\" or \"global.extended \n> =\n> true\"? Although the former seems better to me since other format values\n> could be implemented, like \"global.verbose = standard\"...\n\nMaybe \"core.verbose\"?  We already have \"core.pager\", which kind of \naffects the way all command outputs look like.\n"},{"id":"484030","messageId":"ZT0m68HWZS/tDGtH.jacob@initialcommit.io","threadId":"60408","inReplyTo":"2a0ba4c8e96cb7d2ea66dd1e78cdd39c@manjaro.org","subject":"Re: [RFC PATCH v2 0/6] Noobify format for status, add, restore","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-28T15:21:15Z","receivedAt":"2023-10-28T15:21:21Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"On Sat, Oct 28, 2023 at 07:55:31AM +0200, Dragan Simic wrote:\n> > So I assume an \"add.verbose\" config option would just always print that\n> > without having to specify the -v, --verbose flag when running the\n> > command?\n> \n> Yes, that's how I see it.  Setting \"add.verbose\" to \"true\", to be precise,\n> or to \"basic\", which I'll explain a bit further later in my response.\n\nOk, gotcha!\n\n> > Basically what I'm asking is if commands that already have a --verbose\n> > flag\n> > would just get a config setting that does the existing thing by default?\n> \n> Well, not by default.  The default values would remain \"false\", so nothing\n> jumps out of nowhere.\n\nRight, sorry, I worded that poorly.\n\n> > > > So it seems like currently \"verbose\" is used for various things among\n> > > > the command set...\n> > > \n> > > Yes, that's the basic verbosity, as I named it above.\n\nOk.\n\n> > Would it make sense to try to define a more consistent type of output or\n> > format style for \"verbosity\" across different commands? As it stands it\n> > seems each command treats verbosity in its own way which makes it hard\n> > to interpret exactly what it will do each time...\n> \n> We'd have to follow the already established behavior of the commands, and\n> there are the man pages to describe what's going on with the verbosity for\n> each command.  In other words, nothing would get changed, just some more\n> knobs would be added, for those who prefer to have the additional verbosity\n> enabled.\n\nOk I see.\n\n> > Ok so it sounds like you prefer to use \"verbose\" as the setting key?\n> > I guess at this point that might make more sense since commit.verbose\n> > already exists, and existing options could be packed into it like you\n> > said instead of just true or false.\n> \n> It looks like a logical choice to me.\n> \n\nOk.\n\n> > And then my thing here would just be called \"command.verbose =\n> > extended\"?\n> \n> Yes, that's what I propose.  It also looks like a logical choice to me, and\n> it would leave space for some possible later changes to the\n> \"<command>.verbose = extended\" verbosity, without tying it to the tables.\n> We'd also leave some space that way for even maybe an additional level of\n> verbosity, be it \"<command>.verbose = simple\", \"<command>.verbose =\n> graphical\" or whatever.\n> \n> Perhaps this scheme should also support \"<command>.verbose = basic\", which\n> would be an alias for \"<command>.verbose = true\", for additional clarity.\n> \n\nSounds good!\n\n> \n> Perhaps it would also be good to nudge the newbies a bit by requesting them\n> to enable the extended verbosity for each command by hand.  That way they\n> would both learn a bit about the way git configuration works, which they\n> ultimately can't escape from, and they would be excited to learn new git\n> commands.  Or I at least hope so. :)\n> \n\nHehe ok, maybe there is room in some hints to notify users of the\nextended option...\n\n> > And it's true that some users might only want the extended (or any\n> > format) for specific commands. I think a happy medium then is to have\n> > the command-specific settings like you mention, plus one toplevel\n> > option that enables a specific type of output format among all commands\n> > (and overrides the command-specific settings), so that the user can\n> > choose which they prefer.\n> \n> That's something we can consider as an additional configuration option.\n> That way, users could also enable the basic verbosity for all commands,\n> which may also be usable.\n> \n\nCool!\n\n> > Any thoughts on what the section in the config for a more general\n> > setting like this might be named?\n> \n> Maybe \"core.verbose\"?  We already have \"core.pager\", which kind of affects\n> the way all command outputs look like.\n\nOk! The idea of using \"core\" came to mind but I wasn't sure if that was\nmore for lower-level settings or more general things.\n\nGreat. Thanks a lot for all the feedback. Let me doctor up the patch\nseries to take these things into account and submit an RFC v3 :D\n"},{"id":"484033","messageId":"37e7bd8f6f4b75aa3b31dc98804b1334@manjaro.org","threadId":"60408","inReplyTo":"ZT0m68HWZS/tDGtH.jacob@initialcommit.io","subject":"Re: [RFC PATCH v2 0/6] Noobify format for status, add, restore","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-28T16:20:41Z","receivedAt":"2023-10-28T16:20:45Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-28 17:21, Jacob Stopak wrote:\n> On Sat, Oct 28, 2023 at 07:55:31AM +0200, Dragan Simic wrote:\n>>> Basically what I'm asking is if commands that already have a \n>>> --verbose\n>>> flag would just get a config setting that does the existing thing by\n>>> default?\n>> \n>> Well, not by default.  The default values would remain \"false\", so \n>> nothing\n>> jumps out of nowhere.\n> \n> Right, sorry, I worded that poorly.\n\nNo worries, just wanted to make sure we're on the same page.\n\n>> Yes, that's what I propose.  It also looks like a logical choice to \n>> me, and\n>> it would leave space for some possible later changes to the\n>> \"<command>.verbose = extended\" verbosity, without tying it to the \n>> tables.\n>> We'd also leave some space that way for even maybe an additional level \n>> of\n>> verbosity, be it \"<command>.verbose = simple\", \"<command>.verbose =\n>> graphical\" or whatever.\n>> \n>> Perhaps this scheme should also support \"<command>.verbose = basic\", \n>> which\n>> would be an alias for \"<command>.verbose = true\", for additional \n>> clarity.\n>> \n> Sounds good!\n\nI'm glad you agree.\n\n>> Perhaps it would also be good to nudge the newbies a bit by requesting \n>> them\n>> to enable the extended verbosity for each command by hand.  That way \n>> they\n>> would both learn a bit about the way git configuration works, which \n>> they\n>> ultimately can't escape from, and they would be excited to learn new \n>> git\n>> commands.  Or I at least hope so. :)\n>> \n> Hehe ok, maybe there is room in some hints to notify users of the\n> extended option...\n\nI agree, there should be a well-placed hint, but we'd need to think \nreally well where to place it, so we don't annoy experienced git users \ntoo much, while we also inform the less experienced users.\n\n>> > Any thoughts on what the section in the config for a more general\n>> > setting like this might be named?\n>> \n>> Maybe \"core.verbose\"?  We already have \"core.pager\", which kind of \n>> affects\n>> the way all command outputs look like.\n> \n> Ok! The idea of using \"core\" came to mind but I wasn't sure if that was\n> more for lower-level settings or more general things.\n\nI also considered the \"core\" section to be reserved for the very \nlow-level internal things, but having \"core.pager\" clearly allows other \nappropriate configuration options to be placed here.\n\n> Great. Thanks a lot for all the feedback. Let me doctor up the patch\n> series to take these things into account and submit an RFC v3 :D\n\nSounds good, thank you.  If you agree, I'll go ahead and implement \nsupport for a few \"<command>.verbose\" configuration options during the \nnext week or so, and submit the patches.  I'll most probably come to \nsome more important conclusions while implementing that, which I'll \nrelay over, of course.\n"},{"id":"484034","messageId":"ZT1GWw886XuXwqlw.jacob@initialcommit.io","threadId":"60408","inReplyTo":"37e7bd8f6f4b75aa3b31dc98804b1334@manjaro.org","subject":"Re: [RFC PATCH v2 0/6] Noobify format for status, add, restore","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-28T17:35:23Z","receivedAt":"2023-10-28T17:35:29Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"On Sat, Oct 28, 2023 at 06:20:41PM +0200, Dragan Simic wrote:\n> > Hehe ok, maybe there is room in some hints to notify users of the\n> > extended option...\n> \n> I agree, there should be a well-placed hint, but we'd need to think really\n> well where to place it, so we don't annoy experienced git users too much,\n> while we also inform the less experienced users.\n\nYes, hmm, I wonder if maybe we could add the hint for the extended option\nonly in the case that the user uses the --verbose option either on the\ncommand line or via the config setting. Since using the verbose option\ntells us the user is asking for more details, that might be a good time\nto inform them of the _even more detailed_ option, but of course that\nhint could be disabled easily if they preferred the \"basic\" verbosity.\n\n> > > > Any thoughts on what the section in the config for a more general\n> > > > setting like this might be named?\n> > > \n> > > Maybe \"core.verbose\"?  We already have \"core.pager\", which kind of\n> > > affects the way all command outputs look like.\n> > \n> > Ok! The idea of using \"core\" came to mind but I wasn't sure if that was\n> > more for lower-level settings or more general things.\n> \n> I also considered the \"core\" section to be reserved for the very low-level\n> internal things, but having \"core.pager\" clearly allows other appropriate\n> configuration options to be placed here.\n\nOk awesome!\n\n> > Great. Thanks a lot for all the feedback. Let me doctor up the patch\n> > series to take these things into account and submit an RFC v3 :D\n> \n> Sounds good, thank you.  If you agree, I'll go ahead and implement support\n> for a few \"<command>.verbose\" configuration options during the next week or\n> so, and submit the patches.  I'll most probably come to some more important\n> conclusions while implementing that, which I'll relay over, of course.\n\nYes I agree, that sounds great! Maybe I'll just wait then until seeing\nyour implementation of that before I poke around on mine more. Then I'll\napply your patches locally to add my extended option.\n"},{"id":"484035","messageId":"fd54ef08fa676ec12ad6835f0122c4c0@manjaro.org","threadId":"60408","inReplyTo":"ZT1GWw886XuXwqlw.jacob@initialcommit.io","subject":"Re: [RFC PATCH v2 0/6] Noobify format for status, add, restore","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-28T17:41:47Z","receivedAt":"2023-10-28T17:41:51Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-28 19:35, Jacob Stopak wrote:\n> On Sat, Oct 28, 2023 at 06:20:41PM +0200, Dragan Simic wrote:\n>> I agree, there should be a well-placed hint, but we'd need to think \n>> really\n>> well where to place it, so we don't annoy experienced git users too \n>> much,\n>> while we also inform the less experienced users.\n> \n> Yes, hmm, I wonder if maybe we could add the hint for the extended \n> option\n> only in the case that the user uses the --verbose option either on the\n> command line or via the config setting. Since using the verbose option\n> tells us the user is asking for more details, that might be a good time\n> to inform them of the _even more detailed_ option, but of course that\n> hint could be disabled easily if they preferred the \"basic\" verbosity.\n\nThat's something we can keep thinking about, until we find a good \nsolution.  Also, a detailed review of the current logic behind \ndisplaying the hints should be performed first, if you agree.\n\n>> Sounds good, thank you.  If you agree, I'll go ahead and implement \n>> support\n>> for a few \"<command>.verbose\" configuration options during the next \n>> week or\n>> so, and submit the patches.  I'll most probably come to some more \n>> important\n>> conclusions while implementing that, which I'll relay over, of course.\n> \n> Yes I agree, that sounds great! Maybe I'll just wait then until seeing\n> your implementation of that before I poke around on mine more. Then \n> I'll\n> apply your patches locally to add my extended option.\n\nGreat, thanks.  I'll start working on the patches tomorrow or so, and \nI'll get back with any important conclusions or open questions arising \nfrom that, so we can discuss them further.\n"},{"id":"484036","messageId":"ZT1NeRSSUA9r0KdG.jacob@initialcommit.io","threadId":"60408","inReplyTo":"fd54ef08fa676ec12ad6835f0122c4c0@manjaro.org","subject":"Re: [RFC PATCH v2 0/6] Noobify format for status, add, restore","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-28T18:05:45Z","receivedAt":"2023-10-28T18:05:49Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"On Sat, Oct 28, 2023 at 07:41:47PM +0200, Dragan Simic wrote:\n> That's something we can keep thinking about, until we find a good solution.\n> Also, a detailed review of the current logic behind displaying the hints\n> should be performed first, if you agree.\n\nYes of course.\n\n> > Yes I agree, that sounds great! Maybe I'll just wait then until seeing\n> > your implementation of that before I poke around on mine more. Then I'll\n> > apply your patches locally to add my extended option.\n> \n> Great, thanks.  I'll start working on the patches tomorrow or so, and I'll\n> get back with any important conclusions or open questions arising from that,\n> so we can discuss them further.\n\n:)\n"},{"id":"484066","messageId":"xmqqjzr4gaie.fsf@gitster.g","threadId":"60408","inReplyTo":"20231026224615.675172-2-jacob@initialcommit.io","subject":"Re: [RFC PATCH v2 1/6] status: add noob format from status.noob config","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-10-30T01:32:57Z","receivedAt":"2023-10-30T01:33:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Stopak <jacob@initialcommit.io> writes:\n\n> diff --git a/table.c b/table.c\n> new file mode 100644\n> index 0000000000..15600e117f\n> --- /dev/null\n> +++ b/table.c\n\nYuck, do we need an entirely new file?  What trait are the things\nthat are thrown into this file together supposed to share [*]?  It\nis not very clear to me what the focus of this file is.\n\n\tSide note: for example, stuff in wt-status.c are to compute\n\tper-path status of the working tree and in-index files.\n\n> @@ -0,0 +1,117 @@\n> +#define USE_THE_INDEX_VARIABLE\n\nI personally do not mind, but I suspect many people hate to see this\ncompatibility set of macros used in a newly written source file.\n\n> +static const char *color(int slot, struct wt_status *s)\n> +{\n> +\tconst char *c = \"\";\n> +\tif (want_color(s->use_color))\n> +\t\tc = s->color_palette[slot];\n> +\tif (slot == WT_STATUS_ONBRANCH && color_is_nil(c))\n> +\t\tc = s->color_palette[WT_STATUS_HEADER];\n> +\treturn c;\n> +}\n\nDo we need to duplicate this from other files?  If this is about\n\"git status\", perhaps some parts of this patch, the truly new things\n(rather than what was copied, like this one) can be added to\nwt-status.c instead of adding a new file with unclear focus?\n\n> +static void build_table_border(struct strbuf *buf, int cols)\n> +{\n> +\tstrbuf_reset(buf);\n> +\tstrbuf_addchars(buf, '-', cols);\n> +}\n\nThis seems to be horizontal border; do we need a separate vertical\nborder?\n\n> +static void build_table_entry(struct strbuf *buf, char *entry, int cols)\n> +{\n> +\tstrbuf_reset(buf);\n> +\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2);\n> +\tstrbuf_addstr(buf, entry);\n> +\n> +\t/* Bump right padding if entry length is odd */\n> +\tif (!(strlen(entry) % 2))\n> +\t\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2 + 1);\n> +\telse\n> +\t\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2);\n> +}\n\nThe code assumes that one byte in string \"entry\" occupies one and\nonly one display columns, which is so 20th centry assumption that\ndoes not care about i18n.  Often what takes 3 bytes in a UTF-8\nstring occupies 2 display columns, for example.  In addition, if you\nplan to color entries in the table, some substring would end up to\nbe 0-width.  Your pathname may be so long that 1/3 of a window\nwidth may not be sufficient to show it in its entirety, you might\nneed to show it truncated in the middle.\n\nutf8.c has support for measuring the display width of UTF-8 string,\nwhich is used elsewhere in our code.  You may want to study it if\nyou want to do a \"tabular\" output.  The code in diff.c that shows\ndiffstat has many gems to help what this code wants to do, including\nmeasuring display columns of a string, chomping a long string to fit\nin a desired display columns, etc., by using helpers defined in\nutf8.c\n\nA potential excuse I can think of to have these outside wt-status.c\nand in a separate new file is to have a generic \"table\" layout\nmachinery that is independent from \"git status\" or what each column\nof the table is showing (in other words, they may not be pathnames),\nand reusable by other subcommands that want to show things in the\n\"table\" layout.  But even as a candidate for such a generic table\nmechanism, the above falls far short by hardcoding that it can only\nshow 3-col table whose columns are evenly distributed and nothing\nelse.\n\n> +static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct wt_status *s)\n> +{\n> +\tprintf(_(\"|\"));\n> +\tcolor_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), \"%s\", buf1->buf);\n> +\tprintf(_(\"|\"));\n> +\tcolor_fprintf(s->fp, color(WT_STATUS_CHANGED, s), \"%s\", buf2->buf);\n> +\tprintf(_(\"|\"));\n> +\tcolor_fprintf(s->fp, color(WT_STATUS_UPDATED, s), \"%s\", buf3->buf);\n> +\tprintf(_(\"|\\n\"));\n> +}\n\nHow does the code deal with unknown display width of translated\nversion of \"|\" emitted here?  Are you assuming that no matter how\nthese are translated, they will always occupy one display column\neach?\n\n> +void print_noob_status(struct wt_status *s)\n> +{\n> +\tstruct winsize w;\n> +\tint cols;\n> +\tstruct strbuf table_border = STRBUF_INIT;\n> +\tstruct strbuf table_col_entry_1 = STRBUF_INIT;\n> +\tstruct strbuf table_col_entry_2 = STRBUF_INIT;\n> +\tstruct strbuf table_col_entry_3 = STRBUF_INIT;\n> +\tstruct string_list_item *item;\n> +\n> +\t/* Get terminal width */\n> +\tioctl(STDOUT_FILENO, TIOCGWINSZ, &w);\n> +\tcols = w.ws_col;\n\nLet's not reinvent an incomplete solution before studying and\nfinding out what we already have in our codebase.  Immediately after\nyou got tempted to type TIOCGWINSZ, you can \"git grep\" the codebase\nfor that particular constant to see if we already use it, as that is\none very reasonable way to achieve what this piece of code wants to\ndo (i.e. find out what the display width would be).  You'd find\npager.c:term_columns() and also learned that we want to prepare for\nthe case where the ioctl() is not available.\n\n> +\tbuild_table_entry(&table_col_entry_1, \"Untracked files\", cols);\n> +\tbuild_table_entry(&table_col_entry_2, \"Unstaged changes\", cols);\n> +\tbuild_table_entry(&table_col_entry_3, \"Staging area\", cols);\n\nShouldn't these three strings be translatable?\n\nWhat shoudl happen when these labels are wider than cols/3?\n\n> diff --git a/table.h b/table.h\n> new file mode 100644\n> index 0000000000..c9e8c386de\n> --- /dev/null\n> +++ b/table.h\n> @@ -0,0 +1,6 @@\n> +#ifndef TABLE_H\n> +#define TABLE_H\n> +\n> +void print_noob_status(struct wt_status *s);\n> +\n> +#endif /* TABLE_H */\n\nI am guessing that your plan is to add other \"distim_noob_add()\" and\nother \"noob\" variant of operations for various Git subcommands here,\nbut I really do not think you want to add table.[ch] that has logic\nfor such random set of Git subcommands copied and tweaked from all\nover the place, as the only trait being shared among them will\nbecome \"they are written by Jacob Stopak\", that is not a very useful\ngrouping of the functions.  It is not even \"this file collects all\nthe code that produce tabular output from Git\"---\"git status -s\"\nalready gives tabular output, for example, without using any of the\n\"I only want to draw a table with three columns of equal width\"\nlogic.  Adding code that are necessary to add yet another output\nmode for \"git status\" directly to where various output modes of \"git\nstatus\" are implemented, i.e. wt-status.c, and do similar changes for\neach command would make more sense, I would think.\n\nThanks.\n\n"},{"id":"484067","messageId":"c31418c38996d1e67a4f3602458a5a91@manjaro.org","threadId":"60408","inReplyTo":"xmqqjzr4gaie.fsf@gitster.g","subject":"Re: [RFC PATCH v2 1/6] status: add noob format from status.noob config","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2023-10-30T01:38:01Z","receivedAt":"2023-10-30T01:38:06Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-30 02:32, Junio C Hamano wrote:\n> Jacob Stopak <jacob@initialcommit.io> writes:\n>> diff --git a/table.h b/table.h\n>> new file mode 100644\n>> index 0000000000..c9e8c386de\n>> --- /dev/null\n>> +++ b/table.h\n>> @@ -0,0 +1,6 @@\n>> +#ifndef TABLE_H\n>> +#define TABLE_H\n>> +\n>> +void print_noob_status(struct wt_status *s);\n>> +\n>> +#endif /* TABLE_H */\n> \n> I am guessing that your plan is to add other \"distim_noob_add()\" and\n> other \"noob\" variant of operations for various Git subcommands here,\n> but I really do not think you want to add table.[ch] that has logic\n> for such random set of Git subcommands copied and tweaked from all\n> over the place, as the only trait being shared among them will\n> become \"they are written by Jacob Stopak\", that is not a very useful\n> grouping of the functions.  It is not even \"this file collects all\n> the code that produce tabular output from Git\"---\"git status -s\"\n> already gives tabular output, for example, without using any of the\n> \"I only want to draw a table with three columns of equal width\"\n> logic.  Adding code that are necessary to add yet another output\n> mode for \"git status\" directly to where various output modes of \"git\n> status\" are implemented, i.e. wt-status.c, and do similar changes for\n> each command would make more sense, I would think.\n\nFurthermore, \"extended\" should perhaps be used instead of \"noob\" \nthroughout, to reflect the planned naming of the configuration option \nvalues.\n"},{"id":"484075","messageId":"ZT9IACbL574boSw/.jacob@initialcommit.io","threadId":"60408","inReplyTo":"xmqqjzr4gaie.fsf@gitster.g","subject":"Re: [RFC PATCH v2 1/6] status: add noob format from status.noob config","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2023-10-30T06:06:56Z","receivedAt":"2023-10-30T06:07:02Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"On Mon, Oct 30, 2023 at 10:32:57AM +0900, Junio C Hamano wrote:\n> Jacob Stopak <jacob@initialcommit.io> writes:\n> \n> > diff --git a/table.c b/table.c\n> > new file mode 100644\n> > index 0000000000..15600e117f\n> > --- /dev/null\n> > +++ b/table.c\n> \n> Yuck, do we need an entirely new file?  What trait are the things\n> that are thrown into this file together supposed to share [*]?  It\n> is not very clear to me what the focus of this file is.\n> \n> \tSide note: for example, stuff in wt-status.c are to compute\n> \tper-path status of the working tree and in-index files.\n\nI created a new file because:\n\n  * It will be used by more than just the status command (as patches 3/6\n    and 5/6 show it applied to the \"add\" and \"restore\" commands\n    respectively). And it will be applied to more commands as well.\n\n  * For these RFC versions I mainly wanted to get some kind of minimal\n    POC working, so I could get feedback and build on\n\n  * It was easier to work on it in it's own file.\n\nThat being said, I was probably careless with what I tossed in there.\nI intended it to specifically be things related to populating, coloring,\nand printing the new table format.\n\nBut after reading all of your comments here, I see that I most likely\nmade the error (which I've made before) of being too specific. If I\ngeneralize this file to be a generic table format that can be used for\nanything, then the command-specific details can likely be extracted into\ntheir respective existing files.\n\nAnother thing I totally didn't consider until I read your mail is the\npossibility of refactoring the existing \"git column\" functionality to\nprovide a configurable table output format. At this point I'm not sure\nif that's feasible or makes sense, but it's at least worth considering.\n\n> > @@ -0,0 +1,117 @@\n> > +#define USE_THE_INDEX_VARIABLE\n> \n> I personally do not mind, but I suspect many people hate to see this\n> compatibility set of macros used in a newly written source file.\n\nNoted, I'll try to update this.\n\n> > +static const char *color(int slot, struct wt_status *s)\n> > +{\n> > +\tconst char *c = \"\";\n> > +\tif (want_color(s->use_color))\n> > +\t\tc = s->color_palette[slot];\n> > +\tif (slot == WT_STATUS_ONBRANCH && color_is_nil(c))\n> > +\t\tc = s->color_palette[WT_STATUS_HEADER];\n> > +\treturn c;\n> > +}\n> \n> Do we need to duplicate this from other files?  If this is about\n> \"git status\", perhaps some parts of this patch, the truly new things\n> (rather than what was copied, like this one) can be added to\n> wt-status.c instead of adding a new file with unclear focus?\n\nI just re-used the status coloring out of laziness, since I was thinking\nabout this in a narrow way that it's basically replicating the output\nof Git status, so I figured I'd use the same colors.\n\nNow that I'm thinking more generally I see why this needs to change,\nto make table column output colors flexible.\n\n> > +static void build_table_border(struct strbuf *buf, int cols)\n> > +{\n> > +\tstrbuf_reset(buf);\n> > +\tstrbuf_addchars(buf, '-', cols);\n> > +}\n> \n> This seems to be horizontal border; do we need a separate vertical\n> border?\n\nYes it is a horizontal border, the vertical border pipe characters are\nincluded as a part of each table line being drawn, so I didn't need a\nstandalone vertical border in order to \"draw\" the tables. This one could\nbe renamed to reflect it's horizontal-ness, for clarity.\n\n> > +static void build_table_entry(struct strbuf *buf, char *entry, int cols)\n> > +{\n> > +\tstrbuf_reset(buf);\n> > +\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2);\n> > +\tstrbuf_addstr(buf, entry);\n> > +\n> > +\t/* Bump right padding if entry length is odd */\n> > +\tif (!(strlen(entry) % 2))\n> > +\t\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2 + 1);\n> > +\telse\n> > +\t\tstrbuf_addchars(buf, ' ', (cols / 3 - 1 - strlen(entry)) / 2);\n> > +}\n> \n> The code assumes that one byte in string \"entry\" occupies one and\n> only one display columns, which is so 20th centry assumption that\n> does not care about i18n.  Often what takes 3 bytes in a UTF-8\n> string occupies 2 display columns, for example.  In addition, if you\n> plan to color entries in the table, some substring would end up to\n> be 0-width.\n\nThanks for this info. I'll look into these.\n\n> Your pathname may be so long that 1/3 of a window\n> width may not be sufficient to show it in its entirety, you might\n> need to show it truncated in the middle.\n\nThis is exactly what patch 2/6 does (more details on that below). I\nshould have squashed that into 1/6. I'll do that in the next series.\n\n> utf8.c has support for measuring the display width of UTF-8 string,\n> which is used elsewhere in our code.  You may want to study it if\n> you want to do a \"tabular\" output.  The code in diff.c that shows\n> diffstat has many gems to help what this code wants to do, including\n> measuring display columns of a string, chomping a long string to fit\n> in a desired display columns, etc., by using helpers defined in\n> utf8.c\n\nWell that sounds like exactly what I need. I'll take a look, thanks!\n\n> A potential excuse I can think of to have these outside wt-status.c\n> and in a separate new file is to have a generic \"table\" layout\n> machinery that is independent from \"git status\" or what each column\n> of the table is showing (in other words, they may not be pathnames),\n> and reusable by other subcommands that want to show things in the\n> \"table\" layout.  \n\nYes that is my excuse (altho it may not be a valid one) - this patch\nseries adds the table output format for the \"git add\" (see patch 3/6) and\n\"git restore\" (see patch 5/6) commands. Dragan will be working on\nimplementing verbose config options for several commands, that I will then\nuse as stepping stones to try and add this table format as the \"extended\"\n(not \"noob\") verbosity option.\n\n> But even as a candidate for such a generic table\n> mechanism, the above falls far short by hardcoding that it can only\n> show 3-col table whose columns are evenly distributed and nothing\n> else.\n\nYeah - I was planning to update the table format so that the number of\ncolumns is flexible. For example, I can think of commands like \"git\nstash\" that would likely require 4 columns to display the required info,\nso parameterizing the number of columns is on my agenda.\n\nBut you're right maybe there are many other useful things that a generic\ntable format would include. Things like alignment, spacing, etc. I didn't\nthink about how this might be used outside of my specific use case - I\ntend to get tunnel vision on what I'm working on. I'll try to think more\nbroadly about how a format like this might be applicable, and at least\ntry to implement it in a way that it can be re-used for other things if\nand when the need arises.\n\n> > +static void print_table_body_line(struct strbuf *buf1, struct strbuf *buf2, struct strbuf *buf3, struct wt_status *s)\n> > +{\n> > +\tprintf(_(\"|\"));\n> > +\tcolor_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), \"%s\", buf1->buf);\n> > +\tprintf(_(\"|\"));\n> > +\tcolor_fprintf(s->fp, color(WT_STATUS_CHANGED, s), \"%s\", buf2->buf);\n> > +\tprintf(_(\"|\"));\n> > +\tcolor_fprintf(s->fp, color(WT_STATUS_UPDATED, s), \"%s\", buf3->buf);\n> > +\tprintf(_(\"|\\n\"));\n> > +}\n> \n> How does the code deal with unknown display width of translated\n> version of \"|\" emitted here?  Are you assuming that no matter how\n> these are translated, they will always occupy one display column\n> each?\n\nI'm not familiar with how the translation process works yet, so _if_ the\nversion of the \"|\" that I'm emitting here is a canditate for translation,\nit was not even my intention to do so, as I'm just using the vertical\npipe as a column separator and table border. I think what I need is to\nmake sure these are _not_ translatable, so that we can guarantee the\nsingle character display width? \n\n> > +void print_noob_status(struct wt_status *s)\n> > +{\n> > +\tstruct winsize w;\n> > +\tint cols;\n> > +\tstruct strbuf table_border = STRBUF_INIT;\n> > +\tstruct strbuf table_col_entry_1 = STRBUF_INIT;\n> > +\tstruct strbuf table_col_entry_2 = STRBUF_INIT;\n> > +\tstruct strbuf table_col_entry_3 = STRBUF_INIT;\n> > +\tstruct string_list_item *item;\n> > +\n> > +\t/* Get terminal width */\n> > +\tioctl(STDOUT_FILENO, TIOCGWINSZ, &w);\n> > +\tcols = w.ws_col;\n> \n> Let's not reinvent an incomplete solution before studying and\n> finding out what we already have in our codebase.  Immediately after\n> you got tempted to type TIOCGWINSZ, you can \"git grep\" the codebase\n> for that particular constant to see if we already use it, as that is\n> one very reasonable way to achieve what this piece of code wants to\n> do (i.e. find out what the display width would be).  You'd find\n> pager.c:term_columns() and also learned that we want to prepare for\n> the case where the ioctl() is not available.\n\nAh, ok let me look into that. I have utilized git grepping to understand\nhow various things are done in the codebase, but for some (no good) reason\nI didn't think to try that here.\n\n> > +\tbuild_table_entry(&table_col_entry_1, \"Untracked files\", cols);\n> > +\tbuild_table_entry(&table_col_entry_2, \"Unstaged changes\", cols);\n> > +\tbuild_table_entry(&table_col_entry_3, \"Staging area\", cols);\n> \n> Shouldn't these three strings be translatable?\n\nYes. I'll look into how translatable strings are done in the codebase.\n\n> What should happen when these labels are wider than cols/3?\n\nThat's what patch 2/6 does. It splices out the center of strings that\nare too long to fit in a column, keeping an equal number of characters\nat the start and end of the string, and inserts a \"...\" in the middle.\nAltho this makes sense for paths, this could be a bit confusing for\ntable headers, since it may be hard (or impossible) to read what the\ncolumn's purpose is, so it might be wiser to come up with even shorter\nversions of the table headers to use when the console width is below a\ncertain size that makes the original strings unreadable.\n\n> > diff --git a/table.h b/table.h\n> > new file mode 100644\n> > index 0000000000..c9e8c386de\n> > --- /dev/null\n> > +++ b/table.h\n> > @@ -0,0 +1,6 @@\n> > +#ifndef TABLE_H\n> > +#define TABLE_H\n> > +\n> > +void print_noob_status(struct wt_status *s);\n> > +\n> > +#endif /* TABLE_H */\n> \n> I am guessing that your plan is to add other \"distim_noob_add()\" and\n> other \"noob\" variant of operations for various Git subcommands here,\n> but I really do not think you want to add table.[ch] that has logic\n> for such random set of Git subcommands copied and tweaked from all\n> over the place, as the only trait being shared among them will\n> become \"they are written by Jacob Stopak\", that is not a very useful\n> grouping of the functions.  It is not even \"this file collects all\n> the code that produce tabular output from Git\"---\"git status -s\"\n> already gives tabular output, for example, without using any of the\n> \"I only want to draw a table with three columns of equal width\"\n> logic. Adding code that are necessary to add yet another output\n> mode for \"git status\" directly to where various output modes of \"git\n> status\" are implemented, i.e. wt-status.c, and do similar changes for\n> each command would make more sense, I would think.\n\nIf you look at patches 3/6 and 5/6 I think you'll better understand my\n\"plan\", not that I thought this would by any kind of final version, but\nmore of a starting point to get feedback and build on (hence the RFC).\n\nI think the reason I clung so hard to \"status\" is that it contains a\nlot of the info that I planned to display across various commands, so\nmuch so that I baked it into the new table file itself. I see now that\nneeds to be separated out, as that data to be displayed can be calculated\nin each command itself, and passed into the table functions to build and\nprint the thing.\n\nBut based on your input in this mail I'm going to:\n\n  * Look into the existing \"column\" formatter to see if it can be\n    repurposed for this table stuff.\n\n  * If not, generalize the table stuff so that it can be used for anything\n    and compartmentalize the command-specific stuff where it belongs.\n\n> Thanks.\n\nThank you!\n"},{"id":"486354","messageId":"778c4540924ad076269ac72097cf3789@manjaro.org","threadId":"60408","inReplyTo":"8d45763bb4fa4c7d1e1f69dfaf93e647@manjaro.org","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-01-05T19:14:34Z","receivedAt":"2024-01-05T19:14:35Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2023-10-24 04:21, Dragan Simic wrote:\n> On 2023-10-24 04:03, Junio C Hamano wrote:\n>> I think the \"use more verbose report format to help relatively\n>> inexperienced folks, in exchange for spending more screen real\n>> estate\" is a good direction to think about this thing.\n>> \n>> I am not personally interested in adding such support all that much\n>> myself, but one piece of advice I can offer those who are interested\n>> is not to be too deeply attached to the word \"table\".\n>> \n>> ... snip ...\n>> \n>> So be very careful when choosing what to call this new thing, and\n>> avoid naming it after the implementation details (e.g., in what\n>> particular shape the data gets presented) that may turn out not to\n>> be the most important part of the concept.\n> \n> Totally agreed, \"table\" simply sneaked in and remained here as the\n> term.  Perhaps \"<command>.verbose = extra\" or something similar would\n> be a good choice.\n\nJust a brief update...  Some of the associated patches are already ready \nto go, and some more are still pending to be implemented.  It took me a \nwhile, which I apologize for, and now I've also unfortunately contracted \nsome really bad flu.  I'll be back to the patches as soon as the flu \ndecides to go away.\n\n>> [Footnote]\n>> \n>>  * FWIW, \"git status -s\" is a tabular presentation.  Maybe we can\n>>    add a more verbose form of \"-s\" and be done with it for the\n>>    command?\n> \n> That's also an option.\n"},{"id":"486357","messageId":"ZZjar4li8R7Uo0c3.jacob@initialcommit.io","threadId":"60408","inReplyTo":"778c4540924ad076269ac72097cf3789@manjaro.org","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Jacob Stopak","fromEmail":"jacob@initialcommit.io","sentAt":"2024-01-06T04:44:31Z","receivedAt":"2024-01-06T04:44:36Z","isPatch":true,"sender":{"key":"jacob@initialcommit.io","avatar":"https://avatars.githubusercontent.com/u/49353917?v=4"},"body":"On Fri, Jan 05, 2024 at 08:14:34PM +0100, Dragan Simic wrote:\n> \n> Just a brief update...  Some of the associated patches are already ready to\n> go, and some more are still pending to be implemented.  It took me a while,\n> which I apologize for, and now I've also unfortunately contracted some\n> really bad flu.  I'll be back to the patches as soon as the flu decides to\n> go away.\n\nHey! No worries - thanks for letting me know. I have been thinking about this\nand am looking forward to getting back to it.\n\nHope you recover quickly. Please include me when those patches are ready. Then \nI will try to align my previous patches with them and also I have a bunch of\nrestructuring/refactoring to do based on the previous feedback from Junio.\n\nOh and happy new year.\n"},{"id":"486359","messageId":"32c16092c33682a09ef586a4c719f9a4@manjaro.org","threadId":"60408","inReplyTo":"ZZjar4li8R7Uo0c3.jacob@initialcommit.io","subject":"Re: [RFC PATCH 0/5] Introduce -t, --table for status/add commands","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-01-06T07:06:41Z","receivedAt":"2024-01-06T07:06:44Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-01-06 05:44, Jacob Stopak wrote:\n> On Fri, Jan 05, 2024 at 08:14:34PM +0100, Dragan Simic wrote:\n>> \n>> Just a brief update...  Some of the associated patches are already \n>> ready to\n>> go, and some more are still pending to be implemented.  It took me a \n>> while,\n>> which I apologize for, and now I've also unfortunately contracted some\n>> really bad flu.  I'll be back to the patches as soon as the flu \n>> decides to\n>> go away.\n> \n> Hey! No worries - thanks for letting me know. I have been thinking \n> about this\n> and am looking forward to getting back to it.\n> \n> Hope you recover quickly. Please include me when those patches are \n> ready. Then\n> I will try to align my previous patches with them and also I have a \n> bunch of\n> restructuring/refactoring to do based on the previous feedback from \n> Junio.\n\nI hope to recover soon.  It's been already about two miserable weeks \nsince the flu symptoms started, and it has since moved into my lungs, \nmaking things much worse than usual.\n\nSure, I'll also include a brief summary of our earlier discussions into \nthe cover letter, with the links to the mailing list archives, to \nprovide context for the patches for everyone reviewing them later.  \nLooking forward to these new git features!\n\n> Oh and happy new year.\n\nHappy New Year to you too, and to everybody on the mailing list! :)\n"}]}