Teach git-diff-files the new option --no-index

With this flag and given two paths, git-diff-files behaves as a GNU diff
lookalike (plus the git goodies like --check, colour, etc.).  This flag
is also available in git-diff.  It also works outside of a git repository.

In addition, if git-diff{,-files} is called without revision or stage
parameter, and with exactly two paths at least one of which is not tracked,
the default is --no-index.

So, you can now say

	git diff /etc/inittab /etc/fstab

and it actually works!

This also unifies the duplicated argument parsing between cmd_diff_files()
and builtin_diff_files().

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Johannes Schindelin 2007-02-22 21:50:10 +01:00 committed by Junio C Hamano
parent f5a9264769
commit d516c2d119
8 changed files with 227 additions and 64 deletions

View File

@ -8,7 +8,7 @@ git-diff-files - Compares files in the working tree and the index
SYNOPSIS SYNOPSIS
-------- --------
'git-diff-files' [-q] [-0|-1|-2|-3|-c|--cc] [<common diff options>] [<path>...] 'git-diff-files' [-q] [-0|-1|-2|-3|-c|--cc|-n|--no-index] [<common diff options>] [<path>...]
DESCRIPTION DESCRIPTION
----------- -----------
@ -36,6 +36,9 @@ omit diff output for unmerged entries and just show "Unmerged".
diff, similar to the way 'diff-tree' shows a merge diff, similar to the way 'diff-tree' shows a merge
commit with these flags. commit with these flags.
\-n,\--no-index::
Compare the two given files / directories.
-q:: -q::
Remain silent even on nonexistent files Remain silent even on nonexistent files

View File

@ -23,6 +23,10 @@ tree and the index file, or the index file and the working tree.
further add to the index but you still haven't. You can further add to the index but you still haven't. You can
stage these changes by using gitlink:git-add[1]. stage these changes by using gitlink:git-add[1].
If exactly two paths are given, and at least one is untracked,
compare the two files / directories. This behavior can be
forced by --no-index.
'git-diff' [--options] --cached [<commit>] [--] [<path>...]:: 'git-diff' [--options] --cached [<commit>] [--] [<path>...]::
This form is to view the changes you staged for the next This form is to view the changes you staged for the next

View File

@ -10,42 +10,21 @@
#include "builtin.h" #include "builtin.h"
static const char diff_files_usage[] = static const char diff_files_usage[] =
"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]" "git-diff-files [-q] [-0/-1/2/3 |-c|--cc|-n|--no-index] [<common diff options>] [<path>...]"
COMMON_DIFF_OPTIONS_HELP; COMMON_DIFF_OPTIONS_HELP;
int cmd_diff_files(int argc, const char **argv, const char *prefix) int cmd_diff_files(int argc, const char **argv, const char *prefix)
{ {
struct rev_info rev; struct rev_info rev;
int silent = 0; int nongit = 0;
prefix = setup_git_directory_gently(&nongit);
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 */
rev.abbrev = 0; rev.abbrev = 0;
argc = setup_revisions(argc, argv, &rev, NULL); argc = setup_revisions(argc, argv, &rev, NULL);
while (1 < argc && argv[1][0] == '-') {
if (!strcmp(argv[1], "--base"))
rev.max_count = 1;
else if (!strcmp(argv[1], "--ours"))
rev.max_count = 2;
else if (!strcmp(argv[1], "--theirs"))
rev.max_count = 3;
else if (!strcmp(argv[1], "-q"))
silent = 1;
else
usage(diff_files_usage);
argv++; argc--;
}
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);
/*
* Make sure there are NO revision (i.e. pending object) parameter,
* rev.max_count is reasonable (0 <= n <= 3),
* there is no other revision filtering parameters.
*/
if (rev.pending.nr ||
rev.min_age != -1 || rev.max_age != -1)
usage(diff_files_usage);
return run_diff_files(&rev, silent);
} }

View File

@ -25,40 +25,6 @@ struct blobinfo {
static const char builtin_diff_usage[] = static const char builtin_diff_usage[] =
"git-diff <options> <rev>{0,2} -- <path>*"; "git-diff <options> <rev>{0,2} -- <path>*";
static int builtin_diff_files(struct rev_info *revs,
int argc, const char **argv)
{
int silent = 0;
while (1 < argc) {
const char *arg = argv[1];
if (!strcmp(arg, "--base"))
revs->max_count = 1;
else if (!strcmp(arg, "--ours"))
revs->max_count = 2;
else if (!strcmp(arg, "--theirs"))
revs->max_count = 3;
else if (!strcmp(arg, "-q"))
silent = 1;
else
usage(builtin_diff_usage);
argv++; argc--;
}
/*
* Make sure there are NO revision (i.e. pending object) parameter,
* specified rev.max_count is reasonable (0 <= n <= 3), and
* there is no other revision filtering parameter.
*/
if (revs->pending.nr ||
revs->min_age != -1 ||
revs->max_age != -1 ||
3 < revs->max_count)
usage(builtin_diff_usage);
if (revs->max_count < 0 &&
(revs->diffopt.output_format & DIFF_FORMAT_PATCH))
revs->combine_merges = revs->dense_combined_merges = 1;
return run_diff_files(revs, silent);
}
static void stuff_change(struct diff_options *opt, static void stuff_change(struct diff_options *opt,
unsigned old_mode, unsigned new_mode, unsigned old_mode, unsigned new_mode,
const unsigned char *old_sha1, const unsigned char *old_sha1,
@ -218,6 +184,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
int ents = 0, blobs = 0, paths = 0; int ents = 0, blobs = 0, paths = 0;
const char *path = NULL; const char *path = NULL;
struct blobinfo blob[2]; struct blobinfo blob[2];
int nongit = 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.
@ -239,6 +206,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
* Other cases are errors. * Other cases are errors.
*/ */
prefix = setup_git_directory_gently(&nongit);
git_config(git_diff_ui_config); git_config(git_diff_ui_config);
init_revisions(&rev, prefix); init_revisions(&rev, prefix);
@ -314,7 +282,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
if (!ents) { if (!ents) {
switch (blobs) { switch (blobs) {
case 0: case 0:
return builtin_diff_files(&rev, argc, argv); return run_diff_files_cmd(&rev, argc, argv);
break; break;
case 1: case 1:
if (paths != 1) if (paths != 1)

View File

@ -8,11 +8,218 @@
#include "diffcore.h" #include "diffcore.h"
#include "revision.h" #include "revision.h"
#include "cache-tree.h" #include "cache-tree.h"
#include "path-list.h"
/* /*
* diff-files * diff-files
*/ */
static int read_directory(const char *path, struct path_list *list)
{
DIR *dir;
struct dirent *e;
if (!(dir = opendir(path)))
return error("Could not open directory %s", path);
while ((e = readdir(dir)))
if (strcmp(".", e->d_name) && strcmp("..", e->d_name))
path_list_insert(xstrdup(e->d_name), list);
closedir(dir);
return 0;
}
static int queue_diff(struct diff_options *o,
const char *name1, const char *name2)
{
struct stat st;
int mode1 = 0, mode2 = 0;
if (name1) {
if (stat(name1, &st))
return error("Could not access '%s'", name1);
mode1 = st.st_mode;
}
if (name2) {
if (stat(name2, &st))
return error("Could not access '%s'", name1);
mode2 = st.st_mode;
}
if (mode1 && mode2 && S_ISDIR(mode1) != S_ISDIR(mode2))
return error("file/directory conflict: %s, %s", name1, name2);
if (S_ISDIR(mode1) || S_ISDIR(mode2)) {
char buffer1[PATH_MAX], buffer2[PATH_MAX];
struct path_list p1 = {NULL, 0, 0, 1}, p2 = {NULL, 0, 0, 1};
int len1 = 0, len2 = 0, i1, i2, ret = 0;
if (name1 && read_directory(name1, &p1))
return -1;
if (name2 && read_directory(name2, &p2)) {
path_list_clear(&p1, 0);
return -1;
}
if (name1) {
len1 = strlen(name1);
if (len1 > 0 && name1[len1 - 1] == '/')
len1--;
memcpy(buffer1, name1, len1);
buffer1[len1++] = '/';
}
if (name2) {
len2 = strlen(name2);
if (len2 > 0 && name2[len2 - 1] == '/')
len2--;
memcpy(buffer2, name2, len2);
buffer2[len2++] = '/';
}
for (i1 = i2 = 0; !ret && (i1 < p1.nr || i2 < p2.nr); ) {
const char *n1, *n2;
int comp;
if (i1 == p1.nr)
comp = 1;
else if (i2 == p2.nr)
comp = -1;
else
comp = strcmp(p1.items[i1].path,
p2.items[i2].path);
if (comp > 0)
n1 = NULL;
else {
n1 = buffer1;
strncpy(buffer1 + len1, p1.items[i1++].path,
PATH_MAX - len1);
}
if (comp < 0)
n2 = NULL;
else {
n2 = buffer2;
strncpy(buffer2 + len2, p2.items[i2++].path,
PATH_MAX - len2);
}
ret = queue_diff(o, n1, n2);
}
path_list_clear(&p1, 0);
path_list_clear(&p2, 0);
return ret;
} else {
struct diff_filespec *d1, *d2;
if (o->reverse_diff) {
unsigned tmp;
const char *tmp_c;
tmp = mode1; mode1 = mode2; mode2 = tmp;
tmp_c = name1; name1 = name2; name2 = tmp_c;
}
if (!name1)
name1 = "/dev/null";
if (!name2)
name2 = "/dev/null";
d1 = alloc_filespec(name1);
d2 = alloc_filespec(name2);
fill_filespec(d1, null_sha1, mode1);
fill_filespec(d2, null_sha1, mode2);
diff_queue(&diff_queued_diff, d1, d2);
return 0;
}
}
static int is_in_index(const char *path)
{
int len = strlen(path);
int pos = cache_name_pos(path, len);
char c;
if (pos < 0)
return 0;
if (strncmp(active_cache[pos]->name, path, len))
return 0;
c = active_cache[pos]->name[len];
return c == '\0' || c == '/';
}
static int handle_diff_files_args(struct rev_info *revs,
int argc, const char **argv, int *silent)
{
*silent = 0;
/* revs->max_count == -2 means --no-index */
while (1 < argc && argv[1][0] == '-') {
if (!strcmp(argv[1], "--base"))
revs->max_count = 1;
else if (!strcmp(argv[1], "--ours"))
revs->max_count = 2;
else if (!strcmp(argv[1], "--theirs"))
revs->max_count = 3;
else if (!strcmp(argv[1], "-n") ||
!strcmp(argv[1], "--no-index"))
revs->max_count = -2;
else if (!strcmp(argv[1], "-q"))
*silent = 1;
else
return error("invalid option: %s", argv[1]);
argv++; argc--;
}
if (revs->max_count == -1 && revs->diffopt.nr_paths == 2) {
/*
* If two files are specified, and at least one is untracked,
* default to no-index.
*/
read_cache();
if (!is_in_index(revs->diffopt.paths[0]) ||
!is_in_index(revs->diffopt.paths[1]))
revs->max_count = -2;
}
/*
* Make sure there are NO revision (i.e. pending object) parameter,
* rev.max_count is reasonable (0 <= n <= 3),
* there is no other revision filtering parameters.
*/
if (revs->pending.nr || revs->max_count > 3 ||
revs->min_age != -1 || revs->max_age != -1)
return error("no revision allowed with diff-files");
if (revs->max_count == -1 &&
(revs->diffopt.output_format & DIFF_FORMAT_PATCH))
revs->combine_merges = revs->dense_combined_merges = 1;
return 0;
}
int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)
{
int silent_on_removed;
if (handle_diff_files_args(revs, argc, argv, &silent_on_removed))
return -1;
if (revs->max_count == -2) {
if (revs->diffopt.nr_paths != 2)
return error("need two files/directories with --no-index");
queue_diff(&revs->diffopt, revs->diffopt.paths[0],
revs->diffopt.paths[1]);
diffcore_std(&revs->diffopt);
diff_flush(&revs->diffopt);
return 0;
}
return run_diff_files(revs, silent_on_removed);
}
int run_diff_files(struct rev_info *revs, int silent_on_removed) int run_diff_files(struct rev_info *revs, int silent_on_removed)
{ {
int entries, i; int entries, i;

3
diff.c
View File

@ -2406,7 +2406,8 @@ static void diff_resolve_rename_copy(void)
p->status = DIFF_STATUS_RENAMED; p->status = DIFF_STATUS_RENAMED;
} }
else if (hashcmp(p->one->sha1, p->two->sha1) || else if (hashcmp(p->one->sha1, p->two->sha1) ||
p->one->mode != p->two->mode) p->one->mode != p->two->mode ||
is_null_sha1(p->one->sha1))
p->status = DIFF_STATUS_MODIFIED; p->status = DIFF_STATUS_MODIFIED;
else { else {
/* This is a "no-change" entry and should not /* This is a "no-change" entry and should not

1
diff.h
View File

@ -219,6 +219,7 @@ extern void diff_flush(struct diff_options*);
extern const char *diff_unique_abbrev(const unsigned char *, int); extern const char *diff_unique_abbrev(const unsigned char *, int);
extern int run_diff_files(struct rev_info *revs, int silent_on_removed); extern int run_diff_files(struct rev_info *revs, int silent_on_removed);
extern int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv);
extern int run_diff_index(struct rev_info *revs, int cached); extern int run_diff_index(struct rev_info *revs, int cached);

4
git.c
View File

@ -237,8 +237,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "config", cmd_config }, { "config", cmd_config },
{ "count-objects", cmd_count_objects, RUN_SETUP }, { "count-objects", cmd_count_objects, RUN_SETUP },
{ "describe", cmd_describe, RUN_SETUP }, { "describe", cmd_describe, RUN_SETUP },
{ "diff", cmd_diff, RUN_SETUP | USE_PAGER }, { "diff", cmd_diff, USE_PAGER },
{ "diff-files", cmd_diff_files, RUN_SETUP }, { "diff-files", cmd_diff_files },
{ "diff-index", cmd_diff_index, RUN_SETUP }, { "diff-index", cmd_diff_index, RUN_SETUP },
{ "diff-tree", cmd_diff_tree, RUN_SETUP }, { "diff-tree", cmd_diff_tree, RUN_SETUP },
{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP }, { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },