{"thread":{"id":"36247","subject":"[PATCH/RFC 0/8] git-ls","startedAt":"2014-03-20T10:15:43Z","lastAt":"2014-04-07T17:13:31Z","messageCount":79,"participants":["Nguyễn Thái Ngọc Duy","Matthieu Moy","Duy Nguyen","Junio C Hamano","David Tran","Eric Sunshine"],"isPatch":true,"patchVersion":1,"patchTotal":8},"messages":[{"id":"237154","messageId":"1395310551-23201-1-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":null,"subject":"[PATCH/RFC 0/8] git-ls","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-20T10:15:43Z","receivedAt":"2014-03-20T10:15:43Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Last time I tried this was more than two years ago [1]. It's time for\nanother try and see if the community has any interest in it.\n\nThe command is straight forward, it's a ls-like version for listing\nthings in git. It respects $LS_COLORS and does column output like GNU\nls. \"ls\" shows cached entries (but no recursion), \"ls -o\"\nshow untracked files. I want ls-tree, \"diff --name-only\" and \"diff\n--name-only --cached\" too, but they are not implemented yet.\n\nWIP quality, this is to gather comments on the idea.\n\n[1] http://thread.gmane.org/gmane.comp.version-control.git/166405\n\nNguyễn Thái Ngọc Duy (8):\n  Import $LS_COLORS parsing code from coreutils\n  ls_colors.c: a bit of document on print_color_indicator input\n  ls_colors.c: enable coloring on u+x files\n  ls_colors.c: new color descriptors\n  ls-files: add --color to highlight based on $LS_COLORS\n  ls-files: add --column\n  ls-files: support --max-depth\n  Add git-ls, a user friendly version of ls-files and more\n\n Makefile           |   1 +\n builtin.h          |   1 +\n builtin/ls-files.c |  80 ++++++++-\n git.c              |   1 +\n ls_colors.c (new)  | 487 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n ls_colors.h (new)  |  20 +++\n 6 files changed, 588 insertions(+), 2 deletions(-)\n create mode 100644 ls_colors.c\n create mode 100644 ls_colors.h\n\n-- \n1.9.0.40.gaa8c3ea\n"},{"id":"237155","messageId":"1395310551-23201-2-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395310551-23201-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 1/8] Import $LS_COLORS parsing code from coreutils","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-20T10:15:44Z","receivedAt":"2014-03-20T10:15:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This could could help highlight files in ls-files or status output, or\neven diff --name-only (but that's questionable).\n\nThis code is from coreutils.git commit\n7326d1f1a67edf21947ae98194f98c38b6e9e527 file src/ls.c. This is the\nlast GPL-2 commit before coreutils turns to GPL-3.\n\nThe code is reformatted to fit Git coding style, which is more than\njust adding and removing spaces. For example, \"bool\" is replaced with\n\"int\", or true/false replaced with 1/0, or the use of git's error()\ninstead of error(3). There are also two \"#if 0\" to make it build with\ngit-compat-util.h.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Makefile          |   1 +\n ls_colors.c (new) | 477 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n ls_colors.h (new) |  20 +++\n 3 files changed, 498 insertions(+)\n create mode 100644 ls_colors.c\n create mode 100644 ls_colors.h\n\ndiff --git a/Makefile b/Makefile\nindex f818eec..f6a6e14 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -819,6 +819,7 @@ LIB_OBJS += list-objects.o\n LIB_OBJS += ll-merge.o\n LIB_OBJS += lockfile.o\n LIB_OBJS += log-tree.o\n+LIB_OBJS += ls_colors.o\n LIB_OBJS += mailmap.o\n LIB_OBJS += match-trees.o\n LIB_OBJS += merge.o\ndiff --git a/ls_colors.c b/ls_colors.c\nnew file mode 100644\nindex 0000000..6385446\n--- /dev/null\n+++ b/ls_colors.c\n@@ -0,0 +1,477 @@\n+#include \"git-compat-util.h\"\n+#include \"gettext.h\"\n+#include \"ls_colors.h\"\n+\n+#define STREQ(a, b) (strcmp(a, b) == 0)\n+\n+enum indicator_no {\n+\tC_LEFT, C_RIGHT, C_END, C_NORM, C_FILE, C_DIR, C_LINK, C_FIFO, C_SOCK,\n+\tC_BLK, C_CHR, C_MISSING, C_ORPHAN, C_EXEC, C_DOOR, C_SETUID, C_SETGID,\n+\tC_STICKY, C_OTHER_WRITABLE, C_STICKY_OTHER_WRITABLE\n+};\n+\n+#define FILETYPE_INDICATORS\t\t\t\t\\\n+  {\t\t\t\t\t\t\t\\\n+    C_ORPHAN, C_FIFO, C_CHR, C_DIR, C_BLK, C_FILE,\t\\\n+    C_LINK, C_SOCK, C_FILE, C_DIR\t\t\t\\\n+  }\n+\n+struct bin_str {\n+\tsize_t len;\t\t\t/* Number of bytes */\n+\tconst char *string;\t\t/* Pointer to the same */\n+};\n+\n+struct color_ext_type {\n+\tstruct bin_str ext;\t\t/* The extension we're looking for */\n+\tstruct bin_str seq;\t\t/* The sequence to output when we do */\n+\tstruct color_ext_type *next;\t/* Next in list */\n+};\n+\n+static const char *const indicator_name[]= {\n+\t\"lc\", \"rc\", \"ec\", \"no\", \"fi\", \"di\", \"ln\", \"pi\", \"so\",\n+\t\"bd\", \"cd\", \"mi\", \"or\", \"ex\", \"do\", \"su\", \"sg\", \"st\",\n+\t\"ow\", \"tw\", NULL\n+};\n+\n+#define LEN_STR_PAIR(s) sizeof(s) - 1, s\n+static struct bin_str color_indicator[] = {\n+\t{ LEN_STR_PAIR(\"\\033[\") },\t/* lc: Left of color sequence */\n+\t{ LEN_STR_PAIR(\"m\") },\t\t/* rc: Right of color sequence */\n+\t{ 0, NULL },\t\t\t/* ec: End color (replaces lc+no+rc) */\n+\t{ LEN_STR_PAIR(\"0\") },\t\t/* no: Normal */\n+\t{ LEN_STR_PAIR(\"0\") },\t\t/* fi: File: default */\n+\t{ LEN_STR_PAIR(\"01;34\") },\t/* di: Directory: bright blue */\n+\t{ LEN_STR_PAIR(\"01;36\") },\t/* ln: Symlink: bright cyan */\n+\t{ LEN_STR_PAIR(\"33\") },\t\t/* pi: Pipe: yellow/brown */\n+\t{ LEN_STR_PAIR(\"01;35\") },\t/* so: Socket: bright magenta */\n+\t{ LEN_STR_PAIR(\"01;33\") },\t/* bd: Block device: bright yellow */\n+\t{ LEN_STR_PAIR(\"01;33\") },\t/* cd: Char device: bright yellow */\n+\t{ 0, NULL },\t\t\t/* mi: Missing file: undefined */\n+\t{ 0, NULL },\t\t\t/* or: Orphaned symlink: undefined */\n+\t{ LEN_STR_PAIR(\"01;32\") },\t/* ex: Executable: bright green */\n+\t{ LEN_STR_PAIR(\"01;35\") },\t/* do: Door: bright magenta */\n+\t{ LEN_STR_PAIR(\"37;41\") },\t/* su: setuid: white on red */\n+\t{ LEN_STR_PAIR(\"30;43\") },\t/* sg: setgid: black on yellow */\n+\t{ LEN_STR_PAIR(\"37;44\") },\t/* st: sticky: black on blue */\n+\t{ LEN_STR_PAIR(\"34;42\") },\t/* ow: other-writable: blue on green */\n+\t{ LEN_STR_PAIR(\"30;42\") },\t/* tw: ow w/ sticky: black on green */\n+};\n+\n+static struct color_ext_type *color_ext_list = NULL;\n+/* Buffer for color sequences */\n+static char *color_buf;\n+\n+/*\n+ * True means use colors to mark types.  Also define the different\n+ * colors as well as the stuff for the LS_COLORS environment variable.\n+ * The LS_COLORS variable is now in a termcap-like format.\n+ */\n+static int print_with_color;\n+\n+/*\n+ * When true, in a color listing, color each symlink name according to the\n+ * type of file it points to.  Otherwise, color them according to the `ln'\n+ * directive in LS_COLORS.  Dangling (orphan) symlinks are treated specially,\n+ * regardless.  This is set when `ln=target' appears in LS_COLORS.\n+ */\n+static int color_symlink_as_referent;\n+\n+/*\n+ * Parse a string as part of the LS_COLORS variable; this may involve\n+ * decoding all kinds of escape characters.  If equals_end is set an\n+ * unescaped equal sign ends the string, otherwise only a : or \\0\n+ * does.  Set *OUTPUT_COUNT to the number of bytes output.  Return\n+ * true if successful.\n+ *\n+ * The resulting string is *not* null-terminated, but may contain\n+ * embedded nulls.\n+ *\n+ * Note that both dest and src are char **; on return they point to\n+ * the first free byte after the array and the character that ended\n+ * the input string, respectively.\n+ */\n+static int get_funky_string(char **dest, const char **src, int equals_end,\n+\t\t\t    size_t *output_count)\n+{\n+\tchar num;\t\t\t/* For numerical codes */\n+\tsize_t count;\t\t\t/* Something to count with */\n+\tenum {\n+\t\tST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX,\n+\t\tST_CARET, ST_END, ST_ERROR\n+\t} state;\n+\tconst char *p;\n+\tchar *q;\n+\n+\tp = *src;\t\t\t/* We don't want to double-indirect */\n+\tq = *dest;\t\t\t/* the whole darn time.  */\n+\n+\tcount = 0;\t\t\t/* No characters counted in yet.  */\n+\tnum = 0;\n+\n+\tstate = ST_GND;\t\t/* Start in ground state.  */\n+\twhile (state < ST_END) {\n+\t\tswitch (state) {\n+\t\tcase ST_GND:\t\t/* Ground state (no escapes) */\n+\t\t\tswitch (*p) {\n+\t\t\tcase ':':\n+\t\t\tcase '\\0':\n+\t\t\t\tstate = ST_END;\t/* End of string */\n+\t\t\t\tbreak;\n+\t\t\tcase '\\\\':\n+\t\t\t\tstate = ST_BACKSLASH; /* Backslash scape sequence */\n+\t\t\t\t++p;\n+\t\t\t\tbreak;\n+\t\t\tcase '^':\n+\t\t\t\tstate = ST_CARET; /* Caret escape */\n+\t\t\t\t++p;\n+\t\t\t\tbreak;\n+\t\t\tcase '=':\n+\t\t\t\tif (equals_end) {\n+\t\t\t\t\tstate = ST_END; /* End */\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\t/* else fall through */\n+\t\t\tdefault:\n+\t\t\t\t*(q++) = *(p++);\n+\t\t\t\t++count;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase ST_BACKSLASH:\t/* Backslash escaped character */\n+\t\t\tswitch (*p) {\n+\t\t\tcase '0':\n+\t\t\tcase '1':\n+\t\t\tcase '2':\n+\t\t\tcase '3':\n+\t\t\tcase '4':\n+\t\t\tcase '5':\n+\t\t\tcase '6':\n+\t\t\tcase '7':\n+\t\t\t\tstate = ST_OCTAL;\t/* Octal sequence */\n+\t\t\t\tnum = *p - '0';\n+\t\t\t\tbreak;\n+\t\t\tcase 'x':\n+\t\t\tcase 'X':\n+\t\t\t\tstate = ST_HEX;\t/* Hex sequence */\n+\t\t\t\tnum = 0;\n+\t\t\t\tbreak;\n+\t\t\tcase 'a':\t\t/* Bell */\n+\t\t\t\tnum = '\\a';\n+\t\t\t\tbreak;\n+\t\t\tcase 'b':\t\t/* Backspace */\n+\t\t\t\tnum = '\\b';\n+\t\t\t\tbreak;\n+\t\t\tcase 'e':\t\t/* Escape */\n+\t\t\t\tnum = 27;\n+\t\t\t\tbreak;\n+\t\t\tcase 'f':\t\t/* Form feed */\n+\t\t\t\tnum = '\\f';\n+\t\t\t\tbreak;\n+\t\t\tcase 'n':\t\t/* Newline */\n+\t\t\t\tnum = '\\n';\n+\t\t\t\tbreak;\n+\t\t\tcase 'r':\t\t/* Carriage return */\n+\t\t\t\tnum = '\\r';\n+\t\t\t\tbreak;\n+\t\t\tcase 't':\t\t/* Tab */\n+\t\t\t\tnum = '\\t';\n+\t\t\t\tbreak;\n+\t\t\tcase 'v':\t\t/* Vtab */\n+\t\t\t\tnum = '\\v';\n+\t\t\t\tbreak;\n+\t\t\tcase '?':\t\t/* Delete */\n+\t\t\t\tnum = 127;\n+\t\t\t\tbreak;\n+\t\t\tcase '_':\t\t/* Space */\n+\t\t\t\tnum = ' ';\n+\t\t\t\tbreak;\n+\t\t\tcase '\\0':\t\t/* End of string */\n+\t\t\t\tstate = ST_ERROR;\t/* Error! */\n+\t\t\t\tbreak;\n+\t\t\tdefault:\t\t/* Escaped character like \\ ^ : = */\n+\t\t\t\tnum = *p;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tif (state == ST_BACKSLASH) {\n+\t\t\t\t*(q++) = num;\n+\t\t\t\t++count;\n+\t\t\t\tstate = ST_GND;\n+\t\t\t}\n+\t\t\t++p;\n+\t\t\tbreak;\n+\n+\t\tcase ST_OCTAL:\t\t/* Octal sequence */\n+\t\t\tif (*p < '0' || *p > '7') {\n+\t\t\t\t*(q++) = num;\n+\t\t\t\t++count;\n+\t\t\t\tstate = ST_GND;\n+\t\t\t} else\n+\t\t\t\tnum = (num << 3) + (*(p++) - '0');\n+\t\t\tbreak;\n+\n+\t\tcase ST_HEX:\t\t/* Hex sequence */\n+\t\t\tswitch (*p) {\n+\t\t\tcase '0':\n+\t\t\tcase '1':\n+\t\t\tcase '2':\n+\t\t\tcase '3':\n+\t\t\tcase '4':\n+\t\t\tcase '5':\n+\t\t\tcase '6':\n+\t\t\tcase '7':\n+\t\t\tcase '8':\n+\t\t\tcase '9':\n+\t\t\t\tnum = (num << 4) + (*(p++) - '0');\n+\t\t\t\tbreak;\n+\t\t\tcase 'a':\n+\t\t\tcase 'b':\n+\t\t\tcase 'c':\n+\t\t\tcase 'd':\n+\t\t\tcase 'e':\n+\t\t\tcase 'f':\n+\t\t\t\tnum = (num << 4) + (*(p++) - 'a') + 10;\n+\t\t\t\tbreak;\n+\t\t\tcase 'A':\n+\t\t\tcase 'B':\n+\t\t\tcase 'C':\n+\t\t\tcase 'D':\n+\t\t\tcase 'E':\n+\t\t\tcase 'F':\n+\t\t\t\tnum = (num << 4) + (*(p++) - 'A') + 10;\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\t*(q++) = num;\n+\t\t\t\t++count;\n+\t\t\t\tstate = ST_GND;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase ST_CARET:\t\t/* Caret escape */\n+\t\t\tstate = ST_GND;\t/* Should be the next state... */\n+\t\t\tif (*p >= '@' && *p <= '~') {\n+\t\t\t\t*(q++) = *(p++) & 037;\n+\t\t\t\t++count;\n+\t\t\t} else if (*p == '?') {\n+\t\t\t\t*(q++) = 127;\n+\t\t\t\t++count;\n+\t\t\t} else\n+\t\t\t\tstate = ST_ERROR;\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tabort();\n+\t\t}\n+\t}\n+\n+\t*dest = q;\n+\t*src = p;\n+\t*output_count = count;\n+\n+\treturn state != ST_ERROR;\n+}\n+\n+void parse_ls_color(void)\n+{\n+\tconst char *p;\t\t\t/* Pointer to character being parsed */\n+\tchar *buf;\t\t\t/* color_buf buffer pointer */\n+\tint state;\t\t\t/* State of parser */\n+\tint ind_no;\t\t\t/* Indicator number */\n+\tchar label[3];\t\t\t/* Indicator label */\n+\tstruct color_ext_type *ext;\t/* Extension we are working on */\n+\n+\tif ((p = getenv(\"LS_COLORS\")) == NULL || *p == '\\0')\n+\t\treturn;\n+\n+\text = NULL;\n+\tstrcpy (label, \"??\");\n+\n+\t/*\n+\t * This is an overly conservative estimate, but any possible\n+\t * LS_COLORS string will *not* generate a color_buf longer\n+\t * than itself, so it is a safe way of allocating a buffer in\n+\t * advance.\n+\t */\n+\tbuf = color_buf = xstrdup(p);\n+\n+\tstate = 1;\n+\twhile (state > 0) {\n+\t\tswitch (state) {\n+\t\tcase 1:\t\t/* First label character */\n+\t\t\tswitch (*p) {\n+\t\t\tcase ':':\n+\t\t\t\t++p;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase '*':\n+\t\t\t\t/*\n+\t\t\t\t * Allocate new extension block and add to head of\n+\t\t\t\t * linked list (this way a later definition will\n+\t\t\t\t * override an earlier one, which can be useful for\n+\t\t\t\t * having terminal-specific defs override global).\n+\t\t\t\t */\n+\n+\t\t\t\text = xmalloc(sizeof *ext);\n+\t\t\t\text->next = color_ext_list;\n+\t\t\t\tcolor_ext_list = ext;\n+\n+\t\t\t\t++p;\n+\t\t\t\text->ext.string = buf;\n+\n+\t\t\t\tstate = (get_funky_string(&buf, &p, 1, &ext->ext.len)\n+\t\t\t\t\t ? 4 : -1);\n+\t\t\t\tbreak;\n+\n+\t\t\tcase '\\0':\n+\t\t\t\tstate = 0;\t/* Done! */\n+\t\t\t\tbreak;\n+\n+\t\t\tdefault:\t/* Assume it is file type label */\n+\t\t\t\tlabel[0] = *(p++);\n+\t\t\t\tstate = 2;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase 2:\t\t/* Second label character */\n+\t\t\tif (*p) {\n+\t\t\t\tlabel[1] = *(p++);\n+\t\t\t\tstate = 3;\n+\t\t\t} else\n+\t\t\t\tstate = -1;\t/* Error */\n+\t\t\tbreak;\n+\n+\t\tcase 3:\t\t/* Equal sign after indicator label */\n+\t\t\tstate = -1;\t/* Assume failure...  */\n+\t\t\tif (*(p++) == '=') { /* It *should* be...  */\n+\t\t\t\tfor (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no) {\n+\t\t\t\t\tif (STREQ (label, indicator_name[ind_no])) {\n+\t\t\t\t\t\tcolor_indicator[ind_no].string = buf;\n+\t\t\t\t\t\tstate = (get_funky_string(&buf, &p, 0,\n+\t\t\t\t\t\t\t\t\t  &color_indicator[ind_no].len)\n+\t\t\t\t\t\t\t ? 1 : -1);\n+\t\t\t\t\t\tbreak;\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t\tif (state == -1)\n+\t\t\t\t\terror(_(\"unrecognized prefix: %s\"), label);\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase 4:\t\t/* Equal sign after *.ext */\n+\t\t\tif (*(p++) == '=') {\n+\t\t\t\text->seq.string = buf;\n+\t\t\t\tstate = (get_funky_string(&buf, &p, 0, &ext->seq.len)\n+\t\t\t\t\t ? 1 : -1);\n+\t\t\t} else\n+\t\t\t\tstate = -1;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (state < 0) {\n+\t\tstruct color_ext_type *e;\n+\t\tstruct color_ext_type *e2;\n+\n+\t\terror(_(\"unparsable value for LS_COLORS environment variable\"));\n+\t\tfree(color_buf);\n+\t\tfor (e = color_ext_list; e != NULL; /* empty */) {\n+\t\t\te2 = e;\n+\t\t\te = e->next;\n+\t\t\tfree(e2);\n+\t\t}\n+\t\tprint_with_color = 0;\n+\t}\n+\n+\tif (color_indicator[C_LINK].len == 6 &&\n+\t    !strncmp(color_indicator[C_LINK].string, \"target\", 6))\n+\t\tcolor_symlink_as_referent = 1;\n+}\n+\n+/* Output a color indicator (which may contain nulls).  */\n+static void put_indicator(const struct bin_str *ind)\n+{\n+\tsize_t i;\n+\tconst char *p;\n+\n+\tp = ind->string;\n+\n+\tfor (i = ind->len; i != 0; --i)\n+\t\tputchar(*(p++));\n+}\n+\n+void print_color_indicator(const char *name, mode_t mode, int linkok,\n+\t\t\t   int stat_ok, enum filetype filetype)\n+{\n+\tint type;\n+\tstruct color_ext_type *ext;\t/* Color extension */\n+\tsize_t len;\t\t\t/* Length of name */\n+\n+\t/* Is this a nonexistent file?  If so, linkok == -1.  */\n+\n+\tif (linkok == -1 && color_indicator[C_MISSING].string != NULL)\n+\t\ttype = C_MISSING;\n+\telse if (!stat_ok) {\n+\t\tstatic enum indicator_no filetype_indicator[] = FILETYPE_INDICATORS;\n+\t\ttype = filetype_indicator[filetype];\n+\t} else {\n+\t\tif (S_ISREG(mode)) {\n+\t\t\ttype = C_FILE;\n+\t\t\tif ((mode & S_ISUID) != 0)\n+\t\t\t\ttype = C_SETUID;\n+\t\t\telse if ((mode & S_ISGID) != 0)\n+\t\t\t\ttype = C_SETGID;\n+#if 0\n+\t\t\telse if ((mode & S_IXUGO) != 0)\n+\t\t\t\ttype = C_EXEC;\n+#endif\n+\t\t} else if (S_ISDIR(mode)) {\n+\t\t\tif ((mode & S_ISVTX) && (mode & S_IWOTH))\n+\t\t\t\ttype = C_STICKY_OTHER_WRITABLE;\n+\t\t\telse if ((mode & S_IWOTH) != 0)\n+\t\t\t\ttype = C_OTHER_WRITABLE;\n+\t\t\telse if ((mode & S_ISVTX) != 0)\n+\t\t\t\ttype = C_STICKY;\n+\t\t\telse\n+\t\t\t\ttype = C_DIR;\n+\t\t} else if (S_ISLNK(mode))\n+\t\t\ttype = ((!linkok && color_indicator[C_ORPHAN].string)\n+\t\t\t\t? C_ORPHAN : C_LINK);\n+\t\telse if (S_ISFIFO(mode))\n+\t\t\ttype = C_FIFO;\n+\t\telse if (S_ISSOCK(mode))\n+\t\t\ttype = C_SOCK;\n+\t\telse if (S_ISBLK(mode))\n+\t\t\ttype = C_BLK;\n+\t\telse if (S_ISCHR(mode))\n+\t\t\ttype = C_CHR;\n+#if 0\n+\t\telse if (S_ISDOOR(mode))\n+\t\t\ttype = C_DOOR;\n+#endif\n+\t\telse {\n+\t\t\t/* Classify a file of some other type as C_ORPHAN.  */\n+\t\t\ttype = C_ORPHAN;\n+\t\t}\n+\t}\n+\n+\t/* Check the file's suffix only if still classified as C_FILE.  */\n+\text = NULL;\n+\tif (type == C_FILE) {\n+\t\t/* Test if NAME has a recognized suffix.  */\n+\n+\t\tlen = strlen(name);\n+\t\tname += len;\t\t/* Pointer to final \\0.  */\n+\t\tfor (ext = color_ext_list; ext != NULL; ext = ext->next) {\n+\t\t\tif (ext->ext.len <= len\n+\t\t\t    && strncmp(name - ext->ext.len, ext->ext.string,\n+\t\t\t\t       ext->ext.len) == 0)\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tput_indicator(&color_indicator[C_LEFT]);\n+\tput_indicator(ext ? &(ext->seq) : &color_indicator[type]);\n+\tput_indicator(&color_indicator[C_RIGHT]);\n+}\ndiff --git a/ls_colors.h b/ls_colors.h\nnew file mode 100644\nindex 0000000..3201be6\n--- /dev/null\n+++ b/ls_colors.h\n@@ -0,0 +1,20 @@\n+#ifndef LS_COLORS_H\n+#define LS_COLORS_H\n+\n+enum filetype {\n+\tunknown,\n+\tfifo,\n+\tchardev,\n+\tdirectory,\n+\tblockdev,\n+\tnormal,\n+\tsymbolic_link,\n+\tsock,\n+\twhiteout,\n+\targ_directory\n+};\n+\n+void parse_ls_color(void);\n+void print_color_indicator(const char *name, mode_t mode, int linkok,\n+\t\t\t   int stat_ok, enum filetype filetype);\n+#endif\n-- \n1.9.0.40.gaa8c3ea\n"},{"id":"237156","messageId":"1395310551-23201-3-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395310551-23201-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 2/8] ls_colors.c: a bit of document on print_color_indicator input","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-20T10:15:45Z","receivedAt":"2014-03-20T10:15:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n ls_colors.c | 5 +++++\n 1 file changed, 5 insertions(+)\n\ndiff --git a/ls_colors.c b/ls_colors.c\nindex 6385446..d492ab3 100644\n--- a/ls_colors.c\n+++ b/ls_colors.c\n@@ -401,6 +401,11 @@ static void put_indicator(const struct bin_str *ind)\n \t\tputchar(*(p++));\n }\n \n+/*\n+ * mode is only used if stat_ok is true, else filetype is used. if\n+ * linkok is -1, shows broken link. If linkok is zero and mode says\n+ * it's a link, then show orphan link.\n+ */\n void print_color_indicator(const char *name, mode_t mode, int linkok,\n \t\t\t   int stat_ok, enum filetype filetype)\n {\n-- \n1.9.0.40.gaa8c3ea\n"},{"id":"237157","messageId":"1395310551-23201-4-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395310551-23201-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 3/8] ls_colors.c: enable coloring on u+x files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-20T10:15:46Z","receivedAt":"2014-03-20T10:15:46Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"git-compat-util.h does not seem to carry S_IXUGO. Anyway as far as Git\nis concerned, we only care one executable bit. Hard code it.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n ls_colors.c | 4 +---\n 1 file changed, 1 insertion(+), 3 deletions(-)\n\ndiff --git a/ls_colors.c b/ls_colors.c\nindex d492ab3..23f1e0b 100644\n--- a/ls_colors.c\n+++ b/ls_colors.c\n@@ -427,10 +427,8 @@ void print_color_indicator(const char *name, mode_t mode, int linkok,\n \t\t\t\ttype = C_SETUID;\n \t\t\telse if ((mode & S_ISGID) != 0)\n \t\t\t\ttype = C_SETGID;\n-#if 0\n-\t\t\telse if ((mode & S_IXUGO) != 0)\n+\t\t\telse if ((mode & 0100) != 0)\n \t\t\t\ttype = C_EXEC;\n-#endif\n \t\t} else if (S_ISDIR(mode)) {\n \t\t\tif ((mode & S_ISVTX) && (mode & S_IWOTH))\n \t\t\t\ttype = C_STICKY_OTHER_WRITABLE;\n-- \n1.9.0.40.gaa8c3ea\n"},{"id":"237159","messageId":"1395310551-23201-5-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395310551-23201-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 4/8] ls_colors.c: new color descriptors","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-20T10:15:47Z","receivedAt":"2014-03-20T10:15:47Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"After coreutils moved to GPL-3 a couple more color descriptors were\nadded. parse_ls_color() will abort if it finds any of these so just\nadd them recognized (but never actually use them).\n\nReference commits (in coreutils.git)\n\n0df338f (ls --color: do not colorize files with multiple hard links by default - 2009-06-10)\nadc62b5 (ls: clean up after wrapped+colored file names with clear-to-EOL - 2008-12-31)\n1e48b1f (ls: --color now highlights hard linked files, too - 2008-10-27)\n84f6abf (ls: --color now highlights files with capabilities, too - 2008-08-01)\n483297d (ls --color no longer outputs unnecessary escape sequences - 2008-02-12)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n ls_colors.c | 15 +++++++++++----\n 1 file changed, 11 insertions(+), 4 deletions(-)\n\ndiff --git a/ls_colors.c b/ls_colors.c\nindex 23f1e0b..02fc632 100644\n--- a/ls_colors.c\n+++ b/ls_colors.c\n@@ -5,9 +5,12 @@\n #define STREQ(a, b) (strcmp(a, b) == 0)\n \n enum indicator_no {\n-\tC_LEFT, C_RIGHT, C_END, C_NORM, C_FILE, C_DIR, C_LINK, C_FIFO, C_SOCK,\n+\tC_LEFT, C_RIGHT, C_END, C_RESET, C_NORM,\n+\tC_FILE, C_DIR, C_LINK, C_FIFO, C_SOCK,\n \tC_BLK, C_CHR, C_MISSING, C_ORPHAN, C_EXEC, C_DOOR, C_SETUID, C_SETGID,\n-\tC_STICKY, C_OTHER_WRITABLE, C_STICKY_OTHER_WRITABLE\n+\tC_STICKY, C_OTHER_WRITABLE, C_STICKY_OTHER_WRITABLE,\n+\tC_CAP, C_MULTIHARDLINK, C_CLR_TO_EOL\n+\n };\n \n #define FILETYPE_INDICATORS\t\t\t\t\\\n@@ -28,9 +31,9 @@ struct color_ext_type {\n };\n \n static const char *const indicator_name[]= {\n-\t\"lc\", \"rc\", \"ec\", \"no\", \"fi\", \"di\", \"ln\", \"pi\", \"so\",\n+\t\"lc\", \"rc\", \"ec\", \"rs\", \"no\", \"fi\", \"di\", \"ln\", \"pi\", \"so\",\n \t\"bd\", \"cd\", \"mi\", \"or\", \"ex\", \"do\", \"su\", \"sg\", \"st\",\n-\t\"ow\", \"tw\", NULL\n+\t\"ow\", \"tw\", \"ca\", \"mh\", \"cl\", NULL\n };\n \n #define LEN_STR_PAIR(s) sizeof(s) - 1, s\n@@ -38,6 +41,7 @@ static struct bin_str color_indicator[] = {\n \t{ LEN_STR_PAIR(\"\\033[\") },\t/* lc: Left of color sequence */\n \t{ LEN_STR_PAIR(\"m\") },\t\t/* rc: Right of color sequence */\n \t{ 0, NULL },\t\t\t/* ec: End color (replaces lc+no+rc) */\n+\t{ 0, NULL },\t\t\t/* rs: Reset to ordinary colors */\n \t{ LEN_STR_PAIR(\"0\") },\t\t/* no: Normal */\n \t{ LEN_STR_PAIR(\"0\") },\t\t/* fi: File: default */\n \t{ LEN_STR_PAIR(\"01;34\") },\t/* di: Directory: bright blue */\n@@ -55,6 +59,9 @@ static struct bin_str color_indicator[] = {\n \t{ LEN_STR_PAIR(\"37;44\") },\t/* st: sticky: black on blue */\n \t{ LEN_STR_PAIR(\"34;42\") },\t/* ow: other-writable: blue on green */\n \t{ LEN_STR_PAIR(\"30;42\") },\t/* tw: ow w/ sticky: black on green */\n+\t{ 0, NULL },\t\t\t/* ca: black on red */\n+\t{ 0, NULL },\t\t\t/* mh: disabled by default */\n+\t{ 0, NULL },\t\t\t/* cl: clear to end of line */\n };\n \n static struct color_ext_type *color_ext_list = NULL;\n-- \n1.9.0.40.gaa8c3ea\n"},{"id":"237158","messageId":"1395310551-23201-6-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395310551-23201-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 5/8] ls-files: add --color to highlight based on $LS_COLORS","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-20T10:15:48Z","receivedAt":"2014-03-20T10:15:48Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c | 38 ++++++++++++++++++++++++++++++++++++--\n 1 file changed, 36 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 47c3880..463280e 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -14,6 +14,8 @@\n #include \"resolve-undo.h\"\n #include \"string-list.h\"\n #include \"pathspec.h\"\n+#include \"color.h\"\n+#include \"ls_colors.h\"\n \n static int abbrev;\n static int show_deleted;\n@@ -27,6 +29,7 @@ static int show_killed;\n static int show_valid_bit;\n static int line_terminator = '\\n';\n static int debug_mode;\n+static int use_color;\n \n static const char *prefix;\n static int max_prefix_len;\n@@ -57,6 +60,33 @@ static void write_name(const char *name)\n \t\t\t\t   stdout, line_terminator);\n }\n \n+static void write_dir_entry(const struct dir_entry *ent)\n+{\n+\tif (want_color(use_color)) {\n+\t\tstatic struct strbuf sb = STRBUF_INIT;\n+\t\tstruct stat st;\n+\t\tint statok;\n+\t\tquote_path_relative(ent->name, prefix_len ? prefix : NULL, &sb);\n+\t\tstatok = stat(ent->name, &st) == 0;\n+\t\tprint_color_indicator(sb.buf, st.st_mode, 0, statok, 0);\n+\t\tfputs(sb.buf, stdout);\n+\t\tprintf(\"%s%c\", GIT_COLOR_RESET, line_terminator);\n+\t} else\n+\t\twrite_name(ent->name);\n+}\n+\n+static void write_ce_name(const struct cache_entry *ce)\n+{\n+\tif (want_color(use_color)) {\n+\t\tstatic struct strbuf sb = STRBUF_INIT;\n+\t\tquote_path_relative(ce->name, prefix_len ? prefix : NULL, &sb);\n+\t\tprint_color_indicator(sb.buf, ce->ce_mode, 1, 1, 0);\n+\t\tfputs(sb.buf, stdout);\n+\t\tprintf(\"%s%c\", GIT_COLOR_RESET, line_terminator);\n+\t} else\n+\t\twrite_name(ce->name);\n+}\n+\n static void show_dir_entry(const char *tag, struct dir_entry *ent)\n {\n \tint len = max_prefix_len;\n@@ -68,7 +98,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n \t\treturn;\n \n \tfputs(tag, stdout);\n-\twrite_name(ent->name);\n+\twrite_dir_entry(ent);\n }\n \n static void show_other_files(struct dir_struct *dir)\n@@ -170,7 +200,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n \t\t       find_unique_abbrev(ce->sha1,abbrev),\n \t\t       ce_stage(ce));\n \t}\n-\twrite_name(ce->name);\n+\twrite_ce_name(ce);\n \tif (debug_mode) {\n \t\tconst struct stat_data *sd = &ce->ce_stat_data;\n \n@@ -501,6 +531,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tN_(\"if any <file> is not in the index, treat this as an error\")),\n \t\tOPT_STRING(0, \"with-tree\", &with_tree, N_(\"tree-ish\"),\n \t\t\tN_(\"pretend that paths removed since <tree-ish> are still present\")),\n+\t\tOPT__COLOR(&use_color, N_(\"show color\")),\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n \t\tOPT_END()\n@@ -548,6 +579,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tif (require_work_tree && !is_inside_work_tree())\n \t\tsetup_work_tree();\n \n+\tif (want_color(use_color))\n+\t\tparse_ls_color();\n+\n \tparse_pathspec(&pathspec, 0,\n \t\t       PATHSPEC_PREFER_CWD |\n \t\t       PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,\n-- \n1.9.0.40.gaa8c3ea\n"},{"id":"237160","messageId":"1395310551-23201-7-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395310551-23201-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 6/8] ls-files: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-20T10:15:49Z","receivedAt":"2014-03-20T10:15:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Default pathspec behavior is recursive which includes too many files\nfor effective column output. But if you can do\n\ngit ls-files --column ':(glob)*'\n\nto limit to one level only. It's not exactly the same as GNU ls\n(e.g. directories are never shown) but much closer.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c | 16 ++++++++++++++++\n 1 file changed, 16 insertions(+)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 463280e..a43abdb 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -16,6 +16,7 @@\n #include \"pathspec.h\"\n #include \"color.h\"\n #include \"ls_colors.h\"\n+#include \"column.h\"\n \n static int abbrev;\n static int show_deleted;\n@@ -476,6 +477,7 @@ static int option_parse_exclude_standard(const struct option *opt,\n int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n {\n \tint require_work_tree = 0, show_tag = 0, i;\n+\tunsigned int colopts = 0;\n \tconst char *max_prefix;\n \tstruct dir_struct dir;\n \tstruct exclude_list *el;\n@@ -532,6 +534,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\tOPT_STRING(0, \"with-tree\", &with_tree, N_(\"tree-ish\"),\n \t\t\tN_(\"pretend that paths removed since <tree-ish> are still present\")),\n \t\tOPT__COLOR(&use_color, N_(\"show color\")),\n+\t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n \t\tOPT_END()\n@@ -576,6 +579,10 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tif (dir.exclude_per_dir)\n \t\texc_given = 1;\n \n+\tfinalize_colopts(&colopts, -1);\n+\tif (!line_terminator && explicitly_enable_column(colopts))\n+\t\tdie(_(\"--column and -z are incompatible\"));\n+\n \tif (require_work_tree && !is_inside_work_tree())\n \t\tsetup_work_tree();\n \n@@ -614,10 +621,19 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tdie(\"ls-files --with-tree is incompatible with -s or -u\");\n \t\toverlay_tree_on_cache(with_tree, max_prefix);\n \t}\n+\n+\tif (column_active(colopts)) {\n+\t\tstruct column_options copts;\n+\t\tmemset(&copts, 0, sizeof(copts));\n+\t\trun_column_filter(colopts, &copts);\n+\t}\n \tshow_files(&dir);\n \tif (show_resolve_undo)\n \t\tshow_ru_info();\n \n+\tif (column_active(colopts))\n+\t\tstop_column_filter();\n+\n \tif (ps_matched) {\n \t\tint bad;\n \t\tbad = report_path_error(ps_matched, &pathspec, prefix);\n-- \n1.9.0.40.gaa8c3ea\n"},{"id":"237161","messageId":"1395310551-23201-8-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395310551-23201-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 7/8] ls-files: support --max-depth","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-20T10:15:50Z","receivedAt":"2014-03-20T10:15:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The use case in mind is --max-depth=0 to stop recursion. With this we can do\n\ngit config --global alias.ls 'ls-files --column --color --max-depth=0'\n\nand have \"git ls\" with an output very similar to GNU ls. Another\ninteresting one is\n\ngit config --global alias.lso 'ls-files --column --color --max-depth=0 -o --exclude-standard'\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c | 7 +++++++\n 1 file changed, 7 insertions(+)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex a43abdb..2c51b0a 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -478,6 +478,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n {\n \tint require_work_tree = 0, show_tag = 0, i;\n \tunsigned int colopts = 0;\n+\tint max_depth = -1;\n \tconst char *max_prefix;\n \tstruct dir_struct dir;\n \tstruct exclude_list *el;\n@@ -535,6 +536,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tN_(\"pretend that paths removed since <tree-ish> are still present\")),\n \t\tOPT__COLOR(&use_color, N_(\"show color\")),\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n+\t\t{ OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n+\t\t\tN_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n+\t\t\tNULL, 1 },\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n \t\tOPT_END()\n@@ -591,8 +595,11 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \n \tparse_pathspec(&pathspec, 0,\n \t\t       PATHSPEC_PREFER_CWD |\n+\t\t       (max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0) |\n \t\t       PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,\n \t\t       prefix, argv);\n+\tpathspec.max_depth = max_depth;\n+\tpathspec.recursive = 1;\n \n \t/* Find common prefix for all pathspec's */\n \tmax_prefix = common_prefix(&pathspec);\n-- \n1.9.0.40.gaa8c3ea\n"},{"id":"237162","messageId":"1395310551-23201-9-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395310551-23201-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 8/8] Add git-ls, a user friendly version of ls-files and more","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-20T10:15:51Z","receivedAt":"2014-03-20T10:15:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"For now it's simply a wrapper of ls-files with some default\ngoodies. But I want it to be the UI for ls-tree and perhaps git diff\n--name-only [--cached] too.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin.h          |  1 +\n builtin/ls-files.c | 19 +++++++++++++++++++\n git.c              |  1 +\n 3 files changed, 21 insertions(+)\n\ndiff --git a/builtin.h b/builtin.h\nindex c47c110..177aa7d 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -75,6 +75,7 @@ extern int cmd_index_pack(int argc, const char **argv, const char *prefix);\n extern int cmd_init_db(int argc, const char **argv, const char *prefix);\n extern int cmd_log(int argc, const char **argv, const char *prefix);\n extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);\n+extern int cmd_ls(int argc, const char **argv, const char *prefix);\n extern int cmd_ls_files(int argc, const char **argv, const char *prefix);\n extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 2c51b0a..5354339 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -17,6 +17,7 @@\n #include \"color.h\"\n #include \"ls_colors.h\"\n #include \"column.h\"\n+#include \"argv-array.h\"\n \n static int abbrev;\n static int show_deleted;\n@@ -479,6 +480,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tint require_work_tree = 0, show_tag = 0, i;\n \tunsigned int colopts = 0;\n \tint max_depth = -1;\n+\tint refresh = 0;\n \tconst char *max_prefix;\n \tstruct dir_struct dir;\n \tstruct exclude_list *el;\n@@ -539,6 +541,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t{ OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n \t\t\tN_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n \t\t\tNULL, 1 },\n+\t\tOPT_HIDDEN_BOOL(0, \"refresh-index\", &refresh, N_(\"refresh index\")),\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n \t\tOPT_END()\n@@ -555,6 +558,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \n \tif (read_cache() < 0)\n \t\tdie(\"index file corrupt\");\n+\tif (refresh)\n+\t\trefresh_cache(REFRESH_QUIET);\n \n \targc = parse_options(argc, argv, prefix, builtin_ls_files_options,\n \t\t\tls_files_usage, 0);\n@@ -652,3 +657,17 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \n \treturn 0;\n }\n+\n+int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n+{\n+\tstruct argv_array av = ARGV_ARRAY_INIT;\n+\targv_array_pushl(&av, \"ls-files\",\n+\t\t\t \"--color\", \"--column\", \"--max-depth=0\",\n+\t\t\t \"--exclude-standard\", \"--refresh-index\",\n+\t\t\t NULL);\n+\targv++;\n+\twhile (*argv)\n+\t\targv_array_push(&av, *argv++);\n+\ttrace_argv_printf(av.argv, \"trace: built-in: git\");\n+\treturn cmd_ls_files(av.argc, av.argv, cmd_prefix);\n+}\ndiff --git a/git.c b/git.c\nindex 9efd1a3..682a81e 100644\n--- a/git.c\n+++ b/git.c\n@@ -381,6 +381,7 @@ static struct cmd_struct commands[] = {\n \t{ \"init\", cmd_init_db },\n \t{ \"init-db\", cmd_init_db },\n \t{ \"log\", cmd_log, RUN_SETUP },\n+\t{ \"ls\", cmd_ls, RUN_SETUP },\n \t{ \"ls-files\", cmd_ls_files, RUN_SETUP },\n \t{ \"ls-remote\", cmd_ls_remote, RUN_SETUP_GENTLY },\n \t{ \"ls-tree\", cmd_ls_tree, RUN_SETUP },\n-- \n1.9.0.40.gaa8c3ea\n"},{"id":"237165","messageId":"vpqpplhks7n.fsf@anie.imag.fr","threadId":"36247","inReplyTo":"1395310551-23201-4-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 3/8] ls_colors.c: enable coloring on u+x files","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2014-03-20T11:46:52Z","receivedAt":"2014-03-20T11:46:52Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n\n> git-compat-util.h does not seem to carry S_IXUGO. Anyway as far as Git\n> is concerned, we only care one executable bit. Hard code it.\n\nWhy not use S_IXUSR instead of a hardcoded value? (already used in\npath.c, so shouldn't be a problem wrt portability)\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"237167","messageId":"vpqd2hhkrrs.fsf@anie.imag.fr","threadId":"36247","inReplyTo":"1395310551-23201-9-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 8/8] Add git-ls, a user friendly version of ls-files and more","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2014-03-20T11:56:23Z","receivedAt":"2014-03-20T11:56:23Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n\n> +int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n> +{\n> +\tstruct argv_array av = ARGV_ARRAY_INIT;\n> +\targv_array_pushl(&av, \"ls-files\",\n> +\t\t\t \"--color\", \"--column\", \"--max-depth=0\",\n> +\t\t\t \"--exclude-standard\", \"--refresh-index\",\n> +\t\t\t NULL);\n\nI already have \"git ls\" as an alias for \"git ls-files\n--exclude-standard\", so this change would bring me even more goodies, I\nlike it.\n\nI'm a bit hesitant on the --max-depth=0 part by default. I like the way\nmy \"git ls\" alias recurses by default, but that may be just because I'm\nused to it. Also, disabling it is can only be done by adding\n--max-depth=-1 to the command-line, which isn't very convenient. Perhaps\nthere should be a \"git ls -R\" shorthand for \"git ls --max-depth=-1\".\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"237170","messageId":"CACsJy8DP3buUjT95LwG+Kd70KL67xqEubrHfu9Mqx1RNa-ER2Q@mail.gmail.com","threadId":"36247","inReplyTo":"vpqpplhks7n.fsf@anie.imag.fr","subject":"Re: [PATCH 3/8] ls_colors.c: enable coloring on u+x files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-20T12:14:13Z","receivedAt":"2014-03-20T12:14:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Mar 20, 2014 at 6:46 PM, Matthieu Moy\n<Matthieu.Moy@grenoble-inp.fr> wrote:\n> Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n>\n>> git-compat-util.h does not seem to carry S_IXUGO. Anyway as far as Git\n>> is concerned, we only care one executable bit. Hard code it.\n>\n> Why not use S_IXUSR instead of a hardcoded value? (already used in\n> path.c, so shouldn't be a problem wrt portability)\n\nHmm..maybe cache.h does something to that macro. Will drop this patch\nand include cache.h.\n-- \nDuy\n"},{"id":"237187","messageId":"xmqq7g7ozs0u.fsf@gitster.dls.corp.google.com","threadId":"36247","inReplyTo":"CACsJy8DP3buUjT95LwG+Kd70KL67xqEubrHfu9Mqx1RNa-ER2Q@mail.gmail.com","subject":"Re: [PATCH 3/8] ls_colors.c: enable coloring on u+x files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-03-20T17:41:53Z","receivedAt":"2014-03-20T17:41:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> On Thu, Mar 20, 2014 at 6:46 PM, Matthieu Moy\n> <Matthieu.Moy@grenoble-inp.fr> wrote:\n>> Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n>>\n>>> git-compat-util.h does not seem to carry S_IXUGO. Anyway as far as Git\n>>> is concerned, we only care one executable bit. Hard code it.\n>>\n>> Why not use S_IXUSR instead of a hardcoded value? (already used in\n>> path.c, so shouldn't be a problem wrt portability)\n>\n> Hmm..maybe cache.h does something to that macro. Will drop this patch\n> and include cache.h.\n\nWhy even include cache.h for S_IXUSR?\n\nIn the context of the patch I see S_ISGID mentioned and other S_*\nst_mode things are already in use in this function before this step,\nand presumably you are using them without problems, no?\n"},{"id":"237196","messageId":"loom.20140320T200517-726@post.gmane.org","threadId":"36247","inReplyTo":"1395310551-23201-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 1/8] Import $LS_COLORS parsing code from coreutils","fromName":"David Tran","fromEmail":"unsignedzero@gmail.com","sentAt":"2014-03-20T19:09:16Z","receivedAt":"2014-03-20T19:09:16Z","isPatch":true,"sender":{"key":"unsignedzero@gmail.com","avatar":"https://avatars.githubusercontent.com/u/778125?v=4"},"body":"Nguyễn Thái Ngọc Duy <pclouds <at> gmail.com> writes:\n\n> This could could help highlight files in ls-files or status output, or\n> even diff --name-only (but that's questionable).\n> \n> This code is from coreutils.git commit\n> 7326d1f1a67edf21947ae98194f98c38b6e9e527 file src/ls.c. This is the\n> last GPL-2 commit before coreutils turns to GPL-3.\n> \n\nI don't know if this is something to consider but for my mac, I have another \nvariable CLICOLOR which shows the colors if it is set. This is also true with \nFreeBSD[1] as well. I don't know if that should be checked if you're on those \nsystems.\n\nI think it would be nice to have --color flag as well if you want to enable \ncolor output for just that one output. \n\n[1]: https://unix.stackexchange.com/questions/2897/clicolor-and-ls-colors-in-\nbash\n"},{"id":"237309","messageId":"CACsJy8A2TaGvS0S9P1hOBmqXPTQszKcVTjXDEPX0kTrdN17Xxg@mail.gmail.com","threadId":"36247","inReplyTo":"xmqq7g7ozs0u.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 3/8] ls_colors.c: enable coloring on u+x files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-21T11:52:43Z","receivedAt":"2014-03-21T11:52:43Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Mar 21, 2014 at 12:41 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Why even include cache.h for S_IXUSR?\n>\n> In the context of the patch I see S_ISGID mentioned and other S_*\n> st_mode things are already in use in this function before this step,\n> and presumably you are using them without problems, no?\n\nMy mistake. git-compat-util.h does include S_IXUSR. The original code\nwas S_IXUGO that's in neither files.\n-- \nDuy\n"},{"id":"237310","messageId":"CACsJy8A2gcKHtYVO1+RfGmu8M_rUBXqDE_kS4P5wgKnWRH+HmA@mail.gmail.com","threadId":"36247","inReplyTo":"loom.20140320T200517-726@post.gmane.org","subject":"Re: [PATCH 1/8] Import $LS_COLORS parsing code from coreutils","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-21T11:54:45Z","receivedAt":"2014-03-21T11:54:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Mar 21, 2014 at 2:09 AM, David Tran <unsignedzero@gmail.com> wrote:\n> Nguyễn Thái Ngọc Duy <pclouds <at> gmail.com> writes:\n>\n>> This could could help highlight files in ls-files or status output, or\n>> even diff --name-only (but that's questionable).\n>>\n>> This code is from coreutils.git commit\n>> 7326d1f1a67edf21947ae98194f98c38b6e9e527 file src/ls.c. This is the\n>> last GPL-2 commit before coreutils turns to GPL-3.\n>>\n>\n> I don't know if this is something to consider but for my mac, I have another\n> variable CLICOLOR which shows the colors if it is set. This is also true with\n> FreeBSD[1] as well. I don't know if that should be checked if you're on those\n> systems.\n>\n> I think it would be nice to have --color flag as well if you want to enable\n> color output for just that one output.\n\nMy plan is stick to how git handles colors (e.g. --color and color.*\nconfig variables). Is that enough or do you think git CLICOLOR should\noverride --color and color.*?\n\n>\n> [1]: https://unix.stackexchange.com/questions/2897/clicolor-and-ls-colors-in-\n> bash\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n\n\n\n-- \nDuy\n"},{"id":"237355","messageId":"loom.20140321T205825-839@post.gmane.org","threadId":"36247","inReplyTo":"CACsJy8A2gcKHtYVO1+RfGmu8M_rUBXqDE_kS4P5wgKnWRH+HmA@mail.gmail.com","subject":"Re: [PATCH 1/8] Import $LS_COLORS parsing code from coreutils","fromName":"David Tran","fromEmail":"unsignedzero@gmail.com","sentAt":"2014-03-21T20:01:52Z","receivedAt":"2014-03-21T20:01:52Z","isPatch":true,"sender":{"key":"unsignedzero@gmail.com","avatar":"https://avatars.githubusercontent.com/u/778125?v=4"},"body":"Duy Nguyen <pclouds <at> gmail.com> writes:\n\n> On Fri, Mar 21, 2014 at 2:09 AM, David Tran <unsignedzero <at> gmail.com>\nwrote:\n> > Nguyễn Thái Ngọc Duy <pclouds <at> gmail.com> writes:\n> >\n> >> This could could help highlight files in ls-files or status output, or\n> >> even diff --name-only (but that's questionable).\n> >>\n> >> This code is from coreutils.git commit\n> >> 7326d1f1a67edf21947ae98194f98c38b6e9e527 file src/ls.c. This is the\n> >> last GPL-2 commit before coreutils turns to GPL-3.\n> >>\n> >\n> > I don't know if this is something to consider but for my mac, I have another\n> > variable CLICOLOR which shows the colors if it is set. This is also true\nwith\n> > FreeBSD[1] as well. I don't know if that should be checked if you're on\nthose\n> > systems.\n> >\n> > I think it would be nice to have --color flag as well if you want to enable\n> > color output for just that one output.\n> \n> My plan is stick to how git handles colors (e.g. --color and color.*\n> config variables). Is that enough or do you think git CLICOLOR should\n> override --color and color.*?\n> \nI would say it is not an essential feature to have but something that might\nbe looked into once the color is implemented. If its not set, ignore it. If\nit is set, check if it is truthy, is what I would do.\n"},{"id":"237639","messageId":"1457323768.788823.1395737758945.JavaMail.zimbra@imag.fr","threadId":"36247","inReplyTo":"1395310551-23201-8-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 7/8] ls-files: support --max-depth","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2014-03-25T08:55:58Z","receivedAt":"2014-03-25T08:55:58Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"----- Original Message -----\n> The use case in mind is --max-depth=0 to stop recursion. With this we can do\n> \n> git config --global alias.ls 'ls-files --column --color --max-depth=0'\n> \n> and have \"git ls\" with an output very similar to GNU ls.\n\nOne big difference though: your \"git ls\" does not show directories. I understand that this is easier to implement, but from the user point of view it resulted in a \"wtf\" from me running \"git ls\" in a repository containing essentially directories, and seeing just a README file in the output.\n\nIdeally (for me), directories should be shown with a trailing / like \"ls -F\" does.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"237648","messageId":"CACsJy8Awu6oNOS-m5_DNh_HGeHKgcnowUY+iz4efo3s1HYycnQ@mail.gmail.com","threadId":"36247","inReplyTo":"1457323768.788823.1395737758945.JavaMail.zimbra@imag.fr","subject":"Re: [PATCH 7/8] ls-files: support --max-depth","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-25T11:15:40Z","receivedAt":"2014-03-25T11:15:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Mar 25, 2014 at 3:55 PM, Matthieu Moy\n<matthieu.moy@grenoble-inp.fr> wrote:\n> ----- Original Message -----\n>> The use case in mind is --max-depth=0 to stop recursion. With this we can do\n>>\n>> git config --global alias.ls 'ls-files --column --color --max-depth=0'\n>>\n>> and have \"git ls\" with an output very similar to GNU ls.\n>\n> One big difference though: your \"git ls\" does not show directories. I understand that this is easier to implement, but from the user point of view it resulted in a \"wtf\" from me running \"git ls\" in a repository containing essentially directories, and seeing just a README file in the output.\n\nI was hoping you didn't notice :) It'll be more difficult but not impossible.\n\n> Ideally (for me), directories should be shown with a trailing / like \"ls -F\" does.\n\nI'd rather go with no trailing slash by default and add -F (which\nseems to be more than just '/')\n-- \nDuy\n"},{"id":"237649","messageId":"vpqwqfiwlyt.fsf@anie.imag.fr","threadId":"36247","inReplyTo":"1395310551-23201-7-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 6/8] ls-files: add --column","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2014-03-25T11:34:34Z","receivedAt":"2014-03-25T11:34:34Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n\n> +\t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n\nTo look a bit more like \"ls\", you could add a -1 option as an alias for\n--no-column.\n\nThat would be a good idea only if there is no plan to ever add a -N\noption (like \"git log -42\"). I don't know if any -N option could make\nsense.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"237800","messageId":"1395841697-11742-1-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395310551-23201-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 00/17] git-ls","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:00Z","receivedAt":"2014-03-26T13:48:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Compared to v1, git-ls now does not accept ls-files options (previous\ngit-ls is more like an alias of ls-files). I want this because ls may\ntake a different set of options than ls-files. Most functionality is\nshared so if you're not happy with ls, you can fall back to ls-files.\n\nNew alias options are supported, -1 == --no-column, -R ==\n--max-depth=-1. If more than one file criteria is chosen (e.g. \"ls -cmo\")\nthen --tag is implied. File order is fixed (\"ls-files -cmo\" actually\nshows two or three separate listings, \"ls -cmo\" shows one sorted\nlisting). It also shows directories from the index.\n\nDocumentation is there. No tests yet because the behavior may still\nneed some polishing.\n\nNguyễn Thái Ngọc Duy (17):\n  ls_colors.c: add $LS_COLORS parsing code\n  ls_colors.c: parse color.ls.* from config file\n  ls_colors.c: add function to color a file name\n  ls_colors.c: highlight submodules like directories\n  ls-files: buffer full item in strbuf before printing\n  ls-files: add --color to highlight file names\n  ls-files: add --column\n  ls-files: support --max-depth\n  ls-files: split main ls-files logic into ls_files() function\n  Add git-ls, a user friendly version of ls-files and more\n  ls: -u does not imply showing stages\n  ls: add -R/--recursive short for --max-depth=-1\n  ls: add -1 short for --no-column in the spirit of GNU ls\n  ls: add -t back\n  ls: sort output and remove duplicates\n  ls: do not show duplicate cached entries\n  ls: show directories as well as files\n\n .gitignore                     |   1 +\n Documentation/config.txt       |  22 ++\n Documentation/git-ls-files.txt |  22 ++\n Documentation/git-ls.txt (new) |  95 ++++++++\n Makefile                       |   2 +\n builtin.h                      |   1 +\n builtin/ls-files.c             | 446 +++++++++++++++++++++++++++++-------\n color.h                        |  10 +\n command-list.txt               |   1 +\n git.c                          |   1 +\n ls_colors.c (new)              | 496 +++++++++++++++++++++++++++++++++++++++++\n 11 files changed, 1012 insertions(+), 85 deletions(-)\n create mode 100644 Documentation/git-ls.txt\n create mode 100644 ls_colors.c\n\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237801","messageId":"1395841697-11742-2-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 01/17] ls_colors.c: add $LS_COLORS parsing code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:01Z","receivedAt":"2014-03-26T13:48:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Reusing color settings from $LS_COLORS could give a native look and\nfeel on file coloring.\n\nThis code is basically from coreutils.git [1], rewritten to fit Git.\n\nAs this is from GNU ls, the environment variable CLICOLOR is not\ntested. It is to be decided later whether we should ignore $LS_COLORS\nif $CLICOLOR is not set on Mac or FreeBSD.\n\n[1] commit 7326d1f1a67edf21947ae98194f98c38b6e9e527 file\n    src/ls.c. This is the last GPL-2 commit before coreutils turns to\n    GPL-3.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Makefile          |   1 +\n color.h           |   8 ++\n ls_colors.c (new) | 398 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 407 insertions(+)\n create mode 100644 ls_colors.c\n\ndiff --git a/Makefile b/Makefile\nindex f818eec..f6a6e14 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -819,6 +819,7 @@ LIB_OBJS += list-objects.o\n LIB_OBJS += ll-merge.o\n LIB_OBJS += lockfile.o\n LIB_OBJS += log-tree.o\n+LIB_OBJS += ls_colors.o\n LIB_OBJS += mailmap.o\n LIB_OBJS += match-trees.o\n LIB_OBJS += merge.o\ndiff --git a/color.h b/color.h\nindex 9a8495b..640fc48 100644\n--- a/color.h\n+++ b/color.h\n@@ -45,6 +45,12 @@ struct strbuf;\n #define GIT_COLOR_BG_MAGENTA\t\"\\033[45m\"\n #define GIT_COLOR_BG_CYAN\t\"\\033[46m\"\n \n+#define GIT_COLOR_WHITE_ON_RED    \"\\033[37;41m\"\n+#define GIT_COLOR_WHITE_ON_BLUE   \"\\033[37;44m\"\n+#define GIT_COLOR_BLACK_ON_YELLOW \"\\033[30;43m\"\n+#define GIT_COLOR_BLUE_ON_GREEN   \"\\033[34;42m\"\n+#define GIT_COLOR_BLACK_ON_GREEN  \"\\033[30;42m\"\n+\n /* A special value meaning \"no color selected\" */\n #define GIT_COLOR_NIL \"NIL\"\n \n@@ -87,4 +93,6 @@ void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb);\n \n int color_is_nil(const char *color);\n \n+void parse_ls_color(void);\n+\n #endif /* COLOR_H */\ndiff --git a/ls_colors.c b/ls_colors.c\nnew file mode 100644\nindex 0000000..eb944f4\n--- /dev/null\n+++ b/ls_colors.c\n@@ -0,0 +1,398 @@\n+#include \"cache.h\"\n+#include \"color.h\"\n+\n+enum color_ls {\n+\tLS_LC,\t\t\t/* left, unused */\n+\tLS_RC,\t\t\t/* right, unused */\n+\tLS_EC,\t\t\t/* end color, unused */\n+\tLS_RS,\t\t\t/* reset */\n+\tLS_NO,\t\t\t/* normal */\n+\tLS_FL,\t\t\t/* file, default */\n+\tLS_DI,\t\t\t/* directory */\n+\tLS_LN,\t\t\t/* symlink */\n+\n+\tLS_PI,\t\t\t/* pipe */\n+\tLS_SO,\t\t\t/* socket */\n+\tLS_BD,\t\t\t/* block device */\n+\tLS_CD,\t\t\t/* char device */\n+\tLS_MI,\t\t\t/* missing file */\n+\tLS_OR,\t\t\t/* orphaned symlink */\n+\tLS_EX,\t\t\t/* executable */\n+\tLS_DO,\t\t\t/* Solaris door */\n+\n+\tLS_SU,\t\t\t/* setuid */\n+\tLS_SG,\t\t\t/* setgid */\n+\tLS_ST,\t\t\t/* sticky */\n+\tLS_OW,\t\t\t/* other-writable */\n+\tLS_TW,\t\t\t/* ow with sticky */\n+\tLS_CA,\t\t\t/* cap */\n+\tLS_MH,\t\t\t/* multi hardlink */\n+\tLS_CL,\t\t\t/* clear end of line */\n+\n+\tMAX_LS\n+};\n+\n+static char ls_colors[MAX_LS][COLOR_MAXLEN] = {\n+\t\"\",\n+\t\"\",\n+\t\"\",\n+\tGIT_COLOR_RESET,\n+\tGIT_COLOR_NORMAL,\n+\tGIT_COLOR_NORMAL,\n+\tGIT_COLOR_BOLD_BLUE,\n+\tGIT_COLOR_BOLD_CYAN,\n+\n+\tGIT_COLOR_YELLOW,\n+\tGIT_COLOR_BOLD_MAGENTA,\n+\tGIT_COLOR_BOLD_YELLOW,\n+\tGIT_COLOR_BOLD_YELLOW,\n+\tGIT_COLOR_NORMAL,\n+\tGIT_COLOR_NORMAL,\n+\tGIT_COLOR_BOLD_GREEN,\n+\tGIT_COLOR_BOLD_MAGENTA,\n+\n+\tGIT_COLOR_WHITE_ON_RED,\n+\tGIT_COLOR_BLACK_ON_YELLOW,\n+\tGIT_COLOR_WHITE_ON_BLUE,\n+\tGIT_COLOR_BLUE_ON_GREEN,\n+\tGIT_COLOR_BLACK_ON_GREEN,\n+\t\"\",\n+\t\"\",\n+\t\"\"\n+};\n+\n+static const char *const indicator_name[] = {\n+\t\"lc\", \"rc\", \"ec\", \"rs\", \"no\", \"fi\", \"di\", \"ln\",\n+\t\"pi\", \"so\", \"bd\", \"cd\", \"mi\", \"or\", \"ex\", \"do\",\n+\t\"su\", \"sg\", \"st\", \"ow\", \"tw\", \"ca\", \"mh\", \"cl\",\n+\tNULL\n+};\n+\n+struct bin_str {\n+\tsize_t len;\t\t\t/* Number of bytes */\n+\tconst char *string;\t\t/* Pointer to the same */\n+};\n+\n+struct color_ext_type {\n+\tstruct bin_str ext;\t\t/* The extension we're looking for */\n+\tstruct bin_str seq;\t\t/* The sequence to output when we do */\n+\tstruct color_ext_type *next;\t/* Next in list */\n+};\n+\n+static struct color_ext_type *color_ext_list = NULL;\n+\n+/*\n+ * When true, in a color listing, color each symlink name according to the\n+ * type of file it points to.  Otherwise, color them according to the `ln'\n+ * directive in LS_COLORS.  Dangling (orphan) symlinks are treated specially,\n+ * regardless.  This is set when `ln=target' appears in LS_COLORS.\n+ */\n+static int color_symlink_as_referent;\n+\n+/*\n+ * Parse a string as part of the LS_COLORS variable; this may involve\n+ * decoding all kinds of escape characters.  If equals_end is set an\n+ * unescaped equal sign ends the string, otherwise only a : or \\0\n+ * does.  Set *OUTPUT_COUNT to the number of bytes output.  Return\n+ * true if successful.\n+ *\n+ * The resulting string is *not* null-terminated, but may contain\n+ * embedded nulls.\n+ *\n+ * Note that both dest and src are char **; on return they point to\n+ * the first free byte after the array and the character that ended\n+ * the input string, respectively.\n+ */\n+static int get_funky_string(char **dest, const char **src, int equals_end,\n+\t\t\t    size_t *output_count)\n+{\n+\tchar num;\t\t\t/* For numerical codes */\n+\tsize_t count;\t\t\t/* Something to count with */\n+\tenum {\n+\t\tST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX,\n+\t\tST_CARET, ST_END, ST_ERROR\n+\t} state;\n+\tconst char *p;\n+\tchar *q;\n+\n+\tp = *src;\t\t\t/* We don't want to double-indirect */\n+\tq = *dest;\t\t\t/* the whole darn time.  */\n+\n+\tcount = 0;\t\t\t/* No characters counted in yet.  */\n+\tnum = 0;\n+\n+\tstate = ST_GND;\t\t/* Start in ground state.  */\n+\twhile (state < ST_END) {\n+\t\tswitch (state) {\n+\t\tcase ST_GND:\t\t/* Ground state (no escapes) */\n+\t\t\tswitch (*p) {\n+\t\t\tcase ':':\n+\t\t\tcase '\\0':\n+\t\t\t\tstate = ST_END;\t/* End of string */\n+\t\t\t\tbreak;\n+\t\t\tcase '\\\\':\n+\t\t\t\tstate = ST_BACKSLASH; /* Backslash scape sequence */\n+\t\t\t\t++p;\n+\t\t\t\tbreak;\n+\t\t\tcase '^':\n+\t\t\t\tstate = ST_CARET; /* Caret escape */\n+\t\t\t\t++p;\n+\t\t\t\tbreak;\n+\t\t\tcase '=':\n+\t\t\t\tif (equals_end) {\n+\t\t\t\t\tstate = ST_END; /* End */\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\t/* else fall through */\n+\t\t\tdefault:\n+\t\t\t\t*(q++) = *(p++);\n+\t\t\t\t++count;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase ST_BACKSLASH:\t/* Backslash escaped character */\n+\t\t\tswitch (*p) {\n+\t\t\tcase '0':\n+\t\t\tcase '1':\n+\t\t\tcase '2':\n+\t\t\tcase '3':\n+\t\t\tcase '4':\n+\t\t\tcase '5':\n+\t\t\tcase '6':\n+\t\t\tcase '7':\n+\t\t\t\tstate = ST_OCTAL;\t/* Octal sequence */\n+\t\t\t\tnum = *p - '0';\n+\t\t\t\tbreak;\n+\t\t\tcase 'x':\n+\t\t\tcase 'X':\n+\t\t\t\tstate = ST_HEX;\t/* Hex sequence */\n+\t\t\t\tnum = 0;\n+\t\t\t\tbreak;\n+\t\t\tcase 'a':\t\t/* Bell */\n+\t\t\t\tnum = '\\a';\n+\t\t\t\tbreak;\n+\t\t\tcase 'b':\t\t/* Backspace */\n+\t\t\t\tnum = '\\b';\n+\t\t\t\tbreak;\n+\t\t\tcase 'e':\t\t/* Escape */\n+\t\t\t\tnum = 27;\n+\t\t\t\tbreak;\n+\t\t\tcase 'f':\t\t/* Form feed */\n+\t\t\t\tnum = '\\f';\n+\t\t\t\tbreak;\n+\t\t\tcase 'n':\t\t/* Newline */\n+\t\t\t\tnum = '\\n';\n+\t\t\t\tbreak;\n+\t\t\tcase 'r':\t\t/* Carriage return */\n+\t\t\t\tnum = '\\r';\n+\t\t\t\tbreak;\n+\t\t\tcase 't':\t\t/* Tab */\n+\t\t\t\tnum = '\\t';\n+\t\t\t\tbreak;\n+\t\t\tcase 'v':\t\t/* Vtab */\n+\t\t\t\tnum = '\\v';\n+\t\t\t\tbreak;\n+\t\t\tcase '?':\t\t/* Delete */\n+\t\t\t\tnum = 127;\n+\t\t\t\tbreak;\n+\t\t\tcase '_':\t\t/* Space */\n+\t\t\t\tnum = ' ';\n+\t\t\t\tbreak;\n+\t\t\tcase '\\0':\t\t/* End of string */\n+\t\t\t\tstate = ST_ERROR;\t/* Error! */\n+\t\t\t\tbreak;\n+\t\t\tdefault:\t\t/* Escaped character like \\ ^ : = */\n+\t\t\t\tnum = *p;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tif (state == ST_BACKSLASH) {\n+\t\t\t\t*(q++) = num;\n+\t\t\t\t++count;\n+\t\t\t\tstate = ST_GND;\n+\t\t\t}\n+\t\t\t++p;\n+\t\t\tbreak;\n+\n+\t\tcase ST_OCTAL:\t\t/* Octal sequence */\n+\t\t\tif (*p < '0' || *p > '7') {\n+\t\t\t\t*(q++) = num;\n+\t\t\t\t++count;\n+\t\t\t\tstate = ST_GND;\n+\t\t\t} else\n+\t\t\t\tnum = (num << 3) + (*(p++) - '0');\n+\t\t\tbreak;\n+\n+\t\tcase ST_HEX:\t\t/* Hex sequence */\n+\t\t\tswitch (*p) {\n+\t\t\tcase '0':\n+\t\t\tcase '1':\n+\t\t\tcase '2':\n+\t\t\tcase '3':\n+\t\t\tcase '4':\n+\t\t\tcase '5':\n+\t\t\tcase '6':\n+\t\t\tcase '7':\n+\t\t\tcase '8':\n+\t\t\tcase '9':\n+\t\t\t\tnum = (num << 4) + (*(p++) - '0');\n+\t\t\t\tbreak;\n+\t\t\tcase 'a':\n+\t\t\tcase 'b':\n+\t\t\tcase 'c':\n+\t\t\tcase 'd':\n+\t\t\tcase 'e':\n+\t\t\tcase 'f':\n+\t\t\t\tnum = (num << 4) + (*(p++) - 'a') + 10;\n+\t\t\t\tbreak;\n+\t\t\tcase 'A':\n+\t\t\tcase 'B':\n+\t\t\tcase 'C':\n+\t\t\tcase 'D':\n+\t\t\tcase 'E':\n+\t\t\tcase 'F':\n+\t\t\t\tnum = (num << 4) + (*(p++) - 'A') + 10;\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\t*(q++) = num;\n+\t\t\t\t++count;\n+\t\t\t\tstate = ST_GND;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase ST_CARET:\t\t/* Caret escape */\n+\t\t\tstate = ST_GND;\t/* Should be the next state... */\n+\t\t\tif (*p >= '@' && *p <= '~') {\n+\t\t\t\t*(q++) = *(p++) & 037;\n+\t\t\t\t++count;\n+\t\t\t} else if (*p == '?') {\n+\t\t\t\t*(q++) = 127;\n+\t\t\t\t++count;\n+\t\t\t} else\n+\t\t\t\tstate = ST_ERROR;\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tabort();\n+\t\t}\n+\t}\n+\n+\t*dest = q;\n+\t*src = p;\n+\t*output_count = count;\n+\n+\treturn state != ST_ERROR;\n+}\n+\n+void parse_ls_color(void)\n+{\n+\tconst char *p;\t\t\t/* Pointer to character being parsed */\n+\tchar *buf;\t\t\t/* color_buf buffer pointer */\n+\tint state;\t\t\t/* State of parser */\n+\tint ind_no;\t\t\t/* Indicator number */\n+\tchar label[3];\t\t\t/* Indicator label */\n+\tstruct color_ext_type *ext;\t/* Extension we are working on */\n+\tstatic char *color_buf;\n+\tchar *start;\n+\tsize_t len;\n+\n+\tif ((p = getenv(\"LS_COLORS\")) == NULL || *p == '\\0')\n+\t\treturn;\n+\n+\text = NULL;\n+\tstrcpy (label, \"??\");\n+\n+\t/*\n+\t * This is an overly conservative estimate, but any possible\n+\t * LS_COLORS string will *not* generate a color_buf longer\n+\t * than itself, so it is a safe way of allocating a buffer in\n+\t * advance.\n+\t */\n+\tbuf = color_buf = xstrdup(p);\n+\n+\tstate = 1;\n+\twhile (state > 0) {\n+\t\tswitch (state) {\n+\t\tcase 1:\t\t/* First label character */\n+\t\t\tswitch (*p) {\n+\t\t\tcase ':':\n+\t\t\t\t++p;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase '*':\n+\t\t\t\t/*\n+\t\t\t\t * Allocate new extension block and add to head of\n+\t\t\t\t * linked list (this way a later definition will\n+\t\t\t\t * override an earlier one, which can be useful for\n+\t\t\t\t * having terminal-specific defs override global).\n+\t\t\t\t */\n+\n+\t\t\t\text = xmalloc(sizeof *ext);\n+\t\t\t\text->next = color_ext_list;\n+\t\t\t\tcolor_ext_list = ext;\n+\n+\t\t\t\t++p;\n+\t\t\t\text->ext.string = buf;\n+\n+\t\t\t\tstate = (get_funky_string(&buf, &p, 1, &ext->ext.len)\n+\t\t\t\t\t ? 4 : -1);\n+\t\t\t\tbreak;\n+\n+\t\t\tcase '\\0':\n+\t\t\t\tstate = 0;\t/* Done! */\n+\t\t\t\tbreak;\n+\n+\t\t\tdefault:\t/* Assume it is file type label */\n+\t\t\t\tlabel[0] = *(p++);\n+\t\t\t\tstate = 2;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase 2:\t\t/* Second label character */\n+\t\t\tif (*p) {\n+\t\t\t\tlabel[1] = *(p++);\n+\t\t\t\tstate = 3;\n+\t\t\t} else\n+\t\t\t\tstate = -1;\t/* Error */\n+\t\t\tbreak;\n+\n+\t\tcase 3:\t\t/* Equal sign after indicator label */\n+\t\t\tstate = -1;\t/* Assume failure...  */\n+\t\t\tif (*(p++) != '=')\n+\t\t\t\tbreak;\n+\t\t\tfor (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no) {\n+\t\t\t\tif (!strcmp(label, indicator_name[ind_no])) {\n+\t\t\t\t\tstart = buf;\n+\t\t\t\t\tif (get_funky_string(&buf, &p, 0, &len))\n+\t\t\t\t\t\tstate = 1;\n+\t\t\t\t\telse\n+\t\t\t\t\t\tstate = -1;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (state == -1)\n+\t\t\t\terror(_(\"unrecognized prefix: %s\"), label);\n+\t\t\telse if (ind_no == LS_LN && len == 6 &&\n+\t\t\t\t starts_with(start, \"target\"))\n+\t\t\t\tcolor_symlink_as_referent = 1;\n+\t\t\telse\n+\t\t\t\tsprintf(ls_colors[ind_no], \"\\033[%.*sm\",\n+\t\t\t\t       (int)len, start);\n+\t\t\tbreak;\n+\n+\t\tcase 4:\t\t/* Equal sign after *.ext */\n+\t\t\tif (*(p++) == '=') {\n+\t\t\t\text->seq.string = buf;\n+\t\t\t\tstate = (get_funky_string(&buf, &p, 0, &ext->seq.len)\n+\t\t\t\t\t ? 1 : -1);\n+\t\t\t} else\n+\t\t\t\tstate = -1;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (!strcmp(ls_colors[LS_LN], \"target\"))\n+\t\tcolor_symlink_as_referent = 1;\n+}\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237802","messageId":"1395841697-11742-3-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 02/17] ls_colors.c: parse color.ls.* from config file","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:02Z","receivedAt":"2014-03-26T13:48:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is the second (and preferred) source for color information. This\nwill override $LS_COLORS.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt | 11 +++++++++++\n ls_colors.c              | 26 ++++++++++++++++++++++++++\n 2 files changed, 37 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 73c8973..3fb754e 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -909,6 +909,17 @@ color.status.<slot>::\n \tto red). The values of these variables may be specified as in\n \tcolor.branch.<slot>.\n \n+color.ls.<slot>::\n+\tUse customized color for file name colorization. If not set\n+\tand the environment variable LS_COLORS is set, color settings\n+\tfrom $LS_COLORS are used. `<slot>` can be `normal`, `file`,\n+\t`directory`, `symlink`, `fifo`, `socket`, `block`, `char`,\n+\t`missing`, `orphan`, `executable`, `door`, `setuid`, `setgid`,\n+\t`sticky`, `otherwritable`, `stickyotherwritable`, `cap`,\n+\t`multihardlink`. The values of these variables may be\n+\tspecified as in color.branch.<slot>.\n+\n+\n color.ui::\n \tThis variable determines the default value for variables such\n \tas `color.diff` and `color.grep` that control the use of color\ndiff --git a/ls_colors.c b/ls_colors.c\nindex eb944f4..cef5a92 100644\n--- a/ls_colors.c\n+++ b/ls_colors.c\n@@ -68,6 +68,14 @@ static const char *const indicator_name[] = {\n \tNULL\n };\n \n+static const char* const config_name[] = {\n+\t\"\", \"\", \"\", \"\", \"normal\", \"file\", \"directory\", \"symlink\",\n+\t\"fifo\", \"socket\", \"block\", \"char\", \"missing\", \"orphan\", \"executable\",\n+\t\"door\", \"setuid\", \"setgid\", \"sticky\", \"otherwritable\",\n+\t\"stickyotherwritable\", \"cap\", \"multihardlink\", \"\",\n+\tNULL\n+};\n+\n struct bin_str {\n \tsize_t len;\t\t\t/* Number of bytes */\n \tconst char *string;\t\t/* Pointer to the same */\n@@ -285,6 +293,23 @@ static int get_funky_string(char **dest, const char **src, int equals_end,\n \treturn state != ST_ERROR;\n }\n \n+static int ls_colors_config(const char *var, const char *value, void *cb)\n+{\n+\tint slot;\n+\tif (!starts_with(var, \"color.ls.\"))\n+\t\treturn 0;\n+\tvar += 9;\n+\tfor (slot = 0; config_name[slot]; slot++)\n+\t\tif (!strcasecmp(var, config_name[slot]))\n+\t\t\tbreak;\n+\tif (!config_name[slot])\n+\t\treturn 0;\n+\tif (!value)\n+\t\treturn config_error_nonbool(var);\n+\tcolor_parse(value, var, ls_colors[slot]);\n+\treturn 0;\n+}\n+\n void parse_ls_color(void)\n {\n \tconst char *p;\t\t\t/* Pointer to character being parsed */\n@@ -395,4 +420,5 @@ void parse_ls_color(void)\n \n \tif (!strcmp(ls_colors[LS_LN], \"target\"))\n \t\tcolor_symlink_as_referent = 1;\n+\tgit_config(ls_colors_config, NULL);\n }\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237803","messageId":"1395841697-11742-4-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 03/17] ls_colors.c: add function to color a file name","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:03Z","receivedAt":"2014-03-26T13:48:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Tthe new function is based on print_color_indicator() from commit\n7326d1f1a67edf21947ae98194f98c38b6e9e527 in coreutils.git.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n color.h     |  2 ++\n ls_colors.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 68 insertions(+)\n\ndiff --git a/color.h b/color.h\nindex 640fc48..398369a 100644\n--- a/color.h\n+++ b/color.h\n@@ -94,5 +94,7 @@ void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb);\n int color_is_nil(const char *color);\n \n void parse_ls_color(void);\n+void color_filename(struct strbuf *sb, const char *name,\n+\t\t    const char *display_name, mode_t mode, int linkok);\n \n #endif /* COLOR_H */\ndiff --git a/ls_colors.c b/ls_colors.c\nindex cef5a92..1125329 100644\n--- a/ls_colors.c\n+++ b/ls_colors.c\n@@ -422,3 +422,69 @@ void parse_ls_color(void)\n \t\tcolor_symlink_as_referent = 1;\n \tgit_config(ls_colors_config, NULL);\n }\n+\n+void color_filename(struct strbuf *sb, const char *name,\n+\t\t    const char *display_name, mode_t mode, int linkok)\n+{\n+\tint type;\n+\tstruct color_ext_type *ext;\t/* Color extension */\n+\n+\tif (S_ISREG (mode)) {\n+\t\ttype = LS_FL;\n+\t\tif ((mode & S_ISUID) != 0)\n+\t\t\ttype = LS_SU;\n+\t\telse if ((mode & S_ISGID) != 0)\n+\t\t\ttype = LS_SG;\n+\t\telse if ((mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)\n+\t\t\ttype = LS_EX;\n+\t} else if (S_ISDIR (mode)) {\n+\t\tif ((mode & S_ISVTX) && (mode & S_IWOTH))\n+\t\t\ttype = LS_TW;\n+\t\telse if ((mode & S_IWOTH) != 0)\n+\t\t\ttype = LS_OW;\n+\t\telse if ((mode & S_ISVTX) != 0)\n+\t\t\ttype = LS_ST;\n+\t\telse\n+\t\t\ttype = LS_DI;\n+\t} else if (S_ISLNK (mode))\n+\t\ttype = (!linkok && *ls_colors[LS_OR]) ? LS_OR : LS_LN;\n+\telse if (S_ISFIFO (mode))\n+\t\ttype = LS_PI;\n+\telse if (S_ISSOCK (mode))\n+\t\ttype = LS_SO;\n+\telse if (S_ISBLK (mode))\n+\t\ttype = LS_BD;\n+\telse if (S_ISCHR (mode))\n+\t\ttype = LS_CD;\n+#ifdef S_ISDOOR\n+\telse if (S_ISDOOR (mode))\n+\t\ttype = LS_DO;\n+#endif\n+\telse\n+\t\t/* Classify a file of some other type as C_ORPHAN.  */\n+\t\ttype = LS_OR;\n+\n+\t/* Check the file's suffix only if still classified as C_FILE.  */\n+\text = NULL;\n+\tif (type == LS_FL) {\n+\t\t/* Test if NAME has a recognized suffix.  */\n+\t\tsize_t len = strlen(name);\n+\t\tconst char *p = name + len;\t\t/* Pointer to final \\0.  */\n+\t\tfor (ext = color_ext_list; ext != NULL; ext = ext->next) {\n+\t\t\tif (ext->ext.len <= len &&\n+\t\t\t    !strncmp(p - ext->ext.len, ext->ext.string, ext->ext.len))\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (display_name)\n+\t\tname = display_name;\n+\tif (ext)\n+\t\tstrbuf_addf(sb, \"\\033[%.*sm%s%s\",\n+\t\t\t    (int)ext->seq.len, ext->seq.string,\n+\t\t\t    name, GIT_COLOR_RESET);\n+\telse if (*ls_colors[type])\n+\t\tstrbuf_addf(sb, \"%s%s%s\", ls_colors[type], name, GIT_COLOR_RESET);\n+\telse\n+\t\tstrbuf_addstr(sb, name);\n+}\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237804","messageId":"1395841697-11742-5-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 04/17] ls_colors.c: highlight submodules like directories","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:04Z","receivedAt":"2014-03-26T13:48:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt | 3 ++-\n ls_colors.c              | 8 +++++++-\n 2 files changed, 9 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 3fb754e..6bca55e 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -913,7 +913,8 @@ color.ls.<slot>::\n \tUse customized color for file name colorization. If not set\n \tand the environment variable LS_COLORS is set, color settings\n \tfrom $LS_COLORS are used. `<slot>` can be `normal`, `file`,\n-\t`directory`, `symlink`, `fifo`, `socket`, `block`, `char`,\n+\t`directory`, `submodule`,\n+\t`symlink`, `fifo`, `socket`, `block`, `char`,\n \t`missing`, `orphan`, `executable`, `door`, `setuid`, `setgid`,\n \t`sticky`, `otherwritable`, `stickyotherwritable`, `cap`,\n \t`multihardlink`. The values of these variables may be\ndiff --git a/ls_colors.c b/ls_colors.c\nindex 1125329..0cc4e9b 100644\n--- a/ls_colors.c\n+++ b/ls_colors.c\n@@ -29,6 +29,8 @@ enum color_ls {\n \tLS_MH,\t\t\t/* multi hardlink */\n \tLS_CL,\t\t\t/* clear end of line */\n \n+\tLS_SUBMODULE,\n+\n \tMAX_LS\n };\n \n@@ -58,7 +60,8 @@ static char ls_colors[MAX_LS][COLOR_MAXLEN] = {\n \tGIT_COLOR_BLACK_ON_GREEN,\n \t\"\",\n \t\"\",\n-\t\"\"\n+\t\"\",\n+\tGIT_COLOR_BOLD_BLUE\n };\n \n static const char *const indicator_name[] = {\n@@ -73,6 +76,7 @@ static const char* const config_name[] = {\n \t\"fifo\", \"socket\", \"block\", \"char\", \"missing\", \"orphan\", \"executable\",\n \t\"door\", \"setuid\", \"setgid\", \"sticky\", \"otherwritable\",\n \t\"stickyotherwritable\", \"cap\", \"multihardlink\", \"\",\n+\t\"submodule\",\n \tNULL\n };\n \n@@ -448,6 +452,8 @@ void color_filename(struct strbuf *sb, const char *name,\n \t\t\ttype = LS_DI;\n \t} else if (S_ISLNK (mode))\n \t\ttype = (!linkok && *ls_colors[LS_OR]) ? LS_OR : LS_LN;\n+\telse if (S_ISGITLINK(mode))\n+\t\ttype = LS_SUBMODULE;\n \telse if (S_ISFIFO (mode))\n \t\ttype = LS_PI;\n \telse if (S_ISSOCK (mode))\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237805","messageId":"1395841697-11742-6-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 05/17] ls-files: buffer full item in strbuf before printing","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:05Z","receivedAt":"2014-03-26T13:48:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Buffering so that we can manipulate the strings (e.g. coloring)\nfurther before finally printing them.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c | 48 +++++++++++++++++++++++++++++++++++-------------\n 1 file changed, 35 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 47c3880..6e30592 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -47,18 +47,30 @@ static const char *tag_modified = \"\";\n static const char *tag_skip_worktree = \"\";\n static const char *tag_resolve_undo = \"\";\n \n-static void write_name(const char *name)\n+static void write_name(struct strbuf *sb, const char *name)\n {\n \t/*\n \t * With \"--full-name\", prefix_len=0; this caller needs to pass\n \t * an empty string in that case (a NULL is good for \"\").\n \t */\n-\twrite_name_quoted_relative(name, prefix_len ? prefix : NULL,\n-\t\t\t\t   stdout, line_terminator);\n+\tconst char *real_prefix = prefix_len ? prefix : NULL;\n+\tif (!line_terminator) {\n+\t\tstruct strbuf sb2 = STRBUF_INIT;\n+\t\tstrbuf_addstr(sb, relative_path(name, real_prefix, &sb2));\n+\t\tstrbuf_release(&sb2);\n+\t} else\n+\t\tquote_path_relative(name, real_prefix, sb);\n+\tstrbuf_addch(sb, line_terminator);\n+}\n+\n+static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n+{\n+\tfwrite(sb->buf, sb->len, 1, fp);\n }\n \n static void show_dir_entry(const char *tag, struct dir_entry *ent)\n {\n+\tstatic struct strbuf sb = STRBUF_INIT;\n \tint len = max_prefix_len;\n \n \tif (len >= ent->len)\n@@ -67,8 +79,10 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n \tif (!dir_path_match(ent, &pathspec, len, ps_matched))\n \t\treturn;\n \n-\tfputs(tag, stdout);\n-\twrite_name(ent->name);\n+\tstrbuf_reset(&sb);\n+\tstrbuf_addstr(&sb, tag);\n+\twrite_name(&sb, ent->name);\n+\tstrbuf_fputs(&sb, stdout);\n }\n \n static void show_other_files(struct dir_struct *dir)\n@@ -134,6 +148,7 @@ static void show_killed_files(struct dir_struct *dir)\n \n static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n {\n+\tstatic struct strbuf sb = STRBUF_INIT;\n \tint len = max_prefix_len;\n \n \tif (len >= ce_namelen(ce))\n@@ -161,16 +176,18 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n \t\ttag = alttag;\n \t}\n \n+\tstrbuf_reset(&sb);\n \tif (!show_stage) {\n-\t\tfputs(tag, stdout);\n+\t\tstrbuf_addstr(&sb, tag);\n \t} else {\n-\t\tprintf(\"%s%06o %s %d\\t\",\n-\t\t       tag,\n-\t\t       ce->ce_mode,\n-\t\t       find_unique_abbrev(ce->sha1,abbrev),\n-\t\t       ce_stage(ce));\n+\t\tstrbuf_addf(&sb, \"%s%06o %s %d\\t\",\n+\t\t\t    tag,\n+\t\t\t    ce->ce_mode,\n+\t\t\t    find_unique_abbrev(ce->sha1,abbrev),\n+\t\t\t    ce_stage(ce));\n \t}\n-\twrite_name(ce->name);\n+\twrite_name(&sb, ce->name);\n+\tstrbuf_fputs(&sb, stdout);\n \tif (debug_mode) {\n \t\tconst struct stat_data *sd = &ce->ce_stat_data;\n \n@@ -206,7 +223,12 @@ static void show_ru_info(void)\n \t\t\tprintf(\"%s%06o %s %d\\t\", tag_resolve_undo, ui->mode[i],\n \t\t\t       find_unique_abbrev(ui->sha1[i], abbrev),\n \t\t\t       i + 1);\n-\t\t\twrite_name(path);\n+\t\t\t/*\n+\t\t\t * With \"--full-name\", prefix_len=0; this caller needs to pass\n+\t\t\t * an empty string in that case (a NULL is good for \"\").\n+\t\t\t */\n+\t\t\twrite_name_quoted_relative(path, prefix_len ? prefix : NULL,\n+\t\t\t\t\t\t   stdout, line_terminator);\n \t\t}\n \t}\n }\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237806","messageId":"1395841697-11742-7-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 06/17] ls-files: add --color to highlight file names","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:06Z","receivedAt":"2014-03-26T13:48:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-ls-files.txt |  9 +++++++++\n builtin/ls-files.c             | 38 +++++++++++++++++++++++++++++++++++---\n 2 files changed, 44 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\nindex c0856a6..5c1b7f3 100644\n--- a/Documentation/git-ls-files.txt\n+++ b/Documentation/git-ls-files.txt\n@@ -147,6 +147,15 @@ a space) at the start of each line:\n \tpossible for manual inspection; the exact format may change at\n \tany time.\n \n+--color[=<when>]::\n+\tColor file names. The value must be always (default), never,\n+\tor auto.\n+\n+--no-color::\n+\tTurn off coloring, even when the configuration file gives the\n+\tdefault to color output, same as `--color=never`. This is the\n+\tdefault.\n+\n \\--::\n \tDo not interpret any more arguments as options.\n \ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 6e30592..2857b38 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -14,6 +14,7 @@\n #include \"resolve-undo.h\"\n #include \"string-list.h\"\n #include \"pathspec.h\"\n+#include \"color.h\"\n \n static int abbrev;\n static int show_deleted;\n@@ -27,6 +28,7 @@ static int show_killed;\n static int show_valid_bit;\n static int line_terminator = '\\n';\n static int debug_mode;\n+static int use_color;\n \n static const char *prefix;\n static int max_prefix_len;\n@@ -60,7 +62,6 @@ static void write_name(struct strbuf *sb, const char *name)\n \t\tstrbuf_release(&sb2);\n \t} else\n \t\tquote_path_relative(name, real_prefix, sb);\n-\tstrbuf_addch(sb, line_terminator);\n }\n \n static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n@@ -68,6 +69,21 @@ static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n \tfwrite(sb->buf, sb->len, 1, fp);\n }\n \n+static void write_dir_entry(struct strbuf *sb, const struct dir_entry *ent)\n+{\n+\tstruct strbuf quoted = STRBUF_INIT;\n+\tstruct stat st;\n+\tif (stat(ent->name, &st))\n+\t\tst.st_mode = 0;\n+\twrite_name(&quoted, ent->name);\n+\tif (want_color(use_color))\n+\t\tcolor_filename(sb, ent->name, quoted.buf, st.st_mode, 1);\n+\telse\n+\t\tstrbuf_addbuf(sb, &quoted);\n+\tstrbuf_addch(sb, line_terminator);\n+\tstrbuf_release(&quoted);\n+}\n+\n static void show_dir_entry(const char *tag, struct dir_entry *ent)\n {\n \tstatic struct strbuf sb = STRBUF_INIT;\n@@ -81,7 +97,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n \n \tstrbuf_reset(&sb);\n \tstrbuf_addstr(&sb, tag);\n-\twrite_name(&sb, ent->name);\n+\twrite_dir_entry(&sb, ent);\n \tstrbuf_fputs(&sb, stdout);\n }\n \n@@ -146,6 +162,18 @@ static void show_killed_files(struct dir_struct *dir)\n \t}\n }\n \n+static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)\n+{\n+\tstruct strbuf quoted = STRBUF_INIT;\n+\twrite_name(&quoted, ce->name);\n+\tif (want_color(use_color))\n+\t\tcolor_filename(sb, ce->name, quoted.buf, ce->ce_mode, 1);\n+\telse\n+\t\tstrbuf_addbuf(sb, &quoted);\n+\tstrbuf_addch(sb, line_terminator);\n+\tstrbuf_release(&quoted);\n+}\n+\n static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n {\n \tstatic struct strbuf sb = STRBUF_INIT;\n@@ -186,7 +214,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n \t\t\t    find_unique_abbrev(ce->sha1,abbrev),\n \t\t\t    ce_stage(ce));\n \t}\n-\twrite_name(&sb, ce->name);\n+\twrite_ce_name(&sb, ce);\n \tstrbuf_fputs(&sb, stdout);\n \tif (debug_mode) {\n \t\tconst struct stat_data *sd = &ce->ce_stat_data;\n@@ -523,6 +551,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tN_(\"if any <file> is not in the index, treat this as an error\")),\n \t\tOPT_STRING(0, \"with-tree\", &with_tree, N_(\"tree-ish\"),\n \t\t\tN_(\"pretend that paths removed since <tree-ish> are still present\")),\n+\t\tOPT__COLOR(&use_color, N_(\"show color\")),\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n \t\tOPT_END()\n@@ -570,6 +599,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tif (require_work_tree && !is_inside_work_tree())\n \t\tsetup_work_tree();\n \n+\tif (want_color(use_color))\n+\t\tparse_ls_color();\n+\n \tparse_pathspec(&pathspec, 0,\n \t\t       PATHSPEC_PREFER_CWD |\n \t\t       PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237807","messageId":"1395841697-11742-8-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 07/17] ls-files: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:07Z","receivedAt":"2014-03-26T13:48:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-ls-files.txt |  6 ++++++\n builtin/ls-files.c             | 25 +++++++++++++++++++++++++\n 2 files changed, 31 insertions(+)\n\ndiff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\nindex 5c1b7f3..cd52461 100644\n--- a/Documentation/git-ls-files.txt\n+++ b/Documentation/git-ls-files.txt\n@@ -156,6 +156,12 @@ a space) at the start of each line:\n \tdefault to color output, same as `--color=never`. This is the\n \tdefault.\n \n+--column[=<options>]::\n+--no-column::\n+\tDisplay files in columns. See configuration variable column.ui\n+\tfor option syntax.`--column` and `--no-column` without options\n+\tare equivalent to 'always' and 'never' respectively.\n+\n \\--::\n \tDo not interpret any more arguments as options.\n \ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 2857b38..335d3b0 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -15,6 +15,7 @@\n #include \"string-list.h\"\n #include \"pathspec.h\"\n #include \"color.h\"\n+#include \"column.h\"\n \n static int abbrev;\n static int show_deleted;\n@@ -29,6 +30,7 @@ static int show_valid_bit;\n static int line_terminator = '\\n';\n static int debug_mode;\n static int use_color;\n+static unsigned int colopts;\n \n static const char *prefix;\n static int max_prefix_len;\n@@ -39,6 +41,7 @@ static char *ps_matched;\n static const char *with_tree;\n static int exc_given;\n static int exclude_args;\n+static struct string_list output = STRING_LIST_INIT_NODUP;\n \n static const char *tag_cached = \"\";\n static const char *tag_unmerged = \"\";\n@@ -66,6 +69,10 @@ static void write_name(struct strbuf *sb, const char *name)\n \n static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n {\n+\tif (column_active(colopts)) {\n+\t\tstring_list_append(&output, strbuf_detach(sb, NULL));\n+\t\treturn;\n+\t}\n \tfwrite(sb->buf, sb->len, 1, fp);\n }\n \n@@ -552,6 +559,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\tOPT_STRING(0, \"with-tree\", &with_tree, N_(\"tree-ish\"),\n \t\t\tN_(\"pretend that paths removed since <tree-ish> are still present\")),\n \t\tOPT__COLOR(&use_color, N_(\"show color\")),\n+\t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n \t\tOPT_END()\n@@ -596,6 +604,18 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tif (dir.exclude_per_dir)\n \t\texc_given = 1;\n \n+\tfinalize_colopts(&colopts, -1);\n+\tif (explicitly_enable_column(colopts)) {\n+\t\tif (!line_terminator)\n+\t\t\tdie(_(\"--column and -z are incompatible\"));\n+\t\tif (show_resolve_undo)\n+\t\t\tdie(_(\"--column and --resolve-undo are incompatible\"));\n+\t\tif (debug_mode)\n+\t\t\tdie(_(\"--column and --debug are incompatible\"));\n+\t}\n+\tif (column_active(colopts))\n+\t\tline_terminator = 0;\n+\n \tif (require_work_tree && !is_inside_work_tree())\n \t\tsetup_work_tree();\n \n@@ -638,6 +658,11 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tif (show_resolve_undo)\n \t\tshow_ru_info();\n \n+\tif (column_active(colopts)) {\n+\t\tprint_columns(&output, colopts, NULL);\n+\t\tstring_list_clear(&output, 0);\n+\t}\n+\n \tif (ps_matched) {\n \t\tint bad;\n \t\tbad = report_path_error(ps_matched, &pathspec, prefix);\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237808","messageId":"1395841697-11742-9-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 08/17] ls-files: support --max-depth","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:08Z","receivedAt":"2014-03-26T13:48:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-ls-files.txt | 7 +++++++\n builtin/ls-files.c             | 7 +++++++\n 2 files changed, 14 insertions(+)\n\ndiff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\nindex cd52461..3c022eb 100644\n--- a/Documentation/git-ls-files.txt\n+++ b/Documentation/git-ls-files.txt\n@@ -162,6 +162,13 @@ a space) at the start of each line:\n \tfor option syntax.`--column` and `--no-column` without options\n \tare equivalent to 'always' and 'never' respectively.\n \n+--max-depth <depth>::\n+\tFor each <pathspec> given on command line, descend at most <depth>\n+\tlevels of directories. A negative value means no limit.\n+\tThis option is ignored if <pathspec> contains active wildcards.\n+\tIn other words if \"a*\" matches a directory named \"a*\",\n+\t\"*\" is matched literally so --max-depth is still effective.\n+\n \\--::\n \tDo not interpret any more arguments as options.\n \ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 335d3b0..8eef423 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -503,6 +503,7 @@ static int option_parse_exclude_standard(const struct option *opt,\n int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n {\n \tint require_work_tree = 0, show_tag = 0, i;\n+\tint max_depth = -1;\n \tconst char *max_prefix;\n \tstruct dir_struct dir;\n \tstruct exclude_list *el;\n@@ -560,6 +561,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tN_(\"pretend that paths removed since <tree-ish> are still present\")),\n \t\tOPT__COLOR(&use_color, N_(\"show color\")),\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n+\t\t{ OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n+\t\t\tN_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n+\t\t\tNULL, 1 },\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n \t\tOPT_END()\n@@ -624,8 +628,11 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \n \tparse_pathspec(&pathspec, 0,\n \t\t       PATHSPEC_PREFER_CWD |\n+\t\t       (max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0) |\n \t\t       PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,\n \t\t       prefix, argv);\n+\tpathspec.max_depth = max_depth;\n+\tpathspec.recursive = 1;\n \n \t/* Find common prefix for all pathspec's */\n \tmax_prefix = common_prefix(&pathspec);\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237809","messageId":"1395841697-11742-10-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 09/17] ls-files: split main ls-files logic into ls_files() function","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:09Z","receivedAt":"2014-03-26T13:48:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is a preparation step for the introduction of git-ls. \"git ls\"\nhas a different set of command line options, but it will eventually\ncall ls_files().\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c | 164 +++++++++++++++++++++++++++--------------------------\n 1 file changed, 85 insertions(+), 79 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 8eef423..20ca3f2 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -31,6 +31,8 @@ static int line_terminator = '\\n';\n static int debug_mode;\n static int use_color;\n static unsigned int colopts;\n+static int max_depth = -1;\n+static int show_tag;\n \n static const char *prefix;\n static int max_prefix_len;\n@@ -42,6 +44,8 @@ static const char *with_tree;\n static int exc_given;\n static int exclude_args;\n static struct string_list output = STRING_LIST_INIT_NODUP;\n+static struct dir_struct dir;\n+static struct string_list exclude_list = STRING_LIST_INIT_NODUP;\n \n static const char *tag_cached = \"\";\n static const char *tag_unmerged = \"\";\n@@ -500,89 +504,12 @@ static int option_parse_exclude_standard(const struct option *opt,\n \treturn 0;\n }\n \n-int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n+static int ls_files(const char **argv, const char *prefix)\n {\n-\tint require_work_tree = 0, show_tag = 0, i;\n-\tint max_depth = -1;\n+\tint require_work_tree = 0, i;\n \tconst char *max_prefix;\n-\tstruct dir_struct dir;\n \tstruct exclude_list *el;\n-\tstruct string_list exclude_list = STRING_LIST_INIT_NODUP;\n-\tstruct option builtin_ls_files_options[] = {\n-\t\t{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,\n-\t\t\tN_(\"paths are separated with NUL character\"),\n-\t\t\tPARSE_OPT_NOARG, option_parse_z },\n-\t\tOPT_BOOL('t', NULL, &show_tag,\n-\t\t\tN_(\"identify the file status with tags\")),\n-\t\tOPT_BOOL('v', NULL, &show_valid_bit,\n-\t\t\tN_(\"use lowercase letters for 'assume unchanged' files\")),\n-\t\tOPT_BOOL('c', \"cached\", &show_cached,\n-\t\t\tN_(\"show cached files in the output (default)\")),\n-\t\tOPT_BOOL('d', \"deleted\", &show_deleted,\n-\t\t\tN_(\"show deleted files in the output\")),\n-\t\tOPT_BOOL('m', \"modified\", &show_modified,\n-\t\t\tN_(\"show modified files in the output\")),\n-\t\tOPT_BOOL('o', \"others\", &show_others,\n-\t\t\tN_(\"show other files in the output\")),\n-\t\tOPT_BIT('i', \"ignored\", &dir.flags,\n-\t\t\tN_(\"show ignored files in the output\"),\n-\t\t\tDIR_SHOW_IGNORED),\n-\t\tOPT_BOOL('s', \"stage\", &show_stage,\n-\t\t\tN_(\"show staged contents' object name in the output\")),\n-\t\tOPT_BOOL('k', \"killed\", &show_killed,\n-\t\t\tN_(\"show files on the filesystem that need to be removed\")),\n-\t\tOPT_BIT(0, \"directory\", &dir.flags,\n-\t\t\tN_(\"show 'other' directories' name only\"),\n-\t\t\tDIR_SHOW_OTHER_DIRECTORIES),\n-\t\tOPT_NEGBIT(0, \"empty-directory\", &dir.flags,\n-\t\t\tN_(\"don't show empty directories\"),\n-\t\t\tDIR_HIDE_EMPTY_DIRECTORIES),\n-\t\tOPT_BOOL('u', \"unmerged\", &show_unmerged,\n-\t\t\tN_(\"show unmerged files in the output\")),\n-\t\tOPT_BOOL(0, \"resolve-undo\", &show_resolve_undo,\n-\t\t\t    N_(\"show resolve-undo information\")),\n-\t\t{ OPTION_CALLBACK, 'x', \"exclude\", &exclude_list, N_(\"pattern\"),\n-\t\t\tN_(\"skip files matching pattern\"),\n-\t\t\t0, option_parse_exclude },\n-\t\t{ OPTION_CALLBACK, 'X', \"exclude-from\", &dir, N_(\"file\"),\n-\t\t\tN_(\"exclude patterns are read from <file>\"),\n-\t\t\t0, option_parse_exclude_from },\n-\t\tOPT_STRING(0, \"exclude-per-directory\", &dir.exclude_per_dir, N_(\"file\"),\n-\t\t\tN_(\"read additional per-directory exclude patterns in <file>\")),\n-\t\t{ OPTION_CALLBACK, 0, \"exclude-standard\", &dir, NULL,\n-\t\t\tN_(\"add the standard git exclusions\"),\n-\t\t\tPARSE_OPT_NOARG, option_parse_exclude_standard },\n-\t\t{ OPTION_SET_INT, 0, \"full-name\", &prefix_len, NULL,\n-\t\t\tN_(\"make the output relative to the project top directory\"),\n-\t\t\tPARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },\n-\t\tOPT_BOOL(0, \"error-unmatch\", &error_unmatch,\n-\t\t\tN_(\"if any <file> is not in the index, treat this as an error\")),\n-\t\tOPT_STRING(0, \"with-tree\", &with_tree, N_(\"tree-ish\"),\n-\t\t\tN_(\"pretend that paths removed since <tree-ish> are still present\")),\n-\t\tOPT__COLOR(&use_color, N_(\"show color\")),\n-\t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n-\t\t{ OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n-\t\t\tN_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n-\t\t\tNULL, 1 },\n-\t\tOPT__ABBREV(&abbrev),\n-\t\tOPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n-\t\tOPT_END()\n-\t};\n-\n-\tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n-\t\tusage_with_options(ls_files_usage, builtin_ls_files_options);\n \n-\tmemset(&dir, 0, sizeof(dir));\n-\tprefix = cmd_prefix;\n-\tif (prefix)\n-\t\tprefix_len = strlen(prefix);\n-\tgit_config(git_default_config, NULL);\n-\n-\tif (read_cache() < 0)\n-\t\tdie(\"index file corrupt\");\n-\n-\targc = parse_options(argc, argv, prefix, builtin_ls_files_options,\n-\t\t\tls_files_usage, 0);\n \tel = add_exclude_list(&dir, EXC_CMDL, \"--exclude option\");\n \tfor (i = 0; i < exclude_list.nr; i++) {\n \t\tadd_exclude(exclude_list.items[i].string, \"\", 0, el, --exclude_args);\n@@ -681,3 +608,82 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \n \treturn 0;\n }\n+\n+int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n+{\n+\tstruct option builtin_ls_files_options[] = {\n+\t\t{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,\n+\t\t\tN_(\"paths are separated with NUL character\"),\n+\t\t\tPARSE_OPT_NOARG, option_parse_z },\n+\t\tOPT_BOOL('t', NULL, &show_tag,\n+\t\t\tN_(\"identify the file status with tags\")),\n+\t\tOPT_BOOL('v', NULL, &show_valid_bit,\n+\t\t\tN_(\"use lowercase letters for 'assume unchanged' files\")),\n+\t\tOPT_BOOL('c', \"cached\", &show_cached,\n+\t\t\tN_(\"show cached files in the output (default)\")),\n+\t\tOPT_BOOL('d', \"deleted\", &show_deleted,\n+\t\t\tN_(\"show deleted files in the output\")),\n+\t\tOPT_BOOL('m', \"modified\", &show_modified,\n+\t\t\tN_(\"show modified files in the output\")),\n+\t\tOPT_BOOL('o', \"others\", &show_others,\n+\t\t\tN_(\"show other files in the output\")),\n+\t\tOPT_BIT('i', \"ignored\", &dir.flags,\n+\t\t\tN_(\"show ignored files in the output\"),\n+\t\t\tDIR_SHOW_IGNORED),\n+\t\tOPT_BOOL('s', \"stage\", &show_stage,\n+\t\t\tN_(\"show staged contents' object name in the output\")),\n+\t\tOPT_BOOL('k', \"killed\", &show_killed,\n+\t\t\tN_(\"show files on the filesystem that need to be removed\")),\n+\t\tOPT_BIT(0, \"directory\", &dir.flags,\n+\t\t\tN_(\"show 'other' directories' name only\"),\n+\t\t\tDIR_SHOW_OTHER_DIRECTORIES),\n+\t\tOPT_NEGBIT(0, \"empty-directory\", &dir.flags,\n+\t\t\tN_(\"don't show empty directories\"),\n+\t\t\tDIR_HIDE_EMPTY_DIRECTORIES),\n+\t\tOPT_BOOL('u', \"unmerged\", &show_unmerged,\n+\t\t\tN_(\"show unmerged files in the output\")),\n+\t\tOPT_BOOL(0, \"resolve-undo\", &show_resolve_undo,\n+\t\t\t    N_(\"show resolve-undo information\")),\n+\t\t{ OPTION_CALLBACK, 'x', \"exclude\", &exclude_list, N_(\"pattern\"),\n+\t\t\tN_(\"skip files matching pattern\"),\n+\t\t\t0, option_parse_exclude },\n+\t\t{ OPTION_CALLBACK, 'X', \"exclude-from\", &dir, N_(\"file\"),\n+\t\t\tN_(\"exclude patterns are read from <file>\"),\n+\t\t\t0, option_parse_exclude_from },\n+\t\tOPT_STRING(0, \"exclude-per-directory\", &dir.exclude_per_dir, N_(\"file\"),\n+\t\t\tN_(\"read additional per-directory exclude patterns in <file>\")),\n+\t\t{ OPTION_CALLBACK, 0, \"exclude-standard\", &dir, NULL,\n+\t\t\tN_(\"add the standard git exclusions\"),\n+\t\t\tPARSE_OPT_NOARG, option_parse_exclude_standard },\n+\t\t{ OPTION_SET_INT, 0, \"full-name\", &prefix_len, NULL,\n+\t\t\tN_(\"make the output relative to the project top directory\"),\n+\t\t\tPARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },\n+\t\tOPT_BOOL(0, \"error-unmatch\", &error_unmatch,\n+\t\t\tN_(\"if any <file> is not in the index, treat this as an error\")),\n+\t\tOPT_STRING(0, \"with-tree\", &with_tree, N_(\"tree-ish\"),\n+\t\t\tN_(\"pretend that paths removed since <tree-ish> are still present\")),\n+\t\tOPT__COLOR(&use_color, N_(\"show color\")),\n+\t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n+\t\t{ OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n+\t\t\tN_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n+\t\t\tNULL, 1 },\n+\t\tOPT__ABBREV(&abbrev),\n+\t\tOPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n+\t\tOPT_END()\n+\t};\n+\n+\tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n+\t\tusage_with_options(ls_files_usage, builtin_ls_files_options);\n+\n+\tprefix = cmd_prefix;\n+\tif (prefix)\n+\t\tprefix_len = strlen(prefix);\n+\tgit_config(git_default_config, NULL);\n+\n+\tif (read_cache() < 0)\n+\t\tdie(\"index file corrupt\");\n+\n+\targc = parse_options(argc, argv, prefix, builtin_ls_files_options,\n+\t\t\tls_files_usage, 0);\n+\treturn ls_files(argv, prefix);\n+}\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237810","messageId":"1395841697-11742-11-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 10/17] Add git-ls, a user friendly version of ls-files and more","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:10Z","receivedAt":"2014-03-26T13:48:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is more user friendly version of ls-files:\n\n* it's automatically colored and columnized\n* it refreshes the index like all porcelain commands\n* it defaults to non-recursive behavior like ls\n* :(glob) is on by default so '*.c' means a.c but not a/b.c, use\n  '**/*.c' for that.\n* auto pager\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                     |  1 +\n Documentation/config.txt       | 10 ++++++\n Documentation/git-ls.txt (new) | 82 ++++++++++++++++++++++++++++++++++++++++++\n Makefile                       |  1 +\n builtin.h                      |  1 +\n builtin/ls-files.c             | 70 ++++++++++++++++++++++++++++++++++++\n command-list.txt               |  1 +\n git.c                          |  1 +\n 8 files changed, 167 insertions(+)\n create mode 100644 Documentation/git-ls.txt\n\ndiff --git a/.gitignore b/.gitignore\nindex dc600f9..f91af81 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -76,6 +76,7 @@\n /git-init-db\n /git-instaweb\n /git-log\n+/git-ls\n /git-ls-files\n /git-ls-remote\n /git-ls-tree\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 6bca55e..87a6dcf 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -909,6 +909,12 @@ color.status.<slot>::\n \tto red). The values of these variables may be specified as in\n \tcolor.branch.<slot>.\n \n+color.ls::\n+\tA boolean to enable/disable color in the output of\n+\tlinkgit:git-ls[1]. May be set to `always`, `false` (or\n+\t`never`) or `auto` (or `true`), in which case colors are used\n+\tonly when the output is to a terminal. Defaults to false.\n+\n color.ls.<slot>::\n \tUse customized color for file name colorization. If not set\n \tand the environment variable LS_COLORS is set, color settings\n@@ -981,6 +987,10 @@ column.clean::\n \tSpecify the layout when list items in `git clean -i`, which always\n \tshows files and directories in columns. See `column.ui` for details.\n \n+column.ls::\n+\tSpecify whether to output tag listing in `git ls` in columns.\n+\tSee `column.ui` for details.\n+\n column.status::\n \tSpecify whether to output untracked files in `git status` in columns.\n \tSee `column.ui` for details.\ndiff --git a/Documentation/git-ls.txt b/Documentation/git-ls.txt\nnew file mode 100644\nindex 0000000..67ca522\n--- /dev/null\n+++ b/Documentation/git-ls.txt\n@@ -0,0 +1,82 @@\n+git-ls(1)\n+===============\n+\n+NAME\n+----\n+git-ls - List files\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git ls' (--[cached|deleted|others|ignored|unmerged|modified])*\n+\t(-[c|d|o|i|s|u|m])*\n+\t[options] [<pathspec>...]\n+\n+DESCRIPTION\n+-----------\n+List files (by default in current working directory) that are in the\n+index. Depending on the chosen options, maybe only modified files in\n+working tree are shown, or untracked files...\n+\n+OPTIONS\n+-------\n+-c::\n+--cached::\n+\tShow cached files in the output (default)\n+\n+-d::\n+--deleted::\n+\tShow deleted files in the output\n+\n+-m::\n+--modified::\n+\tShow modified files in the output\n+\n+-o::\n+--others::\n+\tShow other (i.e. untracked) files in the output\n+\n+-i::\n+--ignored::\n+\tShow only ignored files in the output. When showing files in the\n+\tindex, print only those matched by an exclude pattern. When\n+\tshowing \"other\" files, show only those matched by an exclude\n+\tpattern.\n+\n+-u::\n+--unmerged::\n+\tShow unmerged files in the output (forces --stage)\n+\n+--color[=<when>]::\n+\tColor file names. The value must be always (default), never,\n+\tor auto.\n+\n+--no-color::\n+\tTurn off coloring, even when the configuration file gives the\n+\tdefault to color output, same as `--color=never`. This is the\n+\tdefault.\n+\n+--column[=<options>]::\n+--no-column::\n+\tDisplay files in columns. See configuration variable column.ui\n+\tfor option syntax.`--column` and `--no-column` without options\n+\tare equivalent to 'always' and 'never' respectively.\n+\n+--max-depth <depth>::\n+\tFor each <pathspec> given on command line, descend at most <depth>\n+\tlevels of directories. A negative value means no limit.\n+\tThis option is ignored if <pathspec> contains active wildcards.\n+\tIn other words if \"a*\" matches a directory named \"a*\",\n+\t\"*\" is matched literally so --max-depth is still effective.\n+\n+<pathspec>::\n+\tFiles to show. :(glob) magic is enabled and recursion disabled\n+\tby default.\n+\n+SEE ALSO\n+--------\n+linkgit:git-ls-files[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Makefile b/Makefile\nindex f6a6e14..b0bc40a 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -584,6 +584,7 @@ BUILT_INS += git-cherry-pick$X\n BUILT_INS += git-format-patch$X\n BUILT_INS += git-fsck-objects$X\n BUILT_INS += git-init$X\n+BUILT_INS += git-ls$X\n BUILT_INS += git-merge-subtree$X\n BUILT_INS += git-show$X\n BUILT_INS += git-stage$X\ndiff --git a/builtin.h b/builtin.h\nindex c47c110..177aa7d 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -75,6 +75,7 @@ extern int cmd_index_pack(int argc, const char **argv, const char *prefix);\n extern int cmd_init_db(int argc, const char **argv, const char *prefix);\n extern int cmd_log(int argc, const char **argv, const char *prefix);\n extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);\n+extern int cmd_ls(int argc, const char **argv, const char *prefix);\n extern int cmd_ls_files(int argc, const char **argv, const char *prefix);\n extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 20ca3f2..74eb3c2 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -33,6 +33,7 @@ static int use_color;\n static unsigned int colopts;\n static int max_depth = -1;\n static int show_tag;\n+static int porcelain;\n \n static const char *prefix;\n static int max_prefix_len;\n@@ -588,6 +589,10 @@ static int ls_files(const char **argv, const char *prefix)\n \t\t\tdie(\"ls-files --with-tree is incompatible with -s or -u\");\n \t\toverlay_tree_on_cache(with_tree, max_prefix);\n \t}\n+\tif (porcelain) {\n+\t\trefresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL);\n+\t\tsetup_pager();\n+\t}\n \tshow_files(&dir);\n \tif (show_resolve_undo)\n \t\tshow_ru_info();\n@@ -687,3 +692,68 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tls_files_usage, 0);\n \treturn ls_files(argv, prefix);\n }\n+\n+static const char * const ls_usage[] = {\n+\tN_(\"git ls [options] [<file>...]\"),\n+\tNULL\n+};\n+\n+static int git_ls_config(const char *var, const char *value, void *cb)\n+{\n+\tif (starts_with(var, \"column.\"))\n+\t\treturn git_column_config(var, value, \"ls\", &colopts);\n+\tif (!strcmp(var, \"color.ls\")) {\n+\t\tuse_color = git_config_colorbool(var, value);\n+\t\treturn 0;\n+\t}\n+\treturn git_color_default_config(var, value, cb);\n+}\n+\n+int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n+{\n+\tstruct option builtin_ls_options[] = {\n+\t\tOPT_BOOL('c', \"cached\", &show_cached,\n+\t\t\tN_(\"show cached files in the output (default)\")),\n+\t\tOPT_BOOL('d', \"deleted\", &show_deleted,\n+\t\t\tN_(\"show deleted files in the output\")),\n+\t\tOPT_BOOL('m', \"modified\", &show_modified,\n+\t\t\tN_(\"show modified files in the output\")),\n+\t\tOPT_BOOL('o', \"others\", &show_others,\n+\t\t\tN_(\"show other files in the output\")),\n+\t\tOPT_BIT('i', \"ignored\", &dir.flags,\n+\t\t\tN_(\"show ignored files in the output\"),\n+\t\t\tDIR_SHOW_IGNORED),\n+\t\tOPT_BOOL('u', \"unmerged\", &show_unmerged,\n+\t\t\tN_(\"show unmerged files in the output\")),\n+\t\tOPT__COLOR(&use_color, N_(\"show color\")),\n+\t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n+\t\t{ OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n+\t\t\tN_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n+\t\t\tNULL, 1 },\n+\t\tOPT__ABBREV(&abbrev),\n+\t\tOPT_END()\n+\t};\n+\n+\tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n+\t\tusage_with_options(ls_usage, builtin_ls_options);\n+\n+\tprefix = cmd_prefix;\n+\tif (prefix)\n+\t\tprefix_len = strlen(prefix);\n+\tgit_config(git_ls_config, NULL);\n+\n+\tif (read_cache() < 0)\n+\t\tdie(\"index file corrupt\");\n+\n+\t/* default setup */\n+\tporcelain = 1;\n+\tsetenv(GIT_GLOB_PATHSPECS_ENVIRONMENT, \"1\", 1);\n+\texc_given = 1;\n+\tsetup_standard_excludes(&dir);\n+\tuse_color = -1;\n+\tmax_depth = 0;\n+\n+\targc = parse_options(argc, argv, prefix, builtin_ls_options,\n+\t\t\t     ls_usage, 0);\n+\treturn ls_files(argv, prefix);\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex cf36c3d..89e5cef 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -64,6 +64,7 @@ git-init                                mainporcelain common\n git-instaweb                            ancillaryinterrogators\n gitk                                    mainporcelain\n git-log                                 mainporcelain common\n+git-ls                                  mainporcelain\n git-ls-files                            plumbinginterrogators\n git-ls-remote                           plumbinginterrogators\n git-ls-tree                             plumbinginterrogators\ndiff --git a/git.c b/git.c\nindex 9efd1a3..682a81e 100644\n--- a/git.c\n+++ b/git.c\n@@ -381,6 +381,7 @@ static struct cmd_struct commands[] = {\n \t{ \"init\", cmd_init_db },\n \t{ \"init-db\", cmd_init_db },\n \t{ \"log\", cmd_log, RUN_SETUP },\n+\t{ \"ls\", cmd_ls, RUN_SETUP },\n \t{ \"ls-files\", cmd_ls_files, RUN_SETUP },\n \t{ \"ls-remote\", cmd_ls_remote, RUN_SETUP_GENTLY },\n \t{ \"ls-tree\", cmd_ls_tree, RUN_SETUP },\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237811","messageId":"1395841697-11742-12-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 11/17] ls: -u does not imply showing stages","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:11Z","receivedAt":"2014-03-26T13:48:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Showing full index entry information is something for ls-files\nonly. The users of \"git ls\" may just want to know what entries are not\nunmerged.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 74eb3c2..1638983 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -527,7 +527,7 @@ static int ls_files(const char **argv, const char *prefix)\n \t}\n \tif (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed)\n \t\trequire_work_tree = 1;\n-\tif (show_unmerged)\n+\tif (show_unmerged && !porcelain)\n \t\t/*\n \t\t * There's no point in showing unmerged unless\n \t\t * you also show the stage information.\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237812","messageId":"1395841697-11742-13-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 12/17] ls: add -R/--recursive short for --max-depth=-1","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:12Z","receivedAt":"2014-03-26T13:48:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-ls.txt | 4 ++++\n builtin/ls-files.c       | 2 ++\n 2 files changed, 6 insertions(+)\n\ndiff --git a/Documentation/git-ls.txt b/Documentation/git-ls.txt\nindex 67ca522..10df6b0 100644\n--- a/Documentation/git-ls.txt\n+++ b/Documentation/git-ls.txt\n@@ -47,6 +47,10 @@ OPTIONS\n --unmerged::\n \tShow unmerged files in the output (forces --stage)\n \n+-R::\n+--recursive::\n+\tEquivalent of --max-depth=-1 (infinite recursion).\n+\n --color[=<when>]::\n \tColor file names. The value must be always (default), never,\n \tor auto.\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 1638983..772a6ce 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -725,6 +725,8 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tDIR_SHOW_IGNORED),\n \t\tOPT_BOOL('u', \"unmerged\", &show_unmerged,\n \t\t\tN_(\"show unmerged files in the output\")),\n+\t\tOPT_SET_INT('R', \"recursive\", &max_depth,\n+\t\t\t    N_(\"shortcut for --max-depth=-1\"), -1),\n \t\tOPT__COLOR(&use_color, N_(\"show color\")),\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n \t\t{ OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237813","messageId":"1395841697-11742-14-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 13/17] ls: add -1 short for --no-column in the spirit of GNU ls","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:13Z","receivedAt":"2014-03-26T13:48:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-ls.txt | 3 +++\n builtin/ls-files.c       | 2 ++\n 2 files changed, 5 insertions(+)\n\ndiff --git a/Documentation/git-ls.txt b/Documentation/git-ls.txt\nindex 10df6b0..0480c42 100644\n--- a/Documentation/git-ls.txt\n+++ b/Documentation/git-ls.txt\n@@ -51,6 +51,9 @@ OPTIONS\n --recursive::\n \tEquivalent of --max-depth=-1 (infinite recursion).\n \n+-1::\n+\tEquivalent of --no-column.\n+\n --color[=<when>]::\n \tColor file names. The value must be always (default), never,\n \tor auto.\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 772a6ce..014de05 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -729,6 +729,8 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n \t\t\t    N_(\"shortcut for --max-depth=-1\"), -1),\n \t\tOPT__COLOR(&use_color, N_(\"show color\")),\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n+\t\tOPT_SET_INT('1', NULL, &colopts,\n+\t\t\t    N_(\"shortcut for --no-column\"), COL_PARSEOPT),\n \t\t{ OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n \t\t\tN_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n \t\t\tNULL, 1 },\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237814","messageId":"1395841697-11742-15-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 14/17] ls: add -t back","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:14Z","receivedAt":"2014-03-26T13:48:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Tag \"H\" (cached) is not shown though because it's usually the majority\nand becomes noise. Not showing it makes the other tags stand out. -t\nis on by default if more than one file category is selected.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-ls.txt |  6 ++++++\n builtin/ls-files.c       | 27 +++++++++++++++++----------\n 2 files changed, 23 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/git-ls.txt b/Documentation/git-ls.txt\nindex 0480c42..126d9db 100644\n--- a/Documentation/git-ls.txt\n+++ b/Documentation/git-ls.txt\n@@ -47,6 +47,12 @@ OPTIONS\n --unmerged::\n \tShow unmerged files in the output (forces --stage)\n \n+-t::\n+--tag::\n+\tShow a tag to indicate file type, helpful when multiple file\n+\tselections are used. See linkgit::git-ls-files[1] option `-t`\n+\tfor more information.\n+\n -R::\n --recursive::\n \tEquivalent of --max-depth=-1 (infinite recursion).\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 014de05..392d273 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -515,16 +515,6 @@ static int ls_files(const char **argv, const char *prefix)\n \tfor (i = 0; i < exclude_list.nr; i++) {\n \t\tadd_exclude(exclude_list.items[i].string, \"\", 0, el, --exclude_args);\n \t}\n-\tif (show_tag || show_valid_bit) {\n-\t\ttag_cached = \"H \";\n-\t\ttag_unmerged = \"M \";\n-\t\ttag_removed = \"R \";\n-\t\ttag_modified = \"C \";\n-\t\ttag_other = \"? \";\n-\t\ttag_killed = \"K \";\n-\t\ttag_skip_worktree = \"S \";\n-\t\ttag_resolve_undo = \"U \";\n-\t}\n \tif (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed)\n \t\trequire_work_tree = 1;\n \tif (show_unmerged && !porcelain)\n@@ -578,6 +568,20 @@ static int ls_files(const char **argv, const char *prefix)\n \t      show_killed || show_modified || show_resolve_undo))\n \t\tshow_cached = 1;\n \n+\tif (show_tag == -1)\n+\t\tshow_tag = (show_cached + show_deleted + show_others +\n+\t\t\t    show_unmerged + show_killed + show_modified) > 1;\n+\tif (show_tag || show_valid_bit) {\n+\t\ttag_cached = porcelain ? \"  \" : \"H \";\n+\t\ttag_unmerged = \"M \";\n+\t\ttag_removed = \"R \";\n+\t\ttag_modified = \"C \";\n+\t\ttag_other = \"? \";\n+\t\ttag_killed = \"K \";\n+\t\ttag_skip_worktree = \"S \";\n+\t\ttag_resolve_undo = \"U \";\n+\t}\n+\n \tif (max_prefix)\n \t\tprune_cache(max_prefix);\n \tif (with_tree) {\n@@ -727,6 +731,8 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tN_(\"show unmerged files in the output\")),\n \t\tOPT_SET_INT('R', \"recursive\", &max_depth,\n \t\t\t    N_(\"shortcut for --max-depth=-1\"), -1),\n+\t\tOPT_BOOL('t', \"tag\", &show_tag,\n+\t\t\tN_(\"identify the file status with tags\")),\n \t\tOPT__COLOR(&use_color, N_(\"show color\")),\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n \t\tOPT_SET_INT('1', NULL, &colopts,\n@@ -756,6 +762,7 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n \tsetup_standard_excludes(&dir);\n \tuse_color = -1;\n \tmax_depth = 0;\n+\tshow_tag = -1;\n \n \targc = parse_options(argc, argv, prefix, builtin_ls_options,\n \t\t\t     ls_usage, 0);\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237815","messageId":"1395841697-11742-16-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 15/17] ls: sort output and remove duplicates","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:15Z","receivedAt":"2014-03-26T13:48:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When you mix different file types, with ls-files you may get separate\nlisting. For example, \"ls-files -cm\" will show file \"abc\" twice: one\nas part of cached list, one of modified list. With \"ls\" (and this\npatch) they will be in a single sorted list (easier for the eye).\n\nDuplicate entries are also removed. Note that display content is\ncompared, so if you have \"-t\" on, or you color file types differently,\nyou will get duplicate textual entries. This is good imo.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c | 30 +++++++++++++++++++++---------\n 1 file changed, 21 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 392d273..709d8b1 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -57,6 +57,13 @@ static const char *tag_modified = \"\";\n static const char *tag_skip_worktree = \"\";\n static const char *tag_resolve_undo = \"\";\n \n+static int compare_output(const void *a_, const void *b_)\n+{\n+\tconst struct string_list_item *a = a_;\n+\tconst struct string_list_item *b = b_;\n+\treturn strcmp(a->util, b->util);\n+}\n+\n static void write_name(struct strbuf *sb, const char *name)\n {\n \t/*\n@@ -72,10 +79,12 @@ static void write_name(struct strbuf *sb, const char *name)\n \t\tquote_path_relative(name, real_prefix, sb);\n }\n \n-static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n+static void strbuf_fputs(struct strbuf *sb, const char *full_name, FILE *fp)\n {\n-\tif (column_active(colopts)) {\n-\t\tstring_list_append(&output, strbuf_detach(sb, NULL));\n+\tif (column_active(colopts) || porcelain) {\n+\t\tstruct string_list_item *it;\n+\t\tit = string_list_append(&output, strbuf_detach(sb, NULL));\n+\t\tit->util = (void*)full_name;\n \t\treturn;\n \t}\n \tfwrite(sb->buf, sb->len, 1, fp);\n@@ -110,7 +119,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n \tstrbuf_reset(&sb);\n \tstrbuf_addstr(&sb, tag);\n \twrite_dir_entry(&sb, ent);\n-\tstrbuf_fputs(&sb, stdout);\n+\tstrbuf_fputs(&sb, ent->name, stdout);\n }\n \n static void show_other_files(struct dir_struct *dir)\n@@ -227,7 +236,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n \t\t\t    ce_stage(ce));\n \t}\n \twrite_ce_name(&sb, ce);\n-\tstrbuf_fputs(&sb, stdout);\n+\tstrbuf_fputs(&sb, ce->name, stdout);\n \tif (debug_mode) {\n \t\tconst struct stat_data *sd = &ce->ce_stat_data;\n \n@@ -535,7 +544,7 @@ static int ls_files(const char **argv, const char *prefix)\n \t\tif (debug_mode)\n \t\t\tdie(_(\"--column and --debug are incompatible\"));\n \t}\n-\tif (column_active(colopts))\n+\tif (column_active(colopts) || porcelain)\n \t\tline_terminator = 0;\n \n \tif (require_work_tree && !is_inside_work_tree())\n@@ -601,10 +610,13 @@ static int ls_files(const char **argv, const char *prefix)\n \tif (show_resolve_undo)\n \t\tshow_ru_info();\n \n-\tif (column_active(colopts)) {\n-\t\tprint_columns(&output, colopts, NULL);\n-\t\tstring_list_clear(&output, 0);\n+\tif (porcelain) {\n+\t\tqsort(output.items, output.nr, sizeof(*output.items),\n+\t\t      compare_output);\n+\t\tstring_list_remove_duplicates(&output, 0);\n \t}\n+\tprint_columns(&output, colopts, NULL);\n+\tstring_list_clear(&output, 0);\n \n \tif (ps_matched) {\n \t\tint bad;\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237816","messageId":"1395841697-11742-17-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 16/17] ls: do not show duplicate cached entries","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:16Z","receivedAt":"2014-03-26T13:48:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"With the current show_files() \"ls -tcm\" will show\n\n  foo.c\nM foo.c\n\nThe first item is redundant. If \"foo.c\" is modified, we know it's in\nthe cache. Introduce show_files_compact to do that because ls-files is\nplumbing and scripts may already depend on current display behavior.\n\nAnother difference in show_files_compact() is it does not show\nskip-worktree (aka outside sparse checkout) entries anymore, which\nmakes sense in porcelain context.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 51 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 709d8b1..cd8e35c 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -337,6 +337,53 @@ static void show_files(struct dir_struct *dir)\n \t}\n }\n \n+static void show_files_compact(struct dir_struct *dir)\n+{\n+\tint i;\n+\n+\t/* For cached/deleted files we don't need to even do the readdir */\n+\tif (show_others || show_killed) {\n+\t\tif (!show_others)\n+\t\t\tdir->flags |= DIR_COLLECT_KILLED_ONLY;\n+\t\tfill_directory(dir, &pathspec);\n+\t\tif (show_others)\n+\t\t\tshow_other_files(dir);\n+\t\tif (show_killed)\n+\t\t\tshow_killed_files(dir);\n+\t}\n+\tif (!(show_cached || show_stage || show_deleted || show_modified))\n+\t\treturn;\n+\tfor (i = 0; i < active_nr; i++) {\n+\t\tconst struct cache_entry *ce = active_cache[i];\n+\t\tstruct stat st;\n+\t\tint err, shown = 0;\n+\t\tif ((dir->flags & DIR_SHOW_IGNORED) &&\n+\t\t    !ce_excluded(dir, ce))\n+\t\t\tcontinue;\n+\t\tif (show_unmerged && !ce_stage(ce))\n+\t\t\tcontinue;\n+\t\tif (ce->ce_flags & CE_UPDATE)\n+\t\t\tcontinue;\n+\t\tif (ce_skip_worktree(ce))\n+\t\t\tcontinue;\n+\t\terr = lstat(ce->name, &st);\n+\t\tif (show_deleted && err) {\n+\t\t\tshow_ce_entry(tag_removed, ce);\n+\t\t\tshown = 1;\n+\t\t}\n+\t\tif (show_modified && ce_modified(ce, &st, 0)) {\n+\t\t\tshow_ce_entry(tag_modified, ce);\n+\t\t\tshown = 1;\n+\t\t}\n+\t\tif (ce_stage(ce)) {\n+\t\t\tshow_ce_entry(tag_unmerged, ce);\n+\t\t\tshown = 1;\n+\t\t}\n+\t\tif (!shown && show_cached)\n+\t\t\tshow_ce_entry(tag_cached, ce);\n+\t}\n+}\n+\n /*\n  * Prune the index to only contain stuff starting with \"prefix\"\n  */\n@@ -606,7 +653,10 @@ static int ls_files(const char **argv, const char *prefix)\n \t\trefresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL);\n \t\tsetup_pager();\n \t}\n-\tshow_files(&dir);\n+\tif (porcelain)\n+\t\tshow_files_compact(&dir);\n+\telse\n+\t\tshow_files(&dir);\n \tif (show_resolve_undo)\n \t\tshow_ru_info();\n \n-- \n1.9.1.345.ga1a145c\n"},{"id":"237817","messageId":"1395841697-11742-18-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 17/17] ls: show directories as well as files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T13:48:17Z","receivedAt":"2014-03-26T13:48:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The index does not store directories explicitly (except submodules) so\nwe have to figure them out from file list. The function\nshow_directories() deliberately generates duplicate directories and\nexpects the previous patch to remove duplicates.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c | 41 +++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 41 insertions(+)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex cd8e35c..7e50192 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -27,6 +27,7 @@ static int show_resolve_undo;\n static int show_modified;\n static int show_killed;\n static int show_valid_bit;\n+static int show_dirs;\n static int line_terminator = '\\n';\n static int debug_mode;\n static int use_color;\n@@ -337,6 +338,43 @@ static void show_files(struct dir_struct *dir)\n \t}\n }\n \n+static void show_directories(const struct cache_entry *ce)\n+{\n+\tstatic const char *last_directory;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char *p = ce->name + prefix_len;\n+\tconst char *sep;\n+\n+\tif (last_directory) {\n+\t\tint len = strlen(last_directory);\n+\t\tif (!strncmp(ce->name, last_directory, len) &&\n+\t\t    ce->name[len] == '/')\n+\t\t\tp += len + 1;\n+\t}\n+\n+\twhile (*p && (sep = strchr(p, '/'))) {\n+\t\tstruct strbuf sb2 = STRBUF_INIT;\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_add(&sb, ce->name, sep - ce->name);\n+\t\tp = sep + 1;\n+\t\tif (!match_pathspec(&pathspec, sb.buf, sb.len,\n+\t\t\t\t    prefix_len, NULL, 1))\n+\t\t\tcontinue;\n+\t\twrite_name(&sb2, sb.buf);\n+\t\tif (want_color(use_color)) {\n+\t\t\tstruct strbuf sb3 = STRBUF_INIT;\n+\t\t\tcolor_filename(&sb3, ce->name, sb2.buf, S_IFDIR, 1);\n+\t\t\tstrbuf_release(&sb2);\n+\t\t\tsb2 = sb3;\n+\t\t}\n+\t\tif (show_tag)\n+\t\t\tstrbuf_insert(&sb2, 0, tag_cached, strlen(tag_cached));\n+\t\tlast_directory = strbuf_detach(&sb, NULL);\n+\t\tstrbuf_fputs(&sb2, last_directory, NULL);\n+\t\tstrbuf_release(&sb2);\n+\t}\n+}\n+\n static void show_files_compact(struct dir_struct *dir)\n {\n \tint i;\n@@ -357,6 +395,8 @@ static void show_files_compact(struct dir_struct *dir)\n \t\tconst struct cache_entry *ce = active_cache[i];\n \t\tstruct stat st;\n \t\tint err, shown = 0;\n+\t\tif (show_dirs)\n+\t\t\tshow_directories(ce);\n \t\tif ((dir->flags & DIR_SHOW_IGNORED) &&\n \t\t    !ce_excluded(dir, ce))\n \t\t\tcontinue;\n@@ -825,6 +865,7 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n \tuse_color = -1;\n \tmax_depth = 0;\n \tshow_tag = -1;\n+\tshow_dirs = 1;\n \n \targc = parse_options(argc, argv, prefix, builtin_ls_options,\n \t\t\t     ls_usage, 0);\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237832","messageId":"CAPig+cTMRHi2sWQLZzdHZGWT6GBX-3v8exx8B4jpWm0Tgvg6+Q@mail.gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-7-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 06/17] ls-files: add --color to highlight file names","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2014-03-26T19:13:42Z","receivedAt":"2014-03-26T19:13:42Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Mar 26, 2014 at 9:48 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  Documentation/git-ls-files.txt |  9 +++++++++\n>  builtin/ls-files.c             | 38 +++++++++++++++++++++++++++++++++++---\n>  2 files changed, 44 insertions(+), 3 deletions(-)\n>\n> diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\n> index c0856a6..5c1b7f3 100644\n> --- a/Documentation/git-ls-files.txt\n> +++ b/Documentation/git-ls-files.txt\n> @@ -147,6 +147,15 @@ a space) at the start of each line:\n>         possible for manual inspection; the exact format may change at\n>         any time.\n>\n> +--color[=<when>]::\n> +       Color file names. The value must be always (default), never,\n> +       or auto.\n\nHere, the default is \"always\"...\n\n> +--no-color::\n> +       Turn off coloring, even when the configuration file gives the\n> +       default to color output, same as `--color=never`. This is the\n> +       default.\n\nBut, here the default is \"never\".\n\n> +\n>  \\--::\n>         Do not interpret any more arguments as options.\n>\n> diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> index 6e30592..2857b38 100644\n> --- a/builtin/ls-files.c\n> +++ b/builtin/ls-files.c\n> @@ -14,6 +14,7 @@\n>  #include \"resolve-undo.h\"\n>  #include \"string-list.h\"\n>  #include \"pathspec.h\"\n> +#include \"color.h\"\n>\n>  static int abbrev;\n>  static int show_deleted;\n> @@ -27,6 +28,7 @@ static int show_killed;\n>  static int show_valid_bit;\n>  static int line_terminator = '\\n';\n>  static int debug_mode;\n> +static int use_color;\n>\n>  static const char *prefix;\n>  static int max_prefix_len;\n> @@ -60,7 +62,6 @@ static void write_name(struct strbuf *sb, const char *name)\n>                 strbuf_release(&sb2);\n>         } else\n>                 quote_path_relative(name, real_prefix, sb);\n> -       strbuf_addch(sb, line_terminator);\n>  }\n>\n>  static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n> @@ -68,6 +69,21 @@ static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n>         fwrite(sb->buf, sb->len, 1, fp);\n>  }\n>\n> +static void write_dir_entry(struct strbuf *sb, const struct dir_entry *ent)\n> +{\n> +       struct strbuf quoted = STRBUF_INIT;\n> +       struct stat st;\n> +       if (stat(ent->name, &st))\n> +               st.st_mode = 0;\n> +       write_name(&quoted, ent->name);\n> +       if (want_color(use_color))\n> +               color_filename(sb, ent->name, quoted.buf, st.st_mode, 1);\n> +       else\n> +               strbuf_addbuf(sb, &quoted);\n> +       strbuf_addch(sb, line_terminator);\n> +       strbuf_release(&quoted);\n> +}\n> +\n>  static void show_dir_entry(const char *tag, struct dir_entry *ent)\n>  {\n>         static struct strbuf sb = STRBUF_INIT;\n> @@ -81,7 +97,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n>\n>         strbuf_reset(&sb);\n>         strbuf_addstr(&sb, tag);\n> -       write_name(&sb, ent->name);\n> +       write_dir_entry(&sb, ent);\n>         strbuf_fputs(&sb, stdout);\n>  }\n>\n> @@ -146,6 +162,18 @@ static void show_killed_files(struct dir_struct *dir)\n>         }\n>  }\n>\n> +static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)\n> +{\n> +       struct strbuf quoted = STRBUF_INIT;\n> +       write_name(&quoted, ce->name);\n> +       if (want_color(use_color))\n> +               color_filename(sb, ce->name, quoted.buf, ce->ce_mode, 1);\n> +       else\n> +               strbuf_addbuf(sb, &quoted);\n> +       strbuf_addch(sb, line_terminator);\n> +       strbuf_release(&quoted);\n> +}\n> +\n>  static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n>  {\n>         static struct strbuf sb = STRBUF_INIT;\n> @@ -186,7 +214,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n>                             find_unique_abbrev(ce->sha1,abbrev),\n>                             ce_stage(ce));\n>         }\n> -       write_name(&sb, ce->name);\n> +       write_ce_name(&sb, ce);\n>         strbuf_fputs(&sb, stdout);\n>         if (debug_mode) {\n>                 const struct stat_data *sd = &ce->ce_stat_data;\n> @@ -523,6 +551,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n>                         N_(\"if any <file> is not in the index, treat this as an error\")),\n>                 OPT_STRING(0, \"with-tree\", &with_tree, N_(\"tree-ish\"),\n>                         N_(\"pretend that paths removed since <tree-ish> are still present\")),\n> +               OPT__COLOR(&use_color, N_(\"show color\")),\n>                 OPT__ABBREV(&abbrev),\n>                 OPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n>                 OPT_END()\n> @@ -570,6 +599,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n>         if (require_work_tree && !is_inside_work_tree())\n>                 setup_work_tree();\n>\n> +       if (want_color(use_color))\n> +               parse_ls_color();\n> +\n>         parse_pathspec(&pathspec, 0,\n>                        PATHSPEC_PREFER_CWD |\n>                        PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,\n> --\n> 1.9.1.345.ga1a145c\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n"},{"id":"237833","messageId":"CAPig+cSSrc6=nX2=2BpaFRzT4Evf67kuXM8rKBTSkB6JrNN0yQ@mail.gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-4-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 03/17] ls_colors.c: add function to color a file name","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2014-03-26T19:14:44Z","receivedAt":"2014-03-26T19:14:44Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Mar 26, 2014 at 9:48 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Tthe new function is based on print_color_indicator() from commit\n\ns/Tthe/The/\n\n> 7326d1f1a67edf21947ae98194f98c38b6e9e527 in coreutils.git.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  color.h     |  2 ++\n>  ls_colors.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n>  2 files changed, 68 insertions(+)\n>\n> diff --git a/color.h b/color.h\n> index 640fc48..398369a 100644\n> --- a/color.h\n> +++ b/color.h\n> @@ -94,5 +94,7 @@ void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb);\n>  int color_is_nil(const char *color);\n>\n>  void parse_ls_color(void);\n> +void color_filename(struct strbuf *sb, const char *name,\n> +                   const char *display_name, mode_t mode, int linkok);\n>\n>  #endif /* COLOR_H */\n> diff --git a/ls_colors.c b/ls_colors.c\n> index cef5a92..1125329 100644\n> --- a/ls_colors.c\n> +++ b/ls_colors.c\n> @@ -422,3 +422,69 @@ void parse_ls_color(void)\n>                 color_symlink_as_referent = 1;\n>         git_config(ls_colors_config, NULL);\n>  }\n> +\n> +void color_filename(struct strbuf *sb, const char *name,\n> +                   const char *display_name, mode_t mode, int linkok)\n> +{\n> +       int type;\n> +       struct color_ext_type *ext;     /* Color extension */\n> +\n> +       if (S_ISREG (mode)) {\n> +               type = LS_FL;\n> +               if ((mode & S_ISUID) != 0)\n> +                       type = LS_SU;\n> +               else if ((mode & S_ISGID) != 0)\n> +                       type = LS_SG;\n> +               else if ((mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)\n> +                       type = LS_EX;\n> +       } else if (S_ISDIR (mode)) {\n> +               if ((mode & S_ISVTX) && (mode & S_IWOTH))\n> +                       type = LS_TW;\n> +               else if ((mode & S_IWOTH) != 0)\n> +                       type = LS_OW;\n> +               else if ((mode & S_ISVTX) != 0)\n> +                       type = LS_ST;\n> +               else\n> +                       type = LS_DI;\n> +       } else if (S_ISLNK (mode))\n> +               type = (!linkok && *ls_colors[LS_OR]) ? LS_OR : LS_LN;\n> +       else if (S_ISFIFO (mode))\n> +               type = LS_PI;\n> +       else if (S_ISSOCK (mode))\n> +               type = LS_SO;\n> +       else if (S_ISBLK (mode))\n> +               type = LS_BD;\n> +       else if (S_ISCHR (mode))\n> +               type = LS_CD;\n> +#ifdef S_ISDOOR\n> +       else if (S_ISDOOR (mode))\n> +               type = LS_DO;\n> +#endif\n> +       else\n> +               /* Classify a file of some other type as C_ORPHAN.  */\n> +               type = LS_OR;\n> +\n> +       /* Check the file's suffix only if still classified as C_FILE.  */\n> +       ext = NULL;\n> +       if (type == LS_FL) {\n> +               /* Test if NAME has a recognized suffix.  */\n> +               size_t len = strlen(name);\n> +               const char *p = name + len;             /* Pointer to final \\0.  */\n> +               for (ext = color_ext_list; ext != NULL; ext = ext->next) {\n> +                       if (ext->ext.len <= len &&\n> +                           !strncmp(p - ext->ext.len, ext->ext.string, ext->ext.len))\n> +                               break;\n> +               }\n> +       }\n> +\n> +       if (display_name)\n> +               name = display_name;\n> +       if (ext)\n> +               strbuf_addf(sb, \"\\033[%.*sm%s%s\",\n> +                           (int)ext->seq.len, ext->seq.string,\n> +                           name, GIT_COLOR_RESET);\n> +       else if (*ls_colors[type])\n> +               strbuf_addf(sb, \"%s%s%s\", ls_colors[type], name, GIT_COLOR_RESET);\n> +       else\n> +               strbuf_addstr(sb, name);\n> +}\n> --\n> 1.9.1.345.ga1a145c\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n"},{"id":"237835","messageId":"CAPig+cRpL9PYKLEH8DocKWo2-ZnqtD6j30TLES1NyOnk52D+Mw@mail.gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-6-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 05/17] ls-files: buffer full item in strbuf before printing","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2014-03-26T19:22:03Z","receivedAt":"2014-03-26T19:22:03Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Mar 26, 2014 at 9:48 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Buffering so that we can manipulate the strings (e.g. coloring)\n> further before finally printing them.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  builtin/ls-files.c | 48 +++++++++++++++++++++++++++++++++++-------------\n>  1 file changed, 35 insertions(+), 13 deletions(-)\n>\n> diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> index 47c3880..6e30592 100644\n> --- a/builtin/ls-files.c\n> +++ b/builtin/ls-files.c\n> @@ -47,18 +47,30 @@ static const char *tag_modified = \"\";\n>  static const char *tag_skip_worktree = \"\";\n>  static const char *tag_resolve_undo = \"\";\n>\n> -static void write_name(const char *name)\n> +static void write_name(struct strbuf *sb, const char *name)\n>  {\n>         /*\n>          * With \"--full-name\", prefix_len=0; this caller needs to pass\n>          * an empty string in that case (a NULL is good for \"\").\n>          */\n> -       write_name_quoted_relative(name, prefix_len ? prefix : NULL,\n> -                                  stdout, line_terminator);\n> +       const char *real_prefix = prefix_len ? prefix : NULL;\n> +       if (!line_terminator) {\n> +               struct strbuf sb2 = STRBUF_INIT;\n> +               strbuf_addstr(sb, relative_path(name, real_prefix, &sb2));\n> +               strbuf_release(&sb2);\n> +       } else\n> +               quote_path_relative(name, real_prefix, sb);\n> +       strbuf_addch(sb, line_terminator);\n> +}\n> +\n> +static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n> +{\n> +       fwrite(sb->buf, sb->len, 1, fp);\n>  }\n>\n>  static void show_dir_entry(const char *tag, struct dir_entry *ent)\n>  {\n> +       static struct strbuf sb = STRBUF_INIT;\n>         int len = max_prefix_len;\n>\n>         if (len >= ent->len)\n> @@ -67,8 +79,10 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n>         if (!dir_path_match(ent, &pathspec, len, ps_matched))\n>                 return;\n>\n> -       fputs(tag, stdout);\n> -       write_name(ent->name);\n> +       strbuf_reset(&sb);\n> +       strbuf_addstr(&sb, tag);\n> +       write_name(&sb, ent->name);\n> +       strbuf_fputs(&sb, stdout);\n\nstrbuf_release(&sb);\n\n>  }\n>\n>  static void show_other_files(struct dir_struct *dir)\n> @@ -134,6 +148,7 @@ static void show_killed_files(struct dir_struct *dir)\n>\n>  static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n>  {\n> +       static struct strbuf sb = STRBUF_INIT;\n>         int len = max_prefix_len;\n>\n>         if (len >= ce_namelen(ce))\n> @@ -161,16 +176,18 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n>                 tag = alttag;\n>         }\n>\n> +       strbuf_reset(&sb);\n\n'sb' is empty at this point. Why reset it?\n\n>         if (!show_stage) {\n> -               fputs(tag, stdout);\n> +               strbuf_addstr(&sb, tag);\n>         } else {\n> -               printf(\"%s%06o %s %d\\t\",\n> -                      tag,\n> -                      ce->ce_mode,\n> -                      find_unique_abbrev(ce->sha1,abbrev),\n> -                      ce_stage(ce));\n> +               strbuf_addf(&sb, \"%s%06o %s %d\\t\",\n> +                           tag,\n> +                           ce->ce_mode,\n> +                           find_unique_abbrev(ce->sha1,abbrev),\n> +                           ce_stage(ce));\n>         }\n> -       write_name(ce->name);\n> +       write_name(&sb, ce->name);\n> +       strbuf_fputs(&sb, stdout);\n\nstrbuf_release(&sb);\n\n>         if (debug_mode) {\n>                 const struct stat_data *sd = &ce->ce_stat_data;\n>\n> @@ -206,7 +223,12 @@ static void show_ru_info(void)\n>                         printf(\"%s%06o %s %d\\t\", tag_resolve_undo, ui->mode[i],\n>                                find_unique_abbrev(ui->sha1[i], abbrev),\n>                                i + 1);\n> -                       write_name(path);\n> +                       /*\n> +                        * With \"--full-name\", prefix_len=0; this caller needs to pass\n> +                        * an empty string in that case (a NULL is good for \"\").\n> +                        */\n> +                       write_name_quoted_relative(path, prefix_len ? prefix : NULL,\n> +                                                  stdout, line_terminator);\n>                 }\n>         }\n>  }\n> --\n> 1.9.1.345.ga1a145c\n"},{"id":"237840","messageId":"CAPig+cTj_ZM3OOn1ZHwYQQYRtQevhAA33wUH9op_r_u+yLorrg@mail.gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-8-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 07/17] ls-files: add --column","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2014-03-26T19:46:57Z","receivedAt":"2014-03-26T19:46:57Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Mar 26, 2014 at 9:48 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  Documentation/git-ls-files.txt |  6 ++++++\n>  builtin/ls-files.c             | 25 +++++++++++++++++++++++++\n>  2 files changed, 31 insertions(+)\n>\n> diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\n> index 5c1b7f3..cd52461 100644\n> --- a/Documentation/git-ls-files.txt\n> +++ b/Documentation/git-ls-files.txt\n> @@ -156,6 +156,12 @@ a space) at the start of each line:\n>         default to color output, same as `--color=never`. This is the\n>         default.\n>\n> +--column[=<options>]::\n> +--no-column::\n> +       Display files in columns. See configuration variable column.ui\n> +       for option syntax.`--column` and `--no-column` without options\n\nMissing space after period.\n\n> +       are equivalent to 'always' and 'never' respectively.\n> +\n>  \\--::\n>         Do not interpret any more arguments as options.\n>\n> diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> index 2857b38..335d3b0 100644\n> --- a/builtin/ls-files.c\n> +++ b/builtin/ls-files.c\n> @@ -15,6 +15,7 @@\n>  #include \"string-list.h\"\n>  #include \"pathspec.h\"\n>  #include \"color.h\"\n> +#include \"column.h\"\n>\n>  static int abbrev;\n>  static int show_deleted;\n> @@ -29,6 +30,7 @@ static int show_valid_bit;\n>  static int line_terminator = '\\n';\n>  static int debug_mode;\n>  static int use_color;\n> +static unsigned int colopts;\n>\n>  static const char *prefix;\n>  static int max_prefix_len;\n> @@ -39,6 +41,7 @@ static char *ps_matched;\n>  static const char *with_tree;\n>  static int exc_given;\n>  static int exclude_args;\n> +static struct string_list output = STRING_LIST_INIT_NODUP;\n>\n>  static const char *tag_cached = \"\";\n>  static const char *tag_unmerged = \"\";\n> @@ -66,6 +69,10 @@ static void write_name(struct strbuf *sb, const char *name)\n>\n>  static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n>  {\n> +       if (column_active(colopts)) {\n> +               string_list_append(&output, strbuf_detach(sb, NULL));\n> +               return;\n> +       }\n>         fwrite(sb->buf, sb->len, 1, fp);\n>  }\n>\n> @@ -552,6 +559,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n>                 OPT_STRING(0, \"with-tree\", &with_tree, N_(\"tree-ish\"),\n>                         N_(\"pretend that paths removed since <tree-ish> are still present\")),\n>                 OPT__COLOR(&use_color, N_(\"show color\")),\n> +               OPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n>                 OPT__ABBREV(&abbrev),\n>                 OPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n>                 OPT_END()\n> @@ -596,6 +604,18 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n>         if (dir.exclude_per_dir)\n>                 exc_given = 1;\n>\n> +       finalize_colopts(&colopts, -1);\n> +       if (explicitly_enable_column(colopts)) {\n> +               if (!line_terminator)\n> +                       die(_(\"--column and -z are incompatible\"));\n> +               if (show_resolve_undo)\n> +                       die(_(\"--column and --resolve-undo are incompatible\"));\n> +               if (debug_mode)\n> +                       die(_(\"--column and --debug are incompatible\"));\n> +       }\n> +       if (column_active(colopts))\n> +               line_terminator = 0;\n> +\n>         if (require_work_tree && !is_inside_work_tree())\n>                 setup_work_tree();\n>\n> @@ -638,6 +658,11 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n>         if (show_resolve_undo)\n>                 show_ru_info();\n>\n> +       if (column_active(colopts)) {\n> +               print_columns(&output, colopts, NULL);\n> +               string_list_clear(&output, 0);\n> +       }\n> +\n>         if (ps_matched) {\n>                 int bad;\n>                 bad = report_path_error(ps_matched, &pathspec, prefix);\n> --\n> 1.9.1.345.ga1a145c\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n"},{"id":"237841","messageId":"CAPig+cT--UV134qtuComeMytNQNRSsoidkPmNGo-unoGUe0OjQ@mail.gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-9-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 08/17] ls-files: support --max-depth","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2014-03-26T19:50:08Z","receivedAt":"2014-03-26T19:50:08Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Mar 26, 2014 at 9:48 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  Documentation/git-ls-files.txt | 7 +++++++\n>  builtin/ls-files.c             | 7 +++++++\n>  2 files changed, 14 insertions(+)\n>\n> diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\n> index cd52461..3c022eb 100644\n> --- a/Documentation/git-ls-files.txt\n> +++ b/Documentation/git-ls-files.txt\n> @@ -162,6 +162,13 @@ a space) at the start of each line:\n>         for option syntax.`--column` and `--no-column` without options\n>         are equivalent to 'always' and 'never' respectively.\n>\n> +--max-depth <depth>::\n\nOther options in this file are documented as:\n\n    --max-depth=<depth>::\n\n> +       For each <pathspec> given on command line, descend at most <depth>\n> +       levels of directories. A negative value means no limit.\n> +       This option is ignored if <pathspec> contains active wildcards.\n> +       In other words if \"a*\" matches a directory named \"a*\",\n> +       \"*\" is matched literally so --max-depth is still effective.\n\nWould it be worthwhile to mention the default?\n\n> +\n>  \\--::\n>         Do not interpret any more arguments as options.\n>\n> diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> index 335d3b0..8eef423 100644\n> --- a/builtin/ls-files.c\n> +++ b/builtin/ls-files.c\n> @@ -503,6 +503,7 @@ static int option_parse_exclude_standard(const struct option *opt,\n>  int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n>  {\n>         int require_work_tree = 0, show_tag = 0, i;\n> +       int max_depth = -1;\n>         const char *max_prefix;\n>         struct dir_struct dir;\n>         struct exclude_list *el;\n> @@ -560,6 +561,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n>                         N_(\"pretend that paths removed since <tree-ish> are still present\")),\n>                 OPT__COLOR(&use_color, N_(\"show color\")),\n>                 OPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n> +               { OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n> +                       N_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n> +                       NULL, 1 },\n>                 OPT__ABBREV(&abbrev),\n>                 OPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n>                 OPT_END()\n> @@ -624,8 +628,11 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n>\n>         parse_pathspec(&pathspec, 0,\n>                        PATHSPEC_PREFER_CWD |\n> +                      (max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0) |\n>                        PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,\n>                        prefix, argv);\n> +       pathspec.max_depth = max_depth;\n> +       pathspec.recursive = 1;\n>\n>         /* Find common prefix for all pathspec's */\n>         max_prefix = common_prefix(&pathspec);\n> --\n> 1.9.1.345.ga1a145c\n"},{"id":"237843","messageId":"CAPig+cQfUnJ+pDsJbO8WgPtu2_+U46DD-A5jKSPo33gCu3vFKw@mail.gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-11-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 10/17] Add git-ls, a user friendly version of ls-files and more","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2014-03-26T20:16:50Z","receivedAt":"2014-03-26T20:16:50Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Mar 26, 2014 at 9:48 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> This is more user friendly version of ls-files:\n>\n> * it's automatically colored and columnized\n> * it refreshes the index like all porcelain commands\n> * it defaults to non-recursive behavior like ls\n> * :(glob) is on by default so '*.c' means a.c but not a/b.c, use\n>   '**/*.c' for that.\n> * auto pager\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  .gitignore                     |  1 +\n>  Documentation/config.txt       | 10 ++++++\n>  Documentation/git-ls.txt (new) | 82 ++++++++++++++++++++++++++++++++++++++++++\n>  Makefile                       |  1 +\n>  builtin.h                      |  1 +\n>  builtin/ls-files.c             | 70 ++++++++++++++++++++++++++++++++++++\n>  command-list.txt               |  1 +\n>  git.c                          |  1 +\n>  8 files changed, 167 insertions(+)\n>  create mode 100644 Documentation/git-ls.txt\n>\n> diff --git a/.gitignore b/.gitignore\n> index dc600f9..f91af81 100644\n> --- a/.gitignore\n> +++ b/.gitignore\n> @@ -76,6 +76,7 @@\n>  /git-init-db\n>  /git-instaweb\n>  /git-log\n> +/git-ls\n>  /git-ls-files\n>  /git-ls-remote\n>  /git-ls-tree\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index 6bca55e..87a6dcf 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -909,6 +909,12 @@ color.status.<slot>::\n>         to red). The values of these variables may be specified as in\n>         color.branch.<slot>.\n>\n> +color.ls::\n> +       A boolean to enable/disable color in the output of\n> +       linkgit:git-ls[1]. May be set to `always`, `false` (or\n> +       `never`) or `auto` (or `true`), in which case colors are used\n> +       only when the output is to a terminal. Defaults to false.\n> +\n>  color.ls.<slot>::\n>         Use customized color for file name colorization. If not set\n>         and the environment variable LS_COLORS is set, color settings\n> @@ -981,6 +987,10 @@ column.clean::\n>         Specify the layout when list items in `git clean -i`, which always\n>         shows files and directories in columns. See `column.ui` for details.\n>\n> +column.ls::\n> +       Specify whether to output tag listing in `git ls` in columns.\n> +       See `column.ui` for details.\n> +\n>  column.status::\n>         Specify whether to output untracked files in `git status` in columns.\n>         See `column.ui` for details.\n> diff --git a/Documentation/git-ls.txt b/Documentation/git-ls.txt\n> new file mode 100644\n> index 0000000..67ca522\n> --- /dev/null\n> +++ b/Documentation/git-ls.txt\n> @@ -0,0 +1,82 @@\n> +git-ls(1)\n> +===============\n> +\n> +NAME\n> +----\n> +git-ls - List files\n> +\n> +SYNOPSIS\n> +--------\n> +[verse]\n> +'git ls' (--[cached|deleted|others|ignored|unmerged|modified])*\n> +       (-[c|d|o|i|s|u|m])*\n\nDon't you have [...] and (...) transposed? The way it's written, \"-\"\nand \"--\" are valid optional arguments. You probably meant:\n\n    [--(cached|deleted|x|y|z)]...\n    [-(c|d|x|y|z)]...\n\n> +       [options] [<pathspec>...]\n\nHowever, you also have the generic [options] here, which covers all of\nthe above. It probably would make sense to just use [options] and drop\nthe enumerated list.\n\n> +DESCRIPTION\n> +-----------\n> +List files (by default in current working directory) that are in the\n> +index. Depending on the chosen options, maybe only modified files in\n> +working tree are shown, or untracked files...\n> +\n> +OPTIONS\n> +-------\n> +-c::\n> +--cached::\n> +       Show cached files in the output (default)\n\n\"in the output\" is superfluous. Perhaps drop it from each of the descriptions.\n\n> +-d::\n> +--deleted::\n> +       Show deleted files in the output\n\nIs this showing only deleted file or including them in the list of\nfiles otherwise displayed? It's not clear from the description. Same\nquestion for the other options.\n\n> +-m::\n> +--modified::\n> +       Show modified files in the output\n> +\n> +-o::\n> +--others::\n> +       Show other (i.e. untracked) files in the output\n> +\n> +-i::\n> +--ignored::\n> +       Show only ignored files in the output. When showing files in the\n> +       index, print only those matched by an exclude pattern. When\n> +       showing \"other\" files, show only those matched by an exclude\n> +       pattern.\n> +\n> +-u::\n> +--unmerged::\n> +       Show unmerged files in the output (forces --stage)\n\nThis is the only mention of --stage in this document. Not sure what\nit's trying to say.\n\n> +--color[=<when>]::\n> +       Color file names. The value must be always (default), never,\n> +       or auto.\n\nSame problem mentioned in the other patch. Default is \"always\"...\n\n> +\n> +--no-color::\n> +       Turn off coloring, even when the configuration file gives the\n> +       default to color output, same as `--color=never`. This is the\n> +       default.\n\nBut default is also \"never\".\n\n> +\n> +--column[=<options>]::\n> +--no-column::\n> +       Display files in columns. See configuration variable column.ui\n> +       for option syntax.`--column` and `--no-column` without options\n\nMissing space after period.\n\nMore below.\n\n> +       are equivalent to 'always' and 'never' respectively.\n> +\n> +--max-depth <depth>::\n> +       For each <pathspec> given on command line, descend at most <depth>\n> +       levels of directories. A negative value means no limit.\n> +       This option is ignored if <pathspec> contains active wildcards.\n> +       In other words if \"a*\" matches a directory named \"a*\",\n> +       \"*\" is matched literally so --max-depth is still effective.\n> +\n> +<pathspec>::\n> +       Files to show. :(glob) magic is enabled and recursion disabled\n> +       by default.\n> +\n> +SEE ALSO\n> +--------\n> +linkgit:git-ls-files[1]\n> +\n> +GIT\n> +---\n> +Part of the linkgit:git[1] suite\n> diff --git a/Makefile b/Makefile\n> index f6a6e14..b0bc40a 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -584,6 +584,7 @@ BUILT_INS += git-cherry-pick$X\n>  BUILT_INS += git-format-patch$X\n>  BUILT_INS += git-fsck-objects$X\n>  BUILT_INS += git-init$X\n> +BUILT_INS += git-ls$X\n>  BUILT_INS += git-merge-subtree$X\n>  BUILT_INS += git-show$X\n>  BUILT_INS += git-stage$X\n> diff --git a/builtin.h b/builtin.h\n> index c47c110..177aa7d 100644\n> --- a/builtin.h\n> +++ b/builtin.h\n> @@ -75,6 +75,7 @@ extern int cmd_index_pack(int argc, const char **argv, const char *prefix);\n>  extern int cmd_init_db(int argc, const char **argv, const char *prefix);\n>  extern int cmd_log(int argc, const char **argv, const char *prefix);\n>  extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);\n> +extern int cmd_ls(int argc, const char **argv, const char *prefix);\n>  extern int cmd_ls_files(int argc, const char **argv, const char *prefix);\n>  extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);\n>  extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);\n> diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> index 20ca3f2..74eb3c2 100644\n> --- a/builtin/ls-files.c\n> +++ b/builtin/ls-files.c\n> @@ -33,6 +33,7 @@ static int use_color;\n>  static unsigned int colopts;\n>  static int max_depth = -1;\n>  static int show_tag;\n> +static int porcelain;\n>\n>  static const char *prefix;\n>  static int max_prefix_len;\n> @@ -588,6 +589,10 @@ static int ls_files(const char **argv, const char *prefix)\n>                         die(\"ls-files --with-tree is incompatible with -s or -u\");\n>                 overlay_tree_on_cache(with_tree, max_prefix);\n>         }\n> +       if (porcelain) {\n> +               refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL);\n> +               setup_pager();\n> +       }\n>         show_files(&dir);\n>         if (show_resolve_undo)\n>                 show_ru_info();\n> @@ -687,3 +692,68 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n>                         ls_files_usage, 0);\n>         return ls_files(argv, prefix);\n>  }\n> +\n> +static const char * const ls_usage[] = {\n> +       N_(\"git ls [options] [<file>...]\"),\n> +       NULL\n> +};\n> +\n> +static int git_ls_config(const char *var, const char *value, void *cb)\n> +{\n> +       if (starts_with(var, \"column.\"))\n> +               return git_column_config(var, value, \"ls\", &colopts);\n> +       if (!strcmp(var, \"color.ls\")) {\n> +               use_color = git_config_colorbool(var, value);\n> +               return 0;\n> +       }\n> +       return git_color_default_config(var, value, cb);\n> +}\n> +\n> +int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n> +{\n> +       struct option builtin_ls_options[] = {\n> +               OPT_BOOL('c', \"cached\", &show_cached,\n> +                       N_(\"show cached files in the output (default)\")),\n\nDitto regarding the unnecessary and repetitive \"in the output\".\n\n> +               OPT_BOOL('d', \"deleted\", &show_deleted,\n> +                       N_(\"show deleted files in the output\")),\n> +               OPT_BOOL('m', \"modified\", &show_modified,\n> +                       N_(\"show modified files in the output\")),\n> +               OPT_BOOL('o', \"others\", &show_others,\n> +                       N_(\"show other files in the output\")),\n> +               OPT_BIT('i', \"ignored\", &dir.flags,\n> +                       N_(\"show ignored files in the output\"),\n> +                       DIR_SHOW_IGNORED),\n> +               OPT_BOOL('u', \"unmerged\", &show_unmerged,\n> +                       N_(\"show unmerged files in the output\")),\n> +               OPT__COLOR(&use_color, N_(\"show color\")),\n> +               OPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n> +               { OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n> +                       N_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n> +                       NULL, 1 },\n> +               OPT__ABBREV(&abbrev),\n> +               OPT_END()\n> +       };\n> +\n> +       if (argc == 2 && !strcmp(argv[1], \"-h\"))\n> +               usage_with_options(ls_usage, builtin_ls_options);\n> +\n> +       prefix = cmd_prefix;\n> +       if (prefix)\n> +               prefix_len = strlen(prefix);\n> +       git_config(git_ls_config, NULL);\n> +\n> +       if (read_cache() < 0)\n> +               die(\"index file corrupt\");\n> +\n> +       /* default setup */\n> +       porcelain = 1;\n> +       setenv(GIT_GLOB_PATHSPECS_ENVIRONMENT, \"1\", 1);\n> +       exc_given = 1;\n> +       setup_standard_excludes(&dir);\n> +       use_color = -1;\n> +       max_depth = 0;\n> +\n> +       argc = parse_options(argc, argv, prefix, builtin_ls_options,\n> +                            ls_usage, 0);\n> +       return ls_files(argv, prefix);\n> +}\n> diff --git a/command-list.txt b/command-list.txt\n> index cf36c3d..89e5cef 100644\n> --- a/command-list.txt\n> +++ b/command-list.txt\n> @@ -64,6 +64,7 @@ git-init                                mainporcelain common\n>  git-instaweb                            ancillaryinterrogators\n>  gitk                                    mainporcelain\n>  git-log                                 mainporcelain common\n> +git-ls                                  mainporcelain\n>  git-ls-files                            plumbinginterrogators\n>  git-ls-remote                           plumbinginterrogators\n>  git-ls-tree                             plumbinginterrogators\n> diff --git a/git.c b/git.c\n> index 9efd1a3..682a81e 100644\n> --- a/git.c\n> +++ b/git.c\n> @@ -381,6 +381,7 @@ static struct cmd_struct commands[] = {\n>         { \"init\", cmd_init_db },\n>         { \"init-db\", cmd_init_db },\n>         { \"log\", cmd_log, RUN_SETUP },\n> +       { \"ls\", cmd_ls, RUN_SETUP },\n>         { \"ls-files\", cmd_ls_files, RUN_SETUP },\n>         { \"ls-remote\", cmd_ls_remote, RUN_SETUP_GENTLY },\n>         { \"ls-tree\", cmd_ls_tree, RUN_SETUP },\n> --\n> 1.9.1.345.ga1a145c\n"},{"id":"237877","messageId":"CACsJy8BjWVJu=_xyey=oBfCgemVBU4p98b+w0PaUQbFk0HG3Hw@mail.gmail.com","threadId":"36247","inReplyTo":"CAPig+cTMRHi2sWQLZzdHZGWT6GBX-3v8exx8B4jpWm0Tgvg6+Q@mail.gmail.com","subject":"Re: [PATCH v2 06/17] ls-files: add --color to highlight file names","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T23:15:15Z","receivedAt":"2014-03-26T23:15:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Mar 27, 2014 at 2:13 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n> On Wed, Mar 26, 2014 at 9:48 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  Documentation/git-ls-files.txt |  9 +++++++++\n>>  builtin/ls-files.c             | 38 +++++++++++++++++++++++++++++++++++---\n>>  2 files changed, 44 insertions(+), 3 deletions(-)\n>>\n>> diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\n>> index c0856a6..5c1b7f3 100644\n>> --- a/Documentation/git-ls-files.txt\n>> +++ b/Documentation/git-ls-files.txt\n>> @@ -147,6 +147,15 @@ a space) at the start of each line:\n>>         possible for manual inspection; the exact format may change at\n>>         any time.\n>>\n>> +--color[=<when>]::\n>> +       Color file names. The value must be always (default), never,\n>> +       or auto.\n>\n> Here, the default is \"always\"...\n\nThese (.txt changes in other patches as well) are mostly copy and\npaste from existing .txt files. You may want to grep through and fix\nother places as well, in a separate series.\n\n>\n>> +--no-color::\n>> +       Turn off coloring, even when the configuration file gives the\n>> +       default to color output, same as `--color=never`. This is the\n>> +       default.\n>\n> But, here the default is \"never\".\n\nWhat I mean is color is turned off by default for ls-files (in\ncontrast, ls has color on by default). The default 'always' means that\nif you write --color without the <when> part, then it's\n--color=always. How do I phrase to make it clear?\n-- \nDuy\n"},{"id":"237878","messageId":"CACsJy8B653BGo8GEBv1VFMZ1ncEeYXFqY5KcR-4thv9hk2+ApA@mail.gmail.com","threadId":"36247","inReplyTo":"CAPig+cRpL9PYKLEH8DocKWo2-ZnqtD6j30TLES1NyOnk52D+Mw@mail.gmail.com","subject":"Re: [PATCH v2 05/17] ls-files: buffer full item in strbuf before printing","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-26T23:18:30Z","receivedAt":"2014-03-26T23:18:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Mar 27, 2014 at 2:22 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n>>  static void show_dir_entry(const char *tag, struct dir_entry *ent)\n>>  {\n>> +       static struct strbuf sb = STRBUF_INIT;\n>>         int len = max_prefix_len;\n>>\n>>         if (len >= ent->len)\n>> @@ -67,8 +79,10 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n>>         if (!dir_path_match(ent, &pathspec, len, ps_matched))\n>>                 return;\n>>\n>> -       fputs(tag, stdout);\n>> -       write_name(ent->name);\n>> +       strbuf_reset(&sb);\n>> +       strbuf_addstr(&sb, tag);\n>> +       write_name(&sb, ent->name);\n>> +       strbuf_fputs(&sb, stdout);\n>\n> strbuf_release(&sb);\n\nNot strictly necessary because sb is static and will be reset at the\nnext call. I just want to lower the number of allocation (write_name\nallocates some more). It may be a premature optimization though.\n\nThe same for changes in show_ce_entry().\n-- \nDuy\n"},{"id":"237881","messageId":"CAPig+cQ5amNNUh4XHp=tJh8BWpTASThPOB-750SzAdUi3Q1Dzw@mail.gmail.com","threadId":"36247","inReplyTo":"CACsJy8B653BGo8GEBv1VFMZ1ncEeYXFqY5KcR-4thv9hk2+ApA@mail.gmail.com","subject":"Re: [PATCH v2 05/17] ls-files: buffer full item in strbuf before printing","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2014-03-27T05:22:07Z","receivedAt":"2014-03-27T05:22:07Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Mar 26, 2014 at 7:18 PM, Duy Nguyen <pclouds@gmail.com> wrote:\n> On Thu, Mar 27, 2014 at 2:22 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n>>>  static void show_dir_entry(const char *tag, struct dir_entry *ent)\n>>>  {\n>>> +       static struct strbuf sb = STRBUF_INIT;\n>>>         int len = max_prefix_len;\n>>>\n>>>         if (len >= ent->len)\n>>> @@ -67,8 +79,10 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n>>>         if (!dir_path_match(ent, &pathspec, len, ps_matched))\n>>>                 return;\n>>>\n>>> -       fputs(tag, stdout);\n>>> -       write_name(ent->name);\n>>> +       strbuf_reset(&sb);\n>>> +       strbuf_addstr(&sb, tag);\n>>> +       write_name(&sb, ent->name);\n>>> +       strbuf_fputs(&sb, stdout);\n>>\n>> strbuf_release(&sb);\n>\n> Not strictly necessary because sb is static and will be reset at the\n> next call. I just want to lower the number of allocation (write_name\n> allocates some more). It may be a premature optimization though.\n\nAh, yes. I noted the 'static' on my initial read-through but had\nforgotten about it by the time I finally got to a computer with which\nI could send plain-text email. Sorry for the noise.\n\n> The same for changes in show_ce_entry().\n> --\n> Duy\n"},{"id":"237904","messageId":"CACsJy8ANjMd+jMmrXfAdNEdggwhmpPt0QsnvUKdsbscorkqwkw@mail.gmail.com","threadId":"36247","inReplyTo":"CACsJy8Awu6oNOS-m5_DNh_HGeHKgcnowUY+iz4efo3s1HYycnQ@mail.gmail.com","subject":"Re: [PATCH 7/8] ls-files: support --max-depth","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-27T14:36:08Z","receivedAt":"2014-03-27T14:36:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Mar 25, 2014 at 6:15 PM, Duy Nguyen <pclouds@gmail.com> wrote:\n> On Tue, Mar 25, 2014 at 3:55 PM, Matthieu Moy\n> <matthieu.moy@grenoble-inp.fr> wrote:\n>> ----- Original Message -----\n>>> The use case in mind is --max-depth=0 to stop recursion. With this we can do\n>>>\n>>> git config --global alias.ls 'ls-files --column --color --max-depth=0'\n>>>\n>>> and have \"git ls\" with an output very similar to GNU ls.\n>>\n>> One big difference though: your \"git ls\" does not show directories. I understand that this is easier to implement, but from the user point of view it resulted in a \"wtf\" from me running \"git ls\" in a repository containing essentially directories, and seeing just a README file in the output.\n>\n> I was hoping you didn't notice :) It'll be more difficult but not impossible.\n>\n>> Ideally (for me), directories should be shown with a trailing / like \"ls -F\" does.\n>\n> I'd rather go with no trailing slash by default and add -F (which\n> seems to be more than just '/')\n\nAnd we need a new indicator for submodules when -F is used. I think it\nshould be different than '/'. I randomly picked '&' for now. Any\nsuggestions welcome.\n-- \nDuy\n"},{"id":"237964","messageId":"CAPig+cQ_mXQwYyy8naiSxH8=P5Mj2eqJ7jfQsAS9O2--5VyaJA@mail.gmail.com","threadId":"36247","inReplyTo":"CACsJy8BjWVJu=_xyey=oBfCgemVBU4p98b+w0PaUQbFk0HG3Hw@mail.gmail.com","subject":"Re: [PATCH v2 06/17] ls-files: add --color to highlight file names","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2014-03-28T00:49:55Z","receivedAt":"2014-03-28T00:49:55Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Mar 26, 2014 at 7:15 PM, Duy Nguyen <pclouds@gmail.com> wrote:\n> On Thu, Mar 27, 2014 at 2:13 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n>> On Wed, Mar 26, 2014 at 9:48 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>>> ---\n>>>  Documentation/git-ls-files.txt |  9 +++++++++\n>>>  builtin/ls-files.c             | 38 +++++++++++++++++++++++++++++++++++---\n>>>  2 files changed, 44 insertions(+), 3 deletions(-)\n>>>\n>>> diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\n>>> index c0856a6..5c1b7f3 100644\n>>> --- a/Documentation/git-ls-files.txt\n>>> +++ b/Documentation/git-ls-files.txt\n>>> @@ -147,6 +147,15 @@ a space) at the start of each line:\n>>>         possible for manual inspection; the exact format may change at\n>>>         any time.\n>>>\n>>> +--color[=<when>]::\n>>> +       Color file names. The value must be always (default), never,\n>>> +       or auto.\n>>\n>> Here, the default is \"always\"...\n>\n> These (.txt changes in other patches as well) are mostly copy and\n> paste from existing .txt files. You may want to grep through and fix\n> other places as well, in a separate series.\n>\n>>\n>>> +--no-color::\n>>> +       Turn off coloring, even when the configuration file gives the\n>>> +       default to color output, same as `--color=never`. This is the\n>>> +       default.\n>>\n>> But, here the default is \"never\".\n>\n> What I mean is color is turned off by default for ls-files (in\n> contrast, ls has color on by default). The default 'always' means that\n> if you write --color without the <when> part, then it's\n> --color=always. How do I phrase to make it clear?\n\nPerhaps:\n\n    Color file names. The value must be always, never, or auto.\n    `--color` by itself is the same as `--color=always`.\n\n> --\n> Duy\n"},{"id":"237978","messageId":"CAPig+cRay7cRJ=zMSpMNFvDVHUR2q+PgoAimSuM67_Ps1t0Yig@mail.gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-14-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 13/17] ls: add -1 short for --no-column in the spirit of GNU ls","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2014-03-28T03:52:54Z","receivedAt":"2014-03-28T03:52:54Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Mar 26, 2014 at 9:48 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Subject: ls: add -1 short for --no-column in the spirit of GNU ls\n\nThe -1 option is POSIX [1]; not a GNU extension.\n\n[1]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  Documentation/git-ls.txt | 3 +++\n>  builtin/ls-files.c       | 2 ++\n>  2 files changed, 5 insertions(+)\n>\n> diff --git a/Documentation/git-ls.txt b/Documentation/git-ls.txt\n> index 10df6b0..0480c42 100644\n> --- a/Documentation/git-ls.txt\n> +++ b/Documentation/git-ls.txt\n> @@ -51,6 +51,9 @@ OPTIONS\n>  --recursive::\n>         Equivalent of --max-depth=-1 (infinite recursion).\n>\n> +-1::\n> +       Equivalent of --no-column.\n> +\n>  --color[=<when>]::\n>         Color file names. The value must be always (default), never,\n>         or auto.\n> diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> index 772a6ce..014de05 100644\n> --- a/builtin/ls-files.c\n> +++ b/builtin/ls-files.c\n> @@ -729,6 +729,8 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)\n>                             N_(\"shortcut for --max-depth=-1\"), -1),\n>                 OPT__COLOR(&use_color, N_(\"show color\")),\n>                 OPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n> +               OPT_SET_INT('1', NULL, &colopts,\n> +                           N_(\"shortcut for --no-column\"), COL_PARSEOPT),\n>                 { OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n>                         N_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n>                         NULL, 1 },\n> --\n> 1.9.1.345.ga1a145c\n"},{"id":"237981","messageId":"CAPig+cRurqCHyFtpCFOisc=1u06JSpmE9rHQa0ioLxrQMuJ4Dw@mail.gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-17-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 16/17] ls: do not show duplicate cached entries","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2014-03-28T04:04:35Z","receivedAt":"2014-03-28T04:04:35Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Mar 26, 2014 at 9:48 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> With the current show_files() \"ls -tcm\" will show\n>\n>   foo.c\n> M foo.c\n>\n> The first item is redundant. If \"foo.c\" is modified, we know it's in\n> the cache. Introduce show_files_compact to do that because ls-files is\n> plumbing and scripts may already depend on current display behavior.\n>\n> Another difference in show_files_compact() is it does not show\n> skip-worktree (aka outside sparse checkout) entries anymore, which\n> makes sense in porcelain context.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  builtin/ls-files.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++-\n>  1 file changed, 51 insertions(+), 1 deletion(-)\n>\n> diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> index 709d8b1..cd8e35c 100644\n> --- a/builtin/ls-files.c\n> +++ b/builtin/ls-files.c\n> @@ -337,6 +337,53 @@ static void show_files(struct dir_struct *dir)\n>         }\n>  }\n>\n> +static void show_files_compact(struct dir_struct *dir)\n> +{\n> +       int i;\n> +\n> +       /* For cached/deleted files we don't need to even do the readdir */\n> +       if (show_others || show_killed) {\n> +               if (!show_others)\n> +                       dir->flags |= DIR_COLLECT_KILLED_ONLY;\n> +               fill_directory(dir, &pathspec);\n> +               if (show_others)\n> +                       show_other_files(dir);\n> +               if (show_killed)\n> +                       show_killed_files(dir);\n> +       }\n> +       if (!(show_cached || show_stage || show_deleted || show_modified))\n> +               return;\n> +       for (i = 0; i < active_nr; i++) {\n> +               const struct cache_entry *ce = active_cache[i];\n> +               struct stat st;\n> +               int err, shown = 0;\n> +               if ((dir->flags & DIR_SHOW_IGNORED) &&\n> +                   !ce_excluded(dir, ce))\n> +                       continue;\n> +               if (show_unmerged && !ce_stage(ce))\n> +                       continue;\n> +               if (ce->ce_flags & CE_UPDATE)\n> +                       continue;\n> +               if (ce_skip_worktree(ce))\n> +                       continue;\n> +               err = lstat(ce->name, &st);\n> +               if (show_deleted && err) {\n> +                       show_ce_entry(tag_removed, ce);\n> +                       shown = 1;\n> +               }\n> +               if (show_modified && ce_modified(ce, &st, 0)) {\n\nIs it possible for the lstat() to have failed for some reason when we\nget here? If so, relying upon 'st' is unsafe, isn't it?\n\n> +                       show_ce_entry(tag_modified, ce);\n> +                       shown = 1;\n> +               }\n> +               if (ce_stage(ce)) {\n> +                       show_ce_entry(tag_unmerged, ce);\n> +                       shown = 1;\n> +               }\n> +               if (!shown && show_cached)\n> +                       show_ce_entry(tag_cached, ce);\n> +       }\n> +}\n> +\n>  /*\n>   * Prune the index to only contain stuff starting with \"prefix\"\n>   */\n> @@ -606,7 +653,10 @@ static int ls_files(const char **argv, const char *prefix)\n>                 refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL);\n>                 setup_pager();\n>         }\n> -       show_files(&dir);\n> +       if (porcelain)\n> +               show_files_compact(&dir);\n> +       else\n> +               show_files(&dir);\n>         if (show_resolve_undo)\n>                 show_ru_info();\n>\n> --\n> 1.9.1.345.ga1a145c\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n"},{"id":"238005","messageId":"1396012689-22480-1-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"CAPig+cRurqCHyFtpCFOisc=1u06JSpmE9rHQa0ioLxrQMuJ4Dw@mail.gmail.com","subject":"[PATCH] ls-files: do not trust stat info if lstat() fails","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-28T13:18:09Z","receivedAt":"2014-03-28T13:18:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"If 'err' is non-zero, lstat() has failed. Consider the entry modified\nwithout passing the (unreliable) stat info to ce_modified() in this\ncase.\n\nNoticed-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n On Fri, Mar 28, 2014 at 11:04 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n > On Wed, Mar 26, 2014 at 9:48 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n >> +               err = lstat(ce->name, &st);\n >> +               if (show_deleted && err) {\n >> +                       show_ce_entry(tag_removed, ce);\n >> +                       shown = 1;\n >> +               }\n >> +               if (show_modified && ce_modified(ce, &st, 0)) {\n >\n > Is it possible for the lstat() to have failed for some reason when we\n > get here? If so, relying upon 'st' is unsafe, isn't it?\n\n The chance of random stat making ce_modified() return false is pretty\n low, but you're right. This code is a copy from the old show_files().\n I'll fix it in the git-ls series. Meanwhile a patch for maint to fix\n the original function.\n\n builtin/ls-files.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 47c3880..e6bd00e 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -260,7 +260,7 @@ static void show_files(struct dir_struct *dir)\n \t\t\terr = lstat(ce->name, &st);\n \t\t\tif (show_deleted && err)\n \t\t\t\tshow_ce_entry(tag_removed, ce);\n-\t\t\tif (show_modified && ce_modified(ce, &st, 0))\n+\t\t\tif (show_modified && (err || ce_modified(ce, &st, 0)))\n \t\t\t\tshow_ce_entry(tag_modified, ce);\n \t\t}\n \t}\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238006","messageId":"vpqtxaiqvl9.fsf@anie.imag.fr","threadId":"36247","inReplyTo":"CACsJy8Awu6oNOS-m5_DNh_HGeHKgcnowUY+iz4efo3s1HYycnQ@mail.gmail.com","subject":"Re: [PATCH 7/8] ls-files: support --max-depth","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2014-03-28T13:52:18Z","receivedAt":"2014-03-28T13:52:18Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> I'd rather go with no trailing slash by default and add -F (which\n> seems to be more than just '/')\n\n... and then add a configuration variable to let users enable it by\ndefault.\n\nFor GNU ls, I have \"alias ls='ls -F --color=auto'\" in my shell's\nconfiguration, but I cannot push the analogy by aliasing \"git ls\"\nbecause Git doesn't allow aliasing existing commands.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"238007","messageId":"CACsJy8DtW_uUf+G4krc5=EPATNRST3aG9o-bhE34Yb7pw_dXSQ@mail.gmail.com","threadId":"36247","inReplyTo":"vpqtxaiqvl9.fsf@anie.imag.fr","subject":"Re: [PATCH 7/8] ls-files: support --max-depth","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-28T14:15:21Z","receivedAt":"2014-03-28T14:15:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Mar 28, 2014 at 8:52 PM, Matthieu Moy\n<Matthieu.Moy@grenoble-inp.fr> wrote:\n> Duy Nguyen <pclouds@gmail.com> writes:\n>\n>> I'd rather go with no trailing slash by default and add -F (which\n>> seems to be more than just '/')\n>\n> ... and then add a configuration variable to let users enable it by\n> default.\n>\n> For GNU ls, I have \"alias ls='ls -F --color=auto'\" in my shell's\n> configuration, but I cannot push the analogy by aliasing \"git ls\"\n> because Git doesn't allow aliasing existing commands.\n\nI can do that but I want to push for a general solution instead\nof ls-only. How about config key defaults.<cmd>, containing a list of\narguments, that will be prepended to git-<cmd>? Only some commands are\nmarked to support this by adding USE_DEFAULTS in the array commands[]\nin git.c. And \"git --no-defaults <cmd>\" will ignore defaults.<cmd> (or\n\"git -c defaults.<cmd>= <cmd>\" but it's less obvious). GIT_NO_DEFAULTS\ncan also be set, which has the same effect for all commands.\n-- \nDuy\n"},{"id":"238008","messageId":"CACsJy8BR8RoNAayS-rXgCz_=aHrxaoYBJH30jCSCC3ThL1ztjA@mail.gmail.com","threadId":"36247","inReplyTo":"CACsJy8DtW_uUf+G4krc5=EPATNRST3aG9o-bhE34Yb7pw_dXSQ@mail.gmail.com","subject":"Re: [PATCH 7/8] ls-files: support --max-depth","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-28T14:38:12Z","receivedAt":"2014-03-28T14:38:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Mar 28, 2014 at 9:15 PM, Duy Nguyen <pclouds@gmail.com> wrote:\n> On Fri, Mar 28, 2014 at 8:52 PM, Matthieu Moy\n> <Matthieu.Moy@grenoble-inp.fr> wrote:\n>> Duy Nguyen <pclouds@gmail.com> writes:\n>>\n>>> I'd rather go with no trailing slash by default and add -F (which\n>>> seems to be more than just '/')\n>>\n>> ... and then add a configuration variable to let users enable it by\n>> default.\n>>\n>> For GNU ls, I have \"alias ls='ls -F --color=auto'\" in my shell's\n>> configuration, but I cannot push the analogy by aliasing \"git ls\"\n>> because Git doesn't allow aliasing existing commands.\n>\n> I can do that but I want to push for a general solution instead\n> of ls-only. How about config key defaults.<cmd>, containing a list of\n> arguments, that will be prepended to git-<cmd>? Only some commands are\n> marked to support this by adding USE_DEFAULTS in the array commands[]\n> in git.c. And \"git --no-defaults <cmd>\" will ignore defaults.<cmd> (or\n> \"git -c defaults.<cmd>= <cmd>\" but it's less obvious). GIT_NO_DEFAULTS\n> can also be set, which has the same effect for all commands.\n\nAnother option is to make git recognize program name g<something> and\nauto map it to the alias <something>. For example, the symlink \"gls\"\nwill be executed as \"git ls\" (and the alias version is preferred over\nthe builtin one). Of course you can't have alias \"it\" because \"git\" is\nalready taken. It works for many cases, it's faster to type, and it\ndoes not break current scripts.\n-- \nDuy\n"},{"id":"238082","messageId":"1396187769-30863-1-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1395841697-11742-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 00/18] git-ls","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:55:51Z","receivedAt":"2014-03-30T13:55:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"All comments by Eric are fixed in v3. -F is added. And the command\nname is now list-files, not ls. 'ls' is saved for the user to make\nan alias with better default options.\n\nNguyễn Thái Ngọc Duy (18):\n  ls_colors.c: add $LS_COLORS parsing code\n  ls_colors.c: parse color.ls.* from config file\n  ls_colors.c: add a function to color a file name\n  ls_colors.c: highlight submodules like directories\n  ls-files: buffer full item in strbuf before printing\n  ls-files: add --color to highlight file names\n  ls-files: add --column\n  ls-files: support --max-depth\n  Add git-list-files, a user friendly version of ls-files and more\n  list-files: -u does not imply showing stages\n  list-files: add -R/--recursive short for --max-depth=-1\n  list-files: add -1 short for --no-column\n  list-files: add -t back\n  list-files: sort output and remove duplicates\n  list-files: do not show duplicate cached entries\n  list-files: show directories as well as files\n  list-files: add -F/--classify\n  list-files -F: show submodules with the new indicator '&'\n\n .gitignore                             |   1 +\n Documentation/config.txt               |  22 ++\n Documentation/git-list-files.txt (new) |  99 +++++++\n Documentation/git-ls-files.txt         |  20 ++\n Makefile                               |   2 +\n builtin/ls-files.c                     | 358 ++++++++++++++++++++++--\n color.h                                |  10 +\n command-list.txt                       |   1 +\n git.c                                  |   1 +\n ls_colors.c (new)                      | 496 +++++++++++++++++++++++++++++++++\n 10 files changed, 980 insertions(+), 30 deletions(-)\n create mode 100644 Documentation/git-list-files.txt\n create mode 100644 ls_colors.c\n\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238091","messageId":"1396187769-30863-2-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 01/18] ls_colors.c: add $LS_COLORS parsing code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:55:52Z","receivedAt":"2014-03-30T13:55:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Reusing color settings from $LS_COLORS could give a native look and\nfeel on file coloring.\n\nThis code is basically from coreutils.git [1], rewritten to fit Git.\n\nAs this is from GNU ls, the environment variable CLICOLOR is not\ntested. It is to be decided later whether we should ignore $LS_COLORS\nif $CLICOLOR is not set on Mac or FreeBSD.\n\n[1] commit 7326d1f1a67edf21947ae98194f98c38b6e9e527 file\n    src/ls.c. This is the last GPL-2 commit before coreutils turns to\n    GPL-3.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Makefile          |   1 +\n color.h           |   8 ++\n ls_colors.c (new) | 398 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 407 insertions(+)\n create mode 100644 ls_colors.c\n\ndiff --git a/Makefile b/Makefile\nindex f818eec..f6a6e14 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -819,6 +819,7 @@ LIB_OBJS += list-objects.o\n LIB_OBJS += ll-merge.o\n LIB_OBJS += lockfile.o\n LIB_OBJS += log-tree.o\n+LIB_OBJS += ls_colors.o\n LIB_OBJS += mailmap.o\n LIB_OBJS += match-trees.o\n LIB_OBJS += merge.o\ndiff --git a/color.h b/color.h\nindex 9a8495b..640fc48 100644\n--- a/color.h\n+++ b/color.h\n@@ -45,6 +45,12 @@ struct strbuf;\n #define GIT_COLOR_BG_MAGENTA\t\"\\033[45m\"\n #define GIT_COLOR_BG_CYAN\t\"\\033[46m\"\n \n+#define GIT_COLOR_WHITE_ON_RED    \"\\033[37;41m\"\n+#define GIT_COLOR_WHITE_ON_BLUE   \"\\033[37;44m\"\n+#define GIT_COLOR_BLACK_ON_YELLOW \"\\033[30;43m\"\n+#define GIT_COLOR_BLUE_ON_GREEN   \"\\033[34;42m\"\n+#define GIT_COLOR_BLACK_ON_GREEN  \"\\033[30;42m\"\n+\n /* A special value meaning \"no color selected\" */\n #define GIT_COLOR_NIL \"NIL\"\n \n@@ -87,4 +93,6 @@ void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb);\n \n int color_is_nil(const char *color);\n \n+void parse_ls_color(void);\n+\n #endif /* COLOR_H */\ndiff --git a/ls_colors.c b/ls_colors.c\nnew file mode 100644\nindex 0000000..eb944f4\n--- /dev/null\n+++ b/ls_colors.c\n@@ -0,0 +1,398 @@\n+#include \"cache.h\"\n+#include \"color.h\"\n+\n+enum color_ls {\n+\tLS_LC,\t\t\t/* left, unused */\n+\tLS_RC,\t\t\t/* right, unused */\n+\tLS_EC,\t\t\t/* end color, unused */\n+\tLS_RS,\t\t\t/* reset */\n+\tLS_NO,\t\t\t/* normal */\n+\tLS_FL,\t\t\t/* file, default */\n+\tLS_DI,\t\t\t/* directory */\n+\tLS_LN,\t\t\t/* symlink */\n+\n+\tLS_PI,\t\t\t/* pipe */\n+\tLS_SO,\t\t\t/* socket */\n+\tLS_BD,\t\t\t/* block device */\n+\tLS_CD,\t\t\t/* char device */\n+\tLS_MI,\t\t\t/* missing file */\n+\tLS_OR,\t\t\t/* orphaned symlink */\n+\tLS_EX,\t\t\t/* executable */\n+\tLS_DO,\t\t\t/* Solaris door */\n+\n+\tLS_SU,\t\t\t/* setuid */\n+\tLS_SG,\t\t\t/* setgid */\n+\tLS_ST,\t\t\t/* sticky */\n+\tLS_OW,\t\t\t/* other-writable */\n+\tLS_TW,\t\t\t/* ow with sticky */\n+\tLS_CA,\t\t\t/* cap */\n+\tLS_MH,\t\t\t/* multi hardlink */\n+\tLS_CL,\t\t\t/* clear end of line */\n+\n+\tMAX_LS\n+};\n+\n+static char ls_colors[MAX_LS][COLOR_MAXLEN] = {\n+\t\"\",\n+\t\"\",\n+\t\"\",\n+\tGIT_COLOR_RESET,\n+\tGIT_COLOR_NORMAL,\n+\tGIT_COLOR_NORMAL,\n+\tGIT_COLOR_BOLD_BLUE,\n+\tGIT_COLOR_BOLD_CYAN,\n+\n+\tGIT_COLOR_YELLOW,\n+\tGIT_COLOR_BOLD_MAGENTA,\n+\tGIT_COLOR_BOLD_YELLOW,\n+\tGIT_COLOR_BOLD_YELLOW,\n+\tGIT_COLOR_NORMAL,\n+\tGIT_COLOR_NORMAL,\n+\tGIT_COLOR_BOLD_GREEN,\n+\tGIT_COLOR_BOLD_MAGENTA,\n+\n+\tGIT_COLOR_WHITE_ON_RED,\n+\tGIT_COLOR_BLACK_ON_YELLOW,\n+\tGIT_COLOR_WHITE_ON_BLUE,\n+\tGIT_COLOR_BLUE_ON_GREEN,\n+\tGIT_COLOR_BLACK_ON_GREEN,\n+\t\"\",\n+\t\"\",\n+\t\"\"\n+};\n+\n+static const char *const indicator_name[] = {\n+\t\"lc\", \"rc\", \"ec\", \"rs\", \"no\", \"fi\", \"di\", \"ln\",\n+\t\"pi\", \"so\", \"bd\", \"cd\", \"mi\", \"or\", \"ex\", \"do\",\n+\t\"su\", \"sg\", \"st\", \"ow\", \"tw\", \"ca\", \"mh\", \"cl\",\n+\tNULL\n+};\n+\n+struct bin_str {\n+\tsize_t len;\t\t\t/* Number of bytes */\n+\tconst char *string;\t\t/* Pointer to the same */\n+};\n+\n+struct color_ext_type {\n+\tstruct bin_str ext;\t\t/* The extension we're looking for */\n+\tstruct bin_str seq;\t\t/* The sequence to output when we do */\n+\tstruct color_ext_type *next;\t/* Next in list */\n+};\n+\n+static struct color_ext_type *color_ext_list = NULL;\n+\n+/*\n+ * When true, in a color listing, color each symlink name according to the\n+ * type of file it points to.  Otherwise, color them according to the `ln'\n+ * directive in LS_COLORS.  Dangling (orphan) symlinks are treated specially,\n+ * regardless.  This is set when `ln=target' appears in LS_COLORS.\n+ */\n+static int color_symlink_as_referent;\n+\n+/*\n+ * Parse a string as part of the LS_COLORS variable; this may involve\n+ * decoding all kinds of escape characters.  If equals_end is set an\n+ * unescaped equal sign ends the string, otherwise only a : or \\0\n+ * does.  Set *OUTPUT_COUNT to the number of bytes output.  Return\n+ * true if successful.\n+ *\n+ * The resulting string is *not* null-terminated, but may contain\n+ * embedded nulls.\n+ *\n+ * Note that both dest and src are char **; on return they point to\n+ * the first free byte after the array and the character that ended\n+ * the input string, respectively.\n+ */\n+static int get_funky_string(char **dest, const char **src, int equals_end,\n+\t\t\t    size_t *output_count)\n+{\n+\tchar num;\t\t\t/* For numerical codes */\n+\tsize_t count;\t\t\t/* Something to count with */\n+\tenum {\n+\t\tST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX,\n+\t\tST_CARET, ST_END, ST_ERROR\n+\t} state;\n+\tconst char *p;\n+\tchar *q;\n+\n+\tp = *src;\t\t\t/* We don't want to double-indirect */\n+\tq = *dest;\t\t\t/* the whole darn time.  */\n+\n+\tcount = 0;\t\t\t/* No characters counted in yet.  */\n+\tnum = 0;\n+\n+\tstate = ST_GND;\t\t/* Start in ground state.  */\n+\twhile (state < ST_END) {\n+\t\tswitch (state) {\n+\t\tcase ST_GND:\t\t/* Ground state (no escapes) */\n+\t\t\tswitch (*p) {\n+\t\t\tcase ':':\n+\t\t\tcase '\\0':\n+\t\t\t\tstate = ST_END;\t/* End of string */\n+\t\t\t\tbreak;\n+\t\t\tcase '\\\\':\n+\t\t\t\tstate = ST_BACKSLASH; /* Backslash scape sequence */\n+\t\t\t\t++p;\n+\t\t\t\tbreak;\n+\t\t\tcase '^':\n+\t\t\t\tstate = ST_CARET; /* Caret escape */\n+\t\t\t\t++p;\n+\t\t\t\tbreak;\n+\t\t\tcase '=':\n+\t\t\t\tif (equals_end) {\n+\t\t\t\t\tstate = ST_END; /* End */\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\t/* else fall through */\n+\t\t\tdefault:\n+\t\t\t\t*(q++) = *(p++);\n+\t\t\t\t++count;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase ST_BACKSLASH:\t/* Backslash escaped character */\n+\t\t\tswitch (*p) {\n+\t\t\tcase '0':\n+\t\t\tcase '1':\n+\t\t\tcase '2':\n+\t\t\tcase '3':\n+\t\t\tcase '4':\n+\t\t\tcase '5':\n+\t\t\tcase '6':\n+\t\t\tcase '7':\n+\t\t\t\tstate = ST_OCTAL;\t/* Octal sequence */\n+\t\t\t\tnum = *p - '0';\n+\t\t\t\tbreak;\n+\t\t\tcase 'x':\n+\t\t\tcase 'X':\n+\t\t\t\tstate = ST_HEX;\t/* Hex sequence */\n+\t\t\t\tnum = 0;\n+\t\t\t\tbreak;\n+\t\t\tcase 'a':\t\t/* Bell */\n+\t\t\t\tnum = '\\a';\n+\t\t\t\tbreak;\n+\t\t\tcase 'b':\t\t/* Backspace */\n+\t\t\t\tnum = '\\b';\n+\t\t\t\tbreak;\n+\t\t\tcase 'e':\t\t/* Escape */\n+\t\t\t\tnum = 27;\n+\t\t\t\tbreak;\n+\t\t\tcase 'f':\t\t/* Form feed */\n+\t\t\t\tnum = '\\f';\n+\t\t\t\tbreak;\n+\t\t\tcase 'n':\t\t/* Newline */\n+\t\t\t\tnum = '\\n';\n+\t\t\t\tbreak;\n+\t\t\tcase 'r':\t\t/* Carriage return */\n+\t\t\t\tnum = '\\r';\n+\t\t\t\tbreak;\n+\t\t\tcase 't':\t\t/* Tab */\n+\t\t\t\tnum = '\\t';\n+\t\t\t\tbreak;\n+\t\t\tcase 'v':\t\t/* Vtab */\n+\t\t\t\tnum = '\\v';\n+\t\t\t\tbreak;\n+\t\t\tcase '?':\t\t/* Delete */\n+\t\t\t\tnum = 127;\n+\t\t\t\tbreak;\n+\t\t\tcase '_':\t\t/* Space */\n+\t\t\t\tnum = ' ';\n+\t\t\t\tbreak;\n+\t\t\tcase '\\0':\t\t/* End of string */\n+\t\t\t\tstate = ST_ERROR;\t/* Error! */\n+\t\t\t\tbreak;\n+\t\t\tdefault:\t\t/* Escaped character like \\ ^ : = */\n+\t\t\t\tnum = *p;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tif (state == ST_BACKSLASH) {\n+\t\t\t\t*(q++) = num;\n+\t\t\t\t++count;\n+\t\t\t\tstate = ST_GND;\n+\t\t\t}\n+\t\t\t++p;\n+\t\t\tbreak;\n+\n+\t\tcase ST_OCTAL:\t\t/* Octal sequence */\n+\t\t\tif (*p < '0' || *p > '7') {\n+\t\t\t\t*(q++) = num;\n+\t\t\t\t++count;\n+\t\t\t\tstate = ST_GND;\n+\t\t\t} else\n+\t\t\t\tnum = (num << 3) + (*(p++) - '0');\n+\t\t\tbreak;\n+\n+\t\tcase ST_HEX:\t\t/* Hex sequence */\n+\t\t\tswitch (*p) {\n+\t\t\tcase '0':\n+\t\t\tcase '1':\n+\t\t\tcase '2':\n+\t\t\tcase '3':\n+\t\t\tcase '4':\n+\t\t\tcase '5':\n+\t\t\tcase '6':\n+\t\t\tcase '7':\n+\t\t\tcase '8':\n+\t\t\tcase '9':\n+\t\t\t\tnum = (num << 4) + (*(p++) - '0');\n+\t\t\t\tbreak;\n+\t\t\tcase 'a':\n+\t\t\tcase 'b':\n+\t\t\tcase 'c':\n+\t\t\tcase 'd':\n+\t\t\tcase 'e':\n+\t\t\tcase 'f':\n+\t\t\t\tnum = (num << 4) + (*(p++) - 'a') + 10;\n+\t\t\t\tbreak;\n+\t\t\tcase 'A':\n+\t\t\tcase 'B':\n+\t\t\tcase 'C':\n+\t\t\tcase 'D':\n+\t\t\tcase 'E':\n+\t\t\tcase 'F':\n+\t\t\t\tnum = (num << 4) + (*(p++) - 'A') + 10;\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\t*(q++) = num;\n+\t\t\t\t++count;\n+\t\t\t\tstate = ST_GND;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase ST_CARET:\t\t/* Caret escape */\n+\t\t\tstate = ST_GND;\t/* Should be the next state... */\n+\t\t\tif (*p >= '@' && *p <= '~') {\n+\t\t\t\t*(q++) = *(p++) & 037;\n+\t\t\t\t++count;\n+\t\t\t} else if (*p == '?') {\n+\t\t\t\t*(q++) = 127;\n+\t\t\t\t++count;\n+\t\t\t} else\n+\t\t\t\tstate = ST_ERROR;\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tabort();\n+\t\t}\n+\t}\n+\n+\t*dest = q;\n+\t*src = p;\n+\t*output_count = count;\n+\n+\treturn state != ST_ERROR;\n+}\n+\n+void parse_ls_color(void)\n+{\n+\tconst char *p;\t\t\t/* Pointer to character being parsed */\n+\tchar *buf;\t\t\t/* color_buf buffer pointer */\n+\tint state;\t\t\t/* State of parser */\n+\tint ind_no;\t\t\t/* Indicator number */\n+\tchar label[3];\t\t\t/* Indicator label */\n+\tstruct color_ext_type *ext;\t/* Extension we are working on */\n+\tstatic char *color_buf;\n+\tchar *start;\n+\tsize_t len;\n+\n+\tif ((p = getenv(\"LS_COLORS\")) == NULL || *p == '\\0')\n+\t\treturn;\n+\n+\text = NULL;\n+\tstrcpy (label, \"??\");\n+\n+\t/*\n+\t * This is an overly conservative estimate, but any possible\n+\t * LS_COLORS string will *not* generate a color_buf longer\n+\t * than itself, so it is a safe way of allocating a buffer in\n+\t * advance.\n+\t */\n+\tbuf = color_buf = xstrdup(p);\n+\n+\tstate = 1;\n+\twhile (state > 0) {\n+\t\tswitch (state) {\n+\t\tcase 1:\t\t/* First label character */\n+\t\t\tswitch (*p) {\n+\t\t\tcase ':':\n+\t\t\t\t++p;\n+\t\t\t\tbreak;\n+\n+\t\t\tcase '*':\n+\t\t\t\t/*\n+\t\t\t\t * Allocate new extension block and add to head of\n+\t\t\t\t * linked list (this way a later definition will\n+\t\t\t\t * override an earlier one, which can be useful for\n+\t\t\t\t * having terminal-specific defs override global).\n+\t\t\t\t */\n+\n+\t\t\t\text = xmalloc(sizeof *ext);\n+\t\t\t\text->next = color_ext_list;\n+\t\t\t\tcolor_ext_list = ext;\n+\n+\t\t\t\t++p;\n+\t\t\t\text->ext.string = buf;\n+\n+\t\t\t\tstate = (get_funky_string(&buf, &p, 1, &ext->ext.len)\n+\t\t\t\t\t ? 4 : -1);\n+\t\t\t\tbreak;\n+\n+\t\t\tcase '\\0':\n+\t\t\t\tstate = 0;\t/* Done! */\n+\t\t\t\tbreak;\n+\n+\t\t\tdefault:\t/* Assume it is file type label */\n+\t\t\t\tlabel[0] = *(p++);\n+\t\t\t\tstate = 2;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase 2:\t\t/* Second label character */\n+\t\t\tif (*p) {\n+\t\t\t\tlabel[1] = *(p++);\n+\t\t\t\tstate = 3;\n+\t\t\t} else\n+\t\t\t\tstate = -1;\t/* Error */\n+\t\t\tbreak;\n+\n+\t\tcase 3:\t\t/* Equal sign after indicator label */\n+\t\t\tstate = -1;\t/* Assume failure...  */\n+\t\t\tif (*(p++) != '=')\n+\t\t\t\tbreak;\n+\t\t\tfor (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no) {\n+\t\t\t\tif (!strcmp(label, indicator_name[ind_no])) {\n+\t\t\t\t\tstart = buf;\n+\t\t\t\t\tif (get_funky_string(&buf, &p, 0, &len))\n+\t\t\t\t\t\tstate = 1;\n+\t\t\t\t\telse\n+\t\t\t\t\t\tstate = -1;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (state == -1)\n+\t\t\t\terror(_(\"unrecognized prefix: %s\"), label);\n+\t\t\telse if (ind_no == LS_LN && len == 6 &&\n+\t\t\t\t starts_with(start, \"target\"))\n+\t\t\t\tcolor_symlink_as_referent = 1;\n+\t\t\telse\n+\t\t\t\tsprintf(ls_colors[ind_no], \"\\033[%.*sm\",\n+\t\t\t\t       (int)len, start);\n+\t\t\tbreak;\n+\n+\t\tcase 4:\t\t/* Equal sign after *.ext */\n+\t\t\tif (*(p++) == '=') {\n+\t\t\t\text->seq.string = buf;\n+\t\t\t\tstate = (get_funky_string(&buf, &p, 0, &ext->seq.len)\n+\t\t\t\t\t ? 1 : -1);\n+\t\t\t} else\n+\t\t\t\tstate = -1;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (!strcmp(ls_colors[LS_LN], \"target\"))\n+\t\tcolor_symlink_as_referent = 1;\n+}\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238083","messageId":"1396187769-30863-3-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 02/18] ls_colors.c: parse color.ls.* from config file","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:55:53Z","receivedAt":"2014-03-30T13:55:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is the second (and preferred) source for color information. This\nwill override $LS_COLORS.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt | 11 +++++++++++\n ls_colors.c              | 26 ++++++++++++++++++++++++++\n 2 files changed, 37 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 73c8973..3fb754e 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -909,6 +909,17 @@ color.status.<slot>::\n \tto red). The values of these variables may be specified as in\n \tcolor.branch.<slot>.\n \n+color.ls.<slot>::\n+\tUse customized color for file name colorization. If not set\n+\tand the environment variable LS_COLORS is set, color settings\n+\tfrom $LS_COLORS are used. `<slot>` can be `normal`, `file`,\n+\t`directory`, `symlink`, `fifo`, `socket`, `block`, `char`,\n+\t`missing`, `orphan`, `executable`, `door`, `setuid`, `setgid`,\n+\t`sticky`, `otherwritable`, `stickyotherwritable`, `cap`,\n+\t`multihardlink`. The values of these variables may be\n+\tspecified as in color.branch.<slot>.\n+\n+\n color.ui::\n \tThis variable determines the default value for variables such\n \tas `color.diff` and `color.grep` that control the use of color\ndiff --git a/ls_colors.c b/ls_colors.c\nindex eb944f4..cef5a92 100644\n--- a/ls_colors.c\n+++ b/ls_colors.c\n@@ -68,6 +68,14 @@ static const char *const indicator_name[] = {\n \tNULL\n };\n \n+static const char* const config_name[] = {\n+\t\"\", \"\", \"\", \"\", \"normal\", \"file\", \"directory\", \"symlink\",\n+\t\"fifo\", \"socket\", \"block\", \"char\", \"missing\", \"orphan\", \"executable\",\n+\t\"door\", \"setuid\", \"setgid\", \"sticky\", \"otherwritable\",\n+\t\"stickyotherwritable\", \"cap\", \"multihardlink\", \"\",\n+\tNULL\n+};\n+\n struct bin_str {\n \tsize_t len;\t\t\t/* Number of bytes */\n \tconst char *string;\t\t/* Pointer to the same */\n@@ -285,6 +293,23 @@ static int get_funky_string(char **dest, const char **src, int equals_end,\n \treturn state != ST_ERROR;\n }\n \n+static int ls_colors_config(const char *var, const char *value, void *cb)\n+{\n+\tint slot;\n+\tif (!starts_with(var, \"color.ls.\"))\n+\t\treturn 0;\n+\tvar += 9;\n+\tfor (slot = 0; config_name[slot]; slot++)\n+\t\tif (!strcasecmp(var, config_name[slot]))\n+\t\t\tbreak;\n+\tif (!config_name[slot])\n+\t\treturn 0;\n+\tif (!value)\n+\t\treturn config_error_nonbool(var);\n+\tcolor_parse(value, var, ls_colors[slot]);\n+\treturn 0;\n+}\n+\n void parse_ls_color(void)\n {\n \tconst char *p;\t\t\t/* Pointer to character being parsed */\n@@ -395,4 +420,5 @@ void parse_ls_color(void)\n \n \tif (!strcmp(ls_colors[LS_LN], \"target\"))\n \t\tcolor_symlink_as_referent = 1;\n+\tgit_config(ls_colors_config, NULL);\n }\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238085","messageId":"1396187769-30863-4-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 03/18] ls_colors.c: add a function to color a file name","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:55:54Z","receivedAt":"2014-03-30T13:55:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The new function is based on print_color_indicator() from commit\n7326d1f1a67edf21947ae98194f98c38b6e9e527 in coreutils.git.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n color.h     |  2 ++\n ls_colors.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 68 insertions(+)\n\ndiff --git a/color.h b/color.h\nindex 640fc48..398369a 100644\n--- a/color.h\n+++ b/color.h\n@@ -94,5 +94,7 @@ void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb);\n int color_is_nil(const char *color);\n \n void parse_ls_color(void);\n+void color_filename(struct strbuf *sb, const char *name,\n+\t\t    const char *display_name, mode_t mode, int linkok);\n \n #endif /* COLOR_H */\ndiff --git a/ls_colors.c b/ls_colors.c\nindex cef5a92..1125329 100644\n--- a/ls_colors.c\n+++ b/ls_colors.c\n@@ -422,3 +422,69 @@ void parse_ls_color(void)\n \t\tcolor_symlink_as_referent = 1;\n \tgit_config(ls_colors_config, NULL);\n }\n+\n+void color_filename(struct strbuf *sb, const char *name,\n+\t\t    const char *display_name, mode_t mode, int linkok)\n+{\n+\tint type;\n+\tstruct color_ext_type *ext;\t/* Color extension */\n+\n+\tif (S_ISREG (mode)) {\n+\t\ttype = LS_FL;\n+\t\tif ((mode & S_ISUID) != 0)\n+\t\t\ttype = LS_SU;\n+\t\telse if ((mode & S_ISGID) != 0)\n+\t\t\ttype = LS_SG;\n+\t\telse if ((mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)\n+\t\t\ttype = LS_EX;\n+\t} else if (S_ISDIR (mode)) {\n+\t\tif ((mode & S_ISVTX) && (mode & S_IWOTH))\n+\t\t\ttype = LS_TW;\n+\t\telse if ((mode & S_IWOTH) != 0)\n+\t\t\ttype = LS_OW;\n+\t\telse if ((mode & S_ISVTX) != 0)\n+\t\t\ttype = LS_ST;\n+\t\telse\n+\t\t\ttype = LS_DI;\n+\t} else if (S_ISLNK (mode))\n+\t\ttype = (!linkok && *ls_colors[LS_OR]) ? LS_OR : LS_LN;\n+\telse if (S_ISFIFO (mode))\n+\t\ttype = LS_PI;\n+\telse if (S_ISSOCK (mode))\n+\t\ttype = LS_SO;\n+\telse if (S_ISBLK (mode))\n+\t\ttype = LS_BD;\n+\telse if (S_ISCHR (mode))\n+\t\ttype = LS_CD;\n+#ifdef S_ISDOOR\n+\telse if (S_ISDOOR (mode))\n+\t\ttype = LS_DO;\n+#endif\n+\telse\n+\t\t/* Classify a file of some other type as C_ORPHAN.  */\n+\t\ttype = LS_OR;\n+\n+\t/* Check the file's suffix only if still classified as C_FILE.  */\n+\text = NULL;\n+\tif (type == LS_FL) {\n+\t\t/* Test if NAME has a recognized suffix.  */\n+\t\tsize_t len = strlen(name);\n+\t\tconst char *p = name + len;\t\t/* Pointer to final \\0.  */\n+\t\tfor (ext = color_ext_list; ext != NULL; ext = ext->next) {\n+\t\t\tif (ext->ext.len <= len &&\n+\t\t\t    !strncmp(p - ext->ext.len, ext->ext.string, ext->ext.len))\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (display_name)\n+\t\tname = display_name;\n+\tif (ext)\n+\t\tstrbuf_addf(sb, \"\\033[%.*sm%s%s\",\n+\t\t\t    (int)ext->seq.len, ext->seq.string,\n+\t\t\t    name, GIT_COLOR_RESET);\n+\telse if (*ls_colors[type])\n+\t\tstrbuf_addf(sb, \"%s%s%s\", ls_colors[type], name, GIT_COLOR_RESET);\n+\telse\n+\t\tstrbuf_addstr(sb, name);\n+}\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238084","messageId":"1396187769-30863-5-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 04/18] ls_colors.c: highlight submodules like directories","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:55:55Z","receivedAt":"2014-03-30T13:55:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt | 3 ++-\n ls_colors.c              | 8 +++++++-\n 2 files changed, 9 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 3fb754e..6bca55e 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -913,7 +913,8 @@ color.ls.<slot>::\n \tUse customized color for file name colorization. If not set\n \tand the environment variable LS_COLORS is set, color settings\n \tfrom $LS_COLORS are used. `<slot>` can be `normal`, `file`,\n-\t`directory`, `symlink`, `fifo`, `socket`, `block`, `char`,\n+\t`directory`, `submodule`,\n+\t`symlink`, `fifo`, `socket`, `block`, `char`,\n \t`missing`, `orphan`, `executable`, `door`, `setuid`, `setgid`,\n \t`sticky`, `otherwritable`, `stickyotherwritable`, `cap`,\n \t`multihardlink`. The values of these variables may be\ndiff --git a/ls_colors.c b/ls_colors.c\nindex 1125329..0cc4e9b 100644\n--- a/ls_colors.c\n+++ b/ls_colors.c\n@@ -29,6 +29,8 @@ enum color_ls {\n \tLS_MH,\t\t\t/* multi hardlink */\n \tLS_CL,\t\t\t/* clear end of line */\n \n+\tLS_SUBMODULE,\n+\n \tMAX_LS\n };\n \n@@ -58,7 +60,8 @@ static char ls_colors[MAX_LS][COLOR_MAXLEN] = {\n \tGIT_COLOR_BLACK_ON_GREEN,\n \t\"\",\n \t\"\",\n-\t\"\"\n+\t\"\",\n+\tGIT_COLOR_BOLD_BLUE\n };\n \n static const char *const indicator_name[] = {\n@@ -73,6 +76,7 @@ static const char* const config_name[] = {\n \t\"fifo\", \"socket\", \"block\", \"char\", \"missing\", \"orphan\", \"executable\",\n \t\"door\", \"setuid\", \"setgid\", \"sticky\", \"otherwritable\",\n \t\"stickyotherwritable\", \"cap\", \"multihardlink\", \"\",\n+\t\"submodule\",\n \tNULL\n };\n \n@@ -448,6 +452,8 @@ void color_filename(struct strbuf *sb, const char *name,\n \t\t\ttype = LS_DI;\n \t} else if (S_ISLNK (mode))\n \t\ttype = (!linkok && *ls_colors[LS_OR]) ? LS_OR : LS_LN;\n+\telse if (S_ISGITLINK(mode))\n+\t\ttype = LS_SUBMODULE;\n \telse if (S_ISFIFO (mode))\n \t\ttype = LS_PI;\n \telse if (S_ISSOCK (mode))\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238087","messageId":"1396187769-30863-6-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 05/18] ls-files: buffer full item in strbuf before printing","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:55:56Z","receivedAt":"2014-03-30T13:55:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Buffering so that we can manipulate the strings (e.g. coloring)\nfurther before finally printing them.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c | 48 +++++++++++++++++++++++++++++++++++-------------\n 1 file changed, 35 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 47c3880..6e30592 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -47,18 +47,30 @@ static const char *tag_modified = \"\";\n static const char *tag_skip_worktree = \"\";\n static const char *tag_resolve_undo = \"\";\n \n-static void write_name(const char *name)\n+static void write_name(struct strbuf *sb, const char *name)\n {\n \t/*\n \t * With \"--full-name\", prefix_len=0; this caller needs to pass\n \t * an empty string in that case (a NULL is good for \"\").\n \t */\n-\twrite_name_quoted_relative(name, prefix_len ? prefix : NULL,\n-\t\t\t\t   stdout, line_terminator);\n+\tconst char *real_prefix = prefix_len ? prefix : NULL;\n+\tif (!line_terminator) {\n+\t\tstruct strbuf sb2 = STRBUF_INIT;\n+\t\tstrbuf_addstr(sb, relative_path(name, real_prefix, &sb2));\n+\t\tstrbuf_release(&sb2);\n+\t} else\n+\t\tquote_path_relative(name, real_prefix, sb);\n+\tstrbuf_addch(sb, line_terminator);\n+}\n+\n+static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n+{\n+\tfwrite(sb->buf, sb->len, 1, fp);\n }\n \n static void show_dir_entry(const char *tag, struct dir_entry *ent)\n {\n+\tstatic struct strbuf sb = STRBUF_INIT;\n \tint len = max_prefix_len;\n \n \tif (len >= ent->len)\n@@ -67,8 +79,10 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n \tif (!dir_path_match(ent, &pathspec, len, ps_matched))\n \t\treturn;\n \n-\tfputs(tag, stdout);\n-\twrite_name(ent->name);\n+\tstrbuf_reset(&sb);\n+\tstrbuf_addstr(&sb, tag);\n+\twrite_name(&sb, ent->name);\n+\tstrbuf_fputs(&sb, stdout);\n }\n \n static void show_other_files(struct dir_struct *dir)\n@@ -134,6 +148,7 @@ static void show_killed_files(struct dir_struct *dir)\n \n static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n {\n+\tstatic struct strbuf sb = STRBUF_INIT;\n \tint len = max_prefix_len;\n \n \tif (len >= ce_namelen(ce))\n@@ -161,16 +176,18 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n \t\ttag = alttag;\n \t}\n \n+\tstrbuf_reset(&sb);\n \tif (!show_stage) {\n-\t\tfputs(tag, stdout);\n+\t\tstrbuf_addstr(&sb, tag);\n \t} else {\n-\t\tprintf(\"%s%06o %s %d\\t\",\n-\t\t       tag,\n-\t\t       ce->ce_mode,\n-\t\t       find_unique_abbrev(ce->sha1,abbrev),\n-\t\t       ce_stage(ce));\n+\t\tstrbuf_addf(&sb, \"%s%06o %s %d\\t\",\n+\t\t\t    tag,\n+\t\t\t    ce->ce_mode,\n+\t\t\t    find_unique_abbrev(ce->sha1,abbrev),\n+\t\t\t    ce_stage(ce));\n \t}\n-\twrite_name(ce->name);\n+\twrite_name(&sb, ce->name);\n+\tstrbuf_fputs(&sb, stdout);\n \tif (debug_mode) {\n \t\tconst struct stat_data *sd = &ce->ce_stat_data;\n \n@@ -206,7 +223,12 @@ static void show_ru_info(void)\n \t\t\tprintf(\"%s%06o %s %d\\t\", tag_resolve_undo, ui->mode[i],\n \t\t\t       find_unique_abbrev(ui->sha1[i], abbrev),\n \t\t\t       i + 1);\n-\t\t\twrite_name(path);\n+\t\t\t/*\n+\t\t\t * With \"--full-name\", prefix_len=0; this caller needs to pass\n+\t\t\t * an empty string in that case (a NULL is good for \"\").\n+\t\t\t */\n+\t\t\twrite_name_quoted_relative(path, prefix_len ? prefix : NULL,\n+\t\t\t\t\t\t   stdout, line_terminator);\n \t\t}\n \t}\n }\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238086","messageId":"1396187769-30863-7-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 06/18] ls-files: add --color to highlight file names","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:55:57Z","receivedAt":"2014-03-30T13:55:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-ls-files.txt |  7 +++++++\n builtin/ls-files.c             | 38 +++++++++++++++++++++++++++++++++++---\n 2 files changed, 42 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\nindex c0856a6..f006fc1 100644\n--- a/Documentation/git-ls-files.txt\n+++ b/Documentation/git-ls-files.txt\n@@ -147,6 +147,13 @@ a space) at the start of each line:\n \tpossible for manual inspection; the exact format may change at\n \tany time.\n \n+--color[=<when>]::\n+--no-color::\n+\tColor file names. The value must be `always`, `never`, or\n+\t`auto`. `--no-color` is equivalent to\n+\t`--color=never`. `--color` is equivalent to\n+\t`--color=auto`.\n+\n \\--::\n \tDo not interpret any more arguments as options.\n \ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 6e30592..2857b38 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -14,6 +14,7 @@\n #include \"resolve-undo.h\"\n #include \"string-list.h\"\n #include \"pathspec.h\"\n+#include \"color.h\"\n \n static int abbrev;\n static int show_deleted;\n@@ -27,6 +28,7 @@ static int show_killed;\n static int show_valid_bit;\n static int line_terminator = '\\n';\n static int debug_mode;\n+static int use_color;\n \n static const char *prefix;\n static int max_prefix_len;\n@@ -60,7 +62,6 @@ static void write_name(struct strbuf *sb, const char *name)\n \t\tstrbuf_release(&sb2);\n \t} else\n \t\tquote_path_relative(name, real_prefix, sb);\n-\tstrbuf_addch(sb, line_terminator);\n }\n \n static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n@@ -68,6 +69,21 @@ static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n \tfwrite(sb->buf, sb->len, 1, fp);\n }\n \n+static void write_dir_entry(struct strbuf *sb, const struct dir_entry *ent)\n+{\n+\tstruct strbuf quoted = STRBUF_INIT;\n+\tstruct stat st;\n+\tif (stat(ent->name, &st))\n+\t\tst.st_mode = 0;\n+\twrite_name(&quoted, ent->name);\n+\tif (want_color(use_color))\n+\t\tcolor_filename(sb, ent->name, quoted.buf, st.st_mode, 1);\n+\telse\n+\t\tstrbuf_addbuf(sb, &quoted);\n+\tstrbuf_addch(sb, line_terminator);\n+\tstrbuf_release(&quoted);\n+}\n+\n static void show_dir_entry(const char *tag, struct dir_entry *ent)\n {\n \tstatic struct strbuf sb = STRBUF_INIT;\n@@ -81,7 +97,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n \n \tstrbuf_reset(&sb);\n \tstrbuf_addstr(&sb, tag);\n-\twrite_name(&sb, ent->name);\n+\twrite_dir_entry(&sb, ent);\n \tstrbuf_fputs(&sb, stdout);\n }\n \n@@ -146,6 +162,18 @@ static void show_killed_files(struct dir_struct *dir)\n \t}\n }\n \n+static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)\n+{\n+\tstruct strbuf quoted = STRBUF_INIT;\n+\twrite_name(&quoted, ce->name);\n+\tif (want_color(use_color))\n+\t\tcolor_filename(sb, ce->name, quoted.buf, ce->ce_mode, 1);\n+\telse\n+\t\tstrbuf_addbuf(sb, &quoted);\n+\tstrbuf_addch(sb, line_terminator);\n+\tstrbuf_release(&quoted);\n+}\n+\n static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n {\n \tstatic struct strbuf sb = STRBUF_INIT;\n@@ -186,7 +214,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n \t\t\t    find_unique_abbrev(ce->sha1,abbrev),\n \t\t\t    ce_stage(ce));\n \t}\n-\twrite_name(&sb, ce->name);\n+\twrite_ce_name(&sb, ce);\n \tstrbuf_fputs(&sb, stdout);\n \tif (debug_mode) {\n \t\tconst struct stat_data *sd = &ce->ce_stat_data;\n@@ -523,6 +551,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tN_(\"if any <file> is not in the index, treat this as an error\")),\n \t\tOPT_STRING(0, \"with-tree\", &with_tree, N_(\"tree-ish\"),\n \t\t\tN_(\"pretend that paths removed since <tree-ish> are still present\")),\n+\t\tOPT__COLOR(&use_color, N_(\"show color\")),\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n \t\tOPT_END()\n@@ -570,6 +599,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tif (require_work_tree && !is_inside_work_tree())\n \t\tsetup_work_tree();\n \n+\tif (want_color(use_color))\n+\t\tparse_ls_color();\n+\n \tparse_pathspec(&pathspec, 0,\n \t\t       PATHSPEC_PREFER_CWD |\n \t\t       PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238088","messageId":"1396187769-30863-8-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 07/18] ls-files: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:55:58Z","receivedAt":"2014-03-30T13:55:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-ls-files.txt |  6 ++++++\n builtin/ls-files.c             | 28 ++++++++++++++++++++++++++++\n 2 files changed, 34 insertions(+)\n\ndiff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\nindex f006fc1..a5a30c2 100644\n--- a/Documentation/git-ls-files.txt\n+++ b/Documentation/git-ls-files.txt\n@@ -154,6 +154,12 @@ a space) at the start of each line:\n \t`--color=never`. `--color` is equivalent to\n \t`--color=auto`.\n \n+--column[=<options>]::\n+--no-column::\n+\tDisplay files in columns. See configuration variable column.ui\n+\tfor option syntax. `--column` and `--no-column` without options\n+\tare equivalent to 'always' and 'never' respectively.\n+\n \\--::\n \tDo not interpret any more arguments as options.\n \ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 2857b38..a481c37 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -15,6 +15,7 @@\n #include \"string-list.h\"\n #include \"pathspec.h\"\n #include \"color.h\"\n+#include \"column.h\"\n \n static int abbrev;\n static int show_deleted;\n@@ -29,6 +30,7 @@ static int show_valid_bit;\n static int line_terminator = '\\n';\n static int debug_mode;\n static int use_color;\n+static unsigned int colopts;\n \n static const char *prefix;\n static int max_prefix_len;\n@@ -39,6 +41,7 @@ static char *ps_matched;\n static const char *with_tree;\n static int exc_given;\n static int exclude_args;\n+static struct string_list output = STRING_LIST_INIT_NODUP;\n \n static const char *tag_cached = \"\";\n static const char *tag_unmerged = \"\";\n@@ -66,6 +69,10 @@ static void write_name(struct strbuf *sb, const char *name)\n \n static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n {\n+\tif (column_active(colopts)) {\n+\t\tstring_list_append(&output, strbuf_detach(sb, NULL));\n+\t\treturn;\n+\t}\n \tfwrite(sb->buf, sb->len, 1, fp);\n }\n \n@@ -552,6 +559,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\tOPT_STRING(0, \"with-tree\", &with_tree, N_(\"tree-ish\"),\n \t\t\tN_(\"pretend that paths removed since <tree-ish> are still present\")),\n \t\tOPT__COLOR(&use_color, N_(\"show color\")),\n+\t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n \t\tOPT_END()\n@@ -596,6 +604,18 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tif (dir.exclude_per_dir)\n \t\texc_given = 1;\n \n+\tfinalize_colopts(&colopts, -1);\n+\tif (explicitly_enable_column(colopts)) {\n+\t\tif (!line_terminator)\n+\t\t\tdie(_(\"--column and -z are incompatible\"));\n+\t\tif (show_resolve_undo)\n+\t\t\tdie(_(\"--column and --resolve-undo are incompatible\"));\n+\t\tif (debug_mode)\n+\t\t\tdie(_(\"--column and --debug are incompatible\"));\n+\t}\n+\tif (column_active(colopts))\n+\t\tline_terminator = 0;\n+\n \tif (require_work_tree && !is_inside_work_tree())\n \t\tsetup_work_tree();\n \n@@ -638,6 +658,14 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tif (show_resolve_undo)\n \t\tshow_ru_info();\n \n+\tif (column_active(colopts)) {\n+\t\tstruct column_options copts;\n+\t\tmemset(&copts, 0, sizeof(copts));\n+\t\tcopts.padding = 2;\n+\t\tprint_columns(&output, colopts, &copts);\n+\t\tstring_list_clear(&output, 0);\n+\t}\n+\n \tif (ps_matched) {\n \t\tint bad;\n \t\tbad = report_path_error(ps_matched, &pathspec, prefix);\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238089","messageId":"1396187769-30863-9-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 08/18] ls-files: support --max-depth","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:55:59Z","receivedAt":"2014-03-30T13:55:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-ls-files.txt | 7 +++++++\n builtin/ls-files.c             | 7 +++++++\n 2 files changed, 14 insertions(+)\n\ndiff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\nindex a5a30c2..ad621b5 100644\n--- a/Documentation/git-ls-files.txt\n+++ b/Documentation/git-ls-files.txt\n@@ -160,6 +160,13 @@ a space) at the start of each line:\n \tfor option syntax. `--column` and `--no-column` without options\n \tare equivalent to 'always' and 'never' respectively.\n \n+--max-depth=<depth>::\n+\tFor each <pathspec> given on command line, descend at most <depth>\n+\tlevels of directories. A negative value means no limit (default).\n+\tThis option is ignored if <pathspec> contains active wildcards.\n+\tIn other words if \"a*\" matches a directory named \"a*\",\n+\t\"*\" is matched literally so --max-depth is still effective.\n+\n \\--::\n \tDo not interpret any more arguments as options.\n \ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex a481c37..130bed0 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -503,6 +503,7 @@ static int option_parse_exclude_standard(const struct option *opt,\n int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n {\n \tint require_work_tree = 0, show_tag = 0, i;\n+\tint max_depth = -1;\n \tconst char *max_prefix;\n \tstruct dir_struct dir;\n \tstruct exclude_list *el;\n@@ -560,6 +561,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tN_(\"pretend that paths removed since <tree-ish> are still present\")),\n \t\tOPT__COLOR(&use_color, N_(\"show color\")),\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n+\t\t{ OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n+\t\t\tN_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n+\t\t\tNULL, 1 },\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n \t\tOPT_END()\n@@ -624,8 +628,11 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \n \tparse_pathspec(&pathspec, 0,\n \t\t       PATHSPEC_PREFER_CWD |\n+\t\t       (max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0) |\n \t\t       PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,\n \t\t       prefix, argv);\n+\tpathspec.max_depth = max_depth;\n+\tpathspec.recursive = 1;\n \n \t/* Find common prefix for all pathspec's */\n \tmax_prefix = common_prefix(&pathspec);\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238090","messageId":"1396187769-30863-10-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 09/18] Add git-list-files, a user friendly version of ls-files and more","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:56:00Z","receivedAt":"2014-03-30T13:56:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is more user friendly version of ls-files:\n\n* it's automatically colored and columnized\n* it refreshes the index like all porcelain commands\n* it defaults to non-recursive behavior like ls\n* :(glob) is on by default so '*.c' means a.c but not a/b.c, use\n  '**/*.c' for that.\n* auto pager\n\nThe name 'ls' is not taken. It is left for the user to make an alias\nwith better default options.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                             |  1 +\n Documentation/config.txt               | 10 +++++\n Documentation/git-list-files.txt (new) | 80 ++++++++++++++++++++++++++++++++++\n Makefile                               |  1 +\n builtin/ls-files.c                     | 69 +++++++++++++++++++++++++++--\n command-list.txt                       |  1 +\n git.c                                  |  1 +\n 7 files changed, 159 insertions(+), 4 deletions(-)\n create mode 100644 Documentation/git-list-files.txt\n\ndiff --git a/.gitignore b/.gitignore\nindex dc600f9..faeac5d 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -75,6 +75,7 @@\n /git-init\n /git-init-db\n /git-instaweb\n+/git-list-files\n /git-log\n /git-ls-files\n /git-ls-remote\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 6bca55e..e07e8bc 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -909,6 +909,12 @@ color.status.<slot>::\n \tto red). The values of these variables may be specified as in\n \tcolor.branch.<slot>.\n \n+color.list-files::\n+\tA boolean to enable/disable color in the output of\n+\tlinkgit:git-list-files[1]. May be set to `always`, `false` (or\n+\t`never`) or `auto` (or `true`), in which case colors are used\n+\tonly when the output is to a terminal. Defaults to false.\n+\n color.ls.<slot>::\n \tUse customized color for file name colorization. If not set\n \tand the environment variable LS_COLORS is set, color settings\n@@ -981,6 +987,10 @@ column.clean::\n \tSpecify the layout when list items in `git clean -i`, which always\n \tshows files and directories in columns. See `column.ui` for details.\n \n+column.list-files::\n+\tSpecify whether to output tag listing in `git list-files` in columns.\n+\tSee `column.ui` for details.\n+\n column.status::\n \tSpecify whether to output untracked files in `git status` in columns.\n \tSee `column.ui` for details.\ndiff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt\nnew file mode 100644\nindex 0000000..3039e1e\n--- /dev/null\n+++ b/Documentation/git-list-files.txt\n@@ -0,0 +1,80 @@\n+git-list-files(1)\n+===============\n+\n+NAME\n+----\n+git-list-files - List files\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git list-files [options] [<pathspec>...]\n+\n+DESCRIPTION\n+-----------\n+List files (by default in current working directory) that are in the\n+index. Depending on the chosen options, maybe only modified files in\n+working tree are shown, or untracked files...\n+\n+OPTIONS\n+-------\n+-c::\n+--cached::\n+\tShow cached files (default)\n+\n+-d::\n+--deleted::\n+\tShow cached files that are deleted on working directory\n+\n+-m::\n+--modified::\n+\tShow cached files that have modification on working directory\n+\n+-o::\n+--others::\n+\tShow untracked files (and only unignored ones unless -i is\n+\tspecified)\n+\n+-i::\n+--ignored::\n+\tShow only ignored files. When showing files in the index,\n+\tprint only those matched by an exclude pattern. When showing\n+\t\"other\" files, show only those matched by an exclude pattern.\n+\n+-u::\n+--unmerged::\n+\tShow unmerged files\n+\n+--color[=<when>]::\n+--no-color::\n+\tColor file names. The value must be `always`, `never`, or\n+\t`auto`. `--no-color` is equivalent to\n+\t`--color=never`. `--color` is equivalent to\n+\t`--color=auto`. See configuration variable `color.list-files`\n+\tfor the default settings.\n+\n+--column[=<options>]::\n+--no-column::\n+\tDisplay files in columns. See configuration variable column.ui\n+\tfor option syntax. `--column` and `--no-column` without options\n+\tare equivalent to 'always' and 'never' respectively.\n+\n+--max-depth=<depth>::\n+\tFor each <pathspec> given on command line, descend at most <depth>\n+\tlevels of directories. A negative value means no limit.\n+\tThis option is ignored if <pathspec> contains active wildcards.\n+\tIn other words if \"a*\" matches a directory named \"a*\",\n+\t\"*\" is matched literally so --max-depth is still effective.\n+\tThe default is `--max-depth=0`.\n+\n+<pathspec>::\n+\tFiles to show. :(glob) magic is enabled and recursion disabled\n+\tby default.\n+\n+SEE ALSO\n+--------\n+linkgit:git-ls-files[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Makefile b/Makefile\nindex f6a6e14..6104e05 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -584,6 +584,7 @@ BUILT_INS += git-cherry-pick$X\n BUILT_INS += git-format-patch$X\n BUILT_INS += git-fsck-objects$X\n BUILT_INS += git-init$X\n+BUILT_INS += git-list-files$X\n BUILT_INS += git-merge-subtree$X\n BUILT_INS += git-show$X\n BUILT_INS += git-stage$X\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 130bed0..0ae07b0 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -31,6 +31,7 @@ static int line_terminator = '\\n';\n static int debug_mode;\n static int use_color;\n static unsigned int colopts;\n+static int porcelain;\n \n static const char *prefix;\n static int max_prefix_len;\n@@ -459,6 +460,11 @@ static const char * const ls_files_usage[] = {\n \tNULL\n };\n \n+static const char * const ls_usage[] = {\n+\tN_(\"git list-files [options] [<file>...]\"),\n+\tNULL\n+};\n+\n static int option_parse_z(const struct option *opt,\n \t\t\t  const char *arg, int unset)\n {\n@@ -500,6 +506,17 @@ static int option_parse_exclude_standard(const struct option *opt,\n \treturn 0;\n }\n \n+static int git_ls_config(const char *var, const char *value, void *cb)\n+{\n+\tif (starts_with(var, \"column.\"))\n+\t\treturn git_column_config(var, value, \"list-files\", &colopts);\n+\tif (!strcmp(var, \"color.list-files\")) {\n+\t\tuse_color = git_config_colorbool(var, value);\n+\t\treturn 0;\n+\t}\n+\treturn git_color_default_config(var, value, cb);\n+}\n+\n int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n {\n \tint require_work_tree = 0, show_tag = 0, i;\n@@ -568,21 +585,61 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\tOPT_BOOL(0, \"debug\", &debug_mode, N_(\"show debugging data\")),\n \t\tOPT_END()\n \t};\n+\tstruct option builtin_ls_options[] = {\n+\t\tOPT_BOOL('c', \"cached\", &show_cached,\n+\t\t\tN_(\"show cached files (default)\")),\n+\t\tOPT_BOOL('d', \"deleted\", &show_deleted,\n+\t\t\tN_(\"show cached files that are deleted on working directory\")),\n+\t\tOPT_BOOL('m', \"modified\", &show_modified,\n+\t\t\tN_(\"show cached files that have modification on working directory\")),\n+\t\tOPT_BOOL('o', \"others\", &show_others,\n+\t\t\tN_(\"show untracked files\")),\n+\t\tOPT_BIT('i', \"ignored\", &dir.flags,\n+\t\t\tN_(\"show ignored files\"),\n+\t\t\tDIR_SHOW_IGNORED),\n+\t\tOPT_BOOL('u', \"unmerged\", &show_unmerged,\n+\t\t\tN_(\"show unmerged files\")),\n+\t\tOPT__COLOR(&use_color, N_(\"show color\")),\n+\t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n+\t\t{ OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n+\t\t\tN_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n+\t\t\tNULL, 1 },\n+\t\tOPT__ABBREV(&abbrev),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *options;\n+\tconst char * const *help_usage;\n \n+\tif (!strcmp(argv[0], \"list-files\")) {\n+\t\thelp_usage = ls_usage;\n+\t\toptions = builtin_ls_options;\n+\t\tporcelain = 1;\n+\t} else {\n+\t\thelp_usage = ls_files_usage;\n+\t\toptions = builtin_ls_files_options;\n+\t}\n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n-\t\tusage_with_options(ls_files_usage, builtin_ls_files_options);\n+\t\tusage_with_options(help_usage, options);\n \n \tmemset(&dir, 0, sizeof(dir));\n \tprefix = cmd_prefix;\n \tif (prefix)\n \t\tprefix_len = strlen(prefix);\n-\tgit_config(git_default_config, NULL);\n+\n+\tif (porcelain) {\n+\t\tsetenv(GIT_GLOB_PATHSPECS_ENVIRONMENT, \"1\", 1);\n+\t\texc_given = 1;\n+\t\tsetup_standard_excludes(&dir);\n+\t\tuse_color = -1;\n+\t\tmax_depth = 0;\n+\t\tgit_config(git_ls_config, NULL);\n+\t} else\n+\t\tgit_config(git_default_config, NULL);\n \n \tif (read_cache() < 0)\n \t\tdie(\"index file corrupt\");\n \n-\targc = parse_options(argc, argv, prefix, builtin_ls_files_options,\n-\t\t\tls_files_usage, 0);\n+\targc = parse_options(argc, argv, prefix, options, help_usage, 0);\n \tel = add_exclude_list(&dir, EXC_CMDL, \"--exclude option\");\n \tfor (i = 0; i < exclude_list.nr; i++) {\n \t\tadd_exclude(exclude_list.items[i].string, \"\", 0, el, --exclude_args);\n@@ -661,6 +718,10 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tdie(\"ls-files --with-tree is incompatible with -s or -u\");\n \t\toverlay_tree_on_cache(with_tree, max_prefix);\n \t}\n+\tif (porcelain) {\n+\t\trefresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL);\n+\t\tsetup_pager();\n+\t}\n \tshow_files(&dir);\n \tif (show_resolve_undo)\n \t\tshow_ru_info();\ndiff --git a/command-list.txt b/command-list.txt\nindex cf36c3d..bfaca46 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -63,6 +63,7 @@ git-index-pack                          plumbingmanipulators\n git-init                                mainporcelain common\n git-instaweb                            ancillaryinterrogators\n gitk                                    mainporcelain\n+git-list-files                          mainporcelain\n git-log                                 mainporcelain common\n git-ls-files                            plumbinginterrogators\n git-ls-remote                           plumbinginterrogators\ndiff --git a/git.c b/git.c\nindex 9efd1a3..8b46e92 100644\n--- a/git.c\n+++ b/git.c\n@@ -380,6 +380,7 @@ static struct cmd_struct commands[] = {\n \t{ \"index-pack\", cmd_index_pack, RUN_SETUP_GENTLY },\n \t{ \"init\", cmd_init_db },\n \t{ \"init-db\", cmd_init_db },\n+\t{ \"list-files\", cmd_ls_files, RUN_SETUP },\n \t{ \"log\", cmd_log, RUN_SETUP },\n \t{ \"ls-files\", cmd_ls_files, RUN_SETUP },\n \t{ \"ls-remote\", cmd_ls_remote, RUN_SETUP_GENTLY },\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238092","messageId":"1396187769-30863-11-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 10/18] list-files: -u does not imply showing stages","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:56:01Z","receivedAt":"2014-03-30T13:56:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Showing full index entry information is something for ls-files\nonly. The users of \"git list-files\" may just want to know what entries\nare not unmerged.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 0ae07b0..addbcce 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -656,7 +656,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t}\n \tif (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed)\n \t\trequire_work_tree = 1;\n-\tif (show_unmerged)\n+\tif (show_unmerged && !porcelain)\n \t\t/*\n \t\t * There's no point in showing unmerged unless\n \t\t * you also show the stage information.\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238093","messageId":"1396187769-30863-12-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 11/18] list-files: add -R/--recursive short for --max-depth=-1","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:56:02Z","receivedAt":"2014-03-30T13:56:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-list-files.txt | 4 ++++\n builtin/ls-files.c               | 2 ++\n 2 files changed, 6 insertions(+)\n\ndiff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt\nindex 3039e1e..5dccbbc 100644\n--- a/Documentation/git-list-files.txt\n+++ b/Documentation/git-list-files.txt\n@@ -45,6 +45,10 @@ OPTIONS\n --unmerged::\n \tShow unmerged files\n \n+-R::\n+--recursive::\n+\tEquivalent of `--max-depth=-1` (infinite recursion).\n+\n --color[=<when>]::\n --no-color::\n \tColor file names. The value must be `always`, `never`, or\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex addbcce..656b632 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -594,6 +594,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tN_(\"show cached files that have modification on working directory\")),\n \t\tOPT_BOOL('o', \"others\", &show_others,\n \t\t\tN_(\"show untracked files\")),\n+\t\tOPT_SET_INT('R', \"recursive\", &max_depth,\n+\t\t\t    N_(\"shortcut for --max-depth=-1\"), -1),\n \t\tOPT_BIT('i', \"ignored\", &dir.flags,\n \t\t\tN_(\"show ignored files\"),\n \t\t\tDIR_SHOW_IGNORED),\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238094","messageId":"1396187769-30863-13-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 12/18] list-files: add -1 short for --no-column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:56:03Z","receivedAt":"2014-03-30T13:56:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-list-files.txt | 3 +++\n builtin/ls-files.c               | 2 ++\n 2 files changed, 5 insertions(+)\n\ndiff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt\nindex 5dccbbc..725a236 100644\n--- a/Documentation/git-list-files.txt\n+++ b/Documentation/git-list-files.txt\n@@ -49,6 +49,9 @@ OPTIONS\n --recursive::\n \tEquivalent of `--max-depth=-1` (infinite recursion).\n \n+-1::\n+\tEquivalent of --no-column.\n+\n --color[=<when>]::\n --no-color::\n \tColor file names. The value must be `always`, `never`, or\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 656b632..db0ee6b 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -603,6 +603,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tN_(\"show unmerged files\")),\n \t\tOPT__COLOR(&use_color, N_(\"show color\")),\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n+\t\tOPT_SET_INT('1', NULL, &colopts,\n+\t\t\t    N_(\"shortcut for --no-column\"), COL_PARSEOPT),\n \t\t{ OPTION_INTEGER, 0, \"max-depth\", &max_depth, N_(\"depth\"),\n \t\t\tN_(\"descend at most <depth> levels\"), PARSE_OPT_NONEG,\n \t\t\tNULL, 1 },\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238095","messageId":"1396187769-30863-14-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 13/18] list-files: add -t back","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:56:04Z","receivedAt":"2014-03-30T13:56:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Tag \"H\" (cached) is not shown though because it's usually the majority\nand becomes noise. Not showing it makes the other tags stand out. -t\nis on by default if more than one file category is selected.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-list-files.txt |  6 ++++++\n builtin/ls-files.c               | 27 +++++++++++++++++----------\n 2 files changed, 23 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt\nindex 725a236..0ef616b 100644\n--- a/Documentation/git-list-files.txt\n+++ b/Documentation/git-list-files.txt\n@@ -45,6 +45,12 @@ OPTIONS\n --unmerged::\n \tShow unmerged files\n \n+-t::\n+--[no-]tag::\n+\tShow a tag to indicate file type. Automatically turned on with\n+\tmultiple file selections. See linkgit::git-ls-files[1] option\n+\t`-t` for more information.\n+\n -R::\n --recursive::\n \tEquivalent of `--max-depth=-1` (infinite recursion).\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex db0ee6b..14dfd2a 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -596,6 +596,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tN_(\"show untracked files\")),\n \t\tOPT_SET_INT('R', \"recursive\", &max_depth,\n \t\t\t    N_(\"shortcut for --max-depth=-1\"), -1),\n+\t\tOPT_BOOL('t', \"tag\", &show_tag,\n+\t\t\tN_(\"identify the file status with tags\")),\n \t\tOPT_BIT('i', \"ignored\", &dir.flags,\n \t\t\tN_(\"show ignored files\"),\n \t\t\tDIR_SHOW_IGNORED),\n@@ -636,6 +638,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\tsetup_standard_excludes(&dir);\n \t\tuse_color = -1;\n \t\tmax_depth = 0;\n+\t\tshow_tag = -1;\n \t\tgit_config(git_ls_config, NULL);\n \t} else\n \t\tgit_config(git_default_config, NULL);\n@@ -648,16 +651,6 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tfor (i = 0; i < exclude_list.nr; i++) {\n \t\tadd_exclude(exclude_list.items[i].string, \"\", 0, el, --exclude_args);\n \t}\n-\tif (show_tag || show_valid_bit) {\n-\t\ttag_cached = \"H \";\n-\t\ttag_unmerged = \"M \";\n-\t\ttag_removed = \"R \";\n-\t\ttag_modified = \"C \";\n-\t\ttag_other = \"? \";\n-\t\ttag_killed = \"K \";\n-\t\ttag_skip_worktree = \"S \";\n-\t\ttag_resolve_undo = \"U \";\n-\t}\n \tif (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed)\n \t\trequire_work_tree = 1;\n \tif (show_unmerged && !porcelain)\n@@ -711,6 +704,20 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t      show_killed || show_modified || show_resolve_undo))\n \t\tshow_cached = 1;\n \n+\tif (show_tag == -1)\n+\t\tshow_tag = (show_cached + show_deleted + show_others +\n+\t\t\t    show_unmerged + show_killed + show_modified) > 1;\n+\tif (show_tag || show_valid_bit) {\n+\t\ttag_cached = porcelain ? \"  \" : \"H \";\n+\t\ttag_unmerged = \"M \";\n+\t\ttag_removed = \"R \";\n+\t\ttag_modified = \"C \";\n+\t\ttag_other = \"? \";\n+\t\ttag_killed = \"K \";\n+\t\ttag_skip_worktree = \"S \";\n+\t\ttag_resolve_undo = \"U \";\n+\t}\n+\n \tif (max_prefix)\n \t\tprune_cache(max_prefix);\n \tif (with_tree) {\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238096","messageId":"1396187769-30863-15-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 14/18] list-files: sort output and remove duplicates","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:56:05Z","receivedAt":"2014-03-30T13:56:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When you mix different file types, with ls-files you may get separate\nlisting. For example, \"ls-files -cm\" will show file \"abc\" twice: one\nas part of cached list, one of modified list. With \"ls\" (and this\npatch) they will be in a single sorted list (easier for the eye).\n\nDuplicate entries are also removed. Note that display content is\ncompared, so if you have \"-t\" on, or you color file types differently,\nyou will get duplicate textual entries. This is good imo.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c | 36 ++++++++++++++++++++++++------------\n 1 file changed, 24 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 14dfd2a..ff2377f 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -53,6 +53,13 @@ static const char *tag_modified = \"\";\n static const char *tag_skip_worktree = \"\";\n static const char *tag_resolve_undo = \"\";\n \n+static int compare_output(const void *a_, const void *b_)\n+{\n+\tconst struct string_list_item *a = a_;\n+\tconst struct string_list_item *b = b_;\n+\treturn strcmp(a->util, b->util);\n+}\n+\n static void write_name(struct strbuf *sb, const char *name)\n {\n \t/*\n@@ -68,10 +75,12 @@ static void write_name(struct strbuf *sb, const char *name)\n \t\tquote_path_relative(name, real_prefix, sb);\n }\n \n-static void strbuf_fputs(struct strbuf *sb, FILE *fp)\n+static void strbuf_fputs(struct strbuf *sb, const char *full_name, FILE *fp)\n {\n-\tif (column_active(colopts)) {\n-\t\tstring_list_append(&output, strbuf_detach(sb, NULL));\n+\tif (column_active(colopts) || porcelain) {\n+\t\tstruct string_list_item *it;\n+\t\tit = string_list_append(&output, strbuf_detach(sb, NULL));\n+\t\tit->util = (void*)full_name;\n \t\treturn;\n \t}\n \tfwrite(sb->buf, sb->len, 1, fp);\n@@ -106,7 +115,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n \tstrbuf_reset(&sb);\n \tstrbuf_addstr(&sb, tag);\n \twrite_dir_entry(&sb, ent);\n-\tstrbuf_fputs(&sb, stdout);\n+\tstrbuf_fputs(&sb, ent->name, stdout);\n }\n \n static void show_other_files(struct dir_struct *dir)\n@@ -223,7 +232,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)\n \t\t\t    ce_stage(ce));\n \t}\n \twrite_ce_name(&sb, ce);\n-\tstrbuf_fputs(&sb, stdout);\n+\tstrbuf_fputs(&sb, ce->name, stdout);\n \tif (debug_mode) {\n \t\tconst struct stat_data *sd = &ce->ce_stat_data;\n \n@@ -524,6 +533,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tconst char *max_prefix;\n \tstruct dir_struct dir;\n \tstruct exclude_list *el;\n+\tstruct column_options copts;\n \tstruct string_list exclude_list = STRING_LIST_INIT_NODUP;\n \tstruct option builtin_ls_files_options[] = {\n \t\t{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,\n@@ -671,7 +681,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\tif (debug_mode)\n \t\t\tdie(_(\"--column and --debug are incompatible\"));\n \t}\n-\tif (column_active(colopts))\n+\tif (column_active(colopts) || porcelain)\n \t\tline_terminator = 0;\n \n \tif (require_work_tree && !is_inside_work_tree())\n@@ -737,13 +747,15 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tif (show_resolve_undo)\n \t\tshow_ru_info();\n \n-\tif (column_active(colopts)) {\n-\t\tstruct column_options copts;\n-\t\tmemset(&copts, 0, sizeof(copts));\n-\t\tcopts.padding = 2;\n-\t\tprint_columns(&output, colopts, &copts);\n-\t\tstring_list_clear(&output, 0);\n+\tmemset(&copts, 0, sizeof(copts));\n+\tcopts.padding = 2;\n+\tif (porcelain) {\n+\t\tqsort(output.items, output.nr, sizeof(*output.items),\n+\t\t      compare_output);\n+\t\tstring_list_remove_duplicates(&output, 0);\n \t}\n+\tprint_columns(&output, colopts, &copts);\n+\tstring_list_clear(&output, 0);\n \n \tif (ps_matched) {\n \t\tint bad;\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238097","messageId":"1396187769-30863-16-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 15/18] list-files: do not show duplicate cached entries","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:56:06Z","receivedAt":"2014-03-30T13:56:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"With the current show_files() \"list-files -tcm\" will show\n\n  foo.c\nM foo.c\n\nThe first item is redundant. If \"foo.c\" is modified, we know it's in\nthe cache. Introduce show_files_compact to do that because ls-files is\nplumbing and scripts may already depend on current display behavior.\n\nAnother difference in show_files_compact() is it does not show\nskip-worktree (aka outside sparse checkout) entries anymore, which\nmakes sense in porcelain context.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 51 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex ff2377f..9a8f687 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -333,6 +333,53 @@ static void show_files(struct dir_struct *dir)\n \t}\n }\n \n+static void show_files_compact(struct dir_struct *dir)\n+{\n+\tint i;\n+\n+\t/* For cached/deleted files we don't need to even do the readdir */\n+\tif (show_others || show_killed) {\n+\t\tif (!show_others)\n+\t\t\tdir->flags |= DIR_COLLECT_KILLED_ONLY;\n+\t\tfill_directory(dir, &pathspec);\n+\t\tif (show_others)\n+\t\t\tshow_other_files(dir);\n+\t\tif (show_killed)\n+\t\t\tshow_killed_files(dir);\n+\t}\n+\tif (!(show_cached || show_stage || show_deleted || show_modified))\n+\t\treturn;\n+\tfor (i = 0; i < active_nr; i++) {\n+\t\tconst struct cache_entry *ce = active_cache[i];\n+\t\tstruct stat st;\n+\t\tint err, shown = 0;\n+\t\tif ((dir->flags & DIR_SHOW_IGNORED) &&\n+\t\t    !ce_excluded(dir, ce))\n+\t\t\tcontinue;\n+\t\tif (show_unmerged && !ce_stage(ce))\n+\t\t\tcontinue;\n+\t\tif (ce->ce_flags & CE_UPDATE)\n+\t\t\tcontinue;\n+\t\tif (ce_skip_worktree(ce))\n+\t\t\tcontinue;\n+\t\terr = lstat(ce->name, &st);\n+\t\tif (show_deleted && err) {\n+\t\t\tshow_ce_entry(tag_removed, ce);\n+\t\t\tshown = 1;\n+\t\t}\n+\t\tif (show_modified && (err || ce_modified(ce, &st, 0))) {\n+\t\t\tshow_ce_entry(tag_modified, ce);\n+\t\t\tshown = 1;\n+\t\t}\n+\t\tif (ce_stage(ce)) {\n+\t\t\tshow_ce_entry(tag_unmerged, ce);\n+\t\t\tshown = 1;\n+\t\t}\n+\t\tif (!shown && show_cached)\n+\t\t\tshow_ce_entry(tag_cached, ce);\n+\t}\n+}\n+\n /*\n  * Prune the index to only contain stuff starting with \"prefix\"\n  */\n@@ -743,7 +790,10 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\trefresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL);\n \t\tsetup_pager();\n \t}\n-\tshow_files(&dir);\n+\tif (porcelain)\n+\t\tshow_files_compact(&dir);\n+\telse\n+\t\tshow_files(&dir);\n \tif (show_resolve_undo)\n \t\tshow_ru_info();\n \n-- \n1.9.1.345.ga1a145c\n"},{"id":"238098","messageId":"1396187769-30863-17-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 16/18] list-files: show directories as well as files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:56:07Z","receivedAt":"2014-03-30T13:56:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The index does not store directories explicitly (except submodules) so\nwe have to figure them out from file list. The function\nshow_directories() deliberately generates duplicate directories and\nexpects the previous patch to remove duplicates.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/ls-files.c | 44 +++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 43 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 9a8f687..9dc1c39 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -27,6 +27,8 @@ static int show_resolve_undo;\n static int show_modified;\n static int show_killed;\n static int show_valid_bit;\n+static int show_tag;\n+static int show_dirs;\n static int line_terminator = '\\n';\n static int debug_mode;\n static int use_color;\n@@ -333,6 +335,43 @@ static void show_files(struct dir_struct *dir)\n \t}\n }\n \n+static void show_directories(const struct cache_entry *ce)\n+{\n+\tstatic const char *last_directory;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char *p = ce->name + prefix_len;\n+\tconst char *sep;\n+\n+\tif (last_directory) {\n+\t\tint len = strlen(last_directory);\n+\t\tif (!strncmp(ce->name, last_directory, len) &&\n+\t\t    ce->name[len] == '/')\n+\t\t\tp += len + 1;\n+\t}\n+\n+\twhile (*p && (sep = strchr(p, '/'))) {\n+\t\tstruct strbuf sb2 = STRBUF_INIT;\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_add(&sb, ce->name, sep - ce->name);\n+\t\tp = sep + 1;\n+\t\tif (!match_pathspec(&pathspec, sb.buf, sb.len,\n+\t\t\t\t    prefix_len, NULL, 1))\n+\t\t\tcontinue;\n+\t\twrite_name(&sb2, sb.buf);\n+\t\tif (want_color(use_color)) {\n+\t\t\tstruct strbuf sb3 = STRBUF_INIT;\n+\t\t\tcolor_filename(&sb3, ce->name, sb2.buf, S_IFDIR, 1);\n+\t\t\tstrbuf_release(&sb2);\n+\t\t\tsb2 = sb3;\n+\t\t}\n+\t\tif (show_tag)\n+\t\t\tstrbuf_insert(&sb2, 0, tag_cached, strlen(tag_cached));\n+\t\tlast_directory = strbuf_detach(&sb, NULL);\n+\t\tstrbuf_fputs(&sb2, last_directory, NULL);\n+\t\tstrbuf_release(&sb2);\n+\t}\n+}\n+\n static void show_files_compact(struct dir_struct *dir)\n {\n \tint i;\n@@ -353,6 +392,8 @@ static void show_files_compact(struct dir_struct *dir)\n \t\tconst struct cache_entry *ce = active_cache[i];\n \t\tstruct stat st;\n \t\tint err, shown = 0;\n+\t\tif (show_dirs)\n+\t\t\tshow_directories(ce);\n \t\tif ((dir->flags & DIR_SHOW_IGNORED) &&\n \t\t    !ce_excluded(dir, ce))\n \t\t\tcontinue;\n@@ -575,7 +616,7 @@ static int git_ls_config(const char *var, const char *value, void *cb)\n \n int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n {\n-\tint require_work_tree = 0, show_tag = 0, i;\n+\tint require_work_tree = 0, i;\n \tint max_depth = -1;\n \tconst char *max_prefix;\n \tstruct dir_struct dir;\n@@ -696,6 +737,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\tuse_color = -1;\n \t\tmax_depth = 0;\n \t\tshow_tag = -1;\n+\t\tshow_dirs = 1;\n \t\tgit_config(git_ls_config, NULL);\n \t} else\n \t\tgit_config(git_default_config, NULL);\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238099","messageId":"1396187769-30863-18-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 17/18] list-files: add -F/--classify","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:56:08Z","receivedAt":"2014-03-30T13:56:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This appends an indicator after the file name if it's executable, a\ndirectory and so on, like in GNU ls. In fact append_indicator() is a\nrewrite from get_type_indicator() in coreutils.git commit\n7326d1f1a67edf21947ae98194f98c38b6e9e527.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-list-files.txt |  6 ++++++\n builtin/ls-files.c               | 31 +++++++++++++++++++++++++++++++\n 2 files changed, 37 insertions(+)\n\ndiff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt\nindex 0ef616b..22084eb 100644\n--- a/Documentation/git-list-files.txt\n+++ b/Documentation/git-list-files.txt\n@@ -51,6 +51,12 @@ OPTIONS\n \tmultiple file selections. See linkgit::git-ls-files[1] option\n \t`-t` for more information.\n \n+-F::\n+--classify::\n+\tAppend indicator (one of `*/=>@|`, which is executable,\n+\tdirectory, socket, Solaris door, symlink, or fifo\n+\trespectively) to entries.\n+\n -R::\n --recursive::\n \tEquivalent of `--max-depth=-1` (infinite recursion).\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 9dc1c39..2d475f0 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -29,6 +29,7 @@ static int show_killed;\n static int show_valid_bit;\n static int show_tag;\n static int show_dirs;\n+static int show_indicator;\n static int line_terminator = '\\n';\n static int debug_mode;\n static int use_color;\n@@ -77,6 +78,28 @@ static void write_name(struct strbuf *sb, const char *name)\n \t\tquote_path_relative(name, real_prefix, sb);\n }\n \n+static void append_indicator(struct strbuf *sb, mode_t mode)\n+{\n+\tchar c = 0;\n+\tif (S_ISREG(mode)) {\n+\t\tif (mode & (S_IXUSR | S_IXGRP | S_IXOTH))\n+\t\t\tc = '*';\n+\t} else if (S_ISDIR(mode))\n+\t\tc = '/';\n+\telse if (S_ISLNK(mode))\n+\t\tc = '@';\n+\telse if (S_ISFIFO(mode))\n+\t\tc = '|';\n+\telse if (S_ISSOCK(mode))\n+\t\tc = '=';\n+#ifdef S_ISDOOR\n+\telse if (S_ISDOOR(mode))\n+\t\tc = '>';\n+#endif\n+\tif (c)\n+\t\tstrbuf_addch(sb, c);\n+}\n+\n static void strbuf_fputs(struct strbuf *sb, const char *full_name, FILE *fp)\n {\n \tif (column_active(colopts) || porcelain) {\n@@ -99,6 +122,8 @@ static void write_dir_entry(struct strbuf *sb, const struct dir_entry *ent)\n \t\tcolor_filename(sb, ent->name, quoted.buf, st.st_mode, 1);\n \telse\n \t\tstrbuf_addbuf(sb, &quoted);\n+\tif (show_indicator && st.st_mode)\n+\t\tappend_indicator(sb, st.st_mode);\n \tstrbuf_addch(sb, line_terminator);\n \tstrbuf_release(&quoted);\n }\n@@ -189,6 +214,8 @@ static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)\n \t\tcolor_filename(sb, ce->name, quoted.buf, ce->ce_mode, 1);\n \telse\n \t\tstrbuf_addbuf(sb, &quoted);\n+\tif (show_indicator)\n+\t\tappend_indicator(sb, ce->ce_mode);\n \tstrbuf_addch(sb, line_terminator);\n \tstrbuf_release(&quoted);\n }\n@@ -366,6 +393,8 @@ static void show_directories(const struct cache_entry *ce)\n \t\t}\n \t\tif (show_tag)\n \t\t\tstrbuf_insert(&sb2, 0, tag_cached, strlen(tag_cached));\n+\t\tif (show_indicator)\n+\t\t\tappend_indicator(&sb2, S_IFDIR);\n \t\tlast_directory = strbuf_detach(&sb, NULL);\n \t\tstrbuf_fputs(&sb2, last_directory, NULL);\n \t\tstrbuf_release(&sb2);\n@@ -701,6 +730,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\t\tDIR_SHOW_IGNORED),\n \t\tOPT_BOOL('u', \"unmerged\", &show_unmerged,\n \t\t\tN_(\"show unmerged files\")),\n+\t\tOPT_BOOL('F', \"classify\", &show_indicator,\n+\t\t\t N_(\"append indicator (one of */=>@|) to entries\")),\n \t\tOPT__COLOR(&use_color, N_(\"show color\")),\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show files in columns\")),\n \t\tOPT_SET_INT('1', NULL, &colopts,\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238100","messageId":"1396187769-30863-19-git-send-email-pclouds@gmail.com","threadId":"36247","inReplyTo":"1396187769-30863-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 18/18] list-files -F: show submodules with the new indicator '&'","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-30T13:56:09Z","receivedAt":"2014-03-30T13:56:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-list-files.txt | 4 ++--\n builtin/ls-files.c               | 2 ++\n 2 files changed, 4 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt\nindex 22084eb..c57129b 100644\n--- a/Documentation/git-list-files.txt\n+++ b/Documentation/git-list-files.txt\n@@ -53,8 +53,8 @@ OPTIONS\n \n -F::\n --classify::\n-\tAppend indicator (one of `*/=>@|`, which is executable,\n-\tdirectory, socket, Solaris door, symlink, or fifo\n+\tAppend indicator (one of `*/=>@|&`, which is executable,\n+\tdirectory, socket, Solaris door, symlink, fifo, or submodule\n \trespectively) to entries.\n \n -R::\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 2d475f0..f3e34db 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -92,6 +92,8 @@ static void append_indicator(struct strbuf *sb, mode_t mode)\n \t\tc = '|';\n \telse if (S_ISSOCK(mode))\n \t\tc = '=';\n+\telse if (S_ISGITLINK(mode))\n+\t\tc = '&';\n #ifdef S_ISDOOR\n \telse if (S_ISDOOR(mode))\n \t\tc = '>';\n-- \n1.9.1.345.ga1a145c\n"},{"id":"238335","messageId":"xmqq38hvvbr7.fsf@gitster.dls.corp.google.com","threadId":"36247","inReplyTo":"1396012689-22480-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH] ls-files: do not trust stat info if lstat() fails","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-04-02T18:15:24Z","receivedAt":"2014-04-02T18:15:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n\n> If 'err' is non-zero, lstat() has failed. Consider the entry modified\n> without passing the (unreliable) stat info to ce_modified() in this\n> case.\n>\n> Noticed-by: Eric Sunshine <sunshine@sunshineco.com>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  On Fri, Mar 28, 2014 at 11:04 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n>  > On Wed, Mar 26, 2014 at 9:48 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>  >> +               err = lstat(ce->name, &st);\n>  >> +               if (show_deleted && err) {\n>  >> +                       show_ce_entry(tag_removed, ce);\n>  >> +                       shown = 1;\n>  >> +               }\n>  >> +               if (show_modified && ce_modified(ce, &st, 0)) {\n>  >\n>  > Is it possible for the lstat() to have failed for some reason when we\n>  > get here? If so, relying upon 'st' is unsafe, isn't it?\n>\n>  The chance of random stat making ce_modified() return false is pretty\n>  low, but you're right. This code is a copy from the old show_files().\n>  I'll fix it in the git-ls series. Meanwhile a patch for maint to fix\n>  the original function.\n>\n>  builtin/ls-files.c | 2 +-\n>  1 file changed, 1 insertion(+), 1 deletion(-)\n>\n> diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> index 47c3880..e6bd00e 100644\n> --- a/builtin/ls-files.c\n> +++ b/builtin/ls-files.c\n> @@ -260,7 +260,7 @@ static void show_files(struct dir_struct *dir)\n>  \t\t\terr = lstat(ce->name, &st);\n>  \t\t\tif (show_deleted && err)\n>  \t\t\t\tshow_ce_entry(tag_removed, ce);\n> -\t\t\tif (show_modified && ce_modified(ce, &st, 0))\n> +\t\t\tif (show_modified && (err || ce_modified(ce, &st, 0)))\n>  \t\t\t\tshow_ce_entry(tag_modified, ce);\n>  \t\t}\n>  \t}\n\nI am guessing that, even though this was discovered during the\ndevelopment of list-files, is a fix applicable outside the context\nof that series.\n\nI do think the patched result is an improvement than the status quo,\nbut at the same time, I find it insufficient in the context of the\nwhole codepath.  What if errno were other than ENOENT and we were\ntold to show_deleted (with or without show_modified)?  We would end\nup saying the path was deleted and modified at the same time, when\nwe do not know either is or is not true at all, because of the\nfailure to lstat() the path.\n\nWouldn't it be saner to add tag_unknown and do something like this\ninstead, I wonder?\n\n builtin/ls-files.c | 16 +++++++++++-----\n 1 file changed, 11 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 47c3880..af2ce99 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -46,6 +46,7 @@ static const char *tag_killed = \"\";\n static const char *tag_modified = \"\";\n static const char *tag_skip_worktree = \"\";\n static const char *tag_resolve_undo = \"\";\n+static const char *tag_unknown = \"\";\n \n static void write_name(const char *name)\n {\n@@ -249,7 +250,7 @@ static void show_files(struct dir_struct *dir)\n \t\tfor (i = 0; i < active_nr; i++) {\n \t\t\tconst struct cache_entry *ce = active_cache[i];\n \t\t\tstruct stat st;\n-\t\t\tint err;\n+\n \t\t\tif ((dir->flags & DIR_SHOW_IGNORED) &&\n \t\t\t    !ce_excluded(dir, ce))\n \t\t\t\tcontinue;\n@@ -257,11 +258,15 @@ static void show_files(struct dir_struct *dir)\n \t\t\t\tcontinue;\n \t\t\tif (ce_skip_worktree(ce))\n \t\t\t\tcontinue;\n-\t\t\terr = lstat(ce->name, &st);\n-\t\t\tif (show_deleted && err)\n-\t\t\t\tshow_ce_entry(tag_removed, ce);\n-\t\t\tif (show_modified && ce_modified(ce, &st, 0))\n+\t\t\terrno = 0;\n+\t\t\tif (lstat(ce->name, &st)) {\n+\t\t\t\tif (errno != ENOENT && errno != ENOTDIR)\n+\t\t\t\t\tshow_ce_entry(tag_unknown, ce);\n+\t\t\t\telse if (show_deleted)\n+\t\t\t\t\tshow_ce_entry(tag_removed, ce);\n+\t\t\t} else if (show_modified && ce_modified(ce, &st, 0)) {\n \t\t\t\tshow_ce_entry(tag_modified, ce);\n+\t\t\t}\n \t\t}\n \t}\n }\n@@ -533,6 +538,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \t\ttag_killed = \"K \";\n \t\ttag_skip_worktree = \"S \";\n \t\ttag_resolve_undo = \"U \";\n+\t\ttag_unknown = \"! \";\n \t}\n \tif (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed)\n \t\trequire_work_tree = 1;\n"},{"id":"238345","messageId":"CACsJy8DBixHTeqitcAcQRAqY3b4prkwQGgJGuw7hbT7BQAZt9w@mail.gmail.com","threadId":"36247","inReplyTo":"xmqq38hvvbr7.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH] ls-files: do not trust stat info if lstat() fails","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-04-03T12:40:24Z","receivedAt":"2014-04-03T12:40:24Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Apr 3, 2014 at 1:15 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> I am guessing that, even though this was discovered during the\n> development of list-files, is a fix applicable outside the context\n> of that series.\n>\n> I do think the patched result is an improvement than the status quo,\n> but at the same time, I find it insufficient in the context of the\n> whole codepath.  What if errno were other than ENOENT and we were\n> told to show_deleted (with or without show_modified)?  We would end\n> up saying the path was deleted and modified at the same time, when\n> we do not know either is or is not true at all, because of the\n> failure to lstat() the path.\n>\n> Wouldn't it be saner to add tag_unknown and do something like this\n> instead, I wonder?\n\nOr even better to show an error message when the error code is\nunexpected? The unkown tag '!' says \"there are problems\" but if it\nshows up sort of permanently, '!' won't help much, I think.\n-- \nDuy\n"},{"id":"238354","messageId":"xmqqvbuqqstn.fsf@gitster.dls.corp.google.com","threadId":"36247","inReplyTo":"CACsJy8DBixHTeqitcAcQRAqY3b4prkwQGgJGuw7hbT7BQAZt9w@mail.gmail.com","subject":"Re: [PATCH] ls-files: do not trust stat info if lstat() fails","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-04-03T16:30:12Z","receivedAt":"2014-04-03T16:30:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> On Thu, Apr 3, 2014 at 1:15 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> I am guessing that, even though this was discovered during the\n>> development of list-files, is a fix applicable outside the context\n>> of that series.\n>>\n>> I do think the patched result is an improvement than the status quo,\n>> but at the same time, I find it insufficient in the context of the\n>> whole codepath.  What if errno were other than ENOENT and we were\n>> told to show_deleted (with or without show_modified)?  We would end\n>> up saying the path was deleted and modified at the same time, when\n>> we do not know either is or is not true at all, because of the\n>> failure to lstat() the path.\n>>\n>> Wouldn't it be saner to add tag_unknown and do something like this\n>> instead, I wonder?\n>\n> Or even better to show an error message when the error code is\n> unexpected? The unkown tag '!' says \"there are problems\" but if it\n> shows up sort of permanently, '!' won't help much, I think.\n\nI am OK with that approach, but then one question remains: should we\nsay it is deleted, modified, both, or neither?\n"},{"id":"238387","messageId":"CACsJy8CTv5jZKjqq6fAJeVj-sqJHAF761V6TofVWs-V5GPnbcA@mail.gmail.com","threadId":"36247","inReplyTo":"xmqqvbuqqstn.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH] ls-files: do not trust stat info if lstat() fails","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-04-05T08:03:31Z","receivedAt":"2014-04-05T08:03:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Apr 3, 2014 at 11:30 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Duy Nguyen <pclouds@gmail.com> writes:\n>\n>> On Thu, Apr 3, 2014 at 1:15 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>> I am guessing that, even though this was discovered during the\n>>> development of list-files, is a fix applicable outside the context\n>>> of that series.\n>>>\n>>> I do think the patched result is an improvement than the status quo,\n>>> but at the same time, I find it insufficient in the context of the\n>>> whole codepath.  What if errno were other than ENOENT and we were\n>>> told to show_deleted (with or without show_modified)?  We would end\n>>> up saying the path was deleted and modified at the same time, when\n>>> we do not know either is or is not true at all, because of the\n>>> failure to lstat() the path.\n>>>\n>>> Wouldn't it be saner to add tag_unknown and do something like this\n>>> instead, I wonder?\n>>\n>> Or even better to show an error message when the error code is\n>> unexpected? The unkown tag '!' says \"there are problems\" but if it\n>> shows up sort of permanently, '!' won't help much, I think.\n>\n> I am OK with that approach, but then one question remains: should we\n> say it is deleted, modified, both, or neither?\n\nThe question is moot if the user does not ignore stderr because they\nshould just ignore those error-reported entries. If they do\n2>/dev/null, I think we should err on the safe side and say modified.\nWe only say deleted if lstat() returns ENOENT or ENOTDIR like in your\npatch.\n-- \nDuy\n"},{"id":"238474","messageId":"xmqqzjjxm5ac.fsf@gitster.dls.corp.google.com","threadId":"36247","inReplyTo":"CACsJy8CTv5jZKjqq6fAJeVj-sqJHAF761V6TofVWs-V5GPnbcA@mail.gmail.com","subject":"Re: [PATCH] ls-files: do not trust stat info if lstat() fails","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-04-07T17:13:31Z","receivedAt":"2014-04-07T17:13:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n>>> Or even better to show an error message when the error code is\n>>> unexpected? The unkown tag '!' says \"there are problems\" but if it\n>>> shows up sort of permanently, '!' won't help much, I think.\n>>\n>> I am OK with that approach, but then one question remains: should we\n>> say it is deleted, modified, both, or neither?\n>\n> The question is moot if the user does not ignore stderr because they\n> should just ignore those error-reported entries. If they do\n> 2>/dev/null, I think we should err on the safe side and say modified.\n> We only say deleted if lstat() returns ENOENT or ENOTDIR like in your\n> patch.\n\nDoesn't the same reasoning behind \"when we do not know for sure that\na path is not modified, it would be safe if we said the path may be\nmodified\" also tell us that it is safer to say a path may be lost if\nwe cannot tell?\n\nOne likely case where we cannot tell if it is modified would be when\nwe cannot read the path (perhaps the parent directory accidentally\nlost its x-bit).  Saying \"it may be modified\" would be one way to\nhave the user take notice, for an interactive user.  A script that\nruns ls-files may be using the paths to drive \"git add\", \"tar cf -\",\netc. and emitting such an unreadable path is one way to make these\ndownstream commands signal that something fishy is going on by\nerroring out.\n\nSo, I am not sure if we should be silent on an unexpected error when\nwe are asked to report deletes when we would be vocal when we are\nasked to report modifications.\n"}]}