mirror of
https://github.com/git/git.git
synced 2024-11-24 02:17:02 +08:00
diff: add --ignore-blank-lines option
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 add or remove empty lines, but will still show empty lines addition/suppression if they are close enough to "valuable" changes. There are two differences between this option and GNU diff -B option: - GNU diff doesn't have "--inter-hunk-context", so this must be handled - The following sequence looks like a bug (context is displayed twice): $ seq 5 >file1 $ cat <<EOF >file2 change 1 2 3 4 5 change EOF $ diff -u -B file1 file2 --- file1 2013-06-08 22:13:04.471517834 +0200 +++ file2 2013-06-08 22:13:23.275517855 +0200 @@ -1,5 +1,7 @@ +change 1 2 + 3 4 5 @@ -3,3 +5,4 @@ 3 4 5 +change So here is a more thorough description of the option: - real changes are interesting - blank lines that are close enough (less than context size) to interesting changes are considered interesting (recursive definition) - "context" lines are used around each hunk of interesting changes - If two hunks are separated by less than "inter-hunk-context", they will be merged into one. The implementation does the "interesting changes selection" in a single pass. Signed-off-by: Antoine Pelisse <apelisse@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
edca415256
commit
36617af7ed
@ -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.
|
||||
|
2
diff.c
2
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"))
|
||||
|
@ -142,6 +142,351 @@ 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' '
|
||||
test_seq 5 >x &&
|
||||
git update-index x &&
|
||||
test_seq 5 | sed "/3/i \\
|
||||
" >x &&
|
||||
git diff --ignore-blank-lines >out &&
|
||||
>expect &&
|
||||
test_cmp out expect
|
||||
'
|
||||
|
||||
test_expect_success 'ignore-blank-lines: only new lines with space' '
|
||||
test_seq 5 >x &&
|
||||
git update-index x &&
|
||||
test_seq 5 | sed "/3/i \ " >x &&
|
||||
git diff -w --ignore-blank-lines >out &&
|
||||
>expect &&
|
||||
test_cmp out expect
|
||||
'
|
||||
|
||||
test_expect_success 'ignore-blank-lines: after change' '
|
||||
cat <<-\EOF >x &&
|
||||
1
|
||||
2
|
||||
|
||||
3
|
||||
4
|
||||
5
|
||||
|
||||
6
|
||||
7
|
||||
EOF
|
||||
git update-index x &&
|
||||
cat <<-\EOF >x &&
|
||||
change
|
||||
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
|
||||
7
|
||||
EOF
|
||||
git diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&
|
||||
cat <<-\EOF >expected &&
|
||||
diff --git a/x b/x
|
||||
--- a/x
|
||||
+++ b/x
|
||||
@@ -1,6 +1,7 @@
|
||||
+change
|
||||
+
|
||||
1
|
||||
2
|
||||
-
|
||||
3
|
||||
4
|
||||
5
|
||||
EOF
|
||||
compare_diff_patch expected out.tmp
|
||||
'
|
||||
|
||||
test_expect_success 'ignore-blank-lines: before change' '
|
||||
cat <<-\EOF >x &&
|
||||
1
|
||||
2
|
||||
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
EOF
|
||||
git update-index x &&
|
||||
cat <<-\EOF >x &&
|
||||
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
|
||||
6
|
||||
7
|
||||
change
|
||||
EOF
|
||||
git diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&
|
||||
cat <<-\EOF >expected &&
|
||||
diff --git a/x b/x
|
||||
--- a/x
|
||||
+++ b/x
|
||||
@@ -4,5 +4,7 @@
|
||||
3
|
||||
4
|
||||
5
|
||||
+
|
||||
6
|
||||
7
|
||||
+change
|
||||
EOF
|
||||
compare_diff_patch expected out.tmp
|
||||
'
|
||||
|
||||
test_expect_success 'ignore-blank-lines: between changes' '
|
||||
cat <<-\EOF >x &&
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
|
||||
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
10
|
||||
EOF
|
||||
git update-index x &&
|
||||
cat <<-\EOF >x &&
|
||||
change
|
||||
1
|
||||
2
|
||||
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
|
||||
9
|
||||
10
|
||||
change
|
||||
EOF
|
||||
git diff --ignore-blank-lines >out.tmp &&
|
||||
cat <<-\EOF >expected &&
|
||||
diff --git a/x b/x
|
||||
--- a/x
|
||||
+++ b/x
|
||||
@@ -1,5 +1,7 @@
|
||||
+change
|
||||
1
|
||||
2
|
||||
+
|
||||
3
|
||||
4
|
||||
5
|
||||
@@ -8,5 +8,7 @@
|
||||
6
|
||||
7
|
||||
8
|
||||
+
|
||||
9
|
||||
10
|
||||
+change
|
||||
EOF
|
||||
compare_diff_patch expected out.tmp
|
||||
'
|
||||
|
||||
test_expect_success 'ignore-blank-lines: between changes (with interhunkctx)' '
|
||||
test_seq 10 >x &&
|
||||
git update-index x &&
|
||||
cat <<-\EOF >x &&
|
||||
change
|
||||
1
|
||||
2
|
||||
|
||||
3
|
||||
4
|
||||
5
|
||||
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
|
||||
10
|
||||
change
|
||||
EOF
|
||||
git diff --inter-hunk-context=2 --ignore-blank-lines >out.tmp &&
|
||||
cat <<-\EOF >expected &&
|
||||
diff --git a/x b/x
|
||||
--- a/x
|
||||
+++ b/x
|
||||
@@ -1,10 +1,15 @@
|
||||
+change
|
||||
1
|
||||
2
|
||||
+
|
||||
3
|
||||
4
|
||||
5
|
||||
+
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
+
|
||||
10
|
||||
+change
|
||||
EOF
|
||||
compare_diff_patch expected out.tmp
|
||||
'
|
||||
|
||||
test_expect_success 'ignore-blank-lines: scattered spaces' '
|
||||
test_seq 10 >x &&
|
||||
git update-index x &&
|
||||
cat <<-\EOF >x &&
|
||||
change
|
||||
1
|
||||
2
|
||||
3
|
||||
|
||||
4
|
||||
|
||||
5
|
||||
|
||||
6
|
||||
|
||||
|
||||
7
|
||||
|
||||
8
|
||||
9
|
||||
10
|
||||
change
|
||||
EOF
|
||||
git diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp &&
|
||||
cat <<-\EOF >expected &&
|
||||
diff --git a/x b/x
|
||||
--- a/x
|
||||
+++ b/x
|
||||
@@ -1,3 +1,4 @@
|
||||
+change
|
||||
1
|
||||
2
|
||||
3
|
||||
@@ -8,3 +15,4 @@
|
||||
8
|
||||
9
|
||||
10
|
||||
+change
|
||||
EOF
|
||||
compare_diff_patch expected out.tmp
|
||||
'
|
||||
|
||||
test_expect_success 'ignore-blank-lines: spaces coalesce' '
|
||||
test_seq 6 >x &&
|
||||
git update-index x &&
|
||||
cat <<-\EOF >x &&
|
||||
change
|
||||
1
|
||||
2
|
||||
3
|
||||
|
||||
4
|
||||
|
||||
5
|
||||
|
||||
6
|
||||
change
|
||||
EOF
|
||||
git diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp &&
|
||||
cat <<-\EOF >expected &&
|
||||
diff --git a/x b/x
|
||||
--- a/x
|
||||
+++ b/x
|
||||
@@ -1,6 +1,11 @@
|
||||
+change
|
||||
1
|
||||
2
|
||||
3
|
||||
+
|
||||
4
|
||||
+
|
||||
5
|
||||
+
|
||||
6
|
||||
+change
|
||||
EOF
|
||||
compare_diff_patch expected out.tmp
|
||||
'
|
||||
|
||||
test_expect_success 'ignore-blank-lines: mix changes and blank lines' '
|
||||
test_seq 16 >x &&
|
||||
git update-index x &&
|
||||
cat <<-\EOF >x &&
|
||||
change
|
||||
1
|
||||
2
|
||||
|
||||
3
|
||||
4
|
||||
5
|
||||
change
|
||||
6
|
||||
7
|
||||
8
|
||||
|
||||
9
|
||||
10
|
||||
11
|
||||
change
|
||||
12
|
||||
13
|
||||
14
|
||||
|
||||
15
|
||||
16
|
||||
change
|
||||
EOF
|
||||
git diff --ignore-blank-lines >out.tmp &&
|
||||
cat <<-\EOF >expected &&
|
||||
diff --git a/x b/x
|
||||
--- a/x
|
||||
+++ b/x
|
||||
@@ -1,8 +1,11 @@
|
||||
+change
|
||||
1
|
||||
2
|
||||
+
|
||||
3
|
||||
4
|
||||
5
|
||||
+change
|
||||
6
|
||||
7
|
||||
8
|
||||
@@ -9,8 +13,11 @@
|
||||
9
|
||||
10
|
||||
11
|
||||
+change
|
||||
12
|
||||
13
|
||||
14
|
||||
+
|
||||
15
|
||||
16
|
||||
+change
|
||||
EOF
|
||||
compare_diff_patch expected out.tmp
|
||||
'
|
||||
|
||||
test_expect_success 'check mixed spaces and tabs in indent' '
|
||||
|
||||
# This is indented with SP HT SP.
|
||||
|
@ -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)
|
||||
|
@ -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, rec[i]->size, flags);
|
||||
|
||||
rec = &xe->xdf2.recs[xch->i2];
|
||||
for (i = 0; i < xch->chg2 && ignore; i++)
|
||||
ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, 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);
|
||||
|
@ -41,6 +41,7 @@ typedef struct s_xdchange {
|
||||
struct s_xdchange *next;
|
||||
long i1, i2;
|
||||
long chg1, chg2;
|
||||
int ignore;
|
||||
} xdchange_t;
|
||||
|
||||
|
||||
|
@ -56,16 +56,51 @@ 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 discarded.
|
||||
*/
|
||||
xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
|
||||
xdchange_t *xch, *xchp;
|
||||
xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
|
||||
{
|
||||
xdchange_t *xch, *xchp, *lxch;
|
||||
long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
|
||||
long max_ignorable = xecfg->ctxlen;
|
||||
unsigned long ignored = 0; /* number of ignored blank lines */
|
||||
|
||||
for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)
|
||||
if (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)
|
||||
/* remove ignorable changes that are too far before other changes */
|
||||
for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {
|
||||
xch = xchp->next;
|
||||
|
||||
if (xch == NULL ||
|
||||
xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable)
|
||||
*xscr = xch;
|
||||
}
|
||||
|
||||
if (*xscr == NULL)
|
||||
return NULL;
|
||||
|
||||
lxch = *xscr;
|
||||
|
||||
for (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) {
|
||||
long distance = xch->i1 - (xchp->i1 + xchp->chg1);
|
||||
if (distance > max_common)
|
||||
break;
|
||||
|
||||
return xchp;
|
||||
if (distance < max_ignorable && (!xch->ignore || lxch == xchp)) {
|
||||
lxch = xch;
|
||||
ignored = 0;
|
||||
} else if (distance < max_ignorable && xch->ignore) {
|
||||
ignored += xch->chg2;
|
||||
} else if (lxch != xchp &&
|
||||
xch->i1 + ignored - (lxch->i1 + lxch->chg1) > max_common) {
|
||||
break;
|
||||
} else if (!xch->ignore) {
|
||||
lxch = xch;
|
||||
ignored = 0;
|
||||
} else {
|
||||
ignored += xch->chg2;
|
||||
}
|
||||
}
|
||||
|
||||
return lxch;
|
||||
}
|
||||
|
||||
|
||||
@ -139,7 +174,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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -143,6 +143,19 @@ long xdl_guess_lines(mmfile_t *mf, long sample) {
|
||||
return nl + 1;
|
||||
}
|
||||
|
||||
int xdl_blankline(const char *line, long size, long flags)
|
||||
{
|
||||
long i;
|
||||
|
||||
if (!(flags & XDF_WHITESPACE_FLAGS))
|
||||
return (size <= 1);
|
||||
|
||||
for (i = 0; i < size && XDL_ISSPACE(line[i]); i++)
|
||||
;
|
||||
|
||||
return (i == size);
|
||||
}
|
||||
|
||||
int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
|
||||
{
|
||||
int i1, i2;
|
||||
|
@ -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 size, 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);
|
||||
|
Loading…
Reference in New Issue
Block a user