The goal of the patch is to introduce the GNU diff
-B/--ignore-blank-lines as closely as possible. The short option is not
available because it's already used for "break-rewrites".

When this option is used, git-diff will not create hunks that simply
adds or removes empty lines, but will still show empty lines
addition/suppression if they are close enough to "valuable" changes.

Signed-off-by: Antoine Pelisse <apeli...@gmail.com>
---
 Documentation/diff-options.txt |    3 ++
 diff.c                         |    2 ++
 t/t4015-diff-whitespace.sh     |   65 ++++++++++++++++++++++++++++++++++++++++
 xdiff/xdiff.h                  |    2 ++
 xdiff/xdiffi.c                 |   29 +++++++++++++++++-
 xdiff/xdiffi.h                 |    1 +
 xdiff/xemit.c                  |   32 ++++++++++++++++----
 xdiff/xemit.h                  |    2 +-
 xdiff/xutils.c                 |   13 ++++++++
 xdiff/xutils.h                 |    1 +
 10 files changed, 143 insertions(+), 7 deletions(-)

diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 104579d..80f06b7 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -439,6 +439,9 @@ endif::git-format-patch[]
        differences even if one line has whitespace where the other
        line has none.

+--ignore-blank-lines::
+       Ignore changes whose lines are all blank.
+
 --inter-hunk-context=<lines>::
        Show the context between diff hunks, up to the specified number
        of lines, thereby fusing hunks that are close to each other.
diff --git a/diff.c b/diff.c
index f0b3e7c..208094f 100644
--- a/diff.c
+++ b/diff.c
@@ -3593,6 +3593,8 @@ int diff_opt_parse(struct diff_options *options, const 
char **av, int ac)
                DIFF_XDL_SET(options, IGNORE_WHITESPACE_CHANGE);
        else if (!strcmp(arg, "--ignore-space-at-eol"))
                DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
+       else if (!strcmp(arg, "--ignore-blank-lines"))
+               DIFF_XDL_SET(options, IGNORE_BLANK_LINES);
        else if (!strcmp(arg, "--patience"))
                options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
        else if (!strcmp(arg, "--histogram"))
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index cc3db13..b3c4fcc 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -142,6 +142,71 @@ EOF
 git diff --ignore-space-at-eol > out
 test_expect_success 'another test, with --ignore-space-at-eol' 'test_cmp 
expect out'

+test_expect_success 'ignore-blank-lines: only new lines' '
+       seq 5 >x &&
+       git update-index x &&
+       seq 5 | sed "/3/i \\\\" >x &&
+       git diff --ignore-blank-lines >out &&
+       printf "" >expect &&
+       test_cmp out expect
+'
+
+test_expect_success 'ignore-blank-lines: only new lines with space' '
+       seq 5 >x &&
+       git update-index x &&
+       seq 5 | sed "/3/i \ " >x &&
+       git diff -w --ignore-blank-lines >out &&
+       printf "" >expect &&
+       test_cmp out expect
+'
+
+
+test_expect_success 'ignore-blank-lines: with changes' '
+       seq 11 >x &&
+       git update-index x &&
+       cat <<-\EOF >x &&
+
+       1
+       2
+       3
+       change
+       4
+       5
+       6
+       7
+
+       8
+       change
+       9
+       10
+       11
+
+       EOF
+       git diff --ignore-blank-lines >out.tmp &&
+       sed -e "1,/^+++ b\/x/d" <out.tmp >out &&
+       cat <<-\EOF >expect &&
+       @@ -1,6 +2,7 @@
+        1
+        2
+        3
+       +change
+        4
+        5
+        6
+       @@ -5,7 +7,9 @@
+        5
+        6
+        7
+       +
+        8
+       +change
+        9
+        10
+        11
+       EOF
+       test_cmp out expect
+'
+
 test_expect_success 'check mixed spaces and tabs in indent' '

        # This is indented with SP HT SP.
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index 219a3bb..c033991 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -39,6 +39,8 @@ extern "C" {
 #define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)
 #define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)

+#define XDF_IGNORE_BLANK_LINES (1 << 7)
+
 #define XDL_EMIT_FUNCNAMES (1 << 0)
 #define XDL_EMIT_COMMON (1 << 1)
 #define XDL_EMIT_FUNCCONTEXT (1 << 2)
diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index b2eb6db..3cabc78 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -394,6 +394,7 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long 
i1, long i2, long chg1,
        xch->i2 = i2;
        xch->chg1 = chg1;
        xch->chg2 = chg2;
+       xch->ignore = 0;

        return xch;
 }
@@ -544,7 +545,9 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t 
*xscr, xdemitcb_t *ecb,
        xdchange_t *xch, *xche;

        for (xch = xscr; xch; xch = xche->next) {
-               xche = xdl_get_hunk(xch, xecfg);
+               xche = xdl_get_hunk(&xch, xecfg);
+               if (!xch)
+                       break;
                if (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1,
                                     xch->i2, xche->i2 + xche->chg2 - xch->i2,
                                     ecb->priv) < 0)
@@ -553,6 +556,27 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t 
*xscr, xdemitcb_t *ecb,
        return 0;
 }

+static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags)
+{
+       xdchange_t *xch;
+
+       for (xch = xscr; xch; xch = xch->next) {
+               int ignore = 1;
+               xrecord_t **rec;
+               long i;
+
+               rec = &xe->xdf1.recs[xch->i1];
+               for (i = 0; i < xch->chg1 && ignore; i++)
+                       ignore = xdl_blankline(rec[i]->ptr, flags);
+
+               rec = &xe->xdf2.recs[xch->i2];
+               for (i = 0; i < xch->chg2 && ignore; i++)
+                       ignore = xdl_blankline(rec[i]->ptr, flags);
+
+               xch->ignore = ignore;
+       }
+}
+
 int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
             xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
        xdchange_t *xscr;
@@ -571,6 +595,9 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const 
*xpp,
                return -1;
        }
        if (xscr) {
+               if (xpp->flags & XDF_IGNORE_BLANK_LINES)
+                       xdl_mark_ignorable(xscr, &xe, xpp->flags);
+
                if (ef(&xe, xscr, ecb, xecfg) < 0) {

                        xdl_free_script(xscr);
diff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h
index 7a92ea9..8b81206 100644
--- a/xdiff/xdiffi.h
+++ b/xdiff/xdiffi.h
@@ -41,6 +41,7 @@ typedef struct s_xdchange {
        struct s_xdchange *next;
        long i1, i2;
        long chg1, chg2;
+       int ignore;
 } xdchange_t;


diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index d11dbf9..52dfef8 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -56,14 +56,34 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char 
const *pre, xdemitcb_t *
 /*
  * Starting at the passed change atom, find the latest change atom to be 
included
  * inside the differential hunk according to the specified configuration.
+ * Also advance xscr if the first changes must be discareded.
  */
-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg) {
        xdchange_t *xch, *xchp;
        long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
+       long ignorable_context = max_common / 2 - 1;
+       int interesting = 0;

-       for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)
-               if (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)
-                       break;
+       for (xchp = *xscr, xch = (*xscr)->next; xch; xchp = xch, xch = 
xch->next) {
+               long thresh;
+               if (xchp->ignore || xch->ignore)
+                       thresh = ignorable_context;
+               else
+                       thresh = max_common;
+
+               if (!xchp->ignore)
+                       interesting = 1;
+
+               if (xch->i1 - (xchp->i1 + xchp->chg1) > thresh) {
+                       if (interesting)
+                               break;
+                       else
+                               *xscr = xch;
+               }
+       }
+
+       if (!interesting && xchp->ignore)
+               *xscr = NULL;

        return xchp;
 }
@@ -139,7 +159,9 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, 
xdemitcb_t *ecb,
                return xdl_emit_common(xe, xscr, ecb, xecfg);

        for (xch = xscr; xch; xch = xche->next) {
-               xche = xdl_get_hunk(xch, xecfg);
+               xche = xdl_get_hunk(&xch, xecfg);
+               if (!xch)
+                       break;

                s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
                s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
diff --git a/xdiff/xemit.h b/xdiff/xemit.h
index c2e2e83..d297107 100644
--- a/xdiff/xemit.h
+++ b/xdiff/xemit.h
@@ -27,7 +27,7 @@
 typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                           xdemitconf_t const *xecfg);

-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);
+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg);
 int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                  xdemitconf_t const *xecfg);

diff --git a/xdiff/xutils.c b/xdiff/xutils.c
index 9504eae..c047376 100644
--- a/xdiff/xutils.c
+++ b/xdiff/xutils.c
@@ -143,6 +143,19 @@ long xdl_guess_lines(mmfile_t *mf, long sample) {
        return nl + 1;
 }

+int xdl_blankline(const char *line, long flags)
+{
+       long i;
+
+       if (!(flags & XDF_WHITESPACE_FLAGS))
+               return (*line == '\n');
+
+       for (i = 0; line[i] != '\n' && XDL_ISSPACE(line[i]); i++)
+               ;
+
+       return (line[i] == '\n');
+}
+
 int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
 {
        int i1, i2;
diff --git a/xdiff/xutils.h b/xdiff/xutils.h
index ad1428e..b9cceff 100644
--- a/xdiff/xutils.h
+++ b/xdiff/xutils.h
@@ -32,6 +32,7 @@ int xdl_cha_init(chastore_t *cha, long isize, long icount);
 void xdl_cha_free(chastore_t *cha);
 void *xdl_cha_alloc(chastore_t *cha);
 long xdl_guess_lines(mmfile_t *mf, long sample);
+int xdl_blankline(const char *line, long flags);
 int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
 unsigned long xdl_hash_record(char const **data, char const *top, long flags);
 unsigned int xdl_hashbits(unsigned int size);
--
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to