{"thread":{"id":"33940","subject":"[PATCH] diff: add --ignore-blank-lines option","startedAt":"2013-05-26T17:58:18Z","lastAt":"2013-06-19T22:23:34Z","messageCount":26,"participants":["Antoine Pelisse","Johannes Sixt","Junio C Hamano","Eric Sunshine"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"218572","messageId":"1369591098-11267-1-git-send-email-apelisse@gmail.com","threadId":"33940","inReplyTo":null,"subject":"[PATCH] diff: add --ignore-blank-lines option","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-05-26T17:58:18Z","receivedAt":"2013-05-26T17:58:18Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"The goal of the patch is to introduce the GNU diff\n-B/--ignore-blank-lines as closely as possible. The short option is not\navailable because it's already used for \"break-rewrites\".\n\nWhen this option is used, git-diff will not create hunks that simply\nadds or removes empty lines, but will still show empty lines\naddition/suppression if they are close enough to \"valuable\" changes.\n\nSigned-off-by: Antoine Pelisse <apelisse@gmail.com>\n---\n Documentation/diff-options.txt |    3 ++\n diff.c                         |    2 ++\n t/t4015-diff-whitespace.sh     |   65 ++++++++++++++++++++++++++++++++++++++++\n xdiff/xdiff.h                  |    2 ++\n xdiff/xdiffi.c                 |   29 +++++++++++++++++-\n xdiff/xdiffi.h                 |    1 +\n xdiff/xemit.c                  |   32 ++++++++++++++++----\n xdiff/xemit.h                  |    2 +-\n xdiff/xutils.c                 |   13 ++++++++\n xdiff/xutils.h                 |    1 +\n 10 files changed, 143 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 104579d..80f06b7 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -439,6 +439,9 @@ endif::git-format-patch[]\n \tdifferences even if one line has whitespace where the other\n \tline has none.\n\n+--ignore-blank-lines::\n+\tIgnore changes whose lines are all blank.\n+\n --inter-hunk-context=<lines>::\n \tShow the context between diff hunks, up to the specified number\n \tof lines, thereby fusing hunks that are close to each other.\ndiff --git a/diff.c b/diff.c\nindex f0b3e7c..208094f 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -3593,6 +3593,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n \t\tDIFF_XDL_SET(options, IGNORE_WHITESPACE_CHANGE);\n \telse if (!strcmp(arg, \"--ignore-space-at-eol\"))\n \t\tDIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);\n+\telse if (!strcmp(arg, \"--ignore-blank-lines\"))\n+\t\tDIFF_XDL_SET(options, IGNORE_BLANK_LINES);\n \telse if (!strcmp(arg, \"--patience\"))\n \t\toptions->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);\n \telse if (!strcmp(arg, \"--histogram\"))\ndiff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\nindex cc3db13..b3c4fcc 100755\n--- a/t/t4015-diff-whitespace.sh\n+++ b/t/t4015-diff-whitespace.sh\n@@ -142,6 +142,71 @@ EOF\n git diff --ignore-space-at-eol > out\n test_expect_success 'another test, with --ignore-space-at-eol' 'test_cmp expect out'\n\n+test_expect_success 'ignore-blank-lines: only new lines' '\n+\tseq 5 >x &&\n+\tgit update-index x &&\n+\tseq 5 | sed \"/3/i \\\\\\\\\" >x &&\n+\tgit diff --ignore-blank-lines >out &&\n+\tprintf \"\" >expect &&\n+\ttest_cmp out expect\n+'\n+\n+test_expect_success 'ignore-blank-lines: only new lines with space' '\n+\tseq 5 >x &&\n+\tgit update-index x &&\n+\tseq 5 | sed \"/3/i \\ \" >x &&\n+\tgit diff -w --ignore-blank-lines >out &&\n+\tprintf \"\" >expect &&\n+\ttest_cmp out expect\n+'\n+\n+\n+test_expect_success 'ignore-blank-lines: with changes' '\n+\tseq 11 >x &&\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\n+\t1\n+\t2\n+\t3\n+\tchange\n+\t4\n+\t5\n+\t6\n+\t7\n+\n+\t8\n+\tchange\n+\t9\n+\t10\n+\t11\n+\n+\tEOF\n+\tgit diff --ignore-blank-lines >out.tmp &&\n+\tsed -e \"1,/^+++ b\\/x/d\" <out.tmp >out &&\n+\tcat <<-\\EOF >expect &&\n+\t@@ -1,6 +2,7 @@\n+\t 1\n+\t 2\n+\t 3\n+\t+change\n+\t 4\n+\t 5\n+\t 6\n+\t@@ -5,7 +7,9 @@\n+\t 5\n+\t 6\n+\t 7\n+\t+\n+\t 8\n+\t+change\n+\t 9\n+\t 10\n+\t 11\n+\tEOF\n+\ttest_cmp out expect\n+'\n+\n test_expect_success 'check mixed spaces and tabs in indent' '\n\n \t# This is indented with SP HT SP.\ndiff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\nindex 219a3bb..c033991 100644\n--- a/xdiff/xdiff.h\n+++ b/xdiff/xdiff.h\n@@ -39,6 +39,8 @@ extern \"C\" {\n #define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)\n #define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)\n\n+#define XDF_IGNORE_BLANK_LINES (1 << 7)\n+\n #define XDL_EMIT_FUNCNAMES (1 << 0)\n #define XDL_EMIT_COMMON (1 << 1)\n #define XDL_EMIT_FUNCCONTEXT (1 << 2)\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex b2eb6db..3cabc78 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -394,6 +394,7 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,\n \txch->i2 = i2;\n \txch->chg1 = chg1;\n \txch->chg2 = chg2;\n+\txch->ignore = 0;\n\n \treturn xch;\n }\n@@ -544,7 +545,9 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \txdchange_t *xch, *xche;\n\n \tfor (xch = xscr; xch; xch = xche->next) {\n-\t\txche = xdl_get_hunk(xch, xecfg);\n+\t\txche = xdl_get_hunk(&xch, xecfg);\n+\t\tif (!xch)\n+\t\t\tbreak;\n \t\tif (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1,\n \t\t\t\t     xch->i2, xche->i2 + xche->chg2 - xch->i2,\n \t\t\t\t     ecb->priv) < 0)\n@@ -553,6 +556,27 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \treturn 0;\n }\n\n+static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags)\n+{\n+\txdchange_t *xch;\n+\n+\tfor (xch = xscr; xch; xch = xch->next) {\n+\t\tint ignore = 1;\n+\t\txrecord_t **rec;\n+\t\tlong i;\n+\n+\t\trec = &xe->xdf1.recs[xch->i1];\n+\t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n+\t\t\tignore = xdl_blankline(rec[i]->ptr, flags);\n+\n+\t\trec = &xe->xdf2.recs[xch->i2];\n+\t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n+\t\t\tignore = xdl_blankline(rec[i]->ptr, flags);\n+\n+\t\txch->ignore = ignore;\n+\t}\n+}\n+\n int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \t     xdemitconf_t const *xecfg, xdemitcb_t *ecb) {\n \txdchange_t *xscr;\n@@ -571,6 +595,9 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \t\treturn -1;\n \t}\n \tif (xscr) {\n+\t\tif (xpp->flags & XDF_IGNORE_BLANK_LINES)\n+\t\t\txdl_mark_ignorable(xscr, &xe, xpp->flags);\n+\n \t\tif (ef(&xe, xscr, ecb, xecfg) < 0) {\n\n \t\t\txdl_free_script(xscr);\ndiff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h\nindex 7a92ea9..8b81206 100644\n--- a/xdiff/xdiffi.h\n+++ b/xdiff/xdiffi.h\n@@ -41,6 +41,7 @@ typedef struct s_xdchange {\n \tstruct s_xdchange *next;\n \tlong i1, i2;\n \tlong chg1, chg2;\n+\tint ignore;\n } xdchange_t;\n\n\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex d11dbf9..52dfef8 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -56,14 +56,34 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *\n /*\n  * Starting at the passed change atom, find the latest change atom to be included\n  * inside the differential hunk according to the specified configuration.\n+ * Also advance xscr if the first changes must be discareded.\n  */\n-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {\n+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg) {\n \txdchange_t *xch, *xchp;\n \tlong max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;\n+\tlong ignorable_context = max_common / 2 - 1;\n+\tint interesting = 0;\n\n-\tfor (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)\n-\t\tif (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)\n-\t\t\tbreak;\n+\tfor (xchp = *xscr, xch = (*xscr)->next; xch; xchp = xch, xch = xch->next) {\n+\t\tlong thresh;\n+\t\tif (xchp->ignore || xch->ignore)\n+\t\t\tthresh = ignorable_context;\n+\t\telse\n+\t\t\tthresh = max_common;\n+\n+\t\tif (!xchp->ignore)\n+\t\t\tinteresting = 1;\n+\n+\t\tif (xch->i1 - (xchp->i1 + xchp->chg1) > thresh) {\n+\t\t\tif (interesting)\n+\t\t\t\tbreak;\n+\t\t\telse\n+\t\t\t\t*xscr = xch;\n+\t\t}\n+\t}\n+\n+\tif (!interesting && xchp->ignore)\n+\t\t*xscr = NULL;\n\n \treturn xchp;\n }\n@@ -139,7 +159,9 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \t\treturn xdl_emit_common(xe, xscr, ecb, xecfg);\n\n \tfor (xch = xscr; xch; xch = xche->next) {\n-\t\txche = xdl_get_hunk(xch, xecfg);\n+\t\txche = xdl_get_hunk(&xch, xecfg);\n+\t\tif (!xch)\n+\t\t\tbreak;\n\n \t\ts1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);\n \t\ts2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);\ndiff --git a/xdiff/xemit.h b/xdiff/xemit.h\nindex c2e2e83..d297107 100644\n--- a/xdiff/xemit.h\n+++ b/xdiff/xemit.h\n@@ -27,7 +27,7 @@\n typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \t\t\t   xdemitconf_t const *xecfg);\n\n-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);\n+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg);\n int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \t\t  xdemitconf_t const *xecfg);\n\ndiff --git a/xdiff/xutils.c b/xdiff/xutils.c\nindex 9504eae..c047376 100644\n--- a/xdiff/xutils.c\n+++ b/xdiff/xutils.c\n@@ -143,6 +143,19 @@ long xdl_guess_lines(mmfile_t *mf, long sample) {\n \treturn nl + 1;\n }\n\n+int xdl_blankline(const char *line, long flags)\n+{\n+\tlong i;\n+\n+\tif (!(flags & XDF_WHITESPACE_FLAGS))\n+\t\treturn (*line == '\\n');\n+\n+\tfor (i = 0; line[i] != '\\n' && XDL_ISSPACE(line[i]); i++)\n+\t\t;\n+\n+\treturn (line[i] == '\\n');\n+}\n+\n int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)\n {\n \tint i1, i2;\ndiff --git a/xdiff/xutils.h b/xdiff/xutils.h\nindex ad1428e..b9cceff 100644\n--- a/xdiff/xutils.h\n+++ b/xdiff/xutils.h\n@@ -32,6 +32,7 @@ int xdl_cha_init(chastore_t *cha, long isize, long icount);\n void xdl_cha_free(chastore_t *cha);\n void *xdl_cha_alloc(chastore_t *cha);\n long xdl_guess_lines(mmfile_t *mf, long sample);\n+int xdl_blankline(const char *line, long flags);\n int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);\n unsigned long xdl_hash_record(char const **data, char const *top, long flags);\n unsigned int xdl_hashbits(unsigned int size);\n--\n1.7.9.5\n"},{"id":"218585","messageId":"51A2722A.8040200@kdbg.org","threadId":"33940","inReplyTo":"1369591098-11267-1-git-send-email-apelisse@gmail.com","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2013-05-26T20:35:54Z","receivedAt":"2013-05-26T20:35:54Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 26.05.2013 19:58, schrieb Antoine Pelisse:\n> The goal of the patch is to introduce the GNU diff\n> -B/--ignore-blank-lines as closely as possible. The short option is not\n> available because it's already used for \"break-rewrites\".\n> \n> When this option is used, git-diff will not create hunks that simply\n> adds or removes empty lines, but will still show empty lines\n> addition/suppression if they are close enough to \"valuable\" changes.\n\nSo when an addition or removal of a blank line appears in a hunk that\nalso has non-blank-line changes, the addition or removal is not treated\nspecially?\n\nHow is a blank line defined? What happens if a line that has only\nwhitespace is added or removed? I'm thinking of diffs of files with CRLF\nline breaks, where the CR would count as whitespace in the line, I think.\n\n> +--ignore-blank-lines::\n> +\tIgnore changes whose lines are all blank.\n\nI think this is too terse and does not convey what the option really does.\n\n> +test_expect_success 'ignore-blank-lines: only new lines' '\n> +\tseq 5 >x &&\n\nPlease use test_seq instead of seq in all new tests.\n\n-- Hannes\n"},{"id":"218591","messageId":"CALWbr2xzSC8ZGggCUYbS7ZWEOHnZnxFJWOfHCOBkVZFQS9KcEw@mail.gmail.com","threadId":"33940","inReplyTo":"51A2722A.8040200@kdbg.org","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-05-27T07:14:15Z","receivedAt":"2013-05-27T07:14:15Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"On Sun, May 26, 2013 at 10:35 PM, Johannes Sixt <j6t@kdbg.org> wrote:\n> Am 26.05.2013 19:58, schrieb Antoine Pelisse:\n>> The goal of the patch is to introduce the GNU diff\n>> -B/--ignore-blank-lines as closely as possible. The short option is not\n>> available because it's already used for \"break-rewrites\".\n>>\n>> When this option is used, git-diff will not create hunks that simply\n>> adds or removes empty lines, but will still show empty lines\n>> addition/suppression if they are close enough to \"valuable\" changes.\n>\n> So when an addition or removal of a blank line appears in a hunk that\n> also has non-blank-line changes, the addition or removal is not treated\n> specially?\n\nExactly.\n\n> How is a blank line defined? What happens if a line that has only\n> whitespace is added or removed?\n\nxdl_blankline() is the best description of what I considered a blank line.\nIf no --ignore-space-* option is given, it's a line that starts and\nends with '\\n'.\nIf any --ignore-space-* option is given, it's a line that has any\nnumber of isspace(3)-defined characters, followed by '\\n'.\n\n> I'm thinking of diffs of files with CRLF\n\nGood you did, because I didn't ;-)\n\n> line breaks, where the CR would count as whitespace in the line, I think.\n\nWith the current implementation, an empty line with CRLF will not show\nas a blank line if no space option is given. As CR is a space\naccording to isspace(3), the line will be removed with any space\noption.\n\n>> +--ignore-blank-lines::\n>> +     Ignore changes whose lines are all blank.\n>\n> I think this is too terse and does not convey what the option really does.\n\nThat's the description from GNU diff man page. But indeed it could be\nmore precise.\n\n>> +test_expect_success 'ignore-blank-lines: only new lines' '\n>> +     seq 5 >x &&\n>\n> Please use test_seq instead of seq in all new tests.\n\nWill fix.\n\n> -- Hannes\n>\n"},{"id":"219113","messageId":"CALWbr2w0rX3kY4doR3twzSpR5AL7gejtMpNZo_x=S1e25Ytwew@mail.gmail.com","threadId":"33940","inReplyTo":"CALWbr2xzSC8ZGggCUYbS7ZWEOHnZnxFJWOfHCOBkVZFQS9KcEw@mail.gmail.com","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-06-01T08:48:35Z","receivedAt":"2013-06-01T08:48:35Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"On Mon, May 27, 2013 at 9:14 AM, Antoine Pelisse <apelisse@gmail.com> wrote:\n> On Sun, May 26, 2013 at 10:35 PM, Johannes Sixt <j6t@kdbg.org> wrote:\n>> I'm thinking of diffs of files with CRLF\n>\n> Good you did, because I didn't ;-)\n>\n>> line breaks, where the CR would count as whitespace in the line, I think.\n>\n> With the current implementation, an empty line with CRLF will not show\n> as a blank line if no space option is given. As CR is a space\n> according to isspace(3), the line will be removed with any space\n> option.\n\nMaybe it would be worth adding the diff(1) \"--strip-trailing-cr\"\noption. So that you could remove empty lines if you have dos line\nendings while caring about eol space-changes (or other space changes).\n"},{"id":"219392","messageId":"7vzjv57mwx.fsf@alter.siamese.dyndns.org","threadId":"33940","inReplyTo":"1369591098-11267-1-git-send-email-apelisse@gmail.com","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-04T18:26:06Z","receivedAt":"2013-06-04T18:26:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Antoine Pelisse <apelisse@gmail.com> writes:\n\n> +xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg) {\n>  \txdchange_t *xch, *xchp;\n>  \tlong max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;\n> +\tlong ignorable_context = max_common / 2 - 1;\n\nCould you explain how this math works?  Also the logic to use it\nwhen either the previous or the current one is \"blank only\" (as\nopposed to having two different settings for \"both are blank only\"\nand \"only one of them is, and the other is not\", for example)?\n\nThe normal case when neither is blank only, we refrain from\ncollapsing two adjacent xdchanges if the end of xchp (i.e. the\nprevious one) is before the beginning of xch (i.e. the current one\nwe are looking at) by more than max_common lines, which makes sense\nto me because we count one ctxlen for the trailing context for xchp,\ninterhunkctxlen to force collapsing, and another ctxlen for the\nleading context for xch.\n\nWhen we have \n\n    - zero or more \"blank only\" changes, followed by\n    - a meaningful change A, followed by\n    - zero or more blank-only changes, followed by\n    - a meaningful change C,\n\nwe may want to have either two hunks (A with context around it, and\nC with context around it) or a single hunk (precontext before A, all\nthe lines from the beginning of A to the end of C, and postcontext\nafter C).  In either case, we want to discard the leading \"blank\nonly\" changes.\n\nI can sort-of see how the leading \"blank only\" changes are discarded\nin the loop (but not quite---you can ignore everything without any\n\"thresh\", until you set \"interesting\" to true, i.e. seeing A, no?).\n\nIt is not clear to me how you are counting the distance between the\nend of A and the beginning of C, which I think is all that matters,\nto make the decision to coalesce (or not to coalesce) the above into\na single hunk, without looking ahead to find the next xdchange that\nis not marked with xch->ignore flag (that is, when looking at A,\nfind the beginning of C to see if C.begin-A.end is within the usual\nmax_common).\n\nConfused...\n\n> +\tint interesting = 0;\n>\n> -\tfor (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)\n> -\t\tif (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)\n> -\t\t\tbreak;\n> +\tfor (xchp = *xscr, xch = (*xscr)->next; xch; xchp = xch, xch = xch->next) {\n> +\t\tlong thresh;\n> +\t\tif (xchp->ignore || xch->ignore)\n> +\t\t\tthresh = ignorable_context;\n> +\t\telse\n> +\t\t\tthresh = max_common;\n> +\n> +\t\tif (!xchp->ignore)\n> +\t\t\tinteresting = 1;\n> +\n> +\t\tif (xch->i1 - (xchp->i1 + xchp->chg1) > thresh) {\n> +\t\t\tif (interesting)\n> +\t\t\t\tbreak;\n> +\t\t\telse\n> +\t\t\t\t*xscr = xch;\n> +\t\t}\n> +\t}\n> +\n> +\tif (!interesting && xchp->ignore)\n> +\t\t*xscr = NULL;\n>\n>  \treturn xchp;\n>  }\n> @@ -139,7 +159,9 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n>  \t\treturn xdl_emit_common(xe, xscr, ecb, xecfg);\n>\n>  \tfor (xch = xscr; xch; xch = xche->next) {\n> -\t\txche = xdl_get_hunk(xch, xecfg);\n> +\t\txche = xdl_get_hunk(&xch, xecfg);\n> +\t\tif (!xch)\n> +\t\t\tbreak;\n>\n>  \t\ts1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);\n>  \t\ts2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);\n> diff --git a/xdiff/xemit.h b/xdiff/xemit.h\n> index c2e2e83..d297107 100644\n> --- a/xdiff/xemit.h\n> +++ b/xdiff/xemit.h\n> @@ -27,7 +27,7 @@\n>  typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n>  \t\t\t   xdemitconf_t const *xecfg);\n>\n> -xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);\n> +xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg);\n>  int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n>  \t\t  xdemitconf_t const *xecfg);\n>\n> diff --git a/xdiff/xutils.c b/xdiff/xutils.c\n> index 9504eae..c047376 100644\n> --- a/xdiff/xutils.c\n> +++ b/xdiff/xutils.c\n> @@ -143,6 +143,19 @@ long xdl_guess_lines(mmfile_t *mf, long sample) {\n>  \treturn nl + 1;\n>  }\n>\n> +int xdl_blankline(const char *line, long flags)\n> +{\n> +\tlong i;\n> +\n> +\tif (!(flags & XDF_WHITESPACE_FLAGS))\n> +\t\treturn (*line == '\\n');\n> +\n> +\tfor (i = 0; line[i] != '\\n' && XDL_ISSPACE(line[i]); i++)\n> +\t\t;\n> +\n> +\treturn (line[i] == '\\n');\n> +}\n> +\n>  int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)\n>  {\n>  \tint i1, i2;\n> diff --git a/xdiff/xutils.h b/xdiff/xutils.h\n> index ad1428e..b9cceff 100644\n> --- a/xdiff/xutils.h\n> +++ b/xdiff/xutils.h\n> @@ -32,6 +32,7 @@ int xdl_cha_init(chastore_t *cha, long isize, long icount);\n>  void xdl_cha_free(chastore_t *cha);\n>  void *xdl_cha_alloc(chastore_t *cha);\n>  long xdl_guess_lines(mmfile_t *mf, long sample);\n> +int xdl_blankline(const char *line, long flags);\n>  int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);\n>  unsigned long xdl_hash_record(char const **data, char const *top, long flags);\n>  unsigned int xdl_hashbits(unsigned int size);\n> --\n> 1.7.9.5\n"},{"id":"219397","messageId":"CALWbr2zJCxbW8Qug0i=oGvKZV5-vcAugextxMuQp9jaoaZYpyg@mail.gmail.com","threadId":"33940","inReplyTo":"7vzjv57mwx.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-06-04T19:08:03Z","receivedAt":"2013-06-04T19:08:03Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"On Tue, Jun 4, 2013 at 8:26 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Antoine Pelisse <apelisse@gmail.com> writes:\n>\n>> +xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg) {\n>>       xdchange_t *xch, *xchp;\n>>       long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;\n>> +     long ignorable_context = max_common / 2 - 1;\n>\n> Could you explain how this math works?\n\nI think it doesn't, mostly because I misinterpreted the \"interhunkctxlen\".\nI will try to think about that and provide a reroll.\n\nThanks for the review and analysis.\nAntoine,\n"},{"id":"219402","messageId":"7v61xt7gej.fsf@alter.siamese.dyndns.org","threadId":"33940","inReplyTo":"CALWbr2zJCxbW8Qug0i=oGvKZV5-vcAugextxMuQp9jaoaZYpyg@mail.gmail.com","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-04T20:46:44Z","receivedAt":"2013-06-04T20:46:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Antoine Pelisse <apelisse@gmail.com> writes:\n\n> On Tue, Jun 4, 2013 at 8:26 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Antoine Pelisse <apelisse@gmail.com> writes:\n>>\n>>> +xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg) {\n>>>       xdchange_t *xch, *xchp;\n>>>       long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;\n>>> +     long ignorable_context = max_common / 2 - 1;\n>>\n>> Could you explain how this math works?\n>\n> I think it doesn't, mostly because I misinterpreted the \"interhunkctxlen\".\n> I will try to think about that and provide a reroll.\n\nOK.  Thanks.\n\nI think the logic would be more like:\n\n 1. Start from xscr, find the first xchp that is !xchp->ignore;\n    if there is none, we are done. There is no more to show.\n\n 2. Remember the xchp as the beginning.\n\n 3. Tangle ->next pointer to find the next xch that is !xch->ignore;\n    if there is none, we are also done.  xdchanges between the\n    beginning you remembered in the step 2. and your current xchp\n    are the only things we want to show.\n\n 4. Measure the distance between the end of xchp and the beginning\n    of xch.\n\n    - If it is larger than max_common, xdchanges between the\n      beginning you remembered in the step 2. and your current xchp\n      are the only things we want to show.  The next iteration will\n      start by skipping the blank-only changes between xchp and xch.\n\n    - If it is short enough, assign xchp = xch and go back to 3. to\n      find more interesting hunks (that is why we remembered the\n      real \"beginning\" in step 2.).\n"},{"id":"219404","messageId":"CALWbr2yv_vYpuFH4wTOaRwmAT-YZ-O=VMo-idWC_ALwgCkrqHQ@mail.gmail.com","threadId":"33940","inReplyTo":"7v61xt7gej.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-06-04T20:51:01Z","receivedAt":"2013-06-04T20:51:01Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"On Tue, Jun 4, 2013 at 10:46 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> OK.  Thanks.\n>\n> I think the logic would be more like:\n>\n>  1. Start from xscr, find the first xchp that is !xchp->ignore;\n>     if there is none, we are done. There is no more to show.\n>\n>  2. Remember the xchp as the beginning.\n>\n>  3. Tangle ->next pointer to find the next xch that is !xch->ignore;\n>     if there is none, we are also done.  xdchanges between the\n>     beginning you remembered in the step 2. and your current xchp\n>     are the only things we want to show.\n>\n>  4. Measure the distance between the end of xchp and the beginning\n>     of xch.\n>\n>     - If it is larger than max_common, xdchanges between the\n>       beginning you remembered in the step 2. and your current xchp\n>       are the only things we want to show.  The next iteration will\n>       start by skipping the blank-only changes between xchp and xch.\n>\n>     - If it is short enough, assign xchp = xch and go back to 3. to\n>       find more interesting hunks (that is why we remembered the\n>       real \"beginning\" in step 2.).\n\nYeah, I'm doing something pretty much like that right now (though I\nwill have to eventually sleep).\nI decided that it would indeed be easier to split the logic rather\nthan do everything in one loop.\n\nThanks for the help !\n"},{"id":"219861","messageId":"1370724291-30088-1-git-send-email-apelisse@gmail.com","threadId":"33940","inReplyTo":"7v61xt7gej.fsf@alter.siamese.dyndns.org","subject":"[PATCH] diff: add --ignore-blank-lines option","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-06-08T20:44:51Z","receivedAt":"2013-06-08T20:44:51Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"The goal of the patch is to introduce the GNU diff\n-B/--ignore-blank-lines as closely as possible. The short option is not\navailable because it's already used for \"break-rewrites\".\n\nWhen this option is used, git-diff will not create hunks that simply\nadds or removes empty lines, but will still show empty lines\naddition/suppression if they are close enough to \"valuable\" changes.\n\nThere are two differences between this option and GNU diff -B option:\n- GNU diff doesn't have \"--inter-hunk-context\", so this must be handled\n- The following sequence looks like a bug (context is displayed twice):\n\n    $ seq 5 >file1\n    $ cat <<EOF >file2\n    change\n    1\n    2\n\n    3\n    4\n    5\n    change\n    EOF\n    $ diff -u -B file1 file2\n    --- file1\t2013-06-08 22:13:04.471517834 +0200\n    +++ file2\t2013-06-08 22:13:23.275517855 +0200\n    @@ -1,5 +1,7 @@\n    +change\n     1\n     2\n    +\n     3\n     4\n     5\n    @@ -3,3 +5,4 @@\n     3\n     4\n     5\n    +change\n\nSo here is a more thorough description of the option:\n- real changes are interesting\n- blank lines that are close enough (less than context size) to\ninteresting changes are considered interesting (recursive definition)\n- \"context\" lines are used around each hunk of interesting changes\n- If two hunks are separated by less than \"inter-hunk-context\", they\nwill be merged into one.\n\nThe current implementation does the \"interesting changes selection\" in a\nsingle pass.\n\nSigned-off-by: Antoine Pelisse <apelisse@gmail.com>\n---\nHi,\n\nOn Tue, Jun 4, 2013 at 10:46 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> OK. Thanks.\n>\n> I think the logic would be more like:\n>\n>  1. Start from xscr, find the first xchp that is !xchp->ignore;\n>     if there is none, we are done. There is no more to show.\n>\n>  2. Remember the xchp as the beginning.\n>\n>  3. Tangle ->next pointer to find the next xch that is !xch->ignore;\n>     if there is none, we are also done.  xdchanges between the\n>     beginning you remembered in the step 2. and your current xchp\n>     are the only things we want to show.\n>\n>  4. Measure the distance between the end of xchp and the beginning\n>     of xch.\n>\n>     - If it is larger than max_common, xdchanges between the\n>       beginning you remembered in the step 2. and your current xchp\n>       are the only things we want to show.  The next iteration will\n>       start by skipping the blank-only changes between xchp and xch.\n>\n>     - If it is short enough, assign xchp = xch and go back to 3. to\n>       find more interesting hunks (that is why we remembered the\n>       real \"beginning\" in step 2.).\n\nActually it doesn't quite work like that because we don't totally ignore\n\"blank lines\". We want to keep them if they are close enough to other\nchanges.\n\nI've tried to improve the number of tests as it helped me during\nimplementation, and to give a better description of the feature.\n\nThe initial reroll was meant to simplify xdl_get_hunk() but I'm afraid\nit became kind of \"voodoo code\".  I'm not sure if I should provide some\nmore comments about it and where.\n\nPlease let me know if something is not working as expected.\n\nCheers, Antoine\n\n Documentation/diff-options.txt |    3 +\n diff.c                         |    2 +\n t/t4015-diff-whitespace.sh     |  282 ++++++++++++++++++++++++++++++++++++++++\n xdiff/xdiff.h                  |    2 +\n xdiff/xdiffi.c                 |   29 ++++-\n xdiff/xdiffi.h                 |    1 +\n xdiff/xemit.c                  |   47 ++++++-\n xdiff/xemit.h                  |    2 +-\n xdiff/xutils.c                 |   13 ++\n xdiff/xutils.h                 |    1 +\n 10 files changed, 374 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex b8a9b86..4e042d9 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -439,6 +439,9 @@ endif::git-format-patch[]\n \tdifferences even if one line has whitespace where the other\n \tline has none.\n\n+--ignore-blank-lines::\n+\tIgnore changes whose lines are all blank.\n+\n --inter-hunk-context=<lines>::\n \tShow the context between diff hunks, up to the specified number\n \tof lines, thereby fusing hunks that are close to each other.\ndiff --git a/diff.c b/diff.c\nindex f0b3e7c..208094f 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -3593,6 +3593,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n \t\tDIFF_XDL_SET(options, IGNORE_WHITESPACE_CHANGE);\n \telse if (!strcmp(arg, \"--ignore-space-at-eol\"))\n \t\tDIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);\n+\telse if (!strcmp(arg, \"--ignore-blank-lines\"))\n+\t\tDIFF_XDL_SET(options, IGNORE_BLANK_LINES);\n \telse if (!strcmp(arg, \"--patience\"))\n \t\toptions->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);\n \telse if (!strcmp(arg, \"--histogram\"))\ndiff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\nindex cc3db13..fc77713 100755\n--- a/t/t4015-diff-whitespace.sh\n+++ b/t/t4015-diff-whitespace.sh\n@@ -142,6 +142,288 @@ EOF\n git diff --ignore-space-at-eol > out\n test_expect_success 'another test, with --ignore-space-at-eol' 'test_cmp expect out'\n\n+test_expect_success 'ignore-blank-lines: only new lines' '\n+\ttest_seq 5 >x &&\n+\tgit update-index x &&\n+\ttest_seq 5 | sed \"/3/i \\\\\n+\" >x &&\n+\tgit diff --ignore-blank-lines >out &&\n+\t>expect &&\n+\ttest_cmp out expect\n+'\n+\n+test_expect_success 'ignore-blank-lines: only new lines with space' '\n+\ttest_seq 5 >x &&\n+\tgit update-index x &&\n+\ttest_seq 5 | sed \"/3/i \\ \" >x &&\n+\tgit diff -w --ignore-blank-lines >out &&\n+\t>expect &&\n+\ttest_cmp out expect\n+'\n+\n+test_expect_success 'ignore-blank-lines: after change' '\n+\ttest_seq 7 >x &&\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\tchange\n+\t1\n+\t2\n+\n+\t3\n+\t4\n+\n+\t5\n+\t6\n+\t7\n+\n+\tEOF\n+\tgit diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -1,7 +1,10 @@\n+\t+change\n+\t 1\n+\t 2\n+\t+\n+\t 3\n+\t 4\n+\t+\n+\t 5\n+\t 6\n+\t 7\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n+test_expect_success 'ignore-blank-lines: before change' '\n+\ttest_seq 7 >x &&\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\n+\t1\n+\t2\n+\t3\n+\n+\t4\n+\t5\n+\n+\t6\n+\t7\n+\tchange\n+\tEOF\n+\tgit diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -1,7 +2,10 @@\n+\t 1\n+\t 2\n+\t 3\n+\t+\n+\t 4\n+\t 5\n+\t+\n+\t 6\n+\t 7\n+\t+change\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n+test_expect_success 'ignore-blank-lines: between changes' '\n+\ttest_seq 10 >x &&\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\tchange\n+\t1\n+\t2\n+\n+\t3\n+\t4\n+\t5\n+\n+\t6\n+\t7\n+\t8\n+\t9\n+\n+\t10\n+\tchange\n+\tEOF\n+\tgit diff --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -1,5 +1,7 @@\n+\t+change\n+\t 1\n+\t 2\n+\t+\n+\t 3\n+\t 4\n+\t 5\n+\t@@ -7,4 +10,6 @@\n+\t 7\n+\t 8\n+\t 9\n+\t+\n+\t 10\n+\t+change\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n+test_expect_success 'ignore-blank-lines: between changes (with interhunkctx)' '\n+\ttest_seq 10 >x &&\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\tchange\n+\t1\n+\t2\n+\n+\t3\n+\t4\n+\t5\n+\n+\t6\n+\t7\n+\t8\n+\t9\n+\n+\t10\n+\tchange\n+\tEOF\n+\tgit diff --inter-hunk-context=2 --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -1,10 +1,15 @@\n+\t+change\n+\t 1\n+\t 2\n+\t+\n+\t 3\n+\t 4\n+\t 5\n+\t+\n+\t 6\n+\t 7\n+\t 8\n+\t 9\n+\t+\n+\t 10\n+\t+change\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n+test_expect_success 'ignore-blank-lines: scattered spaces' '\n+\ttest_seq 10 >x &&\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\tchange\n+\t1\n+\t2\n+\t3\n+\n+\t4\n+\n+\t5\n+\n+\t6\n+\n+\t7\n+\n+\t8\n+\t9\n+\t10\n+\tchange\n+\tEOF\n+\tgit diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -1,3 +1,4 @@\n+\t+change\n+\t 1\n+\t 2\n+\t 3\n+\t@@ -8,3 +14,4 @@\n+\t 8\n+\t 9\n+\t 10\n+\t+change\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n+test_expect_success 'ignore-blank-lines: mix changes and blank lines' '\n+\ttest_seq 16 >x &&\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\tchange\n+\t1\n+\t2\n+\n+\t3\n+\t4\n+\t5\n+\tchange\n+\t6\n+\t7\n+\t8\n+\n+\t9\n+\t10\n+\t11\n+\tchange\n+\t12\n+\t13\n+\t14\n+\n+\t15\n+\t16\n+\tchange\n+\tEOF\n+\tgit diff --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -1,8 +1,11 @@\n+\t+change\n+\t 1\n+\t 2\n+\t+\n+\t 3\n+\t 4\n+\t 5\n+\t+change\n+\t 6\n+\t 7\n+\t 8\n+\t@@ -9,8 +13,11 @@\n+\t 9\n+\t 10\n+\t 11\n+\t+change\n+\t 12\n+\t 13\n+\t 14\n+\t+\n+\t 15\n+\t 16\n+\t+change\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n test_expect_success 'check mixed spaces and tabs in indent' '\n\n \t# This is indented with SP HT SP.\ndiff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\nindex 219a3bb..c033991 100644\n--- a/xdiff/xdiff.h\n+++ b/xdiff/xdiff.h\n@@ -39,6 +39,8 @@ extern \"C\" {\n #define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)\n #define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)\n\n+#define XDF_IGNORE_BLANK_LINES (1 << 7)\n+\n #define XDL_EMIT_FUNCNAMES (1 << 0)\n #define XDL_EMIT_COMMON (1 << 1)\n #define XDL_EMIT_FUNCCONTEXT (1 << 2)\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex b2eb6db..2358a2d 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -394,6 +394,7 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,\n \txch->i2 = i2;\n \txch->chg1 = chg1;\n \txch->chg2 = chg2;\n+\txch->ignore = 0;\n\n \treturn xch;\n }\n@@ -544,7 +545,9 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \txdchange_t *xch, *xche;\n\n \tfor (xch = xscr; xch; xch = xche->next) {\n-\t\txche = xdl_get_hunk(xch, xecfg);\n+\t\txche = xdl_get_hunk(&xch, xecfg);\n+\t\tif (!xch)\n+\t\t\tbreak;\n \t\tif (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1,\n \t\t\t\t     xch->i2, xche->i2 + xche->chg2 - xch->i2,\n \t\t\t\t     ecb->priv) < 0)\n@@ -553,6 +556,27 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \treturn 0;\n }\n\n+static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags)\n+{\n+\txdchange_t *xch;\n+\n+\tfor (xch = xscr; xch; xch = xch->next) {\n+\t\tint ignore = 1;\n+\t\txrecord_t **rec;\n+\t\tlong i;\n+\n+\t\trec = &xe->xdf1.recs[xch->i1];\n+\t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n+\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\n+\t\trec = &xe->xdf2.recs[xch->i2];\n+\t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n+\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\n+\t\txch->ignore = ignore;\n+\t}\n+}\n+\n int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \t     xdemitconf_t const *xecfg, xdemitcb_t *ecb) {\n \txdchange_t *xscr;\n@@ -571,6 +595,9 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \t\treturn -1;\n \t}\n \tif (xscr) {\n+\t\tif (xpp->flags & XDF_IGNORE_BLANK_LINES)\n+\t\t\txdl_mark_ignorable(xscr, &xe, xpp->flags);\n+\n \t\tif (ef(&xe, xscr, ecb, xecfg) < 0) {\n\n \t\t\txdl_free_script(xscr);\ndiff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h\nindex 7a92ea9..8b81206 100644\n--- a/xdiff/xdiffi.h\n+++ b/xdiff/xdiffi.h\n@@ -41,6 +41,7 @@ typedef struct s_xdchange {\n \tstruct s_xdchange *next;\n \tlong i1, i2;\n \tlong chg1, chg2;\n+\tint ignore;\n } xdchange_t;\n\n\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex d11dbf9..04b4bb1 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -56,16 +56,49 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *\n /*\n  * Starting at the passed change atom, find the latest change atom to be included\n  * inside the differential hunk according to the specified configuration.\n+ * Also advance xscr if the first changes must be discareded.\n  */\n-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {\n-\txdchange_t *xch, *xchp;\n+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg) {\n+\txdchange_t *xch, *xchp, *lxch;\n \tlong max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;\n+\tlong max_ignorable = xecfg->ctxlen;\n+\tint interesting = 1;\n\n-\tfor (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)\n-\t\tif (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)\n+\t/* remove ignorable changes that are too far before other changes */\n+\tfor (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {\n+\t\txch = xchp->next;\n+\n+\t\tif (xch == NULL ||\n+\t\t    xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable)\n+\t\t\t*xscr = xch;\n+\t}\n+\n+\tif (*xscr == NULL)\n+\t\treturn NULL;\n+\n+\tlxch = *xscr;\n+\n+\tfor (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) {\n+\t\tlong distance = xch->i1 - (xchp->i1 + xchp->chg1);\n+\t\tif (distance > max_common)\n \t\t\tbreak;\n\n-\treturn xchp;\n+\t\tif (distance < max_ignorable && (!xch->ignore || interesting)) {\n+\t\t\tlxch = xch;\n+\t\t\tinteresting = 1;\n+\t\t} else if (!interesting) {\n+\t\t\tif (xch->i1 - (lxch->i1 + lxch->chg1) < max_common)\n+\t\t\t\tcontinue;\n+\t\t\telse\n+\t\t\t\tbreak;\n+\t\t} else if (!xch->ignore) {\n+\t\t\tlxch = xch;\n+\t\t} else {\n+\t\t\tinteresting = 0;\n+\t\t}\n+\t}\n+\n+\treturn lxch;\n }\n\n\n@@ -139,7 +172,9 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \t\treturn xdl_emit_common(xe, xscr, ecb, xecfg);\n\n \tfor (xch = xscr; xch; xch = xche->next) {\n-\t\txche = xdl_get_hunk(xch, xecfg);\n+\t\txche = xdl_get_hunk(&xch, xecfg);\n+\t\tif (!xch)\n+\t\t\tbreak;\n\n \t\ts1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);\n \t\ts2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);\ndiff --git a/xdiff/xemit.h b/xdiff/xemit.h\nindex c2e2e83..d297107 100644\n--- a/xdiff/xemit.h\n+++ b/xdiff/xemit.h\n@@ -27,7 +27,7 @@\n typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \t\t\t   xdemitconf_t const *xecfg);\n\n-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);\n+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg);\n int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \t\t  xdemitconf_t const *xecfg);\n\ndiff --git a/xdiff/xutils.c b/xdiff/xutils.c\nindex 9504eae..62cb23d 100644\n--- a/xdiff/xutils.c\n+++ b/xdiff/xutils.c\n@@ -143,6 +143,19 @@ long xdl_guess_lines(mmfile_t *mf, long sample) {\n \treturn nl + 1;\n }\n\n+int xdl_blankline(const char *line, long size, long flags)\n+{\n+\tlong i;\n+\n+\tif (!(flags & XDF_WHITESPACE_FLAGS))\n+\t\treturn (size <= 1);\n+\n+\tfor (i = 0; i < size && XDL_ISSPACE(line[i]); i++)\n+\t\t;\n+\n+\treturn (i == size);\n+}\n+\n int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)\n {\n \tint i1, i2;\ndiff --git a/xdiff/xutils.h b/xdiff/xutils.h\nindex ad1428e..4646ce5 100644\n--- a/xdiff/xutils.h\n+++ b/xdiff/xutils.h\n@@ -32,6 +32,7 @@ int xdl_cha_init(chastore_t *cha, long isize, long icount);\n void xdl_cha_free(chastore_t *cha);\n void *xdl_cha_alloc(chastore_t *cha);\n long xdl_guess_lines(mmfile_t *mf, long sample);\n+int xdl_blankline(const char *line, long size, long flags);\n int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);\n unsigned long xdl_hash_record(char const **data, char const *top, long flags);\n unsigned int xdl_hashbits(unsigned int size);\n--\n1.7.9.5\n"},{"id":"219940","messageId":"CAPig+cTGwhEEaY3th3++-CU9oeONTLohvY8E_zo25ypViUM=0Q@mail.gmail.com","threadId":"33940","inReplyTo":"1370724291-30088-1-git-send-email-apelisse@gmail.com","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-06-09T07:33:38Z","receivedAt":"2013-06-09T07:33:38Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sat, Jun 8, 2013 at 4:44 PM, Antoine Pelisse <apelisse@gmail.com> wrote:\n> The goal of the patch is to introduce the GNU diff\n> -B/--ignore-blank-lines as closely as possible. The short option is not\n> available because it's already used for \"break-rewrites\".\n>\n> When this option is used, git-diff will not create hunks that simply\n> adds or removes empty lines, but will still show empty lines\n\ns/hunks/a hunk/\n...or...\ns/adds or removes/add or remove/\n\n> addition/suppression if they are close enough to \"valuable\" changes.\n>\n> diff --git a/xdiff/xemit.c b/xdiff/xemit.c\n> index d11dbf9..04b4bb1 100644\n> --- a/xdiff/xemit.c\n> +++ b/xdiff/xemit.c\n> @@ -56,16 +56,49 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *\n>  /*\n>   * Starting at the passed change atom, find the latest change atom to be included\n>   * inside the differential hunk according to the specified configuration.\n> + * Also advance xscr if the first changes must be discareded.\n>   */\n\ns/discareded/discarded/\n"},{"id":"220160","messageId":"7vsj0roxnr.fsf@alter.siamese.dyndns.org","threadId":"33940","inReplyTo":"1370724291-30088-1-git-send-email-apelisse@gmail.com","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-09T20:07:52Z","receivedAt":"2013-06-09T20:07:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Antoine Pelisse <apelisse@gmail.com> writes:\n\n> The goal of the patch is to introduce the GNU diff\n> -B/--ignore-blank-lines as closely as possible. The short option is not\n> available because it's already used for \"break-rewrites\".\n>\n> When this option is used, git-diff will not create hunks that simply\n> adds or removes empty lines, but will still show empty lines\n> addition/suppression if they are close enough to \"valuable\" changes.\n>\n> There are two differences between this option and GNU diff -B option:\n> - GNU diff doesn't have \"--inter-hunk-context\", so this must be handled\n> - The following sequence looks like a bug (context is displayed twice):\n>\n>     $ seq 5 >file1\n>     $ cat <<EOF >file2\n>     change\n>     1\n>     2\n>\n>     3\n>     4\n>     5\n>     change\n>     EOF\n>     $ diff -u -B file1 file2\n>     --- file1\t2013-06-08 22:13:04.471517834 +0200\n>     +++ file2\t2013-06-08 22:13:23.275517855 +0200\n>     @@ -1,5 +1,7 @@\n>     +change\n>      1\n>      2\n>     +\n>      3\n>      4\n>      5\n>     @@ -3,3 +5,4 @@\n>      3\n>      4\n>      5\n>     +change\n\nYes, this is a bug in the previous round, and the approach I\noutlined in the previous message was also designed to address it by\ncoalescing adjacent hunks by measuring the distance correctly.\n\n> Actually it doesn't quite work like that because we don't totally ignore\n> \"blank lines\". We want to keep them if they are close enough to other\n> changes.\n\nA new test vector in your patch is a good illustration of this.\n\n> +test_expect_success 'ignore-blank-lines: after change' '\n> +\ttest_seq 7 >x &&\n> +\tgit update-index x &&\n> +\tcat <<-\\EOF >x &&\n> +\tchange\n> +\t1\n> +\t2\n> +\n> +\t3\n> +\t4\n> +\n> +\t5\n> +\t6\n> +\t7\n> +\n> +\tEOF\n\nThe test makes the original with 1 thru 7 to the above shape.  The\nargument for the behaviour in this patch is that additions of these\nnew blank lines are close enough to the real change of inserting the\nfirst line with \"change\".  \n\nIf you are not interested in changes in additions of blank lines (by\nthe way, do we also handle deletions and do your new tests check\nthem?), one could however argue that the user would want not to see\nthe addition of the blank between 4 and 5 or after 7.\n\nAt first glance, it seems impossible to express that if we need to\nshow three lines of context, in other words, this output\n\n\t@@ -1,2 +1,3 @@\n\t+change\n         1\n         2\n\ncannot be a correct patch output --ignore-blank-lines-change output\nbecause it does not show enough context lines after the real change\n(we want 3 lines).\n \nHowever, let's step back and think what other ignore blank options do.\n\nWhen any ignore blank option is used, there will be lines that\nactually has changes (hence should be shown with +/-) but we\ndeliberately ignore their changes (hence, if they ever appear in the\nhunk, they do so as context lines prefixed with SP not +/-).  When\nwe do so, we show the lines from the postimage in the context.\n\nSo in that sense, showing this would actually be acceptable (the\nlast postcontext line in this hunk is a blank line).\n\n\t@@ -1,3 +1,4 @@\n\t+change\n         1\n         2\n\t  \n\nWe are showing the new blank line the change added after 2 as a\nshared context, following the same principle to show from the\npostimage when we turned a line with a real change into a\nnon-change.\n\n> +\tgit diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&\n> +\tcat <<-\\EOF >expected &&\n> +\tdiff --git a/x b/x\n> +\t--- a/x\n> +\t+++ b/x\n> +\t@@ -1,7 +1,10 @@\n> +\t+change\n> +\t 1\n> +\t 2\n> +\t+\n> +\t 3\n> +\t 4\n> +\t+\n> +\t 5\n> +\t 6\n> +\t 7\n> +\tEOF\n> +\tcompare_diff_patch expected out.tmp\n> +'\n\nAnd from that point of view, this expected output may be excessively\nnoisy.\n\nSo I dunno.\n"},{"id":"220162","messageId":"CALWbr2xijB+UD9gwc+HmMdHM9OT+2Lzr9w3h22=CegKHK-Ocng@mail.gmail.com","threadId":"33940","inReplyTo":"7vsj0roxnr.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-06-09T20:32:39Z","receivedAt":"2013-06-09T20:32:39Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"On Sun, Jun 9, 2013 at 10:07 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> by\n> the way, do we also handle deletions and do your new tests check\n> them?\n\nAs stated in the commit message, yes we should, but we don't have\ntests for that.\nI will need to add some as I think I found a bug when removing blank lines.\n\n>> +     git diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&\n>> +     cat <<-\\EOF >expected &&\n>> +     diff --git a/x b/x\n>> +     --- a/x\n>> +     +++ b/x\n>> +     @@ -1,7 +1,10 @@\n>> +     +change\n>> +      1\n>> +      2\n>> +     +\n>> +      3\n>> +      4\n>> +     +\n>> +      5\n>> +      6\n>> +      7\n>> +     EOF\n>> +     compare_diff_patch expected out.tmp\n>> +'\n>\n> And from that point of view, this expected output may be excessively\n> noisy.\n>\n> So I dunno.\n\nIt might be kind of noisy, but I think trying to improve the solution\nmight lead to over-engineering.\nHow would we compute the \"minimal distance between interesting and\nblank\" so that the blank becomes interesting ?\nUsing the context size for that is quite convenient, while creating\nanother variable would probably become overkill..\n\nThe original goal is to remove hunks created solely for\naddition/suppression, and I think it's what it should do for the\nmoment.\nBut of course, I have no strong opinion about that.\n\nAnd by the way, I have also another bug, so you can expect another\nre-roll (sorry about that, it's more complex than I initially\nthought).\n\nThanks a lot,\nAntoine\n"},{"id":"220169","messageId":"7vtxl7nfcd.fsf@alter.siamese.dyndns.org","threadId":"33940","inReplyTo":"CALWbr2xijB+UD9gwc+HmMdHM9OT+2Lzr9w3h22=CegKHK-Ocng@mail.gmail.com","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-09T21:28:50Z","receivedAt":"2013-06-09T21:28:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Antoine Pelisse <apelisse@gmail.com> writes:\n\n> It might be kind of noisy, but I think trying to improve the solution\n> might lead to over-engineering.\n> How would we compute the \"minimal distance between interesting and\n> blank\" so that the blank becomes interesting ?\n> Using the context size for that is quite convenient, while creating\n> another variable would probably become overkill..\n>\n> The original goal is to remove hunks created solely for\n> addition/suppression, and I think it's what it should do for the\n> moment.\n\nSomething like this on top of your original one is what I had in\nmind as a starting point.\n\n t/t4015-diff-whitespace.sh |  5 +----\n xdiff/xemit.c              | 45 ++++++++++++++++++++++++++-------------------\n 2 files changed, 27 insertions(+), 23 deletions(-)\n\ndiff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\nindex b3c4fcc..acc2159 100755\n--- a/t/t4015-diff-whitespace.sh\n+++ b/t/t4015-diff-whitespace.sh\n@@ -185,7 +185,7 @@ test_expect_success 'ignore-blank-lines: with changes' '\n \tgit diff --ignore-blank-lines >out.tmp &&\n \tsed -e \"1,/^+++ b\\/x/d\" <out.tmp >out &&\n \tcat <<-\\EOF >expect &&\n-\t@@ -1,6 +2,7 @@\n+\t@@ -1,11 +2,14 @@\n \t 1\n \t 2\n \t 3\n@@ -193,9 +193,6 @@ test_expect_success 'ignore-blank-lines: with changes' '\n \t 4\n \t 5\n \t 6\n-\t@@ -5,7 +7,9 @@\n-\t 5\n-\t 6\n \t 7\n \t+\n \t 8\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex 52dfef8..27e1105 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -59,32 +59,39 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *\n  * Also advance xscr if the first changes must be discareded.\n  */\n xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg) {\n-\txdchange_t *xch, *xchp;\n+\txdchange_t *xch, *xchp = NULL, *xch_start = NULL;\n \tlong max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;\n-\tlong ignorable_context = max_common / 2 - 1;\n-\tint interesting = 0;\n \n-\tfor (xchp = *xscr, xch = (*xscr)->next; xch; xchp = xch, xch = xch->next) {\n-\t\tlong thresh;\n-\t\tif (xchp->ignore || xch->ignore)\n-\t\t\tthresh = ignorable_context;\n-\t\telse\n-\t\t\tthresh = max_common;\n-\n-\t\tif (!xchp->ignore)\n-\t\t\tinteresting = 1;\n+\t/* Skip the ones that can be ignored from the beginning */\n+\tfor (xch = *xscr; xch; xch = xch->next) {\n+\t\tif (xch->ignore)\n+\t\t\tcontinue;\n+\t\txch_start = xch;\n+\t\tbreak;\n+\t}\n \n-\t\tif (xch->i1 - (xchp->i1 + xchp->chg1) > thresh) {\n-\t\t\tif (interesting)\n+\tfor (xchp = xch_start; xchp; ) {\n+\t\t/* Find the next one that is not ignored */\n+\t\tfor (xch = xchp->next; xch; xch = xch->next)\n+\t\t\tif (!xch->ignore)\n \t\t\t\tbreak;\n-\t\t\telse\n-\t\t\t\t*xscr = xch;\n+\t\tif (!xch)\n+\t\t\tbreak; /* show xch_start thru xchp */\n+\n+\t\t/* are these hunks close enough? */\n+\t\tif ((xchp->i1 + xchp->chg1) - xch->i1 < max_common) {\n+\t\t\txchp = xch;\n+\t\t\tcontinue;\n \t\t}\n-\t}\n \n-\tif (!interesting && xchp->ignore)\n-\t\t*xscr = NULL;\n+\t\t/*\n+\t\t * otherwise, xchp is the last one (inclusive) we want\n+\t\t * to coalesce into a single output hunk.\n+\t\t */\n+\t\tbreak;\n+\t}\n \n+\t*xscr = xch_start;\n \treturn xchp;\n }\n \n-- \n1.8.3-477-gc2fede3\n"},{"id":"220379","messageId":"CALWbr2zLTUYRnaYxbjyU80zpa3Q6WGcquTPAHqWCJnbEZ7RNPQ@mail.gmail.com","threadId":"33940","inReplyTo":"7vsj0roxnr.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-06-10T21:03:28Z","receivedAt":"2013-06-10T21:03:28Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"On Sun, Jun 9, 2013 at 10:07 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> When any ignore blank option is used, there will be lines that\n> actually has changes (hence should be shown with +/-) but we\n> deliberately ignore their changes (hence, if they ever appear in the\n> hunk, they do so as context lines prefixed with SP not +/-).  When\n> we do so, we show the lines from the postimage in the context.\n\nDon't we actually use preimage (see below) ? I think using pre-image\nallows the patch to be applicable to another tree (but ignoring the\nspace changes).\nIf we actually hide new blank lines that are in the context, it means\nthat we won't be able to apply a patch with 2 new blank lines in the 3\nline context.\n\nAnyway, I'm starting to think that \"show blank lines changes near\nother changes\" makes sense more and more sense.\nBy the way I have a patch I *think* is working, but I will check it\nanother thousand times before sending.\n\nCheers,\nAntoine\n\n$ git diff\ndiff --git a/x b/x\nindex e562137..226e35a 100644\n--- a/x\n+++ b/x\n@@ -4,8 +4,9 @@ change\n 3\n 4\n 5\n-6\n-7\n-8\n-9\n+   6\n+7\n+change\n+  8\n+9\n 10\n\n$ git diff -w\ndiff --git a/x b/x\nindex e562137..226e35a 100644\n--- a/x\n+++ b/x\n@@ -6,6 +6,7 @@ change\n 5\n 6\n 7\n+change\n 8\n 9\n 10\n"},{"id":"220387","messageId":"7vsj0pej5s.fsf@alter.siamese.dyndns.org","threadId":"33940","inReplyTo":"CALWbr2zLTUYRnaYxbjyU80zpa3Q6WGcquTPAHqWCJnbEZ7RNPQ@mail.gmail.com","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-10T21:43:27Z","receivedAt":"2013-06-10T21:43:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Antoine Pelisse <apelisse@gmail.com> writes:\n\n> On Sun, Jun 9, 2013 at 10:07 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> When any ignore blank option is used, there will be lines that\n>> actually has changes (hence should be shown with +/-) but we\n>> deliberately ignore their changes (hence, if they ever appear in the\n>> hunk, they do so as context lines prefixed with SP not +/-).  When\n>> we do so, we show the lines from the postimage in the context.\n>\n> Don't we actually use preimage (see below) ? I think using pre-image\n> allows the patch to be applicable to another tree (but ignoring the\n> space changes).\n\nBut the result of such patch application is not usually what you\nwant to use.  If we use postimage (which by the way was a deliberate\ndesign decision we made earlier), at least the review of the patch\nis easier because you would see the end result more clearly.\n\n> If we actually hide new blank lines that are in the context, it means\n> that we won't be able to apply a patch with 2 new blank lines in the 3\n> line context.\n\nYes, but I do not think the point of --ignore-blank-lines is to\nproduce a patch that can be applied in the first place.  It is to\nallow easier eyeballing.\n\n> Anyway, I'm starting to think that \"show blank lines changes near\n> other changes\" makes sense more and more sense.\n\nProbably.\n"},{"id":"220609","messageId":"CALWbr2zOkoX9NKuZGOWDHyG12xE8ASz+gCK8qwgM1Xqr+uqN7A@mail.gmail.com","threadId":"33940","inReplyTo":"7vsj0pej5s.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-06-12T13:21:44Z","receivedAt":"2013-06-12T13:21:44Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"On Mon, Jun 10, 2013 at 11:43 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Antoine Pelisse <apelisse@gmail.com> writes:\n>\n>> On Sun, Jun 9, 2013 at 10:07 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>>> When any ignore blank option is used, there will be lines that\n>>> actually has changes (hence should be shown with +/-) but we\n>>> deliberately ignore their changes (hence, if they ever appear in the\n>>> hunk, they do so as context lines prefixed with SP not +/-).  When\n>>> we do so, we show the lines from the postimage in the context.\n>>\n>> Don't we actually use preimage (see below) ? I think using pre-image\n>> allows the patch to be applicable to another tree (but ignoring the\n>> space changes).\n\nAnswering to myself: OK, my package version of git is 1.7.9.5 while\nthe post-image is used since 1.7.10 or something. That explains my\nconfusion.\n\n> But the result of such patch application is not usually what you\n> want to use.  If we use postimage (which by the way was a deliberate\n> design decision we made earlier), at least the review of the patch\n> is easier because you would see the end result more clearly.\n\nI've found the patch and discussion [1] about that switch from\npre-image to post-image, so I can understand the motives (and see that\nyou actually considered problems for applying such a patch). I always\nfelt confident that running \"git send-email -w\" would send a patch\n(that can be applied) without the potential space errors/changes I\nwould have added.\n\nI think it's unfortunate that Git does generate patches with git-diff\nthat can't be applied if any space option is used. I'm still not\nreally convinced by the pre-image to post-image change, and maybe I\nwould have made it a non-default option. What is done is done, but I'd\nrather like not do the same here, if possible.\n\n>> If we actually hide new blank lines that are in the context, it means\n>> that we won't be able to apply a patch with 2 new blank lines in the 3\n>> line context.\n>\n> Yes, but I do not think the point of --ignore-blank-lines is to\n> produce a patch that can be applied in the first place.  It is to\n> allow easier eyeballing.\n\nI think it can not be applied because it's *hard* for a computer to\nactually find the correct location, and it may be equally hard for the\nreader to evaluate the change with removed/different context.\n\n>> Anyway, I'm starting to think that \"show blank lines changes near\n>> other changes\" makes sense more and more sense.\n>\n> Probably.\n\nI'm glad to see how convinced you are ;)\n\nI will send my patch and see what makes more sense.\n\n[1]: $gmane/188305\n"},{"id":"220652","messageId":"7vhah35jn8.fsf@alter.siamese.dyndns.org","threadId":"33940","inReplyTo":"CALWbr2zOkoX9NKuZGOWDHyG12xE8ASz+gCK8qwgM1Xqr+uqN7A@mail.gmail.com","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-12T17:22:19Z","receivedAt":"2013-06-12T17:22:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Antoine Pelisse <apelisse@gmail.com> writes:\n\n>>> Anyway, I'm starting to think that \"show blank lines changes near\n>>> other changes\" makes sense more and more sense.\n>>\n>> Probably.\n>\n> I'm glad to see how convinced you are ;)\n\nThat is not me \"not convinced\".\n\nIt is merely \"I do not have a strong conviction that you are wrong;\nI'd have to think it (again) after re-reading your patch.\"\n\nThanks.\n"},{"id":"220940","messageId":"1371301305-30160-1-git-send-email-apelisse@gmail.com","threadId":"33940","inReplyTo":"7vhah35jn8.fsf@alter.siamese.dyndns.org","subject":"[PATCH] diff: add --ignore-blank-lines option","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-06-15T13:01:45Z","receivedAt":"2013-06-15T13:01:45Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"The goal of the patch is to introduce the GNU diff\n-B/--ignore-blank-lines as closely as possible. The short option is not\navailable because it's already used for \"break-rewrites\".\n\nWhen this option is used, git-diff will not create hunks that simply\nadd or remove empty lines, but will still show empty lines\naddition/suppression if they are close enough to \"valuable\" changes.\n\nThere are two differences between this option and GNU diff -B option:\n- GNU diff doesn't have \"--inter-hunk-context\", so this must be handled\n- The following sequence looks like a bug (context is displayed twice):\n\n    $ seq 5 >file1\n    $ cat <<EOF >file2\n    change\n    1\n    2\n\n    3\n    4\n    5\n    change\n    EOF\n    $ diff -u -B file1 file2\n    --- file1\t2013-06-08 22:13:04.471517834 +0200\n    +++ file2\t2013-06-08 22:13:23.275517855 +0200\n    @@ -1,5 +1,7 @@\n    +change\n     1\n     2\n    +\n     3\n     4\n     5\n    @@ -3,3 +5,4 @@\n     3\n     4\n     5\n    +change\n\nSo here is a more thorough description of the option:\n- real changes are interesting\n- blank lines that are close enough (less than context size) to\ninteresting changes are considered interesting (recursive definition)\n- \"context\" lines are used around each hunk of interesting changes\n- If two hunks are separated by less than \"inter-hunk-context\", they\nwill be merged into one.\n\nThe current implementation does the \"interesting changes selection\" in a\nsingle pass.\n\nSigned-off-by: Antoine Pelisse <apelisse@gmail.com>\n---\nHey,\nOK I think this version should be fine (I don't have any bug that comes\nto mind).\nPlease review the \"between changes\" test as I made a choice there that can\nbe controversial.\n\nThanks,\nAntoine\n\n Documentation/diff-options.txt |    3 +\n diff.c                         |    2 +\n t/t4015-diff-whitespace.sh     |  308 ++++++++++++++++++++++++++++++++++++++++\n xdiff/xdiff.h                  |    2 +\n xdiff/xdiffi.c                 |   29 +++-\n xdiff/xdiffi.h                 |    1 +\n xdiff/xemit.c                  |   50 ++++++-\n xdiff/xemit.h                  |    2 +-\n xdiff/xutils.c                 |   13 ++\n xdiff/xutils.h                 |    1 +\n 10 files changed, 403 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex b8a9b86..4e042d9 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -439,6 +439,9 @@ endif::git-format-patch[]\n \tdifferences even if one line has whitespace where the other\n \tline has none.\n\n+--ignore-blank-lines::\n+\tIgnore changes whose lines are all blank.\n+\n --inter-hunk-context=<lines>::\n \tShow the context between diff hunks, up to the specified number\n \tof lines, thereby fusing hunks that are close to each other.\ndiff --git a/diff.c b/diff.c\nindex f0b3e7c..208094f 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -3593,6 +3593,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n \t\tDIFF_XDL_SET(options, IGNORE_WHITESPACE_CHANGE);\n \telse if (!strcmp(arg, \"--ignore-space-at-eol\"))\n \t\tDIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);\n+\telse if (!strcmp(arg, \"--ignore-blank-lines\"))\n+\t\tDIFF_XDL_SET(options, IGNORE_BLANK_LINES);\n \telse if (!strcmp(arg, \"--patience\"))\n \t\toptions->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);\n \telse if (!strcmp(arg, \"--histogram\"))\ndiff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\nindex cc3db13..6ed6934 100755\n--- a/t/t4015-diff-whitespace.sh\n+++ b/t/t4015-diff-whitespace.sh\n@@ -142,6 +142,314 @@ EOF\n git diff --ignore-space-at-eol > out\n test_expect_success 'another test, with --ignore-space-at-eol' 'test_cmp expect out'\n\n+test_expect_success 'ignore-blank-lines: only new lines' '\n+\ttest_seq 5 >x &&\n+\tgit update-index x &&\n+\ttest_seq 5 | sed \"/3/i \\\\\n+\" >x &&\n+\tgit diff --ignore-blank-lines >out &&\n+\t>expect &&\n+\ttest_cmp out expect\n+'\n+\n+test_expect_success 'ignore-blank-lines: only new lines with space' '\n+\ttest_seq 5 >x &&\n+\tgit update-index x &&\n+\ttest_seq 5 | sed \"/3/i \\ \" >x &&\n+\tgit diff -w --ignore-blank-lines >out &&\n+\t>expect &&\n+\ttest_cmp out expect\n+'\n+\n+test_expect_success 'ignore-blank-lines: after change' '\n+\tcat <<-\\EOF >x &&\n+\t1\n+\t2\n+\n+\t3\n+\t4\n+\t5\n+\n+\t6\n+\t7\n+\tEOF\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\tchange\n+\n+\t1\n+\t2\n+\t3\n+\t4\n+\t5\n+\t6\n+\n+\t7\n+\tEOF\n+\tgit diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -1,6 +1,7 @@\n+\t+change\n+\t+\n+\t 1\n+\t 2\n+\t-\n+\t 3\n+\t 4\n+\t 5\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n+test_expect_success 'ignore-blank-lines: before change' '\n+\tcat <<-\\EOF >x &&\n+\t1\n+\t2\n+\n+\t3\n+\t4\n+\t5\n+\t6\n+\t7\n+\tEOF\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\n+\t1\n+\t2\n+\t3\n+\t4\n+\t5\n+\n+\t6\n+\t7\n+\tchange\n+\tEOF\n+\tgit diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -4,5 +4,7 @@\n+\t 3\n+\t 4\n+\t 5\n+\t+\n+\t 6\n+\t 7\n+\t+change\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n+test_expect_success 'ignore-blank-lines: between changes' '\n+\tcat <<-\\EOF >x &&\n+\t1\n+\t2\n+\t3\n+\t4\n+\t5\n+\n+\n+\t6\n+\t7\n+\t8\n+\t9\n+\t10\n+\tEOF\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\tchange\n+\t1\n+\t2\n+\n+\t3\n+\t4\n+\t5\n+\t6\n+\t7\n+\t8\n+\n+\t9\n+\t10\n+\tchange\n+\tEOF\n+\tgit diff --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -1,5 +1,7 @@\n+\t+change\n+\t 1\n+\t 2\n+\t+\n+\t 3\n+\t 4\n+\t 5\n+\t@@ -8,5 +8,7 @@\n+\t 6\n+\t 7\n+\t 8\n+\t+\n+\t 9\n+\t 10\n+\t+change\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n+test_expect_success 'ignore-blank-lines: between changes (with interhunkctx)' '\n+\ttest_seq 10 >x &&\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\tchange\n+\t1\n+\t2\n+\n+\t3\n+\t4\n+\t5\n+\n+\t6\n+\t7\n+\t8\n+\t9\n+\n+\t10\n+\tchange\n+\tEOF\n+\tgit diff --inter-hunk-context=2 --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -1,10 +1,15 @@\n+\t+change\n+\t 1\n+\t 2\n+\t+\n+\t 3\n+\t 4\n+\t 5\n+\t+\n+\t 6\n+\t 7\n+\t 8\n+\t 9\n+\t+\n+\t 10\n+\t+change\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n+test_expect_success 'ignore-blank-lines: scattered spaces' '\n+\ttest_seq 10 >x &&\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\tchange\n+\t1\n+\t2\n+\t3\n+\n+\t4\n+\n+\t5\n+\n+\t6\n+\n+\n+\t7\n+\n+\t8\n+\t9\n+\t10\n+\tchange\n+\tEOF\n+\tgit diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -1,3 +1,4 @@\n+\t+change\n+\t 1\n+\t 2\n+\t 3\n+\t@@ -8,3 +15,4 @@\n+\t 8\n+\t 9\n+\t 10\n+\t+change\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n+test_expect_success 'ignore-blank-lines: mix changes and blank lines' '\n+\ttest_seq 16 >x &&\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\tchange\n+\t1\n+\t2\n+\n+\t3\n+\t4\n+\t5\n+\tchange\n+\t6\n+\t7\n+\t8\n+\n+\t9\n+\t10\n+\t11\n+\tchange\n+\t12\n+\t13\n+\t14\n+\n+\t15\n+\t16\n+\tchange\n+\tEOF\n+\tgit diff --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -1,8 +1,11 @@\n+\t+change\n+\t 1\n+\t 2\n+\t+\n+\t 3\n+\t 4\n+\t 5\n+\t+change\n+\t 6\n+\t 7\n+\t 8\n+\t@@ -9,8 +13,11 @@\n+\t 9\n+\t 10\n+\t 11\n+\t+change\n+\t 12\n+\t 13\n+\t 14\n+\t+\n+\t 15\n+\t 16\n+\t+change\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n test_expect_success 'check mixed spaces and tabs in indent' '\n\n \t# This is indented with SP HT SP.\ndiff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\nindex 219a3bb..c033991 100644\n--- a/xdiff/xdiff.h\n+++ b/xdiff/xdiff.h\n@@ -39,6 +39,8 @@ extern \"C\" {\n #define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)\n #define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)\n\n+#define XDF_IGNORE_BLANK_LINES (1 << 7)\n+\n #define XDL_EMIT_FUNCNAMES (1 << 0)\n #define XDL_EMIT_COMMON (1 << 1)\n #define XDL_EMIT_FUNCCONTEXT (1 << 2)\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex b2eb6db..2358a2d 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -394,6 +394,7 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,\n \txch->i2 = i2;\n \txch->chg1 = chg1;\n \txch->chg2 = chg2;\n+\txch->ignore = 0;\n\n \treturn xch;\n }\n@@ -544,7 +545,9 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \txdchange_t *xch, *xche;\n\n \tfor (xch = xscr; xch; xch = xche->next) {\n-\t\txche = xdl_get_hunk(xch, xecfg);\n+\t\txche = xdl_get_hunk(&xch, xecfg);\n+\t\tif (!xch)\n+\t\t\tbreak;\n \t\tif (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1,\n \t\t\t\t     xch->i2, xche->i2 + xche->chg2 - xch->i2,\n \t\t\t\t     ecb->priv) < 0)\n@@ -553,6 +556,27 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \treturn 0;\n }\n\n+static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags)\n+{\n+\txdchange_t *xch;\n+\n+\tfor (xch = xscr; xch; xch = xch->next) {\n+\t\tint ignore = 1;\n+\t\txrecord_t **rec;\n+\t\tlong i;\n+\n+\t\trec = &xe->xdf1.recs[xch->i1];\n+\t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n+\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\n+\t\trec = &xe->xdf2.recs[xch->i2];\n+\t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n+\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\n+\t\txch->ignore = ignore;\n+\t}\n+}\n+\n int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \t     xdemitconf_t const *xecfg, xdemitcb_t *ecb) {\n \txdchange_t *xscr;\n@@ -571,6 +595,9 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \t\treturn -1;\n \t}\n \tif (xscr) {\n+\t\tif (xpp->flags & XDF_IGNORE_BLANK_LINES)\n+\t\t\txdl_mark_ignorable(xscr, &xe, xpp->flags);\n+\n \t\tif (ef(&xe, xscr, ecb, xecfg) < 0) {\n\n \t\t\txdl_free_script(xscr);\ndiff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h\nindex 7a92ea9..8b81206 100644\n--- a/xdiff/xdiffi.h\n+++ b/xdiff/xdiffi.h\n@@ -41,6 +41,7 @@ typedef struct s_xdchange {\n \tstruct s_xdchange *next;\n \tlong i1, i2;\n \tlong chg1, chg2;\n+\tint ignore;\n } xdchange_t;\n\n\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex d11dbf9..3998a94e 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -56,16 +56,52 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *\n /*\n  * Starting at the passed change atom, find the latest change atom to be included\n  * inside the differential hunk according to the specified configuration.\n+ * Also advance xscr if the first changes must be discarded.\n  */\n-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {\n-\txdchange_t *xch, *xchp;\n+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)\n+{\n+\txdchange_t *xch, *xchp, *lxch;\n \tlong max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;\n+\tlong max_ignorable = xecfg->ctxlen;\n+\tunsigned long changes = ULONG_MAX;\n+\n+\t/* remove ignorable changes that are too far before other changes */\n+\tfor (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {\n+\t\txch = xchp->next;\n+\n+\t\tif (xch == NULL ||\n+\t\t    xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable)\n+\t\t\t*xscr = xch;\n+\t}\n+\n+\tif (*xscr == NULL)\n+\t\treturn NULL;\n+\n+\tlxch = *xscr;\n\n-\tfor (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)\n-\t\tif (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)\n+\tfor (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) {\n+\t\tlong distance = xch->i1 - (xchp->i1 + xchp->chg1);\n+\t\tif (distance > max_common)\n \t\t\tbreak;\n\n-\treturn xchp;\n+\t\tif (distance < max_ignorable &&\n+\t\t    (!xch->ignore || changes == ULONG_MAX)) {\n+\t\t\tlxch = xch;\n+\t\t\tchanges = ULONG_MAX;\n+\t\t} else if (changes != ULONG_MAX &&\n+\t\t\t   xch->i1 + changes - (lxch->i1 + lxch->chg1) > max_common) {\n+\t\t\tbreak;\n+\t\t} else if (!xch->ignore) {\n+\t\t\tlxch = xch;\n+\t\t\tchanges = ULONG_MAX;\n+\t\t} else {\n+\t\t\tif (changes == ULONG_MAX)\n+\t\t\t\tchanges = 0;\n+\t\t\tchanges += xch->chg2;\n+\t\t}\n+\t}\n+\n+\treturn lxch;\n }\n\n\n@@ -139,7 +175,9 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \t\treturn xdl_emit_common(xe, xscr, ecb, xecfg);\n\n \tfor (xch = xscr; xch; xch = xche->next) {\n-\t\txche = xdl_get_hunk(xch, xecfg);\n+\t\txche = xdl_get_hunk(&xch, xecfg);\n+\t\tif (!xch)\n+\t\t\tbreak;\n\n \t\ts1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);\n \t\ts2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);\ndiff --git a/xdiff/xemit.h b/xdiff/xemit.h\nindex c2e2e83..d297107 100644\n--- a/xdiff/xemit.h\n+++ b/xdiff/xemit.h\n@@ -27,7 +27,7 @@\n typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \t\t\t   xdemitconf_t const *xecfg);\n\n-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);\n+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg);\n int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \t\t  xdemitconf_t const *xecfg);\n\ndiff --git a/xdiff/xutils.c b/xdiff/xutils.c\nindex 9504eae..62cb23d 100644\n--- a/xdiff/xutils.c\n+++ b/xdiff/xutils.c\n@@ -143,6 +143,19 @@ long xdl_guess_lines(mmfile_t *mf, long sample) {\n \treturn nl + 1;\n }\n\n+int xdl_blankline(const char *line, long size, long flags)\n+{\n+\tlong i;\n+\n+\tif (!(flags & XDF_WHITESPACE_FLAGS))\n+\t\treturn (size <= 1);\n+\n+\tfor (i = 0; i < size && XDL_ISSPACE(line[i]); i++)\n+\t\t;\n+\n+\treturn (i == size);\n+}\n+\n int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)\n {\n \tint i1, i2;\ndiff --git a/xdiff/xutils.h b/xdiff/xutils.h\nindex ad1428e..4646ce5 100644\n--- a/xdiff/xutils.h\n+++ b/xdiff/xutils.h\n@@ -32,6 +32,7 @@ int xdl_cha_init(chastore_t *cha, long isize, long icount);\n void xdl_cha_free(chastore_t *cha);\n void *xdl_cha_alloc(chastore_t *cha);\n long xdl_guess_lines(mmfile_t *mf, long sample);\n+int xdl_blankline(const char *line, long size, long flags);\n int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);\n unsigned long xdl_hash_record(char const **data, char const *top, long flags);\n unsigned int xdl_hashbits(unsigned int size);\n--\n1.7.9.5\n"},{"id":"221068","messageId":"7vzjuog175.fsf@alter.siamese.dyndns.org","threadId":"33940","inReplyTo":"1371301305-30160-1-git-send-email-apelisse@gmail.com","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-17T16:18:54Z","receivedAt":"2013-06-17T16:18:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Antoine Pelisse <apelisse@gmail.com> writes:\n\n> So here is a more thorough description of the option:\n\n> - real changes are interesting\n\nOK, I think I can understand it.\n\n> - blank lines that are close enough (less than context size) to\n>   interesting changes are considered interesting (recursive definition)\n\nOK.\n\n> - \"context\" lines are used around each hunk of interesting changes\n\nOK.\n\n> - If two hunks are separated by less than \"inter-hunk-context\", they\n>   will be merged into one.\n\nMakes sense.\n\n> The current implementation does the \"interesting changes selection\" in a\n> single pass.\n\n\"current\" meaning \"the code after this patch is applied\"?  Is there\na possible future enhancement hinted here?\n\n> +xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)\n> +{\n> +\txdchange_t *xch, *xchp, *lxch;\n>  \tlong max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;\n> +\tlong max_ignorable = xecfg->ctxlen;\n> +\tunsigned long changes = ULONG_MAX;\n> +\n> +\t/* remove ignorable changes that are too far before other changes */\n> +\tfor (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {\n> +\t\txch = xchp->next;\n> +\n> +\t\tif (xch == NULL ||\n> +\t\t    xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable)\n> +\t\t\t*xscr = xch;\n> +\t}\n\nThis strips leading ignorable ones away until we see an unignorable\none.  Looks sane.\n\n> +\tif (*xscr == NULL)\n> +\t\treturn NULL;\n> +\n> +\tlxch = *xscr;\n\n\"lxch\" remembers the last one that is \"interesting\".\n\n> +\tfor (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) {\n> +\t\tlong distance = xch->i1 - (xchp->i1 + xchp->chg1);\n> +\t\tif (distance > max_common)\n>  \t\t\tbreak;\n\nIf we see large-enough gap, the one we processed last (in xchp) is\nthe end of the current hunk.  Looks sane.\n\n> +\t\tif (distance < max_ignorable &&\n> +\t\t    (!xch->ignore || changes == ULONG_MAX)) {\n> +\t\t\tlxch = xch;\n> +\t\t\tchanges = ULONG_MAX;\n\nThe current one is made into the \"last interesting one we have seen\"\nand the hunk continues, if either (1) the current one is interesting\nby itself, or (2) the last one we saw does not match some\nunexplainable criteria to cause changes set to not ULONG_MAX.\n\nPuzzling.\n\n> +\t\t} else if (changes != ULONG_MAX &&\n> +\t\t\t   xch->i1 + changes - (lxch->i1 + lxch->chg1) > max_common) {\n> +\t\t\tbreak;\n\nIf the last one we saw does not match some unexplainable criteria to\ncause changes set to not ULONG_MAX, and the distance between this\none and the last \"intersting\" one is further than the context, this\none will not be a part of the current hunk.\n\nPuzzling.\n\nCould you add comment to the \"changes\" variable and explain what the\nvariable means?\n\n> +\t\t} else if (!xch->ignore) {\n> +\t\t\tlxch = xch;\n> +\t\t\tchanges = ULONG_MAX;\n\nWhen this change by itself is interesting, it becomes the \"last\ninteresting one\" and the hunk continues.\n\n> +\t\t} else {\n> +\t\t\tif (changes == ULONG_MAX)\n> +\t\t\t\tchanges = 0;\n> +\t\t\tchanges += xch->chg2;\n\nPuzzled beyond guessing.  Also it is curious why here and only here\nwe look at chg2 side of the things, not i1/chg1 in this whole thing.\n"},{"id":"221082","messageId":"CALWbr2w3hjdbsEUO4ta7UyoexTfeZQ864F4UeeAyr0uKvQRXbg@mail.gmail.com","threadId":"33940","inReplyTo":"7vzjuog175.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-06-17T17:58:43Z","receivedAt":"2013-06-17T17:58:43Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"On Mon, Jun 17, 2013 at 6:18 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Antoine Pelisse <apelisse@gmail.com> writes:\n>\n>> So here is a more thorough description of the option:\n>\n>> - real changes are interesting\n>\n> OK, I think I can understand it.\n>\n>> - blank lines that are close enough (less than context size) to\n>>   interesting changes are considered interesting (recursive definition)\n>\n> OK.\n>\n>> - \"context\" lines are used around each hunk of interesting changes\n>\n> OK.\n>\n>> - If two hunks are separated by less than \"inter-hunk-context\", they\n>>   will be merged into one.\n>\n> Makes sense.\n>\n>> The current implementation does the \"interesting changes selection\" in a\n>> single pass.\n>\n> \"current\" meaning \"the code after this patch is applied\"?  Is there\n> a possible future enhancement hinted here?\n>\n>> +xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)\n>> +{\n>> +     xdchange_t *xch, *xchp, *lxch;\n>>       long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;\n>> +     long max_ignorable = xecfg->ctxlen;\n>> +     unsigned long changes = ULONG_MAX;\n>> +\n>> +     /* remove ignorable changes that are too far before other changes */\n>> +     for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {\n>> +             xch = xchp->next;\n>> +\n>> +             if (xch == NULL ||\n>> +                 xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable)\n>> +                     *xscr = xch;\n>> +     }\n>\n> This strips leading ignorable ones away until we see an unignorable\n> one.  Looks sane.\n>\n>> +     if (*xscr == NULL)\n>> +             return NULL;\n>> +\n>> +     lxch = *xscr;\n>\n> \"lxch\" remembers the last one that is \"interesting\".\n>\n>> +     for (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) {\n>> +             long distance = xch->i1 - (xchp->i1 + xchp->chg1);\n>> +             if (distance > max_common)\n>>                       break;\n>\n> If we see large-enough gap, the one we processed last (in xchp) is\n> the end of the current hunk.  Looks sane.\n>\n>> +             if (distance < max_ignorable &&\n>> +                 (!xch->ignore || changes == ULONG_MAX)) {\n>> +                     lxch = xch;\n>> +                     changes = ULONG_MAX;\n>\n> The current one is made into the \"last interesting one we have seen\"\n> and the hunk continues, if either (1) the current one is interesting\n> by itself, or (2) the last one we saw does not match some\n> unexplainable criteria to cause changes set to not ULONG_MAX.\n>\n> Puzzling.\n>\n>> +             } else if (changes != ULONG_MAX &&\n>> +                        xch->i1 + changes - (lxch->i1 + lxch->chg1) > max_common) {\n>> +                     break;\n>\n> If the last one we saw does not match some unexplainable criteria to\n> cause changes set to not ULONG_MAX, and the distance between this\n> one and the last \"intersting\" one is further than the context, this\n> one will not be a part of the current hunk.\n>\n> Puzzling.\n>\n> Could you add comment to the \"changes\" variable and explain what the\n> variable means?\n>\n>> +             } else if (!xch->ignore) {\n>> +                     lxch = xch;\n>> +                     changes = ULONG_MAX;\n>\n> When this change by itself is interesting, it becomes the \"last\n> interesting one\" and the hunk continues.\n>\n>> +             } else {\n>> +                     if (changes == ULONG_MAX)\n>> +                             changes = 0;\n>> +                     changes += xch->chg2;\n>\n> Puzzled beyond guessing.  Also it is curious why here and only here\n> we look at chg2 side of the things, not i1/chg1 in this whole thing.\n"},{"id":"221088","messageId":"CALWbr2zM=rD3GE9a=Xyrvz0E5mAMsDesJu8-Zs7JH7W4U4AbeA@mail.gmail.com","threadId":"33940","inReplyTo":"7vzjuog175.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-06-17T19:09:59Z","receivedAt":"2013-06-17T19:09:59Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"On Mon, Jun 17, 2013 at 6:18 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Antoine Pelisse <apelisse@gmail.com> writes:\n>\n>> So here is a more thorough description of the option:\n>\n>> - real changes are interesting\n>\n> OK, I think I can understand it.\n>\n>> - blank lines that are close enough (less than context size) to\n>>   interesting changes are considered interesting (recursive definition)\n>\n> OK.\n>\n>> - \"context\" lines are used around each hunk of interesting changes\n>\n> OK.\n>\n>> - If two hunks are separated by less than \"inter-hunk-context\", they\n>>   will be merged into one.\n>\n> Makes sense.\n>\n>> The current implementation does the \"interesting changes selection\" in a\n>> single pass.\n>\n> \"current\" meaning \"the code after this patch is applied\"?  Is there\n> a possible future enhancement hinted here?\n\nNo. There might be, but I'm not sure it should be discussed right now\n(In case you're curious, I'm thinking about interaction with combined\ndiff). I will take the hint and rephrase.\n\n>> +xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)\n>> +{\n>> +     xdchange_t *xch, *xchp, *lxch;\n>>       long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;\n>> +     long max_ignorable = xecfg->ctxlen;\n>> +     unsigned long changes = ULONG_MAX;\n\nLet me explain what \"changes\" means, as I know it will help the rest\nof the message:\nIt counts the number of *added* blank lines we have ignored since\n\"lxch\" (needed to calculate the distance between lxch and xch)\nIt also has the meaning of what was called \"interesting\" before.\nIf changes == ULONG_MAX, we are still in interesting zone, otherwise\nit means we have ignored \"changes\" *added* blank lines (0 being a\nvalid value).\n(Actually, After rereading this part, it looks like I could check that\nlxch == xchp rather than setting changes to ULONG_MAX).\n\n>> +\n>> +     /* remove ignorable changes that are too far before other changes */\n>> +     for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {\n>> +             xch = xchp->next;\n>> +\n>> +             if (xch == NULL ||\n>> +                 xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable)\n>> +                     *xscr = xch;\n>> +     }\n>\n> This strips leading ignorable ones away until we see an unignorable\n> one.  Looks sane.\n>\n>> +     if (*xscr == NULL)\n>> +             return NULL;\n>> +\n>> +     lxch = *xscr;\n>\n> \"lxch\" remembers the last one that is \"interesting\".\n>\n>> +     for (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) {\n>> +             long distance = xch->i1 - (xchp->i1 + xchp->chg1);\n>> +             if (distance > max_common)\n>>                       break;\n>\n> If we see large-enough gap, the one we processed last (in xchp) is\n> the end of the current hunk.  Looks sane.\n>\n>> +             if (distance < max_ignorable &&\n>> +                 (!xch->ignore || changes == ULONG_MAX)) {\n>> +                     lxch = xch;\n>> +                     changes = ULONG_MAX;\n>\n> The current one is made into the \"last interesting one we have seen\"\n> and the hunk continues, if either (1) the current one is interesting\n> by itself, or (2) the last one we saw does not match some\n> unexplainable criteria to cause changes set to not ULONG_MAX.\n>\n> Puzzling.\n\n- If we are still in interesting zone, we take it, even if it's\nignorable change. Because it's close enough.\n- Otherwise, only take real changes. We are close to another change,\nand we are still in the loop, so it must be interesting.\n\n>> +             } else if (changes != ULONG_MAX &&\n>> +                        xch->i1 + changes - (lxch->i1 + lxch->chg1) > max_common) {\n>> +                     break;\n>\n> If the last one we saw does not match some unexplainable criteria to\n> cause changes set to not ULONG_MAX, and the distance between this\n> one and the last \"intersting\" one is further than the context, this\n> one will not be a part of the current hunk.\n>\n> Puzzling.\n\nIf we are no longer in \"interesting zone\" (changes != ULONG_MAX), it\nmeans we will stop if the distance is too big.\n\"changes\" is used in the calculation to consider the changes we have\nalready ignored (xch->i1 - (lxch->i1 + lxch->chg1) will only work if\nxch and lxch are consecutive, we need to add the blank lines we\nignored).\n\n> Could you add comment to the \"changes\" variable and explain what the\n> variable means?\n>\n>> +             } else if (!xch->ignore) {\n>> +                     lxch = xch;\n>> +                     changes = ULONG_MAX;\n>\n> When this change by itself is interesting, it becomes the \"last\n> interesting one\" and the hunk continues.\n\nExactly, and changes goes back to \"interesting\".\n\n>> +             } else {\n>> +                     if (changes == ULONG_MAX)\n>> +                             changes = 0;\n>> +                     changes += xch->chg2;\n>\n> Puzzled beyond guessing.  Also it is curious why here and only here\n> we look at chg2 side of the things, not i1/chg1 in this whole thing.\n\nchg2 being the number of blank line *additions*.\nI don't want to coalesce two hunks because some blank lines have been\nremoved between the two, so we must not change the distance\ncalculation because of a blank line removal. That behavior can be seen\nin \"ignore-blank-lines: between changes\" test.\n\nHope that makes things clearer,\nThanks again for the thorough reading,\n\nAntoine\n"},{"id":"221094","messageId":"7vd2rkcy6r.fsf@alter.siamese.dyndns.org","threadId":"33940","inReplyTo":"CALWbr2zM=rD3GE9a=Xyrvz0E5mAMsDesJu8-Zs7JH7W4U4AbeA@mail.gmail.com","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-17T19:52:12Z","receivedAt":"2013-06-17T19:52:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Antoine Pelisse <apelisse@gmail.com> writes:\n\n>>> +     unsigned long changes = ULONG_MAX;\n>\n> Let me explain what \"changes\" means, as I know it will help the rest\n> of the message:\n> It counts the number of *added* blank lines we have ignored since\n> \"lxch\" (needed to calculate the distance between lxch and xch)\n> It also has the meaning of what was called \"interesting\" before.\n> If changes == ULONG_MAX, we are still in interesting zone, otherwise\n> it means we have ignored \"changes\" *added* blank lines (0 being a\n> valid value).\n\nOK.  That deserves a comment next to this variable.\n\n> (Actually, After rereading this part, it looks like I could check that\n> lxch == xchp rather than setting changes to ULONG_MAX).\n\nYeah, I think so.\n\n>>> +             if (distance < max_ignorable &&\n>>> +                 (!xch->ignore || changes == ULONG_MAX)) {\n>>> +                     lxch = xch;\n>>> +                     changes = ULONG_MAX;\n>>\n> - If we are still in interesting zone, we take it, even if it's\n> ignorable change. Because it's close enough.\n> - Otherwise, only take real changes. We are close to another change,\n> and we are still in the loop, so it must be interesting.\n\nOK.\n\n>>> +             } else if (changes != ULONG_MAX &&\n>>> +                        xch->i1 + changes - (lxch->i1 + lxch->chg1) > max_common) {\n>>> +                     break;\n>\n> If we are no longer in \"interesting zone\" (changes != ULONG_MAX), it\n> means we will stop if the distance is too big.\n> \"changes\" is used in the calculation to consider the changes we have\n> already ignored (xch->i1 - (lxch->i1 + lxch->chg1) will only work if\n> xch and lxch are consecutive, we need to add the blank lines we\n> ignored).\n\nAnd this uses max_common that is much larger than max_ignorable\nbecause...?\n\nThe last interesting change, with its post context and inter hunk\ngap, together with precontext for this one, is close enough to the\nbeginning of this one.  So it is understandable if xch by itself is\nintereseting to use max_common.  Even an interesting one, if that is\nso far from the last interesting one, should not be part of this\nhunk.\n\nHowever, if the current one is by itself uninteresting, should we\nstill use the max_common, or should this be compared with\nmax_ignorable?\n    \n>> Could you add comment to the \"changes\" variable and explain what the\n>> variable means?\n>>\n>>> +             } else if (!xch->ignore) {\n>>> +                     lxch = xch;\n>>> +                     changes = ULONG_MAX;\n>>\n>> When this change by itself is interesting, it becomes the \"last\n>> interesting one\" and the hunk continues.\n>\n> Exactly, and changes goes back to \"interesting\".\n>\n>>> +             } else {\n>>> +                     if (changes == ULONG_MAX)\n>>> +                             changes = 0;\n>>> +                     changes += xch->chg2;\n>>\n>> Puzzled beyond guessing.  Also it is curious why here and only here\n>> we look at chg2 side of the things, not i1/chg1 in this whole thing.\n>\n> chg2 being the number of blank line *additions*.\n\nThis is on the else side of if (!xch->ignore), so we are looking at\nignored hunk, which means there is only blank line change.  Can chg2\nbe 0 while chg1 is not zero, i.e. xch being a blank line removal?\n\nWhat should happen in that case?  Don't we want to show it, for the\nsame reason we want to keep removal, as long as it is close enough\nto the interesting zone?\n\n> Hope that makes things clearer,\n\nYes, it helped quite a bit.\n"},{"id":"221106","messageId":"CALWbr2x0gxQ8boXEa3WJUbaA8e9imt9Ri_NFmANEhJuK6Moi+A@mail.gmail.com","threadId":"33940","inReplyTo":"7vd2rkcy6r.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-06-17T21:33:08Z","receivedAt":"2013-06-17T21:33:08Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":">>>> +             } else if (changes != ULONG_MAX &&\n>>>> +                        xch->i1 + changes - (lxch->i1 + lxch->chg1) > max_common) {\n>>>> +                     break;\n>>\n>> If we are no longer in \"interesting zone\" (changes != ULONG_MAX), it\n>> means we will stop if the distance is too big.\n>> \"changes\" is used in the calculation to consider the changes we have\n>> already ignored (xch->i1 - (lxch->i1 + lxch->chg1) will only work if\n>> xch and lxch are consecutive, we need to add the blank lines we\n>> ignored).\n>\n> And this uses max_common that is much larger than max_ignorable\n> because...?\n>\n> The last interesting change, with its post context and inter hunk\n> gap, together with precontext for this one, is close enough to the\n> beginning of this one.  So it is understandable if xch by itself is\n> intereseting to use max_common.  Even an interesting one, if that is\n> so far from the last interesting one, should not be part of this\n> hunk.\n>\n> However, if the current one is by itself uninteresting, should we\n> still use the max_common, or should this be compared with\n> max_ignorable?\n\nBecause of the \"recursive definition\", we don't know yet if an\nignorable change will be interesting or not.\nWe need to make sure it will be close to another interesting change first.\nIf it is, it will fall in the first if part, and lxch will catch-up.\nIf not, we will eventually be too far and break.\n\nRe-reading note: OK, This last sentence (\"If not we will eventually be\ntoo far and break\") is actually a bug. We might break before we find\nsomething interesting while we should keep going. For example in such\na case, we should display like this, but won't:\n\n@@ -x,x +x,x @@\n+change   <--- That is lxch\n 1\n 2\n 3\n+       <--- Here we leave \"interesting\"\n 4\n 5\n+       <--- We are too far and quit searching\n 6\n 7\n+\n 8\n 9\n+\n 10\n 11\n+change\n\n>>>> +             } else {\n>>>> +                     if (changes == ULONG_MAX)\n>>>> +                             changes = 0;\n>>>> +                     changes += xch->chg2;\n>>>\n>>> Puzzled beyond guessing.  Also it is curious why here and only here\n>>> we look at chg2 side of the things, not i1/chg1 in this whole thing.\n>>\n>> chg2 being the number of blank line *additions*.\n>\n> This is on the else side of if (!xch->ignore), so we are looking at\n> ignored hunk, which means there is only blank line change.  Can chg2\n> be 0 while chg1 is not zero, i.e. xch being a blank line removal?\n\nExactly. It can be a blank line removal. But I don't want to consider\nit in the calculation.\nHere's why:\nWe have:\n1\n2\n3\n\n\n\n\n4\n5\n6\n\nand change it to:\nchange\n1\n2\n3\n4\n5\n6\nchange\n\nWhat should be the output of diff --ignore-blank-lines ?\n\nI chose this alternative:\n@@ -1,3 +1,4 @@\n+change\n 1\n 2\n 3\n@@ -7,3 +5,4 @@\n 4\n 5\n 6\n+change\n\nWhile one could have chosen:\n@@ -1,10 +1,8 @@\n+change\n 1\n 2\n 3\n-\n-\n-\n-\n 4\n 5\n 6\n+change\n\n> What should happen in that case?  Don't we want to show it, for the\n> same reason we want to keep removal, as long as it is close enough\n> to the interesting zone?\n\nNothing is interesting here, we just leave the interesting zone (if\nnot already left) because everything else failed.\n"},{"id":"221113","messageId":"7v38sgb9o8.fsf@alter.siamese.dyndns.org","threadId":"33940","inReplyTo":"CALWbr2x0gxQ8boXEa3WJUbaA8e9imt9Ri_NFmANEhJuK6Moi+A@mail.gmail.com","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-17T23:27:03Z","receivedAt":"2013-06-17T23:27:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Antoine Pelisse <apelisse@gmail.com> writes:\n\n> Re-reading note: OK, This last sentence (\"If not we will eventually be\n> too far and break\") is actually a bug. We might break before we find\n> something interesting while we should keep going. For example in such\n> a case, we should display like this, but won't:\n\nGlad to see that my question has helped ;-)\n\n>> This is on the else side of if (!xch->ignore), so we are looking at\n>> ignored hunk, which means there is only blank line change.  Can chg2\n>> be 0 while chg1 is not zero, i.e. xch being a blank line removal?\n>\n> Exactly. It can be a blank line removal. But I don't want to consider\n> it in the calculation.\n> Here's why:\n> ...\n> What should be the output of diff --ignore-blank-lines ?\n>\n> I chose this alternative:\n> @@ -1,3 +1,4 @@\n> +change\n>  1\n>  2\n>  3\n> @@ -7,3 +5,4 @@\n>  4\n>  5\n>  6\n> +change\n>\n> While one could have chosen:\n> @@ -1,10 +1,8 @@\n> +change\n>  1\n>  2\n>  3\n> -\n> -\n> -\n> -\n>  4\n>  5\n>  6\n> +change\n> ...\n> Nothing is interesting here, we just leave the interesting zone (if\n> not already left) because everything else failed.\n\nYes, that asymmetry is what I was wondering if we want to have.  If\nwe show additional blanks as a significant event, I am not so sure\nwe can say \"Nothing is interesting here\".\n\nI do not feel strongly either way, but it just felt somewhat\ninconsistent.\n\nThanks.\n"},{"id":"221396","messageId":"1371667567-1907-1-git-send-email-apelisse@gmail.com","threadId":"33940","inReplyTo":"7v38sgb9o8.fsf@alter.siamese.dyndns.org","subject":"[PATCH] diff: add --ignore-blank-lines option","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-06-19T18:46:07Z","receivedAt":"2013-06-19T18:46:07Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"The goal of the patch is to introduce the GNU diff\n-B/--ignore-blank-lines as closely as possible. The short option is not\navailable because it's already used for \"break-rewrites\".\n\nWhen this option is used, git-diff will not create hunks that simply\nadd or remove empty lines, but will still show empty lines\naddition/suppression if they are close enough to \"valuable\" changes.\n\nThere are two differences between this option and GNU diff -B option:\n- GNU diff doesn't have \"--inter-hunk-context\", so this must be handled\n- The following sequence looks like a bug (context is displayed twice):\n\n    $ seq 5 >file1\n    $ cat <<EOF >file2\n    change\n    1\n    2\n\n    3\n    4\n    5\n    change\n    EOF\n    $ diff -u -B file1 file2\n    --- file1\t2013-06-08 22:13:04.471517834 +0200\n    +++ file2\t2013-06-08 22:13:23.275517855 +0200\n    @@ -1,5 +1,7 @@\n    +change\n     1\n     2\n    +\n     3\n     4\n     5\n    @@ -3,3 +5,4 @@\n     3\n     4\n     5\n    +change\n\nSo here is a more thorough description of the option:\n- real changes are interesting\n- blank lines that are close enough (less than context size) to\ninteresting changes are considered interesting (recursive definition)\n- \"context\" lines are used around each hunk of interesting changes\n- If two hunks are separated by less than \"inter-hunk-context\", they\nwill be merged into one.\n\nThe implementation does the \"interesting changes selection\" in a single\npass.\n\nSigned-off-by: Antoine Pelisse <apelisse@gmail.com>\n---\nChanges since last version:\n- \"changes\" variable has been renamed to \"ignored\"\n- \"lxch == xchp\" is used instead of setting \"changes\" to a specific\nvalue (ULONG_MAX)\n- Fixed commit message\n- Fixed bug we discussed earlier:\nI added a new \"else if\" branch to handle that specific case (short-cut the bug)\nI'm unfortunately duplicating some logic, but I don't see any way out of this.\n- Added a test for the bug\n\n Documentation/diff-options.txt |    3 +\n diff.c                         |    2 +\n t/t4015-diff-whitespace.sh     |  345 ++++++++++++++++++++++++++++++++++++++++\n xdiff/xdiff.h                  |    2 +\n xdiff/xdiffi.c                 |   29 +++-\n xdiff/xdiffi.h                 |    1 +\n xdiff/xemit.c                  |   49 +++++-\n xdiff/xemit.h                  |    2 +-\n xdiff/xutils.c                 |   13 ++\n xdiff/xutils.h                 |    1 +\n 10 files changed, 439 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex b8a9b86..4e042d9 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -439,6 +439,9 @@ endif::git-format-patch[]\n \tdifferences even if one line has whitespace where the other\n \tline has none.\n\n+--ignore-blank-lines::\n+\tIgnore changes whose lines are all blank.\n+\n --inter-hunk-context=<lines>::\n \tShow the context between diff hunks, up to the specified number\n \tof lines, thereby fusing hunks that are close to each other.\ndiff --git a/diff.c b/diff.c\nindex f0b3e7c..208094f 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -3593,6 +3593,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n \t\tDIFF_XDL_SET(options, IGNORE_WHITESPACE_CHANGE);\n \telse if (!strcmp(arg, \"--ignore-space-at-eol\"))\n \t\tDIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);\n+\telse if (!strcmp(arg, \"--ignore-blank-lines\"))\n+\t\tDIFF_XDL_SET(options, IGNORE_BLANK_LINES);\n \telse if (!strcmp(arg, \"--patience\"))\n \t\toptions->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);\n \telse if (!strcmp(arg, \"--histogram\"))\ndiff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\nindex cc3db13..3fb4b97 100755\n--- a/t/t4015-diff-whitespace.sh\n+++ b/t/t4015-diff-whitespace.sh\n@@ -142,6 +142,351 @@ EOF\n git diff --ignore-space-at-eol > out\n test_expect_success 'another test, with --ignore-space-at-eol' 'test_cmp expect out'\n\n+test_expect_success 'ignore-blank-lines: only new lines' '\n+\ttest_seq 5 >x &&\n+\tgit update-index x &&\n+\ttest_seq 5 | sed \"/3/i \\\\\n+\" >x &&\n+\tgit diff --ignore-blank-lines >out &&\n+\t>expect &&\n+\ttest_cmp out expect\n+'\n+\n+test_expect_success 'ignore-blank-lines: only new lines with space' '\n+\ttest_seq 5 >x &&\n+\tgit update-index x &&\n+\ttest_seq 5 | sed \"/3/i \\ \" >x &&\n+\tgit diff -w --ignore-blank-lines >out &&\n+\t>expect &&\n+\ttest_cmp out expect\n+'\n+\n+test_expect_success 'ignore-blank-lines: after change' '\n+\tcat <<-\\EOF >x &&\n+\t1\n+\t2\n+\n+\t3\n+\t4\n+\t5\n+\n+\t6\n+\t7\n+\tEOF\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\tchange\n+\n+\t1\n+\t2\n+\t3\n+\t4\n+\t5\n+\t6\n+\n+\t7\n+\tEOF\n+\tgit diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -1,6 +1,7 @@\n+\t+change\n+\t+\n+\t 1\n+\t 2\n+\t-\n+\t 3\n+\t 4\n+\t 5\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n+test_expect_success 'ignore-blank-lines: before change' '\n+\tcat <<-\\EOF >x &&\n+\t1\n+\t2\n+\n+\t3\n+\t4\n+\t5\n+\t6\n+\t7\n+\tEOF\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\n+\t1\n+\t2\n+\t3\n+\t4\n+\t5\n+\n+\t6\n+\t7\n+\tchange\n+\tEOF\n+\tgit diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -4,5 +4,7 @@\n+\t 3\n+\t 4\n+\t 5\n+\t+\n+\t 6\n+\t 7\n+\t+change\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n+test_expect_success 'ignore-blank-lines: between changes' '\n+\tcat <<-\\EOF >x &&\n+\t1\n+\t2\n+\t3\n+\t4\n+\t5\n+\n+\n+\t6\n+\t7\n+\t8\n+\t9\n+\t10\n+\tEOF\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\tchange\n+\t1\n+\t2\n+\n+\t3\n+\t4\n+\t5\n+\t6\n+\t7\n+\t8\n+\n+\t9\n+\t10\n+\tchange\n+\tEOF\n+\tgit diff --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -1,5 +1,7 @@\n+\t+change\n+\t 1\n+\t 2\n+\t+\n+\t 3\n+\t 4\n+\t 5\n+\t@@ -8,5 +8,7 @@\n+\t 6\n+\t 7\n+\t 8\n+\t+\n+\t 9\n+\t 10\n+\t+change\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n+test_expect_success 'ignore-blank-lines: between changes (with interhunkctx)' '\n+\ttest_seq 10 >x &&\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\tchange\n+\t1\n+\t2\n+\n+\t3\n+\t4\n+\t5\n+\n+\t6\n+\t7\n+\t8\n+\t9\n+\n+\t10\n+\tchange\n+\tEOF\n+\tgit diff --inter-hunk-context=2 --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -1,10 +1,15 @@\n+\t+change\n+\t 1\n+\t 2\n+\t+\n+\t 3\n+\t 4\n+\t 5\n+\t+\n+\t 6\n+\t 7\n+\t 8\n+\t 9\n+\t+\n+\t 10\n+\t+change\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n+test_expect_success 'ignore-blank-lines: scattered spaces' '\n+\ttest_seq 10 >x &&\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\tchange\n+\t1\n+\t2\n+\t3\n+\n+\t4\n+\n+\t5\n+\n+\t6\n+\n+\n+\t7\n+\n+\t8\n+\t9\n+\t10\n+\tchange\n+\tEOF\n+\tgit diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -1,3 +1,4 @@\n+\t+change\n+\t 1\n+\t 2\n+\t 3\n+\t@@ -8,3 +15,4 @@\n+\t 8\n+\t 9\n+\t 10\n+\t+change\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n+test_expect_success 'ignore-blank-lines: spaces coalesce' '\n+\ttest_seq 6 >x &&\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\tchange\n+\t1\n+\t2\n+\t3\n+\n+\t4\n+\n+\t5\n+\n+\t6\n+\tchange\n+\tEOF\n+\tgit diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -1,6 +1,11 @@\n+\t+change\n+\t 1\n+\t 2\n+\t 3\n+\t+\n+\t 4\n+\t+\n+\t 5\n+\t+\n+\t 6\n+\t+change\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n+test_expect_success 'ignore-blank-lines: mix changes and blank lines' '\n+\ttest_seq 16 >x &&\n+\tgit update-index x &&\n+\tcat <<-\\EOF >x &&\n+\tchange\n+\t1\n+\t2\n+\n+\t3\n+\t4\n+\t5\n+\tchange\n+\t6\n+\t7\n+\t8\n+\n+\t9\n+\t10\n+\t11\n+\tchange\n+\t12\n+\t13\n+\t14\n+\n+\t15\n+\t16\n+\tchange\n+\tEOF\n+\tgit diff --ignore-blank-lines >out.tmp &&\n+\tcat <<-\\EOF >expected &&\n+\tdiff --git a/x b/x\n+\t--- a/x\n+\t+++ b/x\n+\t@@ -1,8 +1,11 @@\n+\t+change\n+\t 1\n+\t 2\n+\t+\n+\t 3\n+\t 4\n+\t 5\n+\t+change\n+\t 6\n+\t 7\n+\t 8\n+\t@@ -9,8 +13,11 @@\n+\t 9\n+\t 10\n+\t 11\n+\t+change\n+\t 12\n+\t 13\n+\t 14\n+\t+\n+\t 15\n+\t 16\n+\t+change\n+\tEOF\n+\tcompare_diff_patch expected out.tmp\n+'\n+\n test_expect_success 'check mixed spaces and tabs in indent' '\n\n \t# This is indented with SP HT SP.\ndiff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\nindex 219a3bb..c033991 100644\n--- a/xdiff/xdiff.h\n+++ b/xdiff/xdiff.h\n@@ -39,6 +39,8 @@ extern \"C\" {\n #define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)\n #define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)\n\n+#define XDF_IGNORE_BLANK_LINES (1 << 7)\n+\n #define XDL_EMIT_FUNCNAMES (1 << 0)\n #define XDL_EMIT_COMMON (1 << 1)\n #define XDL_EMIT_FUNCCONTEXT (1 << 2)\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex b2eb6db..2358a2d 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -394,6 +394,7 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,\n \txch->i2 = i2;\n \txch->chg1 = chg1;\n \txch->chg2 = chg2;\n+\txch->ignore = 0;\n\n \treturn xch;\n }\n@@ -544,7 +545,9 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \txdchange_t *xch, *xche;\n\n \tfor (xch = xscr; xch; xch = xche->next) {\n-\t\txche = xdl_get_hunk(xch, xecfg);\n+\t\txche = xdl_get_hunk(&xch, xecfg);\n+\t\tif (!xch)\n+\t\t\tbreak;\n \t\tif (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1,\n \t\t\t\t     xch->i2, xche->i2 + xche->chg2 - xch->i2,\n \t\t\t\t     ecb->priv) < 0)\n@@ -553,6 +556,27 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \treturn 0;\n }\n\n+static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags)\n+{\n+\txdchange_t *xch;\n+\n+\tfor (xch = xscr; xch; xch = xch->next) {\n+\t\tint ignore = 1;\n+\t\txrecord_t **rec;\n+\t\tlong i;\n+\n+\t\trec = &xe->xdf1.recs[xch->i1];\n+\t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n+\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\n+\t\trec = &xe->xdf2.recs[xch->i2];\n+\t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n+\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\n+\t\txch->ignore = ignore;\n+\t}\n+}\n+\n int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \t     xdemitconf_t const *xecfg, xdemitcb_t *ecb) {\n \txdchange_t *xscr;\n@@ -571,6 +595,9 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \t\treturn -1;\n \t}\n \tif (xscr) {\n+\t\tif (xpp->flags & XDF_IGNORE_BLANK_LINES)\n+\t\t\txdl_mark_ignorable(xscr, &xe, xpp->flags);\n+\n \t\tif (ef(&xe, xscr, ecb, xecfg) < 0) {\n\n \t\t\txdl_free_script(xscr);\ndiff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h\nindex 7a92ea9..8b81206 100644\n--- a/xdiff/xdiffi.h\n+++ b/xdiff/xdiffi.h\n@@ -41,6 +41,7 @@ typedef struct s_xdchange {\n \tstruct s_xdchange *next;\n \tlong i1, i2;\n \tlong chg1, chg2;\n+\tint ignore;\n } xdchange_t;\n\n\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex d11dbf9..3854fc7 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -56,16 +56,51 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *\n /*\n  * Starting at the passed change atom, find the latest change atom to be included\n  * inside the differential hunk according to the specified configuration.\n+ * Also advance xscr if the first changes must be discarded.\n  */\n-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {\n-\txdchange_t *xch, *xchp;\n+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)\n+{\n+\txdchange_t *xch, *xchp, *lxch;\n \tlong max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;\n+\tlong max_ignorable = xecfg->ctxlen;\n+\tunsigned long ignored = 0; /* number of ignored blank lines */\n+\n+\t/* remove ignorable changes that are too far before other changes */\n+\tfor (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {\n+\t\txch = xchp->next;\n+\n+\t\tif (xch == NULL ||\n+\t\t    xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable)\n+\t\t\t*xscr = xch;\n+\t}\n+\n+\tif (*xscr == NULL)\n+\t\treturn NULL;\n+\n+\tlxch = *xscr;\n\n-\tfor (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)\n-\t\tif (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)\n+\tfor (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) {\n+\t\tlong distance = xch->i1 - (xchp->i1 + xchp->chg1);\n+\t\tif (distance > max_common)\n \t\t\tbreak;\n\n-\treturn xchp;\n+\t\tif (distance < max_ignorable && (!xch->ignore || lxch == xchp)) {\n+\t\t\tlxch = xch;\n+\t\t\tignored = 0;\n+\t\t} else if (distance < max_ignorable && xch->ignore) {\n+\t\t\tignored += xch->chg2;\n+\t\t} else if (lxch != xchp &&\n+\t\t\t   xch->i1 + ignored - (lxch->i1 + lxch->chg1) > max_common) {\n+\t\t\tbreak;\n+\t\t} else if (!xch->ignore) {\n+\t\t\tlxch = xch;\n+\t\t\tignored = 0;\n+\t\t} else {\n+\t\t\tignored += xch->chg2;\n+\t\t}\n+\t}\n+\n+\treturn lxch;\n }\n\n\n@@ -139,7 +174,9 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \t\treturn xdl_emit_common(xe, xscr, ecb, xecfg);\n\n \tfor (xch = xscr; xch; xch = xche->next) {\n-\t\txche = xdl_get_hunk(xch, xecfg);\n+\t\txche = xdl_get_hunk(&xch, xecfg);\n+\t\tif (!xch)\n+\t\t\tbreak;\n\n \t\ts1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);\n \t\ts2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);\ndiff --git a/xdiff/xemit.h b/xdiff/xemit.h\nindex c2e2e83..d297107 100644\n--- a/xdiff/xemit.h\n+++ b/xdiff/xemit.h\n@@ -27,7 +27,7 @@\n typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \t\t\t   xdemitconf_t const *xecfg);\n\n-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);\n+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg);\n int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \t\t  xdemitconf_t const *xecfg);\n\ndiff --git a/xdiff/xutils.c b/xdiff/xutils.c\nindex 9504eae..62cb23d 100644\n--- a/xdiff/xutils.c\n+++ b/xdiff/xutils.c\n@@ -143,6 +143,19 @@ long xdl_guess_lines(mmfile_t *mf, long sample) {\n \treturn nl + 1;\n }\n\n+int xdl_blankline(const char *line, long size, long flags)\n+{\n+\tlong i;\n+\n+\tif (!(flags & XDF_WHITESPACE_FLAGS))\n+\t\treturn (size <= 1);\n+\n+\tfor (i = 0; i < size && XDL_ISSPACE(line[i]); i++)\n+\t\t;\n+\n+\treturn (i == size);\n+}\n+\n int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)\n {\n \tint i1, i2;\ndiff --git a/xdiff/xutils.h b/xdiff/xutils.h\nindex ad1428e..4646ce5 100644\n--- a/xdiff/xutils.h\n+++ b/xdiff/xutils.h\n@@ -32,6 +32,7 @@ int xdl_cha_init(chastore_t *cha, long isize, long icount);\n void xdl_cha_free(chastore_t *cha);\n void *xdl_cha_alloc(chastore_t *cha);\n long xdl_guess_lines(mmfile_t *mf, long sample);\n+int xdl_blankline(const char *line, long size, long flags);\n int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);\n unsigned long xdl_hash_record(char const **data, char const *top, long flags);\n unsigned int xdl_hashbits(unsigned int size);\n--\n1.7.9.5\n"},{"id":"221414","messageId":"7v4nctyc2h.fsf@alter.siamese.dyndns.org","threadId":"33940","inReplyTo":"1371667567-1907-1-git-send-email-apelisse@gmail.com","subject":"Re: [PATCH] diff: add --ignore-blank-lines option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-19T22:23:34Z","receivedAt":"2013-06-19T22:23:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Antoine Pelisse <apelisse@gmail.com> writes:\n\n> So here is a more thorough description of the option:\n> - real changes are interesting\n> - blank lines that are close enough (less than context size) to\n> interesting changes are considered interesting (recursive definition)\n> - \"context\" lines are used around each hunk of interesting changes\n> - If two hunks are separated by less than \"inter-hunk-context\", they\n> will be merged into one.\n\nThanks; will replace what is queued in 'pu'.  Let's advance this\nversion to 'next' soonish.\n"}]}