{"thread":{"id":"11836","subject":"[PATCH 01/13] builtin-apply.c: refactor small part that matches context","startedAt":"2008-02-02T10:54:06Z","lastAt":"2008-02-02T10:54:19Z","messageCount":14,"participants":["Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":13},"messages":[{"id":"67105","messageId":"1201949659-27725-1-git-send-email-gitster@pobox.com","threadId":"11836","inReplyTo":null,"subject":"[PATCH/RFC 00/13] git-apply --whitespace=fix updates","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-02T10:54:06Z","receivedAt":"2008-02-02T10:54:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This mini-series teaches \"git-apply --whitespace=fix\" to cope\nbetter with the whitespace changes it introduces to the context.\n\n[PATCH 01/13] builtin-apply.c: refactor small part that matches context\n[PATCH 02/13] builtin-apply.c: restructure \"offset\" matching\n[PATCH 03/13] builtin-apply.c: push match-beginning/end logic down\n\nThese three are preparatory clean-ups to isolate the parts the\nlater patches need to update.\n\n[PATCH 04/13] builtin-apply.c: make it more line oriented\n[PATCH 05/13] builtin-apply.c: optimize match_beginning/end processing a bit.\n[PATCH 06/13] builtin-apply.c: mark common context lines in lineinfo structure.\n\nThese introduce an array of hash values for each line in the\npatch preimage, postimage and the target file, and simplifies\nthe code to find the place that the hunk applies.  The ultimate\nmotivation is to allow applying patches with whitespace\ndifferences, but this step does not do that yet.\n\nThe matching logic that uses hash code of lines could also make\nit faster to find the place the hunk applies to, but that is not\nthe primary purpose of the change.  We would however need to\nbench it so that this does not at least slow it down too much.\nI haven't done that yet.\n\n[PATCH 07/13] builtin-apply.c: clean-up apply_one_fragment()\n\nThis reduces 3 variables (old, oldlines, oldsize) that keep\ntrack of the preimage being built (and their \"new\" counterparts\nfor the postimage) to 2 variables (old, oldlines).  This cannot\nbe done cleanly without the conversion to a line oriented data\nstructure introduced by 04/13 (remove_first_line() is the most\nproblematic one).\n\n[PATCH 08/13] builtin-apply.c: simplify calling site to apply_line()\n[PATCH 09/13] builtin-apply.c: do not feed copy_wsfix() leading '+'\n[PATCH 10/13] builtin-apply.c: move copy_wsfix() function a bit higher.\n\nThe function apply_line() reads one line of a patch and makes\nwhitespace corrections.  These three are preparatory steps to\nmake the function usable for correcting whitespaces in context\nlines.  The function is renamed to copy_wsfix().\n\n[PATCH 11/13] builtin-apply.c: pass ws_rule down to match_fragment()\n[PATCH 12/13] git-apply --whitespace=fix: fix whitespace fuzz introduced by previous run\n\nThis actually implements the application of a patch whose\npreimage context lines do not match the target file but do match\nif the whitespace breakages in them are corrected.  The fixing\ngoes the other way as well.  We may still have files with\nwhitespace breakages, and the submitter may have based the patch\non a tree whose whitespace breakages have been fixed.  In such a\ncase, the lines in the target file needs to be cleaned up and\ncompared with the cleaned-up context lines from the preimage of\nthe patch.\n\n[PATCH 13/13] core.whitespace: cr-at-eol\n\nThis introduces a new error mode \"cr-at-eol\" which tells git not\nto treat a CR at the end of line as a trailing whitespace error.\nThis is from my earlier patch sent to the list, and does not\ndepend on \"the whitespace correction in the context\" topic\nabove, but rebased because it textually conflicts with the\nseries.\n"},{"id":"67102","messageId":"1201949659-27725-2-git-send-email-gitster@pobox.com","threadId":"11836","inReplyTo":"1201949659-27725-1-git-send-email-gitster@pobox.com","subject":"[PATCH 01/13] builtin-apply.c: refactor small part that matches context","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-02T10:54:07Z","receivedAt":"2008-02-02T10:54:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This moves three \"if\" conditions out of line from find_offset()\nfunction, which is responsible for finding the matching place in\nthe preimage to apply the patch.  There is no change in the\nlogic of the program.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-apply.c |   18 +++++++++++++-----\n 1 files changed, 13 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex 15432b6..2c052f8 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -1437,6 +1437,17 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)\n \t}\n }\n \n+static int match_fragment(const char *buf, unsigned long size,\n+\t\t\t  unsigned long try,\n+\t\t\t  const char *fragment, unsigned long fragsize)\n+{\n+\tif (try + fragsize > size)\n+\t\treturn 0;\n+\tif (memcmp(buf + try, fragment, fragsize))\n+\t\treturn 0;\n+\treturn 1;\n+}\n+\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@@ -1461,8 +1472,7 @@ static int find_offset(const char *buf, unsigned long size,\n \t}\n \n \t/* Exact line number? */\n-\tif ((start + fragsize <= size) &&\n-\t    !memcmp(buf + start, fragment, fragsize))\n+\tif (match_fragment(buf, size, start, fragment, fragsize))\n \t\treturn start;\n \n \t/*\n@@ -1494,9 +1504,7 @@ static int find_offset(const char *buf, unsigned long size,\n \t\t\ttry = forwards;\n \t\t}\n \n-\t\tif (try + fragsize > size)\n-\t\t\tcontinue;\n-\t\tif (memcmp(buf + try, fragment, fragsize))\n+\t\tif (!match_fragment(buf, size, try, fragment, fragsize))\n \t\t\tcontinue;\n \t\tn = (i >> 1)+1;\n \t\tif (i & 1)\n-- \n1.5.4.2.g41ac4\n"},{"id":"67103","messageId":"1201949659-27725-3-git-send-email-gitster@pobox.com","threadId":"11836","inReplyTo":"1201949659-27725-2-git-send-email-gitster@pobox.com","subject":"[PATCH 02/13] builtin-apply.c: restructure \"offset\" matching","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-02T10:54:08Z","receivedAt":"2008-02-02T10:54:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This restructures code to find matching location with offset\nin find_offset() function, so that there is need for only one\ncall site of match_fragment() function.  There still isn't a\nchange in the logic of the program.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-apply.c |   49 +++++++++++++++++++++++++------------------------\n 1 files changed, 25 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex 2c052f8..0a304ab 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -1452,8 +1452,8 @@ 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+\tint i, no_more_backwards, no_more_forwards;\n+\tunsigned long start, backwards, forwards, try;\n \n \tif (fragsize > size)\n \t\treturn -1;\n@@ -1471,32 +1471,44 @@ static int find_offset(const char *buf, unsigned long size,\n \t\t}\n \t}\n \n-\t/* Exact line number? */\n-\tif (match_fragment(buf, size, start, fragment, fragsize))\n-\t\treturn start;\n-\n \t/*\n \t * There's probably some smart way to do this, but I'll leave\n \t * that to the smart and beautiful people. I'm simple and stupid.\n \t */\n \tbackwards = start;\n \tforwards = start;\n+\ttry = start;\n+\n \tfor (i = 0; ; i++) {\n-\t\tunsigned long try;\n-\t\tint n;\n+\t\tno_more_backwards = !backwards;\n+\t\tno_more_forwards = (forwards + fragsize > size);\n+\n+\t\tif (match_fragment(buf, size, try, fragment, fragsize)) {\n+\t\t\tint shift = ((i+1) >> 1);\n+\t\t\tif (i & 1)\n+\t\t\t\tshift = -shift;\n+\t\t\t*lines = shift;\n+\t\t\treturn try;\n+\t\t}\n+\n+\tagain:\n+\t\tif (no_more_backwards && no_more_forwards)\n+\t\t\tbreak;\n \n-\t\t/* \"backward\" */\n \t\tif (i & 1) {\n-\t\t\tif (!backwards) {\n-\t\t\t\tif (forwards + fragsize > size)\n-\t\t\t\t\tbreak;\n-\t\t\t\tcontinue;\n+\t\t\tif (no_more_backwards) {\n+\t\t\t\ti++;\n+\t\t\t\tgoto again;\n \t\t\t}\n \t\t\tdo {\n \t\t\t\t--backwards;\n \t\t\t} while (backwards && buf[backwards-1] != '\\n');\n \t\t\ttry = backwards;\n \t\t} else {\n+\t\t\tif (no_more_forwards) {\n+\t\t\t\ti++;\n+\t\t\t\tgoto again;\n+\t\t\t}\n \t\t\twhile (forwards + fragsize <= size) {\n \t\t\t\tif (buf[forwards++] == '\\n')\n \t\t\t\t\tbreak;\n@@ -1504,18 +1516,7 @@ static int find_offset(const char *buf, unsigned long size,\n \t\t\ttry = forwards;\n \t\t}\n \n-\t\tif (!match_fragment(buf, size, try, fragment, fragsize))\n-\t\t\tcontinue;\n-\t\tn = (i >> 1)+1;\n-\t\tif (i & 1)\n-\t\t\tn = -n;\n-\t\t*lines = n;\n-\t\treturn try;\n \t}\n-\n-\t/*\n-\t * We should start searching forward and backward.\n-\t */\n \treturn -1;\n }\n \n-- \n1.5.4.2.g41ac4\n"},{"id":"67104","messageId":"1201949659-27725-4-git-send-email-gitster@pobox.com","threadId":"11836","inReplyTo":"1201949659-27725-3-git-send-email-gitster@pobox.com","subject":"[PATCH 03/13] builtin-apply.c: push match-beginning/end logic down","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-02T10:54:09Z","receivedAt":"2008-02-02T10:54:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This moves the logic to force match at the beginning and/or at\nthe end of the buffer to the actual function that finds the\nmatch from its caller.  This is a necessary preparation for the\nnext step to allow matching disregarding certain differences,\nsuch as whitespace changes.\n\nWe probably could optimize this even more by taking advantage of\nthe fact that match_beginning and match_end forces the match to\nbe at an exact location (anchored at the beginning and/or the\nend), but that's for another commit.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-apply.c |   46 ++++++++++++++++++++++++++++++++--------------\n 1 files changed, 32 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex 0a304ab..f84a405 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -1439,18 +1439,36 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)\n \n static int match_fragment(const char *buf, unsigned long size,\n \t\t\t  unsigned long try,\n-\t\t\t  const char *fragment, unsigned long fragsize)\n+\t\t\t  const char *fragment, unsigned long fragsize,\n+\t\t\t  int match_beginning, int match_end)\n {\n-\tif (try + fragsize > size)\n+\tif (match_beginning && try)\n \t\treturn 0;\n-\tif (memcmp(buf + try, fragment, fragsize))\n-\t\treturn 0;\n-\treturn 1;\n+\n+\t/*\n+\t * Do we have an exact match?  If we were told to match\n+\t * at the end, size must be exactly at try+fragsize,\n+\t * otherwise try+fragsize must be still within the preimage,\n+\t * and either case, the old piece should match the preimage\n+\t * exactly.\n+\t */\n+\tif ((match_end\n+\t     ? (try + fragsize == size)\n+\t     : (try + fragsize <= size)) &&\n+\t    !memcmp(buf + try, fragment, fragsize))\n+\t\treturn 1;\n+\n+\t/*\n+\t * NEEDSWORK: We can optionally match fuzzily here, but\n+\t * that is for a later round.\n+\t */\n+\treturn 0;\n }\n \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+\t\t       int line, int *lines,\n+\t\t       int match_beginning, int match_end)\n {\n \tint i, no_more_backwards, no_more_forwards;\n \tunsigned long start, backwards, forwards, try;\n@@ -1483,7 +1501,8 @@ static int find_offset(const char *buf, unsigned long size,\n \t\tno_more_backwards = !backwards;\n \t\tno_more_forwards = (forwards + fragsize > size);\n \n-\t\tif (match_fragment(buf, size, try, fragment, fragsize)) {\n+\t\tif (match_fragment(buf, size, try, fragment, fragsize,\n+\t\t\t\t   match_beginning, match_end)) {\n \t\t\tint shift = ((i+1) >> 1);\n \t\t\tif (i & 1)\n \t\t\t\tshift = -shift;\n@@ -1765,17 +1784,16 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,\n \tpos = frag->newpos;\n \tfor (;;) {\n \t\toffset = find_offset(buf->buf, buf->len,\n-\t\t\t\t     oldlines, oldsize, pos, &lines);\n-\t\tif (match_end && offset + oldsize != buf->len)\n-\t\t\toffset = -1;\n-\t\tif (match_beginning && offset)\n-\t\t\toffset = -1;\n+\t\t\t\t     oldlines, oldsize, pos, &lines,\n+\t\t\t\t     match_beginning, match_end);\n \t\tif (offset >= 0) {\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    (buf->len - oldsize - offset == 0))\n+\t\t\t\t/* end of file? */\n \t\t\t\tnewsize -= new_blank_lines_at_end;\n \n-\t\t\t/* Warn if it was necessary to reduce the number\n+\t\t\t/*\n+\t\t\t * Warn if it was necessary to reduce the number\n \t\t\t * of context lines.\n \t\t\t */\n \t\t\tif ((leading != frag->leading) ||\n-- \n1.5.4.2.g41ac4\n"},{"id":"67107","messageId":"1201949659-27725-5-git-send-email-gitster@pobox.com","threadId":"11836","inReplyTo":"1201949659-27725-4-git-send-email-gitster@pobox.com","subject":"[PATCH 04/13] builtin-apply.c: make it more line oriented","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-02T10:54:10Z","receivedAt":"2008-02-02T10:54:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This changes the way git-apply internally works to be more line\noriented.  The logic to find where the patch applies with offset\nused to count line numbers by always counting LF from the\nbeginning of the buffer, but it is simplified because we count\nthe line length of the target file and the preimage snippet\nupfront now.\n\nThe ultimate motivation is to allow applying patches\nwhose preimage context has whitespace corruption that has\nalready been corrected in the local copy.  For that purpose, we\nintroduce a table of line-hash that allows us to match lines\nthat differ only in whitespaces.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-apply.c |  403 ++++++++++++++++++++++++++++++++++++++-----------------\n 1 files changed, 280 insertions(+), 123 deletions(-)\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex f84a405..e046e87 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -161,6 +161,92 @@ struct patch {\n \tstruct patch *next;\n };\n \n+/*\n+ * A line in a file, len-bytes long (includes the terminating LF,\n+ * except for an incomplete line at the end if the file ends with\n+ * one), and its contents hashes to 'hash'.\n+ */\n+struct line {\n+\tsize_t len;\n+\tunsigned hash : 24;\n+\tunsigned flag : 8;\n+};\n+\n+/*\n+ * This represents a \"file\", which is an array of \"lines\".\n+ */\n+struct image {\n+\tchar *buf;\n+\tsize_t len;\n+\tsize_t nr;\n+\tstruct line *line_allocated;\n+\tstruct line *line;\n+};\n+\n+static uint32_t hash_line(const char *cp, size_t len)\n+{\n+\tsize_t i;\n+\tuint32_t h;\n+\tfor (i = 0, h = 0; i < len; i++) {\n+\t\tif (!isspace(cp[i])) {\n+\t\t\th = h * 3 + (cp[i] & 0xff);\n+\t\t}\n+\t}\n+\treturn h;\n+}\n+\n+static void prepare_image(struct image *image, char *buf, size_t len,\n+\t\t\t  int prepare_linetable)\n+{\n+\tconst char *cp, *ep;\n+\tint n;\n+\n+\timage->buf = buf;\n+\timage->len = len;\n+\n+\tif (!prepare_linetable) {\n+\t\timage->line = NULL;\n+\t\timage->line_allocated = NULL;\n+\t\timage->nr = 0;\n+\t\treturn;\n+\t}\n+\n+\tep = image->buf + image->len;\n+\n+\t/* First count lines */\n+\tcp = image->buf;\n+\tn = 0;\n+\twhile (cp < ep) {\n+\t\tcp = strchrnul(cp, '\\n');\n+\t\tn++;\n+\t\tcp++;\n+\t}\n+\n+\timage->line_allocated = xcalloc(n, sizeof(struct line));\n+\timage->line = image->line_allocated;\n+\timage->nr = n;\n+\tcp = image->buf;\n+\tn = 0;\n+\twhile (cp < ep) {\n+\t\tconst char *next;\n+\t\tfor (next = cp; next < ep && *next != '\\n'; next++)\n+\t\t\t;\n+\t\tif (next < ep)\n+\t\t\tnext++;\n+\t\timage->line[n].len = next - cp;\n+\t\timage->line[n].hash = hash_line(cp, next - cp);\n+\t\tcp = next;\n+\t\tn++;\n+\t}\n+}\n+\n+static void clear_image(struct image *image)\n+{\n+\tfree(image->buf);\n+\timage->buf = NULL;\n+\timage->len = 0;\n+}\n+\n static void say_patch_name(FILE *output, const char *pre,\n \t\t\t   struct patch *patch, const char *post)\n {\n@@ -1437,14 +1523,29 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)\n \t}\n }\n \n-static int match_fragment(const char *buf, unsigned long size,\n+static int match_fragment(struct image *img,\n+\t\t\t  struct image *preimage,\n+\t\t\t  struct image *postimage,\n \t\t\t  unsigned long try,\n-\t\t\t  const char *fragment, unsigned long fragsize,\n+\t\t\t  int try_lno,\n \t\t\t  int match_beginning, int match_end)\n {\n-\tif (match_beginning && try)\n+\tint i;\n+\n+\tif (preimage->nr + try_lno > img->nr)\n+\t\treturn 0;\n+\n+\tif (match_beginning && try_lno)\n+\t\treturn 0;\n+\n+\tif (match_end && preimage->nr + try_lno != img->nr)\n \t\treturn 0;\n \n+\t/* Quick hash check */\n+\tfor (i = 0; i < preimage->nr; i++)\n+\t\tif (preimage->line[i].hash != img->line[try_lno + i].hash)\n+\t\t\treturn 0;\n+\n \t/*\n \t * Do we have an exact match?  If we were told to match\n \t * at the end, size must be exactly at try+fragsize,\n@@ -1453,9 +1554,9 @@ static int match_fragment(const char *buf, unsigned long size,\n \t * exactly.\n \t */\n \tif ((match_end\n-\t     ? (try + fragsize == size)\n-\t     : (try + fragsize <= size)) &&\n-\t    !memcmp(buf + try, fragment, fragsize))\n+\t     ? (try + preimage->len == img->len)\n+\t     : (try + preimage->len <= img->len)) &&\n+\t    !memcmp(img->buf + try, preimage->buf, preimage->len))\n \t\treturn 1;\n \n \t/*\n@@ -1465,105 +1566,78 @@ static int match_fragment(const char *buf, unsigned long size,\n \treturn 0;\n }\n \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-\t\t       int match_beginning, int match_end)\n+static int find_pos(struct image *img,\n+\t\t    struct image *preimage,\n+\t\t    struct image *postimage,\n+\t\t    int line,\n+\t\t    int match_beginning, int match_end)\n {\n-\tint i, no_more_backwards, no_more_forwards;\n-\tunsigned long start, backwards, forwards, try;\n+\tint i;\n+\tunsigned long backwards, forwards, try;\n+\tint backwards_lno, forwards_lno, try_lno;\n \n-\tif (fragsize > size)\n+\tif (preimage->nr > img->nr)\n \t\treturn -1;\n \n-\tstart = 0;\n-\tif (line > 1) {\n-\t\tunsigned long offset = 0;\n-\t\ti = line-1;\n-\t\twhile (offset + fragsize <= size) {\n-\t\t\tif (buf[offset++] == '\\n') {\n-\t\t\t\tstart = offset;\n-\t\t\t\tif (!--i)\n-\t\t\t\t\tbreak;\n-\t\t\t}\n-\t\t}\n-\t}\n+\ttry = 0;\n+\tfor (i = 0; i < line; i++)\n+\t\ttry += img->line[i].len;\n \n \t/*\n \t * There's probably some smart way to do this, but I'll leave\n \t * that to the smart and beautiful people. I'm simple and stupid.\n \t */\n-\tbackwards = start;\n-\tforwards = start;\n-\ttry = start;\n+\tbackwards = try;\n+\tbackwards_lno = line;\n+\tforwards = try;\n+\tforwards_lno = line;\n+\ttry_lno = line;\n \n \tfor (i = 0; ; i++) {\n-\t\tno_more_backwards = !backwards;\n-\t\tno_more_forwards = (forwards + fragsize > size);\n-\n-\t\tif (match_fragment(buf, size, try, fragment, fragsize,\n-\t\t\t\t   match_beginning, match_end)) {\n-\t\t\tint shift = ((i+1) >> 1);\n-\t\t\tif (i & 1)\n-\t\t\t\tshift = -shift;\n-\t\t\t*lines = shift;\n-\t\t\treturn try;\n-\t\t}\n+\t\tif (match_fragment(img, preimage, postimage,\n+\t\t\t\t   try, try_lno,\n+\t\t\t\t   match_beginning, match_end))\n+\t\t\treturn try_lno;\n \n \tagain:\n-\t\tif (no_more_backwards && no_more_forwards)\n+\t\tif (backwards_lno == 0 && forwards_lno == img->nr)\n \t\t\tbreak;\n \n \t\tif (i & 1) {\n-\t\t\tif (no_more_backwards) {\n+\t\t\tif (backwards_lno == 0) {\n \t\t\t\ti++;\n \t\t\t\tgoto again;\n \t\t\t}\n-\t\t\tdo {\n-\t\t\t\t--backwards;\n-\t\t\t} while (backwards && buf[backwards-1] != '\\n');\n+\t\t\tbackwards_lno--;\n+\t\t\tbackwards -= img->line[backwards_lno].len;\n \t\t\ttry = backwards;\n+\t\t\ttry_lno = backwards_lno;\n \t\t} else {\n-\t\t\tif (no_more_forwards) {\n+\t\t\tif (forwards_lno == img->nr) {\n \t\t\t\ti++;\n \t\t\t\tgoto again;\n \t\t\t}\n-\t\t\twhile (forwards + fragsize <= size) {\n-\t\t\t\tif (buf[forwards++] == '\\n')\n-\t\t\t\t\tbreak;\n-\t\t\t}\n+\t\t\tforwards += img->line[forwards_lno].len;\n+\t\t\tforwards_lno++;\n \t\t\ttry = forwards;\n+\t\t\ttry_lno = forwards_lno;\n \t\t}\n \n \t}\n \treturn -1;\n }\n \n-static void remove_first_line(const char **rbuf, int *rsize)\n+static void remove_first_line(struct image *img)\n {\n-\tconst char *buf = *rbuf;\n-\tint size = *rsize;\n-\tunsigned long offset;\n-\toffset = 0;\n-\twhile (offset <= size) {\n-\t\tif (buf[offset++] == '\\n')\n-\t\t\tbreak;\n-\t}\n-\t*rsize = size - offset;\n-\t*rbuf = buf + offset;\n+\timg->buf += img->line[0].len;\n+\timg->len -= img->line[0].len;\n+\timg->line++;\n+\timg->nr--;\n }\n \n-static void remove_last_line(const char **rbuf, int *rsize)\n+static void remove_last_line(struct image *img)\n {\n-\tconst char *buf = *rbuf;\n-\tint size = *rsize;\n-\tunsigned long offset;\n-\toffset = size - 1;\n-\twhile (offset > 0) {\n-\t\tif (buf[--offset] == '\\n')\n-\t\t\tbreak;\n-\t}\n-\t*rsize = offset + 1;\n+\timg->len -= img->line[--img->nr].len;\n }\n \n static int apply_line(char *output, const char *patch, int plen,\n@@ -1668,19 +1742,75 @@ 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,\n+static void update_image(struct image *img,\n+\t\t\t int applied_pos,\n+\t\t\t struct image *preimage,\n+\t\t\t struct image *postimage)\n+{\n+\t/*\n+\t * remove the copy of preimage at offset in img\n+\t * and replace it with postimage\n+\t */\n+\tint i, nr;\n+\tsize_t remove_count, insert_count, applied_at = 0;\n+\tchar *result;\n+\n+\tfor (i = 0; i < applied_pos; i++)\n+\t\tapplied_at += img->line[i].len;\n+\n+\tremove_count = 0;\n+\tfor (i = 0; i < preimage->nr; i++)\n+\t\tremove_count += img->line[applied_pos + i].len;\n+\tinsert_count = postimage->len;\n+\n+\t/* Adjust the contents */\n+\tresult = xmalloc(img->len + insert_count - remove_count + 1);\n+\tmemcpy(result, img->buf, applied_at);\n+\tmemcpy(result + applied_at, postimage->buf, postimage->len);\n+\tmemcpy(result + applied_at + postimage->len,\n+\t       img->buf + (applied_at + remove_count),\n+\t       img->len - (applied_at + remove_count));\n+\tfree(img->buf);\n+\timg->buf = result;\n+\timg->len += insert_count - remove_count;\n+\tresult[img->len] = '\\0';\n+\n+\t/* Adjust the line table */\n+\tnr = img->nr + postimage->nr - preimage->nr;\n+\tif (preimage->nr < postimage->nr) {\n+\t\t/*\n+\t\t * NOTE: this knows that we never call remove_first_line()\n+\t\t * on anything other than pre/post image.\n+\t\t */\n+\t\timg->line = xrealloc(img->line, nr * sizeof(*img->line));\n+\t\timg->line_allocated = img->line;\n+\t}\n+\tif (preimage->nr != postimage->nr)\n+\t\tmemmove(img->line + applied_pos + postimage->nr,\n+\t\t\timg->line + applied_pos + preimage->nr,\n+\t\t\t(img->nr - (applied_pos + preimage->nr)) *\n+\t\t\tsizeof(*img->line));\n+\tmemcpy(img->line + applied_pos,\n+\t       postimage->line,\n+\t       postimage->nr * sizeof(*img->line));\n+\timg->nr = nr;\n+}\n+\n+static int apply_one_fragment(struct image *img, struct fragment *frag,\n \t\t\t      int inaccurate_eof, unsigned ws_rule)\n {\n \tint match_beginning, match_end;\n \tconst char *patch = frag->patch;\n-\tint offset, size = frag->size;\n+\tint size = frag->size;\n \tchar *old = xmalloc(size);\n \tchar *new = xmalloc(size);\n-\tconst char *oldlines, *newlines;\n+\tchar *oldlines, *newlines;\n \tint oldsize = 0, newsize = 0;\n \tint new_blank_lines_at_end = 0;\n \tunsigned long leading, trailing;\n-\tint pos, lines;\n+\tint pos, applied_pos;\n+\tstruct image preimage;\n+\tstruct image postimage;\n \n \twhile (size > 0) {\n \t\tchar first;\n@@ -1780,32 +1910,16 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,\n \t\tmatch_end = !trailing;\n \t}\n \n-\tlines = 0;\n-\tpos = frag->newpos;\n+\tpos = frag->newpos ? (frag->newpos - 1) : 0;\n+\tprepare_image(&preimage, oldlines, oldsize, 1);\n+\tprepare_image(&postimage, newlines, newsize, 1);\n \tfor (;;) {\n-\t\toffset = find_offset(buf->buf, buf->len,\n-\t\t\t\t     oldlines, oldsize, pos, &lines,\n-\t\t\t\t     match_beginning, match_end);\n-\t\tif (offset >= 0) {\n-\t\t\tif (ws_error_action == correct_ws_error &&\n-\t\t\t    (buf->len - oldsize - offset == 0))\n-\t\t\t\t/* end of file? */\n-\t\t\t\tnewsize -= new_blank_lines_at_end;\n \n-\t\t\t/*\n-\t\t\t * Warn if it was necessary to reduce the number\n-\t\t\t * of context lines.\n-\t\t\t */\n-\t\t\tif ((leading != frag->leading) ||\n-\t\t\t    (trailing != frag->trailing))\n-\t\t\t\tfprintf(stderr, \"Context reduced to (%ld/%ld)\"\n-\t\t\t\t\t\" to apply fragment at %d\\n\",\n-\t\t\t\t\tleading, trailing, pos + lines);\n-\n-\t\t\tstrbuf_splice(buf, offset, oldsize, newlines, newsize);\n-\t\t\toffset = 0;\n+\t\tapplied_pos = find_pos(img, &preimage, &postimage,\n+\t\t\t\t       pos, match_beginning, match_end);\n+\n+\t\tif (applied_pos >= 0)\n \t\t\tbreak;\n-\t\t}\n \n \t\t/* Am I at my context limits? */\n \t\tif ((leading <= p_context) && (trailing <= p_context))\n@@ -1814,33 +1928,63 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,\n \t\t\tmatch_beginning = match_end = 0;\n \t\t\tcontinue;\n \t\t}\n+\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-\t\t\tremove_first_line(&newlines, &newsize);\n+\t\t\tremove_first_line(&preimage);\n+\t\t\tremove_first_line(&postimage);\n \t\t\tpos--;\n \t\t\tleading--;\n \t\t}\n \t\tif (trailing > leading) {\n-\t\t\tremove_last_line(&oldlines, &oldsize);\n-\t\t\tremove_last_line(&newlines, &newsize);\n+\t\t\tremove_last_line(&preimage);\n+\t\t\tremove_last_line(&postimage);\n \t\t\ttrailing--;\n \t\t}\n \t}\n \n-\tif (offset && apply_verbosely)\n-\t\terror(\"while searching for:\\n%.*s\", oldsize, oldlines);\n+\tif (applied_pos >= 0) {\n+\t\tif (ws_error_action == correct_ws_error &&\n+\t\t    new_blank_lines_at_end &&\n+\t\t    postimage.nr + applied_pos == img->nr) {\n+\t\t\t/*\n+\t\t\t * If the patch application adds blank lines\n+\t\t\t * at the end, and if the patch applies at the\n+\t\t\t * end of the image, remove those added blank\n+\t\t\t * lines.\n+\t\t\t */\n+\t\t\twhile (new_blank_lines_at_end--)\n+\t\t\t\tremove_last_line(&postimage);\n+\t\t}\n+\n+\t\t/*\n+\t\t * Warn if it was necessary to reduce the number\n+\t\t * of context lines.\n+\t\t */\n+\t\tif ((leading != frag->leading) ||\n+\t\t    (trailing != frag->trailing))\n+\t\t\tfprintf(stderr, \"Context reduced to (%ld/%ld)\"\n+\t\t\t\t\" to apply fragment at %d\\n\",\n+\t\t\t\tleading, trailing, applied_pos+1);\n+\t\tupdate_image(img, applied_pos, &preimage, &postimage);\n+\t} else {\n+\t\tif (apply_verbosely)\n+\t\t\terror(\"while searching for:\\n%.*s\", oldsize, oldlines);\n+\t}\n \n \tfree(old);\n \tfree(new);\n-\treturn offset;\n+\tfree(preimage.line_allocated);\n+\tfree(postimage.line_allocated);\n+\n+\treturn (applied_pos < 0);\n }\n \n-static int apply_binary_fragment(struct strbuf *buf, struct patch *patch)\n+static int apply_binary_fragment(struct image *img, struct patch *patch)\n {\n \tstruct fragment *fragment = patch->fragments;\n \tunsigned long len;\n@@ -1857,22 +2001,26 @@ static int apply_binary_fragment(struct strbuf *buf, struct patch *patch)\n \t}\n \tswitch (fragment->binary_patch_method) {\n \tcase BINARY_DELTA_DEFLATED:\n-\t\tdst = patch_delta(buf->buf, buf->len, fragment->patch,\n+\t\tdst = patch_delta(img->buf, img->len, fragment->patch,\n \t\t\t\t  fragment->size, &len);\n \t\tif (!dst)\n \t\t\treturn -1;\n-\t\t/* XXX patch_delta NUL-terminates */\n-\t\tstrbuf_attach(buf, dst, len, len + 1);\n+\t\tclear_image(img);\n+\t\timg->buf = dst;\n+\t\timg->len = len;\n \t\treturn 0;\n \tcase BINARY_LITERAL_DEFLATED:\n-\t\tstrbuf_reset(buf);\n-\t\tstrbuf_add(buf, fragment->patch, fragment->size);\n+\t\tclear_image(img);\n+\t\timg->len = fragment->size;\n+\t\timg->buf = xmalloc(img->len+1);\n+\t\tmemcpy(img->buf, fragment->patch, img->len);\n+\t\timg->buf[img->len] = '\\0';\n \t\treturn 0;\n \t}\n \treturn -1;\n }\n \n-static int apply_binary(struct strbuf *buf, struct patch *patch)\n+static int apply_binary(struct image *img, struct patch *patch)\n {\n \tconst char *name = patch->old_name ? patch->old_name : patch->new_name;\n \tunsigned char sha1[20];\n@@ -1893,7 +2041,7 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)\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+\t\thash_sha1_file(img->buf, img->len, blob_type, sha1);\n \t\tif (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))\n \t\t\treturn error(\"the patch applies to '%s' (%s), \"\n \t\t\t\t     \"which does not match the \"\n@@ -1902,14 +2050,14 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)\n \t}\n \telse {\n \t\t/* Otherwise, the old one must be empty. */\n-\t\tif (buf->len)\n+\t\tif (img->len)\n \t\t\treturn error(\"the patch applies to an empty \"\n \t\t\t\t     \"'%s' but it is not empty\", name);\n \t}\n \n \tget_sha1_hex(patch->new_sha1_prefix, sha1);\n \tif (is_null_sha1(sha1)) {\n-\t\tstrbuf_release(buf);\n+\t\tclear_image(img);\n \t\treturn 0; /* deletion patch */\n \t}\n \n@@ -1924,20 +2072,21 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)\n \t\t\treturn error(\"the necessary postimage %s for \"\n \t\t\t\t     \"'%s' cannot be read\",\n \t\t\t\t     patch->new_sha1_prefix, name);\n-\t\t/* XXX read_sha1_file NUL-terminates */\n-\t\tstrbuf_attach(buf, result, size, size + 1);\n+\t\tclear_image(img);\n+\t\timg->buf = result;\n+\t\timg->len = size;\n \t} else {\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-\t\tif (apply_binary_fragment(buf, patch))\n+\t\tif (apply_binary_fragment(img, patch))\n \t\t\treturn error(\"binary patch does not apply to '%s'\",\n \t\t\t\t     name);\n \n \t\t/* verify that the result matches */\n-\t\thash_sha1_file(buf->buf, buf->len, blob_type, sha1);\n+\t\thash_sha1_file(img->buf, img->len, blob_type, sha1);\n \t\tif (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))\n \t\t\treturn error(\"binary patch to '%s' creates incorrect result (expecting %s, got %s)\",\n \t\t\t\tname, patch->new_sha1_prefix, sha1_to_hex(sha1));\n@@ -1946,7 +2095,7 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)\n \treturn 0;\n }\n \n-static int apply_fragments(struct strbuf *buf, struct patch *patch)\n+static int apply_fragments(struct image *img, struct patch *patch)\n {\n \tstruct fragment *frag = patch->fragments;\n \tconst char *name = patch->old_name ? patch->old_name : patch->new_name;\n@@ -1954,10 +2103,10 @@ static int apply_fragments(struct strbuf *buf, struct patch *patch)\n \tunsigned inaccurate_eof = patch->inaccurate_eof;\n \n \tif (patch->is_binary)\n-\t\treturn apply_binary(buf, patch);\n+\t\treturn apply_binary(img, patch);\n \n \twhile (frag) {\n-\t\tif (apply_one_fragment(buf, frag, inaccurate_eof, ws_rule)) {\n+\t\tif (apply_one_fragment(img, 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@@ -1993,6 +2142,9 @@ static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)\n static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)\n {\n \tstruct strbuf buf;\n+\tstruct image image;\n+\tsize_t len;\n+\tchar *img;\n \n \tstrbuf_init(&buf, 0);\n \tif (cached) {\n@@ -2015,9 +2167,14 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *\n \t\t}\n \t}\n \n-\tif (apply_fragments(&buf, patch) < 0)\n+\timg = strbuf_detach(&buf, &len);\n+\tprepare_image(&image, img, len, !patch->is_binary);\n+\n+\tif (apply_fragments(&image, patch) < 0)\n \t\treturn -1; /* note with --reject this succeeds. */\n-\tpatch->result = strbuf_detach(&buf, &patch->resultsize);\n+\tpatch->result = image.buf;\n+\tpatch->resultsize = image.len;\n+\tfree(image.line_allocated);\n \n \tif (0 < patch->is_delete && patch->resultsize)\n \t\treturn error(\"removal patch leaves file contents\");\n-- \n1.5.4.2.g41ac4\n"},{"id":"67106","messageId":"1201949659-27725-6-git-send-email-gitster@pobox.com","threadId":"11836","inReplyTo":"1201949659-27725-5-git-send-email-gitster@pobox.com","subject":"[PATCH 05/13] builtin-apply.c: optimize match_beginning/end processing a bit.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-02T10:54:11Z","receivedAt":"2008-02-02T10:54:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Wnen the caller knows the hunk needs to match at the beginning\nor at the end, there is no point starting from the line number\nthat is found in the patch and trying match with increasing\noffset.  The logic to find matching lines was made more line\noriented with the previous patch and this optimization is now\ntrivial.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-apply.c |   10 ++++++++++\n 1 files changed, 10 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex e046e87..dc650f1 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -1579,6 +1579,16 @@ static int find_pos(struct image *img,\n \tif (preimage->nr > img->nr)\n \t\treturn -1;\n \n+\t/*\n+\t * If match_begining or match_end is specified, there is no\n+\t * point starting from a wrong line that will never match and\n+\t * wander around and wait for a match at the specified end.\n+\t */\n+\tif (match_beginning)\n+\t\tline = 0;\n+\telse if (match_end)\n+\t\tline = img->nr - preimage->nr;\n+\n \ttry = 0;\n \tfor (i = 0; i < line; i++)\n \t\ttry += img->line[i].len;\n-- \n1.5.4.2.g41ac4\n"},{"id":"67110","messageId":"1201949659-27725-7-git-send-email-gitster@pobox.com","threadId":"11836","inReplyTo":"1201949659-27725-6-git-send-email-gitster@pobox.com","subject":"[PATCH 06/13] builtin-apply.c: mark common context lines in lineinfo structure.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-02T10:54:12Z","receivedAt":"2008-02-02T10:54:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This updates the way preimage and postimage in a patch hunk is\nparsed and prepared for applying.  By looking at image->line[n].flag,\nthe code can tell if it is a common context line that is the\nsame between the preimage and the postimage.\n\nThis matters when we actually start applying a patch with\ncontexts that have whitespace breakages that have already been\nfixed in the target file.\n---\n builtin-apply.c |   57 ++++++++++++++++++++++++++++++------------------------\n 1 files changed, 32 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex dc650f1..acd84f9 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -170,6 +170,7 @@ struct line {\n \tsize_t len;\n \tunsigned hash : 24;\n \tunsigned flag : 8;\n+#define LINE_COMMON     1\n };\n \n /*\n@@ -179,6 +180,7 @@ struct image {\n \tchar *buf;\n \tsize_t len;\n \tsize_t nr;\n+\tsize_t alloc;\n \tstruct line *line_allocated;\n \tstruct line *line;\n };\n@@ -195,49 +197,39 @@ static uint32_t hash_line(const char *cp, size_t len)\n \treturn h;\n }\n \n+static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag)\n+{\n+\tALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc);\n+\timg->line_allocated[img->nr].len = len;\n+\timg->line_allocated[img->nr].hash = hash_line(bol, len);\n+\timg->line_allocated[img->nr].flag = flag;\n+\timg->nr++;\n+}\n+\n static void prepare_image(struct image *image, char *buf, size_t len,\n \t\t\t  int prepare_linetable)\n {\n \tconst char *cp, *ep;\n-\tint n;\n \n+\tmemset(image, 0, sizeof(*image));\n \timage->buf = buf;\n \timage->len = len;\n \n-\tif (!prepare_linetable) {\n-\t\timage->line = NULL;\n-\t\timage->line_allocated = NULL;\n-\t\timage->nr = 0;\n+\tif (!prepare_linetable)\n \t\treturn;\n-\t}\n \n \tep = image->buf + image->len;\n-\n-\t/* First count lines */\n \tcp = image->buf;\n-\tn = 0;\n-\twhile (cp < ep) {\n-\t\tcp = strchrnul(cp, '\\n');\n-\t\tn++;\n-\t\tcp++;\n-\t}\n-\n-\timage->line_allocated = xcalloc(n, sizeof(struct line));\n-\timage->line = image->line_allocated;\n-\timage->nr = n;\n-\tcp = image->buf;\n-\tn = 0;\n \twhile (cp < ep) {\n \t\tconst char *next;\n \t\tfor (next = cp; next < ep && *next != '\\n'; next++)\n \t\t\t;\n \t\tif (next < ep)\n \t\t\tnext++;\n-\t\timage->line[n].len = next - cp;\n-\t\timage->line[n].hash = hash_line(cp, next - cp);\n+\t\tadd_line_info(image, cp, next - cp, 0);\n \t\tcp = next;\n-\t\tn++;\n \t}\n+\timage->line = image->line_allocated;\n }\n \n static void clear_image(struct image *image)\n@@ -1822,6 +1814,9 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,\n \tstruct image preimage;\n \tstruct image postimage;\n \n+\tmemset(&preimage, 0, sizeof(preimage));\n+\tmemset(&postimage, 0, sizeof(postimage));\n+\n \twhile (size > 0) {\n \t\tchar first;\n \t\tint len = linelen(patch, size);\n@@ -1857,10 +1852,14 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,\n \t\t\t\tbreak;\n \t\t\told[oldsize++] = '\\n';\n \t\t\tnew[newsize++] = '\\n';\n+\t\t\tadd_line_info(&preimage, \"\\n\", 1, LINE_COMMON);\n+\t\t\tadd_line_info(&postimage, \"\\n\", 1, LINE_COMMON);\n \t\t\tbreak;\n \t\tcase ' ':\n \t\tcase '-':\n \t\t\tmemcpy(old + oldsize, patch + 1, plen);\n+\t\t\tadd_line_info(&preimage, old + oldsize, plen,\n+\t\t\t\t      (first == ' ' ? LINE_COMMON : 0));\n \t\t\toldsize += plen;\n \t\t\tif (first == '-')\n \t\t\t\tbreak;\n@@ -1869,6 +1868,9 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,\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, ws_rule);\n+\t\t\t\tadd_line_info(&postimage, new + newsize, added,\n+\t\t\t\t\t      (first == '+' ? 0 : LINE_COMMON));\n+\n \t\t\t\tnewsize += added;\n \t\t\t\tif (first == '+' &&\n \t\t\t\t    added == 1 && new[newsize-1] == '\\n')\n@@ -1921,8 +1923,13 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,\n \t}\n \n \tpos = frag->newpos ? (frag->newpos - 1) : 0;\n-\tprepare_image(&preimage, oldlines, oldsize, 1);\n-\tprepare_image(&postimage, newlines, newsize, 1);\n+\tpreimage.buf = old;\n+\tpreimage.len = oldsize;\n+\tpostimage.buf = new;\n+\tpostimage.len = newsize;\n+\tpreimage.line = preimage.line_allocated;\n+\tpostimage.line = postimage.line_allocated;\n+\n \tfor (;;) {\n \n \t\tapplied_pos = find_pos(img, &preimage, &postimage,\n-- \n1.5.4.2.g41ac4\n"},{"id":"67109","messageId":"1201949659-27725-8-git-send-email-gitster@pobox.com","threadId":"11836","inReplyTo":"1201949659-27725-7-git-send-email-gitster@pobox.com","subject":"[PATCH 07/13] builtin-apply.c: clean-up apply_one_fragment()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-02T10:54:13Z","receivedAt":"2008-02-02T10:54:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"We had two pointer variables pointing to the same buffer and an\ninteger variable used to index into its tail part that was\nactive (old, oldlines and oldsize for the preimage, and their\n'new' counterparts for the postimage).\n\nTo help readability, use 'oldlines' as the allocated pointer,\nand use 'old' as the pointer to the tail that advances while the\ncode builds up the contents in the buffer.  The size 'oldsize'\ncan be computed as (old-oldines).\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-apply.c |   55 +++++++++++++++++++++++++++----------------------------\n 1 files changed, 27 insertions(+), 28 deletions(-)\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex acd84f9..7fb3305 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -1804,10 +1804,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,\n \tint match_beginning, match_end;\n \tconst char *patch = frag->patch;\n \tint size = frag->size;\n-\tchar *old = xmalloc(size);\n-\tchar *new = xmalloc(size);\n-\tchar *oldlines, *newlines;\n-\tint oldsize = 0, newsize = 0;\n+\tchar *old, *new, *oldlines, *newlines;\n \tint new_blank_lines_at_end = 0;\n \tunsigned long leading, trailing;\n \tint pos, applied_pos;\n@@ -1816,7 +1813,11 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,\n \n \tmemset(&preimage, 0, sizeof(preimage));\n \tmemset(&postimage, 0, sizeof(postimage));\n+\toldlines = xmalloc(size);\n+\tnewlines = xmalloc(size);\n \n+\told = oldlines;\n+\tnew = newlines;\n \twhile (size > 0) {\n \t\tchar first;\n \t\tint len = linelen(patch, size);\n@@ -1833,7 +1834,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,\n \t\t * followed by \"\\ No newline\", then we also remove the\n \t\t * last one (which is the newline, of course).\n \t\t */\n-\t\tplen = len-1;\n+\t\tplen = len - 1;\n \t\tif (len < size && patch[len] == '\\\\')\n \t\t\tplen--;\n \t\tfirst = *patch;\n@@ -1850,30 +1851,30 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,\n \t\t\tif (plen < 0)\n \t\t\t\t/* ... followed by '\\No newline'; nothing */\n \t\t\t\tbreak;\n-\t\t\told[oldsize++] = '\\n';\n-\t\t\tnew[newsize++] = '\\n';\n+\t\t\t*old++ = '\\n';\n+\t\t\t*new++ = '\\n';\n \t\t\tadd_line_info(&preimage, \"\\n\", 1, LINE_COMMON);\n \t\t\tadd_line_info(&postimage, \"\\n\", 1, LINE_COMMON);\n \t\t\tbreak;\n \t\tcase ' ':\n \t\tcase '-':\n-\t\t\tmemcpy(old + oldsize, patch + 1, plen);\n-\t\t\tadd_line_info(&preimage, old + oldsize, plen,\n+\t\t\tmemcpy(old, patch + 1, plen);\n+\t\t\tadd_line_info(&preimage, old, plen,\n \t\t\t\t      (first == ' ' ? LINE_COMMON : 0));\n-\t\t\toldsize += plen;\n+\t\t\told += plen;\n \t\t\tif (first == '-')\n \t\t\t\tbreak;\n \t\t/* Fall-through for ' ' */\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\tint added = apply_line(new, patch,\n \t\t\t\t\t\t       plen, ws_rule);\n-\t\t\t\tadd_line_info(&postimage, new + newsize, added,\n+\t\t\t\tadd_line_info(&postimage, new, added,\n \t\t\t\t\t      (first == '+' ? 0 : LINE_COMMON));\n \n-\t\t\t\tnewsize += added;\n+\t\t\t\tnew += added;\n \t\t\t\tif (first == '+' &&\n-\t\t\t\t    added == 1 && new[newsize-1] == '\\n')\n+\t\t\t\t    added == 1 && new[-1] == '\\n')\n \t\t\t\t\tadded_blank_line = 1;\n \t\t\t}\n \t\t\tbreak;\n@@ -1892,16 +1893,13 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,\n \t\tpatch += len;\n \t\tsize -= len;\n \t}\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    old > oldlines && old[-1] == '\\n' &&\n+\t    new > newlines && new[-1] == '\\n') {\n+\t\told--;\n+\t\tnew--;\n \t}\n \n-\toldlines = old;\n-\tnewlines = new;\n \tleading = frag->leading;\n \ttrailing = frag->trailing;\n \n@@ -1923,10 +1921,10 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,\n \t}\n \n \tpos = frag->newpos ? (frag->newpos - 1) : 0;\n-\tpreimage.buf = old;\n-\tpreimage.len = oldsize;\n-\tpostimage.buf = new;\n-\tpostimage.len = newsize;\n+\tpreimage.buf = oldlines;\n+\tpreimage.len = old - oldlines;\n+\tpostimage.buf = newlines;\n+\tpostimage.len = new - newlines;\n \tpreimage.line = preimage.line_allocated;\n \tpostimage.line = postimage.line_allocated;\n \n@@ -1990,11 +1988,12 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,\n \t\tupdate_image(img, applied_pos, &preimage, &postimage);\n \t} else {\n \t\tif (apply_verbosely)\n-\t\t\terror(\"while searching for:\\n%.*s\", oldsize, oldlines);\n+\t\t\terror(\"while searching for:\\n%.*s\",\n+\t\t\t      (int)(old - oldlines), oldlines);\n \t}\n \n-\tfree(old);\n-\tfree(new);\n+\tfree(oldlines);\n+\tfree(newlines);\n \tfree(preimage.line_allocated);\n \tfree(postimage.line_allocated);\n \n-- \n1.5.4.2.g41ac4\n"},{"id":"67108","messageId":"1201949659-27725-9-git-send-email-gitster@pobox.com","threadId":"11836","inReplyTo":"1201949659-27725-8-git-send-email-gitster@pobox.com","subject":"[PATCH 08/13] builtin-apply.c: simplify calling site to apply_line()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-02T10:54:14Z","receivedAt":"2008-02-02T10:54:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The function apply_line() changed its behaviour depending on the\nws_error_action, whitespace_error and if the input was a context.\nMake its caller responsible for such checking so that we can convert\nthe function to copy the contents of line while fixing whitespace\nbreakage more easily.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-apply.c |   38 ++++++++++++++++++++------------------\n 1 files changed, 20 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex 7fb3305..d0d008f 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -1642,7 +1642,7 @@ static void remove_last_line(struct image *img)\n \timg->len -= img->line[--img->nr].len;\n }\n \n-static int apply_line(char *output, const char *patch, int plen,\n+static int copy_wsfix(char *output, const char *patch, int plen,\n \t\t      unsigned ws_rule)\n {\n \t/*\n@@ -1659,12 +1659,6 @@ static int apply_line(char *output, const char *patch, int plen,\n \tint need_fix_leading_space = 0;\n \tchar *buf;\n \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@@ -1821,7 +1815,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,\n \twhile (size > 0) {\n \t\tchar first;\n \t\tint len = linelen(patch, size);\n-\t\tint plen;\n+\t\tint plen, added;\n \t\tint added_blank_line = 0;\n \n \t\tif (!len)\n@@ -1866,17 +1860,25 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,\n \t\t\t\tbreak;\n \t\t/* Fall-through for ' ' */\n \t\tcase '+':\n-\t\t\tif (first != '+' || !no_add) {\n-\t\t\t\tint added = apply_line(new, patch,\n-\t\t\t\t\t\t       plen, ws_rule);\n-\t\t\t\tadd_line_info(&postimage, new, added,\n-\t\t\t\t\t      (first == '+' ? 0 : LINE_COMMON));\n-\n-\t\t\t\tnew += added;\n-\t\t\t\tif (first == '+' &&\n-\t\t\t\t    added == 1 && new[-1] == '\\n')\n-\t\t\t\t\tadded_blank_line = 1;\n+\t\t\t/* --no-add does not add new lines */\n+\t\t\tif (first == '+' && no_add)\n+\t\t\t\tbreak;\n+\n+\t\t\tif (first != '+' ||\n+\t\t\t    !whitespace_error ||\n+\t\t\t    ws_error_action != correct_ws_error) {\n+\t\t\t\tmemcpy(new, patch + 1, plen);\n+\t\t\t\tadded = plen;\n+\t\t\t}\n+\t\t\telse {\n+\t\t\t\tadded = copy_wsfix(new, patch, plen, ws_rule);\n \t\t\t}\n+\t\t\tadd_line_info(&postimage, new, added,\n+\t\t\t\t      (first == '+' ? 0 : LINE_COMMON));\n+\t\t\tnew += added;\n+\t\t\tif (first == '+' &&\n+\t\t\t    added == 1 && new[-1] == '\\n')\n+\t\t\t\tadded_blank_line = 1;\n \t\t\tbreak;\n \t\tcase '@': case '\\\\':\n \t\t\t/* Ignore it, we already handled it */\n-- \n1.5.4.2.g41ac4\n"},{"id":"67114","messageId":"1201949659-27725-10-git-send-email-gitster@pobox.com","threadId":"11836","inReplyTo":"1201949659-27725-9-git-send-email-gitster@pobox.com","subject":"[PATCH 09/13] builtin-apply.c: do not feed copy_wsfix() leading '+'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-02T10:54:15Z","receivedAt":"2008-02-02T10:54:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The \"patch\" parameter used to include leading '+' of an added\nline in the patch, and the array was treated as 1-based.  Make\nit accept the contents of the line alone and simplify the code.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-apply.c |   38 ++++++++++++++++++--------------------\n 1 files changed, 18 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex d0d008f..0bc33bd 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -1646,16 +1646,15 @@ static int copy_wsfix(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-\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+\t * plen is number of bytes to be copied from patch, starting\n+\t * at patch.  Typically patch[plen-1] is '\\n', unless this is\n+\t * the incomplete last line.\n \t */\n \tint i;\n \tint add_nl_to_tail = 0;\n \tint fixed = 0;\n-\tint last_tab_in_indent = 0;\n-\tint last_space_in_indent = 0;\n+\tint last_tab_in_indent = -1;\n+\tint last_space_in_indent = -1;\n \tint need_fix_leading_space = 0;\n \tchar *buf;\n \n@@ -1663,11 +1662,11 @@ static int copy_wsfix(char *output, const char *patch, int plen,\n \t * Strip trailing whitespace\n \t */\n \tif ((ws_rule & WS_TRAILING_SPACE) &&\n-\t    (1 < plen && isspace(patch[plen-1]))) {\n-\t\tif (patch[plen] == '\\n')\n+\t    (2 < plen && isspace(patch[plen-2]))) {\n+\t\tif (patch[plen-1] == '\\n')\n \t\t\tadd_nl_to_tail = 1;\n \t\tplen--;\n-\t\twhile (0 < plen && isspace(patch[plen]))\n+\t\twhile (0 < plen && isspace(patch[plen-1]))\n \t\t\tplen--;\n \t\tfixed = 1;\n \t}\n@@ -1675,25 +1674,25 @@ static int copy_wsfix(char *output, const char *patch, int plen,\n \t/*\n \t * Check leading whitespaces (indent)\n \t */\n-\tfor (i = 1; i < plen; i++) {\n+\tfor (i = 0; i < plen; i++) {\n \t\tchar ch = patch[i];\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    8 <= i - last_tab_in_indent)\n \t\t\t\tneed_fix_leading_space = 1;\n-\t\t}\n-\t\telse\n+\t\t} else\n \t\t\tbreak;\n \t}\n \n \tbuf = output;\n \tif (need_fix_leading_space) {\n+\t\t/* Process indent ourselves */\n \t\tint consecutive_spaces = 0;\n \t\tint last = last_tab_in_indent + 1;\n \n@@ -1706,10 +1705,10 @@ static int copy_wsfix(char *output, const char *patch, int plen,\n \t\t}\n \n \t\t/*\n-\t\t * between patch[1..last], strip the funny spaces,\n+\t\t * between patch[0..last-1], strip the funny spaces,\n \t\t * updating them to tab as needed.\n \t\t */\n-\t\tfor (i = 1; i < last; i++, plen--) {\n+\t\tfor (i = 0; i < last; i++) {\n \t\t\tchar ch = patch[i];\n \t\t\tif (ch != ' ') {\n \t\t\t\tconsecutive_spaces = 0;\n@@ -1724,13 +1723,12 @@ static int copy_wsfix(char *output, const char *patch, int plen,\n \t\t}\n \t\twhile (0 < consecutive_spaces--)\n \t\t\t*output++ = ' ';\n+\t\tplen -= last;\n+\t\tpatch += last;\n \t\tfixed = 1;\n-\t\ti = last;\n \t}\n-\telse\n-\t\ti = 1;\n \n-\tmemcpy(output, patch + i, plen);\n+\tmemcpy(output, patch, plen);\n \tif (add_nl_to_tail)\n \t\toutput[plen++] = '\\n';\n \tif (fixed)\n@@ -1871,7 +1869,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,\n \t\t\t\tadded = plen;\n \t\t\t}\n \t\t\telse {\n-\t\t\t\tadded = copy_wsfix(new, patch, plen, ws_rule);\n+\t\t\t\tadded = copy_wsfix(new, patch + 1, plen, ws_rule);\n \t\t\t}\n \t\t\tadd_line_info(&postimage, new, added,\n \t\t\t\t      (first == '+' ? 0 : LINE_COMMON));\n-- \n1.5.4.2.g41ac4\n"},{"id":"67111","messageId":"1201949659-27725-11-git-send-email-gitster@pobox.com","threadId":"11836","inReplyTo":"1201949659-27725-10-git-send-email-gitster@pobox.com","subject":"[PATCH 10/13] builtin-apply.c: move copy_wsfix() function a bit higher.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-02T10:54:16Z","receivedAt":"2008-02-02T10:54:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"I'll be calling this from match_fragment() in later rounds.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-apply.c |  188 +++++++++++++++++++++++++++---------------------------\n 1 files changed, 94 insertions(+), 94 deletions(-)\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex 0bc33bd..2af625a 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -1515,6 +1515,100 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)\n \t}\n }\n \n+static int copy_wsfix(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, starting\n+\t * at patch.  Typically patch[plen-1] is '\\n', unless this is\n+\t * the incomplete last line.\n+\t */\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 need_fix_leading_space = 0;\n+\tchar *buf;\n+\n+\t/*\n+\t * Strip trailing whitespace\n+\t */\n+\tif ((ws_rule & WS_TRAILING_SPACE) &&\n+\t    (2 < plen && isspace(patch[plen-2]))) {\n+\t\tif (patch[plen-1] == '\\n')\n+\t\t\tadd_nl_to_tail = 1;\n+\t\tplen--;\n+\t\twhile (0 < plen && isspace(patch[plen-1]))\n+\t\t\tplen--;\n+\t\tfixed = 1;\n+\t}\n+\n+\t/*\n+\t * Check leading whitespaces (indent)\n+\t */\n+\tfor (i = 0; i < plen; i++) {\n+\t\tchar ch = patch[i];\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    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    8 <= i - last_tab_in_indent)\n+\t\t\t\tneed_fix_leading_space = 1;\n+\t\t} else\n+\t\t\tbreak;\n+\t}\n+\n+\tbuf = output;\n+\tif (need_fix_leading_space) {\n+\t\t/* Process indent ourselves */\n+\t\tint consecutive_spaces = 0;\n+\t\tint last = last_tab_in_indent + 1;\n+\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+\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[0..last-1], strip the funny spaces,\n+\t\t * updating them to tab as needed.\n+\t\t */\n+\t\tfor (i = 0; i < last; i++) {\n+\t\t\tchar ch = patch[i];\n+\t\t\tif (ch != ' ') {\n+\t\t\t\tconsecutive_spaces = 0;\n+\t\t\t\t*output++ = ch;\n+\t\t\t} else {\n+\t\t\t\tconsecutive_spaces++;\n+\t\t\t\tif (consecutive_spaces == 8) {\n+\t\t\t\t\t*output++ = '\\t';\n+\t\t\t\t\tconsecutive_spaces = 0;\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\tplen -= last;\n+\t\tpatch += last;\n+\t\tfixed = 1;\n+\t}\n+\n+\tmemcpy(output, patch, plen);\n+\tif (add_nl_to_tail)\n+\t\toutput[plen++] = '\\n';\n+\tif (fixed)\n+\t\tapplied_after_fixing_ws++;\n+\treturn output + plen - buf;\n+}\n+\n static int match_fragment(struct image *img,\n \t\t\t  struct image *preimage,\n \t\t\t  struct image *postimage,\n@@ -1642,100 +1736,6 @@ static void remove_last_line(struct image *img)\n \timg->len -= img->line[--img->nr].len;\n }\n \n-static int copy_wsfix(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, starting\n-\t * at patch.  Typically patch[plen-1] is '\\n', unless this is\n-\t * the incomplete last line.\n-\t */\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 need_fix_leading_space = 0;\n-\tchar *buf;\n-\n-\t/*\n-\t * Strip trailing whitespace\n-\t */\n-\tif ((ws_rule & WS_TRAILING_SPACE) &&\n-\t    (2 < plen && isspace(patch[plen-2]))) {\n-\t\tif (patch[plen-1] == '\\n')\n-\t\t\tadd_nl_to_tail = 1;\n-\t\tplen--;\n-\t\twhile (0 < plen && isspace(patch[plen-1]))\n-\t\t\tplen--;\n-\t\tfixed = 1;\n-\t}\n-\n-\t/*\n-\t * Check leading whitespaces (indent)\n-\t */\n-\tfor (i = 0; i < plen; i++) {\n-\t\tchar ch = patch[i];\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    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    8 <= i - last_tab_in_indent)\n-\t\t\t\tneed_fix_leading_space = 1;\n-\t\t} else\n-\t\t\tbreak;\n-\t}\n-\n-\tbuf = output;\n-\tif (need_fix_leading_space) {\n-\t\t/* Process indent ourselves */\n-\t\tint consecutive_spaces = 0;\n-\t\tint last = last_tab_in_indent + 1;\n-\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-\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[0..last-1], strip the funny spaces,\n-\t\t * updating them to tab as needed.\n-\t\t */\n-\t\tfor (i = 0; i < last; i++) {\n-\t\t\tchar ch = patch[i];\n-\t\t\tif (ch != ' ') {\n-\t\t\t\tconsecutive_spaces = 0;\n-\t\t\t\t*output++ = ch;\n-\t\t\t} else {\n-\t\t\t\tconsecutive_spaces++;\n-\t\t\t\tif (consecutive_spaces == 8) {\n-\t\t\t\t\t*output++ = '\\t';\n-\t\t\t\t\tconsecutive_spaces = 0;\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\tplen -= last;\n-\t\tpatch += last;\n-\t\tfixed = 1;\n-\t}\n-\n-\tmemcpy(output, patch, plen);\n-\tif (add_nl_to_tail)\n-\t\toutput[plen++] = '\\n';\n-\tif (fixed)\n-\t\tapplied_after_fixing_ws++;\n-\treturn output + plen - buf;\n-}\n-\n static void update_image(struct image *img,\n \t\t\t int applied_pos,\n \t\t\t struct image *preimage,\n-- \n1.5.4.2.g41ac4\n"},{"id":"67113","messageId":"1201949659-27725-12-git-send-email-gitster@pobox.com","threadId":"11836","inReplyTo":"1201949659-27725-11-git-send-email-gitster@pobox.com","subject":"[PATCH 11/13] builtin-apply.c: pass ws_rule down to match_fragment()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-02T10:54:17Z","receivedAt":"2008-02-02T10:54:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This is necessary to allow match_fragment() to attempt a match\nwith a preimage that is based on a version before whitespace\nerrors were fixed.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-apply.c |    8 +++++---\n 1 files changed, 5 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex 2af625a..5f3c047 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -1614,6 +1614,7 @@ static int match_fragment(struct image *img,\n \t\t\t  struct image *postimage,\n \t\t\t  unsigned long try,\n \t\t\t  int try_lno,\n+\t\t\t  unsigned ws_rule,\n \t\t\t  int match_beginning, int match_end)\n {\n \tint i;\n@@ -1656,6 +1657,7 @@ static int find_pos(struct image *img,\n \t\t    struct image *preimage,\n \t\t    struct image *postimage,\n \t\t    int line,\n+\t\t    unsigned ws_rule,\n \t\t    int match_beginning, int match_end)\n {\n \tint i;\n@@ -1691,7 +1693,7 @@ static int find_pos(struct image *img,\n \n \tfor (i = 0; ; i++) {\n \t\tif (match_fragment(img, preimage, postimage,\n-\t\t\t\t   try, try_lno,\n+\t\t\t\t   try, try_lno, ws_rule,\n \t\t\t\t   match_beginning, match_end))\n \t\t\treturn try_lno;\n \n@@ -1930,8 +1932,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,\n \n \tfor (;;) {\n \n-\t\tapplied_pos = find_pos(img, &preimage, &postimage,\n-\t\t\t\t       pos, match_beginning, match_end);\n+\t\tapplied_pos = find_pos(img, &preimage, &postimage, pos,\n+\t\t\t\t       ws_rule, match_beginning, match_end);\n \n \t\tif (applied_pos >= 0)\n \t\t\tbreak;\n-- \n1.5.4.2.g41ac4\n"},{"id":"67115","messageId":"1201949659-27725-13-git-send-email-gitster@pobox.com","threadId":"11836","inReplyTo":"1201949659-27725-12-git-send-email-gitster@pobox.com","subject":"[PATCH 12/13] git-apply --whitespace=fix: fix whitespace fuzz introduced by previous run","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-02T10:54:18Z","receivedAt":"2008-02-02T10:54:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When you have more than one patch series, an earlier one of which\ntries to introduce whitespace breakages and a later one of which\nhas such a new line in its context, \"git-apply --whitespace=fix\"\nwill apply and fix the whitespace breakages in the earlier one,\nmaking the resulting file not to match the context of the later\npatch.\n\nA short demonstration is in the new test, t4125.\n\nFor example, suppose the first patch is:\n\n    diff a/hello.txt b/hello.txt\n    --- a/hello.txt\n    +++ b/hello.txt\n    @@ -20,3 +20,3 @@\n     Hello world.$\n    -How Are you$\n    -Today?$\n    +How are you $\n    +today? $\n\nto fix broken case in the string, but it introduces unwanted\ntrailing whitespaces to the result (pretend you are looking at\n\"cat -e\" output of the patch --- '$' signs are not in the patch\nbut are shown to make the EOL stand out).  And the second patch\nis to change the wording of the greeting further:\n\n    diff a/hello.txt b/hello.txt\n    --- a/hello.txt\n    +++ b/hello.txt\n    @@ -18,5 +18,5 @@\n     Greetings $\n\n    -Hello world.$\n    +Hello, everybody. $\n     How are you $\n    -today? $\n    +these days? $\n\nIf you apply the first one with --whitespace=fix, you will get\nthis as the result:\n\n    Hello world.$\n    How are you$\n    today?$\n\nand this does not match the preimage of the second patch, which\ndemands extra whitespace after \"How are you\" and \"today?\".\n\nThis series is about teaching \"git apply --whitespace=fix\" to\ncope with this situation better.  If the patch does not apply,\nit rewrites the second patch like this and retries:\n\n    diff a/hello.txt b/hello.txt\n    --- a/hello.txt\n    +++ b/hello.txt\n    @@ -18,5 +18,5 @@\n     Greetings$\n\n    -Hello world.$\n    +Hello, everybody.$\n     How are you$\n    -today?$\n    +these days?$\n\nThis is done by rewriting the preimage lines in the hunk\n(i.e. the lines that begin with ' ' or '-'), using the same\nwhitespace fixing rules as it is using to apply the patches, so\nthat it can notice what it did to the previous ones in the\nseries.\n\nA careful reader may notice that the first patch in the example\ndid not touch the \"Greetings\" line, so the trailing whitespace\nthat is in the original preimage of the second patch is not from\nthe series.  Is rewriting this context line a problem?\n\nIf you think about it, you will realize that the reason for the\ndifference is because the submitter's tree was based on an\nearlier version of the file that had whitespaces wrong on that\n\"Greetings\" line, and the change that introduced the \"Greetings\"\nline was added independently of this two-patch series to our\ntree already with an earlier \"git apply --whitespace=fix\".\n\nSo it may appear this logic is rewriting too much, it is not\nso.  It is just rewriting what we would have rewritten in the\npast.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-apply.c          |  133 ++++++++++++++++++++++++++++++++++++++++++++--\n t/t4125-apply-ws-fuzz.sh |  103 +++++++++++++++++++++++++++++++++++\n 2 files changed, 231 insertions(+), 5 deletions(-)\n create mode 100755 t/t4125-apply-ws-fuzz.sh\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex 5f3c047..fccf4a4 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -1516,7 +1516,7 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)\n }\n \n static int copy_wsfix(char *output, const char *patch, int plen,\n-\t\t      unsigned ws_rule)\n+\t\t      unsigned ws_rule, int count_error)\n {\n \t/*\n \t * plen is number of bytes to be copied from patch, starting\n@@ -1604,11 +1604,74 @@ static int copy_wsfix(char *output, const char *patch, int plen,\n \tmemcpy(output, patch, plen);\n \tif (add_nl_to_tail)\n \t\toutput[plen++] = '\\n';\n-\tif (fixed)\n+\tif (fixed && count_error)\n \t\tapplied_after_fixing_ws++;\n \treturn output + plen - buf;\n }\n \n+static void update_pre_post_images(struct image *preimage,\n+\t\t\t\t   struct image *postimage,\n+\t\t\t\t   char *buf,\n+\t\t\t\t   size_t len)\n+{\n+\tint i, ctx;\n+\tchar *new, *old, *fixed;\n+\tstruct image fixed_preimage;\n+\n+\t/*\n+\t * Update the preimage with whitespace fixes.  Note that we\n+\t * are not losing preimage->buf -- apply_one_fragment() will\n+\t * free \"oldlines\".\n+\t */\n+\tprepare_image(&fixed_preimage, buf, len, 1);\n+\tassert(fixed_preimage.nr == preimage->nr);\n+\tfor (i = 0; i < preimage->nr; i++)\n+\t\tfixed_preimage.line[i].flag = preimage->line[i].flag;\n+\tfree(preimage->line_allocated);\n+\t*preimage = fixed_preimage;\n+\n+\t/*\n+\t * Adjust the common context lines in postimage, in place.\n+\t * This is possible because whitespace fixing does not make\n+\t * the string grow.\n+\t */\n+\tnew = old = postimage->buf;\n+\tfixed = preimage->buf;\n+\tfor (i = ctx = 0; i < postimage->nr; i++) {\n+\t\tsize_t len = postimage->line[i].len;\n+\t\tif (!(postimage->line[i].flag & LINE_COMMON)) {\n+\t\t\t/* an added line -- no counterparts in preimage */\n+\t\t\tmemmove(new, old, len);\n+\t\t\told += len;\n+\t\t\tnew += len;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* a common context -- skip it in the original postimage */\n+\t\told += len;\n+\n+\t\t/* and find the corresponding one in the fixed preimage */\n+\t\twhile (ctx < preimage->nr &&\n+\t\t       !(preimage->line[ctx].flag & LINE_COMMON)) {\n+\t\t\tfixed += preimage->line[ctx].len;\n+\t\t\tctx++;\n+\t\t}\n+\t\tif (preimage->nr <= ctx)\n+\t\t\tdie(\"oops\");\n+\n+\t\t/* and copy it in, while fixing the line length */\n+\t\tlen = preimage->line[ctx].len;\n+\t\tmemcpy(new, fixed, len);\n+\t\tnew += len;\n+\t\tfixed += len;\n+\t\tpostimage->line[i].len = len;\n+\t\tctx++;\n+\t}\n+\n+\t/* Fix the length of the whole thing */\n+\tpostimage->len = new - postimage->buf;\n+}\n+\n static int match_fragment(struct image *img,\n \t\t\t  struct image *preimage,\n \t\t\t  struct image *postimage,\n@@ -1618,6 +1681,7 @@ static int match_fragment(struct image *img,\n \t\t\t  int match_beginning, int match_end)\n {\n \tint i;\n+\tchar *fixed_buf, *buf, *orig, *target;\n \n \tif (preimage->nr + try_lno > img->nr)\n \t\treturn 0;\n@@ -1646,10 +1710,68 @@ static int match_fragment(struct image *img,\n \t    !memcmp(img->buf + try, preimage->buf, preimage->len))\n \t\treturn 1;\n \n+\tif (ws_error_action != correct_ws_error)\n+\t\treturn 0;\n+\n+\t/*\n+\t * The hunk does not apply byte-by-byte, but the hash says\n+\t * it might with whitespace fuzz.\n+\t */\n+\tfixed_buf = xmalloc(preimage->len + 1);\n+\tbuf = fixed_buf;\n+\torig = preimage->buf;\n+\ttarget = img->buf + try;\n+\tfor (i = 0; i < preimage->nr; i++) {\n+\t\tsize_t fixlen; /* length after fixing the preimage */\n+\t\tsize_t oldlen = preimage->line[i].len;\n+\t\tsize_t tgtlen = img->line[try_lno + i].len;\n+\t\tsize_t tgtfixlen; /* length after fixing the target line */\n+\t\tchar tgtfixbuf[1024], *tgtfix;\n+\t\tint match;\n+\n+\t\t/* Try fixing the line in the preimage */\n+\t\tfixlen = copy_wsfix(buf, orig, oldlen, ws_rule, 0);\n+\n+\t\t/* Try fixing the line in the target */\n+\t\tif (sizeof(tgtfixbuf) < tgtlen)\n+\t\t\ttgtfix = tgtfixbuf;\n+\t\telse\n+\t\t\ttgtfix = xmalloc(tgtlen);\n+\t\ttgtfixlen = copy_wsfix(tgtfix, target, tgtlen, ws_rule, 0);\n+\n+\t\t/*\n+\t\t * If they match, either the preimage was based on\n+\t\t * a version before our tree fixed whitespace breakage,\n+\t\t * or we are lacking a whitespace-fix patch the tree\n+\t\t * the preimage was based on already had (i.e. target\n+\t\t * has whitespace breakage, the preimage doesn't).\n+\t\t * In either case, we are fixing the whitespace breakages\n+\t\t * so we might as well take the fix together with their\n+\t\t * real change.\n+\t\t */\n+\t\tmatch = (tgtfixlen == fixlen && !memcmp(tgtfix, buf, fixlen));\n+\n+\t\tif (tgtfix != tgtfixbuf)\n+\t\t\tfree(tgtfix);\n+\t\tif (!match)\n+\t\t\tgoto unmatch_exit;\n+\n+\t\torig += oldlen;\n+\t\tbuf += fixlen;\n+\t\ttarget += tgtlen;\n+\t}\n+\n \t/*\n-\t * NEEDSWORK: We can optionally match fuzzily here, but\n-\t * that is for a later round.\n+\t * Yes, the preimage is based on an older version that still\n+\t * has whitespace breakages unfixed, and fixing them makes the\n+\t * hunk match.  Update the context lines in the postimage.\n \t */\n+\tupdate_pre_post_images(preimage, postimage,\n+\t\t\t       fixed_buf, buf - fixed_buf);\n+\treturn 1;\n+\n+ unmatch_exit:\n+\tfree(fixed_buf);\n \treturn 0;\n }\n \n@@ -1871,7 +1993,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,\n \t\t\t\tadded = plen;\n \t\t\t}\n \t\t\telse {\n-\t\t\t\tadded = copy_wsfix(new, patch + 1, plen, ws_rule);\n+\t\t\t\tadded = copy_wsfix(new, patch + 1, plen,\n+\t\t\t\t\t\t   ws_rule, 1);\n \t\t\t}\n \t\t\tadd_line_info(&postimage, new, added,\n \t\t\t\t      (first == '+' ? 0 : LINE_COMMON));\ndiff --git a/t/t4125-apply-ws-fuzz.sh b/t/t4125-apply-ws-fuzz.sh\nnew file mode 100755\nindex 0000000..d6f15be\n--- /dev/null\n+++ b/t/t4125-apply-ws-fuzz.sh\n@@ -0,0 +1,103 @@\n+#!/bin/sh\n+\n+test_description='applying patch that has broken whitespaces in context'\n+\n+. ./test-lib.sh\n+\n+test_expect_success setup '\n+\n+\t>file &&\n+\tgit add file &&\n+\n+\t# file-0 is full of whitespace breakages\n+\tfor l in a bb c d eeee f ggg h\n+\tdo\n+\t\techo \"$l \"\n+\tdone >file-0 &&\n+\n+\t# patch-0 creates a whitespace broken file\n+\tcat file-0 >file &&\n+\tgit diff >patch-0 &&\n+\tgit add file &&\n+\n+\t# file-1 is still full of whitespace breakages,\n+\t# but has one line updated, without fixing any\n+\t# whitespaces.\n+\t# patch-1 records that change.\n+\tsed -e \"s/d/D/\" file-0 >file-1 &&\n+\tcat file-1 >file &&\n+\tgit diff >patch-1 &&\n+\n+\t# patch-all is the effect of both patch-0 and patch-1\n+\t>file &&\n+\tgit add file &&\n+\tcat file-1 >file &&\n+\tgit diff >patch-all &&\n+\n+\t# patch-2 is the same as patch-1 but is based\n+\t# on a version that already has whitespace fixed,\n+\t# and does not introduce whitespace breakages.\n+\tsed -e \"s/ $//\" patch-1 >patch-2 &&\n+\n+\t# If all whitespace breakages are fixed the contents\n+\t# should look like file-fixed\n+\tsed -e \"s/ $//\" file-1 >file-fixed\n+\n+'\n+\n+test_expect_success nofix '\n+\n+\t>file &&\n+\tgit add file &&\n+\n+\t# Baseline.  Applying without fixing any whitespace\n+\t# breakages.\n+\tgit apply --whitespace=nowarn patch-0 &&\n+\tgit apply --whitespace=nowarn patch-1 &&\n+\n+\t# The result should obviously match.\n+\tdiff -u file-1 file\n+'\n+\n+test_expect_success 'withfix (forward)' '\n+\n+\t>file &&\n+\tgit add file &&\n+\n+\t# The first application will munge the context lines\n+\t# the second patch depends on.  We should be able to\n+\t# adjust and still apply.\n+\tgit apply --whitespace=fix patch-0 &&\n+\tgit apply --whitespace=fix patch-1 &&\n+\n+\tdiff -u file-fixed file\n+'\n+\n+test_expect_success 'withfix (backward)' '\n+\n+\t>file &&\n+\tgit add file &&\n+\n+\t# Now we have a whitespace breakages on our side.\n+\tgit apply --whitespace=nowarn patch-0 &&\n+\n+\t# And somebody sends in a patch based on image\n+\t# with whitespace already fixed.\n+\tgit apply --whitespace=fix patch-2 &&\n+\n+\t# The result should accept the whitespace fixed\n+\t# postimage.  But the line with \"h\" is beyond context\n+\t# horizon and left unfixed.\n+\n+\tsed -e /h/d file-fixed >fixed-head &&\n+\tsed -e /h/d file >file-head &&\n+\tdiff -u fixed-head file-head &&\n+\n+\tsed -n -e /h/p file-fixed >fixed-tail &&\n+\tsed -n -e /h/p file >file-tail &&\n+\n+\t! diff -u fixed-tail file-tail\n+\n+'\n+\n+test_done\n-- \n1.5.4.2.g41ac4\n"},{"id":"67112","messageId":"1201949659-27725-14-git-send-email-gitster@pobox.com","threadId":"11836","inReplyTo":"1201949659-27725-13-git-send-email-gitster@pobox.com","subject":"[PATCH 13/13] core.whitespace: cr-at-eol","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-02T10:54:19Z","receivedAt":"2008-02-02T10:54:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This new error mode allows a line to have a carriage return at the\nend of the line when checking and fixing trailing whitespace errors.\n\nSome people like to keep CRLF line ending recorded in the repository,\nand still want to take advantage of the automated trailing whitespace\nstripping.  We still show ^M in the diff output piped to \"less\" to\nremind them that they do have the CR at the end, but these carriage\nreturn characters at the end are no longer flagged as errors.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config.txt |    4 ++++\n builtin-apply.c          |   18 ++++++++++++++----\n cache.h                  |    1 +\n t/t4019-diff-wserror.sh  |   40 ++++++++++++++++++++++++++++++++++++++++\n ws.c                     |   15 +++++++++++++--\n 5 files changed, 72 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 4e222f1..44cb640 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -308,6 +308,10 @@ core.whitespace::\n   error (enabled by default).\n * `indent-with-non-tab` treats a line that is indented with 8 or more\n   space characters as an error (not enabled by default).\n+* `cr-at-eol` treats a carriage-return at the end of line as\n+  part of the line terminator, i.e. with it, `trailing-space`\n+  does not trigger if the character before such a carriage-return\n+  is not a whitespace (not enabled by default).\n \n alias.*::\n \tCommand aliases for the linkgit:git[1] command wrapper - e.g.\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex fccf4a4..2b8ba81 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -1525,6 +1525,7 @@ static int copy_wsfix(char *output, const char *patch, int plen,\n \t */\n \tint i;\n \tint add_nl_to_tail = 0;\n+\tint add_cr_to_tail = 0;\n \tint fixed = 0;\n \tint last_tab_in_indent = -1;\n \tint last_space_in_indent = -1;\n@@ -1536,12 +1537,19 @@ static int copy_wsfix(char *output, const char *patch, int plen,\n \t */\n \tif ((ws_rule & WS_TRAILING_SPACE) &&\n \t    (2 < plen && isspace(patch[plen-2]))) {\n-\t\tif (patch[plen-1] == '\\n')\n+\t\tif (patch[plen - 1] == '\\n') {\n \t\t\tadd_nl_to_tail = 1;\n-\t\tplen--;\n-\t\twhile (0 < plen && isspace(patch[plen-1]))\n \t\t\tplen--;\n-\t\tfixed = 1;\n+\t\t\tif (1 < plen && patch[plen - 1] == '\\r') {\n+\t\t\t\tadd_cr_to_tail = !!(ws_rule & WS_CR_AT_EOL);\n+\t\t\t\tplen--;\n+\t\t\t}\n+\t\t}\n+\t\tif (0 < plen && isspace(patch[plen - 1])) {\n+\t\t\twhile (0 < plen && isspace(patch[plen-1]))\n+\t\t\t\tplen--;\n+\t\t\tfixed = 1;\n+\t\t}\n \t}\n \n \t/*\n@@ -1602,6 +1610,8 @@ static int copy_wsfix(char *output, const char *patch, int plen,\n \t}\n \n \tmemcpy(output, patch, plen);\n+\tif (add_cr_to_tail)\n+\t\toutput[plen++] = '\\r';\n \tif (add_nl_to_tail)\n \t\toutput[plen++] = '\\n';\n \tif (fixed && count_error)\ndiff --git a/cache.h b/cache.h\nindex 549f4bb..ad11c90 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -652,6 +652,7 @@ void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, i\n #define WS_TRAILING_SPACE\t01\n #define WS_SPACE_BEFORE_TAB\t02\n #define WS_INDENT_WITH_NON_TAB\t04\n+#define WS_CR_AT_EOL           010\n #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)\n extern unsigned whitespace_rule_cfg;\n extern unsigned whitespace_rule(const char *);\ndiff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh\nindex 67e080b..0d9cbb6 100755\n--- a/t/t4019-diff-wserror.sh\n+++ b/t/t4019-diff-wserror.sh\n@@ -12,6 +12,7 @@ test_expect_success setup '\n \techo \"         Eight SP indent\" >>F &&\n \techo \" \tHT and SP indent\" >>F &&\n \techo \"With trailing SP \" >>F &&\n+\techo \"Carriage ReturnQ\" | tr Q \"\\015\" >>F &&\n \techo \"No problem\" >>F\n \n '\n@@ -27,6 +28,7 @@ test_expect_success default '\n \tgrep Eight normal >/dev/null &&\n \tgrep HT error >/dev/null &&\n \tgrep With error >/dev/null &&\n+\tgrep Return error >/dev/null &&\n \tgrep No normal >/dev/null\n \n '\n@@ -41,6 +43,7 @@ test_expect_success 'without -trail' '\n \tgrep Eight normal >/dev/null &&\n \tgrep HT error >/dev/null &&\n \tgrep With normal >/dev/null &&\n+\tgrep Return normal >/dev/null &&\n \tgrep No normal >/dev/null\n \n '\n@@ -56,6 +59,7 @@ test_expect_success 'without -trail (attribute)' '\n \tgrep Eight normal >/dev/null &&\n \tgrep HT error >/dev/null &&\n \tgrep With normal >/dev/null &&\n+\tgrep Return normal >/dev/null &&\n \tgrep No normal >/dev/null\n \n '\n@@ -71,6 +75,7 @@ test_expect_success 'without -space' '\n \tgrep Eight normal >/dev/null &&\n \tgrep HT normal >/dev/null &&\n \tgrep With error >/dev/null &&\n+\tgrep Return error >/dev/null &&\n \tgrep No normal >/dev/null\n \n '\n@@ -86,6 +91,7 @@ test_expect_success 'without -space (attribute)' '\n \tgrep Eight normal >/dev/null &&\n \tgrep HT normal >/dev/null &&\n \tgrep With error >/dev/null &&\n+\tgrep Return error >/dev/null &&\n \tgrep No normal >/dev/null\n \n '\n@@ -101,6 +107,7 @@ test_expect_success 'with indent-non-tab only' '\n \tgrep Eight error >/dev/null &&\n \tgrep HT normal >/dev/null &&\n \tgrep With normal >/dev/null &&\n+\tgrep Return normal >/dev/null &&\n \tgrep No normal >/dev/null\n \n '\n@@ -116,6 +123,39 @@ test_expect_success 'with indent-non-tab only (attribute)' '\n \tgrep Eight error >/dev/null &&\n \tgrep HT normal >/dev/null &&\n \tgrep With normal >/dev/null &&\n+\tgrep Return normal >/dev/null &&\n+\tgrep No normal >/dev/null\n+\n+'\n+\n+test_expect_success 'with cr-at-eol' '\n+\n+\trm -f .gitattributes\n+\tgit config core.whitespace cr-at-eol\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 error >/dev/null &&\n+\tgrep Return normal >/dev/null &&\n+\tgrep No normal >/dev/null\n+\n+'\n+\n+test_expect_success 'with cr-at-eol (attribute)' '\n+\n+\tgit config --unset core.whitespace\n+\techo \"F whitespace=trailing,cr-at-eol\" >.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 error >/dev/null &&\n+\tgrep Return normal >/dev/null &&\n \tgrep No normal >/dev/null\n \n '\ndiff --git a/ws.c b/ws.c\nindex d09b9df..5a9ac45 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -14,6 +14,7 @@ static struct whitespace_rule {\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+\t{ \"cr-at-eol\", WS_CR_AT_EOL },\n };\n \n unsigned parse_whitespace_rule(const char *string)\n@@ -124,6 +125,7 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\n \tint written = 0;\n \tint trailing_whitespace = -1;\n \tint trailing_newline = 0;\n+\tint trailing_carriage_return = 0;\n \tint i;\n \n \t/* Logic is simpler if we temporarily ignore the trailing newline. */\n@@ -131,6 +133,11 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\n \t\ttrailing_newline = 1;\n \t\tlen--;\n \t}\n+\tif ((ws_rule & WS_CR_AT_EOL) &&\n+\t    len > 0 && line[len - 1] == '\\r') {\n+\t\ttrailing_carriage_return = 1;\n+\t\tlen--;\n+\t}\n \n \t/* Check for trailing whitespace. */\n \tif (ws_rule & WS_TRAILING_SPACE) {\n@@ -176,8 +183,10 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\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\t/*\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\t */\n \t\tif (trailing_whitespace == -1)\n \t\t\ttrailing_whitespace = len;\n \n@@ -196,6 +205,8 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,\n \t\t\t    len - trailing_whitespace, 1, stream);\n \t\t\tfputs(reset, stream);\n \t\t}\n+\t\tif (trailing_carriage_return)\n+\t\t\tfputc('\\r', stream);\n \t\tif (trailing_newline)\n \t\t\tfputc('\\n', stream);\n \t}\n-- \n1.5.4.2.g41ac4\n"}]}