{"thread":{"id":"11000","subject":"[PATCH 1/2] builtin-apply: rename \"whitespace\" variables and fix styles","startedAt":"2007-11-24T04:24:52Z","lastAt":"2007-12-18T00:51:02Z","messageCount":43,"participants":["Junio C Hamano","J. Bruce Fields","Jakub Narebski","Wincent Colaiuta"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"60799","messageId":"7v8x4os223.fsf@gitster.siamese.dyndns.org","threadId":"11000","inReplyTo":null,"subject":"[PATCH 1/2] builtin-apply: rename \"whitespace\" variables and fix styles","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-24T04:24:52Z","receivedAt":"2007-11-24T04:24:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The variables were somewhat misnamed.\n\n * \"What to do when whitespace errors are detected\" is now called\n   \"ws_error_action\" (used to be called \"new_whitespace\");\n\n * The constants to denote the possible actions are \"nowarn_ws_error\",\n   \"warn_on_ws_error\", \"die_on_ws_error\", and \"correct_ws_error\".  The\n   last one used to be \"strip_whitespace\", but we correct whitespace\n   error in indent (SP followed by HT) and \"strip\" is not quite an\n   accurate name for it.\n\nOther than the renaming of variables and constants, there is no\nfunctional change in this patch.  While we are at it, it also fixes\noverly long lines and multi-line comment styles (which of course do\nnot affect the generated code at all).\n\nAh, by the way, this introduces a synonym \"fix\" that is equivalent to\n\"strip\" that you can give to --whitespace=<what-to-do>, so in that sense\nit is not exactly \"no functional change whatsoever\".\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * This is on top of jc/spht topic that is in 'next'.  The next one is\n   the real thing.\n\n builtin-apply.c |  148 ++++++++++++++++++++++++++++++++++---------------------\n 1 files changed, 91 insertions(+), 57 deletions(-)\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex 8411b38..eb09bfe 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -47,12 +47,12 @@ static unsigned long p_context = ULONG_MAX;\n static const char apply_usage[] =\n \"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...\";\n \n-static enum whitespace_eol {\n-\tnowarn_whitespace,\n-\twarn_on_whitespace,\n-\terror_on_whitespace,\n-\tstrip_whitespace,\n-} new_whitespace = warn_on_whitespace;\n+static enum ws_error_action {\n+\tnowarn_ws_error,\n+\twarn_on_ws_error,\n+\tdie_on_ws_error,\n+\tcorrect_ws_error,\n+} ws_error_action = warn_on_ws_error;\n static int whitespace_error;\n static int squelch_whitespace_errors = 5;\n static int applied_after_fixing_ws;\n@@ -61,28 +61,28 @@ static const char *patch_input_file;\n static void parse_whitespace_option(const char *option)\n {\n \tif (!option) {\n-\t\tnew_whitespace = warn_on_whitespace;\n+\t\tws_error_action = warn_on_ws_error;\n \t\treturn;\n \t}\n \tif (!strcmp(option, \"warn\")) {\n-\t\tnew_whitespace = warn_on_whitespace;\n+\t\tws_error_action = warn_on_ws_error;\n \t\treturn;\n \t}\n \tif (!strcmp(option, \"nowarn\")) {\n-\t\tnew_whitespace = nowarn_whitespace;\n+\t\tws_error_action = nowarn_ws_error;\n \t\treturn;\n \t}\n \tif (!strcmp(option, \"error\")) {\n-\t\tnew_whitespace = error_on_whitespace;\n+\t\tws_error_action = die_on_ws_error;\n \t\treturn;\n \t}\n \tif (!strcmp(option, \"error-all\")) {\n-\t\tnew_whitespace = error_on_whitespace;\n+\t\tws_error_action = die_on_ws_error;\n \t\tsquelch_whitespace_errors = 0;\n \t\treturn;\n \t}\n-\tif (!strcmp(option, \"strip\")) {\n-\t\tnew_whitespace = strip_whitespace;\n+\tif (!strcmp(option, \"strip\") || !strcmp(option, \"fix\")) {\n+\t\tws_error_action = correct_ws_error;\n \t\treturn;\n \t}\n \tdie(\"unrecognized whitespace option '%s'\", option);\n@@ -90,11 +90,8 @@ static void parse_whitespace_option(const char *option)\n \n static void set_default_whitespace_mode(const char *whitespace_option)\n {\n-\tif (!whitespace_option && !apply_default_whitespace) {\n-\t\tnew_whitespace = (apply\n-\t\t\t\t  ? warn_on_whitespace\n-\t\t\t\t  : nowarn_whitespace);\n-\t}\n+\tif (!whitespace_option && !apply_default_whitespace)\n+\t\tws_error_action = (apply ? warn_on_ws_error : nowarn_ws_error);\n }\n \n /*\n@@ -137,6 +134,11 @@ struct fragment {\n #define BINARY_DELTA_DEFLATED\t1\n #define BINARY_LITERAL_DEFLATED 2\n \n+/*\n+ * This represents a \"patch\" to a file, both metainfo changes\n+ * such as creation/deletion, filemode and content changes represented\n+ * as a series of fragments.\n+ */\n struct patch {\n \tchar *new_name, *old_name, *def_name;\n \tunsigned int old_mode, new_mode;\n@@ -158,7 +160,8 @@ struct patch {\n \tstruct patch *next;\n };\n \n-static void say_patch_name(FILE *output, const char *pre, struct patch *patch, const char *post)\n+static void say_patch_name(FILE *output, const char *pre,\n+\t\t\t   struct patch *patch, const char *post)\n {\n \tfputs(pre, output);\n \tif (patch->old_name && patch->new_name &&\n@@ -229,7 +232,8 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)\n \tif (*line == '\"') {\n \t\tstruct strbuf name;\n \n-\t\t/* Proposed \"new-style\" GNU patch/diff format; see\n+\t\t/*\n+\t\t * Proposed \"new-style\" GNU patch/diff format; see\n \t\t * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2\n \t\t */\n \t\tstrbuf_init(&name, 0);\n@@ -499,7 +503,8 @@ static int gitdiff_dissimilarity(const char *line, struct patch *patch)\n \n static int gitdiff_index(const char *line, struct patch *patch)\n {\n-\t/* index line is N hexadecimal, \"..\", N hexadecimal,\n+\t/*\n+\t * index line is N hexadecimal, \"..\", N hexadecimal,\n \t * and optional space with octal mode.\n \t */\n \tconst char *ptr, *eol;\n@@ -550,7 +555,8 @@ static const char *stop_at_slash(const char *line, int llen)\n \treturn NULL;\n }\n \n-/* This is to extract the same name that appears on \"diff --git\"\n+/*\n+ * This is to extract the same name that appears on \"diff --git\"\n  * line.  We do not find and return anything if it is a rename\n  * patch, and it is OK because we will find the name elsewhere.\n  * We need to reliably find name only when it is mode-change only,\n@@ -584,7 +590,8 @@ static char *git_header_name(char *line, int llen)\n \t\t\tgoto free_and_fail1;\n \t\tstrbuf_remove(&first, 0, cp + 1 - first.buf);\n \n-\t\t/* second points at one past closing dq of name.\n+\t\t/*\n+\t\t * second points at one past closing dq of name.\n \t\t * find the second name.\n \t\t */\n \t\twhile ((second < line + llen) && isspace(*second))\n@@ -627,7 +634,8 @@ static char *git_header_name(char *line, int llen)\n \t\treturn NULL;\n \tname++;\n \n-\t/* since the first name is unquoted, a dq if exists must be\n+\t/*\n+\t * since the first name is unquoted, a dq if exists must be\n \t * the beginning of the second name.\n \t */\n \tfor (second = name; second < line + llen; second++) {\n@@ -759,7 +767,7 @@ static int parse_num(const char *line, unsigned long *p)\n }\n \n static int parse_range(const char *line, int len, int offset, const char *expect,\n-\t\t\tunsigned long *p1, unsigned long *p2)\n+\t\t       unsigned long *p1, unsigned long *p2)\n {\n \tint digits, ex;\n \n@@ -868,14 +876,14 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc\n \t\t\treturn offset;\n \t\t}\n \n-\t\t/** --- followed by +++ ? */\n+\t\t/* --- followed by +++ ? */\n \t\tif (memcmp(\"--- \", line,  4) || memcmp(\"+++ \", line + len, 4))\n \t\t\tcontinue;\n \n \t\t/*\n \t\t * We only accept unified patches, so we want it to\n \t\t * at least have \"@@ -a,b +c,d @@\\n\", which is 14 chars\n-\t\t * minimum\n+\t\t * minimum (\"@@ -0,0 +1 @@\\n\" is the shortest).\n \t\t */\n \t\tnextlen = linelen(line + len, size - len);\n \t\tif (size < nextlen + 14 || memcmp(\"@@ -\", line + len + nextlen, 4))\n@@ -932,14 +940,14 @@ static void check_whitespace(const char *line, int len)\n \t\t\terr, patch_input_file, linenr, len-2, line+1);\n }\n \n-\n /*\n  * Parse a unified diff. Note that this really needs to parse each\n  * fragment separately, since the only way to know the difference\n  * between a \"---\" that is part of a patch, and a \"---\" that starts\n  * the next patch is to look at the line counts..\n  */\n-static int parse_fragment(char *line, unsigned long size, struct patch *patch, struct fragment *fragment)\n+static int parse_fragment(char *line, unsigned long size,\n+\t\t\t  struct patch *patch, struct fragment *fragment)\n {\n \tint added, deleted;\n \tint len = linelen(line, size), offset;\n@@ -980,7 +988,7 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s\n \t\t\tbreak;\n \t\tcase '-':\n \t\t\tif (apply_in_reverse &&\n-\t\t\t\t\tnew_whitespace != nowarn_whitespace)\n+\t\t\t    ws_error_action != nowarn_ws_error)\n \t\t\t\tcheck_whitespace(line, len);\n \t\t\tdeleted++;\n \t\t\toldlines--;\n@@ -988,14 +996,15 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s\n \t\t\tbreak;\n \t\tcase '+':\n \t\t\tif (!apply_in_reverse &&\n-\t\t\t\t\tnew_whitespace != nowarn_whitespace)\n+\t\t\t    ws_error_action != nowarn_ws_error)\n \t\t\t\tcheck_whitespace(line, len);\n \t\t\tadded++;\n \t\t\tnewlines--;\n \t\t\ttrailing = 0;\n \t\t\tbreak;\n \n-                /* We allow \"\\ No newline at end of file\". Depending\n+\t\t/*\n+\t\t * We allow \"\\ No newline at end of file\". Depending\n                  * on locale settings when the patch was produced we\n                  * don't know what this line looks like. The only\n                  * thing we do know is that it begins with \"\\ \".\n@@ -1013,7 +1022,8 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s\n \tfragment->leading = leading;\n \tfragment->trailing = trailing;\n \n-\t/* If a fragment ends with an incomplete line, we failed to include\n+\t/*\n+\t * If a fragment ends with an incomplete line, we failed to include\n \t * it in the above loop because we hit oldlines == newlines == 0\n \t * before seeing it.\n \t */\n@@ -1141,7 +1151,8 @@ static struct fragment *parse_binary_hunk(char **buf_p,\n \t\t\t\t\t  int *status_p,\n \t\t\t\t\t  int *used_p)\n {\n-\t/* Expect a line that begins with binary patch method (\"literal\"\n+\t/*\n+\t * Expect a line that begins with binary patch method (\"literal\"\n \t * or \"delta\"), followed by the length of data before deflating.\n \t * a sequence of 'length-byte' followed by base-85 encoded data\n \t * should follow, terminated by a newline.\n@@ -1190,7 +1201,8 @@ static struct fragment *parse_binary_hunk(char **buf_p,\n \t\t\tsize--;\n \t\t\tbreak;\n \t\t}\n-\t\t/* Minimum line is \"A00000\\n\" which is 7-byte long,\n+\t\t/*\n+\t\t * Minimum line is \"A00000\\n\" which is 7-byte long,\n \t\t * and the line length must be multiple of 5 plus 2.\n \t\t */\n \t\tif ((llen < 7) || (llen-2) % 5)\n@@ -1241,7 +1253,8 @@ static struct fragment *parse_binary_hunk(char **buf_p,\n \n static int parse_binary(char *buffer, unsigned long size, struct patch *patch)\n {\n-\t/* We have read \"GIT binary patch\\n\"; what follows is a line\n+\t/*\n+\t * We have read \"GIT binary patch\\n\"; what follows is a line\n \t * that says the patch method (currently, either \"literal\" or\n \t * \"delta\") and the length of data before deflating; a\n \t * sequence of 'length-byte' followed by base-85 encoded data\n@@ -1271,7 +1284,8 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)\n \tif (reverse)\n \t\tused += used_1;\n \telse if (status) {\n-\t\t/* not having reverse hunk is not an error, but having\n+\t\t/*\n+\t\t * Not having reverse hunk is not an error, but having\n \t\t * a corrupt reverse hunk is.\n \t\t */\n \t\tfree((void*) forward->patch);\n@@ -1292,7 +1306,8 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)\n \tif (offset < 0)\n \t\treturn offset;\n \n-\tpatchsize = parse_single_patch(buffer + offset + hdrsize, size - offset - hdrsize, patch);\n+\tpatchsize = parse_single_patch(buffer + offset + hdrsize,\n+\t\t\t\t       size - offset - hdrsize, patch);\n \n \tif (!patchsize) {\n \t\tstatic const char *binhdr[] = {\n@@ -1368,8 +1383,10 @@ static void reverse_patches(struct patch *p)\n \t}\n }\n \n-static const char pluses[] = \"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\";\n-static const char minuses[]= \"----------------------------------------------------------------------\";\n+static const char pluses[] =\n+\"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\";\n+static const char minuses[]=\n+\"----------------------------------------------------------------------\";\n \n static void show_stats(struct patch *patch)\n {\n@@ -1438,7 +1455,9 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)\n \t}\n }\n \n-static int find_offset(const char *buf, unsigned long size, const char *fragment, unsigned long fragsize, int line, int *lines)\n+static int find_offset(const char *buf, unsigned long size,\n+\t\t       const char *fragment, unsigned long fragsize,\n+\t\t       int line, int *lines)\n {\n \tint i;\n \tunsigned long start, backwards, forwards;\n@@ -1539,7 +1558,8 @@ static void remove_last_line(const char **rbuf, int *rsize)\n \n static int apply_line(char *output, const char *patch, int plen)\n {\n-\t/* plen is number of bytes to be copied from patch,\n+\t/*\n+\t * plen is number of bytes to be copied from patch,\n \t * starting at patch+1 (patch[0] is '+').  Typically\n \t * patch[plen] is '\\n', unless this is the incomplete\n \t * last line.\n@@ -1552,12 +1572,15 @@ static int apply_line(char *output, const char *patch, int plen)\n \tint need_fix_leading_space = 0;\n \tchar *buf;\n \n-\tif ((new_whitespace != strip_whitespace) || !whitespace_error ||\n+\tif ((ws_error_action != correct_ws_error) || !whitespace_error ||\n \t    *patch != '+') {\n \t\tmemcpy(output, patch + 1, plen);\n \t\treturn plen;\n \t}\n \n+\t/*\n+\t * Strip trailing whitespace\n+\t */\n \tif (1 < plen && isspace(patch[plen-1])) {\n \t\tif (patch[plen] == '\\n')\n \t\t\tadd_nl_to_tail = 1;\n@@ -1567,6 +1590,9 @@ static int apply_line(char *output, const char *patch, int plen)\n \t\tfixed = 1;\n \t}\n \n+\t/*\n+\t * Check leading whitespaces (indent)\n+\t */\n \tfor (i = 1; i < plen; i++) {\n \t\tchar ch = patch[i];\n \t\tif (ch == '\\t') {\n@@ -1583,7 +1609,8 @@ static int apply_line(char *output, const char *patch, int plen)\n \tbuf = output;\n \tif (need_fix_leading_space) {\n \t\tint consecutive_spaces = 0;\n-\t\t/* between patch[1..last_tab_in_indent] strip the\n+\t\t/*\n+\t\t * between patch[1..last_tab_in_indent] strip the\n \t\t * funny spaces, updating them to tab as needed.\n \t\t */\n \t\tfor (i = 1; i < last_tab_in_indent; i++, plen--) {\n@@ -1613,7 +1640,8 @@ static int apply_line(char *output, const char *patch, int plen)\n \treturn output + plen - buf;\n }\n \n-static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int inaccurate_eof)\n+static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,\n+\t\t\t      int inaccurate_eof)\n {\n \tint match_beginning, match_end;\n \tconst char *patch = frag->patch;\n@@ -1695,8 +1723,9 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int ina\n \t\tsize -= len;\n \t}\n \n-\tif (inaccurate_eof && oldsize > 0 && old[oldsize - 1] == '\\n' &&\n-\t\t\tnewsize > 0 && new[newsize - 1] == '\\n') {\n+\tif (inaccurate_eof &&\n+\t    oldsize > 0 && old[oldsize - 1] == '\\n' &&\n+\t    newsize > 0 && new[newsize - 1] == '\\n') {\n \t\toldsize--;\n \t\tnewsize--;\n \t}\n@@ -1733,7 +1762,7 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int ina\n \t\tif (match_beginning && offset)\n \t\t\toffset = -1;\n \t\tif (offset >= 0) {\n-\t\t\tif (new_whitespace == strip_whitespace &&\n+\t\t\tif (ws_error_action == correct_ws_error &&\n \t\t\t    (buf->len - oldsize - offset == 0)) /* end of file? */\n \t\t\t\tnewsize -= new_blank_lines_at_end;\n \n@@ -1758,9 +1787,10 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int ina\n \t\t\tmatch_beginning = match_end = 0;\n \t\t\tcontinue;\n \t\t}\n-\t\t/* Reduce the number of context lines\n-\t\t * Reduce both leading and trailing if they are equal\n-\t\t * otherwise just reduce the larger context.\n+\t\t/*\n+\t\t * Reduce the number of context lines; reduce both\n+\t\t * leading and trailing if they are equal otherwise\n+\t\t * just reduce the larger context.\n \t\t */\n \t\tif (leading >= trailing) {\n \t\t\tremove_first_line(&oldlines, &oldsize);\n@@ -1820,7 +1850,8 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)\n \tconst char *name = patch->old_name ? patch->old_name : patch->new_name;\n \tunsigned char sha1[20];\n \n-\t/* For safety, we require patch index line to contain\n+\t/*\n+\t * For safety, we require patch index line to contain\n \t * full 40-byte textual SHA1 for old and new, at least for now.\n \t */\n \tif (strlen(patch->old_sha1_prefix) != 40 ||\n@@ -1831,7 +1862,8 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)\n \t\t\t     \"without full index line\", name);\n \n \tif (patch->old_name) {\n-\t\t/* See if the old one matches what the patch\n+\t\t/*\n+\t\t * See if the old one matches what the patch\n \t\t * applies to.\n \t\t */\n \t\thash_sha1_file(buf->buf, buf->len, blob_type, sha1);\n@@ -1868,7 +1900,8 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)\n \t\t/* XXX read_sha1_file NUL-terminates */\n \t\tstrbuf_attach(buf, result, size, size + 1);\n \t} else {\n-\t\t/* We have verified buf matches the preimage;\n+\t\t/*\n+\t\t * We have verified buf matches the preimage;\n \t\t * apply the patch data to it, which is stored\n \t\t * in the patch->fragments->{patch,size}.\n \t\t */\n@@ -2067,7 +2100,8 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)\n \n \tif (new_name && prev_patch && 0 < prev_patch->is_delete &&\n \t    !strcmp(prev_patch->old_name, new_name))\n-\t\t/* A type-change diff is always split into a patch to\n+\t\t/*\n+\t\t * A type-change diff is always split into a patch to\n \t\t * delete old, immediately followed by a patch to\n \t\t * create new (see diff.c::run_diff()); in such a case\n \t\t * it is Ok that the entry to be deleted by the\n@@ -2671,7 +2705,7 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)\n \t\toffset += nr;\n \t}\n \n-\tif (whitespace_error && (new_whitespace == error_on_whitespace))\n+\tif (whitespace_error && (ws_error_action == die_on_ws_error))\n \t\tapply = 0;\n \n \tupdate_index = check_index && apply;\n@@ -2866,7 +2900,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)\n \t\t\t\tsquelched,\n \t\t\t\tsquelched == 1 ? \"\" : \"s\");\n \t\t}\n-\t\tif (new_whitespace == error_on_whitespace)\n+\t\tif (ws_error_action == die_on_ws_error)\n \t\t\tdie(\"%d line%s add%s whitespace errors.\",\n \t\t\t    whitespace_error,\n \t\t\t    whitespace_error == 1 ? \"\" : \"s\",\n-- \n1.5.3.6.1991.ge56ac\n"},{"id":"60800","messageId":"7v4pfcs20b.fsf@gitster.siamese.dyndns.org","threadId":"11000","inReplyTo":"7v8x4os223.fsf@gitster.siamese.dyndns.org","subject":"[PATCH 2/2] builtin-apply: teach whitespace_rules","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-24T04:25:56Z","receivedAt":"2007-11-24T04:25:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"We earlier introduced core.whitespace to allow users to tweak the\ndefinition of what the \"whitespace errors\" are, for the purpose of diff\noutput highlighting.  This teaches the same to git-apply, so that the\ncommand can both detect (when --whitespace=warn option is given) and fix\n(when --whitespace=fix option is given) as configured.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-apply.c          |   68 +++++++++++++++++-------\n t/t4124-apply-ws-rule.sh |  133 ++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 182 insertions(+), 19 deletions(-)\n create mode 100755 t/t4124-apply-ws-rule.sh\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex eb09bfe..e04b493 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -910,23 +910,35 @@ static void check_whitespace(const char *line, int len)\n \t * this function.  That is, an addition of an empty line would\n \t * check the '+' here.  Sneaky...\n \t */\n-\tif (isspace(line[len-2]))\n+\tif ((whitespace_rule & WS_TRAILING_SPACE) && isspace(line[len-2]))\n \t\tgoto error;\n \n \t/*\n \t * Make sure that there is no space followed by a tab in\n \t * indentation.\n \t */\n-\terr = \"Space in indent is followed by a tab\";\n-\tfor (i = 1; i < len; i++) {\n-\t\tif (line[i] == '\\t') {\n-\t\t\tif (seen_space)\n-\t\t\t\tgoto error;\n-\t\t}\n-\t\telse if (line[i] == ' ')\n-\t\t\tseen_space = 1;\n-\t\telse\n-\t\t\tbreak;\n+\tif (whitespace_rule & WS_SPACE_BEFORE_TAB) {\n+\t\terr = \"Space in indent is followed by a tab\";\n+\t\tfor (i = 1; i < len; i++) {\n+\t\t\tif (line[i] == '\\t') {\n+\t\t\t\tif (seen_space)\n+\t\t\t\t\tgoto error;\n+\t\t\t}\n+\t\t\telse if (line[i] == ' ')\n+\t\t\t\tseen_space = 1;\n+\t\t\telse\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * Make sure that the indentation does not contain more than\n+\t * 8 spaces.\n+\t */\n+\tif ((whitespace_rule & WS_INDENT_WITH_NON_TAB) &&\n+\t    (8 < len) && !strncmp(\"+        \", line, 9)) {\n+\t\terr = \"Indent more than 8 places with spaces\";\n+\t\tgoto error;\n \t}\n \treturn;\n \n@@ -1581,7 +1593,8 @@ static int apply_line(char *output, const char *patch, int plen)\n \t/*\n \t * Strip trailing whitespace\n \t */\n-\tif (1 < plen && isspace(patch[plen-1])) {\n+\tif ((whitespace_rule & WS_TRAILING_SPACE) &&\n+\t    (1 < plen && isspace(patch[plen-1]))) {\n \t\tif (patch[plen] == '\\n')\n \t\t\tadd_nl_to_tail = 1;\n \t\tplen--;\n@@ -1597,11 +1610,16 @@ static int apply_line(char *output, const char *patch, int plen)\n \t\tchar ch = patch[i];\n \t\tif (ch == '\\t') {\n \t\t\tlast_tab_in_indent = i;\n-\t\t\tif (0 <= last_space_in_indent)\n+\t\t\tif ((whitespace_rule & WS_SPACE_BEFORE_TAB) &&\n+\t\t\t    0 <= last_space_in_indent)\n+\t\t\t    need_fix_leading_space = 1;\n+\t\t} else if (ch == ' ') {\n+\t\t\tlast_space_in_indent = i;\n+\t\t\tif ((whitespace_rule & WS_INDENT_WITH_NON_TAB) &&\n+\t\t\t    last_tab_in_indent < 0 &&\n+\t\t\t    8 <= i)\n \t\t\t\tneed_fix_leading_space = 1;\n \t\t}\n-\t\telse if (ch == ' ')\n-\t\t\tlast_space_in_indent = i;\n \t\telse\n \t\t\tbreak;\n \t}\n@@ -1609,11 +1627,21 @@ static int apply_line(char *output, const char *patch, int plen)\n \tbuf = output;\n \tif (need_fix_leading_space) {\n \t\tint consecutive_spaces = 0;\n+\t\tint last = last_tab_in_indent + 1;\n+\n+\t\tif (whitespace_rule & WS_INDENT_WITH_NON_TAB) {\n+\t\t\t/* have \"last\" point at one past the indent */\n+\t\t\tif (last_tab_in_indent < last_space_in_indent)\n+\t\t\t\tlast = last_space_in_indent + 1;\n+\t\t\telse\n+\t\t\t\tlast = last_tab_in_indent + 1;\n+\t\t}\n+\n \t\t/*\n-\t\t * between patch[1..last_tab_in_indent] strip the\n-\t\t * funny spaces, updating them to tab as needed.\n+\t\t * between patch[1..last], strip the funny spaces,\n+\t\t * updating them to tab as needed.\n \t\t */\n-\t\tfor (i = 1; i < last_tab_in_indent; i++, plen--) {\n+\t\tfor (i = 1; i < last; i++, plen--) {\n \t\t\tchar ch = patch[i];\n \t\t\tif (ch != ' ') {\n \t\t\t\tconsecutive_spaces = 0;\n@@ -1626,8 +1654,10 @@ static int apply_line(char *output, const char *patch, int plen)\n \t\t\t\t}\n \t\t\t}\n \t\t}\n+\t\twhile (0 < consecutive_spaces--)\n+\t\t\t*output++ = ' ';\n \t\tfixed = 1;\n-\t\ti = last_tab_in_indent;\n+\t\ti = last;\n \t}\n \telse\n \t\ti = 1;\ndiff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh\nnew file mode 100755\nindex 0000000..f53ac46\n--- /dev/null\n+++ b/t/t4124-apply-ws-rule.sh\n@@ -0,0 +1,133 @@\n+#!/bin/sh\n+\n+test_description='core.whitespace rules and git-apply'\n+\n+. ./test-lib.sh\n+\n+prepare_test_file () {\n+\n+\t# A line that has character X is touched iff RULE is in effect:\n+\t#       X  RULE\n+\t#   \t!  trailing-space\n+\t#   \t@  space-before-tab\n+\t#   \t#  indent-with-non-tab\n+\tsed -e \"s/_/ /g\" -e \"s/>/\t/\" <<-\\EOF\n+\t\tAn_SP in an ordinary line>and a HT.\n+\t\t>A HT.\n+\t\t_>A SP and a HT (@).\n+\t\t_>_A SP, a HT and a SP (@).\n+\t\t_______Seven SP.\n+\t\t________Eight SP (#).\n+\t\t_______>Seven SP and a HT (@).\n+\t\t________>Eight SP and a HT (@#).\n+\t\t_______>_Seven SP, a HT and a SP (@).\n+\t\t________>_Eight SP, a HT and a SP (@#).\n+\t\t_______________Fifteen SP (#).\n+\t\t_______________>Fifteen SP and a HT (@#).\n+\t\t________________Sixteen SP (#).\n+\t\t________________>Sixteen SP and a HT (@#).\n+\t\t_____a__Five SP, a non WS, two SP.\n+\t\tA line with a (!) trailing SP_\n+\t\tA line with a (!) trailing HT>\n+\tEOF\n+}\n+\n+apply_patch () {\n+\t>target &&\n+\tsed -e \"s|\\([ab]\\)/file|\\1/target|\" <patch |\n+\tgit apply \"$@\"\n+}\n+\n+test_fix () {\n+\n+\t# fix should not barf\n+\tapply_patch --whitespace=fix || return 1\n+\n+\t# find touched lines\n+\tdiff file target | sed -n -e \"s/^> //p\" >fixed\n+\n+\t# the changed lines are all expeced to change\n+\tfixed_cnt=$(wc -l <fixed)\n+\tcase \"$1\" in\n+\t'') expect_cnt=$fixed_cnt ;;\n+\t?*) expect_cnt=$(grep \"[$1]\" <fixed | wc -l) ;;\n+\tesac\n+\ttest $fixed_cnt -eq $expect_cnt || return 1\n+\n+\t# and we are not missing anything\n+\tcase \"$1\" in\n+\t'') expect_cnt=0 ;;\n+\t?*) expect_cnt=$(grep \"[$1]\" <file | wc -l) ;;\n+\tesac\n+\ttest $fixed_cnt -eq $expect_cnt || return 1\n+\n+\t# Get the patch actually applied\n+\tgit diff-files -p target >fixed-patch\n+\ttest -s fixed-patch && return 0\n+\n+\t# Make sure it is complaint-free\n+\t>target\n+\tgit apply --whitespace=error-all <fixed-patch\n+\n+}\n+\n+test_expect_success setup '\n+\n+\t>file &&\n+\tgit add file &&\n+\tprepare_test_file >file &&\n+\tgit diff-files -p >patch &&\n+\t>target &&\n+\tgit add target\n+\n+'\n+\n+test_expect_success 'whitespace=nowarn, default rule' '\n+\n+\tapply_patch --whitespace=nowarn &&\n+\tdiff file target\n+\n+'\n+\n+test_expect_success 'whitespace=warn, default rule' '\n+\n+\tapply_patch --whitespace=warn &&\n+\tdiff file target\n+\n+'\n+\n+test_expect_success 'whitespace=error-all, default rule' '\n+\n+\tapply_patch --whitespace=error-all && return 1\n+\ttest -s target && return 1\n+\t: happy\n+\n+'\n+\n+test_expect_success 'whitespace=error-all, no rule' '\n+\n+\tgit config core.whitespace -trailing,-space-before,-indent &&\n+\tapply_patch --whitespace=error-all &&\n+\tdiff file target\n+\n+'\n+\n+for t in - ''\n+do\n+\tcase \"$t\" in '') tt='!' ;; *) tt= ;; esac\n+\tfor s in - ''\n+\tdo\n+\t\tcase \"$s\" in '') ts='@' ;; *) ts= ;; esac\n+\t\tfor i in - ''\n+\t\tdo\n+\t\t\tcase \"$i\" in '') ti='#' ;; *) ti= ;; esac\n+\t\t\trule=${t}trailing,${s}space,${i}indent &&\n+\t\t\ttest_expect_success \"rule=$rule\" '\n+\t\t\t\tgit config core.whitespace \"$rule\" &&\n+\t\t\t\ttest_fix \"$tt$ts$ti\"\n+\t\t\t'\n+\t\tdone\n+\tdone\n+done\n+\n+test_done\n-- \n1.5.3.6.1991.ge56ac\n"},{"id":"60822","messageId":"7v7ik7quc6.fsf_-_@gitster.siamese.dyndns.org","threadId":"11000","inReplyTo":"7v4pfcs20b.fsf@gitster.siamese.dyndns.org","subject":"[PATCH 3/2] core.whitespace: documentation updates.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-24T20:09:13Z","receivedAt":"2007-11-24T20:09:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This adds description of core.whitespace to the manual page of git-config,\nand updates the stale description of whitespace handling in the manual\npage of git-apply.\n\nAlso demote \"strip\" to a synonym status for \"fix\" as the value of --whitespace\noption given to git-apply.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * This is meant to conclude the \"War on more than 8-SP indent\" Bruce\n   started some time ago.  It is now configurable, and turned off by\n   default, so hopefully people outside the kernel circle would not mind.\n\n   A possible addition to the repertoire of core.whitespace is to add\n   \"cr-at-end\", which would consider a line that ends with CR an error.\n   We redefine \"trailing-space\" not to complain to a line ending with\n   CRLF but otherwise does not have trailing whitespaces.  To be\n   compatible with the current behaviour, cr-at-end needs to be added to\n   the default set of errors to be detected, but it might be an\n   improvement if we stopped treating 'cr-at-end' as an error by\n   default.\n\n Documentation/config.txt    |   18 ++++++++++++++++--\n Documentation/git-apply.txt |   35 +++++++++++++++++++++--------------\n builtin-apply.c             |    2 +-\n 3 files changed, 38 insertions(+), 17 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex edf50cd..0e71137 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -293,6 +293,20 @@ core.pager::\n \tThe command that git will use to paginate output.  Can be overridden\n \twith the `GIT_PAGER` environment variable.\n \n+core.whitespace::\n+\tA comma separated list of common whitespace problems to\n+\tnotice.  `git diff` will use `color.diff.whitespace` to\n+\thighlight them, and `git apply --whitespace=error` will\n+\tconsider them as errors:\n++\n+* `trailing-space` treats trailing whitespaces at the end of the line\n+  as an error (enabled by default).\n+* `space-before-tab` treats a space character that appears immediately\n+  before a tab character in the initial indent part of the line as an\n+  error (enabled by default).\n+* `indent-with-non-tab` treats a line that is indented with 8 or more\n+  space characters that can be replaced with tab characters.\n+\n alias.*::\n \tCommand aliases for the gitlink:git[1] command wrapper - e.g.\n \tafter defining \"alias.last = cat-file commit HEAD\", the invocation\n@@ -378,8 +392,8 @@ color.diff.<slot>::\n \twhich part of the patch to use the specified color, and is one\n \tof `plain` (context text), `meta` (metainformation), `frag`\n \t(hunk header), `old` (removed lines), `new` (added lines),\n-\t`commit` (commit headers), or `whitespace` (highlighting dubious\n-\twhitespace).  The values of these variables may be specified as\n+\t`commit` (commit headers), or `whitespace` (highlighting\n+\twhitespace errors). The values of these variables may be specified as\n \tin color.branch.<slot>.\n \n color.pager::\ndiff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt\nindex c1c54bf..bae3e7b 100644\n--- a/Documentation/git-apply.txt\n+++ b/Documentation/git-apply.txt\n@@ -13,7 +13,7 @@ SYNOPSIS\n \t  [--apply] [--no-add] [--build-fake-ancestor <file>] [-R | --reverse]\n \t  [--allow-binary-replacement | --binary] [--reject] [-z]\n \t  [-pNUM] [-CNUM] [--inaccurate-eof] [--cached]\n-\t  [--whitespace=<nowarn|warn|error|error-all|strip>]\n+\t  [--whitespace=<nowarn|warn|fix|error|error-all>]\n \t  [--exclude=PATH] [--verbose] [<patch>...]\n \n DESCRIPTION\n@@ -135,25 +135,32 @@ discouraged.\n \tbe useful when importing patchsets, where you want to exclude certain\n \tfiles or directories.\n \n---whitespace=<option>::\n-\tWhen applying a patch, detect a new or modified line\n-\tthat ends with trailing whitespaces (this includes a\n-\tline that solely consists of whitespaces).  By default,\n-\tthe command outputs warning messages and applies the\n-\tpatch.\n-\tWhen gitlink:git-apply[1] is used for statistics and not applying a\n-\tpatch, it defaults to `nowarn`.\n-\tYou can use different `<option>` to control this\n-\tbehavior:\n+--whitespace=<action>::\n+\tWhen applying a patch, detect a new or modified line that has\n+\twhitespace errors.  What are considered whitespace errors is\n+\tcontrolled by `core.whitespace` configuration.  By default,\n+\ttrailing whitespaces (including lines that solely consist of\n+\twhitespaces) and a space character that is immediately followed\n+\tby a tab character inside the initial indent of the line are\n+\tconsidered whitespace errors.\n++\n+By default, the command outputs warning messages but applies the patch.\n+When gitlink:git-apply[1] is used for statistics and not applying a\n+patch, it defaults to `nowarn`.\n++\n+You can use different `<action>` to control this\n+behavior:\n +\n * `nowarn` turns off the trailing whitespace warning.\n * `warn` outputs warnings for a few such errors, but applies the\n-  patch (default).\n+  patch as-is (default).\n+* `fix` outputs warnings for a few such errors, and applies the\n+  patch after fixing them (`strip` is a synonym --- the tool\n+  used to consider only trailing whitespaces as errors, and the\n+  fix involved 'stripping' them, but modern gits do more).\n * `error` outputs warnings for a few such errors, and refuses\n   to apply the patch.\n * `error-all` is similar to `error` but shows all errors.\n-* `strip` outputs warnings for a few such errors, strips out the\n-  trailing whitespaces and applies the patch.\n \n --inaccurate-eof::\n \tUnder certain circumstances, some versions of diff do not correctly\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex e04b493..57efcd5 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -45,7 +45,7 @@ static const char *fake_ancestor;\n static int line_termination = '\\n';\n static unsigned long p_context = ULONG_MAX;\n static const char apply_usage[] =\n-\"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...\";\n+\"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|fix|error|error-all>] <patch>...\";\n \n static enum ws_error_action {\n \tnowarn_ws_error,\n-- \n1.5.3.6.1991.ge56ac\n"},{"id":"60823","messageId":"20071124202257.GC12864@fieldses.org","threadId":"11000","inReplyTo":"7v7ik7quc6.fsf_-_@gitster.siamese.dyndns.org","subject":"Re: [PATCH 3/2] core.whitespace: documentation updates.","fromName":"J. Bruce Fields","fromEmail":"bfields@fieldses.org","sentAt":"2007-11-24T20:22:57Z","receivedAt":"2007-11-24T20:22:57Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"On Sat, Nov 24, 2007 at 12:09:13PM -0800, Junio C Hamano wrote:\n> This adds description of core.whitespace to the manual page of git-config,\n> and updates the stale description of whitespace handling in the manual\n> page of git-apply.\n> \n> Also demote \"strip\" to a synonym status for \"fix\" as the value of --whitespace\n> option given to git-apply.\n> \n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n> \n>  * This is meant to conclude the \"War on more than 8-SP indent\" Bruce\n>    started some time ago.  It is now configurable, and turned off by\n>    default, so hopefully people outside the kernel circle would not mind.\n> \n>    A possible addition to the repertoire of core.whitespace is to add\n>    \"cr-at-end\", which would consider a line that ends with CR an error.\n>    We redefine \"trailing-space\" not to complain to a line ending with\n>    CRLF but otherwise does not have trailing whitespaces.  To be\n>    compatible with the current behaviour, cr-at-end needs to be added to\n>    the default set of errors to be detected, but it might be an\n>    improvement if we stopped treating 'cr-at-end' as an error by\n>    default.\n\nI'd still prefer this to be a gitattributes thing rather than a config\nvariable[1].  Last time I raised this you said something to the effect\nof \"I think you're right, let's fix that before it's merged.\"  Would you\nlike me to work on that?\n\n--b.\n\n[1] A rehash of the argument: config variables vary depending on the\nsystem (/etc/gitconfig), the user (~/.config), or the particular\nrepository ($GIT_DIR/config).  But you don't want the whitespace policy\nto vary that way: all users and repositories should see the same\nwhitespace policy for a given project.  It's entirely possible, however,\nthat you might like the policy to vary depending on the file (Makefile\nvs. main.py?).  And of course getting the policy versioned and\ndistributed to other repositories automatically is nice too.\n\n> \n>  Documentation/config.txt    |   18 ++++++++++++++++--\n>  Documentation/git-apply.txt |   35 +++++++++++++++++++++--------------\n>  builtin-apply.c             |    2 +-\n>  3 files changed, 38 insertions(+), 17 deletions(-)\n> \n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index edf50cd..0e71137 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -293,6 +293,20 @@ core.pager::\n>  \tThe command that git will use to paginate output.  Can be overridden\n>  \twith the `GIT_PAGER` environment variable.\n>  \n> +core.whitespace::\n> +\tA comma separated list of common whitespace problems to\n> +\tnotice.  `git diff` will use `color.diff.whitespace` to\n> +\thighlight them, and `git apply --whitespace=error` will\n> +\tconsider them as errors:\n> ++\n> +* `trailing-space` treats trailing whitespaces at the end of the line\n> +  as an error (enabled by default).\n> +* `space-before-tab` treats a space character that appears immediately\n> +  before a tab character in the initial indent part of the line as an\n> +  error (enabled by default).\n> +* `indent-with-non-tab` treats a line that is indented with 8 or more\n> +  space characters that can be replaced with tab characters.\n> +\n>  alias.*::\n>  \tCommand aliases for the gitlink:git[1] command wrapper - e.g.\n>  \tafter defining \"alias.last = cat-file commit HEAD\", the invocation\n> @@ -378,8 +392,8 @@ color.diff.<slot>::\n>  \twhich part of the patch to use the specified color, and is one\n>  \tof `plain` (context text), `meta` (metainformation), `frag`\n>  \t(hunk header), `old` (removed lines), `new` (added lines),\n> -\t`commit` (commit headers), or `whitespace` (highlighting dubious\n> -\twhitespace).  The values of these variables may be specified as\n> +\t`commit` (commit headers), or `whitespace` (highlighting\n> +\twhitespace errors). The values of these variables may be specified as\n>  \tin color.branch.<slot>.\n>  \n>  color.pager::\n> diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt\n> index c1c54bf..bae3e7b 100644\n> --- a/Documentation/git-apply.txt\n> +++ b/Documentation/git-apply.txt\n> @@ -13,7 +13,7 @@ SYNOPSIS\n>  \t  [--apply] [--no-add] [--build-fake-ancestor <file>] [-R | --reverse]\n>  \t  [--allow-binary-replacement | --binary] [--reject] [-z]\n>  \t  [-pNUM] [-CNUM] [--inaccurate-eof] [--cached]\n> -\t  [--whitespace=<nowarn|warn|error|error-all|strip>]\n> +\t  [--whitespace=<nowarn|warn|fix|error|error-all>]\n>  \t  [--exclude=PATH] [--verbose] [<patch>...]\n>  \n>  DESCRIPTION\n> @@ -135,25 +135,32 @@ discouraged.\n>  \tbe useful when importing patchsets, where you want to exclude certain\n>  \tfiles or directories.\n>  \n> ---whitespace=<option>::\n> -\tWhen applying a patch, detect a new or modified line\n> -\tthat ends with trailing whitespaces (this includes a\n> -\tline that solely consists of whitespaces).  By default,\n> -\tthe command outputs warning messages and applies the\n> -\tpatch.\n> -\tWhen gitlink:git-apply[1] is used for statistics and not applying a\n> -\tpatch, it defaults to `nowarn`.\n> -\tYou can use different `<option>` to control this\n> -\tbehavior:\n> +--whitespace=<action>::\n> +\tWhen applying a patch, detect a new or modified line that has\n> +\twhitespace errors.  What are considered whitespace errors is\n> +\tcontrolled by `core.whitespace` configuration.  By default,\n> +\ttrailing whitespaces (including lines that solely consist of\n> +\twhitespaces) and a space character that is immediately followed\n> +\tby a tab character inside the initial indent of the line are\n> +\tconsidered whitespace errors.\n> ++\n> +By default, the command outputs warning messages but applies the patch.\n> +When gitlink:git-apply[1] is used for statistics and not applying a\n> +patch, it defaults to `nowarn`.\n> ++\n> +You can use different `<action>` to control this\n> +behavior:\n>  +\n>  * `nowarn` turns off the trailing whitespace warning.\n>  * `warn` outputs warnings for a few such errors, but applies the\n> -  patch (default).\n> +  patch as-is (default).\n> +* `fix` outputs warnings for a few such errors, and applies the\n> +  patch after fixing them (`strip` is a synonym --- the tool\n> +  used to consider only trailing whitespaces as errors, and the\n> +  fix involved 'stripping' them, but modern gits do more).\n>  * `error` outputs warnings for a few such errors, and refuses\n>    to apply the patch.\n>  * `error-all` is similar to `error` but shows all errors.\n> -* `strip` outputs warnings for a few such errors, strips out the\n> -  trailing whitespaces and applies the patch.\n>  \n>  --inaccurate-eof::\n>  \tUnder certain circumstances, some versions of diff do not correctly\n> diff --git a/builtin-apply.c b/builtin-apply.c\n> index e04b493..57efcd5 100644\n> --- a/builtin-apply.c\n> +++ b/builtin-apply.c\n> @@ -45,7 +45,7 @@ static const char *fake_ancestor;\n>  static int line_termination = '\\n';\n>  static unsigned long p_context = ULONG_MAX;\n>  static const char apply_usage[] =\n> -\"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...\";\n> +\"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|fix|error|error-all>] <patch>...\";\n>  \n>  static enum ws_error_action {\n>  \tnowarn_ws_error,\n> -- \n> 1.5.3.6.1991.ge56ac\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":"60824","messageId":"7v3auvqq09.fsf@gitster.siamese.dyndns.org","threadId":"11000","inReplyTo":"20071124202257.GC12864@fieldses.org","subject":"Re: [PATCH 3/2] core.whitespace: documentation updates.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-24T21:42:46Z","receivedAt":"2007-11-24T21:42:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"J. Bruce Fields\" <bfields@fieldses.org> writes:\n\n> I'd still prefer this to be a gitattributes thing rather than a config\n> variable[1].  Last time I raised this you said something to the effect\n> of \"I think you're right, let's fix that before it's merged.\"  Would you\n> like me to work on that?\n\nAh, I forgot about that, and you are right.  Go wild.\n"},{"id":"60877","messageId":"20071125215819.GD23820@fieldses.org","threadId":"11000","inReplyTo":"7v3auvqq09.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 3/2] core.whitespace: documentation updates.","fromName":"J. Bruce Fields","fromEmail":"bfields@fieldses.org","sentAt":"2007-11-25T21:58:19Z","receivedAt":"2007-11-25T21:58:19Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"On Sat, Nov 24, 2007 at 01:42:46PM -0800, Junio C Hamano wrote:\n> \"J. Bruce Fields\" <bfields@fieldses.org> writes:\n> \n> > I'd still prefer this to be a gitattributes thing rather than a config\n> > variable[1].  Last time I raised this you said something to the effect\n> > of \"I think you're right, let's fix that before it's merged.\"  Would you\n> > like me to work on that?\n> \n> Ah, I forgot about that, and you are right.  Go wild.\n\nOK, I will go wild, but... very slowly.\n\n--b.\n"},{"id":"62133","messageId":"7vodd4fb2f.fsf@gitster.siamese.dyndns.org","threadId":"11000","inReplyTo":"20071125215819.GD23820@fieldses.org","subject":"Re: [PATCH 3/2] core.whitespace: documentation updates.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-12-06T09:04:56Z","receivedAt":"2007-12-06T09:04:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"J. Bruce Fields\" <bfields@fieldses.org> writes:\n\n> On Sat, Nov 24, 2007 at 01:42:46PM -0800, Junio C Hamano wrote:\n>> \"J. Bruce Fields\" <bfields@fieldses.org> writes:\n>> \n>> > I'd still prefer this to be a gitattributes thing rather than a config\n>> > variable[1].  Last time I raised this you said something to the effect\n>> > of \"I think you're right, let's fix that before it's merged.\"  Would you\n>> > like me to work on that?\n>> \n>> Ah, I forgot about that, and you are right.  Go wild.\n>\n> OK, I will go wild, but... very slowly.\n\nHow wild are you these days ;-)?  I know December is a busy time for\neverybody, and I ended up doing this myself while I was writing up the\nAPI documentation for gitattributes.\n\n-- >8 --\n[PATCH] Use gitattributes to define per-path whitespace rule\n\nThe `core.whitespace` configuration variable allows you to define what\n`diff` and `apply` should consider whitespace errors for all paths in\nthe project (See gitlink:git-config[1]).  This attribute gives you finer\ncontrol per path.\n\nFor example, if you have these in the .gitattributes:\n\n    frotz   whitespace\n    nitfol  -whitespace\n    xyzzy   whitespace=-trailing\n\nall types of whitespace problems known to git are noticed in path 'frotz'\n(i.e. diff shows them in diff.whitespace color, and apply warns about\nthem), no whitespace problem is noticed in path 'nitfol', and the\ndefault types of whitespace problems except \"trailing whitespace\" are\nnoticed for path 'xyzzy'.  A project with mixed Python and C might want\nto have:\n\n    *.c    whitespace\n    *.py   whitespace=-indent-with-non-tab\n\nin its toplevel .gitattributes file.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/gitattributes.txt |   31 +++++++++++++\n Makefile                        |    2 +-\n builtin-apply.c                 |   36 +++++++++------\n cache.h                         |    4 +-\n config.c                        |   50 +--------------------\n diff.c                          |   19 +++++---\n environment.c                   |    2 +-\n t/t4019-diff-wserror.sh         |   47 +++++++++++++++++++\n t/t4124-apply-ws-rule.sh        |   20 ++++++++-\n ws.c                            |   96 +++++++++++++++++++++++++++++++++++++++\n 10 files changed, 233 insertions(+), 74 deletions(-)\n create mode 100644 ws.c\n\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 20cf8ff..c4bcbb9 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -360,6 +360,37 @@ When left unspecified, the driver itself is used for both\n internal merge and the final merge.\n \n \n+Checking whitespace errors\n+~~~~~~~~~~~~~~~~~~~~~~~~~~\n+\n+`whitespace`\n+^^^^^^^^^^^^\n+\n+The `core.whitespace` configuration variable allows you to define what\n+`diff` and `apply` should consider whitespace errors for all paths in\n+the project (See gitlink:git-config[1]).  This attribute gives you finer\n+control per path.\n+\n+Set::\n+\n+\tNotice all types of potential whitespace errors known to git.\n+\n+Unset::\n+\n+\tDo not notice anything as error.\n+\n+Unspecified::\n+\n+\tUse the value of `core.whitespace` configuration variable to\n+\tdecide what to notice as error.\n+\n+String::\n+\n+\tSpecify a comma separate list of common whitespace problems to\n+\tnotice in the same format as `core.whitespace` configuration\n+\tvariable.\n+\n+\n EXAMPLE\n -------\n \ndiff --git a/Makefile b/Makefile\nindex 042f79e..ac6b079 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -312,7 +312,7 @@ LIB_OBJS = \\\n \talloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \\\n \tcolor.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \\\n \tconvert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \\\n-\ttransport.o bundle.o walker.o parse-options.o\n+\ttransport.o bundle.o walker.o parse-options.o ws.o\n \n BUILTIN_OBJS = \\\n \tbuiltin-add.o \\\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex 57efcd5..ee3ef60 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -144,6 +144,7 @@ struct patch {\n \tunsigned int old_mode, new_mode;\n \tint is_new, is_delete;\t/* -1 = unknown, 0 = false, 1 = true */\n \tint rejected;\n+\tunsigned ws_rule;\n \tunsigned long deflate_origlen;\n \tint lines_added, lines_deleted;\n \tint score;\n@@ -898,7 +899,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc\n \treturn -1;\n }\n \n-static void check_whitespace(const char *line, int len)\n+static void check_whitespace(const char *line, int len, unsigned ws_rule)\n {\n \tconst char *err = \"Adds trailing whitespace\";\n \tint seen_space = 0;\n@@ -910,14 +911,14 @@ static void check_whitespace(const char *line, int len)\n \t * this function.  That is, an addition of an empty line would\n \t * check the '+' here.  Sneaky...\n \t */\n-\tif ((whitespace_rule & WS_TRAILING_SPACE) && isspace(line[len-2]))\n+\tif ((ws_rule & WS_TRAILING_SPACE) && isspace(line[len-2]))\n \t\tgoto error;\n \n \t/*\n \t * Make sure that there is no space followed by a tab in\n \t * indentation.\n \t */\n-\tif (whitespace_rule & WS_SPACE_BEFORE_TAB) {\n+\tif (ws_rule & WS_SPACE_BEFORE_TAB) {\n \t\terr = \"Space in indent is followed by a tab\";\n \t\tfor (i = 1; i < len; i++) {\n \t\t\tif (line[i] == '\\t') {\n@@ -935,7 +936,7 @@ static void check_whitespace(const char *line, int len)\n \t * Make sure that the indentation does not contain more than\n \t * 8 spaces.\n \t */\n-\tif ((whitespace_rule & WS_INDENT_WITH_NON_TAB) &&\n+\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) &&\n \t    (8 < len) && !strncmp(\"+        \", line, 9)) {\n \t\terr = \"Indent more than 8 places with spaces\";\n \t\tgoto error;\n@@ -1001,7 +1002,7 @@ static int parse_fragment(char *line, unsigned long size,\n \t\tcase '-':\n \t\t\tif (apply_in_reverse &&\n \t\t\t    ws_error_action != nowarn_ws_error)\n-\t\t\t\tcheck_whitespace(line, len);\n+\t\t\t\tcheck_whitespace(line, len, patch->ws_rule);\n \t\t\tdeleted++;\n \t\t\toldlines--;\n \t\t\ttrailing = 0;\n@@ -1009,7 +1010,7 @@ static int parse_fragment(char *line, unsigned long size,\n \t\tcase '+':\n \t\t\tif (!apply_in_reverse &&\n \t\t\t    ws_error_action != nowarn_ws_error)\n-\t\t\t\tcheck_whitespace(line, len);\n+\t\t\t\tcheck_whitespace(line, len, patch->ws_rule);\n \t\t\tadded++;\n \t\t\tnewlines--;\n \t\t\ttrailing = 0;\n@@ -1318,6 +1319,10 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)\n \tif (offset < 0)\n \t\treturn offset;\n \n+\tpatch->ws_rule = whitespace_rule(patch->new_name\n+\t\t\t\t\t ? patch->new_name\n+\t\t\t\t\t : patch->old_name);\n+\n \tpatchsize = parse_single_patch(buffer + offset + hdrsize,\n \t\t\t\t       size - offset - hdrsize, patch);\n \n@@ -1568,7 +1573,8 @@ static void remove_last_line(const char **rbuf, int *rsize)\n \t*rsize = offset + 1;\n }\n \n-static int apply_line(char *output, const char *patch, int plen)\n+static int apply_line(char *output, const char *patch, int plen,\n+\t\t      unsigned ws_rule)\n {\n \t/*\n \t * plen is number of bytes to be copied from patch,\n@@ -1593,7 +1599,7 @@ static int apply_line(char *output, const char *patch, int plen)\n \t/*\n \t * Strip trailing whitespace\n \t */\n-\tif ((whitespace_rule & WS_TRAILING_SPACE) &&\n+\tif ((ws_rule & WS_TRAILING_SPACE) &&\n \t    (1 < plen && isspace(patch[plen-1]))) {\n \t\tif (patch[plen] == '\\n')\n \t\t\tadd_nl_to_tail = 1;\n@@ -1610,12 +1616,12 @@ static int apply_line(char *output, const char *patch, int plen)\n \t\tchar ch = patch[i];\n \t\tif (ch == '\\t') {\n \t\t\tlast_tab_in_indent = i;\n-\t\t\tif ((whitespace_rule & WS_SPACE_BEFORE_TAB) &&\n+\t\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) &&\n \t\t\t    0 <= last_space_in_indent)\n \t\t\t    need_fix_leading_space = 1;\n \t\t} else if (ch == ' ') {\n \t\t\tlast_space_in_indent = i;\n-\t\t\tif ((whitespace_rule & WS_INDENT_WITH_NON_TAB) &&\n+\t\t\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) &&\n \t\t\t    last_tab_in_indent < 0 &&\n \t\t\t    8 <= i)\n \t\t\t\tneed_fix_leading_space = 1;\n@@ -1629,7 +1635,7 @@ static int apply_line(char *output, const char *patch, int plen)\n \t\tint consecutive_spaces = 0;\n \t\tint last = last_tab_in_indent + 1;\n \n-\t\tif (whitespace_rule & WS_INDENT_WITH_NON_TAB) {\n+\t\tif (ws_rule & WS_INDENT_WITH_NON_TAB) {\n \t\t\t/* have \"last\" point at one past the indent */\n \t\t\tif (last_tab_in_indent < last_space_in_indent)\n \t\t\t\tlast = last_space_in_indent + 1;\n@@ -1671,7 +1677,7 @@ static int apply_line(char *output, const char *patch, int plen)\n }\n \n static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,\n-\t\t\t      int inaccurate_eof)\n+\t\t\t      int inaccurate_eof, unsigned ws_rule)\n {\n \tint match_beginning, match_end;\n \tconst char *patch = frag->patch;\n@@ -1730,7 +1736,7 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,\n \t\tcase '+':\n \t\t\tif (first != '+' || !no_add) {\n \t\t\t\tint added = apply_line(new + newsize, patch,\n-\t\t\t\t\t\t       plen);\n+\t\t\t\t\t\t       plen, ws_rule);\n \t\t\t\tnewsize += added;\n \t\t\t\tif (first == '+' &&\n \t\t\t\t    added == 1 && new[newsize-1] == '\\n')\n@@ -1953,12 +1959,14 @@ static int apply_fragments(struct strbuf *buf, struct patch *patch)\n {\n \tstruct fragment *frag = patch->fragments;\n \tconst char *name = patch->old_name ? patch->old_name : patch->new_name;\n+\tunsigned ws_rule = patch->ws_rule;\n+\tunsigned inaccurate_eof = patch->inaccurate_eof;\n \n \tif (patch->is_binary)\n \t\treturn apply_binary(buf, patch);\n \n \twhile (frag) {\n-\t\tif (apply_one_fragment(buf, frag, patch->inaccurate_eof)) {\n+\t\tif (apply_one_fragment(buf, frag, inaccurate_eof, ws_rule)) {\n \t\t\terror(\"patch failed: %s:%ld\", name, frag->oldpos);\n \t\t\tif (!apply_with_reject)\n \t\t\t\treturn -1;\ndiff --git a/cache.h b/cache.h\nindex 3f42827..9cc6268 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -610,6 +610,8 @@ void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, i\n #define WS_SPACE_BEFORE_TAB\t02\n #define WS_INDENT_WITH_NON_TAB\t04\n #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)\n-extern unsigned whitespace_rule;\n+extern unsigned whitespace_rule_cfg;\n+extern unsigned whitespace_rule(const char *);\n+extern unsigned parse_whitespace_rule(const char *);\n \n #endif /* CACHE_H */\ndiff --git a/config.c b/config.c\nindex d5b9766..2500e0d 100644\n--- a/config.c\n+++ b/config.c\n@@ -246,54 +246,6 @@ static unsigned long get_unit_factor(const char *end)\n \tdie(\"unknown unit: '%s'\", end);\n }\n \n-static struct whitespace_rule {\n-\tconst char *rule_name;\n-\tunsigned rule_bits;\n-} whitespace_rule_names[] = {\n-\t{ \"trailing-space\", WS_TRAILING_SPACE },\n-\t{ \"space-before-tab\", WS_SPACE_BEFORE_TAB },\n-\t{ \"indent-with-non-tab\", WS_INDENT_WITH_NON_TAB },\n-};\n-\n-static unsigned parse_whitespace_rule(const char *string)\n-{\n-\tunsigned rule = WS_DEFAULT_RULE;\n-\n-\twhile (string) {\n-\t\tint i;\n-\t\tsize_t len;\n-\t\tconst char *ep;\n-\t\tint negated = 0;\n-\n-\t\tstring = string + strspn(string, \", \\t\\n\\r\");\n-\t\tep = strchr(string, ',');\n-\t\tif (!ep)\n-\t\t\tlen = strlen(string);\n-\t\telse\n-\t\t\tlen = ep - string;\n-\n-\t\tif (*string == '-') {\n-\t\t\tnegated = 1;\n-\t\t\tstring++;\n-\t\t\tlen--;\n-\t\t}\n-\t\tif (!len)\n-\t\t\tbreak;\n-\t\tfor (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++) {\n-\t\t\tif (strncmp(whitespace_rule_names[i].rule_name,\n-\t\t\t\t    string, len))\n-\t\t\t\tcontinue;\n-\t\t\tif (negated)\n-\t\t\t\trule &= ~whitespace_rule_names[i].rule_bits;\n-\t\t\telse\n-\t\t\t\trule |= whitespace_rule_names[i].rule_bits;\n-\t\t\tbreak;\n-\t\t}\n-\t\tstring = ep;\n-\t}\n-\treturn rule;\n-}\n-\n int git_parse_long(const char *value, long *ret)\n {\n \tif (value && *value) {\n@@ -480,7 +432,7 @@ int git_default_config(const char *var, const char *value)\n \t}\n \n \tif (!strcmp(var, \"core.whitespace\")) {\n-\t\twhitespace_rule = parse_whitespace_rule(value);\n+\t\twhitespace_rule_cfg = parse_whitespace_rule(value);\n \t\treturn 0;\n \t}\n \ndiff --git a/diff.c b/diff.c\nindex 6bb902f..c3a1942 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -454,6 +454,7 @@ static void diff_words_show(struct diff_words_data *diff_words)\n struct emit_callback {\n \tstruct xdiff_emit_state xm;\n \tint nparents, color_diff;\n+\tunsigned ws_rule;\n \tconst char **label_path;\n \tstruct diff_words_data *diff_words;\n \tint *found_changesp;\n@@ -493,8 +494,8 @@ static void emit_line(const char *set, const char *reset, const char *line, int\n }\n \n static void emit_line_with_ws(int nparents,\n-\t\tconst char *set, const char *reset, const char *ws,\n-\t\tconst char *line, int len)\n+\t\t\t      const char *set, const char *reset, const char *ws,\n+\t\t\t      const char *line, int len, unsigned ws_rule)\n {\n \tint col0 = nparents;\n \tint last_tab_in_indent = -1;\n@@ -511,7 +512,7 @@ static void emit_line_with_ws(int nparents,\n \tfor (i = col0; i < len; i++) {\n \t\tif (line[i] == '\\t') {\n \t\t\tlast_tab_in_indent = i;\n-\t\t\tif ((whitespace_rule & WS_SPACE_BEFORE_TAB) &&\n+\t\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) &&\n \t\t\t    0 <= last_space_in_indent)\n \t\t\t\tneed_highlight_leading_space = 1;\n \t\t}\n@@ -520,7 +521,7 @@ static void emit_line_with_ws(int nparents,\n \t\telse\n \t\t\tbreak;\n \t}\n-\tif ((whitespace_rule & WS_INDENT_WITH_NON_TAB) &&\n+\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) &&\n \t    0 <= last_space_in_indent &&\n \t    last_tab_in_indent < 0 &&\n \t    8 <= (i - col0)) {\n@@ -551,7 +552,7 @@ static void emit_line_with_ws(int nparents,\n \ttail = len - 1;\n \tif (line[tail] == '\\n' && i < tail)\n \t\ttail--;\n-\tif (whitespace_rule & WS_TRAILING_SPACE) {\n+\tif (ws_rule & WS_TRAILING_SPACE) {\n \t\twhile (i < tail) {\n \t\t\tif (!isspace(line[tail]))\n \t\t\t\tbreak;\n@@ -578,7 +579,7 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons\n \t\temit_line(set, reset, line, len);\n \telse\n \t\temit_line_with_ws(ecbdata->nparents, set, reset, ws,\n-\t\t\t\tline, len);\n+\t\t\t\t  line, len, ecbdata->ws_rule);\n }\n \n static void fn_out_consume(void *priv, char *line, unsigned long len)\n@@ -994,6 +995,7 @@ struct checkdiff_t {\n \tstruct xdiff_emit_state xm;\n \tconst char *filename;\n \tint lineno, color_diff;\n+\tunsigned ws_rule;\n };\n \n static void checkdiff_consume(void *priv, char *line, unsigned long len)\n@@ -1029,7 +1031,8 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)\n \t\t\tif (white_space_at_end)\n \t\t\t\tprintf(\"white space at end\");\n \t\t\tprintf(\":%s \", reset);\n-\t\t\temit_line_with_ws(1, set, reset, ws, line, len);\n+\t\t\temit_line_with_ws(1, set, reset, ws, line, len,\n+\t\t\t\t\t  data->ws_rule);\n \t\t}\n \n \t\tdata->lineno++;\n@@ -1330,6 +1333,7 @@ static void builtin_diff(const char *name_a,\n \t\tecbdata.label_path = lbl;\n \t\tecbdata.color_diff = o->color_diff;\n \t\tecbdata.found_changesp = &o->found_changes;\n+\t\tecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);\n \t\txpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;\n \t\txecfg.ctxlen = o->context;\n \t\txecfg.flags = XDL_EMIT_FUNCNAMES;\n@@ -1423,6 +1427,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,\n \tdata.filename = name_b ? name_b : name_a;\n \tdata.lineno = 0;\n \tdata.color_diff = o->color_diff;\n+\tdata.ws_rule = whitespace_rule(data.filename);\n \n \tif (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)\n \t\tdie(\"unable to read files to diff\");\ndiff --git a/environment.c b/environment.c\nindex 624dd96..2fbbc8e 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -35,7 +35,7 @@ int pager_in_use;\n int pager_use_color = 1;\n char *editor_program;\n int auto_crlf = 0;\t/* 1: both ways, -1: only when adding git objects */\n-unsigned whitespace_rule = WS_DEFAULT_RULE;\n+unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;\n \n /* This is set by setup_git_dir_gently() and/or git_default_config() */\n char *git_work_tree_cfg;\ndiff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh\nindex dbc895b..67e080b 100755\n--- a/t/t4019-diff-wserror.sh\n+++ b/t/t4019-diff-wserror.sh\n@@ -45,8 +45,24 @@ test_expect_success 'without -trail' '\n \n '\n \n+test_expect_success 'without -trail (attribute)' '\n+\n+\tgit config --unset core.whitespace\n+\techo \"F whitespace=-trail\" >.gitattributes\n+\tgit diff --color >output\n+\tgrep \"$blue_grep\" output >error\n+\tgrep -v \"$blue_grep\" output >normal\n+\n+\tgrep Eight normal >/dev/null &&\n+\tgrep HT error >/dev/null &&\n+\tgrep With normal >/dev/null &&\n+\tgrep No normal >/dev/null\n+\n+'\n+\n test_expect_success 'without -space' '\n \n+\trm -f .gitattributes\n \tgit config core.whitespace -space\n \tgit diff --color >output\n \tgrep \"$blue_grep\" output >error\n@@ -59,8 +75,24 @@ test_expect_success 'without -space' '\n \n '\n \n+test_expect_success 'without -space (attribute)' '\n+\n+\tgit config --unset core.whitespace\n+\techo \"F whitespace=-space\" >.gitattributes\n+\tgit diff --color >output\n+\tgrep \"$blue_grep\" output >error\n+\tgrep -v \"$blue_grep\" output >normal\n+\n+\tgrep Eight normal >/dev/null &&\n+\tgrep HT normal >/dev/null &&\n+\tgrep With error >/dev/null &&\n+\tgrep No normal >/dev/null\n+\n+'\n+\n test_expect_success 'with indent-non-tab only' '\n \n+\trm -f .gitattributes\n \tgit config core.whitespace indent,-trailing,-space\n \tgit diff --color >output\n \tgrep \"$blue_grep\" output >error\n@@ -73,4 +105,19 @@ test_expect_success 'with indent-non-tab only' '\n \n '\n \n+test_expect_success 'with indent-non-tab only (attribute)' '\n+\n+\tgit config --unset core.whitespace\n+\techo \"F whitespace=indent,-trailing,-space\" >.gitattributes\n+\tgit diff --color >output\n+\tgrep \"$blue_grep\" output >error\n+\tgrep -v \"$blue_grep\" output >normal\n+\n+\tgrep Eight error >/dev/null &&\n+\tgrep HT normal >/dev/null &&\n+\tgrep With normal >/dev/null &&\n+\tgrep No normal >/dev/null\n+\n+'\n+\n test_done\ndiff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh\nindex f53ac46..85f3da2 100755\n--- a/t/t4124-apply-ws-rule.sh\n+++ b/t/t4124-apply-ws-rule.sh\n@@ -112,6 +112,15 @@ test_expect_success 'whitespace=error-all, no rule' '\n \n '\n \n+test_expect_success 'whitespace=error-all, no rule (attribute)' '\n+\n+\tgit config --unset core.whitespace &&\n+\techo \"target -whitespace\" >.gitattributes &&\n+\tapply_patch --whitespace=error-all &&\n+\tdiff file target\n+\n+'\n+\n for t in - ''\n do\n \tcase \"$t\" in '') tt='!' ;; *) tt= ;; esac\n@@ -121,11 +130,20 @@ do\n \t\tfor i in - ''\n \t\tdo\n \t\t\tcase \"$i\" in '') ti='#' ;; *) ti= ;; esac\n-\t\t\trule=${t}trailing,${s}space,${i}indent &&\n+\t\t\trule=${t}trailing,${s}space,${i}indent\n+\n+\t\t\trm -f .gitattributes\n \t\t\ttest_expect_success \"rule=$rule\" '\n \t\t\t\tgit config core.whitespace \"$rule\" &&\n \t\t\t\ttest_fix \"$tt$ts$ti\"\n \t\t\t'\n+\n+\t\t\ttest_expect_success \"rule=$rule (attributes)\" '\n+\t\t\t\tgit config --unset core.whitespace &&\n+\t\t\t\techo \"target whitespace=$rule\" >.gitattributes &&\n+\t\t\t\ttest_fix \"$tt$ts$ti\"\n+\t\t\t'\n+\n \t\tdone\n \tdone\n done\ndiff --git a/ws.c b/ws.c\nnew file mode 100644\nindex 0000000..52c10ca\n--- /dev/null\n+++ b/ws.c\n@@ -0,0 +1,96 @@\n+/*\n+ * Whitespace rules\n+ *\n+ * Copyright (c) 2007 Junio C Hamano\n+ */\n+\n+#include \"cache.h\"\n+#include \"attr.h\"\n+\n+static struct whitespace_rule {\n+\tconst char *rule_name;\n+\tunsigned rule_bits;\n+} whitespace_rule_names[] = {\n+\t{ \"trailing-space\", WS_TRAILING_SPACE },\n+\t{ \"space-before-tab\", WS_SPACE_BEFORE_TAB },\n+\t{ \"indent-with-non-tab\", WS_INDENT_WITH_NON_TAB },\n+};\n+\n+unsigned parse_whitespace_rule(const char *string)\n+{\n+\tunsigned rule = WS_DEFAULT_RULE;\n+\n+\twhile (string) {\n+\t\tint i;\n+\t\tsize_t len;\n+\t\tconst char *ep;\n+\t\tint negated = 0;\n+\n+\t\tstring = string + strspn(string, \", \\t\\n\\r\");\n+\t\tep = strchr(string, ',');\n+\t\tif (!ep)\n+\t\t\tlen = strlen(string);\n+\t\telse\n+\t\t\tlen = ep - string;\n+\n+\t\tif (*string == '-') {\n+\t\t\tnegated = 1;\n+\t\t\tstring++;\n+\t\t\tlen--;\n+\t\t}\n+\t\tif (!len)\n+\t\t\tbreak;\n+\t\tfor (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++) {\n+\t\t\tif (strncmp(whitespace_rule_names[i].rule_name,\n+\t\t\t\t    string, len))\n+\t\t\t\tcontinue;\n+\t\t\tif (negated)\n+\t\t\t\trule &= ~whitespace_rule_names[i].rule_bits;\n+\t\t\telse\n+\t\t\t\trule |= whitespace_rule_names[i].rule_bits;\n+\t\t\tbreak;\n+\t\t}\n+\t\tstring = ep;\n+\t}\n+\treturn rule;\n+}\n+\n+static void setup_whitespace_attr_check(struct git_attr_check *check)\n+{\n+\tstatic struct git_attr *attr_whitespace;\n+\n+\tif (!attr_whitespace)\n+\t\tattr_whitespace = git_attr(\"whitespace\", 10);\n+\tcheck[0].attr = attr_whitespace;\n+}\n+\n+unsigned whitespace_rule(const char *pathname)\n+{\n+\tstruct git_attr_check attr_whitespace_rule;\n+\n+\tsetup_whitespace_attr_check(&attr_whitespace_rule);\n+\tif (!git_checkattr(pathname, 1, &attr_whitespace_rule)) {\n+\t\tconst char *value;\n+\n+\t\tvalue = attr_whitespace_rule.value;\n+\t\tif (ATTR_TRUE(value)) {\n+\t\t\t/* true (whitespace) */\n+\t\t\tunsigned all_rule = 0;\n+\t\t\tint i;\n+\t\t\tfor (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)\n+\t\t\t\tall_rule |= whitespace_rule_names[i].rule_bits;\n+\t\t\treturn all_rule;\n+\t\t} else if (ATTR_FALSE(value)) {\n+\t\t\t/* false (-whitespace) */\n+\t\t\treturn 0;\n+\t\t} else if (ATTR_UNSET(value)) {\n+\t\t\t/* reset to default (!whitespace) */\n+\t\t\treturn whitespace_rule_cfg;\n+\t\t} else {\n+\t\t\t/* string */\n+\t\t\treturn parse_whitespace_rule(value);\n+\t\t}\n+\t} else {\n+\t\treturn whitespace_rule_cfg;\n+\t}\n+}\n-- \n1.5.3.7-2155-g4c25\n"},{"id":"62188","messageId":"20071206191826.GB5789@fieldses.org","threadId":"11000","inReplyTo":"7vodd4fb2f.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 3/2] core.whitespace: documentation updates.","fromName":"J. Bruce Fields","fromEmail":"bfields@fieldses.org","sentAt":"2007-12-06T19:18:26Z","receivedAt":"2007-12-06T19:18:26Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"On Thu, Dec 06, 2007 at 01:04:56AM -0800, Junio C Hamano wrote:\n> \"J. Bruce Fields\" <bfields@fieldses.org> writes:\n> \n> > On Sat, Nov 24, 2007 at 01:42:46PM -0800, Junio C Hamano wrote:\n> >> \"J. Bruce Fields\" <bfields@fieldses.org> writes:\n> >> \n> >> > I'd still prefer this to be a gitattributes thing rather than a config\n> >> > variable[1].  Last time I raised this you said something to the effect\n> >> > of \"I think you're right, let's fix that before it's merged.\"  Would you\n> >> > like me to work on that?\n> >> \n> >> Ah, I forgot about that, and you are right.  Go wild.\n> >\n> > OK, I will go wild, but... very slowly.\n> \n> How wild are you these days ;-)?  I know December is a busy time for\n> everybody, and I ended up doing this myself while I was writing up the\n> API documentation for gitattributes.\n\nWow, thanks!  Yes, I haven't done a thing on this.\n\n> -- >8 --\n> [PATCH] Use gitattributes to define per-path whitespace rule\n> \n> The `core.whitespace` configuration variable allows you to define what\n> `diff` and `apply` should consider whitespace errors for all paths in\n> the project (See gitlink:git-config[1]).  This attribute gives you finer\n> control per path.\n\nThat looks like what I'd hoped for.\n\nI'll set aside some time this weekend to play around with it.  (Is there\nany other piece left that needs to be done?)\n\n--b.\n\n> \n> For example, if you have these in the .gitattributes:\n> \n>     frotz   whitespace\n>     nitfol  -whitespace\n>     xyzzy   whitespace=-trailing\n> \n> all types of whitespace problems known to git are noticed in path 'frotz'\n> (i.e. diff shows them in diff.whitespace color, and apply warns about\n> them), no whitespace problem is noticed in path 'nitfol', and the\n> default types of whitespace problems except \"trailing whitespace\" are\n> noticed for path 'xyzzy'.  A project with mixed Python and C might want\n> to have:\n> \n>     *.c    whitespace\n>     *.py   whitespace=-indent-with-non-tab\n> \n> in its toplevel .gitattributes file.\n> \n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  Documentation/gitattributes.txt |   31 +++++++++++++\n>  Makefile                        |    2 +-\n>  builtin-apply.c                 |   36 +++++++++------\n>  cache.h                         |    4 +-\n>  config.c                        |   50 +--------------------\n>  diff.c                          |   19 +++++---\n>  environment.c                   |    2 +-\n>  t/t4019-diff-wserror.sh         |   47 +++++++++++++++++++\n>  t/t4124-apply-ws-rule.sh        |   20 ++++++++-\n>  ws.c                            |   96 +++++++++++++++++++++++++++++++++++++++\n>  10 files changed, 233 insertions(+), 74 deletions(-)\n>  create mode 100644 ws.c\n> \n> diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\n> index 20cf8ff..c4bcbb9 100644\n> --- a/Documentation/gitattributes.txt\n> +++ b/Documentation/gitattributes.txt\n> @@ -360,6 +360,37 @@ When left unspecified, the driver itself is used for both\n>  internal merge and the final merge.\n>  \n>  \n> +Checking whitespace errors\n> +~~~~~~~~~~~~~~~~~~~~~~~~~~\n> +\n> +`whitespace`\n> +^^^^^^^^^^^^\n> +\n> +The `core.whitespace` configuration variable allows you to define what\n> +`diff` and `apply` should consider whitespace errors for all paths in\n> +the project (See gitlink:git-config[1]).  This attribute gives you finer\n> +control per path.\n> +\n> +Set::\n> +\n> +\tNotice all types of potential whitespace errors known to git.\n> +\n> +Unset::\n> +\n> +\tDo not notice anything as error.\n> +\n> +Unspecified::\n> +\n> +\tUse the value of `core.whitespace` configuration variable to\n> +\tdecide what to notice as error.\n> +\n> +String::\n> +\n> +\tSpecify a comma separate list of common whitespace problems to\n> +\tnotice in the same format as `core.whitespace` configuration\n> +\tvariable.\n> +\n> +\n>  EXAMPLE\n>  -------\n>  \n> diff --git a/Makefile b/Makefile\n> index 042f79e..ac6b079 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -312,7 +312,7 @@ LIB_OBJS = \\\n>  \talloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \\\n>  \tcolor.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \\\n>  \tconvert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \\\n> -\ttransport.o bundle.o walker.o parse-options.o\n> +\ttransport.o bundle.o walker.o parse-options.o ws.o\n>  \n>  BUILTIN_OBJS = \\\n>  \tbuiltin-add.o \\\n> diff --git a/builtin-apply.c b/builtin-apply.c\n> index 57efcd5..ee3ef60 100644\n> --- a/builtin-apply.c\n> +++ b/builtin-apply.c\n> @@ -144,6 +144,7 @@ struct patch {\n>  \tunsigned int old_mode, new_mode;\n>  \tint is_new, is_delete;\t/* -1 = unknown, 0 = false, 1 = true */\n>  \tint rejected;\n> +\tunsigned ws_rule;\n>  \tunsigned long deflate_origlen;\n>  \tint lines_added, lines_deleted;\n>  \tint score;\n> @@ -898,7 +899,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc\n>  \treturn -1;\n>  }\n>  \n> -static void check_whitespace(const char *line, int len)\n> +static void check_whitespace(const char *line, int len, unsigned ws_rule)\n>  {\n>  \tconst char *err = \"Adds trailing whitespace\";\n>  \tint seen_space = 0;\n> @@ -910,14 +911,14 @@ static void check_whitespace(const char *line, int len)\n>  \t * this function.  That is, an addition of an empty line would\n>  \t * check the '+' here.  Sneaky...\n>  \t */\n> -\tif ((whitespace_rule & WS_TRAILING_SPACE) && isspace(line[len-2]))\n> +\tif ((ws_rule & WS_TRAILING_SPACE) && isspace(line[len-2]))\n>  \t\tgoto error;\n>  \n>  \t/*\n>  \t * Make sure that there is no space followed by a tab in\n>  \t * indentation.\n>  \t */\n> -\tif (whitespace_rule & WS_SPACE_BEFORE_TAB) {\n> +\tif (ws_rule & WS_SPACE_BEFORE_TAB) {\n>  \t\terr = \"Space in indent is followed by a tab\";\n>  \t\tfor (i = 1; i < len; i++) {\n>  \t\t\tif (line[i] == '\\t') {\n> @@ -935,7 +936,7 @@ static void check_whitespace(const char *line, int len)\n>  \t * Make sure that the indentation does not contain more than\n>  \t * 8 spaces.\n>  \t */\n> -\tif ((whitespace_rule & WS_INDENT_WITH_NON_TAB) &&\n> +\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) &&\n>  \t    (8 < len) && !strncmp(\"+        \", line, 9)) {\n>  \t\terr = \"Indent more than 8 places with spaces\";\n>  \t\tgoto error;\n> @@ -1001,7 +1002,7 @@ static int parse_fragment(char *line, unsigned long size,\n>  \t\tcase '-':\n>  \t\t\tif (apply_in_reverse &&\n>  \t\t\t    ws_error_action != nowarn_ws_error)\n> -\t\t\t\tcheck_whitespace(line, len);\n> +\t\t\t\tcheck_whitespace(line, len, patch->ws_rule);\n>  \t\t\tdeleted++;\n>  \t\t\toldlines--;\n>  \t\t\ttrailing = 0;\n> @@ -1009,7 +1010,7 @@ static int parse_fragment(char *line, unsigned long size,\n>  \t\tcase '+':\n>  \t\t\tif (!apply_in_reverse &&\n>  \t\t\t    ws_error_action != nowarn_ws_error)\n> -\t\t\t\tcheck_whitespace(line, len);\n> +\t\t\t\tcheck_whitespace(line, len, patch->ws_rule);\n>  \t\t\tadded++;\n>  \t\t\tnewlines--;\n>  \t\t\ttrailing = 0;\n> @@ -1318,6 +1319,10 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)\n>  \tif (offset < 0)\n>  \t\treturn offset;\n>  \n> +\tpatch->ws_rule = whitespace_rule(patch->new_name\n> +\t\t\t\t\t ? patch->new_name\n> +\t\t\t\t\t : patch->old_name);\n> +\n>  \tpatchsize = parse_single_patch(buffer + offset + hdrsize,\n>  \t\t\t\t       size - offset - hdrsize, patch);\n>  \n> @@ -1568,7 +1573,8 @@ static void remove_last_line(const char **rbuf, int *rsize)\n>  \t*rsize = offset + 1;\n>  }\n>  \n> -static int apply_line(char *output, const char *patch, int plen)\n> +static int apply_line(char *output, const char *patch, int plen,\n> +\t\t      unsigned ws_rule)\n>  {\n>  \t/*\n>  \t * plen is number of bytes to be copied from patch,\n> @@ -1593,7 +1599,7 @@ static int apply_line(char *output, const char *patch, int plen)\n>  \t/*\n>  \t * Strip trailing whitespace\n>  \t */\n> -\tif ((whitespace_rule & WS_TRAILING_SPACE) &&\n> +\tif ((ws_rule & WS_TRAILING_SPACE) &&\n>  \t    (1 < plen && isspace(patch[plen-1]))) {\n>  \t\tif (patch[plen] == '\\n')\n>  \t\t\tadd_nl_to_tail = 1;\n> @@ -1610,12 +1616,12 @@ static int apply_line(char *output, const char *patch, int plen)\n>  \t\tchar ch = patch[i];\n>  \t\tif (ch == '\\t') {\n>  \t\t\tlast_tab_in_indent = i;\n> -\t\t\tif ((whitespace_rule & WS_SPACE_BEFORE_TAB) &&\n> +\t\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) &&\n>  \t\t\t    0 <= last_space_in_indent)\n>  \t\t\t    need_fix_leading_space = 1;\n>  \t\t} else if (ch == ' ') {\n>  \t\t\tlast_space_in_indent = i;\n> -\t\t\tif ((whitespace_rule & WS_INDENT_WITH_NON_TAB) &&\n> +\t\t\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) &&\n>  \t\t\t    last_tab_in_indent < 0 &&\n>  \t\t\t    8 <= i)\n>  \t\t\t\tneed_fix_leading_space = 1;\n> @@ -1629,7 +1635,7 @@ static int apply_line(char *output, const char *patch, int plen)\n>  \t\tint consecutive_spaces = 0;\n>  \t\tint last = last_tab_in_indent + 1;\n>  \n> -\t\tif (whitespace_rule & WS_INDENT_WITH_NON_TAB) {\n> +\t\tif (ws_rule & WS_INDENT_WITH_NON_TAB) {\n>  \t\t\t/* have \"last\" point at one past the indent */\n>  \t\t\tif (last_tab_in_indent < last_space_in_indent)\n>  \t\t\t\tlast = last_space_in_indent + 1;\n> @@ -1671,7 +1677,7 @@ static int apply_line(char *output, const char *patch, int plen)\n>  }\n>  \n>  static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,\n> -\t\t\t      int inaccurate_eof)\n> +\t\t\t      int inaccurate_eof, unsigned ws_rule)\n>  {\n>  \tint match_beginning, match_end;\n>  \tconst char *patch = frag->patch;\n> @@ -1730,7 +1736,7 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,\n>  \t\tcase '+':\n>  \t\t\tif (first != '+' || !no_add) {\n>  \t\t\t\tint added = apply_line(new + newsize, patch,\n> -\t\t\t\t\t\t       plen);\n> +\t\t\t\t\t\t       plen, ws_rule);\n>  \t\t\t\tnewsize += added;\n>  \t\t\t\tif (first == '+' &&\n>  \t\t\t\t    added == 1 && new[newsize-1] == '\\n')\n> @@ -1953,12 +1959,14 @@ static int apply_fragments(struct strbuf *buf, struct patch *patch)\n>  {\n>  \tstruct fragment *frag = patch->fragments;\n>  \tconst char *name = patch->old_name ? patch->old_name : patch->new_name;\n> +\tunsigned ws_rule = patch->ws_rule;\n> +\tunsigned inaccurate_eof = patch->inaccurate_eof;\n>  \n>  \tif (patch->is_binary)\n>  \t\treturn apply_binary(buf, patch);\n>  \n>  \twhile (frag) {\n> -\t\tif (apply_one_fragment(buf, frag, patch->inaccurate_eof)) {\n> +\t\tif (apply_one_fragment(buf, frag, inaccurate_eof, ws_rule)) {\n>  \t\t\terror(\"patch failed: %s:%ld\", name, frag->oldpos);\n>  \t\t\tif (!apply_with_reject)\n>  \t\t\t\treturn -1;\n> diff --git a/cache.h b/cache.h\n> index 3f42827..9cc6268 100644\n> --- a/cache.h\n> +++ b/cache.h\n> @@ -610,6 +610,8 @@ void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, i\n>  #define WS_SPACE_BEFORE_TAB\t02\n>  #define WS_INDENT_WITH_NON_TAB\t04\n>  #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)\n> -extern unsigned whitespace_rule;\n> +extern unsigned whitespace_rule_cfg;\n> +extern unsigned whitespace_rule(const char *);\n> +extern unsigned parse_whitespace_rule(const char *);\n>  \n>  #endif /* CACHE_H */\n> diff --git a/config.c b/config.c\n> index d5b9766..2500e0d 100644\n> --- a/config.c\n> +++ b/config.c\n> @@ -246,54 +246,6 @@ static unsigned long get_unit_factor(const char *end)\n>  \tdie(\"unknown unit: '%s'\", end);\n>  }\n>  \n> -static struct whitespace_rule {\n> -\tconst char *rule_name;\n> -\tunsigned rule_bits;\n> -} whitespace_rule_names[] = {\n> -\t{ \"trailing-space\", WS_TRAILING_SPACE },\n> -\t{ \"space-before-tab\", WS_SPACE_BEFORE_TAB },\n> -\t{ \"indent-with-non-tab\", WS_INDENT_WITH_NON_TAB },\n> -};\n> -\n> -static unsigned parse_whitespace_rule(const char *string)\n> -{\n> -\tunsigned rule = WS_DEFAULT_RULE;\n> -\n> -\twhile (string) {\n> -\t\tint i;\n> -\t\tsize_t len;\n> -\t\tconst char *ep;\n> -\t\tint negated = 0;\n> -\n> -\t\tstring = string + strspn(string, \", \\t\\n\\r\");\n> -\t\tep = strchr(string, ',');\n> -\t\tif (!ep)\n> -\t\t\tlen = strlen(string);\n> -\t\telse\n> -\t\t\tlen = ep - string;\n> -\n> -\t\tif (*string == '-') {\n> -\t\t\tnegated = 1;\n> -\t\t\tstring++;\n> -\t\t\tlen--;\n> -\t\t}\n> -\t\tif (!len)\n> -\t\t\tbreak;\n> -\t\tfor (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++) {\n> -\t\t\tif (strncmp(whitespace_rule_names[i].rule_name,\n> -\t\t\t\t    string, len))\n> -\t\t\t\tcontinue;\n> -\t\t\tif (negated)\n> -\t\t\t\trule &= ~whitespace_rule_names[i].rule_bits;\n> -\t\t\telse\n> -\t\t\t\trule |= whitespace_rule_names[i].rule_bits;\n> -\t\t\tbreak;\n> -\t\t}\n> -\t\tstring = ep;\n> -\t}\n> -\treturn rule;\n> -}\n> -\n>  int git_parse_long(const char *value, long *ret)\n>  {\n>  \tif (value && *value) {\n> @@ -480,7 +432,7 @@ int git_default_config(const char *var, const char *value)\n>  \t}\n>  \n>  \tif (!strcmp(var, \"core.whitespace\")) {\n> -\t\twhitespace_rule = parse_whitespace_rule(value);\n> +\t\twhitespace_rule_cfg = parse_whitespace_rule(value);\n>  \t\treturn 0;\n>  \t}\n>  \n> diff --git a/diff.c b/diff.c\n> index 6bb902f..c3a1942 100644\n> --- a/diff.c\n> +++ b/diff.c\n> @@ -454,6 +454,7 @@ static void diff_words_show(struct diff_words_data *diff_words)\n>  struct emit_callback {\n>  \tstruct xdiff_emit_state xm;\n>  \tint nparents, color_diff;\n> +\tunsigned ws_rule;\n>  \tconst char **label_path;\n>  \tstruct diff_words_data *diff_words;\n>  \tint *found_changesp;\n> @@ -493,8 +494,8 @@ static void emit_line(const char *set, const char *reset, const char *line, int\n>  }\n>  \n>  static void emit_line_with_ws(int nparents,\n> -\t\tconst char *set, const char *reset, const char *ws,\n> -\t\tconst char *line, int len)\n> +\t\t\t      const char *set, const char *reset, const char *ws,\n> +\t\t\t      const char *line, int len, unsigned ws_rule)\n>  {\n>  \tint col0 = nparents;\n>  \tint last_tab_in_indent = -1;\n> @@ -511,7 +512,7 @@ static void emit_line_with_ws(int nparents,\n>  \tfor (i = col0; i < len; i++) {\n>  \t\tif (line[i] == '\\t') {\n>  \t\t\tlast_tab_in_indent = i;\n> -\t\t\tif ((whitespace_rule & WS_SPACE_BEFORE_TAB) &&\n> +\t\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) &&\n>  \t\t\t    0 <= last_space_in_indent)\n>  \t\t\t\tneed_highlight_leading_space = 1;\n>  \t\t}\n> @@ -520,7 +521,7 @@ static void emit_line_with_ws(int nparents,\n>  \t\telse\n>  \t\t\tbreak;\n>  \t}\n> -\tif ((whitespace_rule & WS_INDENT_WITH_NON_TAB) &&\n> +\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) &&\n>  \t    0 <= last_space_in_indent &&\n>  \t    last_tab_in_indent < 0 &&\n>  \t    8 <= (i - col0)) {\n> @@ -551,7 +552,7 @@ static void emit_line_with_ws(int nparents,\n>  \ttail = len - 1;\n>  \tif (line[tail] == '\\n' && i < tail)\n>  \t\ttail--;\n> -\tif (whitespace_rule & WS_TRAILING_SPACE) {\n> +\tif (ws_rule & WS_TRAILING_SPACE) {\n>  \t\twhile (i < tail) {\n>  \t\t\tif (!isspace(line[tail]))\n>  \t\t\t\tbreak;\n> @@ -578,7 +579,7 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons\n>  \t\temit_line(set, reset, line, len);\n>  \telse\n>  \t\temit_line_with_ws(ecbdata->nparents, set, reset, ws,\n> -\t\t\t\tline, len);\n> +\t\t\t\t  line, len, ecbdata->ws_rule);\n>  }\n>  \n>  static void fn_out_consume(void *priv, char *line, unsigned long len)\n> @@ -994,6 +995,7 @@ struct checkdiff_t {\n>  \tstruct xdiff_emit_state xm;\n>  \tconst char *filename;\n>  \tint lineno, color_diff;\n> +\tunsigned ws_rule;\n>  };\n>  \n>  static void checkdiff_consume(void *priv, char *line, unsigned long len)\n> @@ -1029,7 +1031,8 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)\n>  \t\t\tif (white_space_at_end)\n>  \t\t\t\tprintf(\"white space at end\");\n>  \t\t\tprintf(\":%s \", reset);\n> -\t\t\temit_line_with_ws(1, set, reset, ws, line, len);\n> +\t\t\temit_line_with_ws(1, set, reset, ws, line, len,\n> +\t\t\t\t\t  data->ws_rule);\n>  \t\t}\n>  \n>  \t\tdata->lineno++;\n> @@ -1330,6 +1333,7 @@ static void builtin_diff(const char *name_a,\n>  \t\tecbdata.label_path = lbl;\n>  \t\tecbdata.color_diff = o->color_diff;\n>  \t\tecbdata.found_changesp = &o->found_changes;\n> +\t\tecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);\n>  \t\txpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;\n>  \t\txecfg.ctxlen = o->context;\n>  \t\txecfg.flags = XDL_EMIT_FUNCNAMES;\n> @@ -1423,6 +1427,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,\n>  \tdata.filename = name_b ? name_b : name_a;\n>  \tdata.lineno = 0;\n>  \tdata.color_diff = o->color_diff;\n> +\tdata.ws_rule = whitespace_rule(data.filename);\n>  \n>  \tif (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)\n>  \t\tdie(\"unable to read files to diff\");\n> diff --git a/environment.c b/environment.c\n> index 624dd96..2fbbc8e 100644\n> --- a/environment.c\n> +++ b/environment.c\n> @@ -35,7 +35,7 @@ int pager_in_use;\n>  int pager_use_color = 1;\n>  char *editor_program;\n>  int auto_crlf = 0;\t/* 1: both ways, -1: only when adding git objects */\n> -unsigned whitespace_rule = WS_DEFAULT_RULE;\n> +unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;\n>  \n>  /* This is set by setup_git_dir_gently() and/or git_default_config() */\n>  char *git_work_tree_cfg;\n> diff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh\n> index dbc895b..67e080b 100755\n> --- a/t/t4019-diff-wserror.sh\n> +++ b/t/t4019-diff-wserror.sh\n> @@ -45,8 +45,24 @@ test_expect_success 'without -trail' '\n>  \n>  '\n>  \n> +test_expect_success 'without -trail (attribute)' '\n> +\n> +\tgit config --unset core.whitespace\n> +\techo \"F whitespace=-trail\" >.gitattributes\n> +\tgit diff --color >output\n> +\tgrep \"$blue_grep\" output >error\n> +\tgrep -v \"$blue_grep\" output >normal\n> +\n> +\tgrep Eight normal >/dev/null &&\n> +\tgrep HT error >/dev/null &&\n> +\tgrep With normal >/dev/null &&\n> +\tgrep No normal >/dev/null\n> +\n> +'\n> +\n>  test_expect_success 'without -space' '\n>  \n> +\trm -f .gitattributes\n>  \tgit config core.whitespace -space\n>  \tgit diff --color >output\n>  \tgrep \"$blue_grep\" output >error\n> @@ -59,8 +75,24 @@ test_expect_success 'without -space' '\n>  \n>  '\n>  \n> +test_expect_success 'without -space (attribute)' '\n> +\n> +\tgit config --unset core.whitespace\n> +\techo \"F whitespace=-space\" >.gitattributes\n> +\tgit diff --color >output\n> +\tgrep \"$blue_grep\" output >error\n> +\tgrep -v \"$blue_grep\" output >normal\n> +\n> +\tgrep Eight normal >/dev/null &&\n> +\tgrep HT normal >/dev/null &&\n> +\tgrep With error >/dev/null &&\n> +\tgrep No normal >/dev/null\n> +\n> +'\n> +\n>  test_expect_success 'with indent-non-tab only' '\n>  \n> +\trm -f .gitattributes\n>  \tgit config core.whitespace indent,-trailing,-space\n>  \tgit diff --color >output\n>  \tgrep \"$blue_grep\" output >error\n> @@ -73,4 +105,19 @@ test_expect_success 'with indent-non-tab only' '\n>  \n>  '\n>  \n> +test_expect_success 'with indent-non-tab only (attribute)' '\n> +\n> +\tgit config --unset core.whitespace\n> +\techo \"F whitespace=indent,-trailing,-space\" >.gitattributes\n> +\tgit diff --color >output\n> +\tgrep \"$blue_grep\" output >error\n> +\tgrep -v \"$blue_grep\" output >normal\n> +\n> +\tgrep Eight error >/dev/null &&\n> +\tgrep HT normal >/dev/null &&\n> +\tgrep With normal >/dev/null &&\n> +\tgrep No normal >/dev/null\n> +\n> +'\n> +\n>  test_done\n> diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh\n> index f53ac46..85f3da2 100755\n> --- a/t/t4124-apply-ws-rule.sh\n> +++ b/t/t4124-apply-ws-rule.sh\n> @@ -112,6 +112,15 @@ test_expect_success 'whitespace=error-all, no rule' '\n>  \n>  '\n>  \n> +test_expect_success 'whitespace=error-all, no rule (attribute)' '\n> +\n> +\tgit config --unset core.whitespace &&\n> +\techo \"target -whitespace\" >.gitattributes &&\n> +\tapply_patch --whitespace=error-all &&\n> +\tdiff file target\n> +\n> +'\n> +\n>  for t in - ''\n>  do\n>  \tcase \"$t\" in '') tt='!' ;; *) tt= ;; esac\n> @@ -121,11 +130,20 @@ do\n>  \t\tfor i in - ''\n>  \t\tdo\n>  \t\t\tcase \"$i\" in '') ti='#' ;; *) ti= ;; esac\n> -\t\t\trule=${t}trailing,${s}space,${i}indent &&\n> +\t\t\trule=${t}trailing,${s}space,${i}indent\n> +\n> +\t\t\trm -f .gitattributes\n>  \t\t\ttest_expect_success \"rule=$rule\" '\n>  \t\t\t\tgit config core.whitespace \"$rule\" &&\n>  \t\t\t\ttest_fix \"$tt$ts$ti\"\n>  \t\t\t'\n> +\n> +\t\t\ttest_expect_success \"rule=$rule (attributes)\" '\n> +\t\t\t\tgit config --unset core.whitespace &&\n> +\t\t\t\techo \"target whitespace=$rule\" >.gitattributes &&\n> +\t\t\t\ttest_fix \"$tt$ts$ti\"\n> +\t\t\t'\n> +\n>  \t\tdone\n>  \tdone\n>  done\n> diff --git a/ws.c b/ws.c\n> new file mode 100644\n> index 0000000..52c10ca\n> --- /dev/null\n> +++ b/ws.c\n> @@ -0,0 +1,96 @@\n> +/*\n> + * Whitespace rules\n> + *\n> + * Copyright (c) 2007 Junio C Hamano\n> + */\n> +\n> +#include \"cache.h\"\n> +#include \"attr.h\"\n> +\n> +static struct whitespace_rule {\n> +\tconst char *rule_name;\n> +\tunsigned rule_bits;\n> +} whitespace_rule_names[] = {\n> +\t{ \"trailing-space\", WS_TRAILING_SPACE },\n> +\t{ \"space-before-tab\", WS_SPACE_BEFORE_TAB },\n> +\t{ \"indent-with-non-tab\", WS_INDENT_WITH_NON_TAB },\n> +};\n> +\n> +unsigned parse_whitespace_rule(const char *string)\n> +{\n> +\tunsigned rule = WS_DEFAULT_RULE;\n> +\n> +\twhile (string) {\n> +\t\tint i;\n> +\t\tsize_t len;\n> +\t\tconst char *ep;\n> +\t\tint negated = 0;\n> +\n> +\t\tstring = string + strspn(string, \", \\t\\n\\r\");\n> +\t\tep = strchr(string, ',');\n> +\t\tif (!ep)\n> +\t\t\tlen = strlen(string);\n> +\t\telse\n> +\t\t\tlen = ep - string;\n> +\n> +\t\tif (*string == '-') {\n> +\t\t\tnegated = 1;\n> +\t\t\tstring++;\n> +\t\t\tlen--;\n> +\t\t}\n> +\t\tif (!len)\n> +\t\t\tbreak;\n> +\t\tfor (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++) {\n> +\t\t\tif (strncmp(whitespace_rule_names[i].rule_name,\n> +\t\t\t\t    string, len))\n> +\t\t\t\tcontinue;\n> +\t\t\tif (negated)\n> +\t\t\t\trule &= ~whitespace_rule_names[i].rule_bits;\n> +\t\t\telse\n> +\t\t\t\trule |= whitespace_rule_names[i].rule_bits;\n> +\t\t\tbreak;\n> +\t\t}\n> +\t\tstring = ep;\n> +\t}\n> +\treturn rule;\n> +}\n> +\n> +static void setup_whitespace_attr_check(struct git_attr_check *check)\n> +{\n> +\tstatic struct git_attr *attr_whitespace;\n> +\n> +\tif (!attr_whitespace)\n> +\t\tattr_whitespace = git_attr(\"whitespace\", 10);\n> +\tcheck[0].attr = attr_whitespace;\n> +}\n> +\n> +unsigned whitespace_rule(const char *pathname)\n> +{\n> +\tstruct git_attr_check attr_whitespace_rule;\n> +\n> +\tsetup_whitespace_attr_check(&attr_whitespace_rule);\n> +\tif (!git_checkattr(pathname, 1, &attr_whitespace_rule)) {\n> +\t\tconst char *value;\n> +\n> +\t\tvalue = attr_whitespace_rule.value;\n> +\t\tif (ATTR_TRUE(value)) {\n> +\t\t\t/* true (whitespace) */\n> +\t\t\tunsigned all_rule = 0;\n> +\t\t\tint i;\n> +\t\t\tfor (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)\n> +\t\t\t\tall_rule |= whitespace_rule_names[i].rule_bits;\n> +\t\t\treturn all_rule;\n> +\t\t} else if (ATTR_FALSE(value)) {\n> +\t\t\t/* false (-whitespace) */\n> +\t\t\treturn 0;\n> +\t\t} else if (ATTR_UNSET(value)) {\n> +\t\t\t/* reset to default (!whitespace) */\n> +\t\t\treturn whitespace_rule_cfg;\n> +\t\t} else {\n> +\t\t\t/* string */\n> +\t\t\treturn parse_whitespace_rule(value);\n> +\t\t}\n> +\t} else {\n> +\t\treturn whitespace_rule_cfg;\n> +\t}\n> +}\n> -- \n> 1.5.3.7-2155-g4c25\n> \n"},{"id":"63292","messageId":"1197776919-16121-1-git-send-email-bfields@citi.umich.edu","threadId":"11000","inReplyTo":"7vodd4fb2f.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 3/2] core.whitespace: documentation updates.","fromName":"J. Bruce Fields","fromEmail":"bfields@citi.umich.edu","sentAt":"2007-12-16T03:48:33Z","receivedAt":"2007-12-16T03:48:33Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"I wrote before:\n> On Thu, Dec 06, 2007 at 01:04:56AM -0800, Junio C Hamano wrote:\n> > \"J. Bruce Fields\" <bfields@fieldses.org> writes:\n> > > OK, I will go wild, but... very slowly.\n> >\n> > How wild are you these days ;-)?  I know December is a busy time for\n> > everybody, and I ended up doing this myself while I was writing up the\n> > API documentation for gitattributes.\n> \n> Wow, thanks!  Yes, I haven't done a thing on this.\n> \n> > -- >8 --\n> > [PATCH] Use gitattributes to define per-path whitespace rule\n> >  \n> > The `core.whitespace` configuration variable allows you to define what\n> > `diff` and `apply` should consider whitespace errors for all paths in\n> > the project (See gitlink:git-config[1]).  This attribute gives you\n> > finer\n> > control per path.\n> \n> That looks like what I'd hoped for.\n> \n> I'll set aside some time this weekend to play around with it.\n\nErm, well, some weekend anyway.  You can also pull the following from\n\n\tgit://linux-nfs.org/~bfields/git.git master\n\nif you'd like.\n\n--b.\n"},{"id":"63293","messageId":"1197776919-16121-2-git-send-email-bfields@citi.umich.edu","threadId":"11000","inReplyTo":"1197776919-16121-1-git-send-email-bfields@citi.umich.edu","subject":"[PATCH] whitespace: fix off-by-one error in non-space-in-indent checking","fromName":"J. Bruce Fields","fromEmail":"bfields@citi.umich.edu","sentAt":"2007-12-16T03:48:34Z","receivedAt":"2007-12-16T03:48:34Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"If there were no tabs, and the last space was at position 7, then\npositions 0..7 had spaces, so there were 8 spaces.\n\nUpdate test to check exactly this case.\n\nSigned-off-by: J. Bruce Fields <bfields@citi.umich.edu>\n---\n t/t4015-diff-whitespace.sh |    4 ++--\n ws.c                       |    2 +-\n 2 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\nindex 9bff8f5..0f16bca 100755\n--- a/t/t4015-diff-whitespace.sh\n+++ b/t/t4015-diff-whitespace.sh\n@@ -298,7 +298,7 @@ test_expect_success 'check space before tab in indent (space-before-tab: on)' '\n test_expect_success 'check spaces as indentation (indent-with-non-tab: off)' '\n \n \tgit config core.whitespace \"-indent-with-non-tab\"\n-\techo \"                foo ();\" > x &&\n+\techo \"        foo ();\" > x &&\n \tgit diff --check\n \n '\n@@ -306,7 +306,7 @@ test_expect_success 'check spaces as indentation (indent-with-non-tab: off)' '\n test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' '\n \n \tgit config core.whitespace \"indent-with-non-tab\" &&\n-\techo \"                foo ();\" > x &&\n+\techo \"        foo ();\" > x &&\n \t! git diff --check\n \n '\ndiff --git a/ws.c b/ws.c\nindex 46cbdd6..5ebd109 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -159,5 +159,5 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\n \t}\n \n \t/* Check for indent using non-tab. */\n-\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && leading_space >= 8)\n+\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && leading_space >= 7)\n \t\tresult |= WS_INDENT_WITH_NON_TAB;\n\\ No newline at end of file\n-- \n1.5.4.rc0.41.gf723\n"},{"id":"63296","messageId":"1197776919-16121-3-git-send-email-bfields@citi.umich.edu","threadId":"11000","inReplyTo":"1197776919-16121-2-git-send-email-bfields@citi.umich.edu","subject":"[PATCH] whitespace: reorganize initial-indent check","fromName":"J. Bruce Fields","fromEmail":"bfields@citi.umich.edu","sentAt":"2007-12-16T03:48:35Z","receivedAt":"2007-12-16T03:48:35Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"Reorganize to emphasize the most complicated part of the code (the tab\ncase).\n\nSigned-off-by: J. Bruce Fields <bfields@citi.umich.edu>\n---\n ws.c |   15 +++++++--------\n 1 files changed, 7 insertions(+), 8 deletions(-)\n\ndiff --git a/ws.c b/ws.c\nindex 5ebd109..7165874 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -146,16 +146,15 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\n \n \t/* Check for space before tab in initial indent. */\n \tfor (i = 0; i < len; i++) {\n-\t\tif (line[i] == '\\t') {\n-\t\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) &&\n-\t\t\t    (leading_space != -1))\n-\t\t\t\tresult |= WS_SPACE_BEFORE_TAB;\n-\t\t\tbreak;\n-\t\t}\n-\t\telse if (line[i] == ' ')\n+\t\tif (line[i] == ' ') {\n \t\t\tleading_space = i;\n-\t\telse\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (line[i] != '\\t')\n \t\t\tbreak;\n+\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) && (leading_space != -1))\n+\t\t\tresult |= WS_SPACE_BEFORE_TAB;\n+\t\tbreak;\n \t}\n \n \t/* Check for indent using non-tab. */\n-- \n1.5.4.rc0.41.gf723\n"},{"id":"63297","messageId":"1197776919-16121-4-git-send-email-bfields@citi.umich.edu","threadId":"11000","inReplyTo":"1197776919-16121-3-git-send-email-bfields@citi.umich.edu","subject":"[PATCH] whitespace: minor cleanup","fromName":"J. Bruce Fields","fromEmail":"bfields@citi.umich.edu","sentAt":"2007-12-16T03:48:36Z","receivedAt":"2007-12-16T03:48:36Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"The variable leading_space is initially used to represent the index of\nthe last space seen before a non-space.  Then later it represents the\nindex of the first non-indent character.\n\nIt will prove simpler to replace it by a variable representing a number\nof bytes.  Eventually it will represent the number of bytes written so\nfar (in the stream != NULL case).\n\nSigned-off-by: J. Bruce Fields <bfields@citi.umich.edu>\n---\n ws.c |   21 +++++++++------------\n 1 files changed, 9 insertions(+), 12 deletions(-)\n\ndiff --git a/ws.c b/ws.c\nindex 7165874..1b32e45 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -121,7 +121,7 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\n \t\t\t     const char *reset, const char *ws)\n {\n \tunsigned result = 0;\n-\tint leading_space = -1;\n+\tint written = 0;\n \tint trailing_whitespace = -1;\n \tint trailing_newline = 0;\n \tint i;\n@@ -147,18 +147,18 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\n \t/* Check for space before tab in initial indent. */\n \tfor (i = 0; i < len; i++) {\n \t\tif (line[i] == ' ') {\n-\t\t\tleading_space = i;\n+\t\t\twritten = i + 1;\n \t\t\tcontinue;\n \t\t}\n \t\tif (line[i] != '\\t')\n \t\t\tbreak;\n-\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) && (leading_space != -1))\n+\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) && (written != 0))\n \t\t\tresult |= WS_SPACE_BEFORE_TAB;\n \t\tbreak;\n \t}\n \n \t/* Check for indent using non-tab. */\n-\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && leading_space >= 7)\n+\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && written >= 8)\n \t\tresult |= WS_INDENT_WITH_NON_TAB;\n \n \tif (stream) {\n@@ -166,23 +166,20 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\n \t\tif ((result & WS_SPACE_BEFORE_TAB) ||\n \t\t    (result & WS_INDENT_WITH_NON_TAB)) {\n \t\t\tfputs(ws, stream);\n-\t\t\tfwrite(line, leading_space + 1, 1, stream);\n+\t\t\tfwrite(line, written, 1, stream);\n \t\t\tfputs(reset, stream);\n-\t\t\tleading_space++;\n \t\t}\n-\t\telse\n-\t\t\tleading_space = 0;\n \n-\t\t/* Now the rest of the line starts at leading_space.\n+\t\t/* Now the rest of the line starts at written.\n \t\t * The non-highlighted part ends at trailing_whitespace. */\n \t\tif (trailing_whitespace == -1)\n \t\t\ttrailing_whitespace = len;\n \n \t\t/* Emit non-highlighted (middle) segment. */\n-\t\tif (trailing_whitespace - leading_space > 0) {\n+\t\tif (trailing_whitespace - written > 0) {\n \t\t\tfputs(set, stream);\n-\t\t\tfwrite(line + leading_space,\n-\t\t\t    trailing_whitespace - leading_space, 1, stream);\n+\t\t\tfwrite(line + written,\n+\t\t\t    trailing_whitespace - written, 1, stream);\n \t\t\tfputs(reset, stream);\n \t\t}\n \n-- \n1.5.4.rc0.41.gf723\n"},{"id":"63295","messageId":"1197776919-16121-5-git-send-email-bfields@citi.umich.edu","threadId":"11000","inReplyTo":"1197776919-16121-4-git-send-email-bfields@citi.umich.edu","subject":"[PATCH] whitespace: fix initial-indent checking","fromName":"J. Bruce Fields","fromEmail":"bfields@citi.umich.edu","sentAt":"2007-12-16T03:48:37Z","receivedAt":"2007-12-16T03:48:37Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"After this patch, \"written\" counts the number of bytes up to and\nincluding the most recently seen tab.  This allows us to detect (and\ncount) spaces by comparing to \"i\".\n\nThis allows catching initial indents like '\\t        ' (a tab followed\nby 8 spaces), while previously indent-with-non-tab caught only indents\nthat consisted entirely of spaces.\n\nThis also allows fixing an indent-with-non-tab regression, so we can\nagain detect indents like '\\t \\t'.\n\nAlso update tests to catch these cases.\n\nSigned-off-by: J. Bruce Fields <bfields@citi.umich.edu>\n---\n t/t4015-diff-whitespace.sh |   15 +++++++++++++++\n ws.c                       |   10 ++++------\n 2 files changed, 19 insertions(+), 6 deletions(-)\n\ndiff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\nindex 0f16bca..d30169f 100755\n--- a/t/t4015-diff-whitespace.sh\n+++ b/t/t4015-diff-whitespace.sh\n@@ -125,6 +125,14 @@ test_expect_success 'check mixed spaces and tabs in indent' '\n \n '\n \n+test_expect_success 'check mixed tabs and spaces in indent' '\n+\n+\t# This is indented with HT SP HT.\n+\techo \"\t \tfoo();\" > x &&\n+\tgit diff --check | grep \"space before tab in indent\"\n+\n+'\n+\n test_expect_success 'check with no whitespace errors' '\n \n \tgit commit -m \"snapshot\" &&\n@@ -311,4 +319,11 @@ test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' '\n \n '\n \n+test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: on)' '\n+\n+\tgit config core.whitespace \"indent-with-non-tab\" &&\n+\techo \"\t                foo ();\" > x &&\n+\t! git diff --check\n+\n+'\n test_done\ndiff --git a/ws.c b/ws.c\nindex 1b32e45..aabd509 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -146,19 +146,17 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\n \n \t/* Check for space before tab in initial indent. */\n \tfor (i = 0; i < len; i++) {\n-\t\tif (line[i] == ' ') {\n-\t\t\twritten = i + 1;\n+\t\tif (line[i] == ' ')\n \t\t\tcontinue;\n-\t\t}\n \t\tif (line[i] != '\\t')\n \t\t\tbreak;\n-\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) && (written != 0))\n+\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) && written < i)\n \t\t\tresult |= WS_SPACE_BEFORE_TAB;\n-\t\tbreak;\n+\t\twritten = i + 1;\n \t}\n \n \t/* Check for indent using non-tab. */\n-\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && written >= 8)\n+\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= 8)\n \t\tresult |= WS_INDENT_WITH_NON_TAB;\n \n \tif (stream) {\n-- \n1.5.4.rc0.41.gf723\n"},{"id":"63294","messageId":"1197776919-16121-6-git-send-email-bfields@citi.umich.edu","threadId":"11000","inReplyTo":"1197776919-16121-5-git-send-email-bfields@citi.umich.edu","subject":"[PATCH] whitespace: more accurate initial-indent highlighting","fromName":"J. Bruce Fields","fromEmail":"bfields@citi.umich.edu","sentAt":"2007-12-16T03:48:38Z","receivedAt":"2007-12-16T03:48:38Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"Instead of highlighting the entire initial indent, highlight only the\nproblematic spaces.\n\nIn the case of an indent like ' \\t \\t' there may be multiple problematic\nranges, so it's easiest to emit the highlighting as we go instead of\ntrying rember disjoint ranges and do it all at the end.\n\nSigned-off-by: J. Bruce Fields <bfields@citi.umich.edu>\n---\n ws.c |   24 ++++++++++++++++--------\n 1 files changed, 16 insertions(+), 8 deletions(-)\n\ndiff --git a/ws.c b/ws.c\nindex aabd509..d09b9df 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -150,24 +150,32 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\n \t\t\tcontinue;\n \t\tif (line[i] != '\\t')\n \t\t\tbreak;\n-\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) && written < i)\n+\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) && written < i) {\n \t\t\tresult |= WS_SPACE_BEFORE_TAB;\n+\t\t\tif (stream) {\n+\t\t\t\tfputs(ws, stream);\n+\t\t\t\tfwrite(line + written, i - written, 1, stream);\n+\t\t\t\tfputs(reset, stream);\n+\t\t\t}\n+\t\t} else if (stream)\n+\t\t\tfwrite(line + written, i - written, 1, stream);\n+\t\tif (stream)\n+\t\t\tfwrite(line + i, 1, 1, stream);\n \t\twritten = i + 1;\n \t}\n \n \t/* Check for indent using non-tab. */\n-\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= 8)\n+\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= 8) {\n \t\tresult |= WS_INDENT_WITH_NON_TAB;\n-\n-\tif (stream) {\n-\t\t/* Highlight errors in leading whitespace. */\n-\t\tif ((result & WS_SPACE_BEFORE_TAB) ||\n-\t\t    (result & WS_INDENT_WITH_NON_TAB)) {\n+\t\tif (stream) {\n \t\t\tfputs(ws, stream);\n-\t\t\tfwrite(line, written, 1, stream);\n+\t\t\tfwrite(line + written, i - written, 1, stream);\n \t\t\tfputs(reset, stream);\n \t\t}\n+\t\twritten = i;\n+\t}\n \n+\tif (stream) {\n \t\t/* Now the rest of the line starts at written.\n \t\t * The non-highlighted part ends at trailing_whitespace. */\n \t\tif (trailing_whitespace == -1)\n-- \n1.5.4.rc0.41.gf723\n"},{"id":"63298","messageId":"1197776919-16121-7-git-send-email-bfields@citi.umich.edu","threadId":"11000","inReplyTo":"1197776919-16121-6-git-send-email-bfields@citi.umich.edu","subject":"[PATCH] whitespace: fix config.txt description of indent-with-non-tab","fromName":"J. Bruce Fields","fromEmail":"bfields@citi.umich.edu","sentAt":"2007-12-16T03:48:39Z","receivedAt":"2007-12-16T03:48:39Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"Fix garbled description.\n\nSigned-off-by: J. Bruce Fields <bfields@citi.umich.edu>\n---\n Documentation/config.txt |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex fabe7f8..ce16fc7 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -307,7 +307,7 @@ core.whitespace::\n   before a tab character in the initial indent part of the line as an\n   error (enabled by default).\n * `indent-with-non-tab` treats a line that is indented with 8 or more\n-  space characters that can be replaced with tab characters.\n+  space characters as an error (not enabled by default).\n \n alias.*::\n \tCommand aliases for the gitlink:git[1] command wrapper - e.g.\n-- \n1.5.4.rc0.41.gf723\n"},{"id":"63299","messageId":"20071216035440.GM14377@fieldses.org","threadId":"11000","inReplyTo":"1197776919-16121-5-git-send-email-bfields@citi.umich.edu","subject":"Re: [PATCH] whitespace: fix initial-indent checking","fromName":"J. Bruce Fields","fromEmail":"bfields@fieldses.org","sentAt":"2007-12-16T03:54:40Z","receivedAt":"2007-12-16T03:54:40Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"On Sat, Dec 15, 2007 at 10:48:37PM -0500, J. Bruce Fields wrote:\n> After this patch, \"written\" counts the number of bytes up to and\n> including the most recently seen tab.  This allows us to detect (and\n> count) spaces by comparing to \"i\".\n> \n> This allows catching initial indents like '\\t        ' (a tab followed\n> by 8 spaces), while previously indent-with-non-tab caught only indents\n> that consisted entirely of spaces.\n> \n> This also allows fixing an indent-with-non-tab regression, so we can\n> again detect indents like '\\t \\t'.\n> \n> Also update tests to catch these cases.\n\nOne slightly weird thing about this: I'd expect indent-with-non-tab to\ncatch any sequence of 8 or more contiguous spaces, not just such\nsequences at the end of the indent.  This doesn't quite do that.\n\nYou could make it do that with a few more lines of code.  But really I\ndon't think the combination of indent-with-non-tab without\nspace-before-tab makes any sense.\n\nThe only reason I didn't just modify it to turn on the latter whenever\nthe former is turned on is because I couldn't figure out how to modify\nt/t4124-apply-ws-rule.sh to pass.....\n\nBut, whatever, that's an extremely minor point.  If people try that\ncombination who knows what they expect.\n\n--b.\n"},{"id":"63309","messageId":"fk2pua$b4p$1@ger.gmane.org","threadId":"11000","inReplyTo":"1197776919-16121-5-git-send-email-bfields@citi.umich.edu","subject":"Re: [PATCH] whitespace: fix initial-indent checking","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-12-16T09:08:25Z","receivedAt":"2007-12-16T09:08:25Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"J. Bruce Fields wrote:\n\n> This allows catching initial indents like '\\t        ' (a tab followed\n> by 8 spaces), while previously indent-with-non-tab caught only indents\n> that consisted entirely of spaces.\n\nI prefer to use tabs for indent, but _spaces_ for align. While previous,\nless strict version of check catches indent using spaces, this one also\ncatches _align_ using spaces.\n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"63312","messageId":"25FDB05F-3E85-4E08-90BE-1BE468C07805@wincent.com","threadId":"11000","inReplyTo":"fk2pua$b4p$1@ger.gmane.org","subject":"Re: [PATCH] whitespace: fix initial-indent checking","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-12-16T10:00:55Z","receivedAt":"2007-12-16T10:00:55Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"El 16/12/2007, a las 10:08, Jakub Narebski escribió:\n\n> J. Bruce Fields wrote:\n>\n>> This allows catching initial indents like '\\t        ' (a tab  \n>> followed\n>> by 8 spaces), while previously indent-with-non-tab caught only  \n>> indents\n>> that consisted entirely of spaces.\n>\n> I prefer to use tabs for indent, but _spaces_ for align. While  \n> previous,\n> less strict version of check catches indent using spaces, this one  \n> also\n> catches _align_ using spaces.\n\nI'd say that Jakub's is a fairly common use case (it's used in many  \nplaces in the Git codebase too, I think) so it would be a bad thing to  \nchange the behaviour of \"indent-with-non-tab\".\n\nIf you also want to check for \"align-with-non-tab\" then it really  \nshould be a separate, optional class of whitespace error.\n\nCheers,\nWincent\n"},{"id":"63313","messageId":"B54C9483-90BE-4B45-A3B7-39FACF0E9F62@wincent.com","threadId":"11000","inReplyTo":"1197776919-16121-3-git-send-email-bfields@citi.umich.edu","subject":"Re: [PATCH] whitespace: reorganize initial-indent check","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-12-16T10:02:32Z","receivedAt":"2007-12-16T10:02:32Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"El 16/12/2007, a las 4:48, J. Bruce Fields escribió:\n\n> Reorganize to emphasize the most complicated part of the code (the tab\n> case).\n\nAny chance of either squashing this series into one patch seeing as  \nits all churning over the same part of the code, or resending it with  \nnumbering? The patches seemed to arrive out of order in my mailbox and  \nI don't really know what order they're supposed to be applied in and  \nit's a bit hard to review.\n\nCheers,\nWincent\n"},{"id":"63323","messageId":"20071216162637.GA3934@fieldses.org","threadId":"11000","inReplyTo":"25FDB05F-3E85-4E08-90BE-1BE468C07805@wincent.com","subject":"Re: [PATCH] whitespace: fix initial-indent checking","fromName":"J. Bruce Fields","fromEmail":"bfields@fieldses.org","sentAt":"2007-12-16T16:26:37Z","receivedAt":"2007-12-16T16:26:37Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"On Sun, Dec 16, 2007 at 11:00:55AM +0100, Wincent Colaiuta wrote:\n> El 16/12/2007, a las 10:08, Jakub Narebski escribió:\n>\n>> J. Bruce Fields wrote:\n>>\n>>> This allows catching initial indents like '\\t        ' (a tab followed\n>>> by 8 spaces), while previously indent-with-non-tab caught only indents\n>>> that consisted entirely of spaces.\n>>\n>> I prefer to use tabs for indent, but _spaces_ for align. While previous,\n>> less strict version of check catches indent using spaces, this one also\n>> catches _align_ using spaces.\n\nNo, the previous version didn't work for the align-with-spaces case\neither.  Consider, for example,\n\nstruct widget *find_widget_by_color(struct color *color,\n                                    int nth_match, unsigned long flags)\n\nIf following a \"indent-with-tabs, align-with-spaces\" policy, then the\ninitial whitespaace on the second line should be purely spaces\n(otherwise adjusting the tab stops would ruin the alignment).  But\nindent-with-non-tab would flag this as incorrect even before my fix.\n\n> I'd say that Jakub's is a fairly common use case (it's used in many places \n> in the Git codebase too, I think) so it would be a bad thing to change the \n> behaviour of \"indent-with-non-tab\".\n>\n> If you also want to check for \"align-with-non-tab\" then it really should be \n> a separate, optional class of whitespace error.\n\nI would agree with you if it were not for the fact that if you're using\nan \"indent-with-tabs, align-with-spaces\" policy then the only indent\nwhitespace problems that you can flag automatically are space-before-tab\nproblems; anything else requires knowledge of the language syntax.\n\nSo indent-with-non-tab has only ever been useful for projects that\ninsist on tabs for all sequences of 8 spaces in the initial whitespace.\n\n--b.\n"},{"id":"63328","messageId":"1197822702-5262-1-git-send-email-bfields@citi.umich.edu","threadId":"11000","inReplyTo":"B54C9483-90BE-4B45-A3B7-39FACF0E9F62@wincent.com","subject":"Re: [PATCH] whitespace: reorganize initial-indent check","fromName":"J. Bruce Fields","fromEmail":"bfields@citi.umich.edu","sentAt":"2007-12-16T16:31:36Z","receivedAt":"2007-12-16T16:31:36Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"Wincent Colaiuta wrote:\n> Any chance of either squashing this series into one patch seeing as\n> its all churning over the same part of the code, or resending it with\n> numbering?  The patches seemed to arrive out of order in my mailbox\n> and I don't really know what order they're supposed to be applied in\n> and it's a bit hard to review.\n\nApologies--I forgot the -n on format-patch.... Here you go.  Thanks for\nthe review.\n\n--b.\n"},{"id":"63324","messageId":"1197822702-5262-2-git-send-email-bfields@citi.umich.edu","threadId":"11000","inReplyTo":"1197822702-5262-1-git-send-email-bfields@citi.umich.edu","subject":"[PATCH 1/6] whitespace: fix off-by-one error in non-space-in-indent checking","fromName":"J. Bruce Fields","fromEmail":"bfields@citi.umich.edu","sentAt":"2007-12-16T16:31:37Z","receivedAt":"2007-12-16T16:31:37Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"If there were no tabs, and the last space was at position 7, then\npositions 0..7 had spaces, so there were 8 spaces.\n\nUpdate test to check exactly this case.\n\nSigned-off-by: J. Bruce Fields <bfields@citi.umich.edu>\n---\n t/t4015-diff-whitespace.sh |    4 ++--\n ws.c                       |    2 +-\n 2 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\nindex 9bff8f5..0f16bca 100755\n--- a/t/t4015-diff-whitespace.sh\n+++ b/t/t4015-diff-whitespace.sh\n@@ -298,7 +298,7 @@ test_expect_success 'check space before tab in indent (space-before-tab: on)' '\n test_expect_success 'check spaces as indentation (indent-with-non-tab: off)' '\n \n \tgit config core.whitespace \"-indent-with-non-tab\"\n-\techo \"                foo ();\" > x &&\n+\techo \"        foo ();\" > x &&\n \tgit diff --check\n \n '\n@@ -306,7 +306,7 @@ test_expect_success 'check spaces as indentation (indent-with-non-tab: off)' '\n test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' '\n \n \tgit config core.whitespace \"indent-with-non-tab\" &&\n-\techo \"                foo ();\" > x &&\n+\techo \"        foo ();\" > x &&\n \t! git diff --check\n \n '\ndiff --git a/ws.c b/ws.c\nindex 46cbdd6..5ebd109 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -159,5 +159,5 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\n \t}\n \n \t/* Check for indent using non-tab. */\n-\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && leading_space >= 8)\n+\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && leading_space >= 7)\n \t\tresult |= WS_INDENT_WITH_NON_TAB;\n\\ No newline at end of file\n-- \n1.5.4.rc0.41.gf723\n"},{"id":"63325","messageId":"1197822702-5262-3-git-send-email-bfields@citi.umich.edu","threadId":"11000","inReplyTo":"1197822702-5262-2-git-send-email-bfields@citi.umich.edu","subject":"[PATCH 2/6] whitespace: reorganize initial-indent check","fromName":"J. Bruce Fields","fromEmail":"bfields@citi.umich.edu","sentAt":"2007-12-16T16:31:38Z","receivedAt":"2007-12-16T16:31:38Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"Reorganize to emphasize the most complicated part of the code (the tab\ncase).\n\nSigned-off-by: J. Bruce Fields <bfields@citi.umich.edu>\n---\n ws.c |   15 +++++++--------\n 1 files changed, 7 insertions(+), 8 deletions(-)\n\ndiff --git a/ws.c b/ws.c\nindex 5ebd109..7165874 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -146,16 +146,15 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\n \n \t/* Check for space before tab in initial indent. */\n \tfor (i = 0; i < len; i++) {\n-\t\tif (line[i] == '\\t') {\n-\t\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) &&\n-\t\t\t    (leading_space != -1))\n-\t\t\t\tresult |= WS_SPACE_BEFORE_TAB;\n-\t\t\tbreak;\n-\t\t}\n-\t\telse if (line[i] == ' ')\n+\t\tif (line[i] == ' ') {\n \t\t\tleading_space = i;\n-\t\telse\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (line[i] != '\\t')\n \t\t\tbreak;\n+\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) && (leading_space != -1))\n+\t\t\tresult |= WS_SPACE_BEFORE_TAB;\n+\t\tbreak;\n \t}\n \n \t/* Check for indent using non-tab. */\n-- \n1.5.4.rc0.41.gf723\n"},{"id":"63330","messageId":"1197822702-5262-4-git-send-email-bfields@citi.umich.edu","threadId":"11000","inReplyTo":"1197822702-5262-3-git-send-email-bfields@citi.umich.edu","subject":"[PATCH 3/6] whitespace: minor cleanup","fromName":"J. Bruce Fields","fromEmail":"bfields@citi.umich.edu","sentAt":"2007-12-16T16:31:39Z","receivedAt":"2007-12-16T16:31:39Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"The variable leading_space is initially used to represent the index of\nthe last space seen before a non-space.  Then later it represents the\nindex of the first non-indent character.\n\nIt will prove simpler to replace it by a variable representing a number\nof bytes.  Eventually it will represent the number of bytes written so\nfar (in the stream != NULL case).\n\nSigned-off-by: J. Bruce Fields <bfields@citi.umich.edu>\n---\n ws.c |   21 +++++++++------------\n 1 files changed, 9 insertions(+), 12 deletions(-)\n\ndiff --git a/ws.c b/ws.c\nindex 7165874..1b32e45 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -121,7 +121,7 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\n \t\t\t     const char *reset, const char *ws)\n {\n \tunsigned result = 0;\n-\tint leading_space = -1;\n+\tint written = 0;\n \tint trailing_whitespace = -1;\n \tint trailing_newline = 0;\n \tint i;\n@@ -147,18 +147,18 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\n \t/* Check for space before tab in initial indent. */\n \tfor (i = 0; i < len; i++) {\n \t\tif (line[i] == ' ') {\n-\t\t\tleading_space = i;\n+\t\t\twritten = i + 1;\n \t\t\tcontinue;\n \t\t}\n \t\tif (line[i] != '\\t')\n \t\t\tbreak;\n-\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) && (leading_space != -1))\n+\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) && (written != 0))\n \t\t\tresult |= WS_SPACE_BEFORE_TAB;\n \t\tbreak;\n \t}\n \n \t/* Check for indent using non-tab. */\n-\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && leading_space >= 7)\n+\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && written >= 8)\n \t\tresult |= WS_INDENT_WITH_NON_TAB;\n \n \tif (stream) {\n@@ -166,23 +166,20 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\n \t\tif ((result & WS_SPACE_BEFORE_TAB) ||\n \t\t    (result & WS_INDENT_WITH_NON_TAB)) {\n \t\t\tfputs(ws, stream);\n-\t\t\tfwrite(line, leading_space + 1, 1, stream);\n+\t\t\tfwrite(line, written, 1, stream);\n \t\t\tfputs(reset, stream);\n-\t\t\tleading_space++;\n \t\t}\n-\t\telse\n-\t\t\tleading_space = 0;\n \n-\t\t/* Now the rest of the line starts at leading_space.\n+\t\t/* Now the rest of the line starts at written.\n \t\t * The non-highlighted part ends at trailing_whitespace. */\n \t\tif (trailing_whitespace == -1)\n \t\t\ttrailing_whitespace = len;\n \n \t\t/* Emit non-highlighted (middle) segment. */\n-\t\tif (trailing_whitespace - leading_space > 0) {\n+\t\tif (trailing_whitespace - written > 0) {\n \t\t\tfputs(set, stream);\n-\t\t\tfwrite(line + leading_space,\n-\t\t\t    trailing_whitespace - leading_space, 1, stream);\n+\t\t\tfwrite(line + written,\n+\t\t\t    trailing_whitespace - written, 1, stream);\n \t\t\tfputs(reset, stream);\n \t\t}\n \n-- \n1.5.4.rc0.41.gf723\n"},{"id":"63329","messageId":"1197822702-5262-5-git-send-email-bfields@citi.umich.edu","threadId":"11000","inReplyTo":"1197822702-5262-4-git-send-email-bfields@citi.umich.edu","subject":"[PATCH 4/6] whitespace: fix initial-indent checking","fromName":"J. Bruce Fields","fromEmail":"bfields@citi.umich.edu","sentAt":"2007-12-16T16:31:40Z","receivedAt":"2007-12-16T16:31:40Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"After this patch, \"written\" counts the number of bytes up to and\nincluding the most recently seen tab.  This allows us to detect (and\ncount) spaces by comparing to \"i\".\n\nThis allows catching initial indents like '\\t        ' (a tab followed\nby 8 spaces), while previously indent-with-non-tab caught only indents\nthat consisted entirely of spaces.\n\nThis also allows fixing an indent-with-non-tab regression, so we can\nagain detect indents like '\\t \\t'.\n\nAlso update tests to catch these cases.\n\nSigned-off-by: J. Bruce Fields <bfields@citi.umich.edu>\n---\n t/t4015-diff-whitespace.sh |   15 +++++++++++++++\n ws.c                       |   10 ++++------\n 2 files changed, 19 insertions(+), 6 deletions(-)\n\ndiff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\nindex 0f16bca..d30169f 100755\n--- a/t/t4015-diff-whitespace.sh\n+++ b/t/t4015-diff-whitespace.sh\n@@ -125,6 +125,14 @@ test_expect_success 'check mixed spaces and tabs in indent' '\n \n '\n \n+test_expect_success 'check mixed tabs and spaces in indent' '\n+\n+\t# This is indented with HT SP HT.\n+\techo \"\t \tfoo();\" > x &&\n+\tgit diff --check | grep \"space before tab in indent\"\n+\n+'\n+\n test_expect_success 'check with no whitespace errors' '\n \n \tgit commit -m \"snapshot\" &&\n@@ -311,4 +319,11 @@ test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' '\n \n '\n \n+test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: on)' '\n+\n+\tgit config core.whitespace \"indent-with-non-tab\" &&\n+\techo \"\t                foo ();\" > x &&\n+\t! git diff --check\n+\n+'\n test_done\ndiff --git a/ws.c b/ws.c\nindex 1b32e45..aabd509 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -146,19 +146,17 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\n \n \t/* Check for space before tab in initial indent. */\n \tfor (i = 0; i < len; i++) {\n-\t\tif (line[i] == ' ') {\n-\t\t\twritten = i + 1;\n+\t\tif (line[i] == ' ')\n \t\t\tcontinue;\n-\t\t}\n \t\tif (line[i] != '\\t')\n \t\t\tbreak;\n-\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) && (written != 0))\n+\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) && written < i)\n \t\t\tresult |= WS_SPACE_BEFORE_TAB;\n-\t\tbreak;\n+\t\twritten = i + 1;\n \t}\n \n \t/* Check for indent using non-tab. */\n-\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && written >= 8)\n+\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= 8)\n \t\tresult |= WS_INDENT_WITH_NON_TAB;\n \n \tif (stream) {\n-- \n1.5.4.rc0.41.gf723\n"},{"id":"63326","messageId":"1197822702-5262-6-git-send-email-bfields@citi.umich.edu","threadId":"11000","inReplyTo":"1197822702-5262-5-git-send-email-bfields@citi.umich.edu","subject":"[PATCH 5/6] whitespace: more accurate initial-indent highlighting","fromName":"J. Bruce Fields","fromEmail":"bfields@citi.umich.edu","sentAt":"2007-12-16T16:31:41Z","receivedAt":"2007-12-16T16:31:41Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"Instead of highlighting the entire initial indent, highlight only the\nproblematic spaces.\n\nIn the case of an indent like ' \\t \\t' there may be multiple problematic\nranges, so it's easiest to emit the highlighting as we go instead of\ntrying rember disjoint ranges and do it all at the end.\n\nSigned-off-by: J. Bruce Fields <bfields@citi.umich.edu>\n---\n ws.c |   24 ++++++++++++++++--------\n 1 files changed, 16 insertions(+), 8 deletions(-)\n\ndiff --git a/ws.c b/ws.c\nindex aabd509..d09b9df 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -150,24 +150,32 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\n \t\t\tcontinue;\n \t\tif (line[i] != '\\t')\n \t\t\tbreak;\n-\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) && written < i)\n+\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) && written < i) {\n \t\t\tresult |= WS_SPACE_BEFORE_TAB;\n+\t\t\tif (stream) {\n+\t\t\t\tfputs(ws, stream);\n+\t\t\t\tfwrite(line + written, i - written, 1, stream);\n+\t\t\t\tfputs(reset, stream);\n+\t\t\t}\n+\t\t} else if (stream)\n+\t\t\tfwrite(line + written, i - written, 1, stream);\n+\t\tif (stream)\n+\t\t\tfwrite(line + i, 1, 1, stream);\n \t\twritten = i + 1;\n \t}\n \n \t/* Check for indent using non-tab. */\n-\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= 8)\n+\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= 8) {\n \t\tresult |= WS_INDENT_WITH_NON_TAB;\n-\n-\tif (stream) {\n-\t\t/* Highlight errors in leading whitespace. */\n-\t\tif ((result & WS_SPACE_BEFORE_TAB) ||\n-\t\t    (result & WS_INDENT_WITH_NON_TAB)) {\n+\t\tif (stream) {\n \t\t\tfputs(ws, stream);\n-\t\t\tfwrite(line, written, 1, stream);\n+\t\t\tfwrite(line + written, i - written, 1, stream);\n \t\t\tfputs(reset, stream);\n \t\t}\n+\t\twritten = i;\n+\t}\n \n+\tif (stream) {\n \t\t/* Now the rest of the line starts at written.\n \t\t * The non-highlighted part ends at trailing_whitespace. */\n \t\tif (trailing_whitespace == -1)\n-- \n1.5.4.rc0.41.gf723\n"},{"id":"63327","messageId":"1197822702-5262-7-git-send-email-bfields@citi.umich.edu","threadId":"11000","inReplyTo":"1197822702-5262-6-git-send-email-bfields@citi.umich.edu","subject":"[PATCH 6/6] whitespace: fix config.txt description of indent-with-non-tab","fromName":"J. Bruce Fields","fromEmail":"bfields@citi.umich.edu","sentAt":"2007-12-16T16:31:42Z","receivedAt":"2007-12-16T16:31:42Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"Fix garbled description.\n\nSigned-off-by: J. Bruce Fields <bfields@citi.umich.edu>\n---\n Documentation/config.txt |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex fabe7f8..ce16fc7 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -307,7 +307,7 @@ core.whitespace::\n   before a tab character in the initial indent part of the line as an\n   error (enabled by default).\n * `indent-with-non-tab` treats a line that is indented with 8 or more\n-  space characters that can be replaced with tab characters.\n+  space characters as an error (not enabled by default).\n \n alias.*::\n \tCommand aliases for the gitlink:git[1] command wrapper - e.g.\n-- \n1.5.4.rc0.41.gf723\n"},{"id":"63331","messageId":"1197827882-11710-1-git-send-email-bfields@citi.umich.edu","threadId":"11000","inReplyTo":"1197822702-5262-7-git-send-email-bfields@citi.umich.edu","subject":"builtin-apply whitespace","fromName":"J. Bruce Fields","fromEmail":"bfields@citi.umich.edu","sentAt":"2007-12-16T17:58:00Z","receivedAt":"2007-12-16T17:58:00Z","isPatch":false,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"Apologies, I just realized I also forgot to do the corresponding fix on\nthe apply side.  Here they are.  These are also available at\n\n\tgit://linux-nfs.org/~bfields/git.git master\n\n--b.\n"},{"id":"63332","messageId":"1197827882-11710-2-git-send-email-bfields@citi.umich.edu","threadId":"11000","inReplyTo":"1197827882-11710-1-git-send-email-bfields@citi.umich.edu","subject":"[PATCH 1/2] builtin-apply: minor cleanup of whitespace detection","fromName":"J. Bruce Fields","fromEmail":"bfields@citi.umich.edu","sentAt":"2007-12-16T17:58:01Z","receivedAt":"2007-12-16T17:58:01Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"Use 0 instead of -1 for the case where not tabs or spaces are found; it\nwill make some later math slightly simpler.\n\nSigned-off-by: J. Bruce Fields <bfields@citi.umich.edu>\n---\n builtin-apply.c |    8 ++++----\n 1 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex 2edd83b..bd94a4b 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -1550,8 +1550,8 @@ static int apply_line(char *output, const char *patch, int plen,\n \tint i;\n \tint add_nl_to_tail = 0;\n \tint fixed = 0;\n-\tint last_tab_in_indent = -1;\n-\tint last_space_in_indent = -1;\n+\tint last_tab_in_indent = 0;\n+\tint last_space_in_indent = 0;\n \tint need_fix_leading_space = 0;\n \tchar *buf;\n \n@@ -1582,12 +1582,12 @@ static int apply_line(char *output, const char *patch, int plen,\n \t\tif (ch == '\\t') {\n \t\t\tlast_tab_in_indent = i;\n \t\t\tif ((ws_rule & WS_SPACE_BEFORE_TAB) &&\n-\t\t\t    0 <= last_space_in_indent)\n+\t\t\t    0 < last_space_in_indent)\n \t\t\t    need_fix_leading_space = 1;\n \t\t} else if (ch == ' ') {\n \t\t\tlast_space_in_indent = i;\n \t\t\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) &&\n-\t\t\t    last_tab_in_indent < 0 &&\n+\t\t\t    last_tab_in_indent <= 0 &&\n \t\t\t    8 <= i)\n \t\t\t\tneed_fix_leading_space = 1;\n \t\t}\n-- \n1.5.4.rc0.44.g21147\n"},{"id":"63333","messageId":"1197827882-11710-3-git-send-email-bfields@citi.umich.edu","threadId":"11000","inReplyTo":"1197827882-11710-2-git-send-email-bfields@citi.umich.edu","subject":"[PATCH 2/2] builtin-apply: stronger indent-with-on-tab fixing","fromName":"J. Bruce Fields","fromEmail":"bfields@citi.umich.edu","sentAt":"2007-12-16T17:58:02Z","receivedAt":"2007-12-16T17:58:02Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"Fix any sequence of 8 spaces in initial indent, not just the case where\nthe 8 spaces are the first thing on the line.\n\nSigned-off-by: J. Bruce Fields <bfields@citi.umich.edu>\n---\n builtin-apply.c |    3 +--\n 1 files changed, 1 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex bd94a4b..5e3b4a1 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -1587,8 +1587,7 @@ static int apply_line(char *output, const char *patch, int plen,\n \t\t} else if (ch == ' ') {\n \t\t\tlast_space_in_indent = i;\n \t\t\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) &&\n-\t\t\t    last_tab_in_indent <= 0 &&\n-\t\t\t    8 <= i)\n+\t\t\t    8 <= i - last_tab_in_indent)\n \t\t\t\tneed_fix_leading_space = 1;\n \t\t}\n \t\telse\n-- \n1.5.4.rc0.44.g21147\n"},{"id":"63334","messageId":"200712161916.44715.jnareb@gmail.com","threadId":"11000","inReplyTo":"20071216162637.GA3934@fieldses.org","subject":"Re: [PATCH] whitespace: fix initial-indent checking","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-12-16T18:16:44Z","receivedAt":"2007-12-16T18:16:44Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"J. Bruce Fields wrote:\n> On Sun, Dec 16, 2007 at 11:00:55AM +0100, Wincent Colaiuta wrote:\n>> El 16/12/2007, a las 10:08, Jakub Narebski escribió:\n>>\n>>> J. Bruce Fields wrote:\n>>>\n>>>> This allows catching initial indents like '\\t        ' (a tab followed\n>>>> by 8 spaces), while previously indent-with-non-tab caught only indents\n>>>> that consisted entirely of spaces.\n>>>\n>>> I prefer to use tabs for indent, but _spaces_ for align. While previous,\n>>> less strict version of check catches indent using spaces, this one also\n>>> catches _align_ using spaces.\n> \n> No, the previous version didn't work for the align-with-spaces case\n> either.  Consider, for example,\n> \n> struct widget *find_widget_by_color(struct color *color,\n>                                     int nth_match, unsigned long flags)\n> \n> If following a \"indent-with-tabs, align-with-spaces\" policy, then the\n> initial whitespaace on the second line should be purely spaces\n> (otherwise adjusting the tab stops would ruin the alignment).  But\n> indent-with-non-tab would flag this as incorrect even before my fix.\n\nYes, this is (if we want \"indent with tab, align with spaces\") false\npositive even with current version of indent-with-non-tab policy, but\nit is _rare_ false positive.\n\nIt is useful because it catches quite common \"indent with spaces only\",\nfor example if MTA or editor replaces tabs with spaces, or if editor\npreserves whitespace but it uses spaces for indent.\n\nSo for me this version is a good compromise between false positives\nand catching real indent whitespace errors. The version proposed has\nIMHO too many false positive, while I guess not catching much more\nerrors in practice.\n\n>> I'd say that Jakub's is a fairly common use case (it's used in many places \n>> in the Git codebase too, I think) so it would be a bad thing to change the \n>> behaviour of \"indent-with-non-tab\".\n>>\n>> If you also want to check for \"align-with-non-tab\" then it really should be \n>> a separate, optional class of whitespace error.\n> \n> I would agree with you if it were not for the fact that if you're using\n> an \"indent-with-tabs, align-with-spaces\" policy then the only indent\n> whitespace problems that you can flag automatically are space-before-tab\n> problems; anything else requires knowledge of the language syntax.\n\nUnfortunately quite true (by the way, doesn't new version of\n\"align-with-non-tab\" do not work for Python sources?)\n\nPerhaps it should be called \"no-8spaces\" os something like that: is the\nwidth (in columns) of a tab character configurable, by the way?\n\n> So indent-with-non-tab has only ever been useful for projects that\n> insist on tabs for all sequences of 8 spaces in the initial whitespace.\n\nIMVVHO the new version of \"indent-with-non-tab\" (aka \"no-8-spaces\") is\nuseful _only_ for such project, while old version not only (see comment\nabove).\n\n-- \nJakub Narebski\nPoland\n"},{"id":"63335","messageId":"200712161924.18265.jnareb@gmail.com","threadId":"11000","inReplyTo":"200712161916.44715.jnareb@gmail.com","subject":"Re: [PATCH] whitespace: fix initial-indent checking","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-12-16T18:24:17Z","receivedAt":"2007-12-16T18:24:17Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"By the way, I have just [a beginning of] an idea: what if we look at \nneighbour lines to try to detect whitespace errors? For example when \nusing tabs for indent, spaces for align, I think it is an error if \nspaces sequence after tab begins earlier than tab sequence in neighbour \nline ends.\n\nWhat do you think about it?\n-- \nJakub Narebski\nPoland\n"},{"id":"63341","messageId":"7v3au2l91r.fsf@gitster.siamese.dyndns.org","threadId":"11000","inReplyTo":"20071216162637.GA3934@fieldses.org","subject":"Re: [PATCH] whitespace: fix initial-indent checking","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-12-16T19:43:44Z","receivedAt":"2007-12-16T19:43:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"J. Bruce Fields\" <bfields@fieldses.org> writes:\n\n>>> I prefer to use tabs for indent, but _spaces_ for align. While previous,\n>>> less strict version of check catches indent using spaces, this one also\n>>> catches _align_ using spaces.\n>\n> No, the previous version didn't work for the align-with-spaces case\n> either.\n\nWhen indent-with-non-tab is in effect, it should tabify spaces in indent\nas much as possible.  The error definition is deliberately incompatible\nwith \"align with spaces\" policy.\n\nThe reason I made indent-with-non-tab configurable was to cater to these\npeople.  They can turn it off if they do not want it.\n"},{"id":"63343","messageId":"20071216195956.GA14676@fieldses.org","threadId":"11000","inReplyTo":"200712161916.44715.jnareb@gmail.com","subject":"Re: [PATCH] whitespace: fix initial-indent checking","fromName":"J. Bruce Fields","fromEmail":"bfields@fieldses.org","sentAt":"2007-12-16T19:59:56Z","receivedAt":"2007-12-16T19:59:56Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"On Sun, Dec 16, 2007 at 07:16:44PM +0100, Jakub Narebski wrote:\n> J. Bruce Fields wrote:\n> > No, the previous version didn't work for the align-with-spaces case\n> > either.  Consider, for example,\n> > \n> > struct widget *find_widget_by_color(struct color *color,\n> >                                     int nth_match, unsigned long flags)\n> > \n> > If following a \"indent-with-tabs, align-with-spaces\" policy, then the\n> > initial whitespaace on the second line should be purely spaces\n> > (otherwise adjusting the tab stops would ruin the alignment).  But\n> > indent-with-non-tab would flag this as incorrect even before my fix.\n> \n> Yes, this is (if we want \"indent with tab, align with spaces\") false\n> positive even with current version of indent-with-non-tab policy, but\n> it is _rare_ false positive.\n\nYou can find examples like the above all over the git source, even in C\nwhere the top-level code is indented in main().\n\n> It is useful because it catches quite common \"indent with spaces only\",\n> for example if MTA or editor replaces tabs with spaces, or if editor\n> preserves whitespace but it uses spaces for indent.\n> \n> So for me this version is a good compromise between false positives\n> and catching real indent whitespace errors. The version proposed has\n> IMHO too many false positive, while I guess not catching much more\n> errors in practice.\n\nUnfortunately, this compromise wouldn't solve my problem.\n\nWhich is: I do get the occasional kernel patch with whitespace problems\nuncaught by git's existing checks.  It's annoying to have to fix them up\nmanually (but wastes Andrew Morton's time if I don't).  This shouldn't\nbe necessary, because the kernel has a simple policy for initial\nwhitespace that is completely automatable.\n\nIf we've got to define a fourth whitespace policy for this, well, OK,\nI'll live--tell me what I need to do.  I haven't seen a convincing\nargument for that yet, though.\n\n--b.\n"},{"id":"63345","messageId":"200712162131.04280.jnareb@gmail.com","threadId":"11000","inReplyTo":"20071216195956.GA14676@fieldses.org","subject":"Re: [PATCH] whitespace: fix initial-indent checking","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-12-16T20:31:03Z","receivedAt":"2007-12-16T20:31:03Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Sun, 16 Dec 2007, J. Bruce Fields wrote:\n\n> If we've got to define a fourth whitespace policy for this, well, OK,\n> I'll live--tell me what I need to do.  I haven't seen a convincing\n> argument for that yet, though.\n\nPerhaps the new version of policy should be called indent-with-non-tab,\nand we keep old version under name indent-with-spaces, hmmm...?\n\nBy the way, what about check for diff3/rcsmerge conflict markers?\nThis is not \"whitespace\" error per se, but...\n-- \nJakub Narebski\nPoland\n"},{"id":"63346","messageId":"7vodcqjrtw.fsf@gitster.siamese.dyndns.org","threadId":"11000","inReplyTo":"20071216035440.GM14377@fieldses.org","subject":"Re: [PATCH] whitespace: fix initial-indent checking","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-12-16T20:40:59Z","receivedAt":"2007-12-16T20:40:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"J. Bruce Fields\" <bfields@fieldses.org> writes:\n\n> One slightly weird thing about this: I'd expect indent-with-non-tab to\n> catch any sequence of 8 or more contiguous spaces, not just such\n> sequences at the end of the indent.  This doesn't quite do that.\n\n+-------+-------+-------+-------+-------+-------+-------+-------+-------+\nPersonally, I would hate that.  That would muck with two spaces I\ndeliberately typed after the full stop before this sentence).  Please\ndon't.\n\nEmacs \"M-x tabify\" tends to do this and I found it unsuitable especially\nfor code (I am not complaining, it probably was invented for other\npurposes and not reformatting code):\n\nIf you have original (the run of '>>..>>' is a single tab, '.' is a SP)\n\n        dcba....123\n        fedcba..123\n        gfedcba.123\n\nand \"tabify\" the region, you would get:\n\n        dcba>>>>123\n        fedcba>>123\n        gfedcba.123\n\nThat is fine if you are shooting for minimum number of bytes, but often\nit is not what you want in your code, especially when the part that\nconains the whitespace \"..cba 123..\" is inside a string constant.\n"},{"id":"63347","messageId":"7vk5nejqy6.fsf@gitster.siamese.dyndns.org","threadId":"11000","inReplyTo":"20071216195956.GA14676@fieldses.org","subject":"Re: [PATCH] whitespace: fix initial-indent checking","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-12-16T21:00:01Z","receivedAt":"2007-12-16T21:00:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"J. Bruce Fields\" <bfields@fieldses.org> writes:\n\n> Which is: I do get the occasional kernel patch with whitespace problems\n> uncaught by git's existing checks.  It's annoying to have to fix them up\n> manually (but wastes Andrew Morton's time if I don't).  This shouldn't\n> be necessary, because the kernel has a simple policy for initial\n> whitespace that is completely automatable.\n>\n> If we've got to define a fourth whitespace policy for this, well, OK,\n> I'll live--tell me what I need to do.  I haven't seen a convincing\n> argument for that yet, though.\n\nThere is one \"fix\" you earlier implemented but there is no way to check\nin the current infrastructure of checking one line at a time.  A run of\nblank lines at the end of file.\n\nI personallyy find it much more interesting and useful than the \"align\nwith space\" discussion.\n"},{"id":"63348","messageId":"7vfxy2jqmq.fsf@gitster.siamese.dyndns.org","threadId":"11000","inReplyTo":"B54C9483-90BE-4B45-A3B7-39FACF0E9F62@wincent.com","subject":"Re: [PATCH] whitespace: reorganize initial-indent check","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-12-16T21:06:53Z","receivedAt":"2007-12-16T21:06:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Wincent Colaiuta <win@wincent.com> writes:\n\n> El 16/12/2007, a las 4:48, J. Bruce Fields escribi󺊊> Reorganize to emphasize the most complicated part of the code (the tab\n>> case).\n>\n> Any chance of either squashing this series into one patch seeing as  \n> its all churning over the same part of the code, or resending it with  \n> numbering? The patches seemed to arrive out of order in my mailbox and  \n> I don't really know what order they're supposed to be applied in and  \n> it's a bit hard to review.\n\nI do not think squashing is necessary or a good idea in this case.  As\nfar as I can tell, the series does not have \"oops, this fixes the\nearlier problem I introduced in the series\", but it is purely a logical\nprogression.  The first one fixes a bug introduced by the 0-base\nconversion which can and should stand on its own.\n\nThe mails were properly threaded with in-reply-to so numbering is not\nstrictly necessary either, but would have helped readers with MUA that\ndo not pay attention to that header.\n\nI already see JBF resent the series, which is very nice of him.\n"},{"id":"63350","messageId":"20071216211944.GA25178@fieldses.org","threadId":"11000","inReplyTo":"7vodcqjrtw.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] whitespace: fix initial-indent checking","fromName":"J. Bruce Fields","fromEmail":"bfields@fieldses.org","sentAt":"2007-12-16T21:19:44Z","receivedAt":"2007-12-16T21:19:44Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"On Sun, Dec 16, 2007 at 12:40:59PM -0800, Junio C Hamano wrote:\n> \"J. Bruce Fields\" <bfields@fieldses.org> writes:\n> \n> > One slightly weird thing about this: I'd expect indent-with-non-tab to\n> > catch any sequence of 8 or more contiguous spaces, not just such\n> > sequences at the end of the indent.  This doesn't quite do that.\n> \n> +-------+-------+-------+-------+-------+-------+-------+-------+-------+\n> Personally, I would hate that.  That would muck with two spaces I\n> deliberately typed after the full stop before this sentence).  Please\n> don't.\n\nRight, I was only thinking of literal sequences of 8 contiguous spaces,\nand only in the initial indent.  So it's the failure to flag\n\n\t\"^\\t         \\t\"\n\nthat I find a little unexpected.  Whatever--I don't think it's\nparticularly important.  Like I say, I think there's really only two\ncases anyone really cares about:\n\n\t1. The kernel or git style, where all initial whitespace is\n\ttabbed as much as possible.\n\t2. Various other styles which may be harder to check completely,\n\tbut which are likely to share the \"no spaces before tabs in\n\tinitial indent\" rule as a least common denominator.\n\n> Emacs \"M-x tabify\" tends to do this and I found it unsuitable especially\n> for code (I am not complaining, it probably was invented for other\n> purposes and not reformatting code):\n> \n> If you have original (the run of '>>..>>' is a single tab, '.' is a SP)\n> \n>         dcba....123\n>         fedcba..123\n>         gfedcba.123\n> \n> and \"tabify\" the region, you would get:\n> \n>         dcba>>>>123\n>         fedcba>>123\n>         gfedcba.123\n> \n> That is fine if you are shooting for minimum number of bytes, but often\n> it is not what you want in your code, especially when the part that\n> conains the whitespace \"..cba 123..\" is inside a string constant.\n\nYeah, that sounds pretty irritating.\n\n--b.\n"},{"id":"63375","messageId":"ACA0791E-189F-4E19-AE87-C7D1163C0366@wincent.com","threadId":"11000","inReplyTo":"1197822702-5262-6-git-send-email-bfields@citi.umich.edu","subject":"Re: [PATCH 5/6] whitespace: more accurate initial-indent highlighting","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-12-17T08:00:13Z","receivedAt":"2007-12-17T08:00:13Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"El 16/12/2007, a las 17:31, J. Bruce Fields escribió:\n\n> Instead of highlighting the entire initial indent, highlight only the\n> problematic spaces.\n>\n> In the case of an indent like ' \\t \\t' there may be multiple  \n> problematic\n> ranges, so it's easiest to emit the highlighting as we go instead of\n> trying rember disjoint ranges and do it all at the end.\n\nI'm relatively opposed to mixing the \"check\" and the \"emit\" phases  \nhere because it will make further refactoring harder.\n\nIn the initial version of your series you forgot that there was  \nanother place in the codebase (\"git apply --whitespace=fix\") where  \nwhitespace errors are detected, and so you introduced an inconsistency  \nwhich you later fixed up. To me this is indicative of the fact that we  \nneed to refactor further so that there really is only *one* place  \nwhere the whitespace checking logic is implemented.\n\nBasically I would have proposed extracting out each type of whitespace  \nerror into an inline function in ws.c, where it could be used by both  \ncheck_and_emit_line() in ws.c and apply_one_fragment() in builtin- \napply.c.\n\nUnfortunately, mixing checking and emission phases makes this proposed  \nrefactoring a little bit ugly.\n\nBut I see it's already gone into master as ffe56885.\n\nWincent\n"},{"id":"63376","messageId":"7vwsrdd9wa.fsf@gitster.siamese.dyndns.org","threadId":"11000","inReplyTo":"ACA0791E-189F-4E19-AE87-C7D1163C0366@wincent.com","subject":"Re: [PATCH 5/6] whitespace: more accurate initial-indent highlighting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-12-17T08:04:53Z","receivedAt":"2007-12-17T08:04:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Wincent Colaiuta <win@wincent.com> writes:\n\n> Basically I would have proposed extracting out each type of whitespace  \n> error into an inline function in ws.c, where it could be used by both  \n> check_and_emit_line() in ws.c and apply_one_fragment() in builtin- \n> apply.c.\n>\n> Unfortunately, mixing checking and emission phases makes this proposed  \n> refactoring a little bit ugly.\n\nThe right refactoring would be what JBF hinted in his message, to record\nand return a list of suspicious ranges from the checker function and\nhave the highlighter and the fixer make use of that list.\n\nSuch a refactoring is still possible but I think it is beyond the scope\nof pre 1.5.4 clean-up.\n"},{"id":"63533","messageId":"m3y7bsq1vo.fsf@roke.D-201","threadId":"11000","inReplyTo":"7vwsrdd9wa.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 5/6] whitespace: more accurate initial-indent highlighting","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-12-18T00:32:09Z","receivedAt":"2007-12-18T00:32:09Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Wincent Colaiuta <win@wincent.com> writes:\n> \n> > Basically I would have proposed extracting out each type of whitespace  \n> > error into an inline function in ws.c, where it could be used by both  \n> > check_and_emit_line() in ws.c and apply_one_fragment() in builtin- \n> > apply.c.\n> >\n> > Unfortunately, mixing checking and emission phases makes this proposed  \n> > refactoring a little bit ugly.\n> \n> The right refactoring would be what JBF hinted in his message, to record\n> and return a list of suspicious ranges from the checker function and\n> have the highlighter and the fixer make use of that list.\n> \n> Such a refactoring is still possible but I think it is beyond the scope\n> of pre 1.5.4 clean-up.\n\nBy the way, does \"trailing empty lines at the end of file\" whitespace\nerror get detected and hightlighted with refactored whitespace checking?\n\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"63537","messageId":"7v3au04yh5.fsf@gitster.siamese.dyndns.org","threadId":"11000","inReplyTo":"m3y7bsq1vo.fsf@roke.D-201","subject":"Re: [PATCH 5/6] whitespace: more accurate initial-indent highlighting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-12-18T00:51:02Z","receivedAt":"2007-12-18T00:51:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n> By the way, does \"trailing empty lines at the end of file\" whitespace\n> error get detected and hightlighted with refactored whitespace checking?\n\nDid you read the whole thread yet?\n"}]}