{"thread":{"id":"24491","subject":"[PATCH 0/3] apply: handle traditional patches with spaces in filename","startedAt":"2010-07-24T01:06:18Z","lastAt":"2010-07-24T08:48:48Z","messageCount":6,"participants":["Jonathan Nieder","Andreas Schwab"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"146128","messageId":"20100724010618.GA13670@burratino","threadId":"24491","inReplyTo":null,"subject":"[PATCH 0/3] apply: handle traditional patches with spaces in filename","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-07-24T01:06:18Z","receivedAt":"2010-07-24T01:06:18Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"The goal: let ‘git apply’ handle such filenames as\n“b/debian/licenses/LICENSE.Apache (v2.0)” in patches produced by\nnon-git tools without erroring out.\n\nWhen we last left our heroes[1] in April:\n\n> | The name and last modification time of each file shall be output in\n> | the following format:\n> |\n> | \"---[space]%s  %s%s%s\", file1, <file1 timestamp>, <file1 frac>, <file1 zone>\n> | \"+++[space]%s  %s%s%s\", file2, <file2 timestamp>, <file2 frac>, <file2 zone>\n[...]\n> If this is really describing the format of patches in the wild, that\n> means we should only look for a tab character to terminate the filename.\n[...]\n> A big downside: this does not cope with copy-and-pasted patches with\n> tabs transformed to spaces.  The example [2] consists mostly of\n> file-creation patches, so we can’t look to the repository for hints.\n> Maybe the space-plus-date-plus-newline sequence should be used as a\n> delimiter.\n\nIt turns out that is not so hard.  Maybe it could be rewritten using\nregcomp() and regexec(); if someone wants to do that, I won’t stop\nthem. ;-)\n\nPatch 1 factors out a function to handle \"GNU-format\" C-style quoted\nfilenames in patches.  The only tool I know of that produces this\nformat is git; the discussion in [2] about what characters to escape\nseems to have come to no conclusion.\n\nPatch 2 adds some tests for all those weird characters that might\nappear in a filename.  They abuse “diff” and “pr”; testing on weird\nplatforms would be helpful.\n\nPatch 3 adds the logic to search for a date at the end of a filename\nline, for traditional (non --git) patches only.  If no date is found\nat the end, we return to the previous heuristic, except that the only\naccepted filename terminator is a tab.  Whitespace damage is only\naccepted if there is a timestamp at the end of the line.\n\nThoughts, suggestions, improvements welcome.\n\nJonathan Nieder (3):\n  apply: Split quoted filename handling into new function\n  tests: Test how well “git apply” copes with weird filenames\n  apply: Handle traditional patches with space in filename\n\n builtin/apply.c                  |  251 ++++++++++++++++++++++++++++++++------\n t/t4120-apply-popt.sh            |   35 +++++-\n t/t4135-apply-weird-filenames.sh |  119 ++++++++++++++++++\n 3 files changed, 363 insertions(+), 42 deletions(-)\n create mode 100755 t/t4135-apply-weird-filenames.sh\n\n[1] http://thread.gmane.org/gmane.linux.debian.devel.bugs.general/697969/focus=145543\n[2] http://thread.gmane.org/gmane.comp.version-control.git/9813/focus=10046\n"},{"id":"146129","messageId":"20100724010925.GB13670@burratino","threadId":"24491","inReplyTo":"20100724010618.GA13670@burratino","subject":"[PATCH 1/3] apply: Split quoted filename handling into new function","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-07-24T01:09:25Z","receivedAt":"2010-07-24T01:09:25Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"The new find_name_gnu() function handles new-style ‘--- \"a/foo\"’\npatch header lines, leaving find_name() itself a bit less\ndaunting.\n\nFunctional change: do not clobber the p-value when there are not\nenough path components in a quoted file name to honor it.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n builtin/apply.c       |   70 +++++++++++++++++++++++++++---------------------\n t/t4120-apply-popt.sh |   35 ++++++++++++++++++++++--\n 2 files changed, 71 insertions(+), 34 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex 12ef9ea..efc109e 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -416,44 +416,52 @@ static char *squash_slash(char *name)\n \treturn name;\n }\n \n+static char *find_name_gnu(const char *line, char *def, int p_value)\n+{\n+\tstruct strbuf name = STRBUF_INIT;\n+\tchar *cp;\n+\n+\t/*\n+\t * Proposed \"new-style\" GNU patch/diff format; see\n+\t * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2\n+\t */\n+\tif (unquote_c_style(&name, line, NULL)) {\n+\t\tstrbuf_release(&name);\n+\t\treturn NULL;\n+\t}\n+\n+\tfor (cp = name.buf; p_value; p_value--) {\n+\t\tcp = strchr(cp, '/');\n+\t\tif (!cp) {\n+\t\t\tstrbuf_release(&name);\n+\t\t\treturn NULL;\n+\t\t}\n+\t\tcp++;\n+\t}\n+\n+\t/* name can later be freed, so we need\n+\t * to memmove, not just return cp\n+\t */\n+\tstrbuf_remove(&name, 0, cp - name.buf);\n+\tfree(def);\n+\tif (root)\n+\t\tstrbuf_insert(&name, 0, root, root_len);\n+\treturn squash_slash(strbuf_detach(&name, NULL));\n+}\n+\n static char *find_name(const char *line, char *def, int p_value, int terminate)\n {\n \tint len;\n \tconst char *start = NULL;\n \n+\tif (*line == '\"') {\n+\t\tchar *name = find_name_gnu(line, def, p_value);\n+\t\tif (name)\n+\t\t\treturn name;\n+\t}\n+\n \tif (p_value == 0)\n \t\tstart = line;\n-\n-\tif (*line == '\"') {\n-\t\tstruct strbuf name = STRBUF_INIT;\n-\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\tif (!unquote_c_style(&name, line, NULL)) {\n-\t\t\tchar *cp;\n-\n-\t\t\tfor (cp = name.buf; p_value; p_value--) {\n-\t\t\t\tcp = strchr(cp, '/');\n-\t\t\t\tif (!cp)\n-\t\t\t\t\tbreak;\n-\t\t\t\tcp++;\n-\t\t\t}\n-\t\t\tif (cp) {\n-\t\t\t\t/* name can later be freed, so we need\n-\t\t\t\t * to memmove, not just return cp\n-\t\t\t\t */\n-\t\t\t\tstrbuf_remove(&name, 0, cp - name.buf);\n-\t\t\t\tfree(def);\n-\t\t\t\tif (root)\n-\t\t\t\t\tstrbuf_insert(&name, 0, root, root_len);\n-\t\t\t\treturn squash_slash(strbuf_detach(&name, NULL));\n-\t\t\t}\n-\t\t}\n-\t\tstrbuf_release(&name);\n-\t}\n-\n \tfor (;;) {\n \t\tchar c = *line;\n \ndiff --git a/t/t4120-apply-popt.sh b/t/t4120-apply-popt.sh\nindex b463b4f..2b2d00b 100755\n--- a/t/t4120-apply-popt.sh\n+++ b/t/t4120-apply-popt.sh\n@@ -10,21 +10,50 @@ test_description='git apply -p handling.'\n test_expect_success setup '\n \tmkdir sub &&\n \techo A >sub/file1 &&\n-\tcp sub/file1 file1 &&\n+\tcp sub/file1 file1.saved &&\n \tgit add sub/file1 &&\n \techo B >sub/file1 &&\n \tgit diff >patch.file &&\n-\trm sub/file1 &&\n-\trmdir sub\n+\tgit checkout -- sub/file1 &&\n+\tgit mv sub süb &&\n+\techo B >süb/file1 &&\n+\tgit diff >patch.escaped &&\n+\tgrep \"[\\]\" patch.escaped &&\n+\trm süb/file1 &&\n+\trmdir süb\n '\n \n test_expect_success 'apply git diff with -p2' '\n+\tcp file1.saved file1 &&\n \tgit apply -p2 patch.file\n '\n \n test_expect_success 'apply with too large -p' '\n+\tcp file1.saved file1 &&\n \ttest_must_fail git apply --stat -p3 patch.file 2>err &&\n \tgrep \"removing 3 leading\" err\n '\n \n+test_expect_success 'apply (-p2) traditional diff with funny filenames' '\n+\tcat >patch.quotes <<-\\EOF &&\n+\tdiff -u \"a/\"sub/file1 \"b/\"sub/file1\n+\t--- \"a/\"sub/file1\n+\t+++ \"b/\"sub/file1\n+\t@@ -1 +1 @@\n+\t-A\n+\t+B\n+\tEOF\n+\techo B >expected &&\n+\n+\tcp file1.saved file1 &&\n+\tgit apply -p2 patch.quotes &&\n+\ttest_cmp expected file1\n+'\n+\n+test_expect_success 'apply with too large -p and fancy filename' '\n+\tcp file1.saved file1 &&\n+\ttest_must_fail git apply --stat -p3 patch.escaped 2>err &&\n+\tgrep \"removing 3 leading\" err\n+'\n+\n test_done\n-- \n1.7.2.rc3\n"},{"id":"146130","messageId":"20100724011114.GC13670@burratino","threadId":"24491","inReplyTo":"20100724010618.GA13670@burratino","subject":"[PATCH 2/3] tests: Test how well \"git apply\" copes with weird filenames","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-07-24T01:11:14Z","receivedAt":"2010-07-24T01:11:14Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"The tests assume a reasonably well-behaved “diff -u” and “pr”\nto produce the (possibly whitespace-damaged) patches.  On platforms\nwithout those commands, skip the tests.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n t/t4135-apply-weird-filenames.sh |  119 ++++++++++++++++++++++++++++++++++++++\n 1 files changed, 119 insertions(+), 0 deletions(-)\n create mode 100755 t/t4135-apply-weird-filenames.sh\n\ndiff --git a/t/t4135-apply-weird-filenames.sh b/t/t4135-apply-weird-filenames.sh\nnew file mode 100755\nindex 0000000..2dcb040\n--- /dev/null\n+++ b/t/t4135-apply-weird-filenames.sh\n@@ -0,0 +1,119 @@\n+#!/bin/sh\n+\n+test_description='git apply with weird postimage filenames'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup: empty commit' '\n+\ttest_tick &&\n+\tgit commit --allow-empty -m preimage &&\n+\tgit tag preimage\n+'\n+\n+test_expect_success 'setup: clean-up functions' '\n+\treset_preimage() {\n+\t\tgit checkout -f preimage^0 &&\n+\t\tgit read-tree -u --reset HEAD &&\n+\t\tgit update-index --refresh\n+\t} &&\n+\n+\treset_subdirs() {\n+\t\trm -fr a b &&\n+\t\tmkdir a b\n+\t}\n+'\n+\n+test_expect_success 'setup: test prerequisites' '\n+\techo one >1 &&\n+\techo one >2 &&\n+\tif diff -u 1 2\n+\tthen\n+\t\ttest_set_prereq UNIDIFF\n+\tfi &&\n+\n+\tif diff -pruN 1 2\n+\tthen\n+\t\ttest_set_prereq FULLDIFF\n+\tfi &&\n+\n+\techo \"tab ->  .\" >expected &&\n+\techo \"tab ->\t.\" >with-tab &&\n+\n+\tpr -tT -e8 with-tab >actual &&\n+\tif test_cmp expected actual\n+\tthen\n+\t\ttest_set_prereq PR\n+\tfi\n+'\n+\n+try_filename() {\n+\tdesc=$1\n+\tpostimage=$2\n+\texp1=${3:-success}\n+\texp2=${4:-success}\n+\texp3=${5:-success}\n+\n+\ttest_expect_$exp1 \"$desc, git-style file creation patch\" \"\n+\t\treset_preimage &&\n+\t\techo postimage >'$postimage' &&\n+\t\tgit add -N '$postimage' &&\n+\t\tgit diff HEAD >'git-$desc.diff' &&\n+\n+\t\tgit rm -f --cached '$postimage' &&\n+\t\tmv '$postimage' postimage.saved &&\n+\t\tgit apply -v 'git-$desc.diff' &&\n+\n+\t\ttest_cmp postimage.saved '$postimage'\n+\t\"\n+\n+\ttest_expect_$exp2 UNIDIFF \"$desc, traditional patch\" \"\n+\t\treset_preimage &&\n+\t\techo preimage >'$postimage.orig' &&\n+\t\techo postimage >'$postimage' &&\n+\t\t! diff -u '$postimage.orig' '$postimage' >'diff-$desc.diff' &&\n+\n+\t\tmv '$postimage' postimage.saved &&\n+\t\tmv '$postimage.orig' '$postimage' &&\n+\t\tgit apply -v 'diff-$desc.diff' &&\n+\n+\t\ttest_cmp postimage.saved '$postimage'\n+\t\"\n+\n+\ttest_expect_$exp3 FULLDIFF \"$desc, traditional file creation patch\" \"\n+\t\treset_preimage &&\n+\t\treset_subdirs &&\n+\t\techo postimage >b/'$postimage' &&\n+\t\t! diff -pruN a b >'add-$desc.diff' &&\n+\n+\t\trm -f '$postimage' &&\n+\t\tmv b/'$postimage' postimage.saved &&\n+\t\tgit apply -v 'add-$desc.diff' &&\n+\n+\t\ttest_cmp postimage.saved '$postimage'\n+\t\"\n+}\n+\n+try_filename 'plain'            'postimage.txt'\n+try_filename 'with spaces'      'post image.txt' success failure failure\n+try_filename 'with tab'         'post\timage.txt' success failure failure\n+try_filename 'with backslash'   'post\\image.txt'\n+try_filename 'with quote'       '\"postimage\".txt' success failure success\n+\n+if test_have_prereq FULLDIFF && test_have_prereq PR\n+then\n+\ttest_set_prereq FULLDIFFPR\n+fi\n+test_expect_success FULLDIFFPR 'whitespace-damaged traditional patch' '\n+\treset_preimage &&\n+\treset_subdirs &&\n+\techo postimage >b/postimage.txt &&\n+\t! diff -pruN a b >diff-plain.txt &&\n+\tpr -tT -e8 diff-plain.txt >damaged.diff &&\n+\n+\tmv postimage.txt postimage.saved &&\n+\tgit apply -v damaged.diff &&\n+\n+\ttest_cmp postimage.saved postimage.txt\n+'\n+\n+test_done\n-- \n1.7.2.rc3\n"},{"id":"146134","messageId":"20100724012058.GD13670@burratino","threadId":"24491","inReplyTo":"20100724010618.GA13670@burratino","subject":"[PATCH 3/3] apply: Handle traditional patches with space in filename","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-07-24T01:20:58Z","receivedAt":"2010-07-24T01:20:58Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"To discover filenames from the --- and +++ lines in a traditional\nunified diff, currently ‘git apply’ scans forward for a whitespace\ncharacter on each line and stops there.  It can’t use the whole line\nbecause ‘diff -u’ likes to include timestamps, like so:\n\n --- foo\t2000-07-12 16:56:50.020000414 -0500\n +++ bar\t2010-07-12 16:56:50.020000414 -0500\n\nThe whitespace-seeking heuristic works great, even when the tab\nhas been converted to spaces by some email + copy-and-paste\nrelated corruption.\n\nExcept for one problem: if the filename itself contains whitespace,\nthe inferred filename will be too short.\n\nWhen Giuseppe ran into this problem, it was for a file creation\npatch (filename ‘debian/licenses/LICENSE.global BSD-style Chromium’).\nSo one can’t use the list of files present in the index to deduce an\nappropriate filename (not to mention that way lies madness; see\nv0.99~402, 2005-05-31).\n\nInstead, look for a timestamp and use that if present to mark the end\nof the filename.  If no timestamp is present, the old heuristic is\nused, with one exception: the space character \\040 is not considered\nterminating whitespace any more unless it is followed by a timestamp.\n\nReported-by: Giuseppe Iuculano <iuculano@debian.org>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\nAcked-by: Guido Günther <agx@sigxcpu.org>\n---\nGuido, I have carried over your ack from <http://bugs.debian.org/578752>.\nI hope that is okay.  And to both Guido and Giuseppe, sorry to have\ntaken so long on this.\n\nThat is the end of the series.  I hope it was not too unpleasant a read.\nAs always, thoughts, improvements, bugs welcome.\n\nRegards,\nJonathan\n\n builtin/apply.c                  |  193 +++++++++++++++++++++++++++++++++++---\n t/t4135-apply-weird-filenames.sh |    4 +-\n 2 files changed, 181 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex efc109e..b975c99 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -449,23 +449,157 @@ static char *find_name_gnu(const char *line, char *def, int p_value)\n \treturn squash_slash(strbuf_detach(&name, NULL));\n }\n \n-static char *find_name(const char *line, char *def, int p_value, int terminate)\n+static size_t tz_len(const char *line, size_t len)\n+{\n+\tconst char *tz, *p;\n+\n+\tif (len < strlen(\" +0500\") || line[len-strlen(\" +0500\")] != ' ')\n+\t\treturn 0;\n+\ttz = line + len - strlen(\" +0500\");\n+\n+\tif (tz[1] != '+' && tz[1] != '-')\n+\t\treturn 0;\n+\n+\tfor (p = tz + 2; p != line + len; p++)\n+\t\tif (!isdigit(*p))\n+\t\t\treturn 0;\n+\n+\treturn line + len - tz;\n+}\n+\n+static size_t date_len(const char *line, size_t len)\n+{\n+\tconst char *date, *p;\n+\n+\tif (len < strlen(\"72-02-05\") || line[len-strlen(\"-05\")] != '-')\n+\t\treturn 0;\n+\tp = date = line + len - strlen(\"72-02-05\");\n+\n+\tif (!isdigit(*p++) || !isdigit(*p++) || *p++ != '-' ||\n+\t    !isdigit(*p++) || !isdigit(*p++) || *p++ != '-' ||\n+\t    !isdigit(*p++) || !isdigit(*p++))\t/* Not a date. */\n+\t\treturn 0;\n+\n+\tif (date - line >= strlen(\"19\") &&\n+\t    isdigit(date[-1]) && isdigit(date[-2]))\t/* 4-digit year */\n+\t\tdate -= strlen(\"19\");\n+\n+\treturn line + len - date;\n+}\n+\n+static size_t short_time_len(const char *line, size_t len)\n+{\n+\tconst char *time, *p;\n+\n+\tif (len < strlen(\" 07:01:32\") || line[len-strlen(\":32\")] != ':')\n+\t\treturn 0;\n+\tp = time = line + len - strlen(\" 07:01:32\");\n+\n+\t/* Permit 1-digit hours? */\n+\tif (*p++ != ' ' ||\n+\t    !isdigit(*p++) || !isdigit(*p++) || *p++ != ':' ||\n+\t    !isdigit(*p++) || !isdigit(*p++) || *p++ != ':' ||\n+\t    !isdigit(*p++) || !isdigit(*p++))\t/* Not a time. */\n+\t\treturn 0;\n+\n+\treturn line + len - time;\n+}\n+\n+static size_t fractional_time_len(const char *line, size_t len)\n+{\n+\tconst char *p;\n+\tsize_t n;\n+\n+\t/* Expected format: 19:41:17.620000023 */\n+\tif (!len || !isdigit(line[len - 1]))\n+\t\treturn 0;\n+\tp = line + len - 1;\n+\n+\t/* Fractional seconds. */\n+\twhile (p > line && isdigit(*p))\n+\t\tp--;\n+\tif (*p != '.')\n+\t\treturn 0;\n+\n+\t/* Hours, minutes, and whole seconds. */\n+\tn = short_time_len(line, p - line);\n+\tif (!n)\n+\t\treturn 0;\n+\n+\treturn line + len - p + n;\n+}\n+\n+static size_t trailing_spaces_len(const char *line, size_t len)\n+{\n+\tconst char *p;\n+\n+\t/* Expected format: ' ' x (1 or more)  */\n+\tif (!len || line[len - 1] != ' ')\n+\t\treturn 0;\n+\n+\tp = line + len;\n+\twhile (p != line) {\n+\t\tp--;\n+\t\tif (*p != ' ')\n+\t\t\treturn line + len - (p + 1);\n+\t}\n+\n+\t/* All spaces! */\n+\treturn len;\n+}\n+\n+static size_t diff_timestamp_len(const char *line, size_t len)\n+{\n+\tconst char *end = line + len;\n+\tsize_t n;\n+\n+\t/*\n+\t * Posix: 2010-07-05 19:41:17\n+\t * GNU: 2010-07-05 19:41:17.620000023 -0500\n+\t */\n+\n+\tif (!isdigit(end[-1]))\n+\t\treturn 0;\n+\n+\tn = tz_len(line, end - line);\n+\tend -= n;\n+\n+\tn = short_time_len(line, end - line);\n+\tif (!n)\n+\t\tn = fractional_time_len(line, end - line);\n+\tend -= n;\n+\n+\tn = date_len(line, end - line);\n+\tif (!n)\t/* No date.  Too bad. */\n+\t\treturn 0;\n+\tend -= n;\n+\n+\tif (end == line)\t/* No space before date. */\n+\t\treturn 0;\n+\tif (end[-1] == '\\t') {\t/* Success! */\n+\t\tend--;\n+\t\treturn line + len - end;\n+\t}\n+\tif (end[-1] != ' ')\t/* No space before date. */\n+\t\treturn 0;\n+\n+\t/* Whitespace damage. */\n+\tend -= trailing_spaces_len(line, end - line);\n+\treturn line + len - end;\n+}\n+\n+static char *find_name_common(const char *line, char *def, int p_value,\n+\t\t\t\tconst char *end, int terminate)\n {\n \tint len;\n \tconst char *start = NULL;\n \n-\tif (*line == '\"') {\n-\t\tchar *name = find_name_gnu(line, def, p_value);\n-\t\tif (name)\n-\t\t\treturn name;\n-\t}\n-\n \tif (p_value == 0)\n \t\tstart = line;\n-\tfor (;;) {\n+\twhile (line != end) {\n \t\tchar c = *line;\n \n-\t\tif (isspace(c)) {\n+\t\tif (!end && isspace(c)) {\n \t\t\tif (c == '\\n')\n \t\t\t\tbreak;\n \t\t\tif (name_terminate(start, line-start, c, terminate))\n@@ -505,6 +639,37 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)\n \treturn squash_slash(xmemdupz(start, len));\n }\n \n+static char *find_name(const char *line, char *def, int p_value, int terminate)\n+{\n+\tif (*line == '\"') {\n+\t\tchar *name = find_name_gnu(line, def, p_value);\n+\t\tif (name)\n+\t\t\treturn name;\n+\t}\n+\n+\treturn find_name_common(line, def, p_value, NULL, terminate);\n+}\n+\n+static char *find_name_traditional(const char *line, char *def, int p_value)\n+{\n+\tsize_t len = strlen(line);\n+\tsize_t date_len;\n+\n+\tif (*line == '\"') {\n+\t\tchar *name = find_name_gnu(line, def, p_value);\n+\t\tif (name)\n+\t\t\treturn name;\n+\t}\n+\n+\tlen = strchrnul(line, '\\n') - line;\n+\tdate_len = diff_timestamp_len(line, len);\n+\tif (!date_len)\n+\t\treturn find_name(line, def, p_value, TERM_TAB);\n+\tlen -= date_len;\n+\n+\treturn find_name_common(line, def, p_value, line + len, 0);\n+}\n+\n static int count_slashes(const char *cp)\n {\n \tint cnt = 0;\n@@ -527,7 +692,7 @@ static int guess_p_value(const char *nameline)\n \n \tif (is_dev_null(nameline))\n \t\treturn -1;\n-\tname = find_name(nameline, NULL, 0, TERM_SPACE | TERM_TAB);\n+\tname = find_name_traditional(nameline, NULL, 0);\n \tif (!name)\n \t\treturn -1;\n \tcp = strchr(name, '/');\n@@ -646,16 +811,16 @@ static void parse_traditional_patch(const char *first, const char *second, struc\n \tif (is_dev_null(first)) {\n \t\tpatch->is_new = 1;\n \t\tpatch->is_delete = 0;\n-\t\tname = find_name(second, NULL, p_value, TERM_SPACE | TERM_TAB);\n+\t\tname = find_name_traditional(second, NULL, p_value);\n \t\tpatch->new_name = name;\n \t} else if (is_dev_null(second)) {\n \t\tpatch->is_new = 0;\n \t\tpatch->is_delete = 1;\n-\t\tname = find_name(first, NULL, p_value, TERM_SPACE | TERM_TAB);\n+\t\tname = find_name_traditional(first, NULL, p_value);\n \t\tpatch->old_name = name;\n \t} else {\n-\t\tname = find_name(first, NULL, p_value, TERM_SPACE | TERM_TAB);\n-\t\tname = find_name(second, name, p_value, TERM_SPACE | TERM_TAB);\n+\t\tname = find_name_traditional(first, NULL, p_value);\n+\t\tname = find_name_traditional(second, name, p_value);\n \t\tif (has_epoch_timestamp(first)) {\n \t\t\tpatch->is_new = 1;\n \t\t\tpatch->is_delete = 0;\ndiff --git a/t/t4135-apply-weird-filenames.sh b/t/t4135-apply-weird-filenames.sh\nindex 2dcb040..52f9f5b 100755\n--- a/t/t4135-apply-weird-filenames.sh\n+++ b/t/t4135-apply-weird-filenames.sh\n@@ -94,8 +94,8 @@ try_filename() {\n }\n \n try_filename 'plain'            'postimage.txt'\n-try_filename 'with spaces'      'post image.txt' success failure failure\n-try_filename 'with tab'         'post\timage.txt' success failure failure\n+try_filename 'with spaces'      'post image.txt'\n+try_filename 'with tab'         'post\timage.txt'\n try_filename 'with backslash'   'post\\image.txt'\n try_filename 'with quote'       '\"postimage\".txt' success failure success\n \n-- \n1.7.2.rc3\n"},{"id":"146141","messageId":"m2r5iti9w0.fsf@igel.home","threadId":"24491","inReplyTo":"20100724011114.GC13670@burratino","subject":"Re: [PATCH 2/3] tests: Test how well \"git apply\" copes with weird filenames","fromName":"Andreas Schwab","fromEmail":"schwab@linux-m68k.org","sentAt":"2010-07-24T08:03:59Z","receivedAt":"2010-07-24T08:03:59Z","isPatch":true,"sender":{"key":"schwab@linux-m68k.org","avatar":"https://avatars.githubusercontent.com/u/2175493?v=4"},"body":"Jonathan Nieder <jrnieder@gmail.com> writes:\n\n> The tests assume a reasonably well-behaved “diff -u” and “pr”\n> to produce the (possibly whitespace-damaged) patches.  On platforms\n> without those commands, skip the tests.\n\npr -T is not portable.  What's wrong with expand?\n\nAndreas.\n\n-- \nAndreas Schwab, schwab@linux-m68k.org\nGPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5\n\"And now for something completely different.\"\n"},{"id":"146146","messageId":"20100724084847.GB7150@burratino","threadId":"24491","inReplyTo":"m2r5iti9w0.fsf@igel.home","subject":"Re: [PATCH 2/3] tests: Test how well \"git apply\" copes with weird filenames","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-07-24T08:48:48Z","receivedAt":"2010-07-24T08:48:48Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Andreas Schwab wrote:\n\n> pr -T is not portable.  What's wrong with expand?\n\nNothing at all.  Here’s a patch for squashing in.\n\n-- 8< --\nSubject: t4135 (apply): use expand instead of pr for portability\n\nexpand is just the thing for expanding tabs into spaces, and\nunlike pr -T, it is portable.  Use it.\n\nNoticed-by: Andreas Schwab <schwab@linux-m68k.org>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nThank you. :)\n\n t/t4135-apply-weird-filenames.sh |   17 ++---------------\n 1 files changed, 2 insertions(+), 15 deletions(-)\n\ndiff --git i/t/t4135-apply-weird-filenames.sh w/t/t4135-apply-weird-filenames.sh\nindex 2dcb040..dda554e 100755\n--- i/t/t4135-apply-weird-filenames.sh\n+++ w/t/t4135-apply-weird-filenames.sh\n@@ -34,15 +34,6 @@ test_expect_success 'setup: test prerequisites' '\n \tif diff -pruN 1 2\n \tthen\n \t\ttest_set_prereq FULLDIFF\n-\tfi &&\n-\n-\techo \"tab ->  .\" >expected &&\n-\techo \"tab ->\t.\" >with-tab &&\n-\n-\tpr -tT -e8 with-tab >actual &&\n-\tif test_cmp expected actual\n-\tthen\n-\t\ttest_set_prereq PR\n \tfi\n '\n \n@@ -99,16 +90,12 @@ try_filename 'with tab'         'post\timage.txt' success failure failure\n try_filename 'with backslash'   'post\\image.txt'\n try_filename 'with quote'       '\"postimage\".txt' success failure success\n \n-if test_have_prereq FULLDIFF && test_have_prereq PR\n-then\n-\ttest_set_prereq FULLDIFFPR\n-fi\n-test_expect_success FULLDIFFPR 'whitespace-damaged traditional patch' '\n+test_expect_success FULLDIFF 'whitespace-damaged traditional patch' '\n \treset_preimage &&\n \treset_subdirs &&\n \techo postimage >b/postimage.txt &&\n \t! diff -pruN a b >diff-plain.txt &&\n-\tpr -tT -e8 diff-plain.txt >damaged.diff &&\n+\texpand diff-plain.txt >damaged.diff &&\n \n \tmv postimage.txt postimage.saved &&\n \tgit apply -v damaged.diff &&\n-- \n"}]}