{"thread":{"id":"726","subject":"Broken directory pathname pruning..","startedAt":"2005-05-27T00:41:11Z","lastAt":"2005-06-12T18:34:22Z","messageCount":45,"participants":["Linus Torvalds","Junio C Hamano","Yoichi Yuasa"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"4017","messageId":"Pine.LNX.4.58.0505261731050.17207@ppc970.osdl.org","threadId":"726","inReplyTo":null,"subject":"Broken directory pathname pruning..","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-05-27T00:41:11Z","receivedAt":"2005-05-27T00:41:11Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThe directory-based pathname pruning doesn't work any more.\n\nI used to just say\n\n\tgit-whatchanged -v -p drivers/usb/\n\nand I just noticed that it doesn't work any more.\n\nIt _does_ work if I leave the final '/' off the thing.\n\ndiff-tree itself does this right: that's shown by the fact that with the\nslash in place, we still do get the right _changelog_ that is restricted\nto drivers/usb changes, but the diffs themselves are missing.\n\nSo it seems to be purely \"diffcore_pathspec()\" that is broken.\n\nBtw, I don't think we should call \"diffcore_pathspec()\" at all in\ndiff-tree, because diff-tree already handles \"interesting\" paths correctly\n(and has to do so for performance reasons, since it's not acceptable to\nexpand all the trees and diff them).\n\nSo in the \"git-whatchanged\"  case the fix is as simple as just removing\nthose two lines, but the bug then continues to exist in the other *diff* \nprograms..\n\nJunio?\n\n\t\tLinus\n\ndiff --git a/diff-tree.c b/diff-tree.c\n--- a/diff-tree.c\n+++ b/diff-tree.c\n@@ -268,8 +268,6 @@ static int call_diff_flush(void)\n \t\tdiff_flush(DIFF_FORMAT_NO_OUTPUT, 0);\n \t\treturn 0;\n \t}\n-\tif (nr_paths)\n-\t\tdiffcore_pathspec(paths);\n \tif (header) {\n \t\tif (diff_output_format == DIFF_FORMAT_MACHINE) {\n \t\t\tconst char *ep, *cp;\n\n"},{"id":"4019","messageId":"7voeax7b3u.fsf@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"Pine.LNX.4.58.0505261731050.17207@ppc970.osdl.org","subject":"Re: Broken directory pathname pruning..","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T00:42:45Z","receivedAt":"2005-05-27T00:42:45Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Agreed in both counts.\n\n"},{"id":"4020","messageId":"7vis157ate.fsf@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"Pine.LNX.4.58.0505261731050.17207@ppc970.osdl.org","subject":"[PATCH] allow pathspec to end with a slash","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T00:49:01Z","receivedAt":"2005-05-27T00:49:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Recent rewrite broke \"git-whatchanged -v -p drivers/usb/\" but \n\"git-whatchanged -v -p drivers/usb\" still works.  Just strip out\nthe trailing slashes internally to make it work again.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n# - linus: [PATCH] Make ls-* output consistent with diff-* output format.\n# + (working tree)\ndiff --git a/diffcore-pathspec.c b/diffcore-pathspec.c\n--- a/diffcore-pathspec.c\n+++ b/diffcore-pathspec.c\n@@ -45,8 +45,12 @@ void diffcore_pathspec(const char **path\n \tspeccnt = i;\n \tspec = xmalloc(sizeof(*spec) * speccnt);\n \tfor (i = 0; pathspec[i]; i++) {\n+\t\tint l;\n \t\tspec[i].spec = pathspec[i];\n-\t\tspec[i].len = strlen(pathspec[i]);\n+\t\tl = strlen(pathspec[i]);\n+\t\twhile (pathspec[i][l-1] == '/')\n+\t\t\tl--;\n+\t\tspec[i].len = l;\n \t}\n \n \tfor (i = 0; i < q->nr; i++) {\n\nCompilation finished at Thu May 26 17:46:56\n\n"},{"id":"4021","messageId":"7v7jhl7an8.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vis157ate.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] allow pathspec to end with a slash (take #2)","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T00:52:43Z","receivedAt":"2005-05-27T00:52:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Oops, wrong patch; I am an idiot.\n\n------------\nRecent rewrite broke \"git-whatchanged -v -p drivers/usb/\" but \n\"git-whatchanged -v -p drivers/usb\" still works.  Just strip out\nthe trailing slashes internally to make it work again.\n\nIt uses compare-thing-with-number comparison order instead of\nvisual comparison order ;-).\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\ncd /opt/packrat/playpen/public/in-place/git/git.junio/\njit-diff 1: diffcore-pathspec.c\n# - linus: [PATCH] Make ls-* output consistent with diff-* output format.\n# + (working tree)\ndiff --git a/diffcore-pathspec.c b/diffcore-pathspec.c\n--- a/diffcore-pathspec.c\n+++ b/diffcore-pathspec.c\n@@ -45,8 +45,12 @@ void diffcore_pathspec(const char **path\n \tspeccnt = i;\n \tspec = xmalloc(sizeof(*spec) * speccnt);\n \tfor (i = 0; pathspec[i]; i++) {\n+\t\tint l;\n \t\tspec[i].spec = pathspec[i];\n-\t\tspec[i].len = strlen(pathspec[i]);\n+\t\tl = strlen(pathspec[i]);\n+\t\twhile (l > 0 && pathspec[i][l-1] == '/')\n+\t\t\tl--;\n+\t\tspec[i].len = l;\n \t}\n \n \tfor (i = 0; i < q->nr; i++) {\n\nCompilation finished at Thu May 26 17:50:40\n\n"},{"id":"4038","messageId":"7vsm091887.fsf@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"Pine.LNX.4.58.0505261731050.17207@ppc970.osdl.org","subject":"[PATCH] Diff updates, fixing pathspec and rename/copy interaction.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T06:41:28Z","receivedAt":"2005-05-27T06:41:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"During the mailing list discussion about diff-tree omitting to\ncall diffcore-pathspec, I realized that the current rename/copy\ndifferentiator has a major flaw interacting with pathspec (or\nany other filepair filters, including pickaxe).\n\nThe problem was that in order to tell if the rename-copy source\nstill remains in the resulting tree (that is what determines if\none of the rename-copy can become a rename or everybody needs to\nbe a copy), diffcore-rename was sending a filepair that records\nunmodified source downstream, expecting that it reaches\nresolve_rename_copy() which happens as the final stage before\nactual output happens.  Of course, pathspec and pickaxe can\ninterfere and happily remove that entry, in which case what\nshould be shown as a copy suddenly becomes a rename.\n\nThe problem was fixed by changing the way diffcore-rename\nrecords whether the source file remains in the resulting tree.\n\nThis patch also introduces an optimization for diff-tree -M and\ndiff-tree -C to throw away implausible rename/copy source\ncandidate by keeping the filesize information of already seen\nSHA1 object IDs in a cache.  This cache is activated only when\ndiff-tree is reading from --stdin (i.e. processing sequence of\ntree pairs) and when rename detection is used.\n\nPickaxe acquired another option, --pickaxe-all, to diff-* three\nbrothers.  When a search string is touched, instead of showing\nonly the changed file that touches that searched string, it\nshows the entire changeset.  This is useful to see the change in\ncontext.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff-cache.c             |   13 ++\ndiff-files.c             |   11 +-\ndiff-helper.c            |    9 +-\ndiff-tree.c              |   16 ++-\ndiff.c                   |  209 ++++++++++++++++++++++++++++++-----------------\ndiff.h                   |   18 ++--\ndiffcore-pathspec.c      |    5 -\ndiffcore-pickaxe.c       |   79 +++++++++++++----\ndiffcore-rename.c        |   91 +++++++++-----------\ndiffcore.h               |   14 +--\ngit-external-diff-script |    2 \nt/t4007-rename-3.sh      |  103 +++++++++++++++++++++++\n12 files changed, 392 insertions(+), 178 deletions(-)\nnew file (100755): t/t4007-rename-3.sh\n\ndiff --git a/diff-cache.c b/diff-cache.c\n--- a/diff-cache.c\n+++ b/diff-cache.c\n@@ -5,9 +5,10 @@ static int cached_only = 0;\n static int diff_output_format = DIFF_FORMAT_HUMAN;\n static int match_nonexisting = 0;\n static int detect_rename = 0;\n-static int reverse_diff = 0;\n+static int diff_setup_opt = 0;\n static int diff_score_opt = 0;\n static const char *pickaxe = NULL;\n+static int pickaxe_opts = 0;\n \n /* A file entry went away or appeared */\n static void show_file(const char *prefix, struct cache_entry *ce, unsigned char *sha1, unsigned int mode)\n@@ -202,13 +203,17 @@ int main(int argc, const char **argv)\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"-R\")) {\n-\t\t\treverse_diff = 1;\n+\t\t\tdiff_setup_opt |= DIFF_SETUP_REVERSE;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"-S\")) {\n \t\t\tpickaxe = arg + 2;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(arg, \"--pickaxe-all\")) {\n+\t\t\tpickaxe_opts = DIFF_PICKAXE_ALL;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(arg, \"-m\")) {\n \t\t\tmatch_nonexisting = 1;\n \t\t\tcontinue;\n@@ -224,7 +229,7 @@ int main(int argc, const char **argv)\n \t\tusage(diff_cache_usage);\n \n \t/* The rest is for paths restriction. */\n-\tdiff_setup(reverse_diff);\n+\tdiff_setup(diff_setup_opt);\n \n \tmark_merge_entries();\n \n@@ -238,7 +243,7 @@ int main(int argc, const char **argv)\n \tif (detect_rename)\n \t\tdiffcore_rename(detect_rename, diff_score_opt);\n \tif (pickaxe)\n-\t\tdiffcore_pickaxe(pickaxe);\n+\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n \tif (pathspec)\n \t\tdiffcore_pathspec(pathspec);\n \tdiff_flush(diff_output_format, 1);\ndiff --git a/diff-files.c b/diff-files.c\n--- a/diff-files.c\n+++ b/diff-files.c\n@@ -11,9 +11,10 @@ static const char *diff_files_usage =\n \n static int diff_output_format = DIFF_FORMAT_HUMAN;\n static int detect_rename = 0;\n-static int reverse_diff = 0;\n+static int diff_setup_opt = 0;\n static int diff_score_opt = 0;\n static const char *pickaxe = NULL;\n+static int pickaxe_opts = 0;\n static int silent = 0;\n \n static void show_unmerge(const char *path)\n@@ -51,9 +52,11 @@ int main(int argc, const char **argv)\n \t\telse if (!strcmp(argv[1], \"-z\"))\n \t\t\tdiff_output_format = DIFF_FORMAT_MACHINE;\n \t\telse if (!strcmp(argv[1], \"-R\"))\n-\t\t\treverse_diff = 1;\n+\t\t\tdiff_setup_opt |= DIFF_SETUP_REVERSE;\n \t\telse if (!strcmp(argv[1], \"-S\"))\n \t\t\tpickaxe = argv[1] + 2;\n+\t\telse if (!strcmp(argv[1], \"--pickaxe-all\"))\n+\t\t\tpickaxe_opts = DIFF_PICKAXE_ALL;\n \t\telse if (!strncmp(argv[1], \"-M\", 2)) {\n \t\t\tdiff_score_opt = diff_scoreopt_parse(argv[1]);\n \t\t\tdetect_rename = DIFF_DETECT_RENAME;\n@@ -75,7 +78,7 @@ int main(int argc, const char **argv)\n \t\texit(1);\n \t}\n \n-\tdiff_setup(reverse_diff);\n+\tdiff_setup(diff_setup_opt);\n \n \tfor (i = 0; i < entries; i++) {\n \t\tstruct stat st;\n@@ -116,7 +119,7 @@ int main(int argc, const char **argv)\n \tif (detect_rename)\n \t\tdiffcore_rename(detect_rename, diff_score_opt);\n \tif (pickaxe)\n-\t\tdiffcore_pickaxe(pickaxe);\n+\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n \tif (1 < argc)\n \t\tdiffcore_pathspec(argv + 1);\n \tdiff_flush(diff_output_format, 1);\ndiff --git a/diff-helper.c b/diff-helper.c\n--- a/diff-helper.c\n+++ b/diff-helper.c\n@@ -4,9 +4,9 @@\n #include \"cache.h\"\n #include \"strbuf.h\"\n #include \"diff.h\"\n-#include \"diffcore.h\" /* just for MAX_SCORE */\n \n static const char *pickaxe = NULL;\n+static int pickaxe_opts = 0;\n static int line_termination = '\\n';\n static int inter_name_termination = '\\t';\n \n@@ -24,6 +24,8 @@ int main(int ac, const char **av) {\n \t\telse if (av[1][1] == 'S') {\n \t\t\tpickaxe = av[1] + 2;\n \t\t}\n+\t\telse if (!strcmp(av[1], \"--pickaxe-all\"))\n+\t\t\tpickaxe_opts = DIFF_PICKAXE_ALL;\n \t\telse\n \t\t\tusage(diff_helper_usage);\n \t\tac--; av++;\n@@ -78,7 +80,6 @@ int main(int ac, const char **av) {\n \t\t\tif (status == 'R' || status == 'C') {\n \t\t\t\ttwo_paths = 1;\n \t\t\t\tsscanf(cp, \"%d\", &score);\n-\t\t\t\tscore = score * MAX_SCORE / 100;\n \t\t\t\tif (line_termination) {\n \t\t\t\t\tcp = strchr(cp,\n \t\t\t\t\t\t    inter_name_termination);\n@@ -129,14 +130,14 @@ int main(int ac, const char **av) {\n \t\t\tcontinue;\n \t\t}\n \t\tif (pickaxe)\n-\t\t\tdiffcore_pickaxe(pickaxe);\n+\t\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n \t\tif (1 < ac)\n \t\t\tdiffcore_pathspec(av + 1);\n \t\tdiff_flush(DIFF_FORMAT_PATCH, 0);\n \t\tprintf(\"%s\\n\", sb.buf);\n \t}\n \tif (pickaxe)\n-\t\tdiffcore_pickaxe(pickaxe);\n+\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n \tif (1 < ac)\n \t\tdiffcore_pathspec(av + 1);\n \tdiff_flush(DIFF_FORMAT_PATCH, 0);\ndiff --git a/diff-tree.c b/diff-tree.c\n--- a/diff-tree.c\n+++ b/diff-tree.c\n@@ -10,9 +10,10 @@ static int show_tree_entry_in_recursive \n static int read_stdin = 0;\n static int diff_output_format = DIFF_FORMAT_HUMAN;\n static int detect_rename = 0;\n-static int reverse_diff = 0;\n+static int diff_setup_opt = 0;\n static int diff_score_opt = 0;\n static const char *pickaxe = NULL;\n+static int pickaxe_opts = 0;\n static const char *header = NULL;\n static const char *header_prefix = \"\";\n \n@@ -255,7 +256,7 @@ static int diff_tree_sha1(const unsigned\n \n static void call_diff_setup(void)\n {\n-\tdiff_setup(reverse_diff);\n+\tdiff_setup(diff_setup_opt);\n }\n \n static int call_diff_flush(void)\n@@ -263,7 +264,7 @@ static int call_diff_flush(void)\n \tif (detect_rename)\n \t\tdiffcore_rename(detect_rename, diff_score_opt);\n \tif (pickaxe)\n-\t\tdiffcore_pickaxe(pickaxe);\n+\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n \tif (diff_queue_is_empty()) {\n \t\tdiff_flush(DIFF_FORMAT_NO_OUTPUT, 0);\n \t\treturn 0;\n@@ -504,7 +505,7 @@ int main(int argc, const char **argv)\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"-R\")) {\n-\t\t\treverse_diff = 1;\n+\t\t\tdiff_setup_opt |= DIFF_SETUP_REVERSE;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"-p\")) {\n@@ -516,6 +517,10 @@ int main(int argc, const char **argv)\n \t\t\tpickaxe = arg + 2;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(arg, \"--pickaxe-all\")) {\n+\t\t\tpickaxe_opts = DIFF_PICKAXE_ALL;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strncmp(arg, \"-M\", 2)) {\n \t\t\tdetect_rename = DIFF_DETECT_RENAME;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(arg);\n@@ -580,6 +585,9 @@ int main(int argc, const char **argv)\n \tif (!read_stdin)\n \t\treturn 0;\n \n+\tif (detect_rename)\n+\t\tdiff_setup_opt |= (DIFF_SETUP_USE_SIZE_CACHE |\n+\t\t\t\t   DIFF_SETUP_USE_CACHE);\n \twhile (fgets(line, sizeof(line), stdin))\n \t\tdiff_tree_stdin(line);\n \ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -12,6 +12,7 @@ static const char *diff_opts = \"-pu\";\n static unsigned char null_sha1[20] = { 0, };\n \n static int reverse_diff;\n+static int use_size_cache;\n \n static const char *external_diff(void)\n {\n@@ -141,7 +142,7 @@ static void builtin_diff(const char *nam\n \t\t\tprintf(\"new mode %s\\n\", temp[1].mode);\n \t\t}\n \t\tif (xfrm_msg && xfrm_msg[0])\n-\t\t\tfputs(xfrm_msg, stdout);\n+\t\t\tputs(xfrm_msg);\n \n \t\tif (strncmp(temp[0].mode, temp[1].mode, 3))\n \t\t\t/* we do not run diff between different kind\n@@ -222,12 +223,60 @@ static int work_tree_matches(const char \n \treturn 1;\n }\n \n+static struct sha1_size_cache {\n+\tunsigned char sha1[20];\n+\tunsigned long size;\n+} **sha1_size_cache;\n+static int sha1_size_cache_nr, sha1_size_cache_alloc;\n+\n+static struct sha1_size_cache *locate_size_cache(unsigned char *sha1,\n+\t\t\t\t\t\t unsigned long size)\n+{\n+\tint first, last;\n+\tstruct sha1_size_cache *e;\n+\n+\tfirst = 0;\n+\tlast = sha1_size_cache_nr;\n+\twhile (last > first) {\n+\t\tint next = (last + first) >> 1;\n+\t\te = sha1_size_cache[next];\n+\t\tint cmp = memcmp(e->sha1, sha1, 20);\n+\t\tif (!cmp)\n+\t\t\treturn e;\n+\t\tif (cmp < 0) {\n+\t\t\tlast = next;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tfirst = next+1;\n+\t}\n+\t/* not found */\n+\tif (size == UINT_MAX)\n+\t\treturn NULL;\n+\t/* insert to make it at \"first\" */\n+\tif (sha1_size_cache_alloc <= sha1_size_cache_nr) {\n+\t\tsha1_size_cache_alloc = alloc_nr(sha1_size_cache_alloc);\n+\t\tsha1_size_cache = xrealloc(sha1_size_cache,\n+\t\t\t\t\t   sha1_size_cache_alloc *\n+\t\t\t\t\t   sizeof(*sha1_size_cache));\n+\t}\n+\tsha1_size_cache_nr++;\n+\tif (first < sha1_size_cache_nr)\n+\t\tmemmove(sha1_size_cache + first + 1, sha1_size_cache + first,\n+\t\t\t(sha1_size_cache_nr - first - 1) *\n+\t\t\tsizeof(*sha1_size_cache));\n+\te = xmalloc(sizeof(struct sha1_size_cache));\n+\tsha1_size_cache[first] = e;\n+\tmemcpy(e->sha1, sha1, 20);\n+\te->size = size;\n+\treturn e;\n+}\n+\n /*\n  * While doing rename detection and pickaxe operation, we may need to\n  * grab the data for the blob (or file) for our own in-core comparison.\n  * diff_filespec has data and size fields for this purpose.\n  */\n-int diff_populate_filespec(struct diff_filespec *s)\n+int diff_populate_filespec(struct diff_filespec *s, int size_only)\n {\n \tint err = 0;\n \tif (!DIFF_FILE_VALID(s))\n@@ -235,6 +284,9 @@ int diff_populate_filespec(struct diff_f\n \tif (S_ISDIR(s->mode))\n \t\treturn -1;\n \n+\tif (!use_size_cache)\n+\t\tsize_only = 0;\n+\n \tif (s->data)\n \t\treturn err;\n \tif (!s->sha1_valid ||\n@@ -254,6 +306,8 @@ int diff_populate_filespec(struct diff_f\n \t\ts->size = st.st_size;\n \t\tif (!s->size)\n \t\t\tgoto empty;\n+\t\tif (size_only)\n+\t\t\treturn 0;\n \t\tif (S_ISLNK(st.st_mode)) {\n \t\t\tint ret;\n \t\t\ts->data = xmalloc(s->size);\n@@ -273,9 +327,21 @@ int diff_populate_filespec(struct diff_f\n \t\tclose(fd);\n \t}\n \telse {\n+\t\t/* We cannot do size only for SHA1 blobs */\n \t\tchar type[20];\n+\t\tstruct sha1_size_cache *e;\n+\n+\t\tif (size_only) {\n+\t\t\te = locate_size_cache(s->sha1, UINT_MAX);\n+\t\t\tif (e) {\n+\t\t\t\ts->size = e->size;\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t}\n \t\ts->data = read_sha1_file(s->sha1, type, &s->size);\n \t\ts->should_free = 1;\n+\t\tif (s->data && size_only)\n+\t\t\tlocate_size_cache(s->sha1, s->size);\n \t}\n \treturn 0;\n }\n@@ -361,7 +427,7 @@ static void prepare_temp_file(const char\n \t\treturn;\n \t}\n \telse {\n-\t\tif (diff_populate_filespec(one))\n+\t\tif (diff_populate_filespec(one, 0))\n \t\t\tdie(\"cannot read data blob for %s\", one->path);\n \t\tprep_temp_blob(temp, one->data, one->size,\n \t\t\t       one->sha1, one->mode);\n@@ -492,9 +558,23 @@ static void run_diff(const char *name,\n \t\trun_external_diff(pgm, name, other, one, two, xfrm_msg);\n }\n \n-void diff_setup(int reverse_diff_)\n+void diff_setup(int flags)\n {\n-\treverse_diff = reverse_diff_;\n+\tif (flags & DIFF_SETUP_REVERSE)\n+\t\treverse_diff = 1;\n+\tif (flags & DIFF_SETUP_USE_CACHE) {\n+\t\tif (!active_cache)\n+\t\t\t/* read-cache does not die even when it fails\n+\t\t\t * so it is safe for us to do this here.  Also\n+\t\t\t * it does not smudge active_cache or active_nr\n+\t\t\t * when it fails, so we do not have to worry about\n+\t\t\t * cleaning it up oufselves either.\n+\t\t\t */\n+\t\t\tread_cache();\n+\t}\n+\tif (flags & DIFF_SETUP_USE_SIZE_CACHE)\n+\t\tuse_size_cache = 1;\n+\t\n }\n \n struct diff_queue_struct diff_queued_diff;\n@@ -517,10 +597,18 @@ struct diff_filepair *diff_queue(struct \n \tdp->one = one;\n \tdp->two = two;\n \tdp->score = 0;\n+\tdp->source_stays = 0;\n \tdiff_q(queue, dp);\n \treturn dp;\n }\n \n+void diff_free_filepair(struct diff_filepair *p)\n+{\n+\tdiff_free_filespec_data(p->one);\n+\tdiff_free_filespec_data(p->two);\n+\tfree(p);\n+}\n+\n static void diff_flush_raw(struct diff_filepair *p,\n \t\t\t   int line_termination,\n \t\t\t   int inter_name_termination)\n@@ -615,7 +703,7 @@ static void diff_flush_patch(struct diff\n \t\tsprintf(msg_,\n \t\t\t\"similarity index %d%%\\n\"\n \t\t\t\"copy from %s\\n\"\n-\t\t\t\"copy to %s\\n\",\n+\t\t\t\"copy to %s\",\n \t\t\t(int)(0.5 + p->score * 100.0/MAX_SCORE),\n \t\t\tp->one->path, p->two->path);\n \t\tmsg = msg_;\n@@ -624,7 +712,7 @@ static void diff_flush_patch(struct diff\n \t\tsprintf(msg_,\n \t\t\t\"similarity index %d%%\\n\"\n \t\t\t\"rename old %s\\n\"\n-\t\t\t\"rename new %s\\n\",\n+\t\t\t\"rename new %s\",\n \t\t\t(int)(0.5 + p->score * 100.0/MAX_SCORE),\n \t\t\tp->one->path, p->two->path);\n \t\tmsg = msg_;\n@@ -639,28 +727,6 @@ static void diff_flush_patch(struct diff\n \t\trun_diff(name, other, p->one, p->two, msg);\n }\n \n-int diff_needs_to_stay(struct diff_queue_struct *q, int i,\n-\t\t       struct diff_filespec *it)\n-{\n-\t/* If it will be used in later entry (either stay or used\n-\t * as the source of rename/copy), we need to copy, not rename.\n-\t */\n-\twhile (i < q->nr) {\n-\t\tstruct diff_filepair *p = q->queue[i++];\n-\t\tif (!DIFF_FILE_VALID(p->two))\n-\t\t\tcontinue; /* removed is fine */\n-\t\tif (strcmp(p->one->path, it->path))\n-\t\t\tcontinue; /* not relevant */\n-\n-\t\t/* p has its src set to *it and it is not a delete;\n-\t\t * it will be used for in-place change, rename/copy,\n-\t\t * or just stays there.  We cannot rename it out.\n-\t\t */\n-\t\treturn 1;\n-\t}\n-\treturn 0;\n-}\n-\n int diff_queue_is_empty(void)\n {\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n@@ -689,8 +755,8 @@ void diff_debug_filepair(const struct di\n {\n \tdiff_debug_filespec(p->one, i, \"one\");\n \tdiff_debug_filespec(p->two, i, \"two\");\n-\tfprintf(stderr, \"score %d, status %c\\n\",\n-\t\tp->score, p->status ? : '?');\n+\tfprintf(stderr, \"score %d, status %c source_stays %d\\n\",\n+\t\tp->score, p->status ? : '?', p->source_stays);\n }\n \n void diff_debug_queue(const char *msg, struct diff_queue_struct *q)\n@@ -712,8 +778,6 @@ static void diff_resolve_rename_copy(voi\n \tstruct diff_filepair *p, *pp;\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \n-\t/* This should not depend on the ordering of things. */\n-\n \tdiff_debug_queue(\"resolve-rename-copy\", q);\n \n \tfor (i = 0; i < q->nr; i++) {\n@@ -721,23 +785,28 @@ static void diff_resolve_rename_copy(voi\n \t\tp->status = 0; /* undecided */\n \t\tif (DIFF_PAIR_UNMERGED(p))\n \t\t\tp->status = 'U';\n-\t\telse if (!DIFF_FILE_VALID((p)->one))\n+\t\telse if (!DIFF_FILE_VALID(p->one))\n \t\t\tp->status = 'N';\n-\t\telse if (!DIFF_FILE_VALID((p)->two)) {\n-\t\t\t/* Deletion record should be omitted if there\n-\t\t\t * are rename/copy entries using this one as\n-\t\t\t * the source.  Then we can say one of them\n-\t\t\t * is a rename and the rest are copies.\n+\t\telse if (!DIFF_FILE_VALID(p->two)) {\n+\t\t\t/* Deleted entry may have been picked up by\n+\t\t\t * another rename-copy entry.  So we scan the\n+\t\t\t * queue and if we find one that uses us as the\n+\t\t\t * source we do not say delete for this entry.\n \t\t\t */\n-\t\t\tp->status = 'D';\n \t\t\tfor (j = 0; j < q->nr; j++) {\n \t\t\t\tpp = q->queue[j];\n-\t\t\t\tif (!strcmp(pp->one->path, p->one->path) &&\n-\t\t\t\t    strcmp(pp->one->path, pp->two->path)) {\n+\t\t\t\tif (!strcmp(p->one->path, pp->one->path) &&\n+\t\t\t\t    pp->score) {\n+\t\t\t\t\t/* rename/copy are always valid\n+\t\t\t\t\t * so we do not say DIFF_FILE_VALID()\n+\t\t\t\t\t * on pp->one and pp->two.\n+\t\t\t\t\t */\n \t\t\t\t\tp->status = 'X';\n \t\t\t\t\tbreak;\n \t\t\t\t}\n \t\t\t}\n+\t\t\tif (!p->status)\n+\t\t\t\tp->status = 'D';\n \t\t}\n \t\telse if (DIFF_PAIR_TYPE_CHANGED(p))\n \t\t\tp->status = 'T';\n@@ -746,33 +815,24 @@ static void diff_resolve_rename_copy(voi\n \t\t * whose both sides are valid and of the same type, i.e.\n \t\t * either in-place edit or rename/copy edit.\n \t\t */\n-\t\telse if (strcmp(p->one->path, p->two->path)) {\n-\t\t\t/* See if there is somebody else anywhere that\n-\t\t\t * will keep the path (either modified or\n-\t\t\t * unmodified).  If so, we have to be a copy,\n-\t\t\t * not a rename.  In addition, if there is\n-\t\t\t * some other rename or copy that comes later\n-\t\t\t * than us that uses the same source, we\n-\t\t\t * have to be a copy, not a rename.\n+\t\telse if (p->score) {\n+\t\t\tif (p->source_stays) {\n+\t\t\t\tp->status = 'C';\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\t/* See if there is some other filepair that\n+\t\t\t * copies from the same source as us.  If so\n+\t\t\t * we are a copy.  Otherwise we are a rename.\n \t\t\t */\n-\t\t\tfor (j = 0; j < q->nr; j++) {\n+\t\t\tfor (j = i + 1; j < q->nr; j++) {\n \t\t\t\tpp = q->queue[j];\n \t\t\t\tif (strcmp(pp->one->path, p->one->path))\n-\t\t\t\t\tcontinue;\n-\t\t\t\tif (!strcmp(pp->one->path, pp->two->path)) {\n-\t\t\t\t\tif (DIFF_FILE_VALID(pp->two)) {\n-\t\t\t\t\t\t/* non-delete */\n-\t\t\t\t\t\tp->status = 'C';\n-\t\t\t\t\t\tbreak;\n-\t\t\t\t\t}\n-\t\t\t\t\tcontinue;\n-\t\t\t\t}\n-\t\t\t\t/* pp is a rename/copy ... */\n-\t\t\t\tif (i < j) {\n-\t\t\t\t\t/* ... and comes later than us */\n-\t\t\t\t\tp->status = 'C';\n-\t\t\t\t\tbreak;\n-\t\t\t\t}\n+\t\t\t\t\tcontinue; /* not us */\n+\t\t\t\tif (!pp->score)\n+\t\t\t\t\tcontinue; /* not a rename/copy */\n+\t\t\t\t/* pp is a rename/copy from the same source */\n+\t\t\t\tp->status = 'C';\n+\t\t\t\tbreak;\n \t\t\t}\n \t\t\tif (!p->status)\n \t\t\t\tp->status = 'R';\n@@ -781,8 +841,11 @@ static void diff_resolve_rename_copy(voi\n \t\t\t p->one->mode != p->two->mode)\n \t\t\tp->status = 'M';\n \t\telse\n-\t\t\t/* this is a \"no-change\" entry */\n-\t\t\tp->status = 'X';\n+\t\t\t/* this is a \"no-change\" entry.\n+\t\t\t * should not happen anymore.\n+\t\t\t * p->status = 'X';\n+\t\t\t */\n+\t\t\tdie(\"internal error in diffcore: unmodified entry remains\");\n \t}\n \tdiff_debug_queue(\"resolve-rename-copy done\", q);\n }\n@@ -817,12 +880,8 @@ void diff_flush(int diff_output_style, i\n \t\t\tbreak;\n \t\t}\n \t}\n-\tfor (i = 0; i < q->nr; i++) {\n-\t\tstruct diff_filepair *p = q->queue[i];\n-\t\tdiff_free_filespec_data(p->one);\n-\t\tdiff_free_filespec_data(p->two);\n-\t\tfree(p);\n-\t}\n+\tfor (i = 0; i < q->nr; i++)\n+\t\tdiff_free_filepair(q->queue[i]);\n \tfree(q->queue);\n \tq->queue = NULL;\n \tq->nr = q->alloc = 0;\n@@ -883,7 +942,7 @@ void diff_helper_input(unsigned old_mode\n \tif (new_mode)\n \t\tfill_filespec(two, new_sha1, new_mode);\n \tdp = diff_queue(&diff_queued_diff, one, two);\n-\tdp->score = score;\n+\tdp->score = score * MAX_SCORE / 100;\n \tdp->status = status;\n }\n \ndiff --git a/diff.h b/diff.h\n--- a/diff.h\n+++ b/diff.h\n@@ -28,22 +28,28 @@ extern void diff_unmerge(const char *pat\n \n extern int diff_scoreopt_parse(const char *opt);\n \n-#define DIFF_FORMAT_HUMAN\t0\n-#define DIFF_FORMAT_MACHINE\t1\n-#define DIFF_FORMAT_PATCH\t2\n-#define DIFF_FORMAT_NO_OUTPUT\t3\n-extern void diff_setup(int reverse);\n+#define DIFF_SETUP_REVERSE      \t1\n+#define DIFF_SETUP_USE_CACHE\t\t2\n+#define DIFF_SETUP_USE_SIZE_CACHE\t4\n+extern void diff_setup(int flags);\n \n #define DIFF_DETECT_RENAME\t1\n #define DIFF_DETECT_COPY\t2\n \n extern void diffcore_rename(int rename_copy, int minimum_score);\n \n-extern void diffcore_pickaxe(const char *needle);\n+#define DIFF_PICKAXE_ALL\t1\n+extern void diffcore_pickaxe(const char *needle, int opts);\n+\n extern void diffcore_pathspec(const char **pathspec);\n \n extern int diff_queue_is_empty(void);\n \n+#define DIFF_FORMAT_HUMAN\t0\n+#define DIFF_FORMAT_MACHINE\t1\n+#define DIFF_FORMAT_PATCH\t2\n+#define DIFF_FORMAT_NO_OUTPUT\t3\n+\n extern void diff_flush(int output_style, int resolve_rename_copy);\n \n #endif /* DIFF_H */\ndiff --git a/diffcore-pathspec.c b/diffcore-pathspec.c\n--- a/diffcore-pathspec.c\n+++ b/diffcore-pathspec.c\n@@ -55,11 +55,10 @@ void diffcore_pathspec(const char **path\n \n \tfor (i = 0; i < q->nr; i++) {\n \t\tstruct diff_filepair *p = q->queue[i];\n-\t\tif (matches_pathspec(p->one->path, spec, speccnt) ||\n-\t\t    matches_pathspec(p->two->path, spec, speccnt))\n+\t\tif (matches_pathspec(p->two->path, spec, speccnt))\n \t\t\tdiff_q(&outq, p);\n \t\telse\n-\t\t\tfree(p);\n+\t\t\tdiff_free_filepair(p);\n \t}\n \tfree(q->queue);\n \t*q = outq;\ndiff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c\n--- a/diffcore-pickaxe.c\n+++ b/diffcore-pickaxe.c\n@@ -11,7 +11,7 @@ static int contains(struct diff_filespec\n {\n \tunsigned long offset, sz;\n \tconst char *data;\n-\tif (diff_populate_filespec(one))\n+\tif (diff_populate_filespec(one, 0))\n \t\treturn 0;\n \tsz = one->size;\n \tdata = one->data;\n@@ -21,36 +21,73 @@ static int contains(struct diff_filespec\n \treturn 0;\n }\n \n-void diffcore_pickaxe(const char *needle)\n+void diffcore_pickaxe(const char *needle, int opts)\n {\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \tunsigned long len = strlen(needle);\n-\tint i;\n+\tint i, has_changes;\n \tstruct diff_queue_struct outq;\n \toutq.queue = NULL;\n \toutq.nr = outq.alloc = 0;\n \n-\tfor (i = 0; i < q->nr; i++) {\n-\t\tstruct diff_filepair *p = q->queue[i];\n-\t\tint onum = outq.nr;\n-\t\tif (!DIFF_FILE_VALID(p->one)) {\n-\t\t\tif (!DIFF_FILE_VALID(p->two))\n-\t\t\t\tcontinue; /* ignore nonsense */\n-\t\t\t/* created */\n-\t\t\tif (contains(p->two, needle, len))\n-\t\t\t\tdiff_q(&outq, p);\n+\tif (opts & DIFF_PICKAXE_ALL) {\n+\t\t/* Showing the whole changeset if needle exists */\n+\t\tfor (i = has_changes = 0; !has_changes && i < q->nr; i++) {\n+\t\t\tstruct diff_filepair *p = q->queue[i];\n+\t\t\tif (!DIFF_FILE_VALID(p->one)) {\n+\t\t\t\tif (!DIFF_FILE_VALID(p->two))\n+\t\t\t\t\tcontinue; /* ignore unmerged */\n+\t\t\t\t/* created */\n+\t\t\t\tif (contains(p->two, needle, len))\n+\t\t\t\t\thas_changes++;\n+\t\t\t}\n+\t\t\telse if (!DIFF_FILE_VALID(p->two)) {\n+\t\t\t\tif (contains(p->one, needle, len))\n+\t\t\t\t\thas_changes++;\n+\t\t\t}\n+\t\t\telse if (!diff_unmodified_pair(p) &&\n+\t\t\t\t contains(p->one, needle, len) !=\n+\t\t\t\t contains(p->two, needle, len))\n+\t\t\t\thas_changes++;\n \t\t}\n-\t\telse if (!DIFF_FILE_VALID(p->two)) {\n-\t\t\tif (contains(p->one, needle, len))\n+\t\tif (has_changes)\n+\t\t\treturn; /* not munge the queue */\n+\n+\t\t/* otherwise we will clear the whole queue\n+\t\t * by copying the empty outq at the end of this\n+\t\t * function, but first clear the current entries\n+\t\t * in the queue.\n+\t\t */\n+\t\tfor (i = 0; i < q->nr; i++)\n+\t\t\tdiff_free_filepair(q->queue[i]);\n+\t}\n+\telse \n+\t\t/* Showing only the filepairs that has the needle */\n+\t\tfor (i = 0; i < q->nr; i++) {\n+\t\t\tstruct diff_filepair *p = q->queue[i];\n+\t\t\thas_changes = 0;\n+\t\t\tif (!DIFF_FILE_VALID(p->one)) {\n+\t\t\t\tif (!DIFF_FILE_VALID(p->two))\n+\t\t\t\t\t; /* ignore unmerged */\n+\t\t\t\t/* created */\n+\t\t\t\telse if (contains(p->two, needle, len))\n+\t\t\t\t\thas_changes = 1;\n+\t\t\t}\n+\t\t\telse if (!DIFF_FILE_VALID(p->two)) {\n+\t\t\t\tif (contains(p->one, needle, len))\n+\t\t\t\t\thas_changes = 1;\n+\t\t\t}\n+\t\t\telse if (!diff_unmodified_pair(p) &&\n+\t\t\t\t contains(p->one, needle, len) !=\n+\t\t\t\t contains(p->two, needle, len))\n+\t\t\t\thas_changes = 1;\n+\n+\t\t\tif (has_changes)\n \t\t\t\tdiff_q(&outq, p);\n+\t\t\telse\n+\t\t\t\tdiff_free_filepair(p);\n \t\t}\n-\t\telse if (!diff_unmodified_pair(p) &&\n-\t\t\t contains(p->one, needle, len) !=\n-\t\t\t contains(p->two, needle, len))\n-\t\t\tdiff_q(&outq, p);\n-\t\tif (onum == outq.nr)\n-\t\t\tfree(p);\n-\t}\n+\n \tfree(q->queue);\n \t*q = outq;\n \treturn;\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -52,14 +52,15 @@ static struct diff_rename_dst *locate_re\n \treturn &(rename_dst[first]);\n }\n \n+/* Table of rename/copy src files */\n static struct diff_rename_src {\n \tstruct diff_filespec *one;\n-\tunsigned src_used : 1;\n+\tunsigned src_stays : 1;\n } *rename_src;\n static int rename_src_nr, rename_src_alloc;\n \n-static struct diff_rename_src *locate_rename_src(struct diff_filespec *one,\n-\t\t\t\t\t\t int insert_ok)\n+static struct diff_rename_src *register_rename_src(struct diff_filespec *one,\n+\t\t\t\t\t\t   int src_stays)\n {\n \tint first, last;\n \n@@ -77,9 +78,7 @@ static struct diff_rename_src *locate_re\n \t\t}\n \t\tfirst = next+1;\n \t}\n-\t/* not found */\n-\tif (!insert_ok)\n-\t\treturn NULL;\n+\n \t/* insert to make it at \"first\" */\n \tif (rename_src_alloc <= rename_src_nr) {\n \t\trename_src_alloc = alloc_nr(rename_src_alloc);\n@@ -91,7 +90,7 @@ static struct diff_rename_src *locate_re\n \t\tmemmove(rename_src + first + 1, rename_src + first,\n \t\t\t(rename_src_nr - first - 1) * sizeof(*rename_src));\n \trename_src[first].one = one;\n-\trename_src[first].src_used = 0;\n+\trename_src[first].src_stays = src_stays;\n \treturn &(rename_src[first]);\n }\n \n@@ -100,8 +99,11 @@ static int is_exact_match(struct diff_fi\n \tif (src->sha1_valid && dst->sha1_valid &&\n \t    !memcmp(src->sha1, dst->sha1, 20))\n \t\treturn 1;\n-\tif (diff_populate_filespec(src) || diff_populate_filespec(dst))\n-\t\t/* this is an error but will be caught downstream */\n+\tif (diff_populate_filespec(src, 1) || diff_populate_filespec(dst, 1))\n+\t\treturn 0;\n+\tif (src->size != dst->size)\n+\t\treturn 0;\n+\tif (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))\n \t\treturn 0;\n \tif (src->size == dst->size &&\n \t    !memcmp(src->data, dst->data, src->size))\n@@ -113,7 +115,6 @@ struct diff_score {\n \tint src; /* index in rename_src */\n \tint dst; /* index in rename_dst */\n \tint score;\n-\tint rank;\n };\n \n static int estimate_similarity(struct diff_filespec *src,\n@@ -127,9 +128,11 @@ static int estimate_similarity(struct di\n \t * dst, and then some edit has been applied to dst.\n \t *\n \t * Compare them and return how similar they are, representing\n-\t * the score as an integer between 0 and 10000, except\n-\t * where they match exactly it is considered better than anything\n-\t * else.\n+\t * the score as an integer between 0 and MAX_SCORE.\n+\t *\n+\t * When there is an exact match, it is considered a better\n+\t * match than anything else; the destination does not even\n+\t * call into this function in that case.\n \t */\n \tvoid *delta;\n \tunsigned long delta_size, base_size;\n@@ -149,6 +152,7 @@ static int estimate_similarity(struct di\n \t/* We would not consider edits that change the file size so\n \t * drastically.  delta_size must be smaller than\n \t * (MAX_SCORE-minimum_score)/MAX_SCORE * min(src->size, dst->size).\n+\t *\n \t * Note that base_size == 0 case is handled here already\n \t * and the final score computation below would not have a\n \t * divide-by-zero issue.\n@@ -156,6 +160,9 @@ static int estimate_similarity(struct di\n \tif (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)\n \t\treturn 0;\n \n+\tif (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))\n+\t\treturn 0; /* error but caught downstream */\n+\n \tdelta = diff_delta(src->data, src->size,\n \t\t\t   dst->data, dst->size,\n \t\t\t   &delta_size);\n@@ -163,7 +170,7 @@ static int estimate_similarity(struct di\n \t/* A delta that has a lot of literal additions would have\n \t * big delta_size no matter what else it does.\n \t */\n-\tif (minimum_score < MAX_SCORE * delta_size / base_size)\n+\tif (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)\n \t\treturn 0;\n \n \t/* Estimate the edit size by interpreting delta. */\n@@ -200,15 +207,14 @@ static void record_rename_pair(struct di\n \tfill_filespec(two, dst->sha1, dst->mode);\n \n \tdp = diff_queue(renq, one, two);\n-\tdp->score = score;\n-\n-\trename_src[src_index].src_used = 1;\n+\tdp->score = score ? : 1; /* make sure it is at least 1 */\n+\tdp->source_stays = rename_src[src_index].src_stays;\n \trename_dst[dst_index].pair = dp;\n }\n \n /*\n  * We sort the rename similarity matrix with the score, in descending\n- * order (more similar first).\n+ * order (the most similar first).\n  */\n static int score_compare(const void *a_, const void *b_)\n {\n@@ -223,7 +229,7 @@ int diff_scoreopt_parse(const char *opt)\n \t\treturn -1; /* that is not a -M nor -C option */\n \tdiglen = strspn(opt+2, \"0123456789\");\n \tif (diglen == 0 || strlen(opt+2) != diglen)\n-\t\treturn 0; /* use default */\n+\t\treturn DEFAULT_MINIMUM_SCORE; /* use default */\n \tsscanf(opt+2, \"%d\", &num);\n \tfor (i = 0, scale = 1; i < diglen; i++)\n \t\tscale *= 10;\n@@ -255,9 +261,9 @@ void diffcore_rename(int detect_rename, \n \t\t\telse\n \t\t\t\tlocate_rename_dst(p->two, 1);\n \t\telse if (!DIFF_FILE_VALID(p->two))\n-\t\t\tlocate_rename_src(p->one, 1);\n-\t\telse if (1 < detect_rename) /* find copy, too */\n-\t\t\tlocate_rename_src(p->one, 1);\n+\t\t\tregister_rename_src(p->one, 0);\n+\t\telse if (detect_rename == DIFF_DETECT_COPY)\n+\t\t\tregister_rename_src(p->one, 1);\n \t}\n \tif (rename_dst_nr == 0)\n \t\tgoto cleanup; /* nothing to do */\n@@ -308,7 +314,7 @@ void diffcore_rename(int detect_rename, \n \t\tif (dst->pair)\n \t\t\tcontinue; /* already done, either exact or fuzzy. */\n \t\tif (mx[i].score < minimum_score)\n-\t\t\tbreak; /* there is not any more diffs applicable. */\n+\t\t\tbreak; /* there is no more usable pair. */\n \t\trecord_rename_pair(&renq, mx[i].dst, mx[i].src, mx[i].score);\n \t}\n \tfree(mx);\n@@ -317,28 +323,21 @@ void diffcore_rename(int detect_rename, \n  flush_rest:\n \t/* At this point, we have found some renames and copies and they\n \t * are kept in renq.  The original list is still in *q.\n-\t *\n-\t * Scan the original list and move them into the outq; we will sort\n-\t * outq and swap it into the queue supplied to pass that to\n-\t * downstream, so we assign the sort keys in this loop.\n-\t *\n-\t * See comments at the top of record_rename_pair for numbers used\n-\t * to assign rename_rank.\n \t */\n \toutq.queue = NULL;\n \toutq.nr = outq.alloc = 0;\n \tfor (i = 0; i < q->nr; i++) {\n \t\tstruct diff_filepair *p = q->queue[i];\n-\t\tstruct diff_rename_src *src = locate_rename_src(p->one, 0);\n \t\tstruct diff_rename_dst *dst = locate_rename_dst(p->two, 0);\n \t\tstruct diff_filepair *pair_to_free = NULL;\n \n \t\tif (dst) {\n \t\t\t/* creation */\n \t\t\tif (dst->pair) {\n-\t\t\t\t/* renq has rename/copy already to produce\n-\t\t\t\t * this file, so we do not emit the creation\n-\t\t\t\t * record in the output.\n+\t\t\t\t/* renq has rename/copy to produce\n+\t\t\t\t * this file already, so we do not\n+\t\t\t\t * emit the creation record in the\n+\t\t\t\t * output.\n \t\t\t\t */\n \t\t\t\tdiff_q(&outq, dst->pair);\n \t\t\t\tpair_to_free = p;\n@@ -350,25 +349,17 @@ void diffcore_rename(int detect_rename, \n \t\t\t\tdiff_q(&outq, p);\n \t\t}\n \t\telse if (!diff_unmodified_pair(p))\n-\t\t\t/* all the other cases need to be recorded as is */\n+\t\t\t/* all the usual ones need to be kept */\n \t\t\tdiff_q(&outq, p);\n-\t\telse {\n-\t\t\t/* unmodified pair needs to be recorded only if\n-\t\t\t * it is used as the source of rename/copy\n-\t\t\t */\n-\t\t\tif (src && src->src_used)\n-\t\t\t\tdiff_q(&outq, p);\n-\t\t\telse\n-\t\t\t\tpair_to_free = p;\n-\t\t}\n-\t\tif (pair_to_free) {\n-\t\t\tdiff_free_filespec_data(pair_to_free->one);\n-\t\t\tdiff_free_filespec_data(pair_to_free->two);\n-\t\t\tfree(pair_to_free);\n-\t\t}\n+\t\telse\n+\t\t\t/* no need to keep unmodified pairs */\n+\t\t\tpair_to_free = p;\n+\n+\t\tif (pair_to_free)\n+\t\t\tdiff_free_filepair(pair_to_free);\n \t}\n \tdiff_debug_queue(\"done copying original\", &outq);\n-\n+\t\n \tfree(renq.queue);\n \tfree(q->queue);\n \t*q = outq;\ndiff --git a/diffcore.h b/diffcore.h\n--- a/diffcore.h\n+++ b/diffcore.h\n@@ -33,14 +33,17 @@ extern struct diff_filespec *alloc_files\n extern void fill_filespec(struct diff_filespec *, const unsigned char *,\n \t\t\t  unsigned short);\n \n-extern int diff_populate_filespec(struct diff_filespec *);\n+extern int diff_populate_filespec(struct diff_filespec *, int);\n extern void diff_free_filespec_data(struct diff_filespec *);\n \n struct diff_filepair {\n \tstruct diff_filespec *one;\n \tstruct diff_filespec *two;\n-\tint score; /* only valid when one and two are different paths */\n-\tint status; /* M C R N D U (see Documentation/diff-format.txt) */\n+\tunsigned short int score; /* only valid when one and two are\n+\t\t\t\t   * different paths\n+\t\t\t\t   */\n+\tchar source_stays; /* all of R/C are copies */\n+\tchar status; /* M C R N D U (see Documentation/diff-format.txt) */\n };\n #define DIFF_PAIR_UNMERGED(p) \\\n \t(!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))\n@@ -54,6 +57,8 @@ struct diff_filepair {\n \t(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \\\n \tS_ISLNK(mode) ? S_IFLNK : S_IFDIR)\n \n+extern void diff_free_filepair(struct diff_filepair *);\n+\n extern int diff_unmodified_pair(struct diff_filepair *);\n \n struct diff_queue_struct {\n@@ -68,9 +73,6 @@ extern struct diff_filepair *diff_queue(\n \t\t\t\t\tstruct diff_filespec *);\n extern void diff_q(struct diff_queue_struct *, struct diff_filepair *);\n \n-extern int diff_needs_to_stay(struct diff_queue_struct *, int,\n-\t\t\t      struct diff_filespec *);\n-\n #define DIFF_DEBUG 0\n #if DIFF_DEBUG\n void diff_debug_filespec(struct diff_filespec *, int, const char *);\ndiff --git a/git-external-diff-script b/git-external-diff-script\n--- a/git-external-diff-script\n+++ b/git-external-diff-script\n@@ -59,7 +59,7 @@ then\n     echo \"new mode $mode2\"\n     if test \"$xfrm_msg\" != \"\"\n     then\n-\techo -n $xfrm_msg\n+\techo \"$xfrm_msg\"\n     fi\n fi\n diff ${GIT_DIFF_OPTS-'-pu'} -L \"a/$name1\" -L \"b/$name2\" \"$tmp1\" \"$tmp2\"\ndiff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh\nnew file mode 100755\n--- /dev/null\n+++ b/t/t4007-rename-3.sh\n@@ -0,0 +1,103 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2005 Junio C Hamano\n+#\n+\n+test_description='Rename interaction with pathspec.\n+\n+'\n+. ./test-lib.sh\n+\n+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'\n+_x40=\"$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40\"\n+sanitize_diff_raw='s/ \\('\"$_x40\"'\\) \\1 \\([CR]\\)[0-9]*\t/ \\1 \\1 \\2#\t/'\n+compare_diff_raw () {\n+    # When heuristics are improved, the score numbers would change.\n+    # Ignore them while comparing.\n+    # Also we do not check SHA1 hash generation in this test, which\n+    # is a job for t0000-basic.sh\n+\n+    sed -e \"$sanitize_diff_raw\" <\"$1\" >.tmp-1\n+    sed -e \"$sanitize_diff_raw\" <\"$2\" >.tmp-2\n+    diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2\n+}\n+\n+test_expect_success \\\n+    'prepare reference tree' \\\n+    'mkdir path0 path1 &&\n+     cp ../../COPYING path0/COPYING &&\n+     git-update-cache --add path0/COPYING &&\n+    tree=$(git-write-tree) &&\n+    echo $tree'\n+\n+test_expect_success \\\n+    'prepare work tree' \\\n+    'cp path0/COPYING path1/COPYING &&\n+     git-update-cache --add --remove path0/COPYING path1/COPYING'\n+\n+# In the tree, there is only path0/COPYING.  In the cache, path0 and\n+# path1 both have COPYING and the latter is a copy of path0/COPYING.\n+# Comparing the full tree with cache should tell us so.\n+\n+git-diff-cache -C $tree >current\n+\n+cat >expected <<\\EOF\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100\tpath0/COPYING\tpath1/COPYING\n+EOF\n+\n+test_expect_success \\\n+    'validate the result' \\\n+    'compare_diff_raw current expected'\n+\n+# In the tree, there is only path0/COPYING.  In the cache, path0 and\n+# path1 both have COPYING and the latter is a copy of path0/COPYING.\n+# When we omit output from path0 it should still be able to tell us\n+# that path1/COPYING is result from a copy from path0/COPYING, not\n+# rename, which would imply path0/COPYING is now gone.\n+\n+git-diff-cache -C $tree path1 >current\n+\n+cat >expected <<\\EOF\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100\tpath0/COPYING\tpath1/COPYING\n+EOF\n+\n+test_expect_success \\\n+    'validate the result' \\\n+    'compare_diff_raw current expected'\n+\n+test_expect_success \\\n+    'tweak work tree' \\\n+    'rm -f path0/COPYING &&\n+     git-update-cache --remove path0/COPYING'\n+\n+# In the tree, there is only path0/COPYING.  In the cache, path0 does\n+# not have COPYING anymore and path1 has COPYING which is a copy of\n+# path0/COPYING.  Showing the full tree with cache should tell us about\n+# the rename.\n+\n+git-diff-cache -C $tree >current\n+\n+cat >expected <<\\EOF\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100\tpath0/COPYING\tpath1/COPYING\n+EOF\n+\n+test_expect_success \\\n+    'validate the result' \\\n+    'compare_diff_raw current expected'\n+\n+# In the tree, there is only path0/COPYING.  In the cache, path0 does\n+# not have COPYING anymore and path1 has COPYING which is a copy of\n+# path0/COPYING.  Even if we restrict the output to path1, it still\n+# should show us the rename.\n+\n+git-diff-cache -C $tree path1 >current\n+\n+cat >expected <<\\EOF\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100\tpath0/COPYING\tpath1/COPYING\n+EOF\n+\n+test_expect_success \\\n+    'validate the result' \\\n+    'compare_diff_raw current expected'\n+\n+test_done\n\\ No newline at end of file\n------------------------------------------------\n\n"},{"id":"4048","messageId":"Pine.LNX.4.58.0505270848220.17402@ppc970.osdl.org","threadId":"726","inReplyTo":"7vsm091887.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Diff updates, fixing pathspec and rename/copy interaction.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-05-27T15:56:35Z","receivedAt":"2005-05-27T15:56:35Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Thu, 26 May 2005, Junio C Hamano wrote:\n>\n> During the mailing list discussion about diff-tree omitting to\n> call diffcore-pathspec, I realized that the current rename/copy\n> differentiator has a major flaw interacting with pathspec (or\n> any other filepair filters, including pickaxe).\n> \n> The problem was that in order to tell if the rename-copy source\n> still remains in the resulting tree (that is what determines if\n> one of the rename-copy can become a rename or everybody needs to\n> be a copy), diffcore-rename was sending a filepair that records\n> unmodified source downstream, expecting that it reaches\n> resolve_rename_copy() which happens as the final stage before\n> actual output happens.  Of course, pathspec and pickaxe can\n> interfere and happily remove that entry, in which case what\n> should be shown as a copy suddenly becomes a rename.\n\nUmm.\n\nI would much prefer a _much_ simpler fix at least for the pathname part,\nwhich is to just always require that pathspec handling is done _first_.\n\nWhy? Because that's fundamentally how git-diff-tree has to work, and it's\nhow my mental model has always been: the path limitations are a\nfirst-order filter, and if you give a directory, the end result should\nalways look exactly as if that directory was a project of its own.\n\nIn other words, if you limit yourself to a directory, and there was a\nrename that moved a file from outside that directory into it, then it is\nnot a rename at all, it is a \"create\". The stuff outside the pathspec\nlimit simply doesn't exist.\n\nThis is fundamentally how git-diff-tree has to work for pathspec to make\nany sense at all, and it's also the only usage that makes any sense (ie\nwhen I say \"git-whatchanged -p arch/i386 include/asm-i386\", I expect that\nthe patches that show up _only_ concern themselves with what happened in\nx86, and there's no cross-pollination with other stuff at all, even if\nrename detection is enabled).\n\nThat in turn implies that the other pathspec users have to work the same\nway, for the thing to be consistent.\n\nNow, I don't know how you want pickaxe to work, and maybe you want that to \nrun _after_ rename detection, I dunno. So I suspect you still want to do \nwhat this patch does, but I really don't want pathspec to be involved in \nthis thing..\n\n\t\tLinus\n"},{"id":"4067","messageId":"7vd5rczfz4.fsf@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"Pine.LNX.4.58.0505270848220.17402@ppc970.osdl.org","subject":"Re: [PATCH] Diff updates, fixing pathspec and rename/copy interaction.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T18:22:23Z","receivedAt":"2005-05-27T18:22:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":">>>>> \"LT\" == Linus Torvalds <torvalds@osdl.org> writes:\n\nLT> I would much prefer a _much_ simpler fix at least for the pathname part,\nLT> which is to just always require that pathspec handling is done _first_.\n\nLT> Why? Because that's fundamentally how git-diff-tree has to work, and it's\nLT> how my mental model has always been: the path limitations are a\nLT> first-order filter, and if you give a directory, the end result should\nLT> always look exactly as if that directory was a project of its own.\n\nEnlightment!\n\nMy initial thinking was to do rename/copy first, deliberately\nignoring pathspec (I was thinking about tweaking diff-tree to\nbreak out of its built-in pathspec limit to feed diffcore\n_everything_, even things outside of the specified directories\nwhen --detect-copies-harder is used), to cast a wider net so\nthat it can come up with \"the best matches\".  But what you just\nsaid made me realize that the definition of \"the best matches\" I\nhad in mind was not best at all.  I agree with you 100% that\npathspec should come first before everything else to limit the\nworld diffcore operates in, not just to limit the set of output\npaths.\n\nI still want to do what this patch does for another reason.  It\nis so much simpler if I do not have to carry around _some_\nunmatched pair after rename/copy.  The earlier representation\nwas forcing all the downstream diffcore filters to be aware of\nwhat rename/copy did, which was simply _wrong_.  They should not\nhave to care.\n\nAbout ordering of pickaxe and rename/copy, I think for most uses\nof diffcore filters, not limited to pickaxe, would make more\nsense if they come after rename/copy when rename/copy detection\nis in effect.  What I really wanted to do (I made a side note\ncomment in the earlier discussion about this, saying \"something\nlike streams\", but have not pursued it further) is to make these\ndiffcore filters/transformations stackable so that the main\nprograms can control not just if each of them is used or not,\nbut the order of application of the used ones.  I cannot offhand\nthink of a good use case of having pickaxe come _before_\nrename/copy, but there may be a case the user want to have\nthings in that order, and having the application order not\nhardcoded in three diff-* brothers is a major problem if we\nwanted to give that option.\n\n"},{"id":"4097","messageId":"7vk6lk5lxt.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"Pine.LNX.4.58.0505270848220.17402@ppc970.osdl.org","subject":"[PATCH 00/12] Diff updates","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T22:43:58Z","receivedAt":"2005-05-27T22:43:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This series consists of the following 12 patches.  Most of them\nare bugfixes and cleanups.  The last one is somewhat iffy,\nalthough it does not break things, and lies somewhere between a\nrequest for inclusion and a request for comments.\n\n    [PATCH 01/12] Fix math thinko in similarity estimator.\n    [PATCH 02/12] Introduce diff_free_filepair() funcion.\n    [PATCH 03/12] Make pathspec only care about the detination tree.\n    [PATCH 04/12] Remove unused rank field from diff_core structure.\n    [PATCH 05/12] Do not expose internal scaling to diff-helper.\n    [PATCH 06/12] Remove final newline from the value of xfrm_msg variable.\n    [PATCH 07/12] Clean up diff_setup() to make it more extensible.\n    [PATCH 08/12] Remove a function not used anymore.\n    [PATCH 09/12] Add --pickaxe-all to diff-* brothers.\n    [PATCH 10/12] Fix the way diffcore-rename records unremoved source.\n    [PATCH 11/12] Move pathspec to the beginning of the diffcore chain.\n    [PATCH 12/12] Optimize diff-tree -[CM] --stdin\n\n\n"},{"id":"4099","messageId":"7vekbs5lnx.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vk6lk5lxt.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 01/12] Fix math thinko in similarity estimator.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T22:49:54Z","receivedAt":"2005-05-27T22:49:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The math to reject delta that is too big was confused.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiffcore-rename.c |    2 +-\n1 files changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -163,7 +163,7 @@ static int estimate_similarity(struct di\n \t/* A delta that has a lot of literal additions would have\n \t * big delta_size no matter what else it does.\n \t */\n-\tif (minimum_score < MAX_SCORE * delta_size / base_size)\n+\tif (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)\n \t\treturn 0;\n \n \t/* Estimate the edit size by interpreting delta. */\n------------------------------------------------\n\n"},{"id":"4100","messageId":"7v7jhk5lmx.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vk6lk5lxt.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 02/12] Introduce diff_free_filepair() funcion.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T22:50:30Z","receivedAt":"2005-05-27T22:50:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This introduces a new function to free a common data structure,\nand plugs some leaks.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff.c              |   15 +++++++++------\ndiffcore-pathspec.c |    2 +-\ndiffcore-pickaxe.c  |    2 +-\ndiffcore-rename.c   |    7 ++-----\ndiffcore.h          |    2 ++\n5 files changed, 15 insertions(+), 13 deletions(-)\n\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -521,6 +521,13 @@ struct diff_filepair *diff_queue(struct \n \treturn dp;\n }\n \n+void diff_free_filepair(struct diff_filepair *p)\n+{\n+\tdiff_free_filespec_data(p->one);\n+\tdiff_free_filespec_data(p->two);\n+\tfree(p);\n+}\n+\n static void diff_flush_raw(struct diff_filepair *p,\n \t\t\t   int line_termination,\n \t\t\t   int inter_name_termination)\n@@ -817,12 +824,8 @@ void diff_flush(int diff_output_style, i\n \t\t\tbreak;\n \t\t}\n \t}\n-\tfor (i = 0; i < q->nr; i++) {\n-\t\tstruct diff_filepair *p = q->queue[i];\n-\t\tdiff_free_filespec_data(p->one);\n-\t\tdiff_free_filespec_data(p->two);\n-\t\tfree(p);\n-\t}\n+\tfor (i = 0; i < q->nr; i++)\n+\t\tdiff_free_filepair(q->queue[i]);\n \tfree(q->queue);\n \tq->queue = NULL;\n \tq->nr = q->alloc = 0;\ndiff --git a/diffcore-pathspec.c b/diffcore-pathspec.c\n--- a/diffcore-pathspec.c\n+++ b/diffcore-pathspec.c\n@@ -59,7 +59,7 @@ void diffcore_pathspec(const char **path\n \t\t    matches_pathspec(p->two->path, spec, speccnt))\n \t\t\tdiff_q(&outq, p);\n \t\telse\n-\t\t\tfree(p);\n+\t\t\tdiff_free_filepair(p);\n \t}\n \tfree(q->queue);\n \t*q = outq;\ndiff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c\n--- a/diffcore-pickaxe.c\n+++ b/diffcore-pickaxe.c\n@@ -49,7 +49,7 @@ void diffcore_pickaxe(const char *needle\n \t\t\t contains(p->two, needle, len))\n \t\t\tdiff_q(&outq, p);\n \t\tif (onum == outq.nr)\n-\t\t\tfree(p);\n+\t\t\tdiff_free_filepair(p);\n \t}\n \tfree(q->queue);\n \t*q = outq;\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -361,11 +361,8 @@ void diffcore_rename(int detect_rename, \n \t\t\telse\n \t\t\t\tpair_to_free = p;\n \t\t}\n-\t\tif (pair_to_free) {\n-\t\t\tdiff_free_filespec_data(pair_to_free->one);\n-\t\t\tdiff_free_filespec_data(pair_to_free->two);\n-\t\t\tfree(pair_to_free);\n-\t\t}\n+\t\tif (pair_to_free)\n+\t\t\tdiff_free_filepair(pair_to_free);\n \t}\n \tdiff_debug_queue(\"done copying original\", &outq);\n \ndiff --git a/diffcore.h b/diffcore.h\n--- a/diffcore.h\n+++ b/diffcore.h\n@@ -54,6 +54,8 @@ struct diff_filepair {\n \t(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \\\n \tS_ISLNK(mode) ? S_IFLNK : S_IFDIR)\n \n+extern void diff_free_filepair(struct diff_filepair *);\n+\n extern int diff_unmodified_pair(struct diff_filepair *);\n \n struct diff_queue_struct {\n------------------------------------------------\n\n"},{"id":"4101","messageId":"7vzmug4707.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vk6lk5lxt.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 03/12] Make pathspec only care about the detination tree.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T22:51:52Z","receivedAt":"2005-05-27T22:51:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Earlier it had a misguided attempt to include paths that matches\neither source tree or destination tree after the rename/copy\ndetection.  The new semantics will be that pathspec defines a\nnarrowed down world the diffcore operates in, so it should not\neven look at where in the source tree the path came from.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiffcore-pathspec.c |    3 +--\n1 files changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/diffcore-pathspec.c b/diffcore-pathspec.c\n--- a/diffcore-pathspec.c\n+++ b/diffcore-pathspec.c\n@@ -55,8 +55,7 @@ void diffcore_pathspec(const char **path\n \n \tfor (i = 0; i < q->nr; i++) {\n \t\tstruct diff_filepair *p = q->queue[i];\n-\t\tif (matches_pathspec(p->one->path, spec, speccnt) ||\n-\t\t    matches_pathspec(p->two->path, spec, speccnt))\n+\t\tif (matches_pathspec(p->two->path, spec, speccnt))\n \t\t\tdiff_q(&outq, p);\n \t\telse\n \t\t\tdiff_free_filepair(p);\n------------------------------------------------\n\n"},{"id":"4102","messageId":"7vsm0846yg.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vk6lk5lxt.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 04/12] Remove unused rank field from diff_core structure.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T22:52:55Z","receivedAt":"2005-05-27T22:52:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This removes a field that is no longer used from diff_score\nstructure.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiffcore-rename.c |    1 -\n1 files changed, 1 deletion(-)\n\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -113,7 +113,6 @@ struct diff_score {\n \tint src; /* index in rename_src */\n \tint dst; /* index in rename_dst */\n \tint score;\n-\tint rank;\n };\n \n static int estimate_similarity(struct diff_filespec *src,\n------------------------------------------------\n\n"},{"id":"4103","messageId":"7vll6046xg.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vk6lk5lxt.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 05/12] Do not expose internal scaling to diff-helper.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T22:53:31Z","receivedAt":"2005-05-27T22:53:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Instead we can normalize what diff-raw records at the diffcore\nside.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff-helper.c |    2 --\ndiff.c        |    2 +-\n2 files changed, 1 insertion(+), 3 deletions(-)\n\ndiff --git a/diff-helper.c b/diff-helper.c\n--- a/diff-helper.c\n+++ b/diff-helper.c\n@@ -4,7 +4,6 @@\n #include \"cache.h\"\n #include \"strbuf.h\"\n #include \"diff.h\"\n-#include \"diffcore.h\" /* just for MAX_SCORE */\n \n static const char *pickaxe = NULL;\n static int line_termination = '\\n';\n@@ -78,7 +77,6 @@ int main(int ac, const char **av) {\n \t\t\tif (status == 'R' || status == 'C') {\n \t\t\t\ttwo_paths = 1;\n \t\t\t\tsscanf(cp, \"%d\", &score);\n-\t\t\t\tscore = score * MAX_SCORE / 100;\n \t\t\t\tif (line_termination) {\n \t\t\t\t\tcp = strchr(cp,\n \t\t\t\t\t\t    inter_name_termination);\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -886,7 +886,7 @@ void diff_helper_input(unsigned old_mode\n \tif (new_mode)\n \t\tfill_filespec(two, new_sha1, new_mode);\n \tdp = diff_queue(&diff_queued_diff, one, two);\n-\tdp->score = score;\n+\tdp->score = score * MAX_SCORE / 100;\n \tdp->status = status;\n }\n \n------------------------------------------------\n\n"},{"id":"4104","messageId":"7vfyw846wh.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vk6lk5lxt.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 06/12] Remove final newline from the value of xfrm_msg variable.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T22:54:06Z","receivedAt":"2005-05-27T22:54:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This change makes the implementation of git-external-diff-script\ncleaner.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff.c                   |    6 +++---\ngit-external-diff-script |    2 +-\n2 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -141,7 +141,7 @@ static void builtin_diff(const char *nam\n \t\t\tprintf(\"new mode %s\\n\", temp[1].mode);\n \t\t}\n \t\tif (xfrm_msg && xfrm_msg[0])\n-\t\t\tfputs(xfrm_msg, stdout);\n+\t\t\tputs(xfrm_msg);\n \n \t\tif (strncmp(temp[0].mode, temp[1].mode, 3))\n \t\t\t/* we do not run diff between different kind\n@@ -622,7 +622,7 @@ static void diff_flush_patch(struct diff\n \t\tsprintf(msg_,\n \t\t\t\"similarity index %d%%\\n\"\n \t\t\t\"copy from %s\\n\"\n-\t\t\t\"copy to %s\\n\",\n+\t\t\t\"copy to %s\",\n \t\t\t(int)(0.5 + p->score * 100.0/MAX_SCORE),\n \t\t\tp->one->path, p->two->path);\n \t\tmsg = msg_;\n@@ -631,7 +631,7 @@ static void diff_flush_patch(struct diff\n \t\tsprintf(msg_,\n \t\t\t\"similarity index %d%%\\n\"\n \t\t\t\"rename old %s\\n\"\n-\t\t\t\"rename new %s\\n\",\n+\t\t\t\"rename new %s\",\n \t\t\t(int)(0.5 + p->score * 100.0/MAX_SCORE),\n \t\t\tp->one->path, p->two->path);\n \t\tmsg = msg_;\ndiff --git a/git-external-diff-script b/git-external-diff-script\n--- a/git-external-diff-script\n+++ b/git-external-diff-script\n@@ -59,7 +59,7 @@ then\n     echo \"new mode $mode2\"\n     if test \"$xfrm_msg\" != \"\"\n     then\n-\techo -n $xfrm_msg\n+\techo \"$xfrm_msg\"\n     fi\n fi\n diff ${GIT_DIFF_OPTS-'-pu'} -L \"a/$name1\" -L \"b/$name2\" \"$tmp1\" \"$tmp2\"\n------------------------------------------------\n\n"},{"id":"4105","messageId":"7vacmg46vm.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vk6lk5lxt.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 07/12] Clean up diff_setup() to make it more extensible.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T22:54:37Z","receivedAt":"2005-05-27T22:54:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This changes the argument of diff_setup() from an integer that\nsays if we are feeding reversed diff to a bitmask, so that later\nglobal options can be added more easily.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff-cache.c |    6 +++---\ndiff-files.c |    6 +++---\ndiff-tree.c  |    6 +++---\ndiff.c       |    5 +++--\ndiff.h       |   12 +++++++-----\n5 files changed, 19 insertions(+), 16 deletions(-)\n\ndiff --git a/diff-cache.c b/diff-cache.c\n--- a/diff-cache.c\n+++ b/diff-cache.c\n@@ -5,7 +5,7 @@ static int cached_only = 0;\n static int diff_output_format = DIFF_FORMAT_HUMAN;\n static int match_nonexisting = 0;\n static int detect_rename = 0;\n-static int reverse_diff = 0;\n+static int diff_setup_opt = 0;\n static int diff_score_opt = 0;\n static const char *pickaxe = NULL;\n \n@@ -202,7 +202,7 @@ int main(int argc, const char **argv)\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"-R\")) {\n-\t\t\treverse_diff = 1;\n+\t\t\tdiff_setup_opt |= DIFF_SETUP_REVERSE;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"-S\")) {\n@@ -224,7 +224,7 @@ int main(int argc, const char **argv)\n \t\tusage(diff_cache_usage);\n \n \t/* The rest is for paths restriction. */\n-\tdiff_setup(reverse_diff);\n+\tdiff_setup(diff_setup_opt);\n \n \tmark_merge_entries();\n \ndiff --git a/diff-files.c b/diff-files.c\n--- a/diff-files.c\n+++ b/diff-files.c\n@@ -11,7 +11,7 @@ static const char *diff_files_usage =\n \n static int diff_output_format = DIFF_FORMAT_HUMAN;\n static int detect_rename = 0;\n-static int reverse_diff = 0;\n+static int diff_setup_opt = 0;\n static int diff_score_opt = 0;\n static const char *pickaxe = NULL;\n static int silent = 0;\n@@ -51,7 +51,7 @@ int main(int argc, const char **argv)\n \t\telse if (!strcmp(argv[1], \"-z\"))\n \t\t\tdiff_output_format = DIFF_FORMAT_MACHINE;\n \t\telse if (!strcmp(argv[1], \"-R\"))\n-\t\t\treverse_diff = 1;\n+\t\t\tdiff_setup_opt |= DIFF_SETUP_REVERSE;\n \t\telse if (!strcmp(argv[1], \"-S\"))\n \t\t\tpickaxe = argv[1] + 2;\n \t\telse if (!strncmp(argv[1], \"-M\", 2)) {\n@@ -75,7 +75,7 @@ int main(int argc, const char **argv)\n \t\texit(1);\n \t}\n \n-\tdiff_setup(reverse_diff);\n+\tdiff_setup(diff_setup_opt);\n \n \tfor (i = 0; i < entries; i++) {\n \t\tstruct stat st;\ndiff --git a/diff-tree.c b/diff-tree.c\n--- a/diff-tree.c\n+++ b/diff-tree.c\n@@ -10,7 +10,7 @@ static int show_tree_entry_in_recursive \n static int read_stdin = 0;\n static int diff_output_format = DIFF_FORMAT_HUMAN;\n static int detect_rename = 0;\n-static int reverse_diff = 0;\n+static int diff_setup_opt = 0;\n static int diff_score_opt = 0;\n static const char *pickaxe = NULL;\n static const char *header = NULL;\n@@ -255,7 +255,7 @@ static int diff_tree_sha1(const unsigned\n \n static void call_diff_setup(void)\n {\n-\tdiff_setup(reverse_diff);\n+\tdiff_setup(diff_setup_opt);\n }\n \n static int call_diff_flush(void)\n@@ -497,7 +497,7 @@ int main(int argc, const char **argv)\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"-R\")) {\n-\t\t\treverse_diff = 1;\n+\t\t\tdiff_setup_opt |= DIFF_SETUP_REVERSE;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"-p\")) {\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -492,9 +492,10 @@ static void run_diff(const char *name,\n \t\trun_external_diff(pgm, name, other, one, two, xfrm_msg);\n }\n \n-void diff_setup(int reverse_diff_)\n+void diff_setup(int flags)\n {\n-\treverse_diff = reverse_diff_;\n+\tif (flags & DIFF_SETUP_REVERSE)\n+\t\treverse_diff = 1;\n }\n \n struct diff_queue_struct diff_queued_diff;\ndiff --git a/diff.h b/diff.h\n--- a/diff.h\n+++ b/diff.h\n@@ -28,11 +28,8 @@ extern void diff_unmerge(const char *pat\n \n extern int diff_scoreopt_parse(const char *opt);\n \n-#define DIFF_FORMAT_HUMAN\t0\n-#define DIFF_FORMAT_MACHINE\t1\n-#define DIFF_FORMAT_PATCH\t2\n-#define DIFF_FORMAT_NO_OUTPUT\t3\n-extern void diff_setup(int reverse);\n+#define DIFF_SETUP_REVERSE      \t1\n+extern void diff_setup(int flags);\n \n #define DIFF_DETECT_RENAME\t1\n #define DIFF_DETECT_COPY\t2\n@@ -44,6 +41,11 @@ extern void diffcore_pathspec(const char\n \n extern int diff_queue_is_empty(void);\n \n+#define DIFF_FORMAT_HUMAN\t0\n+#define DIFF_FORMAT_MACHINE\t1\n+#define DIFF_FORMAT_PATCH\t2\n+#define DIFF_FORMAT_NO_OUTPUT\t3\n+\n extern void diff_flush(int output_style, int resolve_rename_copy);\n \n #endif /* DIFF_H */\n------------------------------------------------\n\n"},{"id":"4106","messageId":"7v4qco46uy.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vk6lk5lxt.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 08/12] Remove a function not used anymore.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T22:55:01Z","receivedAt":"2005-05-27T22:55:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Earlier rename/copy detection left unmodified filepair in the\noutput and forced downstream to keep them even when they are\nfiltering, and the diff_needs_to_stay() function was used for\nthe logic.  It is not used anymore, so remove it.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff.c     |   22 ----------------------\ndiffcore.h |    3 ---\n2 files changed, 25 deletions(-)\n\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -647,28 +647,6 @@ static void diff_flush_patch(struct diff\n \t\trun_diff(name, other, p->one, p->two, msg);\n }\n \n-int diff_needs_to_stay(struct diff_queue_struct *q, int i,\n-\t\t       struct diff_filespec *it)\n-{\n-\t/* If it will be used in later entry (either stay or used\n-\t * as the source of rename/copy), we need to copy, not rename.\n-\t */\n-\twhile (i < q->nr) {\n-\t\tstruct diff_filepair *p = q->queue[i++];\n-\t\tif (!DIFF_FILE_VALID(p->two))\n-\t\t\tcontinue; /* removed is fine */\n-\t\tif (strcmp(p->one->path, it->path))\n-\t\t\tcontinue; /* not relevant */\n-\n-\t\t/* p has its src set to *it and it is not a delete;\n-\t\t * it will be used for in-place change, rename/copy,\n-\t\t * or just stays there.  We cannot rename it out.\n-\t\t */\n-\t\treturn 1;\n-\t}\n-\treturn 0;\n-}\n-\n int diff_queue_is_empty(void)\n {\n \tstruct diff_queue_struct *q = &diff_queued_diff;\ndiff --git a/diffcore.h b/diffcore.h\n--- a/diffcore.h\n+++ b/diffcore.h\n@@ -70,9 +70,6 @@ extern struct diff_filepair *diff_queue(\n \t\t\t\t\tstruct diff_filespec *);\n extern void diff_q(struct diff_queue_struct *, struct diff_filepair *);\n \n-extern int diff_needs_to_stay(struct diff_queue_struct *, int,\n-\t\t\t      struct diff_filespec *);\n-\n #define DIFF_DEBUG 0\n #if DIFF_DEBUG\n void diff_debug_filespec(struct diff_filespec *, int, const char *);\n------------------------------------------------\n\n"},{"id":"4107","messageId":"7vy8a02s9r.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vk6lk5lxt.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 09/12] Add --pickaxe-all to diff-* brothers.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T22:55:28Z","receivedAt":"2005-05-27T22:55:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When --pickaxe-all is given in addition to -S, pickaxe shows the\nentire diffs contained in the changeset, not just the diffs for\nthe filepair that touched the sought-after string.  This is\nuseful to see the changes in context.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff-cache.c       |    7 ++++\ndiff-files.c       |    5 ++-\ndiff-helper.c      |    7 +++-\ndiff-tree.c        |    7 ++++\ndiff.h             |    4 ++\ndiffcore-pickaxe.c |   77 +++++++++++++++++++++++++++++++++++++++--------------\n6 files changed, 81 insertions(+), 26 deletions(-)\n\ndiff --git a/diff-cache.c b/diff-cache.c\n--- a/diff-cache.c\n+++ b/diff-cache.c\n@@ -8,6 +8,7 @@ static int detect_rename = 0;\n static int diff_setup_opt = 0;\n static int diff_score_opt = 0;\n static const char *pickaxe = NULL;\n+static int pickaxe_opts = 0;\n \n /* A file entry went away or appeared */\n static void show_file(const char *prefix, struct cache_entry *ce, unsigned char *sha1, unsigned int mode)\n@@ -209,6 +210,10 @@ int main(int argc, const char **argv)\n \t\t\tpickaxe = arg + 2;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(arg, \"--pickaxe-all\")) {\n+\t\t\tpickaxe_opts = DIFF_PICKAXE_ALL;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(arg, \"-m\")) {\n \t\t\tmatch_nonexisting = 1;\n \t\t\tcontinue;\n@@ -238,7 +243,7 @@ int main(int argc, const char **argv)\n \tif (detect_rename)\n \t\tdiffcore_rename(detect_rename, diff_score_opt);\n \tif (pickaxe)\n-\t\tdiffcore_pickaxe(pickaxe);\n+\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n \tif (pathspec)\n \t\tdiffcore_pathspec(pathspec);\n \tdiff_flush(diff_output_format, 1);\ndiff --git a/diff-files.c b/diff-files.c\n--- a/diff-files.c\n+++ b/diff-files.c\n@@ -14,6 +14,7 @@ static int detect_rename = 0;\n static int diff_setup_opt = 0;\n static int diff_score_opt = 0;\n static const char *pickaxe = NULL;\n+static int pickaxe_opts = 0;\n static int silent = 0;\n \n static void show_unmerge(const char *path)\n@@ -54,6 +55,8 @@ int main(int argc, const char **argv)\n \t\t\tdiff_setup_opt |= DIFF_SETUP_REVERSE;\n \t\telse if (!strcmp(argv[1], \"-S\"))\n \t\t\tpickaxe = argv[1] + 2;\n+\t\telse if (!strcmp(argv[1], \"--pickaxe-all\"))\n+\t\t\tpickaxe_opts = DIFF_PICKAXE_ALL;\n \t\telse if (!strncmp(argv[1], \"-M\", 2)) {\n \t\t\tdiff_score_opt = diff_scoreopt_parse(argv[1]);\n \t\t\tdetect_rename = DIFF_DETECT_RENAME;\n@@ -116,7 +119,7 @@ int main(int argc, const char **argv)\n \tif (detect_rename)\n \t\tdiffcore_rename(detect_rename, diff_score_opt);\n \tif (pickaxe)\n-\t\tdiffcore_pickaxe(pickaxe);\n+\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n \tif (1 < argc)\n \t\tdiffcore_pathspec(argv + 1);\n \tdiff_flush(diff_output_format, 1);\ndiff --git a/diff-helper.c b/diff-helper.c\n--- a/diff-helper.c\n+++ b/diff-helper.c\n@@ -6,6 +6,7 @@\n #include \"diff.h\"\n \n static const char *pickaxe = NULL;\n+static int pickaxe_opts = 0;\n static int line_termination = '\\n';\n static int inter_name_termination = '\\t';\n \n@@ -23,6 +24,8 @@ int main(int ac, const char **av) {\n \t\telse if (av[1][1] == 'S') {\n \t\t\tpickaxe = av[1] + 2;\n \t\t}\n+\t\telse if (!strcmp(av[1], \"--pickaxe-all\"))\n+\t\t\tpickaxe_opts = DIFF_PICKAXE_ALL;\n \t\telse\n \t\t\tusage(diff_helper_usage);\n \t\tac--; av++;\n@@ -127,14 +130,14 @@ int main(int ac, const char **av) {\n \t\t\tcontinue;\n \t\t}\n \t\tif (pickaxe)\n-\t\t\tdiffcore_pickaxe(pickaxe);\n+\t\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n \t\tif (1 < ac)\n \t\t\tdiffcore_pathspec(av + 1);\n \t\tdiff_flush(DIFF_FORMAT_PATCH, 0);\n \t\tprintf(\"%s\\n\", sb.buf);\n \t}\n \tif (pickaxe)\n-\t\tdiffcore_pickaxe(pickaxe);\n+\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n \tif (1 < ac)\n \t\tdiffcore_pathspec(av + 1);\n \tdiff_flush(DIFF_FORMAT_PATCH, 0);\ndiff --git a/diff-tree.c b/diff-tree.c\n--- a/diff-tree.c\n+++ b/diff-tree.c\n@@ -13,6 +13,7 @@ static int detect_rename = 0;\n static int diff_setup_opt = 0;\n static int diff_score_opt = 0;\n static const char *pickaxe = NULL;\n+static int pickaxe_opts = 0;\n static const char *header = NULL;\n static const char *header_prefix = \"\";\n \n@@ -263,7 +264,7 @@ static int call_diff_flush(void)\n \tif (detect_rename)\n \t\tdiffcore_rename(detect_rename, diff_score_opt);\n \tif (pickaxe)\n-\t\tdiffcore_pickaxe(pickaxe);\n+\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n \tif (diff_queue_is_empty()) {\n \t\tdiff_flush(DIFF_FORMAT_NO_OUTPUT, 0);\n \t\treturn 0;\n@@ -509,6 +510,10 @@ int main(int argc, const char **argv)\n \t\t\tpickaxe = arg + 2;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(arg, \"--pickaxe-all\")) {\n+\t\t\tpickaxe_opts = DIFF_PICKAXE_ALL;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strncmp(arg, \"-M\", 2)) {\n \t\t\tdetect_rename = DIFF_DETECT_RENAME;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(arg);\ndiff --git a/diff.h b/diff.h\n--- a/diff.h\n+++ b/diff.h\n@@ -36,7 +36,9 @@ extern void diff_setup(int flags);\n \n extern void diffcore_rename(int rename_copy, int minimum_score);\n \n-extern void diffcore_pickaxe(const char *needle);\n+#define DIFF_PICKAXE_ALL\t1\n+extern void diffcore_pickaxe(const char *needle, int opts);\n+\n extern void diffcore_pathspec(const char **pathspec);\n \n extern int diff_queue_is_empty(void);\ndiff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c\n--- a/diffcore-pickaxe.c\n+++ b/diffcore-pickaxe.c\n@@ -21,36 +21,73 @@ static int contains(struct diff_filespec\n \treturn 0;\n }\n \n-void diffcore_pickaxe(const char *needle)\n+void diffcore_pickaxe(const char *needle, int opts)\n {\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \tunsigned long len = strlen(needle);\n-\tint i;\n+\tint i, has_changes;\n \tstruct diff_queue_struct outq;\n \toutq.queue = NULL;\n \toutq.nr = outq.alloc = 0;\n \n-\tfor (i = 0; i < q->nr; i++) {\n-\t\tstruct diff_filepair *p = q->queue[i];\n-\t\tint onum = outq.nr;\n-\t\tif (!DIFF_FILE_VALID(p->one)) {\n-\t\t\tif (!DIFF_FILE_VALID(p->two))\n-\t\t\t\tcontinue; /* ignore nonsense */\n-\t\t\t/* created */\n-\t\t\tif (contains(p->two, needle, len))\n-\t\t\t\tdiff_q(&outq, p);\n+\tif (opts & DIFF_PICKAXE_ALL) {\n+\t\t/* Showing the whole changeset if needle exists */\n+\t\tfor (i = has_changes = 0; !has_changes && i < q->nr; i++) {\n+\t\t\tstruct diff_filepair *p = q->queue[i];\n+\t\t\tif (!DIFF_FILE_VALID(p->one)) {\n+\t\t\t\tif (!DIFF_FILE_VALID(p->two))\n+\t\t\t\t\tcontinue; /* ignore unmerged */\n+\t\t\t\t/* created */\n+\t\t\t\tif (contains(p->two, needle, len))\n+\t\t\t\t\thas_changes++;\n+\t\t\t}\n+\t\t\telse if (!DIFF_FILE_VALID(p->two)) {\n+\t\t\t\tif (contains(p->one, needle, len))\n+\t\t\t\t\thas_changes++;\n+\t\t\t}\n+\t\t\telse if (!diff_unmodified_pair(p) &&\n+\t\t\t\t contains(p->one, needle, len) !=\n+\t\t\t\t contains(p->two, needle, len))\n+\t\t\t\thas_changes++;\n \t\t}\n-\t\telse if (!DIFF_FILE_VALID(p->two)) {\n-\t\t\tif (contains(p->one, needle, len))\n+\t\tif (has_changes)\n+\t\t\treturn; /* not munge the queue */\n+\n+\t\t/* otherwise we will clear the whole queue\n+\t\t * by copying the empty outq at the end of this\n+\t\t * function, but first clear the current entries\n+\t\t * in the queue.\n+\t\t */\n+\t\tfor (i = 0; i < q->nr; i++)\n+\t\t\tdiff_free_filepair(q->queue[i]);\n+\t}\n+\telse \n+\t\t/* Showing only the filepairs that has the needle */\n+\t\tfor (i = 0; i < q->nr; i++) {\n+\t\t\tstruct diff_filepair *p = q->queue[i];\n+\t\t\thas_changes = 0;\n+\t\t\tif (!DIFF_FILE_VALID(p->one)) {\n+\t\t\t\tif (!DIFF_FILE_VALID(p->two))\n+\t\t\t\t\t; /* ignore unmerged */\n+\t\t\t\t/* created */\n+\t\t\t\telse if (contains(p->two, needle, len))\n+\t\t\t\t\thas_changes = 1;\n+\t\t\t}\n+\t\t\telse if (!DIFF_FILE_VALID(p->two)) {\n+\t\t\t\tif (contains(p->one, needle, len))\n+\t\t\t\t\thas_changes = 1;\n+\t\t\t}\n+\t\t\telse if (!diff_unmodified_pair(p) &&\n+\t\t\t\t contains(p->one, needle, len) !=\n+\t\t\t\t contains(p->two, needle, len))\n+\t\t\t\thas_changes = 1;\n+\n+\t\t\tif (has_changes)\n \t\t\t\tdiff_q(&outq, p);\n+\t\t\telse\n+\t\t\t\tdiff_free_filepair(p);\n \t\t}\n-\t\telse if (!diff_unmodified_pair(p) &&\n-\t\t\t contains(p->one, needle, len) !=\n-\t\t\t contains(p->two, needle, len))\n-\t\t\tdiff_q(&outq, p);\n-\t\tif (onum == outq.nr)\n-\t\t\tdiff_free_filepair(p);\n-\t}\n+\n \tfree(q->queue);\n \t*q = outq;\n \treturn;\n------------------------------------------------\n\n"},{"id":"4108","messageId":"7vsm082s90.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vk6lk5lxt.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 10/12] Fix the way diffcore-rename records unremoved source.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T22:55:55Z","receivedAt":"2005-05-27T22:55:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Earier version of diffcore-rename used to keep unmodified\nfilepair in its output so that the last stage of the processing\nthat tells renames from copies can make all of rename/copy to\ncopies.  However this had a bad interaction with other diffcore\nfilters that wanted to run after diffcore-rename, in that such\nunmodified filepair must be retained for proper distinction\nbetween renames and copies to happen.\n\nThis patch fixes the problem by changing the way diffcore-rename\nrecords the information needed to distinguish \"all are copies\"\ncase and \"the last one is a rename\" case.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff.c              |   76 ++++++++++++++++++--------------------\ndiffcore-rename.c   |   63 ++++++++++++-------------------\ndiffcore.h          |    7 ++-\nt/t4007-rename-3.sh |  103 ++++++++++++++++++++++++++++++++++++++++++++++++++++\n4 files changed, 169 insertions(+), 80 deletions(-)\nnew file (100644): t/t4007-rename-3.sh\n\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -518,6 +518,7 @@ struct diff_filepair *diff_queue(struct \n \tdp->one = one;\n \tdp->two = two;\n \tdp->score = 0;\n+\tdp->source_stays = 0;\n \tdiff_q(queue, dp);\n \treturn dp;\n }\n@@ -675,8 +676,8 @@ void diff_debug_filepair(const struct di\n {\n \tdiff_debug_filespec(p->one, i, \"one\");\n \tdiff_debug_filespec(p->two, i, \"two\");\n-\tfprintf(stderr, \"score %d, status %c\\n\",\n-\t\tp->score, p->status ? : '?');\n+\tfprintf(stderr, \"score %d, status %c source_stays %d\\n\",\n+\t\tp->score, p->status ? : '?', p->source_stays);\n }\n \n void diff_debug_queue(const char *msg, struct diff_queue_struct *q)\n@@ -698,8 +699,6 @@ static void diff_resolve_rename_copy(voi\n \tstruct diff_filepair *p, *pp;\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \n-\t/* This should not depend on the ordering of things. */\n-\n \tdiff_debug_queue(\"resolve-rename-copy\", q);\n \n \tfor (i = 0; i < q->nr; i++) {\n@@ -707,23 +706,28 @@ static void diff_resolve_rename_copy(voi\n \t\tp->status = 0; /* undecided */\n \t\tif (DIFF_PAIR_UNMERGED(p))\n \t\t\tp->status = 'U';\n-\t\telse if (!DIFF_FILE_VALID((p)->one))\n+\t\telse if (!DIFF_FILE_VALID(p->one))\n \t\t\tp->status = 'N';\n-\t\telse if (!DIFF_FILE_VALID((p)->two)) {\n-\t\t\t/* Deletion record should be omitted if there\n-\t\t\t * are rename/copy entries using this one as\n-\t\t\t * the source.  Then we can say one of them\n-\t\t\t * is a rename and the rest are copies.\n+\t\telse if (!DIFF_FILE_VALID(p->two)) {\n+\t\t\t/* Deleted entry may have been picked up by\n+\t\t\t * another rename-copy entry.  So we scan the\n+\t\t\t * queue and if we find one that uses us as the\n+\t\t\t * source we do not say delete for this entry.\n \t\t\t */\n-\t\t\tp->status = 'D';\n \t\t\tfor (j = 0; j < q->nr; j++) {\n \t\t\t\tpp = q->queue[j];\n-\t\t\t\tif (!strcmp(pp->one->path, p->one->path) &&\n-\t\t\t\t    strcmp(pp->one->path, pp->two->path)) {\n+\t\t\t\tif (!strcmp(p->one->path, pp->one->path) &&\n+\t\t\t\t    pp->score) {\n+\t\t\t\t\t/* rename/copy are always valid\n+\t\t\t\t\t * so we do not say DIFF_FILE_VALID()\n+\t\t\t\t\t * on pp->one and pp->two.\n+\t\t\t\t\t */\n \t\t\t\t\tp->status = 'X';\n \t\t\t\t\tbreak;\n \t\t\t\t}\n \t\t\t}\n+\t\t\tif (!p->status)\n+\t\t\t\tp->status = 'D';\n \t\t}\n \t\telse if (DIFF_PAIR_TYPE_CHANGED(p))\n \t\t\tp->status = 'T';\n@@ -732,33 +736,24 @@ static void diff_resolve_rename_copy(voi\n \t\t * whose both sides are valid and of the same type, i.e.\n \t\t * either in-place edit or rename/copy edit.\n \t\t */\n-\t\telse if (strcmp(p->one->path, p->two->path)) {\n-\t\t\t/* See if there is somebody else anywhere that\n-\t\t\t * will keep the path (either modified or\n-\t\t\t * unmodified).  If so, we have to be a copy,\n-\t\t\t * not a rename.  In addition, if there is\n-\t\t\t * some other rename or copy that comes later\n-\t\t\t * than us that uses the same source, we\n-\t\t\t * have to be a copy, not a rename.\n+\t\telse if (p->score) {\n+\t\t\tif (p->source_stays) {\n+\t\t\t\tp->status = 'C';\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\t/* See if there is some other filepair that\n+\t\t\t * copies from the same source as us.  If so\n+\t\t\t * we are a copy.  Otherwise we are a rename.\n \t\t\t */\n-\t\t\tfor (j = 0; j < q->nr; j++) {\n+\t\t\tfor (j = i + 1; j < q->nr; j++) {\n \t\t\t\tpp = q->queue[j];\n \t\t\t\tif (strcmp(pp->one->path, p->one->path))\n-\t\t\t\t\tcontinue;\n-\t\t\t\tif (!strcmp(pp->one->path, pp->two->path)) {\n-\t\t\t\t\tif (DIFF_FILE_VALID(pp->two)) {\n-\t\t\t\t\t\t/* non-delete */\n-\t\t\t\t\t\tp->status = 'C';\n-\t\t\t\t\t\tbreak;\n-\t\t\t\t\t}\n-\t\t\t\t\tcontinue;\n-\t\t\t\t}\n-\t\t\t\t/* pp is a rename/copy ... */\n-\t\t\t\tif (i < j) {\n-\t\t\t\t\t/* ... and comes later than us */\n-\t\t\t\t\tp->status = 'C';\n-\t\t\t\t\tbreak;\n-\t\t\t\t}\n+\t\t\t\t\tcontinue; /* not us */\n+\t\t\t\tif (!pp->score)\n+\t\t\t\t\tcontinue; /* not a rename/copy */\n+\t\t\t\t/* pp is a rename/copy from the same source */\n+\t\t\t\tp->status = 'C';\n+\t\t\t\tbreak;\n \t\t\t}\n \t\t\tif (!p->status)\n \t\t\t\tp->status = 'R';\n@@ -767,8 +762,11 @@ static void diff_resolve_rename_copy(voi\n \t\t\t p->one->mode != p->two->mode)\n \t\t\tp->status = 'M';\n \t\telse\n-\t\t\t/* this is a \"no-change\" entry */\n-\t\t\tp->status = 'X';\n+\t\t\t/* this is a \"no-change\" entry.\n+\t\t\t * should not happen anymore.\n+\t\t\t * p->status = 'X';\n+\t\t\t */\n+\t\t\tdie(\"internal error in diffcore: unmodified entry remains\");\n \t}\n \tdiff_debug_queue(\"resolve-rename-copy done\", q);\n }\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -52,14 +52,15 @@ static struct diff_rename_dst *locate_re\n \treturn &(rename_dst[first]);\n }\n \n+/* Table of rename/copy src files */\n static struct diff_rename_src {\n \tstruct diff_filespec *one;\n-\tunsigned src_used : 1;\n+\tunsigned src_stays : 1;\n } *rename_src;\n static int rename_src_nr, rename_src_alloc;\n \n-static struct diff_rename_src *locate_rename_src(struct diff_filespec *one,\n-\t\t\t\t\t\t int insert_ok)\n+static struct diff_rename_src *register_rename_src(struct diff_filespec *one,\n+\t\t\t\t\t\t   int src_stays)\n {\n \tint first, last;\n \n@@ -77,9 +78,7 @@ static struct diff_rename_src *locate_re\n \t\t}\n \t\tfirst = next+1;\n \t}\n-\t/* not found */\n-\tif (!insert_ok)\n-\t\treturn NULL;\n+\n \t/* insert to make it at \"first\" */\n \tif (rename_src_alloc <= rename_src_nr) {\n \t\trename_src_alloc = alloc_nr(rename_src_alloc);\n@@ -91,7 +90,7 @@ static struct diff_rename_src *locate_re\n \t\tmemmove(rename_src + first + 1, rename_src + first,\n \t\t\t(rename_src_nr - first - 1) * sizeof(*rename_src));\n \trename_src[first].one = one;\n-\trename_src[first].src_used = 0;\n+\trename_src[first].src_stays = src_stays;\n \treturn &(rename_src[first]);\n }\n \n@@ -199,15 +198,14 @@ static void record_rename_pair(struct di\n \tfill_filespec(two, dst->sha1, dst->mode);\n \n \tdp = diff_queue(renq, one, two);\n-\tdp->score = score;\n-\n-\trename_src[src_index].src_used = 1;\n+\tdp->score = score ? : 1; /* make sure it is at least 1 */\n+\tdp->source_stays = rename_src[src_index].src_stays;\n \trename_dst[dst_index].pair = dp;\n }\n \n /*\n  * We sort the rename similarity matrix with the score, in descending\n- * order (more similar first).\n+ * order (the most similar first).\n  */\n static int score_compare(const void *a_, const void *b_)\n {\n@@ -254,9 +252,9 @@ void diffcore_rename(int detect_rename, \n \t\t\telse\n \t\t\t\tlocate_rename_dst(p->two, 1);\n \t\telse if (!DIFF_FILE_VALID(p->two))\n-\t\t\tlocate_rename_src(p->one, 1);\n-\t\telse if (1 < detect_rename) /* find copy, too */\n-\t\t\tlocate_rename_src(p->one, 1);\n+\t\t\tregister_rename_src(p->one, 0);\n+\t\telse if (detect_rename == DIFF_DETECT_COPY)\n+\t\t\tregister_rename_src(p->one, 1);\n \t}\n \tif (rename_dst_nr == 0)\n \t\tgoto cleanup; /* nothing to do */\n@@ -280,7 +278,7 @@ void diffcore_rename(int detect_rename, \n \t * doing the delta matrix altogether.\n \t */\n \tif (renq.nr == rename_dst_nr)\n-\t\tgoto flush_rest;\n+\t\tgoto cleanup;\n \n \tnum_create = (rename_dst_nr - renq.nr);\n \tnum_src = rename_src_nr;\n@@ -307,37 +305,30 @@ void diffcore_rename(int detect_rename, \n \t\tif (dst->pair)\n \t\t\tcontinue; /* already done, either exact or fuzzy. */\n \t\tif (mx[i].score < minimum_score)\n-\t\t\tbreak; /* there is not any more diffs applicable. */\n+\t\t\tbreak; /* there is no more usable pair. */\n \t\trecord_rename_pair(&renq, mx[i].dst, mx[i].src, mx[i].score);\n \t}\n \tfree(mx);\n \tdiff_debug_queue(\"done detecting fuzzy\", &renq);\n \n- flush_rest:\n+ cleanup:\n \t/* At this point, we have found some renames and copies and they\n \t * are kept in renq.  The original list is still in *q.\n-\t *\n-\t * Scan the original list and move them into the outq; we will sort\n-\t * outq and swap it into the queue supplied to pass that to\n-\t * downstream, so we assign the sort keys in this loop.\n-\t *\n-\t * See comments at the top of record_rename_pair for numbers used\n-\t * to assign rename_rank.\n \t */\n \toutq.queue = NULL;\n \toutq.nr = outq.alloc = 0;\n \tfor (i = 0; i < q->nr; i++) {\n \t\tstruct diff_filepair *p = q->queue[i];\n-\t\tstruct diff_rename_src *src = locate_rename_src(p->one, 0);\n \t\tstruct diff_rename_dst *dst = locate_rename_dst(p->two, 0);\n \t\tstruct diff_filepair *pair_to_free = NULL;\n \n \t\tif (dst) {\n \t\t\t/* creation */\n \t\t\tif (dst->pair) {\n-\t\t\t\t/* renq has rename/copy already to produce\n-\t\t\t\t * this file, so we do not emit the creation\n-\t\t\t\t * record in the output.\n+\t\t\t\t/* renq has rename/copy to produce\n+\t\t\t\t * this file already, so we do not\n+\t\t\t\t * emit the creation record in the\n+\t\t\t\t * output.\n \t\t\t\t */\n \t\t\t\tdiff_q(&outq, dst->pair);\n \t\t\t\tpair_to_free = p;\n@@ -349,17 +340,12 @@ void diffcore_rename(int detect_rename, \n \t\t\t\tdiff_q(&outq, p);\n \t\t}\n \t\telse if (!diff_unmodified_pair(p))\n-\t\t\t/* all the other cases need to be recorded as is */\n+\t\t\t/* all the usual ones need to be kept */\n \t\t\tdiff_q(&outq, p);\n-\t\telse {\n-\t\t\t/* unmodified pair needs to be recorded only if\n-\t\t\t * it is used as the source of rename/copy\n-\t\t\t */\n-\t\t\tif (src && src->src_used)\n-\t\t\t\tdiff_q(&outq, p);\n-\t\t\telse\n-\t\t\t\tpair_to_free = p;\n-\t\t}\n+\t\telse\n+\t\t\t/* no need to keep unmodified pairs */\n+\t\t\tpair_to_free = p;\n+\n \t\tif (pair_to_free)\n \t\t\tdiff_free_filepair(pair_to_free);\n \t}\n@@ -370,7 +356,6 @@ void diffcore_rename(int detect_rename, \n \t*q = outq;\n \tdiff_debug_queue(\"done collapsing\", q);\n \n- cleanup:\n \tfree(rename_dst);\n \trename_dst = NULL;\n \trename_dst_nr = rename_dst_alloc = 0;\ndiff --git a/diffcore.h b/diffcore.h\n--- a/diffcore.h\n+++ b/diffcore.h\n@@ -39,8 +39,11 @@ extern void diff_free_filespec_data(stru\n struct diff_filepair {\n \tstruct diff_filespec *one;\n \tstruct diff_filespec *two;\n-\tint score; /* only valid when one and two are different paths */\n-\tint status; /* M C R N D U (see Documentation/diff-format.txt) */\n+\tunsigned short int score; /* only valid when one and two are\n+\t\t\t\t   * different paths\n+\t\t\t\t   */\n+\tchar source_stays; /* all of R/C are copies */\n+\tchar status; /* M C R N D U (see Documentation/diff-format.txt) */\n };\n #define DIFF_PAIR_UNMERGED(p) \\\n \t(!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))\ndiff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh\nnew file mode 100644\n--- /dev/null\n+++ b/t/t4007-rename-3.sh\n@@ -0,0 +1,103 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2005 Junio C Hamano\n+#\n+\n+test_description='Rename interaction with pathspec.\n+\n+'\n+. ./test-lib.sh\n+\n+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'\n+_x40=\"$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40\"\n+sanitize_diff_raw='s/ \\('\"$_x40\"'\\) \\1 \\([CR]\\)[0-9]*\t/ \\1 \\1 \\2#\t/'\n+compare_diff_raw () {\n+    # When heuristics are improved, the score numbers would change.\n+    # Ignore them while comparing.\n+    # Also we do not check SHA1 hash generation in this test, which\n+    # is a job for t0000-basic.sh\n+\n+    sed -e \"$sanitize_diff_raw\" <\"$1\" >.tmp-1\n+    sed -e \"$sanitize_diff_raw\" <\"$2\" >.tmp-2\n+    diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2\n+}\n+\n+test_expect_success \\\n+    'prepare reference tree' \\\n+    'mkdir path0 path1 &&\n+     cp ../../COPYING path0/COPYING &&\n+     git-update-cache --add path0/COPYING &&\n+    tree=$(git-write-tree) &&\n+    echo $tree'\n+\n+test_expect_success \\\n+    'prepare work tree' \\\n+    'cp path0/COPYING path1/COPYING &&\n+     git-update-cache --add --remove path0/COPYING path1/COPYING'\n+\n+# In the tree, there is only path0/COPYING.  In the cache, path0 and\n+# path1 both have COPYING and the latter is a copy of path0/COPYING.\n+# Comparing the full tree with cache should tell us so.\n+\n+git-diff-cache -C $tree >current\n+\n+cat >expected <<\\EOF\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100\tpath0/COPYING\tpath1/COPYING\n+EOF\n+\n+test_expect_success \\\n+    'validate the result' \\\n+    'compare_diff_raw current expected'\n+\n+# In the tree, there is only path0/COPYING.  In the cache, path0 and\n+# path1 both have COPYING and the latter is a copy of path0/COPYING.\n+# When we omit output from path0 it should still be able to tell us\n+# that path1/COPYING is result from a copy from path0/COPYING, not\n+# rename, which would imply path0/COPYING is now gone.\n+\n+git-diff-cache -C $tree path1 >current\n+\n+cat >expected <<\\EOF\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100\tpath0/COPYING\tpath1/COPYING\n+EOF\n+\n+test_expect_success \\\n+    'validate the result' \\\n+    'compare_diff_raw current expected'\n+\n+test_expect_success \\\n+    'tweak work tree' \\\n+    'rm -f path0/COPYING &&\n+     git-update-cache --remove path0/COPYING'\n+\n+# In the tree, there is only path0/COPYING.  In the cache, path0 does\n+# not have COPYING anymore and path1 has COPYING which is a copy of\n+# path0/COPYING.  Showing the full tree with cache should tell us about\n+# the rename.\n+\n+git-diff-cache -C $tree >current\n+\n+cat >expected <<\\EOF\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100\tpath0/COPYING\tpath1/COPYING\n+EOF\n+\n+test_expect_success \\\n+    'validate the result' \\\n+    'compare_diff_raw current expected'\n+\n+# In the tree, there is only path0/COPYING.  In the cache, path0 does\n+# not have COPYING anymore and path1 has COPYING which is a copy of\n+# path0/COPYING.  Even if we restrict the output to path1, it still\n+# should show us the rename.\n+\n+git-diff-cache -C $tree path1 >current\n+\n+cat >expected <<\\EOF\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100\tpath0/COPYING\tpath1/COPYING\n+EOF\n+\n+test_expect_success \\\n+    'validate the result' \\\n+    'compare_diff_raw current expected'\n+\n+test_done\n------------------------------------------------\n\n"},{"id":"4109","messageId":"7vmzqg2s8e.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vk6lk5lxt.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 11/12] Move pathspec to the beginning of the diffcore chain.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T22:56:17Z","receivedAt":"2005-05-27T22:56:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This changes the way how pathspec is used in the three diff-*\nbrothers.  Earlier, they tried to grab as much information from\nthe original input and used pathspec to limit the output.  This\nversion uses pathspec upfront to narrow the world diffcore\noperates in, so \"git-diff-* <arguments> some-directory\" does not\nlook at things outside the specified subtree when finding\nrename/copy or running pickaxe.\n\nSince diff-tree already takes this view and does not feed\nanything outside the specified directotires to begin with, this\npatch does not have to touch that command.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff-cache.c        |    4 ++--\ndiff-files.c        |    4 ++--\ndiff-helper.c       |    8 ++++----\nt/t4007-rename-3.sh |   22 +++++++++++-----------\n4 files changed, 19 insertions(+), 19 deletions(-)\n\ndiff --git a/diff-cache.c b/diff-cache.c\n--- a/diff-cache.c\n+++ b/diff-cache.c\n@@ -240,12 +240,12 @@ int main(int argc, const char **argv)\n \t\tdie(\"unable to read tree object %s\", tree_name);\n \n \tret = diff_cache(active_cache, active_nr);\n+\tif (pathspec)\n+\t\tdiffcore_pathspec(pathspec);\n \tif (detect_rename)\n \t\tdiffcore_rename(detect_rename, diff_score_opt);\n \tif (pickaxe)\n \t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n-\tif (pathspec)\n-\t\tdiffcore_pathspec(pathspec);\n \tdiff_flush(diff_output_format, 1);\n \treturn ret;\n }\ndiff --git a/diff-files.c b/diff-files.c\n--- a/diff-files.c\n+++ b/diff-files.c\n@@ -116,12 +116,12 @@ int main(int argc, const char **argv)\n \t\tshow_modified(oldmode, mode, ce->sha1, null_sha1,\n \t\t\t      ce->name);\n \t}\n+\tif (1 < argc)\n+\t\tdiffcore_pathspec(argv + 1);\n \tif (detect_rename)\n \t\tdiffcore_rename(detect_rename, diff_score_opt);\n \tif (pickaxe)\n \t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n-\tif (1 < argc)\n-\t\tdiffcore_pathspec(argv + 1);\n \tdiff_flush(diff_output_format, 1);\n \treturn 0;\n }\ndiff --git a/diff-helper.c b/diff-helper.c\n--- a/diff-helper.c\n+++ b/diff-helper.c\n@@ -129,17 +129,17 @@ int main(int ac, const char **av) {\n \t\t\t\t\t  new_path);\n \t\t\tcontinue;\n \t\t}\n-\t\tif (pickaxe)\n-\t\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n \t\tif (1 < ac)\n \t\t\tdiffcore_pathspec(av + 1);\n+\t\tif (pickaxe)\n+\t\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n \t\tdiff_flush(DIFF_FORMAT_PATCH, 0);\n \t\tprintf(\"%s\\n\", sb.buf);\n \t}\n-\tif (pickaxe)\n-\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n \tif (1 < ac)\n \t\tdiffcore_pathspec(av + 1);\n+\tif (pickaxe)\n+\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n \tdiff_flush(DIFF_FORMAT_PATCH, 0);\n \treturn 0;\n }\ndiff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh\n--- a/t/t4007-rename-3.sh\n+++ b/t/t4007-rename-3.sh\n@@ -46,23 +46,23 @@ cat >expected <<\\EOF\n EOF\n \n test_expect_success \\\n-    'validate the result' \\\n+    'validate the result (#1)' \\\n     'compare_diff_raw current expected'\n \n # In the tree, there is only path0/COPYING.  In the cache, path0 and\n # path1 both have COPYING and the latter is a copy of path0/COPYING.\n-# When we omit output from path0 it should still be able to tell us\n-# that path1/COPYING is result from a copy from path0/COPYING, not\n-# rename, which would imply path0/COPYING is now gone.\n+# However when we say we care only about path1, we should just see\n+# path1/COPYING suddenly appearing from nowhere, not detected as\n+# a copy from path0/COPYING.\n \n git-diff-cache -C $tree path1 >current\n \n cat >expected <<\\EOF\n-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100\tpath0/COPYING\tpath1/COPYING\n+:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 N\tpath1/COPYING\n EOF\n \n test_expect_success \\\n-    'validate the result' \\\n+    'validate the result (#2)' \\\n     'compare_diff_raw current expected'\n \n test_expect_success \\\n@@ -82,22 +82,22 @@ cat >expected <<\\EOF\n EOF\n \n test_expect_success \\\n-    'validate the result' \\\n+    'validate the result (#3)' \\\n     'compare_diff_raw current expected'\n \n # In the tree, there is only path0/COPYING.  In the cache, path0 does\n # not have COPYING anymore and path1 has COPYING which is a copy of\n-# path0/COPYING.  Even if we restrict the output to path1, it still\n-# should show us the rename.\n+# path0/COPYING.  When we say we care only about path1, we should just\n+# see path1/COPYING appearing from nowhere.\n \n git-diff-cache -C $tree path1 >current\n \n cat >expected <<\\EOF\n-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100\tpath0/COPYING\tpath1/COPYING\n+:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 N\tpath1/COPYING\n EOF\n \n test_expect_success \\\n-    'validate the result' \\\n+    'validate the result (#4)' \\\n     'compare_diff_raw current expected'\n \n test_done\n------------------------------------------------\n\n"},{"id":"4110","messageId":"7vhdgo2s7t.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vk6lk5lxt.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 12/12] Optimize diff-tree -[CM] --stdin","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T22:56:38Z","receivedAt":"2005-05-27T22:56:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This attempts to optimize \"diff-tree -[CM] --stdin\", which\ncompares successible tree pairs.  This optimization does not\nmake much sense for other commands in the diff-* brothers.\n\nWhen reading from --stdin and using rename/copy detection, the\npatch makes diff-tree to read the current index file first.\nThis is done to reuse the optimization used by diff-cache in the\nnon-cached case.  Similarity estimator can avoid expanding a\nblob if the index says what is in the work tree has an exact\ncopy of that blob already expanded.\n\nAnother optimization the patch makes is to check only file sizes\nfirst to terminate similarity estimation early.  In order for\nthis to work, it needs a way to tell the size of the blob\nwithout expanding it.  Since an obvious way of doing it, which\nis to keep all the blobs previously used in the memory, is too\ncostly, it does so by keeping the filesize for each object it\nhas already seen in memory.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff-tree.c        |    3 +\ndiff.c             |   83 +++++++++++++++++++++++++++++++++++++++++++++++++++--\ndiff.h             |    2 +\ndiffcore-pickaxe.c |    2 -\ndiffcore-rename.c  |   19 ++++++++----\ndiffcore.h         |    2 -\n6 files changed, 102 insertions(+), 9 deletions(-)\n\ndiff --git a/diff-tree.c b/diff-tree.c\n--- a/diff-tree.c\n+++ b/diff-tree.c\n@@ -578,6 +578,9 @@ int main(int argc, const char **argv)\n \tif (!read_stdin)\n \t\treturn 0;\n \n+\tif (detect_rename)\n+\t\tdiff_setup_opt |= (DIFF_SETUP_USE_SIZE_CACHE |\n+\t\t\t\t   DIFF_SETUP_USE_CACHE);\n \twhile (fgets(line, sizeof(line), stdin))\n \t\tdiff_tree_stdin(line);\n \ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -12,6 +12,7 @@ static const char *diff_opts = \"-pu\";\n static unsigned char null_sha1[20] = { 0, };\n \n static int reverse_diff;\n+static int use_size_cache;\n \n static const char *external_diff(void)\n {\n@@ -222,12 +223,60 @@ static int work_tree_matches(const char \n \treturn 1;\n }\n \n+static struct sha1_size_cache {\n+\tunsigned char sha1[20];\n+\tunsigned long size;\n+} **sha1_size_cache;\n+static int sha1_size_cache_nr, sha1_size_cache_alloc;\n+\n+static struct sha1_size_cache *locate_size_cache(unsigned char *sha1,\n+\t\t\t\t\t\t unsigned long size)\n+{\n+\tint first, last;\n+\tstruct sha1_size_cache *e;\n+\n+\tfirst = 0;\n+\tlast = sha1_size_cache_nr;\n+\twhile (last > first) {\n+\t\tint next = (last + first) >> 1;\n+\t\te = sha1_size_cache[next];\n+\t\tint cmp = memcmp(e->sha1, sha1, 20);\n+\t\tif (!cmp)\n+\t\t\treturn e;\n+\t\tif (cmp < 0) {\n+\t\t\tlast = next;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tfirst = next+1;\n+\t}\n+\t/* not found */\n+\tif (size == UINT_MAX)\n+\t\treturn NULL;\n+\t/* insert to make it at \"first\" */\n+\tif (sha1_size_cache_alloc <= sha1_size_cache_nr) {\n+\t\tsha1_size_cache_alloc = alloc_nr(sha1_size_cache_alloc);\n+\t\tsha1_size_cache = xrealloc(sha1_size_cache,\n+\t\t\t\t\t   sha1_size_cache_alloc *\n+\t\t\t\t\t   sizeof(*sha1_size_cache));\n+\t}\n+\tsha1_size_cache_nr++;\n+\tif (first < sha1_size_cache_nr)\n+\t\tmemmove(sha1_size_cache + first + 1, sha1_size_cache + first,\n+\t\t\t(sha1_size_cache_nr - first - 1) *\n+\t\t\tsizeof(*sha1_size_cache));\n+\te = xmalloc(sizeof(struct sha1_size_cache));\n+\tsha1_size_cache[first] = e;\n+\tmemcpy(e->sha1, sha1, 20);\n+\te->size = size;\n+\treturn e;\n+}\n+\n /*\n  * While doing rename detection and pickaxe operation, we may need to\n  * grab the data for the blob (or file) for our own in-core comparison.\n  * diff_filespec has data and size fields for this purpose.\n  */\n-int diff_populate_filespec(struct diff_filespec *s)\n+int diff_populate_filespec(struct diff_filespec *s, int size_only)\n {\n \tint err = 0;\n \tif (!DIFF_FILE_VALID(s))\n@@ -235,6 +284,9 @@ int diff_populate_filespec(struct diff_f\n \tif (S_ISDIR(s->mode))\n \t\treturn -1;\n \n+\tif (!use_size_cache)\n+\t\tsize_only = 0;\n+\n \tif (s->data)\n \t\treturn err;\n \tif (!s->sha1_valid ||\n@@ -254,6 +306,8 @@ int diff_populate_filespec(struct diff_f\n \t\ts->size = st.st_size;\n \t\tif (!s->size)\n \t\t\tgoto empty;\n+\t\tif (size_only)\n+\t\t\treturn 0;\n \t\tif (S_ISLNK(st.st_mode)) {\n \t\t\tint ret;\n \t\t\ts->data = xmalloc(s->size);\n@@ -273,9 +327,21 @@ int diff_populate_filespec(struct diff_f\n \t\tclose(fd);\n \t}\n \telse {\n+\t\t/* We cannot do size only for SHA1 blobs */\n \t\tchar type[20];\n+\t\tstruct sha1_size_cache *e;\n+\n+\t\tif (size_only) {\n+\t\t\te = locate_size_cache(s->sha1, UINT_MAX);\n+\t\t\tif (e) {\n+\t\t\t\ts->size = e->size;\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t}\n \t\ts->data = read_sha1_file(s->sha1, type, &s->size);\n \t\ts->should_free = 1;\n+\t\tif (s->data && size_only)\n+\t\t\tlocate_size_cache(s->sha1, s->size);\n \t}\n \treturn 0;\n }\n@@ -361,7 +427,7 @@ static void prepare_temp_file(const char\n \t\treturn;\n \t}\n \telse {\n-\t\tif (diff_populate_filespec(one))\n+\t\tif (diff_populate_filespec(one, 0))\n \t\t\tdie(\"cannot read data blob for %s\", one->path);\n \t\tprep_temp_blob(temp, one->data, one->size,\n \t\t\t       one->sha1, one->mode);\n@@ -496,6 +562,19 @@ void diff_setup(int flags)\n {\n \tif (flags & DIFF_SETUP_REVERSE)\n \t\treverse_diff = 1;\n+\tif (flags & DIFF_SETUP_USE_CACHE) {\n+\t\tif (!active_cache)\n+\t\t\t/* read-cache does not die even when it fails\n+\t\t\t * so it is safe for us to do this here.  Also\n+\t\t\t * it does not smudge active_cache or active_nr\n+\t\t\t * when it fails, so we do not have to worry about\n+\t\t\t * cleaning it up oufselves either.\n+\t\t\t */\n+\t\t\tread_cache();\n+\t}\n+\tif (flags & DIFF_SETUP_USE_SIZE_CACHE)\n+\t\tuse_size_cache = 1;\n+\t\n }\n \n struct diff_queue_struct diff_queued_diff;\ndiff --git a/diff.h b/diff.h\n--- a/diff.h\n+++ b/diff.h\n@@ -29,6 +29,8 @@ extern void diff_unmerge(const char *pat\n extern int diff_scoreopt_parse(const char *opt);\n \n #define DIFF_SETUP_REVERSE      \t1\n+#define DIFF_SETUP_USE_CACHE\t\t2\n+#define DIFF_SETUP_USE_SIZE_CACHE\t4\n extern void diff_setup(int flags);\n \n #define DIFF_DETECT_RENAME\t1\ndiff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c\n--- a/diffcore-pickaxe.c\n+++ b/diffcore-pickaxe.c\n@@ -11,7 +11,7 @@ static int contains(struct diff_filespec\n {\n \tunsigned long offset, sz;\n \tconst char *data;\n-\tif (diff_populate_filespec(one))\n+\tif (diff_populate_filespec(one, 0))\n \t\treturn 0;\n \tsz = one->size;\n \tdata = one->data;\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -99,8 +99,11 @@ static int is_exact_match(struct diff_fi\n \tif (src->sha1_valid && dst->sha1_valid &&\n \t    !memcmp(src->sha1, dst->sha1, 20))\n \t\treturn 1;\n-\tif (diff_populate_filespec(src) || diff_populate_filespec(dst))\n-\t\t/* this is an error but will be caught downstream */\n+\tif (diff_populate_filespec(src, 1) || diff_populate_filespec(dst, 1))\n+\t\treturn 0;\n+\tif (src->size != dst->size)\n+\t\treturn 0;\n+\tif (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))\n \t\treturn 0;\n \tif (src->size == dst->size &&\n \t    !memcmp(src->data, dst->data, src->size))\n@@ -125,9 +128,11 @@ static int estimate_similarity(struct di\n \t * dst, and then some edit has been applied to dst.\n \t *\n \t * Compare them and return how similar they are, representing\n-\t * the score as an integer between 0 and 10000, except\n-\t * where they match exactly it is considered better than anything\n-\t * else.\n+\t * the score as an integer between 0 and MAX_SCORE.\n+\t *\n+\t * When there is an exact match, it is considered a better\n+\t * match than anything else; the destination does not even\n+\t * call into this function in that case.\n \t */\n \tvoid *delta;\n \tunsigned long delta_size, base_size;\n@@ -147,6 +152,7 @@ static int estimate_similarity(struct di\n \t/* We would not consider edits that change the file size so\n \t * drastically.  delta_size must be smaller than\n \t * (MAX_SCORE-minimum_score)/MAX_SCORE * min(src->size, dst->size).\n+\t *\n \t * Note that base_size == 0 case is handled here already\n \t * and the final score computation below would not have a\n \t * divide-by-zero issue.\n@@ -154,6 +160,9 @@ static int estimate_similarity(struct di\n \tif (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)\n \t\treturn 0;\n \n+\tif (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))\n+\t\treturn 0; /* error but caught downstream */\n+\n \tdelta = diff_delta(src->data, src->size,\n \t\t\t   dst->data, dst->size,\n \t\t\t   &delta_size);\ndiff --git a/diffcore.h b/diffcore.h\n--- a/diffcore.h\n+++ b/diffcore.h\n@@ -33,7 +33,7 @@ extern struct diff_filespec *alloc_files\n extern void fill_filespec(struct diff_filespec *, const unsigned char *,\n \t\t\t  unsigned short);\n \n-extern int diff_populate_filespec(struct diff_filespec *);\n+extern int diff_populate_filespec(struct diff_filespec *, int);\n extern void diff_free_filespec_data(struct diff_filespec *);\n \n struct diff_filepair {\n------------------------------------------------\n\n"},{"id":"4112","messageId":"7v3bs82rwh.fsf@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vk6lk5lxt.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 00/12] Diff updates","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-27T23:03:26Z","receivedAt":"2005-05-27T23:03:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"I've said \"I am done with diff\" twice on this list already.\nAside from unevitable bug reports ;-) I think this time I am\ndone.  At least the major ones I wanted to do.\n\nExcept one thing.  What do you think about the current behaviour\nof \"diff-cache -p (uncached\") in a work tree which was freshly\nchecked-out, unmodified but you \"touch\"ed some files to make\nthem stat-dirty?\n\nI think ancient diff-cache did not report those files, but with\nthe new \"diff --git\" headers it will show the \"diff --git\"\nheader mentioning those files followed by no content nor mode\nchanges.  Admittedly this matches the diff-raw output behaviour\nmore closely, but I find it a bit distracting.  Do you care\nabout cleaning this up?\n\n\n"},{"id":"4130","messageId":"7vis13wth4.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7v3bs82rwh.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] Do not show empty diff in diff-cache uncached.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-28T10:11:19Z","receivedAt":"2005-05-28T10:11:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Pre- \"diff --git\" built-in diff did not add any extended header\non its own, so it did not show anything for unmodified but\nstat-dirty file from diff-cache command without --cached flag.\n\nRecent diff-cache produces \"diff --git\" header internally before\ncalling the \"diff\" command, which results in an empty diff for\nsuch a file, cluttering the output.  This patch fixes this.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff.c            |   26 ++++++++++++++++++++++++++\ndiffcore-rename.c |   17 -----------------\ndiffcore.h        |    2 ++\n3 files changed, 28 insertions(+), 17 deletions(-)\n\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -684,6 +684,23 @@ int diff_unmodified_pair(struct diff_fil\n \treturn 0;\n }\n \n+int is_exact_match(struct diff_filespec *src, struct diff_filespec *dst)\n+{\n+\tif (src->sha1_valid && dst->sha1_valid &&\n+\t    !memcmp(src->sha1, dst->sha1, 20))\n+\t\treturn 1;\n+\tif (diff_populate_filespec(src, 1) || diff_populate_filespec(dst, 1))\n+\t\treturn 0;\n+\tif (src->size != dst->size)\n+\t\treturn 0;\n+\tif (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))\n+\t\treturn 0;\n+\tif (src->size == dst->size &&\n+\t    !memcmp(src->data, dst->data, src->size))\n+\t\treturn 1;\n+\treturn 0;\n+}\n+\n static void diff_flush_patch(struct diff_filepair *p)\n {\n \tconst char *name, *other;\n@@ -692,6 +709,15 @@ static void diff_flush_patch(struct diff\n \tif (diff_unmodified_pair(p))\n \t\treturn;\n \n+\t/* When diff-raw would have said \"look at the filesystem\", we\n+\t * need to see if the two are the same and if so not to emit\n+\t * anything at all.  Avoid is_exact_match() comparison when it\n+\t * does not matter.\n+\t */\n+\tif ((DIFF_FILE_VALID(p->two) && !p->two->sha1_valid) &&\n+\t    is_exact_match(p->one, p->two))\n+\t\treturn;\n+\n \tname = p->one->path;\n \tother = (strcmp(name, p->two->path) ? p->two->path : NULL);\n \tif ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -94,23 +94,6 @@ static struct diff_rename_src *register_\n \treturn &(rename_src[first]);\n }\n \n-static int is_exact_match(struct diff_filespec *src, struct diff_filespec *dst)\n-{\n-\tif (src->sha1_valid && dst->sha1_valid &&\n-\t    !memcmp(src->sha1, dst->sha1, 20))\n-\t\treturn 1;\n-\tif (diff_populate_filespec(src, 1) || diff_populate_filespec(dst, 1))\n-\t\treturn 0;\n-\tif (src->size != dst->size)\n-\t\treturn 0;\n-\tif (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))\n-\t\treturn 0;\n-\tif (src->size == dst->size &&\n-\t    !memcmp(src->data, dst->data, src->size))\n-\t\treturn 1;\n-\treturn 0;\n-}\n-\n struct diff_score {\n \tint src; /* index in rename_src */\n \tint dst; /* index in rename_dst */\ndiff --git a/diffcore.h b/diffcore.h\n--- a/diffcore.h\n+++ b/diffcore.h\n@@ -29,6 +29,8 @@ struct diff_filespec {\n \tunsigned should_munmap : 1; /* data should be munmap()'ed */\n };\n \n+extern int is_exact_match(struct diff_filespec *, struct diff_filespec *);\n+\n extern struct diff_filespec *alloc_filespec(const char *);\n extern void fill_filespec(struct diff_filespec *, const unsigned char *,\n \t\t\t  unsigned short);\n------------------------------------------------\n\n"},{"id":"4145","messageId":"7vr7frrw8x.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vis13wth4.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH] Diff: two fixes.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-28T19:22:38Z","receivedAt":"2005-05-28T19:22:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The count-delta routine sometimes overcounted the copied source\nmaterial which resulted in unsigned int wraparound.\n\nThe latest diff-cache (uncached) fix to eliminate empty diffs\nfrom the output revealed that is_exact_match() was not careful\nenough, which resulted in a sanity check routine triggering when\na file is added to an index.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ncount-delta.c |    3 +++\ndiff.c        |    3 +++\n2 files changed, 6 insertions(+)\n\ndiff --git a/count-delta.c b/count-delta.c\n--- a/count-delta.c\n+++ b/count-delta.c\n@@ -88,5 +88,8 @@ unsigned long count_delta(void *delta_bu\n \t/* delete size is what was _not_ copied from source.\n \t * edit size is that and literal additions.\n \t */\n+\tif (src_size + added_literal < copied_from_source)\n+\t\t/* we ended up overcounting and underflowed */\n+\t\treturn 0;\n \treturn (src_size - copied_from_source) + added_literal;\n }\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -689,6 +689,9 @@ int is_exact_match(struct diff_filespec \n \tif (src->sha1_valid && dst->sha1_valid &&\n \t    !memcmp(src->sha1, dst->sha1, 20))\n \t\treturn 1;\n+\t/* if either is invalid they cannot match */\n+\tif (!DIFF_FILE_VALID(src) || !DIFF_FILE_VALID(dst))\n+\t\treturn 0;\n \tif (diff_populate_filespec(src, 1) || diff_populate_filespec(dst, 1))\n \t\treturn 0;\n \tif (src->size != dst->size)\n------------------------------------------------\n\n"},{"id":"4153","messageId":"7v3bs6slwl.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vr7frrw8x.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH] diff-helper: fix R/C score parsing under -z flag.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-29T04:20:42Z","receivedAt":"2005-05-29T04:20:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The score number that follows R/C status letter was parsed but\nthe parse pointer was not updated, causing the entire line to\nbecome unrecognized.  This patch fixes this problem.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff-helper.c |   17 ++++++++---------\n1 files changed, 8 insertions(+), 9 deletions(-)\n\ndiff --git a/diff-helper.c b/diff-helper.c\n--- a/diff-helper.c\n+++ b/diff-helper.c\n@@ -80,17 +80,16 @@ int main(int ac, const char **av) {\n \t\t\tif (!strchr(\"MCRNDU\", status))\n \t\t\t\tbreak;\n \t\t\ttwo_paths = score = 0;\n-\t\t\tif (status == 'R' || status == 'C') {\n+\t\t\tif (status == 'R' || status == 'C')\n \t\t\t\ttwo_paths = 1;\n-\t\t\t\tsscanf(cp, \"%d\", &score);\n-\t\t\t\tif (line_termination) {\n-\t\t\t\t\tcp = strchr(cp,\n-\t\t\t\t\t\t    inter_name_termination);\n-\t\t\t\t\tif (!cp)\n-\t\t\t\t\t\tbreak;\n-\t\t\t\t}\n-\t\t\t}\n \n+\t\t\t/* pick up score if exists */\n+\t\t\tif (sscanf(cp, \"%d\", &score) != 1)\n+\t\t\t\tscore = 0;\n+\t\t\tcp = strchr(cp,\n+\t\t\t\t    inter_name_termination);\n+\t\t\tif (!cp)\n+\t\t\t\tbreak;\n \t\t\tif (*cp++ != inter_name_termination)\n \t\t\t\tbreak;\n \n------------------------------------------------\n\n"},{"id":"4155","messageId":"7voeaur4fu.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7v3bs6slwl.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH] diff-cache: diff-patch (-p) format fixes.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-29T05:23:17Z","receivedAt":"2005-05-29T05:23:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This fixes two more bugs.\n\n - When diff-cache is run with -R and without --cached, a\n   stat-dirty but otherwise unmodified file still produced an\n   empty \"diff --git\" header (earlier fix was only for the case\n   without -R).\n\n - When diff-cache is run without --cached, an unmodifed file\n   that has different mode bits from the tree incorrectly picked\n   up the mode bits from the filesystem, when it should have\n   used what was recorded in the tree object.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff.c |   26 ++++++++++++++++++++++----\n1 files changed, 22 insertions(+), 4 deletions(-)\n\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -183,6 +183,9 @@ void fill_filespec(struct diff_filespec \n  * Given a name and sha1 pair, if the dircache tells us the file in\n  * the work tree has that object contents, return true, so that\n  * prepare_temp_file() does not have to inflate and extract.\n+ *\n+ * NOTE: this function does not use the mode bits, so diff_filespec\n+ * users must be careful about mode handling!\n  */\n static int work_tree_matches(const char *name, const unsigned char *sha1)\n {\n@@ -394,6 +397,10 @@ static void prepare_temp_file(const char\n \n \tif (!one->sha1_valid ||\n \t    work_tree_matches(name, one->sha1)) {\n+\t\t/* NOTE: we only say the matching file has the same\n+\t\t * contents.  It may have a different mode, in which\n+\t\t * case we should not pick it up from the filesystem!\n+\t\t */\n \t\tstruct stat st;\n \t\tif (lstat(name, &st) < 0) {\n \t\t\tif (errno == ENOENT)\n@@ -421,8 +428,15 @@ static void prepare_temp_file(const char\n \t\t\t\tstrcpy(temp->hex, sha1_to_hex(null_sha1));\n \t\t\telse\n \t\t\t\tstrcpy(temp->hex, sha1_to_hex(one->sha1));\n-\t\t\tsprintf(temp->mode, \"%06o\",\n-\t\t\t\tS_IFREG |ce_permissions(st.st_mode));\n+\t\t\t/* even though we borrow the contents from the\n+\t\t\t * work tree, we want our mode if we are not told\n+\t\t\t * to look at the filesystem.\n+\t\t\t */\n+\t\t\tif (one->sha1_valid)\n+\t\t\t\tsprintf(temp->mode, \"%06o\", one->mode);\n+\t\t\telse\n+\t\t\t\tsprintf(temp->mode, \"%06o\",\n+\t\t\t\t\tS_IFREG | ce_permissions(st.st_mode));\n \t\t}\n \t\treturn;\n \t}\n@@ -716,9 +730,13 @@ static void diff_flush_patch(struct diff\n \t * need to see if the two are the same and if so not to emit\n \t * anything at all.  Avoid is_exact_match() comparison when it\n \t * does not matter.\n+\t * Notice that \"look at the filesystem\" can happen on p->one\n+\t * if we are operating under reverse-diff option.\n \t */\n-\tif ((DIFF_FILE_VALID(p->two) && !p->two->sha1_valid) &&\n-\t    is_exact_match(p->one, p->two))\n+\tif ( ((DIFF_FILE_VALID(p->one) && !p->one->sha1_valid) ||\n+\t      (DIFF_FILE_VALID(p->two) && !p->two->sha1_valid)) &&\n+\t     is_exact_match(p->one, p->two) &&\n+\t     (p->one->mode == p->two->mode) )\n \t\treturn;\n \n \tname = p->one->path;\n------------------------------------------------\n\n"},{"id":"4160","messageId":"7vy89ypfcc.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7voeaur4fu.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH] diff: code clean-up.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-29T09:10:43Z","receivedAt":"2005-05-29T09:10:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":" - The previous fix for the \"diff-cache -p mode bits problem\"\n   had an unnecessary conditional statement not to use the mode\n   that came from the calling program when we get 0{40} SHA1.\n   This was totally unnecessary, since the caller gives the\n   valid mode bits even when calling us with 0{40} SHA1.\n   This mistake did not break anything, since the mode bits we\n   got ourselves were the same one taken from the filesystem,\n   but it was doing unnecessary work.  This has been fixed.\n\n - DIFF_PAIR_RENAME() macro is introduced to distinguish a filepair\n   that is a rename/copy (the definition of which is src and dst\n   are different paths, of course).  This removes the hack used\n   in the record_rename_pair() to always put some non-zero value\n   in the score field.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff.c            |   17 +++++++----------\ndiffcore-rename.c |    2 +-\ndiffcore.h        |    6 +++---\n3 files changed, 11 insertions(+), 14 deletions(-)\n\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -429,14 +429,11 @@ static void prepare_temp_file(const char\n \t\t\telse\n \t\t\t\tstrcpy(temp->hex, sha1_to_hex(one->sha1));\n \t\t\t/* even though we borrow the contents from the\n-\t\t\t * work tree, we want our mode if we are not told\n-\t\t\t * to look at the filesystem.\n+\t\t\t * work tree, we always want our mode.  mode is\n+\t\t\t * trustworthy even when !(one->sha1_valid), as\n+\t\t\t * long as DIFF_FILE_VALID(one).\n \t\t\t */\n-\t\t\tif (one->sha1_valid)\n-\t\t\t\tsprintf(temp->mode, \"%06o\", one->mode);\n-\t\t\telse\n-\t\t\t\tsprintf(temp->mode, \"%06o\",\n-\t\t\t\t\tS_IFREG | ce_permissions(st.st_mode));\n+\t\t\tsprintf(temp->mode, \"%06o\", one->mode);\n \t\t}\n \t\treturn;\n \t}\n@@ -843,7 +840,7 @@ static void diff_resolve_rename_copy(voi\n \t\t\tfor (j = 0; j < q->nr; j++) {\n \t\t\t\tpp = q->queue[j];\n \t\t\t\tif (!strcmp(p->one->path, pp->one->path) &&\n-\t\t\t\t    pp->score) {\n+\t\t\t\t    DIFF_PAIR_RENAME(pp)) {\n \t\t\t\t\t/* rename/copy are always valid\n \t\t\t\t\t * so we do not say DIFF_FILE_VALID()\n \t\t\t\t\t * on pp->one and pp->two.\n@@ -862,7 +859,7 @@ static void diff_resolve_rename_copy(voi\n \t\t * whose both sides are valid and of the same type, i.e.\n \t\t * either in-place edit or rename/copy edit.\n \t\t */\n-\t\telse if (p->score) {\n+\t\telse if (DIFF_PAIR_RENAME(p)) {\n \t\t\tif (p->source_stays) {\n \t\t\t\tp->status = 'C';\n \t\t\t\tcontinue;\n@@ -875,7 +872,7 @@ static void diff_resolve_rename_copy(voi\n \t\t\t\tpp = q->queue[j];\n \t\t\t\tif (strcmp(pp->one->path, p->one->path))\n \t\t\t\t\tcontinue; /* not us */\n-\t\t\t\tif (!pp->score)\n+\t\t\t\tif (!DIFF_PAIR_RENAME(pp))\n \t\t\t\t\tcontinue; /* not a rename/copy */\n \t\t\t\t/* pp is a rename/copy from the same source */\n \t\t\t\tp->status = 'C';\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -190,7 +190,7 @@ static void record_rename_pair(struct di\n \tfill_filespec(two, dst->sha1, dst->mode);\n \n \tdp = diff_queue(renq, one, two);\n-\tdp->score = score ? : 1; /* make sure it is at least 1 */\n+\tdp->score = score;\n \tdp->source_stays = rename_src[src_index].src_stays;\n \trename_dst[dst_index].pair = dp;\n }\ndiff --git a/diffcore.h b/diffcore.h\n--- a/diffcore.h\n+++ b/diffcore.h\n@@ -42,15 +42,15 @@ extern void diff_free_filespec_data(stru\n struct diff_filepair {\n \tstruct diff_filespec *one;\n \tstruct diff_filespec *two;\n-\tunsigned short int score; /* only valid when one and two are\n-\t\t\t\t   * different paths\n-\t\t\t\t   */\n+\tunsigned short int score;\n \tchar source_stays; /* all of R/C are copies */\n \tchar status; /* M C R N D U (see Documentation/diff-format.txt) */\n };\n #define DIFF_PAIR_UNMERGED(p) \\\n \t(!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))\n \n+#define DIFF_PAIR_RENAME(p) (strcmp((p)->one->path, (p)->two->path))\n+\n #define DIFF_PAIR_TYPE_CHANGED(p) \\\n \t((S_IFMT & (p)->one->mode) != (S_IFMT & (p)->two->mode))\n \n------------------------------------------------\n\n"},{"id":"4185","messageId":"Pine.LNX.4.58.0505291151250.10545@ppc970.osdl.org","threadId":"726","inReplyTo":"7vis13wth4.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Do not show empty diff in diff-cache uncached.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-05-29T18:53:56Z","receivedAt":"2005-05-29T18:53:56Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 28 May 2005, Junio C Hamano wrote:\n>\n> Pre- \"diff --git\" built-in diff did not add any extended header\n> on its own, so it did not show anything for unmodified but\n> stat-dirty file from diff-cache command without --cached flag.\n> \n> Recent diff-cache produces \"diff --git\" header internally before\n> calling the \"diff\" command, which results in an empty diff for\n> such a file, cluttering the output.  This patch fixes this.\n\nI'm not sure I like this.\n\nI actually _expect_ that \"git-diff-files\" will show files that don't match \nthe index, even if they happen to have the exact content that the index \npoints to. It's how I know whether the index is up-to-date or not.\n\nThe exact same thing is trye of git-diff-cache. If something isn't \nup-to-date in the cache, you should show it, since certain operations \ndepend on the cache being updated.\n\n\t\tLinus\n"},{"id":"4193","messageId":"7vis11n69l.fsf@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"Pine.LNX.4.58.0505291151250.10545@ppc970.osdl.org","subject":"Re: [PATCH] Do not show empty diff in diff-cache uncached.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-29T20:09:42Z","receivedAt":"2005-05-29T20:09:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":">>>>> \"LT\" == Linus Torvalds <torvalds@osdl.org> writes:\n\nLT> I'm not sure I like this.\n\nLT> I actually _expect_ that \"git-diff-files\" will show files that don't match \nLT> the index, even if they happen to have the exact content that the index \nLT> points to. It's how I know whether the index is up-to-date or not.\n\nLT> The exact same thing is trye of git-diff-cache. If something isn't \nLT> up-to-date in the cache, you should show it, since certain operations \nLT> depend on the cache being updated.\n\nLet me make sure that we are on the same page.  I am only\ntalking about '-p' (diff-patch) case in this patch.  The code\nshould continue to show the 0{40} SHA1 on the right hand side in\ndiff-raw output.  Do you still want to see the header in that\ncase to match the diff-raw exactly?\n\n\n"},{"id":"4199","messageId":"7v3bs5k8d1.fsf@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vis11n69l.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Do not show empty diff in diff-cache uncached.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-29T21:52:42Z","receivedAt":"2005-05-29T21:52:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":">>>>> \"JCH\" == Junio C Hamano <junkio@cox.net> writes:\n\nJCH> Let me make sure that we are on the same page.\n\nLinus,\n\n    now I've examined what are and what aren't merged in your\ntree, I think I know the answer to that question.  The set you\npicked look sensible to me.\n\nI'm taking a look at the resulting tree to see if there are\nfixes and cleanups that I still think should be merged.  I'll\nfeed them afresh to you later, if there are any, after rebasing\nthe patch to the tip of your tree.\n\nPlease disregard the patches you have already discarded so far;\nthis request-to-discard includes -O and -B enhancements.\n\n"},{"id":"4211","messageId":"7vmzqdiore.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7v3bs5k8d1.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 0/3] Leftover bits after 12-series","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-29T23:41:25Z","receivedAt":"2005-05-29T23:41:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":">>>>> \"JCH\" == Junio C Hamano <junkio@cox.net> writes:\n\nJCH> I'm taking a look at the resulting tree to see if there are\nJCH> fixes and cleanups that I still think should be merged.  I'll\nJCH> feed them afresh to you later, if there are any, after rebasing\nJCH> the patch to the tip of your tree.\n\nI'll be submitting a set of cleanup and bugfix patches.  The\nfirst one is a real bugfix.  The latter two are cleanups.\n\n    [PATCH 1/3] diff-helper: Fix R/C score parsing under -z flag.\n    [PATCH 2/3] diff: consolidate various calls into diffcore.\n    [PATCH 3/3] diff: code clean-up.\n\n\n"},{"id":"4216","messageId":"7vacmdh9kc.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vmzqdiore.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 1/3] diff-helper: Fix R/C score parsing under -z flag.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-29T23:54:59Z","receivedAt":"2005-05-29T23:54:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The score number that follow R/C status were parsed but the\nparse pointer was not updated, causing the entire line to become\nunrecognized.  This patch fixes this problem.\n\nThere was a test missing to catch this breakage, which this\ncommit adds as t4009-diff-rename-4.sh.  The diff-raw tests used\nin related t4005-diff-rename-2.sh (the same test without -z) and\nt4007-rename-3.sh were stricter than necessarily, despite that\nthe comment for the tests said otherwise.  This patch also\ncorrects them.\n\nThe documentation is updated to say that the status can\noptionally be followed by a number called \"score\"; it does not\nhave to stay similarity index forever and there is no reason to\nlimit it only to C and R.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\nDocumentation/diff-format.txt |    2 \ndiff-helper.c                 |   17 +--\nt/t4005-diff-rename-2.sh      |   20 ++--\nt/t4007-rename-3.sh           |    2 \nt/t4009-diff-rename-4.sh      |  196 ++++++++++++++++++++++++++++++++++++++++++\n5 files changed, 219 insertions(+), 18 deletions(-)\nnew file (100755): t/t4009-diff-rename-4.sh\n\ndiff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt\n--- a/Documentation/diff-format.txt\n+++ b/Documentation/diff-format.txt\n@@ -36,7 +36,7 @@ That is, from the left to the right:\n   (6) sha1 for \"src\"; 0{40} if creation or unmerged.\n   (7) a space.\n   (8) sha1 for \"dst\"; 0{40} if creation, unmerged or \"look at work tree\".\n-  (9) status, followed by similarlity index number only for C and R.\n+  (9) status, followed by optional \"score\" number.\n  (10) a tab or a NUL when '-z' option is used.\n  (11) path for \"src\"\n  (12) a tab or a NUL when '-z' option is used; only exists for C or R.\ndiff --git a/diff-helper.c b/diff-helper.c\n--- a/diff-helper.c\n+++ b/diff-helper.c\n@@ -80,17 +80,16 @@ int main(int ac, const char **av) {\n \t\t\tif (!strchr(\"MCRNDU\", status))\n \t\t\t\tbreak;\n \t\t\ttwo_paths = score = 0;\n-\t\t\tif (status == 'R' || status == 'C') {\n+\t\t\tif (status == 'R' || status == 'C')\n \t\t\t\ttwo_paths = 1;\n-\t\t\t\tsscanf(cp, \"%d\", &score);\n-\t\t\t\tif (line_termination) {\n-\t\t\t\t\tcp = strchr(cp,\n-\t\t\t\t\t\t    inter_name_termination);\n-\t\t\t\t\tif (!cp)\n-\t\t\t\t\t\tbreak;\n-\t\t\t\t}\n-\t\t\t}\n \n+\t\t\t/* pick up score if exists */\n+\t\t\tif (sscanf(cp, \"%d\", &score) != 1)\n+\t\t\t\tscore = 0;\n+\t\t\tcp = strchr(cp,\n+\t\t\t\t    inter_name_termination);\n+\t\t\tif (!cp)\n+\t\t\t\tbreak;\n \t\t\tif (*cp++ != inter_name_termination)\n \t\t\t\tbreak;\n \ndiff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh\n--- a/t/t4005-diff-rename-2.sh\n+++ b/t/t4005-diff-rename-2.sh\n@@ -8,11 +8,17 @@ test_description='Same rename detection \n '\n . ./test-lib.sh\n \n+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'\n+_x40=\"$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40\"\n+sanitize_diff_raw='s/ '\"$_x40\"' '\"$_x40\"' \\([A-Z]\\)[0-9]*\t/ X X \\1#\t/'\n compare_diff_raw () {\n     # When heuristics are improved, the score numbers would change.\n     # Ignore them while comparing.\n-    sed -e 's/ \\([CR]\\)[0-9]*\t/\\1#/' <\"$1\" >.tmp-1\n-    sed -e 's/ \\([CR]\\)[0-9]*\t/\\1#/' <\"$2\" >.tmp-2\n+    # Also we do not check SHA1 hash generation in this test, which\n+    # is a job for t0000-basic.sh\n+\n+    sed -e \"$sanitize_diff_raw\" <\"$1\" >.tmp-1\n+    sed -e \"$sanitize_diff_raw\" <\"$2\" >.tmp-2\n     diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2\n }\n \n@@ -109,11 +115,6 @@ test_expect_success \\\n     'validate output from rename/copy detection (#2)' \\\n     'compare_diff_raw current expected'\n \n-test_expect_success \\\n-    'prepare work tree once again' \\\n-    'cat ../../COPYING >COPYING &&\n-     git-update-cache --add --remove COPYING COPYING.1'\n-\n # make sure diff-helper can grok it.\n mv expected diff-raw\n GIT_DIFF_OPTS=--unified=0 git-diff-helper <diff-raw >current\n@@ -151,6 +152,11 @@ test_expect_success \\\n # anything about rezrov nor COPYING, since the revised again diff-raw\n # nows how to say Copy.\n \n+test_expect_success \\\n+    'prepare work tree once again' \\\n+    'cat ../../COPYING >COPYING &&\n+     git-update-cache --add --remove COPYING COPYING.1'\n+\n git-diff-cache -C $tree >current\n cat >expected <<\\EOF\n :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234\tCOPYING\tCOPYING.1\ndiff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh\n--- a/t/t4007-rename-3.sh\n+++ b/t/t4007-rename-3.sh\n@@ -10,7 +10,7 @@ test_description='Rename interaction wit\n \n _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'\n _x40=\"$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40\"\n-sanitize_diff_raw='s/ \\('\"$_x40\"'\\) \\1 \\([CR]\\)[0-9]*\t/ \\1 \\1 \\2#\t/'\n+sanitize_diff_raw='s/ '\"$_x40\"' '\"$_x40\"' \\([A-Z]\\)[0-9]*\t/ X X \\1#\t/'\n compare_diff_raw () {\n     # When heuristics are improved, the score numbers would change.\n     # Ignore them while comparing.\ndiff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh\nnew file mode 100755\n--- /dev/null\n+++ b/t/t4009-diff-rename-4.sh\n@@ -0,0 +1,196 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2005 Junio C Hamano\n+#\n+\n+test_description='Same rename detection as t4003 but testing diff-raw -z.\n+\n+'\n+. ./test-lib.sh\n+\n+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'\n+_x40=\"$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40\"\n+sanitize_diff_raw='/^:/s/ '\"$_x40\"' '\"$_x40\"' \\([A-Z]\\)[0-9]*$/ X X \\1#/'\n+compare_diff_raw () {\n+    # When heuristics are improved, the score numbers would change.\n+    # Ignore them while comparing.\n+    # Also we do not check SHA1 hash generation in this test, which\n+    # is a job for t0000-basic.sh\n+\n+    tr '\\0' '\\012' <\"$1\" | sed -e \"$sanitize_diff_raw\" >.tmp-1\n+    tr '\\0' '\\012' <\"$2\" | sed -e \"$sanitize_diff_raw\" >.tmp-2\n+    diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2\n+}\n+\n+compare_diff_patch () {\n+    # When heuristics are improved, the score numbers would change.\n+    # Ignore them while comparing.\n+    sed -e '/^similarity index [0-9]*%$/d' <\"$1\" >.tmp-1\n+    sed -e '/^similarity index [0-9]*%$/d' <\"$2\" >.tmp-2\n+    diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2\n+}\n+\n+test_expect_success \\\n+    'prepare reference tree' \\\n+    'cat ../../COPYING >COPYING &&\n+     echo frotz >rezrov &&\n+    git-update-cache --add COPYING rezrov &&\n+    tree=$(git-write-tree) &&\n+    echo $tree'\n+\n+test_expect_success \\\n+    'prepare work tree' \\\n+    'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&\n+    sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&\n+    rm -f COPYING &&\n+    git-update-cache --add --remove COPYING COPYING.?'\n+\n+# tree has COPYING and rezrov.  work tree has COPYING.1 and COPYING.2,\n+# both are slightly edited, and unchanged rezrov.  We say COPYING.1\n+# and COPYING.2 are based on COPYING, and do not say anything about\n+# rezrov.\n+\n+git-diff-cache -z -M $tree >current\n+\n+cat >expected <<\\EOF\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234\n+COPYING\n+COPYING.1\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 R1234\n+COPYING\n+COPYING.2\n+EOF\n+\n+test_expect_success \\\n+    'validate output from rename/copy detection (#1)' \\\n+    'compare_diff_raw current expected'\n+\n+# make sure diff-helper can grok it.\n+mv current diff-raw\n+GIT_DIFF_OPTS=--unified=0 git-diff-helper -z <diff-raw >current\n+cat >expected <<\\EOF\n+diff --git a/COPYING b/COPYING.1\n+copy from COPYING\n+copy to COPYING.1\n+--- a/COPYING\n++++ b/COPYING.1\n+@@ -6 +6 @@\n+- HOWEVER, in order to allow a migration to GPLv3 if that seems like\n++ However, in order to allow a migration to GPLv3 if that seems like\n+diff --git a/COPYING b/COPYING.2\n+rename old COPYING\n+rename new COPYING.2\n+--- a/COPYING\n++++ b/COPYING.2\n+@@ -2 +2 @@\n+- Note that the only valid version of the GPL as far as this project\n++ Note that the only valid version of the G.P.L as far as this project\n+@@ -6 +6 @@\n+- HOWEVER, in order to allow a migration to GPLv3 if that seems like\n++ HOWEVER, in order to allow a migration to G.P.Lv3 if that seems like\n+@@ -12 +12 @@\n+-\tThis file is licensed under the GPL v2, or a later version\n++\tThis file is licensed under the G.P.L v2, or a later version\n+EOF\n+\n+test_expect_success \\\n+    'validate output from diff-helper (#1)' \\\n+    'compare_diff_patch current expected'\n+\n+################################################################\n+\n+test_expect_success \\\n+    'prepare work tree again' \\\n+    'mv COPYING.2 COPYING &&\n+     git-update-cache --add --remove COPYING COPYING.1 COPYING.2'\n+\n+# tree has COPYING and rezrov.  work tree has COPYING and COPYING.1,\n+# both are slightly edited, and unchanged rezrov.  We say COPYING.1\n+# is based on COPYING and COPYING is still there, and do not say anything\n+# about rezrov.\n+\n+git-diff-cache -z -C $tree >current\n+cat >expected <<\\EOF\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 M\n+COPYING\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234\n+COPYING\n+COPYING.1\n+EOF\n+\n+test_expect_success \\\n+    'validate output from rename/copy detection (#2)' \\\n+    'compare_diff_raw current expected'\n+\n+# make sure diff-helper can grok it.\n+mv current diff-raw\n+GIT_DIFF_OPTS=--unified=0 git-diff-helper -z <diff-raw >current\n+cat >expected <<\\EOF\n+diff --git a/COPYING b/COPYING\n+--- a/COPYING\n++++ b/COPYING\n+@@ -2 +2 @@\n+- Note that the only valid version of the GPL as far as this project\n++ Note that the only valid version of the G.P.L as far as this project\n+@@ -6 +6 @@\n+- HOWEVER, in order to allow a migration to GPLv3 if that seems like\n++ HOWEVER, in order to allow a migration to G.P.Lv3 if that seems like\n+@@ -12 +12 @@\n+-\tThis file is licensed under the GPL v2, or a later version\n++\tThis file is licensed under the G.P.L v2, or a later version\n+diff --git a/COPYING b/COPYING.1\n+copy from COPYING\n+copy to COPYING.1\n+--- a/COPYING\n++++ b/COPYING.1\n+@@ -6 +6 @@\n+- HOWEVER, in order to allow a migration to GPLv3 if that seems like\n++ However, in order to allow a migration to GPLv3 if that seems like\n+EOF\n+\n+test_expect_success \\\n+    'validate output from diff-helper (#2)' \\\n+    'compare_diff_patch current expected'\n+\n+################################################################\n+\n+# tree has COPYING and rezrov.  work tree has the same COPYING and\n+# copy-edited COPYING.1, and unchanged rezrov.  We should not say\n+# anything about rezrov nor COPYING, since the revised again diff-raw\n+# nows how to say Copy.\n+\n+test_expect_success \\\n+    'prepare work tree once again' \\\n+    'cat ../../COPYING >COPYING &&\n+     git-update-cache --add --remove COPYING COPYING.1'\n+\n+git-diff-cache -z -C $tree >current\n+cat >expected <<\\EOF\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234\n+COPYING\n+COPYING.1\n+EOF\n+\n+test_expect_success \\\n+    'validate output from rename/copy detection (#3)' \\\n+    'compare_diff_raw current expected'\n+\n+# make sure diff-helper can grok it.\n+mv current diff-raw\n+GIT_DIFF_OPTS=--unified=0 git-diff-helper -z <diff-raw >current\n+cat >expected <<\\EOF\n+diff --git a/COPYING b/COPYING.1\n+copy from COPYING\n+copy to COPYING.1\n+--- a/COPYING\n++++ b/COPYING.1\n+@@ -6 +6 @@\n+- HOWEVER, in order to allow a migration to GPLv3 if that seems like\n++ However, in order to allow a migration to GPLv3 if that seems like\n+EOF\n+\n+test_expect_success \\\n+    'validate output from diff-helper (#3)' \\\n+    'compare_diff_patch current expected'\n+\n+test_done\n------------------------------------------------\n\n"},{"id":"4217","messageId":"7v64x1h9ia.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vmzqdiore.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 2/3] diff: consolidate various calls into diffcore.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-29T23:56:13Z","receivedAt":"2005-05-29T23:56:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The three diff-* brothers had a sequence of calls into diffcore\nthat were almost identical.  Introduce a new diffcore_std()\nfunction that takes all the necessary arguments to consolidate\nit.  This will make later enhancements and changing the order of\ndiffcore application simpler.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff-cache.c |    9 +++------\ndiff-files.c |    9 +++------\ndiff-tree.c  |    7 +++----\ndiff.c       |   12 ++++++++++++\ndiff.h       |    4 ++++\n5 files changed, 25 insertions(+), 16 deletions(-)\n\ndiff --git a/diff-cache.c b/diff-cache.c\n--- a/diff-cache.c\n+++ b/diff-cache.c\n@@ -240,12 +240,9 @@ int main(int argc, const char **argv)\n \t\tdie(\"unable to read tree object %s\", tree_name);\n \n \tret = diff_cache(active_cache, active_nr);\n-\tif (pathspec)\n-\t\tdiffcore_pathspec(pathspec);\n-\tif (detect_rename)\n-\t\tdiffcore_rename(detect_rename, diff_score_opt);\n-\tif (pickaxe)\n-\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n+\tdiffcore_std(pathspec,\n+\t\t     detect_rename, diff_score_opt,\n+\t\t     pickaxe, pickaxe_opts);\n \tdiff_flush(diff_output_format, 1);\n \treturn ret;\n }\ndiff --git a/diff-files.c b/diff-files.c\n--- a/diff-files.c\n+++ b/diff-files.c\n@@ -116,12 +116,9 @@ int main(int argc, const char **argv)\n \t\tshow_modified(oldmode, mode, ce->sha1, null_sha1,\n \t\t\t      ce->name);\n \t}\n-\tif (1 < argc)\n-\t\tdiffcore_pathspec(argv + 1);\n-\tif (detect_rename)\n-\t\tdiffcore_rename(detect_rename, diff_score_opt);\n-\tif (pickaxe)\n-\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n+\tdiffcore_std(argv + 1,\n+\t\t     detect_rename, diff_score_opt,\n+\t\t     pickaxe, pickaxe_opts);\n \tdiff_flush(diff_output_format, 1);\n \treturn 0;\n }\ndiff --git a/diff-tree.c b/diff-tree.c\n--- a/diff-tree.c\n+++ b/diff-tree.c\n@@ -261,10 +261,9 @@ static void call_diff_setup(void)\n \n static int call_diff_flush(void)\n {\n-\tif (detect_rename)\n-\t\tdiffcore_rename(detect_rename, diff_score_opt);\n-\tif (pickaxe)\n-\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n+\tdiffcore_std(0,\n+\t\t     detect_rename, diff_score_opt,\n+\t\t     pickaxe, pickaxe_opts);\n \tif (diff_queue_is_empty()) {\n \t\tdiff_flush(DIFF_FORMAT_NO_OUTPUT, 0);\n \t\treturn 0;\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -887,6 +887,18 @@ void diff_flush(int diff_output_style, i\n \tq->nr = q->alloc = 0;\n }\n \n+void diffcore_std(const char **paths,\n+\t\t  int detect_rename, int rename_score,\n+\t\t  const char *pickaxe, int pickaxe_opts)\n+{\n+\tif (paths && paths[0])\n+\t\tdiffcore_pathspec(paths);\n+\tif (detect_rename)\n+\t\tdiffcore_rename(detect_rename, rename_score);\n+\tif (pickaxe)\n+\t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n+}\n+\n void diff_addremove(int addremove, unsigned mode,\n \t\t    const unsigned char *sha1,\n \t\t    const char *base, const char *path)\ndiff --git a/diff.h b/diff.h\n--- a/diff.h\n+++ b/diff.h\n@@ -43,6 +43,10 @@ extern void diffcore_pickaxe(const char \n \n extern void diffcore_pathspec(const char **pathspec);\n \n+extern void diffcore_std(const char **paths,\n+\t\t\t int detect_rename, int rename_score,\n+\t\t\t const char *pickaxe, int pickaxe_opts);\n+\n extern int diff_queue_is_empty(void);\n \n #define DIFF_FORMAT_HUMAN\t0\n\n"},{"id":"4219","messageId":"7v4qclh9hb.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vmzqdiore.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 3/3] diff: code clean-up and removal of rename hack.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-29T23:56:48Z","receivedAt":"2005-05-29T23:56:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"A new macro, DIFF_PAIR_RENAME(), is introduced to distinguish a\nfilepair that is a rename/copy (the definition of which is src\nand dst are different paths, of course).  This removes the hack\nused in the record_rename_pair() to always put a non-zero value\nin the score field.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff.c            |    6 +++---\ndiffcore-rename.c |    2 +-\ndiffcore.h        |    6 +++---\n3 files changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -796,7 +796,7 @@ static void diff_resolve_rename_copy(voi\n \t\t\tfor (j = 0; j < q->nr; j++) {\n \t\t\t\tpp = q->queue[j];\n \t\t\t\tif (!strcmp(p->one->path, pp->one->path) &&\n-\t\t\t\t    pp->score) {\n+\t\t\t\t    DIFF_PAIR_RENAME(pp)) {\n \t\t\t\t\t/* rename/copy are always valid\n \t\t\t\t\t * so we do not say DIFF_FILE_VALID()\n \t\t\t\t\t * on pp->one and pp->two.\n@@ -815,7 +815,7 @@ static void diff_resolve_rename_copy(voi\n \t\t * whose both sides are valid and of the same type, i.e.\n \t\t * either in-place edit or rename/copy edit.\n \t\t */\n-\t\telse if (p->score) {\n+\t\telse if (DIFF_PAIR_RENAME(p)) {\n \t\t\tif (p->source_stays) {\n \t\t\t\tp->status = 'C';\n \t\t\t\tcontinue;\n@@ -828,7 +828,7 @@ static void diff_resolve_rename_copy(voi\n \t\t\t\tpp = q->queue[j];\n \t\t\t\tif (strcmp(pp->one->path, p->one->path))\n \t\t\t\t\tcontinue; /* not us */\n-\t\t\t\tif (!pp->score)\n+\t\t\t\tif (!DIFF_PAIR_RENAME(pp))\n \t\t\t\t\tcontinue; /* not a rename/copy */\n \t\t\t\t/* pp is a rename/copy from the same source */\n \t\t\t\tp->status = 'C';\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -207,7 +207,7 @@ static void record_rename_pair(struct di\n \tfill_filespec(two, dst->sha1, dst->mode);\n \n \tdp = diff_queue(renq, one, two);\n-\tdp->score = score ? : 1; /* make sure it is at least 1 */\n+\tdp->score = score;\n \tdp->source_stays = rename_src[src_index].src_stays;\n \trename_dst[dst_index].pair = dp;\n }\ndiff --git a/diffcore.h b/diffcore.h\n--- a/diffcore.h\n+++ b/diffcore.h\n@@ -39,15 +39,15 @@ extern void diff_free_filespec_data(stru\n struct diff_filepair {\n \tstruct diff_filespec *one;\n \tstruct diff_filespec *two;\n-\tunsigned short int score; /* only valid when one and two are\n-\t\t\t\t   * different paths\n-\t\t\t\t   */\n+\tunsigned short int score;\n \tchar source_stays; /* all of R/C are copies */\n \tchar status; /* M C R N D U (see Documentation/diff-format.txt) */\n };\n #define DIFF_PAIR_UNMERGED(p) \\\n \t(!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))\n \n+#define DIFF_PAIR_RENAME(p) (strcmp((p)->one->path, (p)->two->path))\n+\n #define DIFF_PAIR_TYPE_CHANGED(p) \\\n \t((S_IFMT & (p)->one->mode) != (S_IFMT & (p)->two->mode))\n \n\n"},{"id":"4233","messageId":"Pine.LNX.4.58.0505292231580.10545@ppc970.osdl.org","threadId":"726","inReplyTo":"7v3bs5k8d1.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Do not show empty diff in diff-cache uncached.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-05-30T05:34:59Z","receivedAt":"2005-05-30T05:34:59Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 29 May 2005, Junio C Hamano wrote:\n> \n> Please disregard the patches you have already discarded so far;\n> this request-to-discard includes -O and -B enhancements.\n\nI actually like -B, it's just that that patch depended on -O and also came \nwith a separate patch that was the reason I liked -B in the first place..\n\nIO, if we have\n\n\t/* start out with files \"a\" and \"b\" */\n\tmv b c\n\tmv a b\n\tgit-update-cache --add --remove a b c\n\tgit-diff-cache -B HEAD\n\nthen I htink you're 100% right that it should show up as two renames in\na diffs, and \"-B\" would catch it. I think that's a great thing.\n\n\t\tLinus\n"},{"id":"4234","messageId":"7v8y1x9s3y.fsf@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"Pine.LNX.4.58.0505292231580.10545@ppc970.osdl.org","subject":"Re: [PATCH] Do not show empty diff in diff-cache uncached.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-30T05:53:53Z","receivedAt":"2005-05-30T05:53:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":">>>>> \"LT\" == Linus Torvalds <torvalds@osdl.org> writes:\n\nLT> On Sun, 29 May 2005, Junio C Hamano wrote:\n>> \n>> Please disregard the patches you have already discarded so far;\n>> this request-to-discard includes -O and -B enhancements.\n\nLT> I actually like -B, it's just that that patch depended on -O and also came \nLT> with a separate patch that was the reason I liked -B in the first place..\n\nGood.  I actually just finished testing the complete reorder of\nthe patches and about to start the final review before throwing\nthe bundle at you again.  In the new set, -B comes before -O.\n\n"},{"id":"4235","messageId":"7vekbp8ajm.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vmzqdiore.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 0/4]","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-30T06:58:37Z","receivedAt":"2005-05-30T06:58:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus,\n\n\tas promised, I am sending a couple of further cleanup,\nand a pair of new diffcore routines.\n\nI am assuming that you would have applied the three clean-up\npatches I sent after you rejected some of my 12-piece set,\nbefore you use these four.  They come on top of the previous\nthree.  Although I do not think they depend on the previous\nthree, I am letting you know that that was how I tested these\nfour:\n\n    [PATCH 1/4] diff: further cleanup.\n    [PATCH 2/4] diff: fix the culling of unneeded delete record.\n    [PATCH 3/4] Add -B flag to diff-* brothers.\n    [PATCH 4/4] Add -O<orderfile> option to diff-* brothers.\n\nThe third one is the gem of this series.  I think I covered the\nbasics I can think of in the new test script, but there could\nstill be cases that rename/copy detector does something\ninteresting when broken pairs are involved.  Please give it a\ngood beating before you use it for anything important.  This\nbeing diff routine, it obviously cannot corrupt your data,\nthough.\n\nThe fourth one was what both you and Petr expressed reluctance,\nalthough Thomas was supportive.  I admit it is of \"nice to have\"\ncategory not \"great we need to have it inside\" category, but it\nis my favorite.\n\nOh, before I forget, I was wondering if you want me to mark\nbroken pair in any special way, just line I mark matched\nrename/copy pairs.  Something along the lines of:\n\n    diff --git a/foo b/foo\n    dissimilarity index 100%\n    deleted file mode 100644\n    --- a/foo\n    +++ /dev/null\n    @@ ...\n    diff --git a/foo b/foo\n    dissimilarity index 100%\n    new file mode 100644\n    --- /dev/null\n    +++ a/foo\n    @@ ...\n\n\n"},{"id":"4237","messageId":"7vacmd8a4k.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vekbp8ajm.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 1/4] diff: further cleanup.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-30T07:07:39Z","receivedAt":"2005-05-30T07:07:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When preparing data to feed the external diff, we should give\nthe mode we obtained from the caller, even when we are dealing\nwith a file with 0{40} SHA1 (i.e. the caller said \"look at the\nfilesystem\"), since the mode passed by the caller via\ndiff_addremove() or diff_change() is always trustworthy.\n\nThis is _not_ a bugfix --- the existing code stat() on the file\nifself and does the same computation on st.st_mode to compute\nthe mode the same way the caller did to give the original mode.\nWe cannot remove the stat() call from here, but the extra\ncomputation to create the mode value is unnecessary.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n diff.c |    9 +++++++--\n 1 files changed, 7 insertions(+), 2 deletions(-)\n\n9c88ea020142346a56e36cea87e349814675f3f5 (from f96e0e2250f29f5ba2ae06c6b401a83fa1b828b4)\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -421,8 +421,13 @@ static void prepare_temp_file(const char\n \t\t\t\tstrcpy(temp->hex, sha1_to_hex(null_sha1));\n \t\t\telse\n \t\t\t\tstrcpy(temp->hex, sha1_to_hex(one->sha1));\n-\t\t\tsprintf(temp->mode, \"%06o\",\n-\t\t\t\tS_IFREG |ce_permissions(st.st_mode));\n+\t\t\t/* Even though we may sometimes borrow the\n+\t\t\t * contents from the work tree, we always want\n+\t\t\t * one->mode.  mode is trustworthy even when\n+\t\t\t * !(one->sha1_valid), as long as\n+\t\t\t * DIFF_FILE_VALID(one).\n+\t\t\t */\n+\t\t\tsprintf(temp->mode, \"%06o\", one->mode);\n \t\t}\n \t\treturn;\n \t}\n\n"},{"id":"4236","messageId":"7v4qcl8a3s.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vekbp8ajm.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 2/4] diff: fix the culling of unneeded delete record.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-30T07:08:07Z","receivedAt":"2005-05-30T07:08:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The commit 15d061b435a7e3b6bead39df3889f4af78c4b00a\n\n    [PATCH] Fix the way diffcore-rename records unremoved source.\n\nstill leaves unneeded delete records in its output stream by\nmistake, which was covered up by having an extra check to turn\nsuch a delete into a no-op downstream.  Fix the check in the\ndiffcore-rename to simplify the output routine.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n diff.c            |   23 ++---------------------\n diffcore-rename.c |   44 +++++++++++++++++++++++++++++++++-----------\n 2 files changed, 35 insertions(+), 32 deletions(-)\n\n3fde726e95a828e680c95297f185d6d25fcf853a (from 9c88ea020142346a56e36cea87e349814675f3f5)\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -792,27 +792,8 @@ static void diff_resolve_rename_copy(voi\n \t\t\tp->status = 'U';\n \t\telse if (!DIFF_FILE_VALID(p->one))\n \t\t\tp->status = 'N';\n-\t\telse if (!DIFF_FILE_VALID(p->two)) {\n-\t\t\t/* Deleted entry may have been picked up by\n-\t\t\t * another rename-copy entry.  So we scan the\n-\t\t\t * queue and if we find one that uses us as the\n-\t\t\t * source we do not say delete for this entry.\n-\t\t\t */\n-\t\t\tfor (j = 0; j < q->nr; j++) {\n-\t\t\t\tpp = q->queue[j];\n-\t\t\t\tif (!strcmp(p->one->path, pp->one->path) &&\n-\t\t\t\t    DIFF_PAIR_RENAME(pp)) {\n-\t\t\t\t\t/* rename/copy are always valid\n-\t\t\t\t\t * so we do not say DIFF_FILE_VALID()\n-\t\t\t\t\t * on pp->one and pp->two.\n-\t\t\t\t\t */\n-\t\t\t\t\tp->status = 'X';\n-\t\t\t\t\tbreak;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\tif (!p->status)\n-\t\t\t\tp->status = 'D';\n-\t\t}\n+\t\telse if (!DIFF_FILE_VALID(p->two))\n+\t\t\tp->status = 'D';\n \t\telse if (DIFF_PAIR_TYPE_CHANGED(p))\n \t\t\tp->status = 'T';\n \ndiff --git a/diffcore-rename.c b/diffcore-rename.c\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -328,26 +328,48 @@ void diffcore_rename(int detect_rename, \n \toutq.nr = outq.alloc = 0;\n \tfor (i = 0; i < q->nr; i++) {\n \t\tstruct diff_filepair *p = q->queue[i];\n-\t\tstruct diff_rename_dst *dst = locate_rename_dst(p->two, 0);\n \t\tstruct diff_filepair *pair_to_free = NULL;\n \n-\t\tif (dst) {\n-\t\t\t/* creation */\n-\t\t\tif (dst->pair) {\n-\t\t\t\t/* renq has rename/copy to produce\n-\t\t\t\t * this file already, so we do not\n-\t\t\t\t * emit the creation record in the\n-\t\t\t\t * output.\n-\t\t\t\t */\n+\t\tif (!DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {\n+\t\t\t/*\n+\t\t\t * Creation\n+\t\t\t *\n+\t\t\t * We would output this create record if it has\n+\t\t\t * not been turned into a rename/copy already.\n+\t\t\t */\n+\t\t\tstruct diff_rename_dst *dst =\n+\t\t\t\tlocate_rename_dst(p->two, 0);\n+\t\t\tif (dst && dst->pair) {\n \t\t\t\tdiff_q(&outq, dst->pair);\n \t\t\t\tpair_to_free = p;\n \t\t\t}\n \t\t\telse\n-\t\t\t\t/* no matching rename/copy source, so record\n-\t\t\t\t * this as a creation.\n+\t\t\t\t/* no matching rename/copy source, so\n+\t\t\t\t * record this as a creation.\n \t\t\t\t */\n \t\t\t\tdiff_q(&outq, p);\n \t\t}\n+\t\telse if (DIFF_FILE_VALID(p->one) && !DIFF_FILE_VALID(p->two)) {\n+\t\t\t/*\n+\t\t\t * Deletion\n+\t\t\t *\n+\t\t\t * We would output this delete record if renq\n+\t\t\t * does not have a rename/copy to move\n+\t\t\t * p->one->path out.\n+\t\t\t */\n+\t\t\tfor (j = 0; j < renq.nr; j++)\n+\t\t\t\tif (!strcmp(renq.queue[j]->one->path,\n+\t\t\t\t\t    p->one->path))\n+\t\t\t\t\tbreak;\n+\t\t\tif (j < renq.nr)\n+\t\t\t\t/* this path remains */\n+\t\t\t\tpair_to_free = p;\n+\n+\t\t\tif (pair_to_free)\n+\t\t\t\t;\n+\t\t\telse\n+\t\t\t\tdiff_q(&outq, p);\n+\t\t}\n \t\telse if (!diff_unmodified_pair(p))\n \t\t\t/* all the usual ones need to be kept */\n \t\t\tdiff_q(&outq, p);\n\n"},{"id":"4238","messageId":"7vy89x6vii.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vekbp8ajm.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 3/4] Add -B flag to diff-* brothers.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-30T07:08:37Z","receivedAt":"2005-05-30T07:08:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"A new diffcore transformation, diffcore-break.c, is introduced.\n\nWhen the -B flag is given, a patch that represents a complete\nrewrite is broken into a deletion followed by a creation.  This\nmakes it easier to review such a complete rewrite patch.\n\nThe -B flag takes the same syntax as the -M and -C flags to\nspecify the minimum amount of non-source material the resulting\nfile needs to have to be considered a complete rewrite, and\ndefaults to 99% if not specified.\n\nAs the new test t4008-diff-break-rewrite.sh demonstrates, if a\nfile is a complete rewrite, it is broken into a delete/create\npair, which can further be subjected to the usual rename\ndetection if -M or -C is used.  For example, if file0 gets\ncompletely rewritten to make it as if it were rather based on\nfile1 which itself disappeared, the following happens:\n\n    The original change looks like this:\n\n\tfile0     --> file0' (quite different from file0)\n\tfile1     --> /dev/null\n\n    After diffcore-break runs, it would become this:\n\n\tfile0     --> /dev/null\n\t/dev/null --> file0'\n\tfile1     --> /dev/null\n\n    Then diffcore-rename matches them up:\n\n\tfile1     --> file0'\n\nThe internal score values are finer grained now.  Earlier\nmaximum of 10000 has been raised to 60000; there is no user\nvisible changes but there is no reason to waste available bits.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n Documentation/git-diff-cache.txt |    5 \n Documentation/git-diff-files.txt |    5 \n Documentation/git-diff-tree.txt  |    5 \n Makefile                         |    3 \n diff-cache.c                     |   11 +-\n diff-files.c                     |    8 +\n diff-tree.c                      |    8 +\n diff.c                           |   21 +++\n diff.h                           |    5 \n diffcore-break.c                 |  127 +++++++++++++++++++++++\n diffcore-rename.c                |   45 ++++++--\n diffcore.h                       |   12 +-\n t/t4008-diff-break-rewrite.sh    |  207 +++++++++++++++++++++++++++++++++++++++\n 13 files changed, 433 insertions(+), 29 deletions(-)\n\n96c0ba825059e65743bfce718e54413c4583ff35 (from 3fde726e95a828e680c95297f185d6d25fcf853a)\ndiff --git a/Documentation/git-diff-cache.txt b/Documentation/git-diff-cache.txt\n--- a/Documentation/git-diff-cache.txt\n+++ b/Documentation/git-diff-cache.txt\n@@ -9,7 +9,7 @@ git-diff-cache - Compares content and mo\n \n SYNOPSIS\n --------\n-'git-diff-cache' [-p] [-r] [-z] [-m] [-M] [-R] [-C] [-S<string>] [--pickaxe-all] [--cached] <tree-ish> [<path>...]\n+'git-diff-cache' [-p] [-r] [-z] [-m] [-B] [-M] [-R] [-C] [-S<string>] [--pickaxe-all] [--cached] <tree-ish> [<path>...]\n \n DESCRIPTION\n -----------\n@@ -35,6 +35,9 @@ OPTIONS\n -z::\n \t\\0 line termination on output\n \n+-B::\n+\tBreak complete rewrite changes into pairs of delete and create.\n+\n -M::\n \tDetect renames.\n \ndiff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt\n--- a/Documentation/git-diff-files.txt\n+++ b/Documentation/git-diff-files.txt\n@@ -9,7 +9,7 @@ git-diff-files - Compares files in the w\n \n SYNOPSIS\n --------\n-'git-diff-files' [-p] [-q] [-r] [-z] [-M] [-C] [-R] [-S<string>] [--pickaxe-all] [<pattern>...]\n+'git-diff-files' [-p] [-q] [-r] [-z] [-B] [-M] [-C] [-R] [-S<string>] [--pickaxe-all] [<pattern>...]\n \n DESCRIPTION\n -----------\n@@ -29,6 +29,9 @@ OPTIONS\n -R::\n \tOutput diff in reverse.\n \n+-B::\n+\tBreak complete rewrite changes into pairs of delete and create.\n+\n -M::\n \tDetect renames.\n \ndiff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt\n--- a/Documentation/git-diff-tree.txt\n+++ b/Documentation/git-diff-tree.txt\n@@ -9,7 +9,7 @@ git-diff-tree - Compares the content and\n \n SYNOPSIS\n --------\n-'git-diff-tree' [-p] [-r] [-z] [--stdin] [-M] [-R] [-C] [-S<string>] [--pickaxe-all] [-m] [-s] [-v] [-t] <tree-ish> <tree-ish> [<pattern>]\\*\n+'git-diff-tree' [-p] [-r] [-z] [--stdin] [-B] [-M] [-R] [-C] [-S<string>] [--pickaxe-all] [-m] [-s] [-v] [-t] <tree-ish> <tree-ish> [<pattern>]\\*\n \n DESCRIPTION\n -----------\n@@ -33,6 +33,9 @@ OPTIONS\n \tgenerate patch (see section on generating patches).  For\n \tgit-diff-tree, this flag implies '-r' as well.\n \n+-B::\n+\tBreak complete rewrite changes into pairs of delete and create.\n+\n -M::\n \tDetect renames.\n \ndiff --git a/Makefile b/Makefile\n--- a/Makefile\n+++ b/Makefile\n@@ -48,7 +48,7 @@ LIB_OBJS += strbuf.o\n \n LIB_H += diff.h count-delta.h\n LIB_OBJS += diff.o diffcore-rename.o diffcore-pickaxe.o diffcore-pathspec.o \\\n-\tcount-delta.o\n+\tcount-delta.o diffcore-break.o\n \n LIB_OBJS += gitenv.o\n \n@@ -130,6 +130,7 @@ diff.o: $(LIB_H) diffcore.h\n diffcore-rename.o : $(LIB_H) diffcore.h\n diffcore-pathspec.o : $(LIB_H) diffcore.h\n diffcore-pickaxe.o : $(LIB_H) diffcore.h\n+diffcore-break.o : $(LIB_H) diffcore.h\n \n test: all\n \t$(MAKE) -C t/ all\ndiff --git a/diff-cache.c b/diff-cache.c\n--- a/diff-cache.c\n+++ b/diff-cache.c\n@@ -9,6 +9,7 @@ static int diff_setup_opt = 0;\n static int diff_score_opt = 0;\n static const char *pickaxe = NULL;\n static int pickaxe_opts = 0;\n+static int diff_break_opt = -1;\n \n /* A file entry went away or appeared */\n static void show_file(const char *prefix, struct cache_entry *ce, unsigned char *sha1, unsigned int mode)\n@@ -188,6 +189,10 @@ int main(int argc, const char **argv)\n \t\t\tdiff_output_format = DIFF_FORMAT_PATCH;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strncmp(arg, \"-B\", 2)) {\n+\t\t\tdiff_break_opt = diff_scoreopt_parse(arg);\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strncmp(arg, \"-M\", 2)) {\n \t\t\tdetect_rename = DIFF_DETECT_RENAME;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(arg);\n@@ -240,9 +245,11 @@ int main(int argc, const char **argv)\n \t\tdie(\"unable to read tree object %s\", tree_name);\n \n \tret = diff_cache(active_cache, active_nr);\n-\tdiffcore_std(pathspec,\n+\n+\tdiffcore_std(pathspec ? : NULL,\n \t\t     detect_rename, diff_score_opt,\n-\t\t     pickaxe, pickaxe_opts);\n+\t\t     pickaxe, pickaxe_opts,\n+\t\t     diff_break_opt);\n \tdiff_flush(diff_output_format, 1);\n \treturn ret;\n }\ndiff --git a/diff-files.c b/diff-files.c\n--- a/diff-files.c\n+++ b/diff-files.c\n@@ -15,6 +15,7 @@ static int diff_setup_opt = 0;\n static int diff_score_opt = 0;\n static const char *pickaxe = NULL;\n static int pickaxe_opts = 0;\n+static int diff_break_opt = -1;\n static int silent = 0;\n \n static void show_unmerge(const char *path)\n@@ -57,6 +58,8 @@ int main(int argc, const char **argv)\n \t\t\tpickaxe = argv[1] + 2;\n \t\telse if (!strcmp(argv[1], \"--pickaxe-all\"))\n \t\t\tpickaxe_opts = DIFF_PICKAXE_ALL;\n+\t\telse if (!strncmp(argv[1], \"-B\", 2))\n+\t\t\tdiff_break_opt = diff_scoreopt_parse(argv[1]);\n \t\telse if (!strncmp(argv[1], \"-M\", 2)) {\n \t\t\tdiff_score_opt = diff_scoreopt_parse(argv[1]);\n \t\t\tdetect_rename = DIFF_DETECT_RENAME;\n@@ -116,9 +119,10 @@ int main(int argc, const char **argv)\n \t\tshow_modified(oldmode, mode, ce->sha1, null_sha1,\n \t\t\t      ce->name);\n \t}\n-\tdiffcore_std(argv + 1,\n+\tdiffcore_std((1 < argc) ? argv + 1 : NULL,\n \t\t     detect_rename, diff_score_opt,\n-\t\t     pickaxe, pickaxe_opts);\n+\t\t     pickaxe, pickaxe_opts,\n+\t\t     diff_break_opt);\n \tdiff_flush(diff_output_format, 1);\n \treturn 0;\n }\ndiff --git a/diff-tree.c b/diff-tree.c\n--- a/diff-tree.c\n+++ b/diff-tree.c\n@@ -14,6 +14,7 @@ static int diff_setup_opt = 0;\n static int diff_score_opt = 0;\n static const char *pickaxe = NULL;\n static int pickaxe_opts = 0;\n+static int diff_break_opt = -1;\n static const char *header = NULL;\n static const char *header_prefix = \"\";\n \n@@ -263,7 +264,8 @@ static int call_diff_flush(void)\n {\n \tdiffcore_std(0,\n \t\t     detect_rename, diff_score_opt,\n-\t\t     pickaxe, pickaxe_opts);\n+\t\t     pickaxe, pickaxe_opts,\n+\t\t     diff_break_opt);\n \tif (diff_queue_is_empty()) {\n \t\tdiff_flush(DIFF_FORMAT_NO_OUTPUT, 0);\n \t\treturn 0;\n@@ -523,6 +525,10 @@ int main(int argc, const char **argv)\n \t\t\tdiff_score_opt = diff_scoreopt_parse(arg);\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strncmp(arg, \"-B\", 2)) {\n+\t\t\tdiff_break_opt = diff_scoreopt_parse(arg);\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(arg, \"-z\")) {\n \t\t\tdiff_output_format = DIFF_FORMAT_MACHINE;\n \t\t\tcontinue;\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -603,6 +603,7 @@ struct diff_filepair *diff_queue(struct \n \tdp->two = two;\n \tdp->score = 0;\n \tdp->source_stays = 0;\n+\tdp->broken_pair = 0;\n \tdiff_q(queue, dp);\n \treturn dp;\n }\n@@ -637,6 +638,16 @@ static void diff_flush_raw(struct diff_f\n \t\tsprintf(status, \"%c%03d\", p->status,\n \t\t\t(int)(0.5 + p->score * 100.0/MAX_SCORE));\n \t\tbreak;\n+\tcase 'N': case 'D':\n+\t\ttwo_paths = 0;\n+\t\tif (p->score)\n+\t\t\tsprintf(status, \"%c%03d\", p->status,\n+\t\t\t\t(int)(0.5 + p->score * 100.0/MAX_SCORE));\n+\t\telse {\n+\t\t\tstatus[0] = p->status;\n+\t\t\tstatus[1] = 0;\n+\t\t}\n+\t\tbreak;\n \tdefault:\n \t\ttwo_paths = 0;\n \t\tstatus[0] = p->status;\n@@ -760,8 +771,9 @@ void diff_debug_filepair(const struct di\n {\n \tdiff_debug_filespec(p->one, i, \"one\");\n \tdiff_debug_filespec(p->two, i, \"two\");\n-\tfprintf(stderr, \"score %d, status %c source_stays %d\\n\",\n-\t\tp->score, p->status ? : '?', p->source_stays);\n+\tfprintf(stderr, \"score %d, status %c stays %d broken %d\\n\",\n+\t\tp->score, p->status ? : '?',\n+\t\tp->source_stays, p->broken_pair);\n }\n \n void diff_debug_queue(const char *msg, struct diff_queue_struct *q)\n@@ -875,10 +887,13 @@ void diff_flush(int diff_output_style, i\n \n void diffcore_std(const char **paths,\n \t\t  int detect_rename, int rename_score,\n-\t\t  const char *pickaxe, int pickaxe_opts)\n+\t\t  const char *pickaxe, int pickaxe_opts,\n+\t\t  int break_opt)\n {\n \tif (paths && paths[0])\n \t\tdiffcore_pathspec(paths);\n+\tif (0 <= break_opt)\n+\t\tdiffcore_break(break_opt);\n \tif (detect_rename)\n \t\tdiffcore_rename(detect_rename, rename_score);\n \tif (pickaxe)\ndiff --git a/diff.h b/diff.h\n--- a/diff.h\n+++ b/diff.h\n@@ -43,9 +43,12 @@ extern void diffcore_pickaxe(const char \n \n extern void diffcore_pathspec(const char **pathspec);\n \n+extern void diffcore_break(int);\n+\n extern void diffcore_std(const char **paths,\n \t\t\t int detect_rename, int rename_score,\n-\t\t\t const char *pickaxe, int pickaxe_opts);\n+\t\t\t const char *pickaxe, int pickaxe_opts,\n+\t\t\t int break_opt);\n \n extern int diff_queue_is_empty(void);\n \ndiff --git a/diffcore-break.c b/diffcore-break.c\nnew file mode 100644\n--- /dev/null\n+++ b/diffcore-break.c\n@@ -0,0 +1,127 @@\n+/*\n+ * Copyright (C) 2005 Junio C Hamano\n+ */\n+#include \"cache.h\"\n+#include \"diff.h\"\n+#include \"diffcore.h\"\n+#include \"delta.h\"\n+#include \"count-delta.h\"\n+\n+static int very_different(struct diff_filespec *src,\n+\t\t\t  struct diff_filespec *dst,\n+\t\t\t  int min_score)\n+{\n+\t/* dst is recorded as a modification of src.  Are they so\n+\t * different that we are better off recording this as a pair\n+\t * of delete and create?  min_score is the minimum amount of\n+\t * new material that must exist in the dst and not in src for\n+\t * the pair to be considered a complete rewrite, and recommended\n+\t * to be set to a very high value, 99% or so.\n+\t *\n+\t * The value we return represents the amount of new material\n+\t * that is in dst and not in src.  We return 0 when we do not\n+\t * want to get the filepair broken.\n+\t */\n+\tvoid *delta;\n+\tunsigned long delta_size, base_size;\n+\n+\tif (!S_ISREG(src->mode) || !S_ISREG(dst->mode))\n+\t\treturn 0; /* leave symlink rename alone */\n+\n+\tif (diff_populate_filespec(src, 1) || diff_populate_filespec(dst, 1))\n+\t\treturn 0; /* error but caught downstream */\n+\n+\tdelta_size = ((src->size < dst->size) ?\n+\t\t      (dst->size - src->size) : (src->size - dst->size));\n+\n+\t/* Notice that we use max of src and dst as the base size,\n+\t * unlike rename similarity detection.  This is so that we do\n+\t * not mistake a large addition as a complete rewrite.\n+\t */\n+\tbase_size = ((src->size < dst->size) ? dst->size : src->size);\n+\n+\t/*\n+\t * If file size difference is too big compared to the\n+\t * base_size, we declare this a complete rewrite.\n+\t */\n+\tif (base_size * min_score < delta_size * MAX_SCORE)\n+\t\treturn MAX_SCORE;\n+\n+\tif (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))\n+\t\treturn 0; /* error but caught downstream */\n+\n+\tdelta = diff_delta(src->data, src->size,\n+\t\t\t   dst->data, dst->size,\n+\t\t\t   &delta_size);\n+\n+\t/* A delta that has a lot of literal additions would have\n+\t * big delta_size no matter what else it does.\n+\t */\n+\tif (base_size * min_score < delta_size * MAX_SCORE)\n+\t\treturn MAX_SCORE;\n+\n+\t/* Estimate the edit size by interpreting delta. */\n+\tdelta_size = count_delta(delta, delta_size);\n+\tfree(delta);\n+\tif (delta_size == UINT_MAX)\n+\t\treturn 0; /* error in delta computation */\n+\n+\tif (base_size < delta_size)\n+\t\treturn MAX_SCORE;\n+\n+\treturn delta_size * MAX_SCORE / base_size; \n+}\n+\n+void diffcore_break(int min_score)\n+{\n+\tstruct diff_queue_struct *q = &diff_queued_diff;\n+\tstruct diff_queue_struct outq;\n+\tint i;\n+\n+\tif (!min_score)\n+\t\tmin_score = DEFAULT_BREAK_SCORE;\n+\n+\toutq.nr = outq.alloc = 0;\n+\toutq.queue = NULL;\n+\n+\tfor (i = 0; i < q->nr; i++) {\n+\t\tstruct diff_filepair *p = q->queue[i];\n+\t\tint score;\n+\n+\t\t/* We deal only with in-place edit of non directory.\n+\t\t * We do not break anything else.\n+\t\t */\n+\t\tif (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two) &&\n+\t\t    !S_ISDIR(p->one->mode) && !S_ISDIR(p->two->mode) &&\n+\t\t    !strcmp(p->one->path, p->two->path)) {\n+\t\t\tscore = very_different(p->one, p->two, min_score);\n+\t\t\tif (min_score <= score) {\n+\t\t\t\t/* Split this into delete and create */\n+\t\t\t\tstruct diff_filespec *null_one, *null_two;\n+\t\t\t\tstruct diff_filepair *dp;\n+\n+\t\t\t\t/* deletion of one */\n+\t\t\t\tnull_one = alloc_filespec(p->one->path);\n+\t\t\t\tdp = diff_queue(&outq, p->one, null_one);\n+\t\t\t\tdp->score = score;\n+\t\t\t\tdp->broken_pair = 1;\n+\n+\t\t\t\t/* creation of two */\n+\t\t\t\tnull_two = alloc_filespec(p->two->path);\n+\t\t\t\tdp = diff_queue(&outq, null_two, p->two);\n+\t\t\t\tdp->score = score;\n+\t\t\t\tdp->broken_pair = 1;\n+\n+\t\t\t\tfree(p); /* not diff_free_filepair(), we are\n+\t\t\t\t\t  * reusing one and two here.\n+\t\t\t\t\t  */\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n+\t\tdiff_q(&outq, p);\n+\t}\n+\tfree(q->queue);\n+\t*q = outq;\n+\n+\treturn;\n+}\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -225,8 +225,8 @@ static int score_compare(const void *a_,\n int diff_scoreopt_parse(const char *opt)\n {\n \tint diglen, num, scale, i;\n-\tif (opt[0] != '-' || (opt[1] != 'M' && opt[1] != 'C'))\n-\t\treturn -1; /* that is not a -M nor -C option */\n+\tif (opt[0] != '-' || (opt[1] != 'M' && opt[1] != 'C' && opt[1] != 'B'))\n+\t\treturn -1; /* that is not a -M, -C nor -B option */\n \tdiglen = strspn(opt+2, \"0123456789\");\n \tif (diglen == 0 || strlen(opt+2) != diglen)\n \t\treturn 0; /* use default */\n@@ -249,7 +249,7 @@ void diffcore_rename(int detect_rename, \n \tint num_create, num_src, dst_cnt;\n \n \tif (!minimum_score)\n-\t\tminimum_score = DEFAULT_MINIMUM_SCORE;\n+\t\tminimum_score = DEFAULT_RENAME_SCORE;\n \trenq.queue = NULL;\n \trenq.nr = renq.alloc = 0;\n \n@@ -353,17 +353,36 @@ void diffcore_rename(int detect_rename, \n \t\t\t/*\n \t\t\t * Deletion\n \t\t\t *\n-\t\t\t * We would output this delete record if renq\n-\t\t\t * does not have a rename/copy to move\n-\t\t\t * p->one->path out.\n+\t\t\t * We would output this delete record if:\n+\t\t\t *\n+\t\t\t * (1) this is a broken delete and the counterpart\n+\t\t\t *     broken create remains in the output; or\n+\t\t\t * (2) this is not a broken delete, and renq does\n+\t\t\t *     not have a rename/copy to move p->one->path\n+\t\t\t *     out.\n+\t\t\t *\n+\t\t\t * Otherwise, the counterpart broken create\n+\t\t\t * has been turned into a rename-edit; or\n+\t\t\t * delete did not have a matching create to\n+\t\t\t * begin with.\n \t\t\t */\n-\t\t\tfor (j = 0; j < renq.nr; j++)\n-\t\t\t\tif (!strcmp(renq.queue[j]->one->path,\n-\t\t\t\t\t    p->one->path))\n-\t\t\t\t\tbreak;\n-\t\t\tif (j < renq.nr)\n-\t\t\t\t/* this path remains */\n-\t\t\t\tpair_to_free = p;\n+\t\t\tif (DIFF_PAIR_BROKEN(p)) {\n+\t\t\t\t/* broken delete */\n+\t\t\t\tstruct diff_rename_dst *dst =\n+\t\t\t\t\tlocate_rename_dst(p->one, 0);\n+\t\t\t\tif (dst && dst->pair)\n+\t\t\t\t\t/* counterpart is now rename/copy */\n+\t\t\t\t\tpair_to_free = p;\n+\t\t\t}\n+\t\t\telse {\n+\t\t\t\tfor (j = 0; j < renq.nr; j++)\n+\t\t\t\t\tif (!strcmp(renq.queue[j]->one->path,\n+\t\t\t\t\t\t    p->one->path))\n+\t\t\t\t\t\tbreak;\n+\t\t\t\tif (j < renq.nr)\n+\t\t\t\t\t/* this path remains */\n+\t\t\t\t\tpair_to_free = p;\n+\t\t\t}\n \n \t\t\tif (pair_to_free)\n \t\t\t\t;\ndiff --git a/diffcore.h b/diffcore.h\n--- a/diffcore.h\n+++ b/diffcore.h\n@@ -8,8 +8,9 @@\n  * (e.g. diffcore-rename, diffcore-pickaxe).  Never include this header\n  * in anything else.\n  */\n-#define MAX_SCORE 10000\n-#define DEFAULT_MINIMUM_SCORE 5000\n+#define MAX_SCORE 60000\n+#define DEFAULT_RENAME_SCORE 30000 /* rename/copy similarity minimum (50%) */\n+#define DEFAULT_BREAK_SCORE  59400 /* minimum for break to happen (99%)*/\n \n #define RENAME_DST_MATCHED 01\n \n@@ -40,14 +41,19 @@ struct diff_filepair {\n \tstruct diff_filespec *one;\n \tstruct diff_filespec *two;\n \tunsigned short int score;\n-\tchar source_stays; /* all of R/C are copies */\n \tchar status; /* M C R N D U (see Documentation/diff-format.txt) */\n+\tunsigned source_stays : 1; /* all of R/C are copies */\n+\tunsigned broken_pair : 1;\n };\n #define DIFF_PAIR_UNMERGED(p) \\\n \t(!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))\n \n #define DIFF_PAIR_RENAME(p) (strcmp((p)->one->path, (p)->two->path))\n \n+#define DIFF_PAIR_BROKEN(p) \\\n+\t( (!DIFF_FILE_VALID((p)->one) != !DIFF_FILE_VALID((p)->two)) && \\\n+\t  ((p)->broken_pair != 0) )\n+\n #define DIFF_PAIR_TYPE_CHANGED(p) \\\n \t((S_IFMT & (p)->one->mode) != (S_IFMT & (p)->two->mode))\n \ndiff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh\nnew file mode 100755\n--- /dev/null\n+++ b/t/t4008-diff-break-rewrite.sh\n@@ -0,0 +1,207 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2005 Junio C Hamano\n+#\n+\n+test_description='Break and then rename\n+\n+We have two very different files, file0 and file1, registered in a tree.\n+\n+We update file1 so drastically that it is more similar to file0, and\n+then remove file0.  With -B, changes to file1 should be broken into\n+separate delete and create, resulting in removal of file0, removal of\n+original file1 and creation of completely rewritten file1.\n+\n+Further, with -B and -M together, these three modifications should\n+turn into rename-edit of file0 into file1.\n+\n+Starting from the same two files in the tree, we swap file0 and file1.\n+With -B, this should be detected as two complete rewrites, resulting in\n+four changes in total.\n+\n+Further, with -B and -M together, these should turn into two renames.\n+'\n+. ./test-lib.sh\n+\n+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'\n+_x40=\"$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40\"\n+sanitize_diff_raw='s/ '\"$_x40\"' '\"$_x40\"' \\([CDNR]\\)[0-9]*\t/ X X \\1#\t/'\n+compare_diff_raw () {\n+    # When heuristics are improved, the score numbers would change.\n+    # Ignore them while comparing.\n+    # Also we do not check SHA1 hash generation in this test, which\n+    # is a job for t0000-basic.sh\n+\n+    sed -e \"$sanitize_diff_raw\" <\"$1\" >.tmp-1\n+    sed -e \"$sanitize_diff_raw\" <\"$2\" >.tmp-2\n+    diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2\n+}\n+\n+test_expect_success \\\n+    setup \\\n+    'cat ../../README >file0 &&\n+     cat ../../COPYING >file1 &&\n+    git-update-cache --add file0 file1 &&\n+    tree=$(git-write-tree) &&\n+    echo \"$tree\"'\n+\n+test_expect_success \\\n+    'change file1 with copy-edit of file0 and remove file0' \\\n+    'sed -e \"s/git/GIT/\" file0 >file1 &&\n+     rm -f file0 &&\n+    git-update-cache --remove file0 file1'\n+\n+test_expect_success \\\n+    'run diff with -B' \\\n+    'git-diff-cache -B --cached \"$tree\" >current'\n+\n+cat >expected <<\\EOF\n+:100644 000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 0000000000000000000000000000000000000000 D\tfile0\n+:100644 000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0000000000000000000000000000000000000000 D100\tfile1\n+:000000 100644 0000000000000000000000000000000000000000 11e331465a89c394dc25c780de230043750c1ec8 N100\tfile1\n+EOF\n+\n+test_expect_success \\\n+    'validate result of -B (#1)' \\\n+    'compare_diff_raw current expected'\n+\n+test_expect_success \\\n+    'run diff with -B and -M' \\\n+    'git-diff-cache -B -M \"$tree\" >current'\n+\n+cat >expected <<\\EOF\n+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 08bb2fb671deff4c03a4d4a0a1315dff98d5732c R100\tfile0\tfile1\n+EOF\n+\n+test_expect_success \\\n+    'validate result of -B -M (#2)' \\\n+    'compare_diff_raw current expected'\n+\n+test_expect_success \\\n+    'swap file0 and file1' \\\n+    'rm -f file0 file1 &&\n+     git-read-tree -m $tree &&\n+     git-checkout-cache -f -u -a &&\n+     mv file0 tmp &&\n+     mv file1 file0 &&\n+     mv tmp file1 &&\n+     git-update-cache file0 file1'\n+\n+test_expect_success \\\n+    'run diff with -B' \\\n+    'git-diff-cache -B \"$tree\" >current'\n+\n+cat >expected <<\\EOF\n+:100644 000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 0000000000000000000000000000000000000000 D100\tfile0\n+:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 N100\tfile0\n+:100644 000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0000000000000000000000000000000000000000 D100\tfile1\n+:000000 100644 0000000000000000000000000000000000000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 N100\tfile1\n+EOF\n+\n+test_expect_success \\\n+    'validate result of -B (#3)' \\\n+    'compare_diff_raw current expected'\n+\n+test_expect_success \\\n+    'run diff with -B and -M' \\\n+    'git-diff-cache -B -M \"$tree\" >current'\n+\n+cat >expected <<\\EOF\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100\tfile1\tfile0\n+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 R100\tfile0\tfile1\n+EOF\n+\n+test_expect_success \\\n+    'validate result of -B -M (#4)' \\\n+    'compare_diff_raw current expected'\n+\n+test_expect_success \\\n+    'make file0 into something completely different' \\\n+    'rm -f file0 &&\n+     ln -s frotz file0 &&\n+     git-update-cache file0 file1'\n+\n+test_expect_success \\\n+    'run diff with -B' \\\n+    'git-diff-cache -B \"$tree\" >current'\n+\n+cat >expected <<\\EOF\n+:100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T\tfile0\n+:100644 000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0000000000000000000000000000000000000000 D100\tfile1\n+:000000 100644 0000000000000000000000000000000000000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 N100\tfile1\n+EOF\n+\n+test_expect_success \\\n+    'validate result of -B (#5)' \\\n+    'compare_diff_raw current expected'\n+\n+test_expect_success \\\n+    'run diff with -B' \\\n+    'git-diff-cache -B -M \"$tree\" >current'\n+\n+# This should not mistake file0 as the copy source of new file1\n+# due to type differences.\n+cat >expected <<\\EOF\n+:100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T\tfile0\n+:100644 000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0000000000000000000000000000000000000000 D100\tfile1\n+:000000 100644 0000000000000000000000000000000000000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 N100\tfile1\n+EOF\n+\n+test_expect_success \\\n+    'validate result of -B -M (#6)' \\\n+    'compare_diff_raw current expected'\n+\n+test_expect_success \\\n+    'run diff with -M' \\\n+    'git-diff-cache -M \"$tree\" >current'\n+\n+# This should not mistake file0 as the copy source of new file1\n+# due to type differences.\n+cat >expected <<\\EOF\n+:100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T\tfile0\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M\tfile1\n+EOF\n+\n+test_expect_success \\\n+    'validate result of -M (#7)' \\\n+    'compare_diff_raw current expected'\n+\n+test_expect_success \\\n+    'file1 edited to look like file0 and file0 rename-edited to file2' \\\n+    'rm -f file0 file1 &&\n+     git-read-tree -m $tree &&\n+     git-checkout-cache -f -u -a &&\n+     sed -e \"s/git/GIT/\" file0 >file1 &&\n+     sed -e \"s/git/GET/\" file0 >file2 &&\n+     rm -f file0\n+     git-update-cache --add --remove file0 file1 file2'\n+\n+test_expect_success \\\n+    'run diff with -B' \\\n+    'git-diff-cache -B \"$tree\" >current'\n+\n+cat >expected <<\\EOF\n+:100644 000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 0000000000000000000000000000000000000000 D\tfile0\n+:100644 000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0000000000000000000000000000000000000000 D100\tfile1\n+:000000 100644 0000000000000000000000000000000000000000 08bb2fb671deff4c03a4d4a0a1315dff98d5732c N100\tfile1\n+:000000 100644 0000000000000000000000000000000000000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 N\tfile2\n+EOF\n+\n+test_expect_success \\\n+    'validate result of -B (#8)' \\\n+    'compare_diff_raw current expected'\n+\n+test_expect_success \\\n+    'run diff with -B -M' \\\n+    'git-diff-cache -B -M \"$tree\" >current'\n+\n+cat >expected <<\\EOF\n+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 08bb2fb671deff4c03a4d4a0a1315dff98d5732c C095\tfile0\tfile1\n+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 59f832e5c8b3f7e486be15ad0cd3e95ba9af8998 R095\tfile0\tfile2\n+EOF\n+\n+test_expect_success \\\n+    'validate result of -B -M (#9)' \\\n+    'compare_diff_raw current expected'\n+\n+test_done\n\n"},{"id":"4239","messageId":"7vpsv96vho.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"7vekbp8ajm.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 4/4] Add -O<orderfile> option to diff-* brothers.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-30T07:09:07Z","receivedAt":"2005-05-30T07:09:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"A new diffcore filter diffcore-order is introduced.  This takes\na text file each of whose line is a shell glob pattern.  Patches\nthat match a glob pattern on an earlier line in the file are\noutput before patches that match a later line, and patches that\ndo not match any glob pattern are output last.\n\nA typical orderfile for git project probably should look like\nthis:\n\n    README\n    Makefile\n    Documentation\n    *.h\n    *.c\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n Documentation/git-diff-cache.txt |    6 +\n Documentation/git-diff-files.txt |    6 +\n Documentation/git-diff-tree.txt  |    6 +\n Makefile                         |    3 \n diff-cache.c                     |    8 ++\n diff-files.c                     |    6 +\n diff-tree.c                      |    8 ++\n diff.c                           |    5 +\n diff.h                           |    7 +-\n diffcore-order.c                 |  122 +++++++++++++++++++++++++++++++++++++++\n 10 files changed, 167 insertions(+), 10 deletions(-)\n\n6575b8d66ea48891aaba3fba3eb8a4fddc596c2c (from 96c0ba825059e65743bfce718e54413c4583ff35)\ndiff --git a/Documentation/git-diff-cache.txt b/Documentation/git-diff-cache.txt\n--- a/Documentation/git-diff-cache.txt\n+++ b/Documentation/git-diff-cache.txt\n@@ -9,7 +9,7 @@ git-diff-cache - Compares content and mo\n \n SYNOPSIS\n --------\n-'git-diff-cache' [-p] [-r] [-z] [-m] [-B] [-M] [-R] [-C] [-S<string>] [--pickaxe-all] [--cached] <tree-ish> [<path>...]\n+'git-diff-cache' [-p] [-r] [-z] [-m] [-B] [-M] [-R] [-C] [-O<orderfile>] [-S<string>] [--pickaxe-all] [--cached] <tree-ish> [<path>...]\n \n DESCRIPTION\n -----------\n@@ -52,6 +52,10 @@ OPTIONS\n \tchangeset, not just the files that contains the change\n \tin <string>.\n \n+-O<orderfile>::\n+\tOutput the patch in the order specified in the\n+\t<orderfile>, which has one shell glob pattern per line.\n+\n -R::\n \tOutput diff in reverse.\n \ndiff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt\n--- a/Documentation/git-diff-files.txt\n+++ b/Documentation/git-diff-files.txt\n@@ -9,7 +9,7 @@ git-diff-files - Compares files in the w\n \n SYNOPSIS\n --------\n-'git-diff-files' [-p] [-q] [-r] [-z] [-B] [-M] [-C] [-R] [-S<string>] [--pickaxe-all] [<pattern>...]\n+'git-diff-files' [-p] [-q] [-r] [-z] [-B] [-M] [-C] [-R] [-O<orderfile>] [-S<string>] [--pickaxe-all] [<pattern>...]\n \n DESCRIPTION\n -----------\n@@ -46,6 +46,10 @@ OPTIONS\n \tchangeset, not just the files that contains the change\n \tin <string>.\n \n+-O<orderfile>::\n+\tOutput the patch in the order specified in the\n+\t<orderfile>, which has one shell glob pattern per line.\n+\n -r::\n \tThis flag does not mean anything.  It is there only to match\n \tgit-diff-tree.  Unlike git-diff-tree, git-diff-files always looks\ndiff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt\n--- a/Documentation/git-diff-tree.txt\n+++ b/Documentation/git-diff-tree.txt\n@@ -9,7 +9,7 @@ git-diff-tree - Compares the content and\n \n SYNOPSIS\n --------\n-'git-diff-tree' [-p] [-r] [-z] [--stdin] [-B] [-M] [-R] [-C] [-S<string>] [--pickaxe-all] [-m] [-s] [-v] [-t] <tree-ish> <tree-ish> [<pattern>]\\*\n+'git-diff-tree' [-p] [-r] [-z] [--stdin] [-B] [-M] [-R] [-C] [-O<orderfile>] [-S<string>] [--pickaxe-all] [-m] [-s] [-v] [-t] <tree-ish> <tree-ish> [<pattern>]\\*\n \n DESCRIPTION\n -----------\n@@ -53,6 +53,10 @@ OPTIONS\n \tchangeset, not just the files that contains the change\n \tin <string>.\n \n+-O<orderfile>::\n+\tOutput the patch in the order specified in the\n+\t<orderfile>, which has one shell glob pattern per line.\n+\n -r::\n \trecurse\n \ndiff --git a/Makefile b/Makefile\n--- a/Makefile\n+++ b/Makefile\n@@ -48,7 +48,7 @@ LIB_OBJS += strbuf.o\n \n LIB_H += diff.h count-delta.h\n LIB_OBJS += diff.o diffcore-rename.o diffcore-pickaxe.o diffcore-pathspec.o \\\n-\tcount-delta.o diffcore-break.o\n+\tcount-delta.o diffcore-break.o diffcore-order.o\n \n LIB_OBJS += gitenv.o\n \n@@ -131,6 +131,7 @@ diffcore-rename.o : $(LIB_H) diffcore.h\n diffcore-pathspec.o : $(LIB_H) diffcore.h\n diffcore-pickaxe.o : $(LIB_H) diffcore.h\n diffcore-break.o : $(LIB_H) diffcore.h\n+diffcore-order.o : $(LIB_H) diffcore.h\n \n test: all\n \t$(MAKE) -C t/ all\ndiff --git a/diff-cache.c b/diff-cache.c\n--- a/diff-cache.c\n+++ b/diff-cache.c\n@@ -10,6 +10,7 @@ static int diff_score_opt = 0;\n static const char *pickaxe = NULL;\n static int pickaxe_opts = 0;\n static int diff_break_opt = -1;\n+static const char *orderfile = NULL;\n \n /* A file entry went away or appeared */\n static void show_file(const char *prefix, struct cache_entry *ce, unsigned char *sha1, unsigned int mode)\n@@ -215,6 +216,10 @@ int main(int argc, const char **argv)\n \t\t\tpickaxe = arg + 2;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strncmp(arg, \"-O\", 2)) {\n+\t\t\torderfile = arg + 2;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(arg, \"--pickaxe-all\")) {\n \t\t\tpickaxe_opts = DIFF_PICKAXE_ALL;\n \t\t\tcontinue;\n@@ -249,7 +254,8 @@ int main(int argc, const char **argv)\n \tdiffcore_std(pathspec ? : NULL,\n \t\t     detect_rename, diff_score_opt,\n \t\t     pickaxe, pickaxe_opts,\n-\t\t     diff_break_opt);\n+\t\t     diff_break_opt,\n+\t\t     orderfile);\n \tdiff_flush(diff_output_format, 1);\n \treturn ret;\n }\ndiff --git a/diff-files.c b/diff-files.c\n--- a/diff-files.c\n+++ b/diff-files.c\n@@ -16,6 +16,7 @@ static int diff_score_opt = 0;\n static const char *pickaxe = NULL;\n static int pickaxe_opts = 0;\n static int diff_break_opt = -1;\n+static const char *orderfile = NULL;\n static int silent = 0;\n \n static void show_unmerge(const char *path)\n@@ -56,6 +57,8 @@ int main(int argc, const char **argv)\n \t\t\tdiff_setup_opt |= DIFF_SETUP_REVERSE;\n \t\telse if (!strncmp(argv[1], \"-S\", 2))\n \t\t\tpickaxe = argv[1] + 2;\n+\t\telse if (!strncmp(argv[1], \"-O\", 2))\n+\t\t\torderfile = argv[1] + 2;\n \t\telse if (!strcmp(argv[1], \"--pickaxe-all\"))\n \t\t\tpickaxe_opts = DIFF_PICKAXE_ALL;\n \t\telse if (!strncmp(argv[1], \"-B\", 2))\n@@ -122,7 +125,8 @@ int main(int argc, const char **argv)\n \tdiffcore_std((1 < argc) ? argv + 1 : NULL,\n \t\t     detect_rename, diff_score_opt,\n \t\t     pickaxe, pickaxe_opts,\n-\t\t     diff_break_opt);\n+\t\t     diff_break_opt,\n+\t\t     orderfile);\n \tdiff_flush(diff_output_format, 1);\n \treturn 0;\n }\ndiff --git a/diff-tree.c b/diff-tree.c\n--- a/diff-tree.c\n+++ b/diff-tree.c\n@@ -15,6 +15,7 @@ static int diff_score_opt = 0;\n static const char *pickaxe = NULL;\n static int pickaxe_opts = 0;\n static int diff_break_opt = -1;\n+static const char *orderfile = NULL;\n static const char *header = NULL;\n static const char *header_prefix = \"\";\n \n@@ -265,7 +266,8 @@ static int call_diff_flush(void)\n \tdiffcore_std(0,\n \t\t     detect_rename, diff_score_opt,\n \t\t     pickaxe, pickaxe_opts,\n-\t\t     diff_break_opt);\n+\t\t     diff_break_opt,\n+\t\t     orderfile);\n \tif (diff_queue_is_empty()) {\n \t\tdiff_flush(DIFF_FORMAT_NO_OUTPUT, 0);\n \t\treturn 0;\n@@ -511,6 +513,10 @@ int main(int argc, const char **argv)\n \t\t\tpickaxe = arg + 2;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strncmp(arg, \"-O\", 2)) {\n+\t\t\torderfile = arg + 2;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(arg, \"--pickaxe-all\")) {\n \t\t\tpickaxe_opts = DIFF_PICKAXE_ALL;\n \t\t\tcontinue;\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -888,7 +888,8 @@ void diff_flush(int diff_output_style, i\n void diffcore_std(const char **paths,\n \t\t  int detect_rename, int rename_score,\n \t\t  const char *pickaxe, int pickaxe_opts,\n-\t\t  int break_opt)\n+\t\t  int break_opt,\n+\t\t  const char *orderfile)\n {\n \tif (paths && paths[0])\n \t\tdiffcore_pathspec(paths);\n@@ -898,6 +899,8 @@ void diffcore_std(const char **paths,\n \t\tdiffcore_rename(detect_rename, rename_score);\n \tif (pickaxe)\n \t\tdiffcore_pickaxe(pickaxe, pickaxe_opts);\n+\tif (orderfile)\n+\t\tdiffcore_order(orderfile);\n }\n \n void diff_addremove(int addremove, unsigned mode,\ndiff --git a/diff.h b/diff.h\n--- a/diff.h\n+++ b/diff.h\n@@ -43,12 +43,15 @@ extern void diffcore_pickaxe(const char \n \n extern void diffcore_pathspec(const char **pathspec);\n \n-extern void diffcore_break(int);\n+extern void diffcore_order(const char *orderfile);\n+\n+extern void diffcore_break(int max_score);\n \n extern void diffcore_std(const char **paths,\n \t\t\t int detect_rename, int rename_score,\n \t\t\t const char *pickaxe, int pickaxe_opts,\n-\t\t\t int break_opt);\n+\t\t\t int break_opt,\n+\t\t\t const char *orderfile);\n \n extern int diff_queue_is_empty(void);\n \ndiff --git a/diffcore-order.c b/diffcore-order.c\nnew file mode 100644\n--- /dev/null\n+++ b/diffcore-order.c\n@@ -0,0 +1,122 @@\n+/*\n+ * Copyright (C) 2005 Junio C Hamano\n+ */\n+#include \"cache.h\"\n+#include \"diff.h\"\n+#include \"diffcore.h\"\n+#include <fnmatch.h>\n+\n+static char **order;\n+static int order_cnt;\n+\n+static void prepare_order(const char *orderfile)\n+{\n+\tint fd, cnt, pass;\n+\tvoid *map;\n+\tchar *cp, *endp;\n+\tstruct stat st;\n+\n+\tif (order)\n+\t\treturn;\n+\n+\tfd = open(orderfile, O_RDONLY);\n+\tif (fd < 0)\n+\t\treturn;\n+\tif (fstat(fd, &st)) {\n+\t\tclose(fd);\n+\t\treturn;\n+\t}\n+\tmap = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);\n+\tclose(fd);\n+\tif (-1 == (int)(long)map)\n+\t\treturn;\n+\tendp = map + st.st_size;\n+\tfor (pass = 0; pass < 2; pass++) {\n+\t\tcnt = 0;\n+\t\tcp = map;\n+\t\twhile (cp < endp) {\n+\t\t\tchar *ep;\n+\t\t\tfor (ep = cp; ep < endp && *ep != '\\n'; ep++)\n+\t\t\t\t;\n+\t\t\t/* cp to ep has one line */\n+\t\t\tif (*cp == '\\n' || *cp == '#')\n+\t\t\t\t; /* comment */\n+\t\t\telse if (pass == 0)\n+\t\t\t\tcnt++;\n+\t\t\telse {\n+\t\t\t\tif (*ep == '\\n') {\n+\t\t\t\t\t*ep = 0;\n+\t\t\t\t\torder[cnt] = cp;\n+\t\t\t\t}\n+\t\t\t\telse {\n+\t\t\t\t\torder[cnt] = xmalloc(ep-cp+1);\n+\t\t\t\t\tmemcpy(order[cnt], cp, ep-cp);\n+\t\t\t\t\torder[cnt][ep-cp] = 0;\n+\t\t\t\t}\n+\t\t\t\tcnt++;\n+\t\t\t}\n+\t\t\tif (ep < endp)\n+\t\t\t\tep++;\n+\t\t\tcp = ep;\n+\t\t}\n+\t\tif (pass == 0) {\n+\t\t\torder_cnt = cnt;\n+\t\t\torder = xmalloc(sizeof(*order) * cnt);\n+\t\t}\n+\t}\n+}\n+\n+struct pair_order {\n+\tstruct diff_filepair *pair;\n+\tint orig_order;\n+\tint order;\n+};\n+\n+static int match_order(const char *path)\n+{\n+\tint i;\n+\tchar p[PATH_MAX];\n+\n+\tfor (i = 0; i < order_cnt; i++) {\n+\t\tstrcpy(p, path);\n+\t\twhile (p[0]) {\n+\t\t\tchar *cp;\n+\t\t\tif (!fnmatch(order[i], p, 0))\n+\t\t\t\treturn i;\n+\t\t\tcp = strrchr(p, '/');\n+\t\t\tif (!cp)\n+\t\t\t\tbreak;\n+\t\t\t*cp = 0;\n+\t\t}\n+\t}\n+\treturn order_cnt;\n+}\n+\n+static int compare_pair_order(const void *a_, const void *b_)\n+{\n+\tstruct pair_order const *a, *b;\n+\ta = (struct pair_order const *)a_;\n+\tb = (struct pair_order const *)b_;\n+\tif (a->order != b->order)\n+\t\treturn a->order - b->order;\n+\treturn a->orig_order - b->orig_order;\n+}\n+\n+void diffcore_order(const char *orderfile)\n+{\n+\tstruct diff_queue_struct *q = &diff_queued_diff;\n+\tstruct pair_order *o = xmalloc(sizeof(*o) * q->nr);\n+\tint i;\n+\n+\tprepare_order(orderfile);\n+\tfor (i = 0; i < q->nr; i++) {\n+\t\to[i].pair = q->queue[i];\n+\t\to[i].orig_order = i;\n+\t\to[i].order = match_order(o[i].pair->two->path);\n+\t}\n+\tqsort(o, q->nr, sizeof(*o), compare_pair_order);\n+\tfor (i = 0; i < q->nr; i++)\n+\t\tq->queue[i] = o[i].pair;\n+\tfree(o);\n+\treturn;\n+}\n\n"},{"id":"4524","messageId":"20050604111755.50a34338.yuasa@hh.iij4u.or.jp","threadId":"726","inReplyTo":"7vhdgo2s7t.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 12/12] Optimize diff-tree -[CM] --stdin","fromName":"Yoichi Yuasa","fromEmail":"yuasa@hh.iij4u.or.jp","sentAt":"2005-06-04T02:17:55Z","receivedAt":"2005-06-04T02:17:55Z","isPatch":true,"sender":{"key":"yuasa@hh.iij4u.or.jp","avatar":null},"body":"Hi,\n\nOn Fri, 27 May 2005 15:56:38 -0700\nJunio C Hamano <junkio@cox.net> wrote:\n\n>\n\n<snip>\n\n> diff --git a/diff.c b/diff.c\n> --- a/diff.c\n> +++ b/diff.c\n> @@ -12,6 +12,7 @@ static const char *diff_opts = \"-pu\";\n>  static unsigned char null_sha1[20] = { 0, };\n>  \n>  static int reverse_diff;\n> +static int use_size_cache;\n>  \n>  static const char *external_diff(void)\n>  {\n> @@ -222,12 +223,60 @@ static int work_tree_matches(const char \n>  \treturn 1;\n>  }\n>  \n> +static struct sha1_size_cache {\n> +\tunsigned char sha1[20];\n> +\tunsigned long size;\n> +} **sha1_size_cache;\n> +static int sha1_size_cache_nr, sha1_size_cache_alloc;\n> +\n> +static struct sha1_size_cache *locate_size_cache(unsigned char *sha1,\n> +\t\t\t\t\t\t unsigned long size)\n> +{\n> +\tint first, last;\n> +\tstruct sha1_size_cache *e;\n> +\n> +\tfirst = 0;\n> +\tlast = sha1_size_cache_nr;\n> +\twhile (last > first) {\n> +\t\tint next = (last + first) >> 1;\n> +\t\te = sha1_size_cache[next];\n> +\t\tint cmp = memcmp(e->sha1, sha1, 20);\n\nI got build error in diff.c\nPlease apply the following patch.\n\nThanks,\n\nYoichi\n\nSigned-off-by: Yoichi Yuasa <yuasa@hh.iij4u.or.jp>\n\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -244,9 +244,9 @@ static struct sha1_size_cache *locate_si\n \tfirst = 0;\n \tlast = sha1_size_cache_nr;\n \twhile (last > first) {\n-\t\tint next = (last + first) >> 1;\n+\t\tint cmp, next = (last + first) >> 1;\n \t\te = sha1_size_cache[next];\n-\t\tint cmp = memcmp(e->sha1, sha1, 20);\n+\t\tcmp = memcmp(e->sha1, sha1, 20);\n \t\tif (!cmp)\n \t\t\treturn e;\n \t\tif (cmp < 0) {\n"},{"id":"4886","messageId":"7vpsusqxsy.fsf_-_@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"Pine.LNX.4.58.0505291151250.10545@ppc970.osdl.org","subject":"[PATCH] apply.c: tolerate diff from a dirty but unchanged path","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-06-11T23:27:25Z","receivedAt":"2005-06-11T23:27:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"I just noticed a problem with empty \"diff --git\" header a dirty\nbut otherwise unmodified path spits out.  The problem is related\nto an old topic:\n\n    Re: [PATCH] Do not show empty diff in diff-cache uncached.\n    >>>>> \"LT\" == Linus Torvalds <torvalds@osdl.org> writes:\n\n    LT> On Sat, 28 May 2005, Junio C Hamano wrote:\n    >> Recent diff-cache produces \"diff --git\" header internally before\n    >> calling the \"diff\" command, which results in an empty diff for\n    >> such a file, cluttering the output.  This patch fixes this.\n\n    LT> I actually _expect_ that \"git-diff-files\" will show\n    LT> files that don't match the index, even if they happen to\n    LT> have the exact content that the index points to. It's\n    LT> how I know whether the index is up-to-date or not.\n\nI now agree with the reasoning and think showing an empty \"diff\n--git\" header is a good thing.  I had something like this, while\nfixing a rename/copy bug:\n\n    ...\n     #endif\n\n     static void diff_resolve_rename_copy(void)\n    diff --git a/diffcore-break.c b/diffcore-break.c\n    diff --git a/diffcore-rename.c b/diffcore-rename.c\n    --- a/diffcore-rename.c\n    +++ b/diffcore-rename.c\n    @@ -249,8 +249,12 @@ void diffcore_rename(int detect_rename, \n    ...\n\nI touched diffcore-break.c and decided I should not change it so\nI reverted, which is why the empty \"diff --git\" for that file\nshows that the cache is dirty.  The problem is that currently,\n\"git-apply --stat\" seems to barf reading such a patch.\n\nHere is a proposed fix.\n\n------------\napply.c: tolerate diff from a dirty but otherwise unchanged cache.\n\nFor such an entry, \"diff --git\" line without anything else is\noutput, but the next \"diff --git\" header is not really treated\nas an unrecognized diff.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\ndiff --git a/apply.c b/apply.c\n--- a/apply.c\n+++ b/apply.c\n@@ -343,7 +343,7 @@ static int gitdiff_dissimilarity(const c\n  */\n static int gitdiff_unrecognized(const char *line, struct patch *patch)\n {\n-\treturn -1;\n+\treturn -2;\n }\n \n static char *git_header_name(char *line)\n@@ -453,8 +453,12 @@ static int parse_git_header(char *line, \n \t\t\tint oplen = strlen(p->str);\n \t\t\tif (len < oplen || memcmp(p->str, line, oplen))\n \t\t\t\tcontinue;\n-\t\t\tif (p->fn(line + oplen, patch) < 0)\n+\t\t\tswitch (p->fn(line + oplen, patch)) {\n+\t\t\tcase -1:\n \t\t\t\treturn offset;\n+\t\t\tcase -2:\n+\t\t\t\treturn -1;\n+\t\t\t}\n \t\t\tbreak;\n \t\t}\n \t}\n\n"},{"id":"4900","messageId":"Pine.LNX.4.58.0506120900200.2286@ppc970.osdl.org","threadId":"726","inReplyTo":"7vpsusqxsy.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] apply.c: tolerate diff from a dirty but unchanged path","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-06-12T16:14:28Z","receivedAt":"2005-06-12T16:14:28Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 11 Jun 2005, Junio C Hamano wrote:\n> \n> Here is a proposed fix.\n\nThis is wrong. It will make us reject all patches that don't end the magic \nsequence of \"@@ -\", ie it will make us reject pure renames etc.\n\nI'll fix it up,\n\n\t\tLinus\n"},{"id":"4902","messageId":"Pine.LNX.4.58.0506121003450.2286@ppc970.osdl.org","threadId":"726","inReplyTo":"Pine.LNX.4.58.0506120900200.2286@ppc970.osdl.org","subject":"Re: [PATCH] apply.c: tolerate diff from a dirty but unchanged path","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-06-12T17:05:56Z","receivedAt":"2005-06-12T17:05:56Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 12 Jun 2005, Linus Torvalds wrote:\n> \n> I'll fix it up,\n\nOne-liner fix checked in: we should ignore all git headers that are just a \nsingle line. A valid git header is _always_ multiple lines: either you \nhave the \"---/+++\" lines of a diff, or you have the old/new lines of a \nmode or name change.\n\nSo the\n\n\tif (git_hdr_len < 0)\n\ntest of the return value of \"parse_git_header()\" was changed into\n\n\tif (git_hdr_len <= len)\n\nand that cleanly solves the problem.\n\n\t\tLinus\n"},{"id":"4906","messageId":"7vll5fl901.fsf@assigned-by-dhcp.cox.net","threadId":"726","inReplyTo":"Pine.LNX.4.58.0506121003450.2286@ppc970.osdl.org","subject":"Re: [PATCH] apply.c: tolerate diff from a dirty but unchanged path","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-06-12T18:34:22Z","receivedAt":"2005-06-12T18:34:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":">>>>> \"LT\" == Linus Torvalds <torvalds@osdl.org> writes:\n\nLT> One-liner fix checked in: we should ignore all git headers that are just a \nLT> single line. A valid git header is _always_ multiple lines: either you \nLT> have the \"---/+++\" lines of a diff, or you have the old/new lines of a \nLT> mode or name change.\n\nYup.  I was about to say \"it always amazes me\" but by now I\nshould instead say \"I am used to see\" that your fix to my\ncrapola is always cleaner, simpler, and shorter.\n\nBy the way, just to help you sort the heap of recent patches\nfrom me, here is the list of patches from me not in your tree\nthat I think you _could_ consider.  Other patches I posted to\nthe list that have not been merged are mostly earlier attempts\nthat I consider you have already discarded; there _could_ be\nsomething I am forgetting, but if even I myself forget, they\nprobably do not matter ;-):\n\n------------\nThis is a fix for a bug that would embarrass me unless fixed.\n\n    Subject: [PATCH] Fix rename/copy when dealing with temporarily broken...\n    Date: Sat, 11 Jun 2005 20:55:20 -0700\n    Message-ID: <7vwtp0p6tz.fsf@assigned-by-dhcp.cox.net>\n\nA good test case is to run and compare these two in the GIT\nrepository.  The latter case is mishandled with unpatched code:\n\n    commit=7ef76925d9c19ef74874e1735e2436e56d0c4897\n    git-diff-tree -C $commit\n    git-diff-tree -B -C $commit\n\n------------\nThis one adjusts what the tutorial tells the user to expect to\nthe reality with the \"corrected\" -B behaviour we already have in\nyour tree.  We must have this before 1.0 in order not to confuse\nthe reader.  Unless you drop -B from git-status-script, that is.\n\n    Subject: [PATCH 5/4] Tutorial update to adjust for -B fix\n    Date: Fri, 03 Jun 2005 12:11:07 -0700\n    Message-ID: <7vd5r3l0hg.fsf_-_@assigned-by-dhcp.cox.net>\n\n------------\nThese two are enhancements that would help writing the\nultra-smart three-way merge that would look at not just merge\nbase and two heads, but the changes in the ancestry chain:\n\n    Message-ID: <7vpsut7k89.fsf@assigned-by-dhcp.cox.net>\n    Subject: [PATCH] diff-tree: --find-copies-harder\n    Date: Fri, 10 Jun 2005 18:31:02 -0700\n\n    Message-ID: <7vr7f8p6qu.fsf@assigned-by-dhcp.cox.net>\n    Subject: [PATCH] Add --diff-filter= output restriction to diff-* family.\n    Date: Sat, 11 Jun 2005 20:57:13 -0700\n\nI mentioned them in the reply to this message from you:\n\n    Date: Thu, 9 Jun 2005 08:15:52 -0700 (PDT)\n    Subject: Re: [PATCH 3/3] read-tree -m 3-way: handle more trivial merges\n    Message-ID: <Pine.LNX.4.58.0506090800580.2286@ppc970.osdl.org>\n\n    No, I think this is quite possibly wrong for several reasons.\n    ...\n    So I just need a little convincing that this is a good idea.\n\n------------\nThese are reworked \"help carrying forward local changes in 2-way\nfast forward\".\n\n    Message-ID: <7vd5qt7k2d.fsf@assigned-by-dhcp.cox.net>\n    Subject: [PATCH 1/3] Clean up read-tree two-way tests.\n    Date: Fri, 10 Jun 2005 18:34:34 -0700\n\n    Message-ID: <7v7jh17jzr.fsf@assigned-by-dhcp.cox.net>\n    Subject: [PATCH 2/3] read-tree --emu23.\n    Date: Fri, 10 Jun 2005 18:36:08 -0700\n\n    Message-ID: <7v1x797jx0.fsf@assigned-by-dhcp.cox.net>\n    Subject: [PATCH 3/3] read-tree: fix too strong index requirement #5ALT\n    Date: Fri, 10 Jun 2005 18:37:47 -0700\n\n    Message-ID: <7voeadxlvo.fsf@assigned-by-dhcp.cox.net>\n    Subject: [PATCH 4/3] Finish making --emu23 equivalent to pure 2-way merge.\n    Date: Sat, 11 Jun 2005 02:50:51 -0700\n\n    Message-ID: <7vfyvpxlqi.fsf@assigned-by-dhcp.cox.net>\n    Subject: [PATCH 5/3] read-tree: loosen too strict index requirements\n    Date: Sat, 11 Jun 2005 02:53:57 -0700\n\nThe first four implement --emu23, a two-tree fast forward\nemulated internally using the three-way mechanism.  This is to\nhelp cases where the user has local changes since the old head\nby not refusing to run in many cases straight 2-tree fast\nforward would refuse, and letting the user use the usual\nmerge-cache three-way merge postprocessing machinery to carry\nlocal changes forward instead.\n\nThe changes these four introduce do affect 3-way in 3 cases,\nwhich you objected, but this set has smaller impact than my\nearlier attempts.  It only resolves 3 extra cases the original\n3-way refuses to touch (my earlier one made it to resolve 3\ncases that involve removed paths as well, none of which this\nround touches, and leaves stages in the resulting index).  The\ncases new code internally resolves, instead of refusing to run,\nare:\n\n - only \"merged head\" created a new path, and the index happened\n   to have the same change; we resolve it internally by taking\n   the \"merged head\" and keep the index dirty if it was (#2ALT).\n\n - only \"our head\" created a new path, and the cache entry is\n   dirty (i.e. user has local changes in the work tree file); we\n   resolve it internally by taking \"our head\" and keep the index\n   dirty if it was (#3ALT).\n\n - both \"merged head\" and \"our head\" created a new path\n   identically, and the cache entry is dirty; we resolve it\n   internally by taking \"our head\" and keep the index dirty if\n   it was (#5ALT).\n\nI think your earlier objection of \"closing the door to the\nclever merge algorithm\" would not apply to these three cases.\nNote that --emu23 does not need 3-way code to resolve the #2ALT\ncase, but needs the other two cases not refused; the patch makes\nit resolve #2ALT case only to keep things symmetric with #3ALT\ncase --- otherwise users of 3-way merge would get confusing (but\nstill correct) results.\n\nThe last one [5/3] deals with the case where a path is only\nchanged in the merged head in 3-way case, and the index already\nhappens to have a version from the merged head (probably the\nuser was playing with a patch floating around in the mailing\nlist before initiating the merge).  In this case we can make the\n3-way merge succeed instead of refusing to run, and that is what\nthis patch is about.  This does not change the outcome of the\nmerge, and I think your \"closing the door\" objection would not\napply to it.\n\n------------\nThis is to enhance the git-*-script suite.  I do not mind too\nmuch if it stays outside, but I think it would be useful to\nother people as well, not just me.\n\n    Message-ID: <7vll5h7k5t.fsf@assigned-by-dhcp.cox.net>\n    Subject: [PATCH] Add script for patch submission via e-mail.\n    Date: Fri, 10 Jun 2005 18:32:30 -0700\n\n------------\nThis attempts to split the \"too big main() that does it all\"\nwhich you hated.  Purely cosmetic at this point but as you\npointed out it is a good discipline to help later maintenance to\nhave a set of smaller single-task functions.\n\n    Message-ID: <7vu0k56517.fsf_-_@assigned-by-dhcp.cox.net>\n    Subject: [PATCH] diff-stages: unuglify the too big main() function.\n    Date: Fri, 10 Jun 2005 18:44:36 -0700\n\n\n"}]}