{"thread":{"id":"6898","subject":"[PATCH] Teach git-diff{,-files} the new option `--no-index`","startedAt":"2007-02-20T18:46:28Z","lastAt":"2007-02-23T16:07:16Z","messageCount":13,"participants":["Johannes Schindelin","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"35133","messageId":"Pine.LNX.4.63.0702201944340.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6898","inReplyTo":null,"subject":"[PATCH] Teach git-diff{,-files} the new option `--no-index`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-02-20T18:46:28Z","receivedAt":"2007-02-20T18:46:28Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nWith this flag and given two paths, git-diff-files behaves as a GNU diff\nlookalike (plus the git goodies like --check, colour, etc.).  This flag\nis also available in git-diff.  It also works outside of a git repository.\n\nIn addition, if git-diff{,-files} is called without revision or stage\nparameter, and with exactly two paths at least one of which is not tracked,\nthe default is --no-index.\n\nThis also unifies the duplicated argument parsing between cmd_diff_files()\nand builtin_diff_files().\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tIs this better than diff2 or what?\n\n\tThis patch is only weakly tested; basically it passes `make test`.\n\n Documentation/git-diff-files.txt |    5 +-\n Documentation/git-diff.txt       |    4 +\n builtin-diff-files.c             |   29 +-----\n builtin-diff.c                   |   38 +-------\n diff-lib.c                       |  207 +++++++++++++++++++++++++++++++++++++-\n diff.c                           |    3 +-\n diff.h                           |    2 +-\n git.c                            |    4 +-\n wt-status.c                      |    2 +-\n 9 files changed, 226 insertions(+), 68 deletions(-)\n\ndiff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt\nindex 7248b35..b78c4c6 100644\n--- a/Documentation/git-diff-files.txt\n+++ b/Documentation/git-diff-files.txt\n@@ -8,7 +8,7 @@ git-diff-files - Compares files in the working tree and the index\n \n SYNOPSIS\n --------\n-'git-diff-files' [-q] [-0|-1|-2|-3|-c|--cc] [<common diff options>] [<path>...]\n+'git-diff-files' [-q] [-0|-1|-2|-3|-c|--cc|-n|--no-index] [<common diff options>] [<path>...]\n \n DESCRIPTION\n -----------\n@@ -36,6 +36,9 @@ omit diff output for unmerged entries and just show \"Unmerged\".\n \tdiff, similar to the way 'diff-tree' shows a merge\n \tcommit with these flags.\n \n+\\-n,\\--no-index::\n+\tCompare the two given files / directories.\n+\n -q::\n \tRemain silent even on nonexistent files\n \ndiff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt\nindex 6a098df..12a531d 100644\n--- a/Documentation/git-diff.txt\n+++ b/Documentation/git-diff.txt\n@@ -23,6 +23,10 @@ tree and the index file, or the index file and the working tree.\n \tfurther add to the index but you still haven't.  You can\n \tstage these changes by using gitlink:git-add[1].\n \n+\tIf exactly two paths are given, and at least one is untracked,\n+\tcompare the two files / directories. This behavior can be\n+\tforced by --no-index.\n+\n 'git-diff' [--options] --cached [<commit>] [--] [<path>...]::\n \n \tThis form is to view the changes you staged for the next\ndiff --git a/builtin-diff-files.c b/builtin-diff-files.c\nindex 5d4a5c5..8d78dfa 100644\n--- a/builtin-diff-files.c\n+++ b/builtin-diff-files.c\n@@ -10,42 +10,21 @@\n #include \"builtin.h\"\n \n static const char diff_files_usage[] =\n-\"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]\"\n+\"git-diff-files [-q] [-0/-1/2/3 |-c|--cc|-n|--no-index] [<common diff options>] [<path>...]\"\n COMMON_DIFF_OPTIONS_HELP;\n \n int cmd_diff_files(int argc, const char **argv, const char *prefix)\n {\n \tstruct rev_info rev;\n-\tint silent = 0;\n+\tint nongit = 0;\n \n+\tprefix = setup_git_directory_gently(&nongit);\n \tinit_revisions(&rev, prefix);\n \tgit_config(git_default_config); /* no \"diff\" UI options */\n \trev.abbrev = 0;\n \n \targc = setup_revisions(argc, argv, &rev, NULL);\n-\twhile (1 < argc && argv[1][0] == '-') {\n-\t\tif (!strcmp(argv[1], \"--base\"))\n-\t\t\trev.max_count = 1;\n-\t\telse if (!strcmp(argv[1], \"--ours\"))\n-\t\t\trev.max_count = 2;\n-\t\telse if (!strcmp(argv[1], \"--theirs\"))\n-\t\t\trev.max_count = 3;\n-\t\telse if (!strcmp(argv[1], \"-q\"))\n-\t\t\tsilent = 1;\n-\t\telse\n-\t\t\tusage(diff_files_usage);\n-\t\targv++; argc--;\n-\t}\n \tif (!rev.diffopt.output_format)\n \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\n-\n-\t/*\n-\t * Make sure there are NO revision (i.e. pending object) parameter,\n-\t * rev.max_count is reasonable (0 <= n <= 3),\n-\t * there is no other revision filtering parameters.\n-\t */\n-\tif (rev.pending.nr ||\n-\t    rev.min_age != -1 || rev.max_age != -1)\n-\t\tusage(diff_files_usage);\n-\treturn run_diff_files(&rev, silent);\n+\treturn run_diff_files(&rev, argc, argv);\n }\ndiff --git a/builtin-diff.c b/builtin-diff.c\nindex a659020..9334589 100644\n--- a/builtin-diff.c\n+++ b/builtin-diff.c\n@@ -25,40 +25,6 @@ struct blobinfo {\n static const char builtin_diff_usage[] =\n \"git-diff <options> <rev>{0,2} -- <path>*\";\n \n-static int builtin_diff_files(struct rev_info *revs,\n-\t\t\t      int argc, const char **argv)\n-{\n-\tint silent = 0;\n-\twhile (1 < argc) {\n-\t\tconst char *arg = argv[1];\n-\t\tif (!strcmp(arg, \"--base\"))\n-\t\t\trevs->max_count = 1;\n-\t\telse if (!strcmp(arg, \"--ours\"))\n-\t\t\trevs->max_count = 2;\n-\t\telse if (!strcmp(arg, \"--theirs\"))\n-\t\t\trevs->max_count = 3;\n-\t\telse if (!strcmp(arg, \"-q\"))\n-\t\t\tsilent = 1;\n-\t\telse\n-\t\t\tusage(builtin_diff_usage);\n-\t\targv++; argc--;\n-\t}\n-\t/*\n-\t * Make sure there are NO revision (i.e. pending object) parameter,\n-\t * specified rev.max_count is reasonable (0 <= n <= 3), and\n-\t * there is no other revision filtering parameter.\n-\t */\n-\tif (revs->pending.nr ||\n-\t    revs->min_age != -1 ||\n-\t    revs->max_age != -1 ||\n-\t    3 < revs->max_count)\n-\t\tusage(builtin_diff_usage);\n-\tif (revs->max_count < 0 &&\n-\t    (revs->diffopt.output_format & DIFF_FORMAT_PATCH))\n-\t\trevs->combine_merges = revs->dense_combined_merges = 1;\n-\treturn run_diff_files(revs, silent);\n-}\n-\n static void stuff_change(struct diff_options *opt,\n \t\t\t unsigned old_mode, unsigned new_mode,\n \t\t\t const unsigned char *old_sha1,\n@@ -218,6 +184,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \tint ents = 0, blobs = 0, paths = 0;\n \tconst char *path = NULL;\n \tstruct blobinfo blob[2];\n+\tint nongit = 0;\n \n \t/*\n \t * We could get N tree-ish in the rev.pending_objects list.\n@@ -239,6 +206,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \t * Other cases are errors.\n \t */\n \n+\tprefix = setup_git_directory_gently(&nongit);\n \tgit_config(git_diff_ui_config);\n \tinit_revisions(&rev, prefix);\n \n@@ -314,7 +282,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \tif (!ents) {\n \t\tswitch (blobs) {\n \t\tcase 0:\n-\t\t\treturn builtin_diff_files(&rev, argc, argv);\n+\t\t\treturn run_diff_files(&rev, argc, argv);\n \t\t\tbreak;\n \t\tcase 1:\n \t\t\tif (paths != 1)\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 556d534..83d6682 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -8,15 +8,218 @@\n #include \"diffcore.h\"\n #include \"revision.h\"\n #include \"cache-tree.h\"\n+#include \"path-list.h\"\n \n /*\n  * diff-files\n  */\n \n-int run_diff_files(struct rev_info *revs, int silent_on_removed)\n+static int read_directory(const char *path, struct path_list *list)\n+{\n+\tDIR *dir;\n+\tstruct dirent *e;\n+\n+\tif (!(dir = opendir(path)))\n+\t\treturn error(\"Could not open directory %s\", path);\n+\n+\twhile ((e = readdir(dir)))\n+\t\tif (strcmp(\".\", e->d_name) && strcmp(\"..\", e->d_name))\n+\t\t\tpath_list_insert(xstrdup(e->d_name), list);\n+\n+\tclosedir(dir);\n+\treturn 0;\n+}\n+\n+static int queue_diff(struct diff_options *o,\n+\t\tconst char *name1, const char *name2)\n+{\n+\tstruct stat st;\n+\tint mode1 = 0, mode2 = 0;\n+\n+\tif (name1) {\n+\t\tif (stat(name1, &st))\n+\t\t\treturn error(\"Could not access '%s'\", name1);\n+\t\tmode1 = st.st_mode;\n+\t}\n+\tif (name2) {\n+\t\tif (stat(name2, &st))\n+\t\t\treturn error(\"Could not access '%s'\", name1);\n+\t\tmode2 = st.st_mode;\n+\t}\n+\n+\tif (mode1 && mode2 && S_ISDIR(mode1) != S_ISDIR(mode2))\n+\t\treturn error(\"file/directory conflict: %s, %s\", name1, name2);\n+\n+\tif (S_ISDIR(mode1) || S_ISDIR(mode2)) {\n+\t\tchar buffer1[PATH_MAX], buffer2[PATH_MAX];\n+\t\tstruct path_list p1 = {NULL, 0, 0, 1}, p2 = {NULL, 0, 0, 1};\n+\t\tint len1 = 0, len2 = 0, i1, i2, ret = 0;\n+\n+\t\tif (name1 && read_directory(name1, &p1))\n+\t\t\treturn -1;\n+\t\tif (name2 && read_directory(name2, &p2)) {\n+\t\t\tpath_list_clear(&p1, 0);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (name1) {\n+\t\t\tlen1 = strlen(name1);\n+\t\t\tif (len1 > 0 && name1[len1 - 1] == '/')\n+\t\t\t\tlen1--;\n+\t\t\tmemcpy(buffer1, name1, len1);\n+\t\t\tbuffer1[len1++] = '/';\n+\t\t}\n+\n+\t\tif (name2) {\n+\t\t\tlen2 = strlen(name2);\n+\t\t\tif (len2 > 0 && name2[len2 - 1] == '/')\n+\t\t\t\tlen2--;\n+\t\t\tmemcpy(buffer2, name2, len2);\n+\t\t\tbuffer2[len2++] = '/';\n+\t\t}\n+\n+\t\tfor (i1 = i2 = 0; !ret && (i1 < p1.nr || i2 < p2.nr); ) {\n+\t\t\tconst char *n1, *n2;\n+\t\t\tint comp;\n+\n+\t\t\tif (i1 == p1.nr)\n+\t\t\t\tcomp = 1;\n+\t\t\telse if (i2 == p2.nr)\n+\t\t\t\tcomp = -1;\n+\t\t\telse\n+\t\t\t\tcomp = strcmp(p1.items[i1].path,\n+\t\t\t\t\tp2.items[i2].path);\n+\n+\t\t\tif (comp > 0)\n+\t\t\t\tn1 = NULL;\n+\t\t\telse {\n+\t\t\t\tn1 = buffer1;\n+\t\t\t\tstrncpy(buffer1 + len1, p1.items[i1++].path,\n+\t\t\t\t\t\tPATH_MAX - len1);\n+\t\t\t}\n+\n+\t\t\tif (comp < 0)\n+\t\t\t\tn2 = NULL;\n+\t\t\telse {\n+\t\t\t\tn2 = buffer2;\n+\t\t\t\tstrncpy(buffer2 + len2, p2.items[i2++].path,\n+\t\t\t\t\t\tPATH_MAX - len2);\n+\t\t\t}\n+\n+\t\t\tret = queue_diff(o, n1, n2);\n+\t\t}\n+\t\tpath_list_clear(&p1, 0);\n+\t\tpath_list_clear(&p2, 0);\n+\n+\t\treturn ret;\n+\t} else {\n+\t\tstruct diff_filespec *d1, *d2;\n+\n+\t\tif (o->reverse_diff) {\n+\t\t\tunsigned tmp;\n+\t\t\tconst char *tmp_c;\n+\t\t\ttmp = mode1; mode1 = mode2; mode2 = tmp;\n+\t\t\ttmp_c = name1; name1 = name2; name2 = tmp_c;\n+\t\t}\n+\n+\t\tif (!name1)\n+\t\t\tname1 = \"/dev/null\";\n+\t\tif (!name2)\n+\t\t\tname2 = \"/dev/null\";\n+\t\td1 = alloc_filespec(name1);\n+\t\td2 = alloc_filespec(name2);\n+\t\tfill_filespec(d1, null_sha1, mode1);\n+\t\tfill_filespec(d2, null_sha1, mode2);\n+\n+\t\tdiff_queue(&diff_queued_diff, d1, d2);\n+\t\treturn 0;\n+\t}\n+}\n+\n+static int is_in_index(const char *path)\n+{\n+\tint len = strlen(path);\n+\tint pos = cache_name_pos(path, len);\n+\tchar c;\n+\n+\tif (pos < 0)\n+\t\treturn 0;\n+\tif (strncmp(active_cache[pos]->name, path, len))\n+\t\treturn 0;\n+\tc = active_cache[pos]->name[len];\n+\treturn c == '\\0' || c == '/';\n+}\n+\n+static int handle_diff_files_args(struct rev_info *revs,\n+\t\tint argc, const char **argv, int *silent)\n+{\n+\t*silent = 0;\n+\n+\t/* revs->max_count == -2 means --no-index */\n+\twhile (1 < argc && argv[1][0] == '-') {\n+\t\tif (!strcmp(argv[1], \"--base\"))\n+\t\t\trevs->max_count = 1;\n+\t\telse if (!strcmp(argv[1], \"--ours\"))\n+\t\t\trevs->max_count = 2;\n+\t\telse if (!strcmp(argv[1], \"--theirs\"))\n+\t\t\trevs->max_count = 3;\n+\t\telse if (!strcmp(argv[1], \"-n\") ||\n+\t\t\t\t!strcmp(argv[1], \"--no-index\"))\n+\t\t\trevs->max_count = -2;\n+\t\telse if (!strcmp(argv[1], \"-q\"))\n+\t\t\t*silent = 1;\n+\t\telse\n+\t\t\treturn error(\"invalid option: %s\", argv[1]);\n+\t\targv++; argc--;\n+\t}\n+\n+\tif (revs->max_count == -1 && revs->diffopt.nr_paths == 2) {\n+\t\t/*\n+\t\t * If two files are specified, and at least one is untracked,\n+\t\t * default to no-index.\n+\t\t */\n+\t\tread_cache();\n+\t\tif (!is_in_index(revs->diffopt.paths[0]) ||\n+\t\t\t\t\t!is_in_index(revs->diffopt.paths[1]))\n+\t\t\trevs->max_count = -2;\n+\t}\n+\n+\t/*\n+\t * Make sure there are NO revision (i.e. pending object) parameter,\n+\t * rev.max_count is reasonable (0 <= n <= 3),\n+\t * there is no other revision filtering parameters.\n+\t */\n+\tif (revs->pending.nr || revs->max_count > 3 ||\n+\t    revs->min_age != -1 || revs->max_age != -1)\n+\t\treturn error(\"no revision allowed with diff-files\");\n+\n+\tif (revs->max_count == -1 &&\n+\t    (revs->diffopt.output_format & DIFF_FORMAT_PATCH))\n+\t\trevs->combine_merges = revs->dense_combined_merges = 1;\n+\n+\treturn 0;\n+}\n+\n+int run_diff_files(struct rev_info *revs, int argc, const char **argv)\n {\n \tint entries, i;\n-\tint diff_unmerged_stage = revs->max_count;\n+\tint diff_unmerged_stage;\n+\tint silent_on_removed;\n+\n+\tif (handle_diff_files_args(revs, argc, argv, &silent_on_removed))\n+\t\treturn -1;\n+\n+\tdiff_unmerged_stage = revs->max_count;\n+\n+\tif (revs->max_count == -2) {\n+\t\tif (revs->diffopt.nr_paths != 2)\n+\t\t\treturn error(\"need two files/directories with --no-index\");\n+\t\tqueue_diff(&revs->diffopt, revs->diffopt.paths[0],\n+\t\t\t\trevs->diffopt.paths[1]);\n+\t\tdiffcore_std(&revs->diffopt);\n+\t\tdiff_flush(&revs->diffopt);\n+\t\treturn 0;\n+\t}\n \n \tif (diff_unmerged_stage < 0)\n \t\tdiff_unmerged_stage = 2;\ndiff --git a/diff.c b/diff.c\nindex 12c8b2b..701880a 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -2406,7 +2406,8 @@ static void diff_resolve_rename_copy(void)\n \t\t\t\tp->status = DIFF_STATUS_RENAMED;\n \t\t}\n \t\telse if (hashcmp(p->one->sha1, p->two->sha1) ||\n-\t\t\t p->one->mode != p->two->mode)\n+\t\t\t p->one->mode != p->two->mode ||\n+\t\t\t is_null_sha1(p->one->sha1))\n \t\t\tp->status = DIFF_STATUS_MODIFIED;\n \t\telse {\n \t\t\t/* This is a \"no-change\" entry and should not\ndiff --git a/diff.h b/diff.h\nindex eece65d..cc28418 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -218,7 +218,7 @@ extern void diff_flush(struct diff_options*);\n \n extern const char *diff_unique_abbrev(const unsigned char *, int);\n \n-extern int run_diff_files(struct rev_info *revs, int silent_on_removed);\n+extern int run_diff_files(struct rev_info *revs, int argc, const char **argv);\n \n extern int run_diff_index(struct rev_info *revs, int cached);\n \ndiff --git a/git.c b/git.c\nindex 4dd1967..79fc73c 100644\n--- a/git.c\n+++ b/git.c\n@@ -237,8 +237,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp)\n \t\t{ \"config\", cmd_config },\n \t\t{ \"count-objects\", cmd_count_objects, RUN_SETUP },\n \t\t{ \"describe\", cmd_describe, RUN_SETUP },\n-\t\t{ \"diff\", cmd_diff, RUN_SETUP | USE_PAGER },\n-\t\t{ \"diff-files\", cmd_diff_files, RUN_SETUP },\n+\t\t{ \"diff\", cmd_diff, USE_PAGER },\n+\t\t{ \"diff-files\", cmd_diff_files },\n \t\t{ \"diff-index\", cmd_diff_index, RUN_SETUP },\n \t\t{ \"diff-tree\", cmd_diff_tree, RUN_SETUP },\n \t\t{ \"fmt-merge-msg\", cmd_fmt_merge_msg, RUN_SETUP },\ndiff --git a/wt-status.c b/wt-status.c\nindex 2879c3d..b68eea3 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -231,7 +231,7 @@ static void wt_status_print_changed(struct wt_status *s)\n \trev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;\n \trev.diffopt.format_callback = wt_status_print_changed_cb;\n \trev.diffopt.format_callback_data = s;\n-\trun_diff_files(&rev, 0);\n+\trun_diff_files(&rev, 0, NULL);\n }\n \n static void wt_status_print_untracked(struct wt_status *s)\n-- \n1.5.0.50.gb7581-dirty\n"},{"id":"35225","messageId":"7vzm76fpia.fsf@assigned-by-dhcp.cox.net","threadId":"6898","inReplyTo":"Pine.LNX.4.63.0702201944340.22628@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] Teach git-diff{,-files} the new option `--no-index`","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-02-22T10:36:13Z","receivedAt":"2007-02-22T10:36:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> \tIs this better than diff2 or what?\n\nProbably.\n\n> +int run_diff_files(struct rev_info *revs, int argc, const char **argv)\n>  {\n>  \tint entries, i;\n> +\tint diff_unmerged_stage;\n> +\tint silent_on_removed;\n> +\n> +\tif (handle_diff_files_args(revs, argc, argv, &silent_on_removed))\n> +\t\treturn -1;\n> +\n> +\tdiff_unmerged_stage = revs->max_count;\n> +\n> +\tif (revs->max_count == -2) {\n> +\t\tif (revs->diffopt.nr_paths != 2)\n> +\t\t\treturn error(\"need two files/directories with --no-index\");\n> +\t\tqueue_diff(&revs->diffopt, revs->diffopt.paths[0],\n> +\t\t\t\trevs->diffopt.paths[1]);\n> +\t\tdiffcore_std(&revs->diffopt);\n> +\t\tdiff_flush(&revs->diffopt);\n> +\t\treturn 0;\n> +\t}\n\nHowever, I do not like this part.\n\nI do not mind the \"max_count == -2 is some magic\" hack too much,\nbut I do not think it belongs to a generic service function\nwhose purpose is to run \"diff-files\" aka \"diff between index and\nworking tree\".  run_diff_files() is used by other applications\n(most notably wt-status) for example, and I expect we would need\nthat when we rewrite git-commit itself in C.\n"},{"id":"35237","messageId":"Pine.LNX.4.63.0702221724090.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6898","inReplyTo":"7vzm76fpia.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Teach git-diff{,-files} the new option `--no-index`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-02-22T16:25:49Z","receivedAt":"2007-02-22T16:25:49Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 22 Feb 2007, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > +int run_diff_files(struct rev_info *revs, int argc, const char **argv)\n> >  {\n> >  \tint entries, i;\n> > +\tint diff_unmerged_stage;\n> > +\tint silent_on_removed;\n> > +\n> > +\tif (handle_diff_files_args(revs, argc, argv, &silent_on_removed))\n> > +\t\treturn -1;\n> > +\n> > +\tdiff_unmerged_stage = revs->max_count;\n> > +\n> > +\tif (revs->max_count == -2) {\n> > +\t\tif (revs->diffopt.nr_paths != 2)\n> > +\t\t\treturn error(\"need two files/directories with --no-index\");\n> > +\t\tqueue_diff(&revs->diffopt, revs->diffopt.paths[0],\n> > +\t\t\t\trevs->diffopt.paths[1]);\n> > +\t\tdiffcore_std(&revs->diffopt);\n> > +\t\tdiff_flush(&revs->diffopt);\n> > +\t\treturn 0;\n> > +\t}\n> \n> I do not mind the \"max_count == -2 is some magic\" hack too much,\n> but I do not think it belongs to a generic service function\n> whose purpose is to run \"diff-files\" aka \"diff between index and\n> working tree\".  run_diff_files() is used by other applications\n> (most notably wt-status) for example, and I expect we would need\n> that when we rewrite git-commit itself in C.\n\nWell, the alternative is to duplicate code, as you need both the option \nparsing and the special handling of max_count==-2 in both builtin-diff.c \nand builtin-diff-files.c.\n\nCiao,\nDscho\n"},{"id":"35244","messageId":"7vvehuf4ic.fsf@assigned-by-dhcp.cox.net","threadId":"6898","inReplyTo":"Pine.LNX.4.63.0702221724090.22628@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] Teach git-diff{,-files} the new option `--no-index`","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-02-22T18:09:47Z","receivedAt":"2007-02-22T18:09:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Hi,\n>\n> On Thu, 22 Feb 2007, Junio C Hamano wrote:\n>\n>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>> \n>> > +int run_diff_files(struct rev_info *revs, int argc, const char **argv)\n>> >  {\n>> >  \tint entries, i;\n>> > +\tint diff_unmerged_stage;\n>> > +\tint silent_on_removed;\n>> > +\n>> > +\tif (handle_diff_files_args(revs, argc, argv, &silent_on_removed))\n>> > +\t\treturn -1;\n>> > +\n>> > +\tdiff_unmerged_stage = revs->max_count;\n>> > +\n>> > +\tif (revs->max_count == -2) {\n>> > +\t\tif (revs->diffopt.nr_paths != 2)\n>> > +\t\t\treturn error(\"need two files/directories with --no-index\");\n>> > +\t\tqueue_diff(&revs->diffopt, revs->diffopt.paths[0],\n>> > +\t\t\t\trevs->diffopt.paths[1]);\n>> > +\t\tdiffcore_std(&revs->diffopt);\n>> > +\t\tdiff_flush(&revs->diffopt);\n>> > +\t\treturn 0;\n>> > +\t}\n>> \n>> I do not mind the \"max_count == -2 is some magic\" hack too much,\n>> but I do not think it belongs to a generic service function\n>> whose purpose is to run \"diff-files\" aka \"diff between index and\n>> working tree\".  run_diff_files() is used by other applications\n>> (most notably wt-status) for example, and I expect we would need\n>> that when we rewrite git-commit itself in C.\n>\n> Well, the alternative is to duplicate code, as you need both the option \n> parsing and the special handling of max_count==-2 in both builtin-diff.c \n> and builtin-diff-files.c.\n\nCan't they made to call a new function, which has the option\nparsing followed by the switch between magic max_count==-2\ncodepath and run_diff_files()?\n"},{"id":"35246","messageId":"Pine.LNX.4.63.0702221926390.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6898","inReplyTo":"7vvehuf4ic.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Teach git-diff{,-files} the new option `--no-index`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-02-22T18:27:15Z","receivedAt":"2007-02-22T18:27:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 22 Feb 2007, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > Hi,\n> >\n> > On Thu, 22 Feb 2007, Junio C Hamano wrote:\n> >\n> >> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> >> \n> >> > +int run_diff_files(struct rev_info *revs, int argc, const char **argv)\n> >> >  {\n> >> >  \tint entries, i;\n> >> > +\tint diff_unmerged_stage;\n> >> > +\tint silent_on_removed;\n> >> > +\n> >> > +\tif (handle_diff_files_args(revs, argc, argv, &silent_on_removed))\n> >> > +\t\treturn -1;\n> >> > +\n> >> > +\tdiff_unmerged_stage = revs->max_count;\n> >> > +\n> >> > +\tif (revs->max_count == -2) {\n> >> > +\t\tif (revs->diffopt.nr_paths != 2)\n> >> > +\t\t\treturn error(\"need two files/directories with --no-index\");\n> >> > +\t\tqueue_diff(&revs->diffopt, revs->diffopt.paths[0],\n> >> > +\t\t\t\trevs->diffopt.paths[1]);\n> >> > +\t\tdiffcore_std(&revs->diffopt);\n> >> > +\t\tdiff_flush(&revs->diffopt);\n> >> > +\t\treturn 0;\n> >> > +\t}\n> >> \n> >> I do not mind the \"max_count == -2 is some magic\" hack too much,\n> >> but I do not think it belongs to a generic service function\n> >> whose purpose is to run \"diff-files\" aka \"diff between index and\n> >> working tree\".  run_diff_files() is used by other applications\n> >> (most notably wt-status) for example, and I expect we would need\n> >> that when we rewrite git-commit itself in C.\n> >\n> > Well, the alternative is to duplicate code, as you need both the option \n> > parsing and the special handling of max_count==-2 in both builtin-diff.c \n> > and builtin-diff-files.c.\n> \n> Can't they made to call a new function, which has the option\n> parsing followed by the switch between magic max_count==-2\n> codepath and run_diff_files()?\n\nHow about adding \"int always_use_index\" to the signature of \nrun_diff_files()?\n\nCiao,\nDscho\n"},{"id":"35247","messageId":"Pine.LNX.4.63.0702221952140.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6898","inReplyTo":"Pine.LNX.4.63.0702221926390.22628@wbgn013.biozentrum.uni-wuerzburg.de","subject":"[PATCH] run_diff_files(): add option to prevent --no-index","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-02-22T18:54:35Z","receivedAt":"2007-02-22T18:54:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nAn important user, wt-status, does not want no-index. So add a\nparameter to run_diff_files() which says if it is allowed or not.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\t> How about adding \"int always_use_index\" to the signature of \n\t> run_diff_files()?\n\n\tI called the parameter \"allow_no_index\", since it is a little \n\tfriendlier. I have been accused of being not friendly enough.\n\n builtin-diff-files.c |    2 +-\n builtin-diff.c       |    2 +-\n diff-lib.c           |    5 +++--\n diff.h               |    2 +-\n wt-status.c          |    2 +-\n 5 files changed, 7 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin-diff-files.c b/builtin-diff-files.c\nindex 8d78dfa..05a81b1 100644\n--- a/builtin-diff-files.c\n+++ b/builtin-diff-files.c\n@@ -26,5 +26,5 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)\n \targc = setup_revisions(argc, argv, &rev, NULL);\n \tif (!rev.diffopt.output_format)\n \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\n-\treturn run_diff_files(&rev, argc, argv);\n+\treturn run_diff_files(&rev, argc, argv, 1);\n }\ndiff --git a/builtin-diff.c b/builtin-diff.c\nindex 9334589..7f1ad60 100644\n--- a/builtin-diff.c\n+++ b/builtin-diff.c\n@@ -282,7 +282,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \tif (!ents) {\n \t\tswitch (blobs) {\n \t\tcase 0:\n-\t\t\treturn run_diff_files(&rev, argc, argv);\n+\t\t\treturn run_diff_files(&rev, argc, argv, 1);\n \t\t\tbreak;\n \t\tcase 1:\n \t\t\tif (paths != 1)\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 83d6682..7877f5c 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -200,7 +200,8 @@ static int handle_diff_files_args(struct rev_info *revs,\n \treturn 0;\n }\n \n-int run_diff_files(struct rev_info *revs, int argc, const char **argv)\n+int run_diff_files(struct rev_info *revs, int argc, const char **argv,\n+\t\tint allow_no_index)\n {\n \tint entries, i;\n \tint diff_unmerged_stage;\n@@ -211,7 +212,7 @@ int run_diff_files(struct rev_info *revs, int argc, const char **argv)\n \n \tdiff_unmerged_stage = revs->max_count;\n \n-\tif (revs->max_count == -2) {\n+\tif (allow_no_index && revs->max_count == -2) {\n \t\tif (revs->diffopt.nr_paths != 2)\n \t\t\treturn error(\"need two files/directories with --no-index\");\n \t\tqueue_diff(&revs->diffopt, revs->diffopt.paths[0],\ndiff --git a/diff.h b/diff.h\nindex cc28418..bf2d3c9 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -218,7 +218,7 @@ extern void diff_flush(struct diff_options*);\n \n extern const char *diff_unique_abbrev(const unsigned char *, int);\n \n-extern int run_diff_files(struct rev_info *revs, int argc, const char **argv);\n+extern int run_diff_files(struct rev_info *revs, int argc, const char **argv, int allow_no_index);\n \n extern int run_diff_index(struct rev_info *revs, int cached);\n \ndiff --git a/wt-status.c b/wt-status.c\nindex b68eea3..4e8904d 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -231,7 +231,7 @@ static void wt_status_print_changed(struct wt_status *s)\n \trev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;\n \trev.diffopt.format_callback = wt_status_print_changed_cb;\n \trev.diffopt.format_callback_data = s;\n-\trun_diff_files(&rev, 0, NULL);\n+\trun_diff_files(&rev, 0, NULL, 0);\n }\n \n static void wt_status_print_untracked(struct wt_status *s)\n-- \n1.5.0.51.ge5582-dirty\n"},{"id":"35252","messageId":"7vmz36f1bv.fsf@assigned-by-dhcp.cox.net","threadId":"6898","inReplyTo":"Pine.LNX.4.63.0702221926390.22628@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] Teach git-diff{,-files} the new option `--no-index`","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-02-22T19:18:28Z","receivedAt":"2007-02-22T19:18:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> Can't they made to call a new function, which has the option\n>> parsing followed by the switch between magic max_count==-2\n>> codepath and run_diff_files()?\n>\n> How about adding \"int always_use_index\" to the signature of \n> run_diff_files()?\n\nThe \"magic\" part is not about the index at all (it is about\nusing only that magic part to do diff on filesystem), and the\noriginal code is about \"index vs filesystem\".  I would rather\nkeep them in separate function.  That is:\n\n\trun_diff_files(); /* compares index vs filesystem */\n                \n\trun_diff_filesystem(); /* runs diff2 */\n\n        run_diff_files_cmd() {\n             \t/* a new helper */\n\t\tparse args;\n\t\tif (magic)\n                \trun_diff_filesystem();\n\t\telse\n                \trun_diff_files();\n\t}\n\n\tbuiltin_diff() {\n        \tif (not about two trees nor two blobs ...)\n                \trun_diff_files_cmd();\n\t\telse if (about two trees)\n                \trun_diff_tree();\n\t\telse if (about tree and index)\n                \trun_diff_index();\n\t}\n\n        wt_status() {\n        \trun_diff_files();\n                run_diff_index();\n\t}\n\nFor one thing, I want to move read_cache() out of\nrun_diff_files() and run_diff_index() to their callers, so that\nthe caller can use a handcrafted active_cache[] that is somewhat\ndifferent from what read_cache() originally read.\n"},{"id":"35255","messageId":"Pine.LNX.4.63.0702222051490.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6898","inReplyTo":"7vmz36f1bv.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Teach git-diff{,-files} the new option `--no-index`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-02-22T20:01:59Z","receivedAt":"2007-02-22T20:01:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 22 Feb 2007, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> >> Can't they made to call a new function, which has the option\n> >> parsing followed by the switch between magic max_count==-2\n> >> codepath and run_diff_files()?\n> >\n> > How about adding \"int always_use_index\" to the signature of \n> > run_diff_files()?\n> \n> The \"magic\" part is not about the index at all (it is about\n> using only that magic part to do diff on filesystem), and the\n> original code is about \"index vs filesystem\".\n\nI don't understand. diff-files until --no-index _always_ diffed against \nthe index. Now, if \"allow_no_index\" is non-zero, under very special \ncircumstances diff-files does the DWIMery.\n\n> I would rather keep them in separate function.  That is:\n> \n> \trun_diff_files(); /* compares index vs filesystem */\n>                 \n> \trun_diff_filesystem(); /* runs diff2 */\n> \n>         run_diff_files_cmd() {\n>              \t/* a new helper */\n> \t\tparse args;\n> \t\tif (magic)\n>                 \trun_diff_filesystem();\n> \t\telse\n>                 \trun_diff_files();\n> \t}\n> \n> \tbuiltin_diff() {\n>         \tif (not about two trees nor two blobs ...)\n>                 \trun_diff_files_cmd();\n> \t\telse if (about two trees)\n>                 \trun_diff_tree();\n> \t\telse if (about tree and index)\n>                 \trun_diff_index();\n> \t}\n> \n>         wt_status() {\n>         \trun_diff_files();\n>                 run_diff_index();\n> \t}\n\nSo you mean only add run_diff_files_cmd(), called by diff and diff-files?\n\n> For one thing, I want to move read_cache() out of run_diff_files() and \n> run_diff_index() to their callers, so that the caller can use a \n> handcrafted active_cache[] that is somewhat different from what \n> read_cache() originally read.\n\nI don't understand. Would it not be easier to \"return active_nr\" in \nread_cache_from(path) _also_ when active_cache is set? Or alternatively \ncheck if active_alloc is set?\n\nCiao,\nDscho\n"},{"id":"35262","messageId":"7vd542dji4.fsf@assigned-by-dhcp.cox.net","threadId":"6898","inReplyTo":"Pine.LNX.4.63.0702222051490.22628@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] Teach git-diff{,-files} the new option `--no-index`","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-02-22T20:28:51Z","receivedAt":"2007-02-22T20:28:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> For one thing, I want to move read_cache() out of run_diff_files() and \n>> run_diff_index() to their callers, so that the caller can use a \n>> handcrafted active_cache[] that is somewhat different from what \n>> read_cache() originally read.\n>\n> I don't understand. Would it not be easier to \"return active_nr\" in \n> read_cache_from(path) _also_ when active_cache is set? Or alternatively \n> check if active_alloc is set?\n\nI do not understand your questions.\n\nWhat I am getting at is that in the current code (without your\npatch), run_diff_files() is about diff between $GIT_DIR/index\nand working tree.  I want to make it diff between active_cache[]\nand working tree.\n\nThe caller still can use read_cache() or read_cache_from(path)\nto populate active_cache[], but it is not limited to.  It can do\nother index manipulations before calling run_diff_files().  For\nexample, it can do read_tree() to populate active_cache[] before\ncalling run_diff_files() and/or run_diff_index().  Or maybe it\ncan run multi-tree read_tree() to populate active_cache[],\ninternally merge them without writing the merge results out, and\nthen call run_diff_files() to show \"here is what would happen if\nyou were to run a merge now\", all inside active_cache[].\n\nOf course such a caller needs to be careful not to write\nactive_cache[] out when it shouldn't, but that goes without\nsaying.  git-diff-index already mucks with active_cache[] with\nfull intention of not writing it out.\n\nPlease see near the tip of 'pu', around here, for much simpler\nusage pattern I had in mind:\n\ncommit 910d798768989d8fe581c2afcfd501307db81b10\nAuthor: Junio C Hamano <junkio@cox.net>\n\n    git-status: use in-core --refresh in a read-only repository.\n\ncommit c5cc9b14a0f36d7fab66b3152f0ebcb442674bfc\nAuthor: Junio C Hamano <junkio@cox.net>\n\n    git-runstatus --refresh\n\ncommit b4e1e4a787d3771f617182b3344dcdd9224bd0cb\nAuthor: Junio C Hamano <junkio@cox.net>\n\n    run_diff_{files,index}(): update calling convention.\n"},{"id":"35266","messageId":"Pine.LNX.4.63.0702222146280.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6898","inReplyTo":"7vd542dji4.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Teach git-diff{,-files} the new option `--no-index`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-02-22T20:47:56Z","receivedAt":"2007-02-22T20:47:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 22 Feb 2007, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> >> For one thing, I want to move read_cache() out of run_diff_files() and \n> >> run_diff_index() to their callers, so that the caller can use a \n> >> handcrafted active_cache[] that is somewhat different from what \n> >> read_cache() originally read.\n> >\n> > I don't understand. Would it not be easier to \"return active_nr\" in \n> > read_cache_from(path) _also_ when active_cache is set? Or alternatively \n> > check if active_alloc is set?\n> \n> I do not understand your questions.\n> \n> What I am getting at is that in the current code (without your\n> patch), run_diff_files() is about diff between $GIT_DIR/index\n> and working tree.  I want to make it diff between active_cache[]\n> and working tree.\n\nMy point was: you could just ignore the read_cache() calls if they just \nreturned, doing nothing, as long as there is an active_cache.\n\nBut that's probably too dirty.\n\nAnyway, I restructured the patch as per your comments, and will send it \nafter this mail.\n\nCiao,\nDscho\n"},{"id":"35267","messageId":"Pine.LNX.4.63.0702222148050.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6898","inReplyTo":"7vd542dji4.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH, N'TH TRY] Teach git-diff-files the new option `--no-index`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-02-22T20:50:10Z","receivedAt":"2007-02-22T20:50:10Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nWith this flag and given two paths, git-diff-files behaves as a GNU diff\nlookalike (plus the git goodies like --check, colour, etc.).  This flag\nis also available in git-diff.  It also works outside of a git repository.\n\nIn addition, if git-diff{,-files} is called without revision or stage\nparameter, and with exactly two paths at least one of which is not tracked,\nthe default is --no-index.\n\nSo, you can now say\n\n\tgit diff /etc/inittab /etc/fstab\n\nand it actually works!\n\nThis also unifies the duplicated argument parsing between cmd_diff_files()\nand builtin_diff_files().\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/git-diff-files.txt |    5 +-\n Documentation/git-diff.txt       |    4 +\n builtin-diff-files.c             |   29 +-----\n builtin-diff.c                   |   38 +-------\n diff-lib.c                       |  207 ++++++++++++++++++++++++++++++++++++++\n diff.c                           |    3 +-\n diff.h                           |    1 +\n git.c                            |    4 +-\n 8 files changed, 227 insertions(+), 64 deletions(-)\n\ndiff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt\nindex 7248b35..b78c4c6 100644\n--- a/Documentation/git-diff-files.txt\n+++ b/Documentation/git-diff-files.txt\n@@ -8,7 +8,7 @@ git-diff-files - Compares files in the working tree and the index\n \n SYNOPSIS\n --------\n-'git-diff-files' [-q] [-0|-1|-2|-3|-c|--cc] [<common diff options>] [<path>...]\n+'git-diff-files' [-q] [-0|-1|-2|-3|-c|--cc|-n|--no-index] [<common diff options>] [<path>...]\n \n DESCRIPTION\n -----------\n@@ -36,6 +36,9 @@ omit diff output for unmerged entries and just show \"Unmerged\".\n \tdiff, similar to the way 'diff-tree' shows a merge\n \tcommit with these flags.\n \n+\\-n,\\--no-index::\n+\tCompare the two given files / directories.\n+\n -q::\n \tRemain silent even on nonexistent files\n \ndiff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt\nindex 6a098df..12a531d 100644\n--- a/Documentation/git-diff.txt\n+++ b/Documentation/git-diff.txt\n@@ -23,6 +23,10 @@ tree and the index file, or the index file and the working tree.\n \tfurther add to the index but you still haven't.  You can\n \tstage these changes by using gitlink:git-add[1].\n \n+\tIf exactly two paths are given, and at least one is untracked,\n+\tcompare the two files / directories. This behavior can be\n+\tforced by --no-index.\n+\n 'git-diff' [--options] --cached [<commit>] [--] [<path>...]::\n \n \tThis form is to view the changes you staged for the next\ndiff --git a/builtin-diff-files.c b/builtin-diff-files.c\nindex 5d4a5c5..e1199f8 100644\n--- a/builtin-diff-files.c\n+++ b/builtin-diff-files.c\n@@ -10,42 +10,21 @@\n #include \"builtin.h\"\n \n static const char diff_files_usage[] =\n-\"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]\"\n+\"git-diff-files [-q] [-0/-1/2/3 |-c|--cc|-n|--no-index] [<common diff options>] [<path>...]\"\n COMMON_DIFF_OPTIONS_HELP;\n \n int cmd_diff_files(int argc, const char **argv, const char *prefix)\n {\n \tstruct rev_info rev;\n-\tint silent = 0;\n+\tint nongit = 0;\n \n+\tprefix = setup_git_directory_gently(&nongit);\n \tinit_revisions(&rev, prefix);\n \tgit_config(git_default_config); /* no \"diff\" UI options */\n \trev.abbrev = 0;\n \n \targc = setup_revisions(argc, argv, &rev, NULL);\n-\twhile (1 < argc && argv[1][0] == '-') {\n-\t\tif (!strcmp(argv[1], \"--base\"))\n-\t\t\trev.max_count = 1;\n-\t\telse if (!strcmp(argv[1], \"--ours\"))\n-\t\t\trev.max_count = 2;\n-\t\telse if (!strcmp(argv[1], \"--theirs\"))\n-\t\t\trev.max_count = 3;\n-\t\telse if (!strcmp(argv[1], \"-q\"))\n-\t\t\tsilent = 1;\n-\t\telse\n-\t\t\tusage(diff_files_usage);\n-\t\targv++; argc--;\n-\t}\n \tif (!rev.diffopt.output_format)\n \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\n-\n-\t/*\n-\t * Make sure there are NO revision (i.e. pending object) parameter,\n-\t * rev.max_count is reasonable (0 <= n <= 3),\n-\t * there is no other revision filtering parameters.\n-\t */\n-\tif (rev.pending.nr ||\n-\t    rev.min_age != -1 || rev.max_age != -1)\n-\t\tusage(diff_files_usage);\n-\treturn run_diff_files(&rev, silent);\n+\treturn run_diff_files_cmd(&rev, argc, argv);\n }\ndiff --git a/builtin-diff.c b/builtin-diff.c\nindex a659020..54bbf02 100644\n--- a/builtin-diff.c\n+++ b/builtin-diff.c\n@@ -25,40 +25,6 @@ struct blobinfo {\n static const char builtin_diff_usage[] =\n \"git-diff <options> <rev>{0,2} -- <path>*\";\n \n-static int builtin_diff_files(struct rev_info *revs,\n-\t\t\t      int argc, const char **argv)\n-{\n-\tint silent = 0;\n-\twhile (1 < argc) {\n-\t\tconst char *arg = argv[1];\n-\t\tif (!strcmp(arg, \"--base\"))\n-\t\t\trevs->max_count = 1;\n-\t\telse if (!strcmp(arg, \"--ours\"))\n-\t\t\trevs->max_count = 2;\n-\t\telse if (!strcmp(arg, \"--theirs\"))\n-\t\t\trevs->max_count = 3;\n-\t\telse if (!strcmp(arg, \"-q\"))\n-\t\t\tsilent = 1;\n-\t\telse\n-\t\t\tusage(builtin_diff_usage);\n-\t\targv++; argc--;\n-\t}\n-\t/*\n-\t * Make sure there are NO revision (i.e. pending object) parameter,\n-\t * specified rev.max_count is reasonable (0 <= n <= 3), and\n-\t * there is no other revision filtering parameter.\n-\t */\n-\tif (revs->pending.nr ||\n-\t    revs->min_age != -1 ||\n-\t    revs->max_age != -1 ||\n-\t    3 < revs->max_count)\n-\t\tusage(builtin_diff_usage);\n-\tif (revs->max_count < 0 &&\n-\t    (revs->diffopt.output_format & DIFF_FORMAT_PATCH))\n-\t\trevs->combine_merges = revs->dense_combined_merges = 1;\n-\treturn run_diff_files(revs, silent);\n-}\n-\n static void stuff_change(struct diff_options *opt,\n \t\t\t unsigned old_mode, unsigned new_mode,\n \t\t\t const unsigned char *old_sha1,\n@@ -218,6 +184,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \tint ents = 0, blobs = 0, paths = 0;\n \tconst char *path = NULL;\n \tstruct blobinfo blob[2];\n+\tint nongit = 0;\n \n \t/*\n \t * We could get N tree-ish in the rev.pending_objects list.\n@@ -239,6 +206,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \t * Other cases are errors.\n \t */\n \n+\tprefix = setup_git_directory_gently(&nongit);\n \tgit_config(git_diff_ui_config);\n \tinit_revisions(&rev, prefix);\n \n@@ -314,7 +282,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \tif (!ents) {\n \t\tswitch (blobs) {\n \t\tcase 0:\n-\t\t\treturn builtin_diff_files(&rev, argc, argv);\n+\t\t\treturn run_diff_files_cmd(&rev, argc, argv);\n \t\t\tbreak;\n \t\tcase 1:\n \t\t\tif (paths != 1)\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 556d534..a9b5149 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -8,11 +8,218 @@\n #include \"diffcore.h\"\n #include \"revision.h\"\n #include \"cache-tree.h\"\n+#include \"path-list.h\"\n \n /*\n  * diff-files\n  */\n \n+static int read_directory(const char *path, struct path_list *list)\n+{\n+\tDIR *dir;\n+\tstruct dirent *e;\n+\n+\tif (!(dir = opendir(path)))\n+\t\treturn error(\"Could not open directory %s\", path);\n+\n+\twhile ((e = readdir(dir)))\n+\t\tif (strcmp(\".\", e->d_name) && strcmp(\"..\", e->d_name))\n+\t\t\tpath_list_insert(xstrdup(e->d_name), list);\n+\n+\tclosedir(dir);\n+\treturn 0;\n+}\n+\n+static int queue_diff(struct diff_options *o,\n+\t\tconst char *name1, const char *name2)\n+{\n+\tstruct stat st;\n+\tint mode1 = 0, mode2 = 0;\n+\n+\tif (name1) {\n+\t\tif (stat(name1, &st))\n+\t\t\treturn error(\"Could not access '%s'\", name1);\n+\t\tmode1 = st.st_mode;\n+\t}\n+\tif (name2) {\n+\t\tif (stat(name2, &st))\n+\t\t\treturn error(\"Could not access '%s'\", name1);\n+\t\tmode2 = st.st_mode;\n+\t}\n+\n+\tif (mode1 && mode2 && S_ISDIR(mode1) != S_ISDIR(mode2))\n+\t\treturn error(\"file/directory conflict: %s, %s\", name1, name2);\n+\n+\tif (S_ISDIR(mode1) || S_ISDIR(mode2)) {\n+\t\tchar buffer1[PATH_MAX], buffer2[PATH_MAX];\n+\t\tstruct path_list p1 = {NULL, 0, 0, 1}, p2 = {NULL, 0, 0, 1};\n+\t\tint len1 = 0, len2 = 0, i1, i2, ret = 0;\n+\n+\t\tif (name1 && read_directory(name1, &p1))\n+\t\t\treturn -1;\n+\t\tif (name2 && read_directory(name2, &p2)) {\n+\t\t\tpath_list_clear(&p1, 0);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (name1) {\n+\t\t\tlen1 = strlen(name1);\n+\t\t\tif (len1 > 0 && name1[len1 - 1] == '/')\n+\t\t\t\tlen1--;\n+\t\t\tmemcpy(buffer1, name1, len1);\n+\t\t\tbuffer1[len1++] = '/';\n+\t\t}\n+\n+\t\tif (name2) {\n+\t\t\tlen2 = strlen(name2);\n+\t\t\tif (len2 > 0 && name2[len2 - 1] == '/')\n+\t\t\t\tlen2--;\n+\t\t\tmemcpy(buffer2, name2, len2);\n+\t\t\tbuffer2[len2++] = '/';\n+\t\t}\n+\n+\t\tfor (i1 = i2 = 0; !ret && (i1 < p1.nr || i2 < p2.nr); ) {\n+\t\t\tconst char *n1, *n2;\n+\t\t\tint comp;\n+\n+\t\t\tif (i1 == p1.nr)\n+\t\t\t\tcomp = 1;\n+\t\t\telse if (i2 == p2.nr)\n+\t\t\t\tcomp = -1;\n+\t\t\telse\n+\t\t\t\tcomp = strcmp(p1.items[i1].path,\n+\t\t\t\t\tp2.items[i2].path);\n+\n+\t\t\tif (comp > 0)\n+\t\t\t\tn1 = NULL;\n+\t\t\telse {\n+\t\t\t\tn1 = buffer1;\n+\t\t\t\tstrncpy(buffer1 + len1, p1.items[i1++].path,\n+\t\t\t\t\t\tPATH_MAX - len1);\n+\t\t\t}\n+\n+\t\t\tif (comp < 0)\n+\t\t\t\tn2 = NULL;\n+\t\t\telse {\n+\t\t\t\tn2 = buffer2;\n+\t\t\t\tstrncpy(buffer2 + len2, p2.items[i2++].path,\n+\t\t\t\t\t\tPATH_MAX - len2);\n+\t\t\t}\n+\n+\t\t\tret = queue_diff(o, n1, n2);\n+\t\t}\n+\t\tpath_list_clear(&p1, 0);\n+\t\tpath_list_clear(&p2, 0);\n+\n+\t\treturn ret;\n+\t} else {\n+\t\tstruct diff_filespec *d1, *d2;\n+\n+\t\tif (o->reverse_diff) {\n+\t\t\tunsigned tmp;\n+\t\t\tconst char *tmp_c;\n+\t\t\ttmp = mode1; mode1 = mode2; mode2 = tmp;\n+\t\t\ttmp_c = name1; name1 = name2; name2 = tmp_c;\n+\t\t}\n+\n+\t\tif (!name1)\n+\t\t\tname1 = \"/dev/null\";\n+\t\tif (!name2)\n+\t\t\tname2 = \"/dev/null\";\n+\t\td1 = alloc_filespec(name1);\n+\t\td2 = alloc_filespec(name2);\n+\t\tfill_filespec(d1, null_sha1, mode1);\n+\t\tfill_filespec(d2, null_sha1, mode2);\n+\n+\t\tdiff_queue(&diff_queued_diff, d1, d2);\n+\t\treturn 0;\n+\t}\n+}\n+\n+static int is_in_index(const char *path)\n+{\n+\tint len = strlen(path);\n+\tint pos = cache_name_pos(path, len);\n+\tchar c;\n+\n+\tif (pos < 0)\n+\t\treturn 0;\n+\tif (strncmp(active_cache[pos]->name, path, len))\n+\t\treturn 0;\n+\tc = active_cache[pos]->name[len];\n+\treturn c == '\\0' || c == '/';\n+}\n+\n+static int handle_diff_files_args(struct rev_info *revs,\n+\t\tint argc, const char **argv, int *silent)\n+{\n+\t*silent = 0;\n+\n+\t/* revs->max_count == -2 means --no-index */\n+\twhile (1 < argc && argv[1][0] == '-') {\n+\t\tif (!strcmp(argv[1], \"--base\"))\n+\t\t\trevs->max_count = 1;\n+\t\telse if (!strcmp(argv[1], \"--ours\"))\n+\t\t\trevs->max_count = 2;\n+\t\telse if (!strcmp(argv[1], \"--theirs\"))\n+\t\t\trevs->max_count = 3;\n+\t\telse if (!strcmp(argv[1], \"-n\") ||\n+\t\t\t\t!strcmp(argv[1], \"--no-index\"))\n+\t\t\trevs->max_count = -2;\n+\t\telse if (!strcmp(argv[1], \"-q\"))\n+\t\t\t*silent = 1;\n+\t\telse\n+\t\t\treturn error(\"invalid option: %s\", argv[1]);\n+\t\targv++; argc--;\n+\t}\n+\n+\tif (revs->max_count == -1 && revs->diffopt.nr_paths == 2) {\n+\t\t/*\n+\t\t * If two files are specified, and at least one is untracked,\n+\t\t * default to no-index.\n+\t\t */\n+\t\tread_cache();\n+\t\tif (!is_in_index(revs->diffopt.paths[0]) ||\n+\t\t\t\t\t!is_in_index(revs->diffopt.paths[1]))\n+\t\t\trevs->max_count = -2;\n+\t}\n+\n+\t/*\n+\t * Make sure there are NO revision (i.e. pending object) parameter,\n+\t * rev.max_count is reasonable (0 <= n <= 3),\n+\t * there is no other revision filtering parameters.\n+\t */\n+\tif (revs->pending.nr || revs->max_count > 3 ||\n+\t    revs->min_age != -1 || revs->max_age != -1)\n+\t\treturn error(\"no revision allowed with diff-files\");\n+\n+\tif (revs->max_count == -1 &&\n+\t    (revs->diffopt.output_format & DIFF_FORMAT_PATCH))\n+\t\trevs->combine_merges = revs->dense_combined_merges = 1;\n+\n+\treturn 0;\n+}\n+\n+int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)\n+{\n+\tint silent_on_removed;\n+\n+\tif (handle_diff_files_args(revs, argc, argv, &silent_on_removed))\n+\t\treturn -1;\n+\n+\tif (revs->max_count == -2) {\n+\t\tif (revs->diffopt.nr_paths != 2)\n+\t\t\treturn error(\"need two files/directories with --no-index\");\n+\t\tqueue_diff(&revs->diffopt, revs->diffopt.paths[0],\n+\t\t\t\trevs->diffopt.paths[1]);\n+\t\tdiffcore_std(&revs->diffopt);\n+\t\tdiff_flush(&revs->diffopt);\n+\t\treturn 0;\n+\t}\n+\n+\treturn run_diff_files(revs, silent_on_removed);\n+}\n+\n int run_diff_files(struct rev_info *revs, int silent_on_removed)\n {\n \tint entries, i;\ndiff --git a/diff.c b/diff.c\nindex 12c8b2b..701880a 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -2406,7 +2406,8 @@ static void diff_resolve_rename_copy(void)\n \t\t\t\tp->status = DIFF_STATUS_RENAMED;\n \t\t}\n \t\telse if (hashcmp(p->one->sha1, p->two->sha1) ||\n-\t\t\t p->one->mode != p->two->mode)\n+\t\t\t p->one->mode != p->two->mode ||\n+\t\t\t is_null_sha1(p->one->sha1))\n \t\t\tp->status = DIFF_STATUS_MODIFIED;\n \t\telse {\n \t\t\t/* This is a \"no-change\" entry and should not\ndiff --git a/diff.h b/diff.h\nindex eece65d..b608828 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -219,6 +219,7 @@ extern void diff_flush(struct diff_options*);\n extern const char *diff_unique_abbrev(const unsigned char *, int);\n \n extern int run_diff_files(struct rev_info *revs, int silent_on_removed);\n+extern int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv);\n \n extern int run_diff_index(struct rev_info *revs, int cached);\n \ndiff --git a/git.c b/git.c\nindex 4dd1967..79fc73c 100644\n--- a/git.c\n+++ b/git.c\n@@ -237,8 +237,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp)\n \t\t{ \"config\", cmd_config },\n \t\t{ \"count-objects\", cmd_count_objects, RUN_SETUP },\n \t\t{ \"describe\", cmd_describe, RUN_SETUP },\n-\t\t{ \"diff\", cmd_diff, RUN_SETUP | USE_PAGER },\n-\t\t{ \"diff-files\", cmd_diff_files, RUN_SETUP },\n+\t\t{ \"diff\", cmd_diff, USE_PAGER },\n+\t\t{ \"diff-files\", cmd_diff_files },\n \t\t{ \"diff-index\", cmd_diff_index, RUN_SETUP },\n \t\t{ \"diff-tree\", cmd_diff_tree, RUN_SETUP },\n \t\t{ \"fmt-merge-msg\", cmd_fmt_merge_msg, RUN_SETUP },\n-- \n1.5.0.51.ge5582-dirty\n"},{"id":"35321","messageId":"7vmz353xpd.fsf@assigned-by-dhcp.cox.net","threadId":"6898","inReplyTo":"Pine.LNX.4.63.0702222148050.22628@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH, N'TH TRY] Teach git-diff-files the new option `--no-index`","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-02-23T11:44:30Z","receivedAt":"2007-02-23T11:44:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> With this flag and given two paths, git-diff-files behaves as a GNU diff\n> lookalike (plus the git goodies like --check, colour, etc.).  This flag\n> is also available in git-diff.  It also works outside of a git repository.\n>\n> In addition, if git-diff{,-files} is called without revision or stage\n> parameter, and with exactly two paths at least one of which is not tracked,\n> the default is --no-index.\n>\n> So, you can now say\n>\n> \tgit diff /etc/inittab /etc/fstab\n>\n> and it actually works!\n\nModulo double slashes X-<.\n\n        $ diff --git a//etc/inittab b//etc/fstab\n        index 04eabd6..7314c1a 100644\n        --- a//etc/inittab\n        +++ b//etc/fstab\n\t@@ -1,69 +1,13 @@\n\t...\n\nThis problem is not new; this should fix it.\n\n-- >8 --\ndiff-patch: Avoid emitting double-slashes in textual patch.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\ndiff --git a/diff.c b/diff.c\nindex 5ecb122..84937cb 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -219,6 +219,9 @@ static void emit_rewrite_diff(const char *name_a,\n \tconst char *new = diff_get_color(color_diff, DIFF_FILE_NEW);\n \tconst char *reset = diff_get_color(color_diff, DIFF_RESET);\n \n+\tname_a += (*name_a == '/');\n+\tname_b += (*name_b == '/');\n+\n \tname_a_tab = strchr(name_a, ' ') ? \"\\t\" : \"\";\n \tname_b_tab = strchr(name_b, ' ') ? \"\\t\" : \"\";\n \n@@ -1064,8 +1067,8 @@ static void builtin_diff(const char *name_a,\n \tconst char *set = diff_get_color(o->color_diff, DIFF_METAINFO);\n \tconst char *reset = diff_get_color(o->color_diff, DIFF_RESET);\n \n-\ta_one = quote_two(\"a/\", name_a);\n-\tb_two = quote_two(\"b/\", name_b);\n+\ta_one = quote_two(\"a/\", name_a + (*name_a == '/'));\n+\tb_two = quote_two(\"b/\", name_b + (*name_b == '/'));\n \tlbl[0] = DIFF_FILE_VALID(one) ? a_one : \"/dev/null\";\n \tlbl[1] = DIFF_FILE_VALID(two) ? b_two : \"/dev/null\";\n \tprintf(\"%sdiff --git %s %s%s\\n\", set, a_one, b_two, reset);\n"},{"id":"35327","messageId":"Pine.LNX.4.63.0702231705370.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6898","inReplyTo":"7vmz353xpd.fsf@assigned-by-dhcp.cox.net","subject":"Re: diff-patch: Avoid emitting double-slashes in textual patch.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-02-23T16:07:16Z","receivedAt":"2007-02-23T16:07:16Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 23 Feb 2007, Junio C Hamano wrote:\n\n> -\ta_one = quote_two(\"a/\", name_a);\n> -\tb_two = quote_two(\"b/\", name_b);\n> +\ta_one = quote_two(\"a/\", name_a + (*name_a == '/'));\n> +\tb_two = quote_two(\"b/\", name_b + (*name_b == '/'));\n\nI briefly thought about something like this instead:\n\n-\ta_one = quote_two(\"a/\", name_a + (*name_a == '/'));\n-\tb_two = quote_two(\"b/\", name_b + (*name_b == '/'));\n+\ta_one = quote_two(*name_a == '/' ? \"\" : \"a/\", name_a);\n+\tb_two = quote_two(*name_b == '/' ? \"\" : \"b/\", name_b);\n \tlbl[0] = DIFF_FILE_VALID(one) ? a_one : \"/dev/null\";\n \tlbl[1] = DIFF_FILE_VALID(two) ? b_two : \"/dev/null\";\n-\tprintf(\"%sdiff --git %s %s%s\\n\", set, a_one, b_two, reset);\n+\tprintf(\"%sdiff %s%s %s%s\\n\", set, *name_a == '/' || *name_b == '/' ?\n+\t\t\t\"\" : \"--git \", a_one, b_two, reset);\n\nbut I think your approach is better.\n\nCiao,\nDscho\n"}]}