{"thread":{"id":"20368","subject":"[PATCHv4] git apply: option to ignore whitespace differences","startedAt":"2009-08-04T11:16:49Z","lastAt":"2009-09-01T09:53:54Z","messageCount":3,"participants":["Giuseppe Bilotta","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"119486","messageId":"1249384609-30240-1-git-send-email-giuseppe.bilotta@gmail.com","threadId":"20368","inReplyTo":null,"subject":"[PATCHv4] git apply: option to ignore whitespace differences","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2009-08-04T11:16:49Z","receivedAt":"2009-08-04T11:16:49Z","isPatch":false,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"Introduce --ignore-whitespace option and corresponding config bool to\nignore whitespace differences while applying patches, akin to the\n'patch' program.\n\n'git am', 'git rebase' and the bash git completion are made aware of\nthis option.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n\nFollowing Junio's suggestion, the whitespace in the test has been replaced as\nappropriate with Z to prevent mangling on patch application. Some test cases\nhave also been made more credible, and documentation has been added to all of\nthem.\n\n Documentation/config.txt               |    8 ++\n Documentation/git-am.txt               |    5 +-\n Documentation/git-apply.txt            |   13 +++\n Documentation/git-rebase.txt           |    3 +-\n builtin-apply.c                        |  172 ++++++++++++++++++++++++++++++--\n cache.h                                |    1 +\n contrib/completion/git-completion.bash |    3 +\n environment.c                          |    1 +\n git-am.sh                              |    4 +-\n git-rebase.sh                          |    3 +\n t/t4107-apply-ignore-whitespace.sh     |  176 ++++++++++++++++++++++++++++++++\n 11 files changed, 379 insertions(+), 10 deletions(-)\n create mode 100755 t/t4107-apply-ignore-whitespace.sh\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex c6f09f8..0b53cab 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -461,6 +461,14 @@ it will be treated as a shell command.  For example, defining\n executed from the top-level directory of a repository, which may\n not necessarily be the current directory.\n \n+apply.ignorewhitespace::\n+\tWhen set to 'change', tells 'git-apply' to ignore changes in\n+\twhitespace, in the same way as the '--ignore-space-change'\n+\toption.\n+\tWhen set to one of: no, none, never, false tells 'git-apply' to\n+\trespect all whitespace differences.\n+\tSee linkgit:git-apply[1].\n+\n apply.whitespace::\n \tTells 'git-apply' how to handle whitespaces, in the same way\n \tas the '--whitespace' option. See linkgit:git-apply[1].\ndiff --git a/Documentation/git-am.txt b/Documentation/git-am.txt\nindex 32e689b..fcacc94 100644\n--- a/Documentation/git-am.txt\n+++ b/Documentation/git-am.txt\n@@ -11,7 +11,7 @@ SYNOPSIS\n [verse]\n 'git am' [--signoff] [--keep] [--utf8 | --no-utf8]\n \t [--3way] [--interactive] [--committer-date-is-author-date]\n-\t [--ignore-date]\n+\t [--ignore-date] [--ignore-space-change | --ignore-whitespace]\n \t [--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]\n \t [--reject] [-q | --quiet]\n \t [<mbox> | <Maildir>...]\n@@ -65,6 +65,9 @@ default.   You can use `--no-utf8` to override this.\n \tit is supposed to apply to and we have those blobs\n \tavailable locally.\n \n+--ignore-date::\n+--ignore-space-change::\n+--ignore-whitespace::\n --whitespace=<option>::\n -C<n>::\n -p<n>::\ndiff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt\nindex 735374d..5ee8c91 100644\n--- a/Documentation/git-apply.txt\n+++ b/Documentation/git-apply.txt\n@@ -13,6 +13,7 @@ SYNOPSIS\n \t  [--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]\n \t  [--allow-binary-replacement | --binary] [--reject] [-z]\n \t  [-pNUM] [-CNUM] [--inaccurate-eof] [--recount] [--cached]\n+\t  [--ignore-space-change | --ignore-whitespace ]\n \t  [--whitespace=<nowarn|warn|fix|error|error-all>]\n \t  [--exclude=PATH] [--include=PATH] [--directory=<root>]\n \t  [--verbose] [<patch>...]\n@@ -149,6 +150,14 @@ patch to each path is used.  A patch to a path that does not match any\n include/exclude pattern is used by default if there is no include pattern\n on the command line, and ignored if there is any include pattern.\n \n+--ignore-space-change::\n+--ignore-whitespace::\n+\tWhen applying a patch, ignore changes in whitespace in context\n+\tlines if necessary.\n+\tContext lines will preserve their whitespace, and they will not\n+\tundergo whitespace fixing regardless of the value of the\n+\t`--whitespace` option. New lines will still be fixed, though.\n+\n --whitespace=<action>::\n \tWhen applying a patch, detect a new or modified line that has\n \twhitespace errors.  What are considered whitespace errors is\n@@ -205,6 +214,10 @@ running `git apply --directory=modules/git-gui`.\n Configuration\n -------------\n \n+apply.ignorewhitespace::\n+\tSet to 'change' if you want changes in whitespace to be ignored by default.\n+\tSet to one of: no, none, never, false if you want changes in\n+\twhitespace to be significant.\n apply.whitespace::\n \tWhen no `--whitespace` flag is given from the command\n \tline, this configuration item is used as the default.\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex db1b71d..0aefc34 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -268,8 +268,9 @@ OPTIONS\n \texit with the message \"Current branch is up to date\" in such a\n \tsituation.\n \n+--ignore-whitespace::\n --whitespace=<option>::\n-\tThis flag is passed to the 'git-apply' program\n+\tThese flag are passed to the 'git-apply' program\n \t(see linkgit:git-apply[1]) that applies the patch.\n \tIncompatible with the --interactive option.\n \ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex 39dc96a..7ec5b8b 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -61,6 +61,13 @@ static enum ws_error_action {\n static int whitespace_error;\n static int squelch_whitespace_errors = 5;\n static int applied_after_fixing_ws;\n+\n+static enum ws_ignore {\n+\tignore_ws_none,\n+\tignore_ws_change,\n+} ws_ignore_action = ignore_ws_none;\n+\n+\n static const char *patch_input_file;\n static const char *root;\n static int root_len;\n@@ -97,6 +104,21 @@ static void parse_whitespace_option(const char *option)\n \tdie(\"unrecognized whitespace option '%s'\", option);\n }\n \n+static void parse_ignorewhitespace_option(const char *option)\n+{\n+\tif (!option || !strcmp(option, \"no\") ||\n+\t    !strcmp(option, \"false\") || !strcmp(option, \"never\") ||\n+\t    !strcmp(option, \"none\")) {\n+\t\tws_ignore_action = ignore_ws_none;\n+\t\treturn;\n+\t}\n+\tif (!strcmp(option, \"change\")) {\n+\t\tws_ignore_action = ignore_ws_change;\n+\t\treturn;\n+\t}\n+\tdie(\"unrecognized whitespace ignore option '%s'\", option);\n+}\n+\n static void set_default_whitespace_mode(const char *whitespace_option)\n {\n \tif (!whitespace_option && !apply_default_whitespace)\n@@ -214,6 +236,62 @@ static uint32_t hash_line(const char *cp, size_t len)\n \treturn h;\n }\n \n+/*\n+ * Compare lines s1 of length n1 and s2 of length n2, ignoring\n+ * whitespace difference. Returns 1 if they match, 0 otherwise\n+ */\n+static int fuzzy_matchlines(const char *s1, size_t n1,\n+\t\t\t    const char *s2, size_t n2)\n+{\n+\tconst char *last1 = s1 + n1 - 1;\n+\tconst char *last2 = s2 + n2 - 1;\n+\tint result = 0;\n+\n+\tif (n1 < 0 || n2 < 0)\n+\t\treturn 0;\n+\n+\t/* ignore line endings */\n+\twhile ((*last1 == '\\r') || (*last1 == '\\n'))\n+\t\tlast1--;\n+\twhile ((*last2 == '\\r') || (*last2 == '\\n'))\n+\t\tlast2--;\n+\n+\t/* skip leading whitespace */\n+\twhile (isspace(*s1) && (s1 <= last1))\n+\t\ts1++;\n+\twhile (isspace(*s2) && (s2 <= last2))\n+\t\ts2++;\n+\t/* early return if both lines are empty */\n+\tif ((s1 > last1) && (s2 > last2))\n+\t\treturn 1;\n+\twhile (!result) {\n+\t\tresult = *s1++ - *s2++;\n+\t\t/*\n+\t\t * Skip whitespace inside. We check for whitespace on\n+\t\t * both buffers because we don't want \"a b\" to match\n+\t\t * \"ab\"\n+\t\t */\n+\t\tif (isspace(*s1) && isspace(*s2)) {\n+\t\t\twhile (isspace(*s1) && s1 <= last1)\n+\t\t\t\ts1++;\n+\t\t\twhile (isspace(*s2) && s2 <= last2)\n+\t\t\t\ts2++;\n+\t\t}\n+\t\t/*\n+\t\t * If we reached the end on one side only,\n+\t\t * lines don't match\n+\t\t */\n+\t\tif (\n+\t\t    ((s2 > last2) && (s1 <= last1)) ||\n+\t\t    ((s1 > last1) && (s2 <= last2)))\n+\t\t\treturn 0;\n+\t\tif ((s1 > last1) && (s2 > last2))\n+\t\t\tbreak;\n+\t}\n+\n+\treturn !result;\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@@ -1672,10 +1750,17 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)\n \t}\n }\n \n+/*\n+ * Update the preimage, and the common lines in postimage,\n+ * from buffer buf of length len. If postlen is 0 the postimage\n+ * is updated in place, otherwise it's updated on a new buffer\n+ * of length postlen\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+\t\t\t\t   size_t len, size_t postlen)\n {\n \tint i, ctx;\n \tchar *new, *old, *fixed;\n@@ -1694,11 +1779,19 @@ static void update_pre_post_images(struct image *preimage,\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 * Adjust the common context lines in postimage. This can be\n+\t * done in-place when we are just doing whitespace fixing,\n+\t * which does not make the string grow, but needs a new buffer\n+\t * when ignoring whitespace causes the update, since in this case\n+\t * we could have e.g. tabs converted to multiple spaces.\n+\t * We trust the caller to tell us if the update can be done\n+\t * in place (postlen==0) or not.\n \t */\n-\tnew = old = postimage->buf;\n+\told = postimage->buf;\n+\tif (postlen)\n+\t\tnew = postimage->buf = xmalloc(postlen);\n+\telse\n+\t\tnew = old;\n \tfixed = preimage->buf;\n \tfor (i = ctx = 0; i < postimage->nr; i++) {\n \t\tsize_t len = postimage->line[i].len;\n@@ -1773,12 +1866,57 @@ static int match_fragment(struct image *img,\n \t    !memcmp(img->buf + try, preimage->buf, preimage->len))\n \t\treturn 1;\n \n+\t/*\n+\t * No exact match. If we are ignoring whitespace, run a line-by-line\n+\t * fuzzy matching. We collect all the line length information because\n+\t * we need it to adjust whitespace if we match.\n+\t */\n+\tif (ws_ignore_action == ignore_ws_change) {\n+\t\tsize_t imgoff = 0;\n+\t\tsize_t preoff = 0;\n+\t\tsize_t postlen = postimage->len;\n+\t\tsize_t imglen[preimage->nr];\n+\t\tfor (i = 0; i < preimage->nr; i++) {\n+\t\t\timglen[i] = img->line[try_lno+i].len;\n+\t\t\tsize_t prelen = preimage->line[i].len;\n+\t\t\tif (!fuzzy_matchlines(\n+\t\t\t\timg->buf + try + imgoff, imglen[i],\n+\t\t\t\tpreimage->buf + preoff, prelen))\n+\t\t\t\treturn 0;\n+\t\t\tif (preimage->line[i].flag & LINE_COMMON)\n+\t\t\t\tpostlen += imglen[i] - prelen;\n+\t\t\timgoff += imglen[i];\n+\t\t\tpreoff += prelen;\n+\t\t}\n+\n+\t\t/*\n+\t\t * Ok, the preimage matches with whitespace fuzz. Update it and\n+\t\t * the common postimage lines to use the same whitespace as the\n+\t\t * target. imgoff now holds the true length of the target that\n+\t\t * matches the preimage, and we need to update the line lengths\n+\t\t * of the preimage to match the target ones.\n+\t\t */\n+\t\tfixed_buf = xmalloc(imgoff);\n+\t\tmemcpy(fixed_buf, img->buf + try, imgoff);\n+\t\tfor (i = 0; i < preimage->nr; i++)\n+\t\t\tpreimage->line[i].len = imglen[i];\n+\n+\t\t/*\n+\t\t * Update the preimage buffer and the postimage context lines.\n+\t\t */\n+\t\tupdate_pre_post_images(preimage, postimage,\n+\t\t\t\tfixed_buf, imgoff, postlen);\n+\t\treturn 1;\n+\t}\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 * it might with whitespace fuzz. We haven't been asked to\n+\t * ignore whitespace, we were asked to correct whitespace\n+\t * errors, so let's try matching after whitespace correction.\n \t */\n \tfixed_buf = xmalloc(preimage->len + 1);\n \tbuf = fixed_buf;\n@@ -1830,7 +1968,7 @@ static int match_fragment(struct image *img,\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+\t\t\t       fixed_buf, buf - fixed_buf, 0);\n \treturn 1;\n \n  unmatch_exit:\n@@ -3272,6 +3410,8 @@ static int git_apply_config(const char *var, const char *value, void *cb)\n {\n \tif (!strcmp(var, \"apply.whitespace\"))\n \t\treturn git_config_string(&apply_default_whitespace, var, value);\n+\telse if (!strcmp(var, \"apply.ignorewhitespace\"))\n+\t\treturn git_config_string(&apply_default_ignorewhitespace, var, value);\n \treturn git_default_config(var, value, cb);\n }\n \n@@ -3308,6 +3448,16 @@ static int option_parse_z(const struct option *opt,\n \treturn 0;\n }\n \n+static int option_parse_space_change(const struct option *opt,\n+\t\t\t  const char *arg, int unset)\n+{\n+\tif (unset)\n+\t\tws_ignore_action = ignore_ws_none;\n+\telse\n+\t\tws_ignore_action = ignore_ws_change;\n+\treturn 0;\n+}\n+\n static int option_parse_whitespace(const struct option *opt,\n \t\t\t\t   const char *arg, int unset)\n {\n@@ -3384,6 +3534,12 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)\n \t\t{ OPTION_CALLBACK, 0, \"whitespace\", &whitespace_option, \"action\",\n \t\t\t\"detect new or modified lines that have whitespace errors\",\n \t\t\t0, option_parse_whitespace },\n+\t\t{ OPTION_CALLBACK, 0, \"ignore-space-change\", NULL, NULL,\n+\t\t\t\"ignore changes in whitespace when finding context\",\n+\t\t\tPARSE_OPT_NOARG, option_parse_space_change },\n+\t\t{ OPTION_CALLBACK, 0, \"ignore-whitespace\", NULL, NULL,\n+\t\t\t\"ignore changes in whitespace when finding context\",\n+\t\t\tPARSE_OPT_NOARG, option_parse_space_change },\n \t\tOPT_BOOLEAN('R', \"reverse\", &apply_in_reverse,\n \t\t\t\"apply the patch in reverse\"),\n \t\tOPT_BOOLEAN(0, \"unidiff-zero\", &unidiff_zero,\n@@ -3408,6 +3564,8 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)\n \tgit_config(git_apply_config, NULL);\n \tif (apply_default_whitespace)\n \t\tparse_whitespace_option(apply_default_whitespace);\n+\tif (apply_default_ignorewhitespace)\n+\t\tparse_ignorewhitespace_option(apply_default_ignorewhitespace);\n \n \targc = parse_options(argc, argv, prefix, builtin_apply_options,\n \t\t\tapply_usage, 0);\ndiff --git a/cache.h b/cache.h\nindex 0bbcb1f..69efe1b 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -509,6 +509,7 @@ extern int log_all_ref_updates;\n extern int warn_ambiguous_refs;\n extern int shared_repository;\n extern const char *apply_default_whitespace;\n+extern const char *apply_default_ignorewhitespace;\n extern int zlib_compression_level;\n extern int core_compression_level;\n extern int core_compression_seen;\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 5543dc4..bf688e1 100755\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -674,6 +674,7 @@ _git_am ()\n \t--*)\n \t\t__gitcomp \"\n \t\t\t--3way --committer-date-is-author-date --ignore-date\n+\t\t\t--ignore-whitespace --ignore-space-change\n \t\t\t--interactive --keep --no-utf8 --signoff --utf8\n \t\t\t--whitespace=\n \t\t\t\"\n@@ -695,6 +696,7 @@ _git_apply ()\n \t\t\t--stat --numstat --summary --check --index\n \t\t\t--cached --index-info --reverse --reject --unidiff-zero\n \t\t\t--apply --no-add --exclude=\n+\t\t\t--ignore-whitespace --ignore-space-change\n \t\t\t--whitespace= --inaccurate-eof --verbose\n \t\t\t\"\n \t\treturn\n@@ -1537,6 +1539,7 @@ _git_config ()\n \t__gitcomp \"\n \t\tadd.ignore-errors\n \t\talias.\n+\t\tapply.ignorewhitespace\n \t\tapply.whitespace\n \t\tbranch.autosetupmerge\n \t\tbranch.autosetuprebase\ndiff --git a/environment.c b/environment.c\nindex 73bccf1..5de6837 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -26,6 +26,7 @@ const char *git_commit_encoding;\n const char *git_log_output_encoding;\n int shared_repository = PERM_UMASK;\n const char *apply_default_whitespace;\n+const char *apply_default_ignorewhitespace;\n int zlib_compression_level = Z_BEST_SPEED;\n int core_compression_level;\n int core_compression_seen;\ndiff --git a/git-am.sh b/git-am.sh\nindex d64d997..ab39b3c 100755\n--- a/git-am.sh\n+++ b/git-am.sh\n@@ -16,6 +16,8 @@ s,signoff       add a Signed-off-by line to the commit message\n u,utf8          recode into utf8 (default)\n k,keep          pass -k flag to git-mailinfo\n whitespace=     pass it through git-apply\n+ignore-space-change pass it through git-apply\n+ignore-whitespace pass it through git-apply\n directory=      pass it through git-apply\n C=              pass it through git-apply\n p=              pass it through git-apply\n@@ -303,7 +305,7 @@ do\n \t\tgit_apply_opt=\"$git_apply_opt $(sq \"$1$2\")\"; shift ;;\n \t--patch-format)\n \t\tshift ; patch_format=\"$1\" ;;\n-\t--reject)\n+\t--reject|--ignore-whitespace|--ignore-space-change)\n \t\tgit_apply_opt=\"$git_apply_opt $1\" ;;\n \t--committer-date-is-author-date)\n \t\tcommitter_date_is_author_date=t ;;\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex 18bc694..d741752 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -333,6 +333,9 @@ do\n \t\t\t;;\n \t\tesac\n \t\t;;\n+\t--ignore-whitespace)\n+\t\tgit_am_opt=\"$git_am_opt $1\"\n+\t\t;;\n \t--committer-date-is-author-date|--ignore-date)\n \t\tgit_am_opt=\"$git_am_opt $1\"\n \t\tforce_rebase=t\ndiff --git a/t/t4107-apply-ignore-whitespace.sh b/t/t4107-apply-ignore-whitespace.sh\nnew file mode 100755\nindex 0000000..821449c\n--- /dev/null\n+++ b/t/t4107-apply-ignore-whitespace.sh\n@@ -0,0 +1,176 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2009 Giuseppe Bilotta\n+# Copyright (c) 2005 Junio C Hamano\n+# Copyright (c) 2005 Robert Fitzsimons\n+#\n+\n+test_description='git-apply --ignore-whitespace.\n+\n+'\n+. ./test-lib.sh\n+\n+# setup\n+\n+cat > patch1.patch <<\\EOF\n+diff --git a/main.c b/main.c\n+new file mode 100644\n+--- /dev/null\n++++ b/main.c\n+@@ -0,0 +1,22 @@\n++#include <stdio.h>\n++\n++void print_int(int num);\n++int func(int num);\n++\n++int main() {\n++       int i;\n++\n++       for (i = 0; i < 10; i++) {\n++               print_int(func(i)); /* stuff */\n++       }\n++\n++       return 0;\n++}\n++\n++int func(int num) {\n++       return num * num;\n++}\n++\n++void print_int(int num) {\n++       printf(\"%d\", num);\n++}\n+EOF\n+\n+# Since whitespace is very significant and we want to prevent whitespace\n+# mangling when creating this test from a patch, we protect 'fixable'\n+# whitespace by replacing spaces with Z and replacing them at patch\n+# creation time, hence the sed trick.\n+\n+\n+# This patch will fail unless whitespace differences are being ignored\n+\n+sed -e 's/Z/ /g' > patch2.patch <<\\EOF\n+diff --git a/main.c b/main.c\n+--- a/main.c\n++++ b/main.c\n+@@ -10,6 +10,8 @@\n+Z\t\tprint_int(func(i)); /* stuff */\n+Z\t}\n+Z\n++\tprintf(\"\\n\");\n++\n+Z\treturn 0;\n+Z}\n+Z\n+EOF\n+\n+# This patch will fail even though whitespace differences are being ignored\n+# because of the missing string at EOL. TODO: this testcase should be\n+# improved by creating a line that has the same hash with and without\n+# the final string.\n+\n+sed -e 's/Z/ /g' > patch3.patch <<\\EOF\n+diff --git a/main.c b/main.c\n+--- a/main.c\n++++ b/main.c\n+@@ -10,3 +10,4 @@\n+Z\tfor (i = 0; i < 10; i++) {\n+Z\t\tprint_int(func(i));Z\n++\t\t/* stuff */\n+Z\t}\n+EOF\n+\n+# This patch will fail even though whitespace differences are being ignored\n+# because of the missing EOL at EOF.\n+\n+sed -e 's/Z/ /g' > patch4.patch <<\\EOF\n+diff --git a/main.c b/main.c\n+--- a/main.c\n++++ b/main.c\n+@@ -21,1 +21,1 @@\n+-\t};Z\n+\\ No newline at end of file\n++\t};\n+EOF\n+\n+# This patch will fail unless whitespace differences are being ignored\n+\n+sed -e 's/Z/ /g' > patch5.patch <<\\EOF\n+diff --git a/main.c b/main.c\n+--- a/main.c\n++++ b/main.c\n+@@ -2,2 +2,3 @@\n+Z\tvoid print_int(int num);\n++\t/* a comment */\n+Z\tint func(int num);\n+EOF\n+\n+# And this is how the final output should be\n+cat > main.c.final <<\\EOF\n+#include <stdio.h>\n+\n+void print_int(int num);\n+\t/* a comment */\n+int func(int num);\n+\n+int main() {\n+       int i;\n+\n+       for (i = 0; i < 10; i++) {\n+               print_int(func(i)); /* stuff */\n+       }\n+\n+\tprintf(\"\\n\");\n+\n+       return 0;\n+}\n+\n+int func(int num) {\n+       return num * num;\n+}\n+\n+void print_int(int num) {\n+       printf(\"%d\", num);\n+}\n+EOF\n+\n+test_expect_success \"file creation\" \\\n+    'git-apply patch1.patch'\n+\n+test_expect_success \"patch2 fails (retab)\" \\\n+    'test_must_fail git-apply patch2.patch'\n+\n+test_expect_success \"patch2 applies with --ignore-whitespace\" \\\n+    'git-apply --ignore-whitespace patch2.patch'\n+\n+test_expect_success \"patch2 reverse applies with --ignore-space-change\" \\\n+    'git-apply -R --ignore-space-change patch2.patch'\n+\n+git config apply.ignorewhitespace change\n+\n+test_expect_success \"patch2 applies (apply.ignorewhitespace = change)\" \\\n+    'git-apply patch2.patch'\n+\n+test_expect_success \"patch3 fails (missing string at EOL)\" \\\n+    'test_must_fail git-apply patch3.patch'\n+\n+test_expect_success \"patch4 fails (missing EOL at EOF)\" \\\n+    'test_must_fail git-apply patch4.patch'\n+\n+test_expect_success \"patch5 applies (leading whitespace)\" \\\n+    'git-apply patch5.patch'\n+\n+test_expect_success \"patches don't mangle whitespace\" \\\n+    'diff -q main.c main.c.final'\n+\n+rm -f main.c\n+test_expect_success \"re-create file (with --ignore-whitespace)\" \\\n+    'git-apply patch1.patch'\n+\n+test_expect_success \"patch5 fails (--no-ignore-whitespace)\" \\\n+    'test_must_fail git-apply --no-ignore-whitespace patch5.patch'\n+\n+test_done\n+\n+\n-- \n1.6.4.216.g15680.dirty\n"},{"id":"122231","messageId":"7vocpvvxaw.fsf@alter.siamese.dyndns.org","threadId":"20368","inReplyTo":"1249384609-30240-1-git-send-email-giuseppe.bilotta@gmail.com","subject":"Re: [PATCHv4] git apply: option to ignore whitespace differences","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-09-01T09:17:27Z","receivedAt":"2009-09-01T09:17:27Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Giuseppe Bilotta <giuseppe.bilotta@gmail.com> writes:\n\n> diff --git a/builtin-apply.c b/builtin-apply.c\n> index 39dc96a..7ec5b8b 100644\n> --- a/builtin-apply.c\n> +++ b/builtin-apply.c\n> @@ -1773,12 +1866,57 @@ static int match_fragment(struct image *img,\n>  \t    !memcmp(img->buf + try, preimage->buf, preimage->len))\n>  \t\treturn 1;\n>  \n> +\t/*\n> +\t * No exact match. If we are ignoring whitespace, run a line-by-line\n> +\t * fuzzy matching. We collect all the line length information because\n> +\t * we need it to adjust whitespace if we match.\n> +\t */\n> +\tif (ws_ignore_action == ignore_ws_change) {\n> +\t\tsize_t imgoff = 0;\n> +\t\tsize_t preoff = 0;\n> +\t\tsize_t postlen = postimage->len;\n> +\t\tsize_t imglen[preimage->nr];\n> +\t\tfor (i = 0; i < preimage->nr; i++) {\n> +\t\t\timglen[i] = img->line[try_lno+i].len;\n> +\t\t\tsize_t prelen = preimage->line[i].len;\n> +\t\t\tif (!fuzzy_matchlines(\n> +\t\t\t\timg->buf + try + imgoff, imglen[i],\n> +\t\t\t\tpreimage->buf + preoff, prelen))\n> +\t\t\t\treturn 0;\n> +\t\t\tif (preimage->line[i].flag & LINE_COMMON)\n> +\t\t\t\tpostlen += imglen[i] - prelen;\n> +\t\t\timgoff += imglen[i];\n> +\t\t\tpreoff += prelen;\n> +\t\t}\n> +\n> +\t\t/*\n> +\t\t * Ok, the preimage matches with whitespace fuzz. Update it and\n> +\t\t * the common postimage lines to use the same whitespace as the\n> +\t\t * target. imgoff now holds the true length of the target that\n> +\t\t * matches the preimage, and we need to update the line lengths\n> +\t\t * of the preimage to match the target ones.\n> +\t\t */\n> +\t\tfixed_buf = xmalloc(imgoff);\n> +\t\tmemcpy(fixed_buf, img->buf + try, imgoff);\n> +\t\tfor (i = 0; i < preimage->nr; i++)\n> +\t\t\tpreimage->line[i].len = imglen[i];\n> +\n> +\t\t/*\n> +\t\t * Update the preimage buffer and the postimage context lines.\n> +\t\t */\n> +\t\tupdate_pre_post_images(preimage, postimage,\n> +\t\t\t\tfixed_buf, imgoff, postlen);\n> +\t\treturn 1;\n> +\t}\n> +\n\nWhy do you need imglen[] vla here?  IOW, can't the above be simply like\nthis?\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex ae11b41..c8372a0 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -1874,20 +1874,18 @@ static int match_fragment(struct image *img,\n \tif (ws_ignore_action == ignore_ws_change) {\n \t\tsize_t imgoff = 0;\n \t\tsize_t preoff = 0;\n \t\tsize_t postlen = postimage->len;\n-\t\tsize_t imglen[preimage->nr];\n \t\tfor (i = 0; i < preimage->nr; i++) {\n \t\t\tsize_t prelen = preimage->line[i].len;\n+\t\t\tsize_t imglen = img->line[try_lno+i].len;\n \n-\t\t\timglen[i] = img->line[try_lno+i].len;\n-\t\t\tif (!fuzzy_matchlines(\n-\t\t\t\timg->buf + try + imgoff, imglen[i],\n-\t\t\t\tpreimage->buf + preoff, prelen))\n+\t\t\tif (!fuzzy_matchlines(img->buf + try + imgoff, imglen,\n+\t\t\t\t\t      preimage->buf + preoff, prelen))\n \t\t\t\treturn 0;\n \t\t\tif (preimage->line[i].flag & LINE_COMMON)\n-\t\t\t\tpostlen += imglen[i] - prelen;\n-\t\t\timgoff += imglen[i];\n+\t\t\t\tpostlen += imglen - prelen;\n+\t\t\timgoff += imglen;\n \t\t\tpreoff += prelen;\n \t\t}\n \n \t\t/*\n@@ -1899,9 +1897,9 @@ static int match_fragment(struct image *img,\n \t\t */\n \t\tfixed_buf = xmalloc(imgoff);\n \t\tmemcpy(fixed_buf, img->buf + try, imgoff);\n \t\tfor (i = 0; i < preimage->nr; i++)\n-\t\t\tpreimage->line[i].len = imglen[i];\n+\t\t\tpreimage->line[i].len = img->line[try_lno+i].len;\n \n \t\t/*\n \t\t * Update the preimage buffer and the postimage context lines.\n \t\t */\n"},{"id":"122233","messageId":"cb7bb73a0909010253j530814cdqe67872d226b5623a@mail.gmail.com","threadId":"20368","inReplyTo":"7vocpvvxaw.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv4] git apply: option to ignore whitespace differences","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2009-09-01T09:53:54Z","receivedAt":"2009-09-01T09:53:54Z","isPatch":false,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"On Tue, Sep 1, 2009 at 11:17 AM, Junio C Hamano<gitster@pobox.com> wrote:\n>\n> Why do you need imglen[] vla here?  IOW, can't the above be simply like\n> this?\n>\n> diff --git a/builtin-apply.c b/builtin-apply.c\n> index ae11b41..c8372a0 100644\n> --- a/builtin-apply.c\n> +++ b/builtin-apply.c\n> @@ -1874,20 +1874,18 @@ static int match_fragment(struct image *img,\n>        if (ws_ignore_action == ignore_ws_change) {\n>                size_t imgoff = 0;\n>                size_t preoff = 0;\n>                size_t postlen = postimage->len;\n> -               size_t imglen[preimage->nr];\n>                for (i = 0; i < preimage->nr; i++) {\n>                        size_t prelen = preimage->line[i].len;\n> +                       size_t imglen = img->line[try_lno+i].len;\n>\n> -                       imglen[i] = img->line[try_lno+i].len;\n> -                       if (!fuzzy_matchlines(\n> -                               img->buf + try + imgoff, imglen[i],\n> -                               preimage->buf + preoff, prelen))\n> +                       if (!fuzzy_matchlines(img->buf + try + imgoff, imglen,\n> +                                             preimage->buf + preoff, prelen))\n>                                return 0;\n>                        if (preimage->line[i].flag & LINE_COMMON)\n> -                               postlen += imglen[i] - prelen;\n> -                       imgoff += imglen[i];\n> +                               postlen += imglen - prelen;\n> +                       imgoff += imglen;\n>                        preoff += prelen;\n>                }\n>\n>                /*\n> @@ -1899,9 +1897,9 @@ static int match_fragment(struct image *img,\n>                 */\n>                fixed_buf = xmalloc(imgoff);\n>                memcpy(fixed_buf, img->buf + try, imgoff);\n>                for (i = 0; i < preimage->nr; i++)\n> -                       preimage->line[i].len = imglen[i];\n> +                       preimage->line[i].len = img->line[try_lno+i].len;\n\nYep, I think that would do it. I'm not sure why I was doing it that\nother way. Maybe a leftover from when I was still getting confident\nwith the code and I hadn't yet found the var that held the initial\nmatch line, or something like that.\n\n-- \nGiuseppe \"Oblomov\" Bilotta\n"}]}