Allow git-diff exit with codes similar to diff(1)

This introduces a new command-line option: --exit-code. The diff
programs will return 1 for differences, return 0 for equality, and
something else for errors.

Signed-off-by: Alex Riesen <raa.lkml@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Alex Riesen
2007-03-14 01:17:04 +01:00
committed by Junio C Hamano
parent 803527f1d9
commit 41bbf9d585
9 changed files with 118 additions and 14 deletions

View File

@ -159,5 +159,10 @@
-w:: -w::
Shorthand for "--ignore-all-space". Shorthand for "--ignore-all-space".
--exit-code::
Make the program exit with codes similar to diff(1).
That is, it exits with 1 if there were differences and
0 means no differences.
For more detailed explanation on these common options, see also For more detailed explanation on these common options, see also
link:diffcore.html[diffcore documentation]. link:diffcore.html[diffcore documentation].

View File

@ -17,6 +17,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
{ {
struct rev_info rev; struct rev_info rev;
int nongit = 0; int nongit = 0;
int result;
prefix = setup_git_directory_gently(&nongit); prefix = setup_git_directory_gently(&nongit);
init_revisions(&rev, prefix); init_revisions(&rev, prefix);
@ -29,5 +30,6 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
argc = setup_revisions(argc, argv, &rev, NULL); argc = setup_revisions(argc, argv, &rev, NULL);
if (!rev.diffopt.output_format) if (!rev.diffopt.output_format)
rev.diffopt.output_format = DIFF_FORMAT_RAW; rev.diffopt.output_format = DIFF_FORMAT_RAW;
return run_diff_files_cmd(&rev, argc, argv); result = run_diff_files_cmd(&rev, argc, argv);
return rev.diffopt.exit_with_status ? rev.diffopt.has_changes: result;
} }

View File

@ -14,6 +14,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
struct rev_info rev; struct rev_info rev;
int cached = 0; int cached = 0;
int i; int i;
int result;
init_revisions(&rev, prefix); init_revisions(&rev, prefix);
git_config(git_default_config); /* no "diff" UI options */ git_config(git_default_config); /* no "diff" UI options */
@ -42,5 +43,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
perror("read_cache"); perror("read_cache");
return -1; return -1;
} }
return run_diff_index(&rev, cached); result = run_diff_index(&rev, cached);
return rev.diffopt.exit_with_status ? rev.diffopt.has_changes: result;
} }

View File

@ -118,7 +118,8 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
} }
if (!read_stdin) if (!read_stdin)
return 0; return opt->diffopt.exit_with_status ?
opt->diffopt.has_changes: 0;
if (opt->diffopt.detect_rename) if (opt->diffopt.detect_rename)
opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE | opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
@ -133,5 +134,5 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
else else
diff_tree_stdin(line); diff_tree_stdin(line);
} }
return 0; return opt->diffopt.exit_with_status ? opt->diffopt.has_changes: 0;
} }

View File

@ -190,6 +190,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
const char *path = NULL; const char *path = NULL;
struct blobinfo blob[2]; struct blobinfo blob[2];
int nongit = 0; int nongit = 0;
int result = 0;
/* /*
* We could get N tree-ish in the rev.pending_objects list. * We could get N tree-ish in the rev.pending_objects list.
@ -292,17 +293,17 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
if (!ents) { if (!ents) {
switch (blobs) { switch (blobs) {
case 0: case 0:
return run_diff_files_cmd(&rev, argc, argv); result = run_diff_files_cmd(&rev, argc, argv);
break; break;
case 1: case 1:
if (paths != 1) if (paths != 1)
usage(builtin_diff_usage); usage(builtin_diff_usage);
return builtin_diff_b_f(&rev, argc, argv, blob, path); result = builtin_diff_b_f(&rev, argc, argv, blob, path);
break; break;
case 2: case 2:
if (paths) if (paths)
usage(builtin_diff_usage); usage(builtin_diff_usage);
return builtin_diff_blobs(&rev, argc, argv, blob); result = builtin_diff_blobs(&rev, argc, argv, blob);
break; break;
default: default:
usage(builtin_diff_usage); usage(builtin_diff_usage);
@ -311,19 +312,21 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
else if (blobs) else if (blobs)
usage(builtin_diff_usage); usage(builtin_diff_usage);
else if (ents == 1) else if (ents == 1)
return builtin_diff_index(&rev, argc, argv); result = builtin_diff_index(&rev, argc, argv);
else if (ents == 2) else if (ents == 2)
return builtin_diff_tree(&rev, argc, argv, ent); result = builtin_diff_tree(&rev, argc, argv, ent);
else if ((ents == 3) && (ent[0].item->flags & UNINTERESTING)) { else if ((ents == 3) && (ent[0].item->flags & UNINTERESTING)) {
/* diff A...B where there is one sane merge base between /* diff A...B where there is one sane merge base between
* A and B. We have ent[0] == merge-base, ent[1] == A, * A and B. We have ent[0] == merge-base, ent[1] == A,
* and ent[2] == B. Show diff between the base and B. * and ent[2] == B. Show diff between the base and B.
*/ */
ent[1] = ent[2]; ent[1] = ent[2];
return builtin_diff_tree(&rev, argc, argv, ent); result = builtin_diff_tree(&rev, argc, argv, ent);
} }
else else
return builtin_diff_combined(&rev, argc, argv, result = builtin_diff_combined(&rev, argc, argv,
ent, ents); ent, ents);
usage(builtin_diff_usage); if (rev.diffopt.exit_with_status)
result = rev.diffopt.has_changes;
return result;
} }

View File

@ -170,8 +170,10 @@ static int handle_diff_files_args(struct rev_info *revs,
else if (!strcmp(argv[1], "--theirs")) else if (!strcmp(argv[1], "--theirs"))
revs->max_count = 3; revs->max_count = 3;
else if (!strcmp(argv[1], "-n") || else if (!strcmp(argv[1], "-n") ||
!strcmp(argv[1], "--no-index")) !strcmp(argv[1], "--no-index")) {
revs->max_count = -2; revs->max_count = -2;
revs->diffopt.exit_with_status = 1;
}
else if (!strcmp(argv[1], "-q")) else if (!strcmp(argv[1], "-q"))
*silent = 1; *silent = 1;
else else
@ -237,6 +239,7 @@ int setup_diff_no_index(struct rev_info *revs,
break; break;
} else if (i < argc - 3 && !strcmp(argv[i], "--no-index")) { } else if (i < argc - 3 && !strcmp(argv[i], "--no-index")) {
i = argc - 3; i = argc - 3;
revs->diffopt.exit_with_status = 1;
break; break;
} }
if (argc != i + 2 || (!is_outside_repo(argv[i + 1], nongit, prefix) && if (argc != i + 2 || (!is_outside_repo(argv[i + 1], nongit, prefix) &&

6
diff.c
View File

@ -2134,6 +2134,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
options->color_diff = options->color_diff_words = 1; options->color_diff = options->color_diff_words = 1;
else if (!strcmp(arg, "--no-renames")) else if (!strcmp(arg, "--no-renames"))
options->detect_rename = 0; options->detect_rename = 0;
else if (!strcmp(arg, "--exit-code"))
options->exit_with_status = 1;
else else
return 0; return 0;
return 1; return 1;
@ -2910,6 +2912,8 @@ void diffcore_std(struct diff_options *options)
diffcore_order(options->orderfile); diffcore_order(options->orderfile);
diff_resolve_rename_copy(); diff_resolve_rename_copy();
diffcore_apply_filter(options->filter); diffcore_apply_filter(options->filter);
if (options->exit_with_status)
options->has_changes = !!diff_queued_diff.nr;
} }
@ -2920,6 +2924,8 @@ void diffcore_std_no_resolve(struct diff_options *options)
if (options->orderfile) if (options->orderfile)
diffcore_order(options->orderfile); diffcore_order(options->orderfile);
diffcore_apply_filter(options->filter); diffcore_apply_filter(options->filter);
if (options->exit_with_status)
options->has_changes = !!diff_queued_diff.nr;
} }
void diff_addremove(struct diff_options *options, void diff_addremove(struct diff_options *options,

5
diff.h
View File

@ -56,7 +56,8 @@ struct diff_options {
silent_on_remove:1, silent_on_remove:1,
find_copies_harder:1, find_copies_harder:1,
color_diff:1, color_diff:1,
color_diff_words:1; color_diff_words:1,
exit_with_status:1;
int context; int context;
int break_opt; int break_opt;
int detect_rename; int detect_rename;
@ -71,6 +72,8 @@ struct diff_options {
const char *msg_sep; const char *msg_sep;
const char *stat_sep; const char *stat_sep;
long xdl_opts; long xdl_opts;
/* 0 - no differences; only meaningful if exit_with_status set */
int has_changes;
int stat_width; int stat_width;
int stat_name_width; int stat_name_width;

79
t/t4017-diff-retval.sh Executable file
View File

@ -0,0 +1,79 @@
#!/bin/sh
test_description='Return value of diffs'
. ./test-lib.sh
test_expect_success 'setup' '
echo 1 >a &&
git add . &&
git commit -m first &&
echo 2 >b &&
git add . &&
git commit -a -m second
'
test_expect_success 'git diff-tree HEAD^ HEAD' '
git diff-tree --exit-code HEAD^ HEAD
test $? = 1
'
test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
git diff-tree --exit-code HEAD^ HEAD -- a
test $? = 0
'
test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
git diff-tree --exit-code HEAD^ HEAD -- b
test $? = 1
'
test_expect_success 'echo HEAD | git diff-tree --stdin' '
echo $(git rev-parse HEAD) | git diff-tree --exit-code --stdin
test $? = 1
'
test_expect_success 'git diff-tree HEAD HEAD' '
git diff-tree --exit-code HEAD HEAD
test $? = 0
'
test_expect_success 'git diff-files' '
git diff-files --exit-code
test $? = 0
'
test_expect_success 'git diff-index --cached HEAD' '
git diff-index --exit-code --cached HEAD
test $? = 0
'
test_expect_success 'git diff-index --cached HEAD^' '
git diff-index --exit-code --cached HEAD^
test $? = 1
'
test_expect_success 'git diff-index --cached HEAD^' '
echo text >>b &&
echo 3 >c &&
git add . && {
git diff-index --exit-code --cached HEAD^
test $? = 1
}
'
test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
git commit -m "text in b" && {
git diff-tree -p --exit-code -Stext HEAD^ HEAD -- b
test $? = 1
}
'
test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
git diff-tree -p --exit-code -Snot-found HEAD^ HEAD -- b
test $? = 0
'
test_expect_success 'git diff-files' '
echo 3 >>c && {
git diff-files --exit-code
test $? = 1
}
'
test_expect_success 'git diff-index --cached HEAD' '
git update-index c && {
git diff-index --exit-code --cached HEAD
test $? = 1
}
'
test_done