Merge branch 'nd/columns'

A couple of commands learn --column option to produce columnar output.

By Nguyễn Thái Ngọc Duy (9) and Zbigniew Jędrzejewski-Szmek (1)
* nd/columns:
  tag: add --column
  column: support piping stdout to external git-column process
  status: add --column
  branch: add --column
  help: reuse print_columns() for help -a
  column: add dense layout support
  t9002: work around shells that are unable to set COLUMNS to 1
  column: add columnar layout
  Stop starting pager recursively
  Add column layout skeleton and git-column
maint
Junio C Hamano 2012-05-03 15:13:31 -07:00
commit f4ed0af6e2
27 changed files with 1105 additions and 48 deletions

1
.gitignore vendored
View File

@ -26,6 +26,7 @@
/git-cherry-pick /git-cherry-pick
/git-clean /git-clean
/git-clone /git-clone
/git-column
/git-commit /git-commit
/git-commit-tree /git-commit-tree
/git-config /git-config

View File

@ -856,6 +856,44 @@ color.ui::
`never` if you prefer git commands not to use color unless enabled `never` if you prefer git commands not to use color unless enabled
explicitly with some other configuration or the `--color` option. explicitly with some other configuration or the `--color` option.


column.ui::
Specify whether supported commands should output in columns.
This variable consists of a list of tokens separated by spaces
or commas:
+
--
`always`;;
always show in columns
`never`;;
never show in columns
`auto`;;
show in columns if the output is to the terminal
`column`;;
fill columns before rows (default)
`row`;;
fill rows before columns
`plain`;;
show in one column
`dense`;;
make unequal size columns to utilize more space
`nodense`;;
make equal size columns
--
+
This option defaults to 'never'.

column.branch::
Specify whether to output branch listing in `git branch` in columns.
See `column.ui` for details.

column.status::
Specify whether to output untracked files in `git status` in columns.
See `column.ui` for details.

column.tag::
Specify whether to output tag listing in `git tag` in columns.
See `column.ui` for details.

commit.status:: commit.status::
A boolean to enable/disable inclusion of status information in the A boolean to enable/disable inclusion of status information in the
commit message template when using an editor to prepare the commit commit message template when using an editor to prepare the commit

View File

@ -10,6 +10,7 @@ SYNOPSIS
[verse] [verse]
'git branch' [--color[=<when>] | --no-color] [-r | -a] 'git branch' [--color[=<when>] | --no-color] [-r | -a]
[--list] [-v [--abbrev=<length> | --no-abbrev]] [--list] [-v [--abbrev=<length> | --no-abbrev]]
[--column[=<options>] | --no-column]
[(--merged | --no-merged | --contains) [<commit>]] [<pattern>...] [(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]
'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>] 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
'git branch' (-m | -M) [<oldbranch>] <newbranch> 'git branch' (-m | -M) [<oldbranch>] <newbranch>
@ -107,6 +108,14 @@ OPTIONS
default to color output. default to color output.
Same as `--color=never`. Same as `--color=never`.


--column[=<options>]::
--no-column::
Display branch listing in columns. See configuration variable
column.branch for option syntax.`--column` and `--no-column`
without options are equivalent to 'always' and 'never' respectively.
+
This option is only applicable in non-verbose mode.

-r:: -r::
--remotes:: --remotes::
List or delete (if used with -d) the remote-tracking branches. List or delete (if used with -d) the remote-tracking branches.

View File

@ -0,0 +1,53 @@
git-column(1)
=============

NAME
----
git-column - Display data in columns

SYNOPSIS
--------
[verse]
'git column' [--command=<name>] [--[raw-]mode=<mode>] [--width=<width>]
[--indent=<string>] [--nl=<string>] [--pading=<n>]

DESCRIPTION
-----------
This command formats its input into multiple columns.

OPTIONS
-------
--command=<name>::
Look up layout mode using configuration variable column.<name> and
column.ui.

--mode=<mode>::
Specify layout mode. See configuration variable column.ui for option
syntax.

--raw-mode=<n>::
Same as --mode but take mode encoded as a number. This is mainly used
by other commands that have already parsed layout mode.

--width=<width>::
Specify the terminal width. By default 'git column' will detect the
terminal width, or fall back to 80 if it is unable to do so.

--indent=<string>::
String to be printed at the beginning of each line.

--nl=<N>::
String to be printed at the end of each line,
including newline character.

--padding=<N>::
The number of spaces between columns. One space by default.


Author
------
Written by Nguyen Thai Ngoc Duy <pclouds@gmail.com>

GIT
---
Part of the linkgit:git[1] suite

View File

@ -77,6 +77,13 @@ configuration variable documented in linkgit:git-config[1].
Terminate entries with NUL, instead of LF. This implies Terminate entries with NUL, instead of LF. This implies
the `--porcelain` output format if no other format is given. the `--porcelain` output format if no other format is given.


--column[=<options>]::
--no-column::
Display untracked files in columns. See configuration variable
column.status for option syntax.`--column` and `--no-column`
without options are equivalent to 'always' and 'never'
respectively.



OUTPUT OUTPUT
------ ------

View File

@ -13,6 +13,7 @@ SYNOPSIS
<tagname> [<commit> | <object>] <tagname> [<commit> | <object>]
'git tag' -d <tagname>... 'git tag' -d <tagname>...
'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>] 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
[--column[=<options>] | --no-column] [<pattern>...]
[<pattern>...] [<pattern>...]
'git tag' -v <tagname>... 'git tag' -v <tagname>...


@ -84,6 +85,14 @@ OPTIONS
using fnmatch(3)). Multiple patterns may be given; if any of using fnmatch(3)). Multiple patterns may be given; if any of
them matches, the tag is shown. them matches, the tag is shown.


--column[=<options>]::
--no-column::
Display tag listing in columns. See configuration variable
column.tag for option syntax.`--column` and `--no-column`
without options are equivalent to 'always' and 'never' respectively.
+
This option is only applicable when listing tags without annotation lines.

--contains <commit>:: --contains <commit>::
Only list tags which contain the specified commit. Only list tags which contain the specified commit.



View File

@ -688,6 +688,7 @@ LIB_OBJS += bulk-checkin.o
LIB_OBJS += bundle.o LIB_OBJS += bundle.o
LIB_OBJS += cache-tree.o LIB_OBJS += cache-tree.o
LIB_OBJS += color.o LIB_OBJS += color.o
LIB_OBJS += column.o
LIB_OBJS += combine-diff.o LIB_OBJS += combine-diff.o
LIB_OBJS += commit.o LIB_OBJS += commit.o
LIB_OBJS += compat/obstack.o LIB_OBJS += compat/obstack.o
@ -818,6 +819,7 @@ BUILTIN_OBJS += builtin/checkout-index.o
BUILTIN_OBJS += builtin/checkout.o BUILTIN_OBJS += builtin/checkout.o
BUILTIN_OBJS += builtin/clean.o BUILTIN_OBJS += builtin/clean.o
BUILTIN_OBJS += builtin/clone.o BUILTIN_OBJS += builtin/clone.o
BUILTIN_OBJS += builtin/column.o
BUILTIN_OBJS += builtin/commit-tree.o BUILTIN_OBJS += builtin/commit-tree.o
BUILTIN_OBJS += builtin/commit.o BUILTIN_OBJS += builtin/commit.o
BUILTIN_OBJS += builtin/config.o BUILTIN_OBJS += builtin/config.o
@ -2224,6 +2226,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h
builtin/commit.o builtin/revert.o wt-status.o: wt-status.h builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
builtin/tar-tree.o archive-tar.o: tar.h builtin/tar-tree.o archive-tar.o: tar.h
connect.o transport.o url.o http-backend.o: url.h connect.o transport.o url.o http-backend.o: url.h
builtin/branch.o builtin/commit.o builtin/tag.o column.o help.o pager.o: column.h
http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h



View File

@ -61,6 +61,7 @@ extern int cmd_cherry(int argc, const char **argv, const char *prefix);
extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix); extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
extern int cmd_clone(int argc, const char **argv, const char *prefix); extern int cmd_clone(int argc, const char **argv, const char *prefix);
extern int cmd_clean(int argc, const char **argv, const char *prefix); extern int cmd_clean(int argc, const char **argv, const char *prefix);
extern int cmd_column(int argc, const char **argv, const char *prefix);
extern int cmd_commit(int argc, const char **argv, const char *prefix); extern int cmd_commit(int argc, const char **argv, const char *prefix);
extern int cmd_commit_tree(int argc, const char **argv, const char *prefix); extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
extern int cmd_config(int argc, const char **argv, const char *prefix); extern int cmd_config(int argc, const char **argv, const char *prefix);

View File

@ -15,6 +15,8 @@
#include "branch.h" #include "branch.h"
#include "diff.h" #include "diff.h"
#include "revision.h" #include "revision.h"
#include "string-list.h"
#include "column.h"


static const char * const builtin_branch_usage[] = { static const char * const builtin_branch_usage[] = {
"git branch [options] [-r | -a] [--merged | --no-merged]", "git branch [options] [-r | -a] [--merged | --no-merged]",
@ -53,6 +55,9 @@ static enum merge_filter {
} merge_filter; } merge_filter;
static unsigned char merge_filter_ref[20]; static unsigned char merge_filter_ref[20];


static struct string_list output = STRING_LIST_INIT_DUP;
static unsigned int colopts;

static int parse_branch_color_slot(const char *var, int ofs) static int parse_branch_color_slot(const char *var, int ofs)
{ {
if (!strcasecmp(var+ofs, "plain")) if (!strcasecmp(var+ofs, "plain"))
@ -70,6 +75,8 @@ static int parse_branch_color_slot(const char *var, int ofs)


static int git_branch_config(const char *var, const char *value, void *cb) static int git_branch_config(const char *var, const char *value, void *cb)
{ {
if (!prefixcmp(var, "column."))
return git_column_config(var, value, "branch", &colopts);
if (!strcmp(var, "color.branch")) { if (!strcmp(var, "color.branch")) {
branch_use_color = git_config_colorbool(var, value); branch_use_color = git_config_colorbool(var, value);
return 0; return 0;
@ -482,7 +489,12 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
else if (verbose) else if (verbose)
/* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */ /* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
add_verbose_info(&out, item, verbose, abbrev); add_verbose_info(&out, item, verbose, abbrev);
printf("%s\n", out.buf); if (column_active(colopts)) {
assert(!verbose && "--column and --verbose are incompatible");
string_list_append(&output, out.buf);
} else {
printf("%s\n", out.buf);
}
strbuf_release(&name); strbuf_release(&name);
strbuf_release(&out); strbuf_release(&out);
} }
@ -741,6 +753,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG, PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
opt_parse_merge_filter, (intptr_t) "HEAD", opt_parse_merge_filter, (intptr_t) "HEAD",
}, },
OPT_COLUMN(0, "column", &colopts, "list branches in columns"),
OPT_END(), OPT_END(),
}; };


@ -763,6 +776,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
} }
hashcpy(merge_filter_ref, head_sha1); hashcpy(merge_filter_ref, head_sha1);



argc = parse_options(argc, argv, prefix, options, builtin_branch_usage, argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
0); 0);


@ -774,12 +788,22 @@ int cmd_branch(int argc, const char **argv, const char *prefix)


if (abbrev == -1) if (abbrev == -1)
abbrev = DEFAULT_ABBREV; abbrev = DEFAULT_ABBREV;
finalize_colopts(&colopts, -1);
if (verbose) {
if (explicitly_enable_column(colopts))
die(_("--column and --verbose are incompatible"));
colopts = 0;
}


if (delete) if (delete)
return delete_branches(argc, argv, delete > 1, kinds, quiet); return delete_branches(argc, argv, delete > 1, kinds, quiet);
else if (list) else if (list) {
return print_ref_list(kinds, detached, verbose, abbrev, int ret = print_ref_list(kinds, detached, verbose, abbrev,
with_commit, argv); with_commit, argv);
print_columns(&output, colopts, NULL);
string_list_clear(&output, 0);
return ret;
}
else if (edit_description) { else if (edit_description) {
const char *branch_name; const char *branch_name;
struct strbuf branch_ref = STRBUF_INIT; struct strbuf branch_ref = STRBUF_INIT;

59
builtin/column.c Normal file
View File

@ -0,0 +1,59 @@
#include "builtin.h"
#include "cache.h"
#include "strbuf.h"
#include "parse-options.h"
#include "string-list.h"
#include "column.h"

static const char * const builtin_column_usage[] = {
"git column [options]",
NULL
};
static unsigned int colopts;

static int column_config(const char *var, const char *value, void *cb)
{
return git_column_config(var, value, cb, &colopts);
}

int cmd_column(int argc, const char **argv, const char *prefix)
{
struct string_list list = STRING_LIST_INIT_DUP;
struct strbuf sb = STRBUF_INIT;
struct column_options copts;
const char *command = NULL, *real_command = NULL;
struct option options[] = {
OPT_STRING(0, "command", &real_command, "name", "lookup config vars"),
OPT_COLUMN(0, "mode", &colopts, "layout to use"),
OPT_INTEGER(0, "raw-mode", &colopts, "layout to use"),
OPT_INTEGER(0, "width", &copts.width, "Maximum width"),
OPT_STRING(0, "indent", &copts.indent, "string", "Padding space on left border"),
OPT_INTEGER(0, "nl", &copts.nl, "Padding space on right border"),
OPT_INTEGER(0, "padding", &copts.padding, "Padding space between columns"),
OPT_END()
};

/* This one is special and must be the first one */
if (argc > 1 && !prefixcmp(argv[1], "--command=")) {
command = argv[1] + 10;
git_config(column_config, (void *)command);
} else
git_config(column_config, NULL);

memset(&copts, 0, sizeof(copts));
copts.width = term_columns();
copts.padding = 1;
argc = parse_options(argc, argv, "", options, builtin_column_usage, 0);
if (argc)
usage_with_options(builtin_column_usage, options);
if (real_command || command) {
if (!real_command || !command || strcmp(real_command, command))
die(_("--command must be the first argument"));
}
finalize_colopts(&colopts, -1);
while (!strbuf_getline(&sb, stdin, '\n'))
string_list_append(&list, sb.buf);

print_columns(&list, colopts, &copts);
return 0;
}

View File

@ -27,6 +27,7 @@
#include "quote.h" #include "quote.h"
#include "submodule.h" #include "submodule.h"
#include "gpg-interface.h" #include "gpg-interface.h"
#include "column.h"


static const char * const builtin_commit_usage[] = { static const char * const builtin_commit_usage[] = {
"git commit [options] [--] <filepattern>...", "git commit [options] [--] <filepattern>...",
@ -88,6 +89,7 @@ static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
static int no_post_rewrite, allow_empty_message; static int no_post_rewrite, allow_empty_message;
static char *untracked_files_arg, *force_date, *ignore_submodule_arg; static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
static char *sign_commit; static char *sign_commit;
static unsigned int colopts;


/* /*
* The default commit message cleanup mode will remove the lines * The default commit message cleanup mode will remove the lines
@ -1173,6 +1175,8 @@ static int git_status_config(const char *k, const char *v, void *cb)
{ {
struct wt_status *s = cb; struct wt_status *s = cb;


if (!prefixcmp(k, "column."))
return git_column_config(k, v, "status", &colopts);
if (!strcmp(k, "status.submodulesummary")) { if (!strcmp(k, "status.submodulesummary")) {
int is_bool; int is_bool;
s->submodule_summary = git_config_bool_or_int(k, v, &is_bool); s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
@ -1238,6 +1242,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
{ OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, "when", { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, "when",
"ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)", "ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)",
PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
OPT_COLUMN(0, "column", &colopts, "list untracked files in columns"),
OPT_END(), OPT_END(),
}; };


@ -1251,6 +1256,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, argc = parse_options(argc, argv, prefix,
builtin_status_options, builtin_status_options,
builtin_status_usage, 0); builtin_status_usage, 0);
finalize_colopts(&colopts, -1);
s.colopts = colopts;


if (null_termination && status_format == STATUS_FORMAT_LONG) if (null_termination && status_format == STATUS_FORMAT_LONG)
status_format = STATUS_FORMAT_PORCELAIN; status_format = STATUS_FORMAT_PORCELAIN;

View File

@ -9,6 +9,7 @@
#include "common-cmds.h" #include "common-cmds.h"
#include "parse-options.h" #include "parse-options.h"
#include "run-command.h" #include "run-command.h"
#include "column.h"
#include "help.h" #include "help.h"


static struct man_viewer_list { static struct man_viewer_list {
@ -30,6 +31,7 @@ enum help_format {
}; };


static int show_all = 0; static int show_all = 0;
static unsigned int colopts;
static enum help_format help_format = HELP_FORMAT_NONE; static enum help_format help_format = HELP_FORMAT_NONE;
static struct option builtin_help_options[] = { static struct option builtin_help_options[] = {
OPT_BOOLEAN('a', "all", &show_all, "print all available commands"), OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
@ -251,6 +253,8 @@ static int add_man_viewer_info(const char *var, const char *value)


static int git_help_config(const char *var, const char *value, void *cb) static int git_help_config(const char *var, const char *value, void *cb)
{ {
if (!prefixcmp(var, "column."))
return git_column_config(var, value, "help", &colopts);
if (!strcmp(var, "help.format")) { if (!strcmp(var, "help.format")) {
if (!value) if (!value)
return config_error_nonbool(var); return config_error_nonbool(var);
@ -424,8 +428,9 @@ int cmd_help(int argc, const char **argv, const char *prefix)
parsed_help_format = help_format; parsed_help_format = help_format;


if (show_all) { if (show_all) {
git_config(git_help_config, NULL);
printf(_("usage: %s%s"), _(git_usage_string), "\n\n"); printf(_("usage: %s%s"), _(git_usage_string), "\n\n");
list_commands(&main_cmds, &other_cmds); list_commands(colopts, &main_cmds, &other_cmds);
printf("%s\n", _(git_more_info_string)); printf("%s\n", _(git_more_info_string));
return 0; return 0;
} }

View File

@ -16,6 +16,7 @@
#include "revision.h" #include "revision.h"
#include "gpg-interface.h" #include "gpg-interface.h"
#include "sha1-array.h" #include "sha1-array.h"
#include "column.h"


static const char * const git_tag_usage[] = { static const char * const git_tag_usage[] = {
"git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]", "git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
@ -33,6 +34,7 @@ struct tag_filter {
}; };


static struct sha1_array points_at; static struct sha1_array points_at;
static unsigned int colopts;


static int match_pattern(const char **patterns, const char *ref) static int match_pattern(const char **patterns, const char *ref)
{ {
@ -263,6 +265,8 @@ static int git_tag_config(const char *var, const char *value, void *cb)
int status = git_gpg_config(var, value, cb); int status = git_gpg_config(var, value, cb);
if (status) if (status)
return status; return status;
if (!prefixcmp(var, "column."))
return git_column_config(var, value, "tag", &colopts);
return git_default_config(var, value, cb); return git_default_config(var, value, cb);
} }


@ -459,6 +463,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_STRING('u', "local-user", &keyid, "key-id", OPT_STRING('u', "local-user", &keyid, "key-id",
"use another key to sign the tag"), "use another key to sign the tag"),
OPT__FORCE(&force, "replace the tag if exists"), OPT__FORCE(&force, "replace the tag if exists"),
OPT_COLUMN(0, "column", &colopts, "show tag list in columns"),


OPT_GROUP("Tag listing options"), OPT_GROUP("Tag listing options"),
{ {
@ -495,9 +500,25 @@ int cmd_tag(int argc, const char **argv, const char *prefix)


if (list + delete + verify > 1) if (list + delete + verify > 1)
usage_with_options(git_tag_usage, options); usage_with_options(git_tag_usage, options);
if (list) finalize_colopts(&colopts, -1);
return list_tags(argv, lines == -1 ? 0 : lines, if (list && lines != -1) {
with_commit); if (explicitly_enable_column(colopts))
die(_("--column and -n are incompatible"));
colopts = 0;
}
if (list) {
int ret;
if (column_active(colopts)) {
struct column_options copts;
memset(&copts, 0, sizeof(copts));
copts.padding = 2;
run_column_filter(colopts, &copts);
}
ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit);
if (column_active(colopts))
stop_column_filter();
return ret;
}
if (lines != -1) if (lines != -1)
die(_("-n option is only allowed with -l.")); die(_("-n option is only allowed with -l."));
if (with_commit) if (with_commit)

434
column.c Normal file
View File

@ -0,0 +1,434 @@
#include "cache.h"
#include "column.h"
#include "string-list.h"
#include "parse-options.h"
#include "run-command.h"
#include "utf8.h"

#define XY2LINEAR(d, x, y) (COL_LAYOUT((d)->colopts) == COL_COLUMN ? \
(x) * (d)->rows + (y) : \
(y) * (d)->cols + (x))

struct column_data {
const struct string_list *list;
unsigned int colopts;
struct column_options opts;

int rows, cols;
int *len; /* cell length */
int *width; /* index to the longest row in column */
};

/* return length of 's' in letters, ANSI escapes stripped */
static int item_length(unsigned int colopts, const char *s)
{
int len, i = 0;
struct strbuf str = STRBUF_INIT;

strbuf_addstr(&str, s);
while ((s = strstr(str.buf + i, "\033[")) != NULL) {
int len = strspn(s + 2, "0123456789;");
i = s - str.buf;
strbuf_remove(&str, i, len + 3); /* \033[<len><func char> */
}
len = utf8_strwidth(str.buf);
strbuf_release(&str);
return len;
}

/*
* Calculate cell width, rows and cols for a table of equal cells, given
* table width and how many spaces between cells.
*/
static void layout(struct column_data *data, int *width)
{
int i;

*width = 0;
for (i = 0; i < data->list->nr; i++)
if (*width < data->len[i])
*width = data->len[i];

*width += data->opts.padding;

data->cols = (data->opts.width - strlen(data->opts.indent)) / *width;
if (data->cols == 0)
data->cols = 1;

data->rows = DIV_ROUND_UP(data->list->nr, data->cols);
}

static void compute_column_width(struct column_data *data)
{
int i, x, y;
for (x = 0; x < data->cols; x++) {
data->width[x] = XY2LINEAR(data, x, 0);
for (y = 0; y < data->rows; y++) {
i = XY2LINEAR(data, x, y);
if (i < data->list->nr &&
data->len[data->width[x]] < data->len[i])
data->width[x] = i;
}
}
}

/*
* Shrink all columns by shortening them one row each time (and adding
* more columns along the way). Hopefully the longest cell will be
* moved to the next column, column is shrunk so we have more space
* for new columns. The process ends when the whole thing no longer
* fits in data->total_width.
*/
static void shrink_columns(struct column_data *data)
{
data->width = xrealloc(data->width,
sizeof(*data->width) * data->cols);
while (data->rows > 1) {
int x, total_width, cols, rows;
rows = data->rows;
cols = data->cols;

data->rows--;
data->cols = DIV_ROUND_UP(data->list->nr, data->rows);
if (data->cols != cols)
data->width = xrealloc(data->width,
sizeof(*data->width) * data->cols);
compute_column_width(data);

total_width = strlen(data->opts.indent);
for (x = 0; x < data->cols; x++) {
total_width += data->len[data->width[x]];
total_width += data->opts.padding;
}
if (total_width > data->opts.width) {
data->rows = rows;
data->cols = cols;
break;
}
}
compute_column_width(data);
}

/* Display without layout when not enabled */
static void display_plain(const struct string_list *list,
const char *indent, const char *nl)
{
int i;

for (i = 0; i < list->nr; i++)
printf("%s%s%s", indent, list->items[i].string, nl);
}

/* Print a cell to stdout with all necessary leading/traling space */
static int display_cell(struct column_data *data, int initial_width,
const char *empty_cell, int x, int y)
{
int i, len, newline;

i = XY2LINEAR(data, x, y);
if (i >= data->list->nr)
return -1;

len = data->len[i];
if (data->width && data->len[data->width[x]] < initial_width) {
/*
* empty_cell has initial_width chars, if real column
* is narrower, increase len a bit so we fill less
* space.
*/
len += initial_width - data->len[data->width[x]];
len -= data->opts.padding;
}

if (COL_LAYOUT(data->colopts) == COL_COLUMN)
newline = i + data->rows >= data->list->nr;
else
newline = x == data->cols - 1 || i == data->list->nr - 1;

printf("%s%s%s",
x == 0 ? data->opts.indent : "",
data->list->items[i].string,
newline ? data->opts.nl : empty_cell + len);
return 0;
}

/* Display COL_COLUMN or COL_ROW */
static void display_table(const struct string_list *list,
unsigned int colopts,
const struct column_options *opts)
{
struct column_data data;
int x, y, i, initial_width;
char *empty_cell;

memset(&data, 0, sizeof(data));
data.list = list;
data.colopts = colopts;
data.opts = *opts;

data.len = xmalloc(sizeof(*data.len) * list->nr);
for (i = 0; i < list->nr; i++)
data.len[i] = item_length(colopts, list->items[i].string);

layout(&data, &initial_width);

if (colopts & COL_DENSE)
shrink_columns(&data);

empty_cell = xmalloc(initial_width + 1);
memset(empty_cell, ' ', initial_width);
empty_cell[initial_width] = '\0';
for (y = 0; y < data.rows; y++) {
for (x = 0; x < data.cols; x++)
if (display_cell(&data, initial_width, empty_cell, x, y))
break;
}

free(data.len);
free(data.width);
free(empty_cell);
}

void print_columns(const struct string_list *list, unsigned int colopts,
const struct column_options *opts)
{
struct column_options nopts;

if (!list->nr)
return;
assert((colopts & COL_ENABLE_MASK) != COL_AUTO);

memset(&nopts, 0, sizeof(nopts));
nopts.indent = opts && opts->indent ? opts->indent : "";
nopts.nl = opts && opts->nl ? opts->nl : "\n";
nopts.padding = opts ? opts->padding : 1;
nopts.width = opts && opts->width ? opts->width : term_columns() - 1;
if (!column_active(colopts)) {
display_plain(list, "", "\n");
return;
}
switch (COL_LAYOUT(colopts)) {
case COL_PLAIN:
display_plain(list, nopts.indent, nopts.nl);
break;
case COL_ROW:
case COL_COLUMN:
display_table(list, colopts, &nopts);
break;
default:
die("BUG: invalid layout mode %d", COL_LAYOUT(colopts));
}
}

int finalize_colopts(unsigned int *colopts, int stdout_is_tty)
{
if ((*colopts & COL_ENABLE_MASK) == COL_AUTO) {
if (stdout_is_tty < 0)
stdout_is_tty = isatty(1);
*colopts &= ~COL_ENABLE_MASK;
if (stdout_is_tty)
*colopts |= COL_ENABLED;
}
return 0;
}

struct colopt {
const char *name;
unsigned int value;
unsigned int mask;
};

#define LAYOUT_SET 1
#define ENABLE_SET 2

static int parse_option(const char *arg, int len, unsigned int *colopts,
int *group_set)
{
struct colopt opts[] = {
{ "always", COL_ENABLED, COL_ENABLE_MASK },
{ "never", COL_DISABLED, COL_ENABLE_MASK },
{ "auto", COL_AUTO, COL_ENABLE_MASK },
{ "plain", COL_PLAIN, COL_LAYOUT_MASK },
{ "column", COL_COLUMN, COL_LAYOUT_MASK },
{ "row", COL_ROW, COL_LAYOUT_MASK },
{ "dense", COL_DENSE, 0 },
};
int i;

for (i = 0; i < ARRAY_SIZE(opts); i++) {
int set = 1, arg_len = len, name_len;
const char *arg_str = arg;

if (!opts[i].mask) {
if (arg_len > 2 && !strncmp(arg_str, "no", 2)) {
arg_str += 2;
arg_len -= 2;
set = 0;
}
}

name_len = strlen(opts[i].name);
if (arg_len != name_len ||
strncmp(arg_str, opts[i].name, name_len))
continue;

switch (opts[i].mask) {
case COL_ENABLE_MASK:
*group_set |= ENABLE_SET;
break;
case COL_LAYOUT_MASK:
*group_set |= LAYOUT_SET;
break;
}

if (opts[i].mask)
*colopts = (*colopts & ~opts[i].mask) | opts[i].value;
else {
if (set)
*colopts |= opts[i].value;
else
*colopts &= ~opts[i].value;
}
return 0;
}

return error("unsupported option '%s'", arg);
}

static int parse_config(unsigned int *colopts, const char *value)
{
const char *sep = " ,";
int group_set = 0;

while (*value) {
int len = strcspn(value, sep);
if (len) {
if (parse_option(value, len, colopts, &group_set))
return -1;

value += len;
}
value += strspn(value, sep);
}
/*
* Setting layout implies "always" if neither always, never
* nor auto is specified.
*
* Current value in COL_ENABLE_MASK is disregarded. This means if
* you set column.ui = auto and pass --column=row, then "auto"
* will become "always".
*/
if ((group_set & LAYOUT_SET) && !(group_set & ENABLE_SET))
*colopts = (*colopts & ~COL_ENABLE_MASK) | COL_ENABLED;
return 0;
}

static int column_config(const char *var, const char *value,
const char *key, unsigned int *colopts)
{
if (!value)
return config_error_nonbool(var);
if (parse_config(colopts, value))
return error("invalid column.%s mode %s", key, value);
return 0;
}

int git_column_config(const char *var, const char *value,
const char *command, unsigned int *colopts)
{
const char *it = skip_prefix(var, "column.");
if (!it)
return 0;

if (!strcmp(it, "ui"))
return column_config(var, value, "ui", colopts);

if (command && !strcmp(it, command))
return column_config(var, value, it, colopts);

return 0;
}

int parseopt_column_callback(const struct option *opt,
const char *arg, int unset)
{
unsigned int *colopts = opt->value;
*colopts |= COL_PARSEOPT;
*colopts &= ~COL_ENABLE_MASK;
if (unset) /* --no-column == never */
return 0;
/* --column == always unless "arg" states otherwise */
*colopts |= COL_ENABLED;
if (arg)
return parse_config(colopts, arg);

return 0;
}

static int fd_out = -1;
static struct child_process column_process;

int run_column_filter(int colopts, const struct column_options *opts)
{
const char *av[10];
int ret, ac = 0;
struct strbuf sb_colopt = STRBUF_INIT;
struct strbuf sb_width = STRBUF_INIT;
struct strbuf sb_padding = STRBUF_INIT;

if (fd_out != -1)
return -1;

av[ac++] = "column";
strbuf_addf(&sb_colopt, "--raw-mode=%d", colopts);
av[ac++] = sb_colopt.buf;
if (opts && opts->width) {
strbuf_addf(&sb_width, "--width=%d", opts->width);
av[ac++] = sb_width.buf;
}
if (opts && opts->indent) {
av[ac++] = "--indent";
av[ac++] = opts->indent;
}
if (opts && opts->padding) {
strbuf_addf(&sb_padding, "--padding=%d", opts->padding);
av[ac++] = sb_padding.buf;
}
av[ac] = NULL;

fflush(stdout);
memset(&column_process, 0, sizeof(column_process));
column_process.in = -1;
column_process.out = dup(1);
column_process.git_cmd = 1;
column_process.argv = av;

ret = start_command(&column_process);

strbuf_release(&sb_colopt);
strbuf_release(&sb_width);
strbuf_release(&sb_padding);

if (ret)
return -2;

fd_out = dup(1);
close(1);
dup2(column_process.in, 1);
close(column_process.in);
return 0;
}

int stop_column_filter(void)
{
if (fd_out == -1)
return -1;

fflush(stdout);
close(1);
finish_command(&column_process);
dup2(fd_out, 1);
close(fd_out);
fd_out = -1;
return 0;
}

45
column.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef COLUMN_H
#define COLUMN_H

#define COL_LAYOUT_MASK 0x000F
#define COL_ENABLE_MASK 0x0030 /* always, never or auto */
#define COL_PARSEOPT 0x0040 /* --column is given from cmdline */
#define COL_DENSE 0x0080 /* Shrink columns when possible,
making space for more columns */

#define COL_DISABLED 0x0000 /* must be zero */
#define COL_ENABLED 0x0010
#define COL_AUTO 0x0020

#define COL_LAYOUT(c) ((c) & COL_LAYOUT_MASK)
#define COL_COLUMN 0 /* Fill columns before rows */
#define COL_ROW 1 /* Fill rows before columns */
#define COL_PLAIN 15 /* one column */

#define explicitly_enable_column(c) \
(((c) & COL_PARSEOPT) && column_active(c))

struct column_options {
int width;
int padding;
const char *indent;
const char *nl;
};

struct option;
extern int parseopt_column_callback(const struct option *, const char *, int);
extern int git_column_config(const char *var, const char *value,
const char *command, unsigned int *colopts);
extern int finalize_colopts(unsigned int *colopts, int stdout_is_tty);
static inline int column_active(unsigned int colopts)
{
return (colopts & COL_ENABLE_MASK) == COL_ENABLED;
}

extern void print_columns(const struct string_list *list, unsigned int colopts,
const struct column_options *opts);

extern int run_column_filter(int colopts, const struct column_options *);
extern int stop_column_filter(void);

#endif

View File

@ -20,6 +20,7 @@ git-cherry-pick mainporcelain
git-citool mainporcelain git-citool mainporcelain
git-clean mainporcelain git-clean mainporcelain
git-clone mainporcelain common git-clone mainporcelain common
git-column purehelpers
git-commit mainporcelain common git-commit mainporcelain common
git-commit-tree plumbingmanipulators git-commit-tree plumbingmanipulators
git-config ancillarymanipulators git-config ancillarymanipulators

1
git.c
View File

@ -348,6 +348,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE }, { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE }, { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
{ "clone", cmd_clone }, { "clone", cmd_clone },
{ "column", cmd_column, RUN_SETUP_GENTLY },
{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE }, { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
{ "commit-tree", cmd_commit_tree, RUN_SETUP }, { "commit-tree", cmd_commit_tree, RUN_SETUP },
{ "config", cmd_config, RUN_SETUP_GENTLY }, { "config", cmd_config, RUN_SETUP_GENTLY },

58
help.c
View File

@ -4,6 +4,8 @@
#include "levenshtein.h" #include "levenshtein.h"
#include "help.h" #include "help.h"
#include "common-cmds.h" #include "common-cmds.h"
#include "string-list.h"
#include "column.h"


void add_cmdname(struct cmdnames *cmds, const char *name, int len) void add_cmdname(struct cmdnames *cmds, const char *name, int len)
{ {
@ -70,31 +72,25 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
cmds->cnt = cj; cmds->cnt = cj;
} }


static void pretty_print_string_list(struct cmdnames *cmds, int longest) static void pretty_print_string_list(struct cmdnames *cmds,
unsigned int colopts)
{ {
int cols = 1, rows; struct string_list list = STRING_LIST_INIT_NODUP;
int space = longest + 1; /* min 1 SP between words */ struct column_options copts;
int max_cols = term_columns() - 1; /* don't print *on* the edge */ int i;
int i, j;


if (space < max_cols) for (i = 0; i < cmds->cnt; i++)
cols = max_cols / space; string_list_append(&list, cmds->names[i]->name);
rows = DIV_ROUND_UP(cmds->cnt, cols); /*

* always enable column display, we only consult column.*
for (i = 0; i < rows; i++) { * about layout strategy and stuff
printf(" "); */

colopts = (colopts & ~COL_ENABLE_MASK) | COL_ENABLED;
for (j = 0; j < cols; j++) { memset(&copts, 0, sizeof(copts));
int n = j * rows + i; copts.indent = " ";
int size = space; copts.padding = 2;
if (n >= cmds->cnt) print_columns(&list, colopts, &copts);
break; string_list_clear(&list, 0);
if (j == cols-1 || n + rows >= cmds->cnt)
size = 1;
printf("%-*s", size, cmds->names[n]->name);
}
putchar('\n');
}
} }


static int is_executable(const char *name) static int is_executable(const char *name)
@ -203,29 +199,21 @@ void load_command_list(const char *prefix,
exclude_cmds(other_cmds, main_cmds); exclude_cmds(other_cmds, main_cmds);
} }


void list_commands(struct cmdnames *main_cmds, struct cmdnames *other_cmds) void list_commands(unsigned int colopts,
struct cmdnames *main_cmds, struct cmdnames *other_cmds)
{ {
int i, longest = 0;

for (i = 0; i < main_cmds->cnt; i++)
if (longest < main_cmds->names[i]->len)
longest = main_cmds->names[i]->len;
for (i = 0; i < other_cmds->cnt; i++)
if (longest < other_cmds->names[i]->len)
longest = other_cmds->names[i]->len;

if (main_cmds->cnt) { if (main_cmds->cnt) {
const char *exec_path = git_exec_path(); const char *exec_path = git_exec_path();
printf_ln(_("available git commands in '%s'"), exec_path); printf_ln(_("available git commands in '%s'"), exec_path);
putchar('\n'); putchar('\n');
pretty_print_string_list(main_cmds, longest); pretty_print_string_list(main_cmds, colopts);
putchar('\n'); putchar('\n');
} }


if (other_cmds->cnt) { if (other_cmds->cnt) {
printf_ln(_("git commands available from elsewhere on your $PATH")); printf_ln(_("git commands available from elsewhere on your $PATH"));
putchar('\n'); putchar('\n');
pretty_print_string_list(other_cmds, longest); pretty_print_string_list(other_cmds, colopts);
putchar('\n'); putchar('\n');
} }
} }

3
help.h
View File

@ -25,7 +25,6 @@ extern void add_cmdname(struct cmdnames *cmds, const char *name, int len);
/* Here we require that excludes is a sorted list. */ /* Here we require that excludes is a sorted list. */
extern void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes); extern void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
extern int is_in_cmdlist(struct cmdnames *cmds, const char *name); extern int is_in_cmdlist(struct cmdnames *cmds, const char *name);
extern void list_commands(struct cmdnames *main_cmds, extern void list_commands(unsigned int colopts, struct cmdnames *main_cmds, struct cmdnames *other_cmds);
struct cmdnames *other_cmds);


#endif /* HELP_H */ #endif /* HELP_H */

View File

@ -73,7 +73,7 @@ void setup_pager(void)
{ {
const char *pager = git_pager(isatty(1)); const char *pager = git_pager(isatty(1));


if (!pager) if (!pager || pager_in_use())
return; return;


/* /*

View File

@ -234,5 +234,7 @@ extern int parse_opt_noop_cb(const struct option *, const char *, int);
PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 } PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }
#define OPT__COLOR(var, h) \ #define OPT__COLOR(var, h) \
OPT_COLOR_FLAG(0, "color", (var), (h)) OPT_COLOR_FLAG(0, "color", (var), (h))
#define OPT_COLUMN(s, l, v, h) \
{ OPTION_CALLBACK, (s), (l), (v), "style", (h), PARSE_OPT_OPTARG, parseopt_column_callback }


#endif #endif

View File

@ -160,6 +160,83 @@ test_expect_success 'git branch --list -d t should fail' '
test_path_is_missing .git/refs/heads/t test_path_is_missing .git/refs/heads/t
' '


test_expect_success 'git branch --column' '
COLUMNS=81 git branch --column=column >actual &&
cat >expected <<\EOF &&
a/b/c bam foo l * master n o/p r
abc bar j/k m/m master2 o/o q
EOF
test_cmp expected actual
'

test_expect_success 'git branch --column with an extremely long branch name' '
long=this/is/a/part/of/long/branch/name &&
long=z$long/$long/$long/$long &&
test_when_finished "git branch -d $long" &&
git branch $long &&
COLUMNS=80 git branch --column=column >actual &&
cat >expected <<EOF &&
a/b/c
abc
bam
bar
foo
j/k
l
m/m
* master
master2
n
o/o
o/p
q
r
$long
EOF
test_cmp expected actual
'

test_expect_success 'git branch with column.*' '
git config column.ui column &&
git config column.branch "dense" &&
COLUMNS=80 git branch >actual &&
git config --unset column.branch &&
git config --unset column.ui &&
cat >expected <<\EOF &&
a/b/c bam foo l * master n o/p r
abc bar j/k m/m master2 o/o q
EOF
test_cmp expected actual
'

test_expect_success 'git branch --column -v should fail' '
test_must_fail git branch --column -v
'

test_expect_success 'git branch -v with column.ui ignored' '
git config column.ui column &&
COLUMNS=80 git branch -v | cut -c -10 | sed "s/ *$//" >actual &&
git config --unset column.ui &&
cat >expected <<\EOF &&
a/b/c
abc
bam
bar
foo
j/k
l
m/m
* master
master2
n
o/o
o/p
q
r
EOF
test_cmp expected actual
'

mv .git/config .git/config-saved mv .git/config .git/config-saved


test_expect_success 'git branch -m q q2 without config should succeed' ' test_expect_success 'git branch -m q q2 without config should succeed' '

View File

@ -263,6 +263,50 @@ test_expect_success 'tag -l can accept multiple patterns' '
test_cmp expect actual test_cmp expect actual
' '


test_expect_success 'listing tags in column' '
COLUMNS=40 git tag -l --column=row >actual &&
cat >expected <<\EOF &&
a1 aa1 cba t210 t211
v0.2.1 v1.0 v1.0.1 v1.1.3
EOF
test_cmp expected actual
'

test_expect_success 'listing tags in column with column.*' '
git config column.tag row &&
git config column.ui dense &&
COLUMNS=40 git tag -l >actual &&
git config --unset column.ui &&
git config --unset column.tag &&
cat >expected <<\EOF &&
a1 aa1 cba t210 t211
v0.2.1 v1.0 v1.0.1 v1.1.3
EOF
test_cmp expected actual
'

test_expect_success 'listing tag with -n --column should fail' '
test_must_fail git tag --column -n
'

test_expect_success 'listing tags -n in column with column.ui ignored' '
git config column.ui "row dense" &&
COLUMNS=40 git tag -l -n >actual &&
git config --unset column.ui &&
cat >expected <<\EOF &&
a1 Foo
aa1 Foo
cba Foo
t210 Foo
t211 Foo
v0.2.1 Foo
v1.0 Foo
v1.0.1 Foo
v1.1.3 Foo
EOF
test_cmp expected actual
'

# creating and verifying lightweight tags: # creating and verifying lightweight tags:


test_expect_success \ test_expect_success \

View File

@ -59,6 +59,30 @@ test_expect_success 'status (1)' '
test_i18ngrep "use \"git rm --cached <file>\.\.\.\" to unstage" output test_i18ngrep "use \"git rm --cached <file>\.\.\.\" to unstage" output
' '


test_expect_success 'status --column' '
COLUMNS=50 git status --column="column dense" >output &&
cat >expect <<\EOF &&
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: dir2/added
#
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: dir1/modified
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# dir1/untracked dir2/untracked untracked
# dir2/modified output
EOF
test_cmp expect output
'

cat >expect <<\EOF cat >expect <<\EOF
# On branch master # On branch master
# Changes to be committed: # Changes to be committed:

180
t/t9002-column.sh Executable file
View File

@ -0,0 +1,180 @@
#!/bin/sh

test_description='git column'
. ./test-lib.sh

test_expect_success 'setup' '
cat >lista <<\EOF
one
two
three
four
five
six
seven
eight
nine
ten
eleven
EOF
'

test_expect_success 'never' '
git column --indent=Z --mode=never <lista >actual &&
test_cmp lista actual
'

test_expect_success 'always' '
cat >expected <<\EOF &&
Zone
Ztwo
Zthree
Zfour
Zfive
Zsix
Zseven
Zeight
Znine
Zten
Zeleven
EOF
git column --indent=Z --mode=plain <lista >actual &&
test_cmp expected actual
'

test_expect_success '80 columns' '
cat >expected <<\EOF &&
one two three four five six seven eight nine ten eleven
EOF
COLUMNS=80 git column --mode=column <lista >actual &&
test_cmp expected actual
'

cat >expected <<\EOF
one
two
three
four
five
six
seven
eight
nine
ten
eleven
EOF

test_expect_success COLUMNS_CAN_BE_1 'COLUMNS = 1' '
COLUMNS=1 git column --mode=column <lista >actual &&
test_cmp expected actual
'

test_expect_success 'width = 1' '
git column --mode=column --width=1 <lista >actual &&
test_cmp expected actual
'

COLUMNS=20
export COLUMNS

test_expect_success '20 columns' '
cat >expected <<\EOF &&
one seven
two eight
three nine
four ten
five eleven
six
EOF
git column --mode=column <lista >actual &&
test_cmp expected actual
'

test_expect_success '20 columns, nodense' '
cat >expected <<\EOF &&
one seven
two eight
three nine
four ten
five eleven
six
EOF
git column --mode=column,nodense < lista > actual &&
test_cmp expected actual
'

test_expect_success '20 columns, dense' '
cat >expected <<\EOF &&
one five nine
two six ten
three seven eleven
four eight
EOF
git column --mode=column,dense < lista > actual &&
test_cmp expected actual
'

test_expect_success '20 columns, padding 2' '
cat >expected <<\EOF &&
one seven
two eight
three nine
four ten
five eleven
six
EOF
git column --mode=column --padding 2 <lista >actual &&
test_cmp expected actual
'

test_expect_success '20 columns, indented' '
cat >expected <<\EOF &&
one seven
two eight
three nine
four ten
five eleven
six
EOF
git column --mode=column --indent=" " <lista >actual &&
test_cmp expected actual
'

test_expect_success '20 columns, row first' '
cat >expected <<\EOF &&
one two
three four
five six
seven eight
nine ten
eleven
EOF
git column --mode=row <lista >actual &&
test_cmp expected actual
'

test_expect_success '20 columns, row first, nodense' '
cat >expected <<\EOF &&
one two
three four
five six
seven eight
nine ten
eleven
EOF
git column --mode=row,nodense <lista >actual &&
test_cmp expected actual
'

test_expect_success '20 columns, row first, dense' '
cat >expected <<\EOF &&
one two three
four five six
seven eight nine
ten eleven
EOF
git column --mode=row,dense <lista >actual &&
test_cmp expected actual
'

test_done

View File

@ -11,6 +11,7 @@
#include "remote.h" #include "remote.h"
#include "refs.h" #include "refs.h"
#include "submodule.h" #include "submodule.h"
#include "column.h"


static char default_wt_status_colors[][COLOR_MAXLEN] = { static char default_wt_status_colors[][COLOR_MAXLEN] = {
GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */ GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
@ -641,6 +642,8 @@ static void wt_status_print_other(struct wt_status *s,
{ {
int i; int i;
struct strbuf buf = STRBUF_INIT; struct strbuf buf = STRBUF_INIT;
static struct string_list output = STRING_LIST_INIT_DUP;
struct column_options copts;


if (!l->nr) if (!l->nr)
return; return;
@ -649,12 +652,33 @@ static void wt_status_print_other(struct wt_status *s,


for (i = 0; i < l->nr; i++) { for (i = 0; i < l->nr; i++) {
struct string_list_item *it; struct string_list_item *it;
const char *path;
it = &(l->items[i]); it = &(l->items[i]);
path = quote_path(it->string, strlen(it->string),
&buf, s->prefix);
if (column_active(s->colopts)) {
string_list_append(&output, path);
continue;
}
status_printf(s, color(WT_STATUS_HEADER, s), "\t"); status_printf(s, color(WT_STATUS_HEADER, s), "\t");
status_printf_more(s, color(WT_STATUS_UNTRACKED, s), status_printf_more(s, color(WT_STATUS_UNTRACKED, s),
"%s\n", quote_path(it->string, strlen(it->string), "%s\n", path);
&buf, s->prefix));
} }

strbuf_release(&buf);
if (!column_active(s->colopts))
return;

strbuf_addf(&buf, "%s#\t%s",
color(WT_STATUS_HEADER, s),
color(WT_STATUS_UNTRACKED, s));
memset(&copts, 0, sizeof(copts));
copts.padding = 1;
copts.indent = buf.buf;
if (want_color(s->use_color))
copts.nl = GIT_COLOR_RESET "\n";
print_columns(&output, s->colopts, &copts);
string_list_clear(&output, 0);
strbuf_release(&buf); strbuf_release(&buf);
} }



View File

@ -56,6 +56,7 @@ struct wt_status {
enum untracked_status_type show_untracked_files; enum untracked_status_type show_untracked_files;
const char *ignore_submodule_arg; const char *ignore_submodule_arg;
char color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN]; char color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];
int colopts;


/* These are computed during processing of the individual sections */ /* These are computed during processing of the individual sections */
int commitable; int commitable;