Skip to content
/ git Public
forked from git/git

Commit

Permalink
diff: change semantics of "ignore whitespace" options
Browse files Browse the repository at this point in the history
Traditionally, the --ignore-whitespace* options have merely meant to tell
the diff output routine that some class of differences are not worth
showing in the textual diff output, so that the end user has easier time
to review the remaining (presumably more meaningful) changes.  These
options never affected the outcome of the command, given as the exit
status when the --exit-code option was in effect (either directly or
indirectly).

When you have only whitespace changes, however, you might expect

	git diff -b --exit-code

to report that there is _no_ change with zero exit status.

Change the semantics of --ignore-whitespace* options to mean more than
"omit showing the difference in text".

The exit status, when --exit-code is in effect, is computed by checking if
we found any differences at the path level, while diff frontends feed
filepairs to the diffcore engine.  When "ignore whitespace" options are in
effect, we defer this determination until the very end of diffcore
transformation.  We simply do not know until the textual diff is
generated, which comes very late in the pipeline.

When --quiet is in effect, various diff frontends optimize by breaking out
early from the loop that enumerates the filepairs, when we find the first
path level difference; when --ignore-whitespace* is used the above change
automatically disables this optimization.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
gitster committed Jul 29, 2009
1 parent 0a53e9d commit f245194
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 4 deletions.
34 changes: 31 additions & 3 deletions diff.c
Original file line number Diff line number Diff line change
Expand Up @@ -2378,6 +2378,20 @@ int diff_setup_done(struct diff_options *options)
if (count > 1)
die("--name-only, --name-status, --check and -s are mutually exclusive");

/*
* Most of the time we can say "there are changes"
* only by checking if there are changed paths, but
* --ignore-whitespace* options force us to look
* inside contets.
*/

if (DIFF_XDL_TST(options, IGNORE_WHITESPACE) ||
DIFF_XDL_TST(options, IGNORE_WHITESPACE_CHANGE) ||
DIFF_XDL_TST(options, IGNORE_WHITESPACE_AT_EOL))
DIFF_OPT_SET(options, DIFF_FROM_CONTENTS);
else
DIFF_OPT_CLR(options, DIFF_FROM_CONTENTS);

if (DIFF_OPT_TST(options, FIND_COPIES_HARDER))
options->detect_rename = DIFF_DETECT_COPY;

Expand Down Expand Up @@ -3330,6 +3344,18 @@ void diff_flush(struct diff_options *options)
q->nr = q->alloc = 0;
if (options->close_file)
fclose(options->file);

/*
* Report the contents level differences with HAS_CHANGES;
* diff_addremove/diff_change does not set the bit when
* DIFF_FROM_CONTENTS is in effect (e.g. with -w).
*/
if (DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) {
if (options->found_changes)
DIFF_OPT_SET(options, HAS_CHANGES);
else
DIFF_OPT_CLR(options, HAS_CHANGES);
}
}

static void diffcore_apply_filter(const char *filter)
Expand Down Expand Up @@ -3466,7 +3492,7 @@ void diffcore_std(struct diff_options *options)
diff_resolve_rename_copy();
diffcore_apply_filter(options->filter);

if (diff_queued_diff.nr)
if (diff_queued_diff.nr && !DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
DIFF_OPT_SET(options, HAS_CHANGES);
else
DIFF_OPT_CLR(options, HAS_CHANGES);
Expand Down Expand Up @@ -3526,7 +3552,8 @@ void diff_addremove(struct diff_options *options,
fill_filespec(two, sha1, mode);

diff_queue(&diff_queued_diff, one, two);
DIFF_OPT_SET(options, HAS_CHANGES);
if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
DIFF_OPT_SET(options, HAS_CHANGES);
}

void diff_change(struct diff_options *options,
Expand Down Expand Up @@ -3558,7 +3585,8 @@ void diff_change(struct diff_options *options,
fill_filespec(two, new_sha1, new_mode);

diff_queue(&diff_queued_diff, one, two);
DIFF_OPT_SET(options, HAS_CHANGES);
if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
DIFF_OPT_SET(options, HAS_CHANGES);
}

void diff_unmerge(struct diff_options *options,
Expand Down
1 change: 1 addition & 0 deletions diff.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
#define DIFF_OPT_DIRSTAT_CUMULATIVE (1 << 19)
#define DIFF_OPT_DIRSTAT_BY_FILE (1 << 20)
#define DIFF_OPT_ALLOW_TEXTCONV (1 << 21)
#define DIFF_OPT_DIFF_FROM_CONTENTS (1 << 22)
#define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag)
#define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag)
#define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag)
Expand Down
63 changes: 63 additions & 0 deletions t/t4037-whitespace-status.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/bin/sh

test_description='diff --exit-code with whitespace'
. ./test-lib.sh

test_expect_success setup '
mkdir a b &&
echo >c &&
echo >a/d &&
echo >b/e &&
git add . &&
test_tick &&
git commit -m initial &&
echo " " >a/d &&
test_tick &&
git commit -a -m second &&
echo " " >a/d &&
echo " " >b/e &&
git add a/d
'

test_expect_success 'diff-tree --exit-code' '
test_must_fail git diff --exit-code HEAD^ HEAD &&
test_must_fail git diff-tree --exit-code HEAD^ HEAD
'

test_expect_success 'diff-tree -b --exit-code' '
git diff -b --exit-code HEAD^ HEAD &&
git diff-tree -b -p --exit-code HEAD^ HEAD &&
git diff-tree -b --exit-code HEAD^ HEAD
'

test_expect_success 'diff-index --cached --exit-code' '
test_must_fail git diff --cached --exit-code HEAD &&
test_must_fail git diff-index --cached --exit-code HEAD
'

test_expect_success 'diff-index -b -p --cached --exit-code' '
git diff -b --cached --exit-code HEAD &&
git diff-index -b -p --cached --exit-code HEAD
'

test_expect_success 'diff-index --exit-code' '
test_must_fail git diff --exit-code HEAD &&
test_must_fail git diff-index --exit-code HEAD
'

test_expect_success 'diff-index -b -p --exit-code' '
git diff -b --exit-code HEAD &&
git diff-index -b -p --exit-code HEAD
'

test_expect_success 'diff-files --exit-code' '
test_must_fail git diff --exit-code &&
test_must_fail git diff-files --exit-code
'

test_expect_success 'diff-files -b -p --exit-code' '
git diff -b --exit-code &&
git diff-files -b -p --exit-code
'

test_done
3 changes: 2 additions & 1 deletion tree-diff.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,8 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, stru
int baselen = strlen(base);

for (;;) {
if (DIFF_OPT_TST(opt, QUIET) && DIFF_OPT_TST(opt, HAS_CHANGES))
if (DIFF_OPT_TST(opt, QUIET) &&
DIFF_OPT_TST(opt, HAS_CHANGES))
break;
if (opt->nr_paths) {
skip_uninteresting(t1, base, baselen, opt);
Expand Down

0 comments on commit f245194

Please sign in to comment.