{"thread":{"id":"688","subject":"updated design for the diff-raw format.","startedAt":"2005-05-21T23:12:57Z","lastAt":"2005-05-23T04:38:54Z","messageCount":33,"participants":["Junio C Hamano","Linus Torvalds","Thomas Glanzmann","Niklas Hoglund"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"3728","messageId":"7vwtpsdvgm.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":null,"subject":"updated design for the diff-raw format.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-21T23:12:57Z","receivedAt":"2005-05-21T23:12:57Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"After the introduction of the diff-core infrastructure, Linus\nand I had a couple more back-and-forth, but by accident these\nexchanges happened off the list.  I'll be replaying a couple of\nmessages to let people know about these because this change will\naffect all the Porcalain implementations.\n\n\n"},{"id":"3729","messageId":"7vr7g0dvbj.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"7vwtpsdvgm.fsf@assigned-by-dhcp.cox.net","subject":"Re: updated design for the diff-raw format.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-21T23:16:00Z","receivedAt":"2005-05-21T23:16:00Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"(first of the replayed exchange)\n\nTo: Linus Torvalds <torvalds@osdl.org>\nSubject: Re: [PATCH 3/3] Diff overhaul, adding the other half of copy\n detection.\nFrom: Junio C Hamano <junkio@cox.net>\nDate: Sat, 21 May 2005 10:56:06 -0700\nMessage-ID: <7v4qcwihu1.fsf@assigned-by-dhcp.cox.net>\n\nGIT_DIFF_OPTS=--unified=0 is good to me as well (GNU diffutils\n2.8.1).\n\nNow I think I am done with diff, except one thing.  And this is\nquite an incompatible change so I do not know how well it would\nwork in practice.  I am not even advocating this.  It is more\nlike me thinking aloud.\n\nThe diff-raw format we have been dealing with (sorry about '\\t'\nvs ' ' gotcha again) is internally enhanced by diff-core.  It\nfirst introduces entries for unmodified paths; '*' entries that\nhas the same mode/sha1 in from->to pair are such entries, and\nthat is what the change in the [PATCH 3/3] is about.\n\n    *100644->100644 blob 233a250...->66818b4... file0\n    *100755->100755 blob fc77389...->7b72d3d... file1\n    +100644 blob 233a250... file2\n    -100755 blob fc77389... file3\n    *100644->100644 blob 233a250...->233a250... file4\n\nThen diff-core internally extends the format to make things all\nlook like this ('*' and '-' are gone and each record acquires\nthe second path).\n\n    100644->100644 233a250...->66818b4... file0 file0\n    100755->100755 fc77389...->7b72d3d... file1 file1\n    ______->100644 _______...->233a250... file2 file2\n    100755->______ fc77389...->_______... file3 file3\n    100644->100644 233a250...->233a250... file4 file4\n\nInternally \"______\" above are represented with a separate flag\n(file_valid), and denotes the absense of either src or dst.\n\nThe diff-core is all about manipulating this type of list and\nchanging one such list into a different list.\n\nFor example, rename-edit of file3 into file2 is detected by\ndiffcore-rename module and these entries:\n\n    ______->100644 _______...->233a250... file2 file2\n    100755->______ fc77389...->_______... file3 file3\n\nbecome:\n\n    100755->100644 fc77389...->233a250... file3 file2\n\nWhat the diffcore-pickaxe does can also be explained clearly\nwith this model.  It takes such a list and works as a \"grep\".\n\nOnce we start to think of it this way, it becomes quite tempting\nto change the diff-raw format to actually match the above\nconcept.  Meaning, (1) drop the operation letter +/-/*\n(inferrable by looking at the both sides of ->); (2) drop\nblob/tree (inferrable it from mode); (3) give two paths (usually\nthey are the same paths); (4) and perhaps replace '->' with the\nsame column separator.  Like this:\n\n    100644 100644 233a250... 66818b4... file0 file0\n    100755 100755 fc77389... 7b72d3d... file1 file1\n    ______ 100644 _______... 233a250... file2 file2\n    100755 ______ fc77389... _______... file3 file3\n    100644 100644 233a250... 233a250... file4 file4\n\nAgain, I am not even advocating this.  It is more like me\nstill thinking aloud.\n\n\n\n\n\n\n"},{"id":"3730","messageId":"7vll68dv8y.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"7vwtpsdvgm.fsf@assigned-by-dhcp.cox.net","subject":"Re: updated design for the diff-raw format.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-21T23:17:33Z","receivedAt":"2005-05-21T23:17:33Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"(second of the replayed message, with blessing from Linus)\n\nDate: Sat, 21 May 2005 11:24:31 -0700 (PDT)\nFrom: Linus Torvalds <torvalds@osdl.org>\nTo: Junio C Hamano <junkio@cox.net>\nSubject: Re: [PATCH 3/3] Diff overhaul, adding the other half of copy detection.\nMessage-ID: <Pine.LNX.4.58.0505211107160.2206@ppc970.osdl.org>\n\nOn Sat, 21 May 2005, Junio C Hamano wrote:\n> \n> Once we start to think of it this way, it becomes quite tempting\n> to change the diff-raw format to actually match the above\n> concept.\n\nI agree, and I was going to suggest changing the \"raw\" diff output for all\nthe same reasons. So I think you should do it, as the old format was based\non not really knowing where this all would take us. I think your proposed\nformat is visually nicer, and it's obviously more flexible.\n\nSmall suggestion on termination of the thing:\n - add a \"inter_name_termination\" variable, which defaults to '\\t' (the \n   same way \"line_termination\" defaults to '\\n')\n - make \"-z\" set both \"inter_name_termination\" _and_ \"line_termination\" to \n   0.\n - make the spacing be fixed (and add a test for it, so that there is \n   never any confusion): regular spaces between the non-file-names, and \n   \"inter_name_termination\" before the filenames, and \"line_termination\" \n   after the second filename.\n\nThis has a few results:\n\n - the default output is perfectly readable, if long\n\n - \"cut\" (which defaults to TAB delimeter) can directly pick up the\n   three fields from a line: \"state\", \"file1\" and \"file2\"\n\n - even if you use the \"readable\" output (as opposed to the \"-z\" \n   machine-readable one), spaces in filenames are unambiguous, and we only \n   screw up on TAB and NL.\n\n   Spaces in names are normal in many things. NL/TAB really _are_ unusual, \n   and I could imagine that some porcelain could actually disallow them \n   (and if that happens, we could support that by add a flag to\n   \"update-cache\" to refuse to touch such files, the same way we refuse \n   non-canonical filenames now).\n\n - the -z flag results in fairly unreadable output, but is at least \n   totally parseable with all filename characters allowed.\n\nWith that in place, the new format would be a lot _easier_ to parse than \nthe old one, I think. And will be more flexible, and since it's a \nfixed-column format, it's actually pretty readable for humans too, as long \nas the terminal line is wide enough.\n\n>     100644 100644 233a250... 66818b4... file0 file0\n>     100755 100755 fc77389... 7b72d3d... file1 file1\n>     ______ 100644 _______... 233a250... file2 file2\n>     100755 ______ fc77389... _______... file3 file3\n>     100644 100644 233a250... 233a250... file4 file4\n> \n> Again, I am not even advocating this.  It is more like me\n> still thinking aloud.\n\nNo, I think it's really good. The _one_ thing I'd do is to maybe put a \nspecial character at the beginning of the line, so that \"diff-helper\" has \nan ever easier time to know whether it should care or not. Something that \nnormally wouldn't show up at the beginning of a line, like ':'.\n\n(This would have the secondary advantage that yuou could run \"diff-helper\"  \nmultiple times and not care whether it was already expanded or not. Right\nnow that is impossible: diff-helper can't know the difference between an\nalready-expanded diff that has a line that begins with a '+' or '-' and a\neleted/new object line).\n\n\t\tLinus\n\n\n\n\n"},{"id":"3731","messageId":"7vhdgwdv7d.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"7vwtpsdvgm.fsf@assigned-by-dhcp.cox.net","subject":"Re: updated design for the diff-raw format.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-21T23:18:30Z","receivedAt":"2005-05-21T23:18:30Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"(third of the replayed messages)\n\nTo: Linus Torvalds <torvalds@osdl.org>\nSubject: Re: [PATCH 3/3] Diff overhaul, adding the other half of copy\n detection.\nFrom: Junio C Hamano <junkio@cox.net>\nDate: Sat, 21 May 2005 13:36:25 -0700\nMessage-ID: <7vfywggvue.fsf@assigned-by-dhcp.cox.net>\n\n>>>>> \"LT\" == Linus Torvalds <torvalds@osdl.org> writes:\n\nLT> On Sat, 21 May 2005, Junio C Hamano wrote:\n>> \n>> Once we start to think of it this way, it becomes quite tempting\n>> to change the diff-raw format to actually match the above\n>> concept.\n\nLT> I agree, and I was going to suggest changing the \"raw\" diff output for all\nLT> the same reasons. So I think you should do it, as the old format was based\nLT> on not really knowing where this all would take us. I think your proposed\nLT> format is visually nicer, and it's obviously more flexible.\n\nLT> Small suggestion on termination of the thing:\nLT>  - add a \"inter_name_termination\" variable, which defaults to '\\t' (the \nLT>    same way \"line_termination\" defaults to '\\n')\nLT>  - make \"-z\" set both \"inter_name_termination\" _and_ \"line_termination\" to \nLT>    0.\nLT>  - make the spacing be fixed (and add a test for it, so that there is \nLT>    never any confusion): regular spaces between the non-file-names, and \nLT>    \"inter_name_termination\" before the filenames, and \"line_termination\" \nLT>    after the second filename.\n\nI am debating myself if I wanted to add this to the above list:\n\n     - omit the inter_name_termination and second path if both\n       paths are the same, only when doing human-readable\n       (i.e. inter_name_termination != line_termination).\n\nI'm not going to do this immediately though, for two reasons.\n\nSomehow I failed to CC the GIT list the message you are\nresponding to.  Discussing a change with an impact of this scale\nneeds to be taken public before going further, so with your\npermission I would like to repost both my original (\"Once we\nstart to think of it this way...\")  and your response to the GIT\nlist first.  At least I feel that Petr needs to be in the loop\nabout this one.\n\nAnother reason is that, as I said, I still have problems about\nthe diffcore interface, namely the lack of interface for the\napplications to ask diffcore what the final outcome is.  The\n\"diff-tree not being to omit its header output when pickaxe says\nthe result is empty\" problem is primarily what bothers me, but I\nthink we want a more generic interface for the application to\ninspect the result (not just emptiness check), probably before\nstarting to feed the resulting diff list to the external diff.\n\nNote that this interface needs to be inspection only---if the\napplication wants to further manipulate the result, then we\nshould extend list of diffcore transformations called from\ndiff_flush().  Which takes me to another point --- maybe the\nlist of diffcore transformations called from diff_flush() should\nbe made stackable, like streams.\n\n\n\n\n\n\n"},{"id":"3732","messageId":"7vd5rkdv5p.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"7vwtpsdvgm.fsf@assigned-by-dhcp.cox.net","subject":"Re: updated design for the diff-raw format.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-21T23:19:30Z","receivedAt":"2005-05-21T23:19:30Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"(fourth of the replayed messages)\n\nDate: Sat, 21 May 2005 15:03:06 -0700 (PDT)\nFrom: Linus Torvalds <torvalds@osdl.org>\nTo: Junio C Hamano <junkio@cox.net>\nSubject: Re: [PATCH 3/3] Diff overhaul, adding the other half of copy detection.\nMessage-ID: <Pine.LNX.4.58.0505211452180.2206@ppc970.osdl.org>\n\nOn Sat, 21 May 2005, Junio C Hamano wrote:\n> \n>      - omit the inter_name_termination and second path if both\n>        paths are the same, only when doing human-readable\n>        (i.e. inter_name_termination != line_termination).\n\nHmm. I guess that's fair enough, since it's still easily parseable even if\nyou don't want to use the -z version in scripts (as many tools are better\nat handling line-terminated stuff than handling zero-terminations).\n\nIt might become another flag too, if somebody ends up caring.\n\n> Somehow I failed to CC the GIT list the message you are\n> responding to.  Discussing a change with an impact of this scale\n> needs to be taken public before going further, so with your\n> permission I would like to repost both my original (\"Once we\n> start to think of it this way...\")  and your response to the GIT\n> list first.  At least I feel that Petr needs to be in the loop\n> about this one.\n\nSure. Although I doube people use the raw diff output except to (a) feed \nto diff-helper or (b) check that it's non-empty.\n\nBut absolutely, post the previous (and this) one.\n\n> Another reason is that, as I said, I still have problems about the\n> diffcore interface, namely the lack of interface for the applications to\n> ask diffcore what the final outcome is.  The \"diff-tree not being to\n> omit its header output when pickaxe says the result is empty\" problem is\n> primarily what bothers me, but I think we want a more generic interface\n> for the application to inspect the result (not just emptiness check),\n> probably before starting to feed the resulting diff list to the external\n> diff.\n\nWhy not just have a \"is there anything pending\" query before doing the \nflush? And always put _everything_ in the pending category, regardless of \nwhether detect_rename/copy is in effect (but if it's not in effect, then \nflushing the pending queue is obviously just a \"go through it and flush \nit\" without any other complexity).\n\nIn other words, there would be four clear stages to this:\n\n 1) diff_setup()\n\tremove \"detect_rename\" and \"diff_score_opt\" and \"reverse_diff\" \n\tfrom this, since they are irrelevant until you _show_ the diffs\n\n 2) diff_queue() *n\n\ttell diff-core about the files we are going to diff\n\n 3) diff_detect_rename()\n\tthis is what takes the \"detect_rename\" flag and \"diff_score_opt\", \n\tand walks the list of diffs and potentially changes them into \n\t\"rename\" and \"copy\" diffs.\n\n 4) diff_flush()\n\tthis just prints out the result (either as a raw diff or as \n\tpatches). This takes the \"reverse_diff\" flag that was removed from \n\tdiff_setup().\n\nand then you can always query the state of the diff tree before stage 3 or \nbefore stage 4.\n\nIn fact, there's no reason not to even _change_ the diff-queue in magic \nways before (3) or (4) depending on what you want to do. For example, your \n\"-S\" thing might want to do it's thing between stages (3) and (4).\n\n\t\tLinus\n\n\n\n\n"},{"id":"3735","messageId":"7v1x80dluj.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"7vwtpsdvgm.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] Prepare diffcore interface for diff-tree header supression.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-22T02:40:36Z","receivedAt":"2005-05-22T02:40:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This does not actually supress the extra headers when pickaxe is\nused, but prepares enough support for diff-tree to implement it.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff-cache.c       |   10 ++++++----\ndiff-files.c       |   10 ++++++----\ndiff-helper.c      |   13 +++++++------\ndiff-tree.c        |   26 ++++++++++++++++++--------\ndiff.c             |   43 ++++++++++++++++++++++---------------------\ndiff.h             |   12 +++++++-----\ndiffcore-pickaxe.c |    3 ++-\ndiffcore-rename.c  |    4 ++--\ndiffcore.h         |    3 +--\n9 files changed, 71 insertions(+), 53 deletions(-)\n\ndiff --git a/diff-cache.c b/diff-cache.c\n--- a/diff-cache.c\n+++ b/diff-cache.c\n@@ -214,9 +214,7 @@ int main(int argc, char **argv)\n \tif (argc != 2 || get_sha1(argv[1], tree_sha1))\n \t\tusage(diff_cache_usage);\n \n-\tdiff_setup(detect_rename, diff_score_opt, pickaxe,\n-\t\t   reverse_diff, (generate_patch ? -1 : line_termination),\n-\t\t   NULL, 0);\n+\tdiff_setup(reverse_diff, (generate_patch ? -1 : line_termination));\n \n \tmark_merge_entries();\n \n@@ -227,6 +225,10 @@ int main(int argc, char **argv)\n \t\tdie(\"unable to read tree object %s\", argv[1]);\n \n \tret = diff_cache(active_cache, active_nr);\n-\tdiff_flush();\n+\tif (detect_rename)\n+\t\tdiff_detect_rename(detect_rename, diff_score_opt);\n+\tif (pickaxe)\n+\t\tdiff_pickaxe(pickaxe);\n+\tdiff_flush(NULL, 0);\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@@ -92,9 +92,7 @@ int main(int argc, char **argv)\n \t\texit(1);\n \t}\n \n-\tdiff_setup(detect_rename, diff_score_opt, pickaxe,\n-\t\t   reverse_diff, (generate_patch ? -1 : line_termination),\n-\t\t   NULL, 0);\n+\tdiff_setup(reverse_diff, (generate_patch ? -1 : line_termination));\n \n \tfor (i = 0; i < entries; i++) {\n \t\tstruct stat st;\n@@ -136,6 +134,10 @@ int main(int argc, char **argv)\n \t\tshow_modified(oldmode, mode, ce->sha1, null_sha1,\n \t\t\t      ce->name);\n \t}\n-\tdiff_flush();\n+\tif (detect_rename)\n+\t\tdiff_detect_rename(detect_rename, diff_score_opt);\n+\tif (pickaxe)\n+\t\tdiff_pickaxe(pickaxe);\n+\tdiff_flush(NULL, 0);\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@@ -127,10 +127,7 @@ int main(int ac, const char **av) {\n \t}\n \t/* the remaining parameters are paths patterns */\n \n-\tdiff_setup(detect_rename, diff_score_opt, pickaxe,\n-\t\t   reverse, (generate_patch ? -1 : line_termination),\n-\t\t   av+1, ac-1);\n-\n+\tdiff_setup(reverse, (generate_patch ? -1 : line_termination));\n \twhile (1) {\n \t\tint status;\n \t\tread_line(&sb, stdin, line_termination);\n@@ -138,11 +135,15 @@ int main(int ac, const char **av) {\n \t\t\tbreak;\n \t\tstatus = parse_diff_raw_output(sb.buf);\n \t\tif (status) {\n-\t\t\tdiff_flush();\n+\t\t\tdiff_flush(av+1, ac-1);\n \t\t\tprintf(\"%s%c\", sb.buf, line_termination);\n \t\t}\n \t}\n \n-\tdiff_flush();\n+\tif (detect_rename)\n+\t\tdiff_detect_rename(detect_rename, diff_score_opt);\n+\tif (pickaxe)\n+\t\tdiff_pickaxe(pickaxe);\n+\tdiff_flush(av+1, ac-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@@ -267,16 +267,28 @@ static int diff_tree_sha1(const unsigned\n \treturn retval;\n }\n \n+static void call_diff_setup(void)\n+{\n+\tdiff_setup(reverse_diff, (generate_patch ? -1 : line_termination));\n+}\n+\n+static void call_diff_flush(void)\n+{\n+\tif (detect_rename)\n+\t\tdiff_detect_rename(detect_rename, diff_score_opt);\n+\tif (pickaxe)\n+\t\tdiff_pickaxe(pickaxe);\n+\tdiff_flush(NULL, 0);\n+}\n+\n static int diff_tree_sha1_top(const unsigned char *old,\n \t\t\t      const unsigned char *new, const char *base)\n {\n \tint ret;\n \n-\tdiff_setup(detect_rename, diff_score_opt, pickaxe,\n-\t\t   reverse_diff, (generate_patch ? -1 : line_termination),\n-\t\t   NULL, 0);\n+\tcall_diff_setup();\n \tret = diff_tree_sha1(old, new, base);\n-\tdiff_flush();\n+\tcall_diff_flush();\n \treturn ret;\n }\n \n@@ -286,15 +298,13 @@ static int diff_root_tree(const unsigned\n \tvoid *tree;\n \tunsigned long size;\n \n-\tdiff_setup(detect_rename, diff_score_opt, pickaxe,\n-\t\t   reverse_diff, (generate_patch ? -1 : line_termination),\n-\t\t   NULL, 0);\n+\tcall_diff_setup();\n \ttree = read_object_with_reference(new, \"tree\", &size, NULL);\n \tif (!tree)\n \t\tdie(\"unable to read root tree (%s)\", sha1_to_hex(new));\n \tretval = diff_tree(\"\", 0, tree, size, base);\n \tfree(tree);\n-\tdiff_flush();\n+\tcall_diff_flush();\n \treturn retval;\n }\n \ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -12,13 +12,10 @@\n static const char *diff_opts = \"-pu\";\n static unsigned char null_sha1[20] = { 0, };\n \n-static int detect_rename;\n static int reverse_diff;\n static int diff_raw_output = -1;\n static const char **pathspec;\n static int speccnt;\n-static const char *pickaxe;\n-static int minimum_score;\n \n static const char *external_diff(void)\n {\n@@ -512,21 +509,13 @@ int diff_scoreopt_parse(const char *opt)\n \treturn MAX_SCORE * num / scale;\n }\n \n-void diff_setup(int detect_rename_, int minimum_score_,\n-\t\tconst char *pickaxe_,\n-\t\tint reverse_diff_, int diff_raw_output_,\n-\t\tconst char **pathspec_, int speccnt_)\n+void diff_setup(int reverse_diff_, int diff_raw_output_)\n {\n-\tdetect_rename = detect_rename_;\n \treverse_diff = reverse_diff_;\n-\tpathspec = pathspec_;\n \tdiff_raw_output = diff_raw_output_;\n-\tspeccnt = speccnt_;\n-\tminimum_score = minimum_score_ ? : DEFAULT_MINIMUM_SCORE;\n-\tpickaxe = pickaxe_;\n }\n \n-static struct diff_queue_struct queued_diff;\n+struct diff_queue_struct diff_queued_diff;\n \n struct diff_filepair *diff_queue(struct diff_queue_struct *queue,\n \t\t\t\t  struct diff_filespec *one,\n@@ -636,15 +625,27 @@ static void diff_flush_one(struct diff_f\n \t\tdiff_flush_patch(p);\n }\n \n-void diff_flush(void)\n+int diff_queue_is_empty(void)\n {\n-\tstruct diff_queue_struct *q = &queued_diff;\n+\tstruct diff_queue_struct *q = &diff_queued_diff;\n \tint i;\n \n-\tif (detect_rename)\n-\t\tdiff_detect_rename(q, detect_rename, minimum_score);\n-\tif (pickaxe)\n-\t\tdiff_pickaxe(q, pickaxe);\n+\tfor (i = 0; i < q->nr; i++) {\n+\t\tstruct diff_filepair *p = q->queue[i];\n+\t\tif (!identical(p->one, p->two))\n+\t\t\treturn 0;\n+\t}\n+\treturn 1;\n+}\n+\n+void diff_flush(const char **pathspec_, int speccnt_)\n+{\n+\tstruct diff_queue_struct *q = &diff_queued_diff;\n+\tint i;\n+\n+\tpathspec = pathspec_;\n+\tspeccnt = speccnt_;\n+\n \tfor (i = 0; i < q->nr; i++)\n \t\tdiff_flush_one(q->queue[i]);\n \n@@ -693,7 +694,7 @@ void diff_addremove(int addremove, unsig\n \tif (addremove != '-')\n \t\tfill_filespec(two, sha1, mode);\n \n-\tdiff_queue(&queued_diff, one, two);\n+\tdiff_queue(&diff_queued_diff, one, two);\n }\n \n void diff_change(unsigned old_mode, unsigned new_mode,\n@@ -716,7 +717,7 @@ void diff_change(unsigned old_mode, unsi\n \tfill_filespec(one, old_sha1, old_mode);\n \tfill_filespec(two, new_sha1, new_mode);\n \n-\tdiff_queue(&queued_diff, one, two);\n+\tdiff_queue(&diff_queued_diff, one, two);\n }\n \n void diff_unmerge(const char *path)\ndiff --git a/diff.h b/diff.h\n--- a/diff.h\n+++ b/diff.h\n@@ -19,11 +19,13 @@ extern void diff_unmerge(const char *pat\n \n extern int diff_scoreopt_parse(const char *opt);\n \n-extern void diff_setup(int detect_rename, int minimum_score,\n-\t\t       const char *pickaxe,\n-\t\t       int reverse, int raw_output,\n-\t\t       const char **spec, int cnt);\n+extern void diff_setup(int reverse, int diff_raw_output);\n \n-extern void diff_flush(void);\n+extern void diff_detect_rename(int, int);\n+extern void diff_pickaxe(const char *);\n+\n+extern int diff_queue_is_empty(void);\n+\n+extern void diff_flush(const char **, int);\n \n #endif /* DIFF_H */\ndiff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c\n--- a/diffcore-pickaxe.c\n+++ b/diffcore-pickaxe.c\n@@ -21,8 +21,9 @@ static int contains(struct diff_filespec\n \treturn 0;\n }\n \n-void diff_pickaxe(struct diff_queue_struct *q, const char *needle)\n+void diff_pickaxe(const char *needle)\n {\n+\tstruct diff_queue_struct *q = &diff_queued_diff;\n \tunsigned long len = strlen(needle);\n \tint i;\n \tstruct diff_queue_struct outq;\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -224,10 +224,10 @@ static int needs_to_stay(struct diff_que\n \treturn 0;\n }\n \n-void diff_detect_rename(struct diff_queue_struct *q,\n-\t\t\tint detect_rename,\n+void diff_detect_rename(int detect_rename,\n \t\t\tint minimum_score)\n {\n+\tstruct diff_queue_struct *q = &diff_queued_diff;\n \tstruct diff_queue_struct outq;\n \tstruct diff_rename_pool created, deleted, stay;\n \tstruct diff_rename_pool *(srcs[2]);\ndiff --git a/diffcore.h b/diffcore.h\n--- a/diffcore.h\n+++ b/diffcore.h\n@@ -52,10 +52,9 @@ struct diff_queue_struct {\n \tint nr;\n };\n \n+extern struct diff_queue_struct diff_queued_diff;\n extern struct diff_filepair *diff_queue(struct diff_queue_struct *,\n \t\t\t\t\tstruct diff_filespec *,\n \t\t\t\t\tstruct diff_filespec *);\n-extern void diff_detect_rename(struct diff_queue_struct *, int, int);\n-extern void diff_pickaxe(struct diff_queue_struct *, const char *);\n \n #endif\n------------------------------------------------\n\n"},{"id":"3736","messageId":"7vvf5cc779.fsf_-_@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"7v1x80dluj.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] The diff-raw format updates.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-22T02:42:18Z","receivedAt":"2005-05-22T02:42:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Update the diff-raw format as Linus and I discussed, except that\nit does not use sequence of underscore '_' letters to express\nnonexistence.  All '0' mode is used for that purpose instead.\n\nThe new diff-raw format can express rename/copy, and the earlier\nrestriction that -M and -C _must_ be used with the patch format\noutput is no longer necessary.  The patch makes -M and -C flags\nindependent of -p flag, so you need to say git-whatchanged -M -p\nto get the diff/patch format.\n\nUpdated are both documentations and tests.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\nDocumentation/diff-format.txt    |   29 +++--\nDocumentation/git-diff-cache.txt |    4 \nDocumentation/git-diff-files.txt |    4 \nDocumentation/git-diff-tree.txt  |    5 \ndiff-cache.c                     |   12 --\ndiff-files.c                     |   12 --\ndiff-helper.c                    |  153 ++++++++++++----------------\ndiff-tree.c                      |   13 +-\ndiff.c                           |  132 +++++++++++++-----------\ndiff.h                           |   12 ++\ndiffcore-pickaxe.c               |    6 -\ndiffcore-rename.c                |   20 +--\ndiffcore.h                       |    2 \nt/t0000-basic.sh                 |   16 +-\nt/t4002-diff-basic.sh            |  209 +++++++++++++++++++--------------------\nt/t4003-diff-rename-1.sh         |    6 -\n16 files changed, 322 insertions(+), 313 deletions(-)\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@@ -16,25 +16,30 @@ git-diff-tree [-r] <tree-ish-1> <tree-is\n git-diff-files [<pattern>...]::\n         compares the cache and the files on the filesystem.\n \n-The following desription uses \"old\" and \"new\" to mean those\n-compared entities.\n \n-For files in old but not in new (i.e. removed):\n+An output line is formatted this way:\n \n-    -<mode> \\t <type> \\t <object> \\t <path>\n+  ':' <mode> ' ' <mode> ' ' <sha1> ' ' <sha1> I <path> I <path> L\n \n-For files not in old but in new (i.e. added):\n+By default, I and L are '\\t' and '\\n' respectively.  When '-z'\n+flag is in effect, both I and L are '\\0'.\n \n-    +<mode> \\t <type> \\t <object> \\t <path>\n+In each <mode>, <sha1> and <path> pair, left hand side describes\n+the left hand side of what is being compared (<tree-ish> in\n+git-diff-cache, <tree-ish-1> in git-diff-tree, cache contents in\n+git-diff-files).  Non-existence is shown by having 000000 in the\n+<mode> column.  That is, 000000 appears as the first <mode> for\n+newly created files, and as the second <mode> for deleted files.\n \n-For files that differ:\n+Usually two <path> are the same.  When rename/copy detection is\n+used, however, an \"create\" and another \"delete\" records can be\n+merged into a single record that has two <path>, old name and\n+new name.\n \n-    *<old-mode>-><new-mode> \\t <type> \\t <old-sha1>-><new-sha1> \\t <path>\n+<sha1> is shown as all 0's if new is a file on the filesystem\n+and it is out of sync with the cache.  Example:\n \n-<new-sha1> is shown as all 0's if new is a file on the\n-filesystem and it is out of sync with the cache.  Example:\n-\n-  *100644->100644 blob    5be4a4.......->000000.......      file.c\n+  :100644 100644 5be4a4...... 000000......    file.c    file.c\n \n \n Generating patches with -p\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@@ -34,10 +34,10 @@ OPTIONS\n \t\\0 line termination on output\n \n -M::\n-\tDetect renames; implies -p.\n+\tDetect renames.\n \n -C::\n-\tDetect copies as well as renames; implies -p.\n+\tDetect copies as well as renames.\n \n -S<string>::\n \tLook for differences that contains the change in <string>.\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@@ -30,10 +30,10 @@ OPTIONS\n \tOutput diff in reverse.\n \n -M::\n-\tDetect renames; implies -p.\n+\tDetect renames.\n \n -C::\n-\tDetect copies as well as renames; implies -p.\n+\tDetect copies as well as renames.\n \n -S<string>::\n \tLook for differences that contains the change in <string>.\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@@ -34,11 +34,10 @@ OPTIONS\n \tgit-diff-tree, this flag implies '-r' as well.\n \n -M::\n-\tDetect renames; implies -p, in turn implying also '-r'.\n+\tDetect renames.\n \n -C::\n-\tDetect copies as well as renames; implies -p, in turn\n-\timplying also '-r'.\n+\tDetect copies as well as renames.\n \n -R::\n \tOutput diff in reverse.\ndiff --git a/diff-cache.c b/diff-cache.c\n--- a/diff-cache.c\n+++ b/diff-cache.c\n@@ -2,9 +2,8 @@\n #include \"diff.h\"\n \n static int cached_only = 0;\n-static int generate_patch = 0;\n+static int diff_output_format = DIFF_FORMAT_HUMAN;\n static int match_nonexisting = 0;\n-static int line_termination = '\\n';\n static int detect_rename = 0;\n static int reverse_diff = 0;\n static int diff_score_opt = 0;\n@@ -174,22 +173,21 @@ int main(int argc, char **argv)\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"-p\")) {\n-\t\t\tgenerate_patch = 1;\n+\t\t\tdiff_output_format = DIFF_FORMAT_PATCH;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strncmp(arg, \"-M\", 2)) {\n-\t\t\tgenerate_patch = detect_rename = 1;\n+\t\t\tdetect_rename = 1;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(arg);\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strncmp(arg, \"-C\", 2)) {\n-\t\t\tgenerate_patch = 1;\n \t\t\tdetect_rename = 2;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(arg);\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"-z\")) {\n-\t\t\tline_termination = '\\0';\n+\t\t\tdiff_output_format = DIFF_FORMAT_MACHINE;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"-R\")) {\n@@ -214,7 +212,7 @@ int main(int argc, char **argv)\n \tif (argc != 2 || get_sha1(argv[1], tree_sha1))\n \t\tusage(diff_cache_usage);\n \n-\tdiff_setup(reverse_diff, (generate_patch ? -1 : line_termination));\n+\tdiff_setup(reverse_diff, diff_output_format);\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@@ -9,8 +9,7 @@\n static const char *diff_files_usage =\n \"git-diff-files [-p] [-q] [-r] [-z] [-M] [-C] [-R] [-S<string>] [paths...]\";\n \n-static int generate_patch = 0;\n-static int line_termination = '\\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_score_opt = 0;\n@@ -57,7 +56,7 @@ int main(int argc, char **argv)\n \n \twhile (1 < argc && argv[1][0] == '-') {\n \t\tif (!strcmp(argv[1], \"-p\"))\n-\t\t\tgenerate_patch = 1;\n+\t\t\tdiff_output_format = DIFF_FORMAT_PATCH;\n \t\telse if (!strcmp(argv[1], \"-q\"))\n \t\t\tsilent = 1;\n \t\telse if (!strcmp(argv[1], \"-r\"))\n@@ -65,19 +64,18 @@ int main(int argc, char **argv)\n \t\telse if (!strcmp(argv[1], \"-s\"))\n \t\t\t; /* no-op */\n \t\telse if (!strcmp(argv[1], \"-z\"))\n-\t\t\tline_termination = 0;\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\telse if (!strcmp(argv[1], \"-S\"))\n \t\t\tpickaxe = argv[1] + 2;\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 = generate_patch = 1;\n+\t\t\tdetect_rename = 1;\n \t\t}\n \t\telse if (!strncmp(argv[1], \"-C\", 2)) {\n \t\t\tdiff_score_opt = diff_scoreopt_parse(argv[1]);\n \t\t\tdetect_rename = 2;\n-\t\t\tgenerate_patch = 1;\n \t\t}\n \t\telse\n \t\t\tusage(diff_files_usage);\n@@ -92,7 +90,7 @@ int main(int argc, char **argv)\n \t\texit(1);\n \t}\n \n-\tdiff_setup(reverse_diff, (generate_patch ? -1 : line_termination));\n+\tdiff_setup(reverse_diff, diff_output_format);\n \n \tfor (i = 0; i < entries; i++) {\n \t\tstruct stat st;\ndiff --git a/diff-helper.c b/diff-helper.c\n--- a/diff-helper.c\n+++ b/diff-helper.c\n@@ -8,88 +8,51 @@\n \n static int detect_rename = 0;\n static int diff_score_opt = 0;\n-static int generate_patch = 1;\n static const char *pickaxe = NULL;\n+static int diff_output_style = DIFF_FORMAT_PATCH;\n+static int line_termination = '\\n';\n+static int inter_name_termination = '\\t';\n \n-static int parse_oneside_change(const char *cp, int *mode,\n-\t\t\t\tunsigned char *sha1, char *path)\n+static int parse_diff_raw(char *buf1, char *buf2, char *buf3)\n {\n-\tint ch, m;\n+\tchar old_path[PATH_MAX];\n+\tunsigned char old_sha1[20], new_sha1[20];\n+\tchar *ep;\n+\tchar *cp = buf1;\n+\tint ch, old_mode, new_mode;\n \n-\tm = 0;\n-\twhile ((ch = *cp) && '0' <= ch && ch <= '7') {\n-\t\tm = (m << 3) | (ch - '0');\n+\told_mode = new_mode = 0;\n+\twhile ((ch = *cp) && ('0' <= ch && ch <= '7')) {\n+\t\told_mode = (old_mode << 3) | (ch - '0');\n+\t\tcp++;\n+\t}\n+\tif (*cp++ != ' ')\n+\t\treturn -1;\n+\twhile ((ch = *cp) && ('0' <= ch && ch <= '7')) {\n+\t\tnew_mode = (new_mode << 3) | (ch - '0');\n \t\tcp++;\n \t}\n-\t*mode = m;\n-\tif (strncmp(cp, \"\\tblob\\t\", 6) && strncmp(cp, \" blob \", 6) &&\n-\t    strncmp(cp, \"\\ttree\\t\", 6) && strncmp(cp, \" tree \", 6))\n+\tif (*cp++ != ' ')\n \t\treturn -1;\n-\tcp += 6;\n-\tif (get_sha1_hex(cp, sha1))\n+\tif (get_sha1_hex(cp, old_sha1))\n \t\treturn -1;\n \tcp += 40;\n-\tif ((*cp != '\\t') && *cp != ' ')\n+\tif (*cp++ != ' ')\n \t\treturn -1;\n-\tstrcpy(path, ++cp);\n-\treturn 0;\n-}\n-\n-static int parse_diff_raw_output(const char *buf)\n-{\n-\tchar path[PATH_MAX];\n-\tunsigned char old_sha1[20], new_sha1[20];\n-\tconst char *cp = buf;\n-\tint ch, old_mode, new_mode;\n-\n-\tswitch (*cp++) {\n-\tcase 'U':\n-\t\tdiff_unmerge(cp + 1);\n-\t\tbreak;\n-\tcase '+':\n-\t\tif (parse_oneside_change(cp, &new_mode, new_sha1, path))\n-\t\t\treturn -1;\n-\t\tdiff_addremove('+', new_mode, new_sha1, path, NULL);\n-\t\tbreak;\n-\tcase '-':\n-\t\tif (parse_oneside_change(cp, &old_mode, old_sha1, path))\n-\t\t\treturn -1;\n-\t\tdiff_addremove('-', old_mode, old_sha1, path, NULL);\n-\t\tbreak;\n-\tcase '*':\n-\t\told_mode = new_mode = 0;\n-\t\twhile ((ch = *cp) && ('0' <= ch && ch <= '7')) {\n-\t\t\told_mode = (old_mode << 3) | (ch - '0');\n-\t\t\tcp++;\n-\t\t}\n-\t\tif (strncmp(cp, \"->\", 2))\n-\t\t\treturn -1;\n-\t\tcp += 2;\n-\t\twhile ((ch = *cp) && ('0' <= ch && ch <= '7')) {\n-\t\t\tnew_mode = (new_mode << 3) | (ch - '0');\n-\t\t\tcp++;\n-\t\t}\n-\t\tif (strncmp(cp, \"\\tblob\\t\", 6) && strncmp(cp, \" blob \", 6) &&\n-\t\t    strncmp(cp, \"\\ttree\\t\", 6) && strncmp(cp, \" tree \", 6))\n-\t\t\treturn -1;\n-\t\tcp += 6;\n-\t\tif (get_sha1_hex(cp, old_sha1))\n-\t\t\treturn -1;\n-\t\tcp += 40;\n-\t\tif (strncmp(cp, \"->\", 2))\n-\t\t\treturn -1;\n-\t\tcp += 2;\n-\t\tif (get_sha1_hex(cp, new_sha1))\n-\t\t\treturn -1;\n-\t\tcp += 40;\n-\t\tif ((*cp != '\\t') && *cp != ' ')\n-\t\t\treturn -1;\n-\t\tstrcpy(path, ++cp);\n-\t\tdiff_change(old_mode, new_mode, old_sha1, new_sha1, path, NULL);\n-\t\tbreak;\n-\tdefault:\n+\tif (get_sha1_hex(cp, new_sha1))\n \t\treturn -1;\n-\t}\n+\tcp += 40;\n+\tif (*cp++ != inter_name_termination)\n+\t\treturn -1;\n+\tif (buf2)\n+\t\tcp = buf2;\n+\tep = strchr(cp, inter_name_termination);\n+\tif (!ep)\n+\t\treturn -1;\n+\t*ep++ = 0;\n+\tstrcpy(old_path, cp);\n+\tdiff_guif(old_mode, new_mode, old_sha1, new_sha1,\n+\t\t  old_path, buf3 ? buf3 : ep);\n \treturn 0;\n }\n \n@@ -97,19 +60,22 @@ static const char *diff_helper_usage =\n \t\"git-diff-helper [-z] [-R] [-M] [-C] [-S<string>] paths...\";\n \n int main(int ac, const char **av) {\n-\tstruct strbuf sb;\n-\tint reverse = 0;\n-\tint line_termination = '\\n';\n+\tstruct strbuf sb1, sb2, sb3;\n+\tint reverse_diff = 0;\n \n-\tstrbuf_init(&sb);\n+\tstrbuf_init(&sb1);\n+\tstrbuf_init(&sb2);\n+\tstrbuf_init(&sb3);\n \n \twhile (1 < ac && av[1][0] == '-') {\n \t\tif (av[1][1] == 'R')\n-\t\t\treverse = 1;\n+\t\t\treverse_diff = 1;\n \t\telse if (av[1][1] == 'z')\n-\t\t\tline_termination = 0;\n+\t\t\tline_termination = inter_name_termination = 0;\n \t\telse if (av[1][1] == 'p') /* hidden from the help */\n-\t\t\tgenerate_patch = 0;\n+\t\t\tdiff_output_style = DIFF_FORMAT_HUMAN;\n+\t\telse if (av[1][1] == 'P') /* hidden from the help */\n+\t\t\tdiff_output_style = DIFF_FORMAT_MACHINE;\n \t\telse if (av[1][1] == 'M') {\n \t\t\tdetect_rename = 1;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(av[1]);\n@@ -127,19 +93,38 @@ int main(int ac, const char **av) {\n \t}\n \t/* the remaining parameters are paths patterns */\n \n-\tdiff_setup(reverse, (generate_patch ? -1 : line_termination));\n+\tdiff_setup(reverse_diff, diff_output_style);\n \twhile (1) {\n \t\tint status;\n-\t\tread_line(&sb, stdin, line_termination);\n-\t\tif (sb.eof)\n+\t\tread_line(&sb1, stdin, line_termination);\n+\t\tif (sb1.eof)\n \t\t\tbreak;\n-\t\tstatus = parse_diff_raw_output(sb.buf);\n+\t\tswitch (sb1.buf[0]) {\n+\t\tcase 'U':\n+\t\t\tdiff_unmerge(sb1.buf + 2);\n+\t\t\tcontinue;\n+\t\tcase ':':\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tgoto unrecognized;\n+\t\t}\n+\t\tif (!line_termination) {\n+\t\t\tread_line(&sb2, stdin, line_termination);\n+\t\t\tif (sb2.eof)\n+\t\t\t\tbreak;\n+\t\t\tread_line(&sb3, stdin, line_termination);\n+\t\t\tif (sb3.eof)\n+\t\t\t\tbreak;\n+\t\t\tstatus = parse_diff_raw(sb1.buf+1, sb2.buf, sb3.buf);\n+\t\t}\n+\t\telse\n+\t\t\tstatus = parse_diff_raw(sb1.buf+1, NULL, NULL);\n \t\tif (status) {\n+\t\tunrecognized:\n \t\t\tdiff_flush(av+1, ac-1);\n-\t\t\tprintf(\"%s%c\", sb.buf, line_termination);\n+\t\t\tprintf(\"%s%c\", sb1.buf, line_termination);\n \t\t}\n \t}\n-\n \tif (detect_rename)\n \t\tdiff_detect_rename(detect_rename, diff_score_opt);\n \tif (pickaxe)\ndiff --git a/diff-tree.c b/diff-tree.c\n--- a/diff-tree.c\n+++ b/diff-tree.c\n@@ -8,8 +8,7 @@ static int verbose_header = 0;\n static int ignore_merges = 1;\n static int recursive = 0;\n static int read_stdin = 0;\n-static int line_termination = '\\n';\n-static int generate_patch = 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_score_opt = 0;\n@@ -269,7 +268,7 @@ static int diff_tree_sha1(const unsigned\n \n static void call_diff_setup(void)\n {\n-\tdiff_setup(reverse_diff, (generate_patch ? -1 : line_termination));\n+\tdiff_setup(reverse_diff, diff_output_format);\n }\n \n static void call_diff_flush(void)\n@@ -501,7 +500,8 @@ int main(int argc, char **argv)\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"-p\")) {\n-\t\t\trecursive = generate_patch = 1;\n+\t\t\tdiff_output_format = DIFF_FORMAT_PATCH;\n+\t\t\trecursive = 1;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strncmp(arg, \"-S\", 2)) {\n@@ -509,18 +509,17 @@ int main(int argc, char **argv)\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strncmp(arg, \"-M\", 2)) {\n-\t\t\tdetect_rename = recursive = generate_patch = 1;\n+\t\t\tdetect_rename = 1;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(arg);\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strncmp(arg, \"-C\", 2)) {\n \t\t\tdetect_rename = 2;\n-\t\t\trecursive = generate_patch = 1;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(arg);\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"-z\")) {\n-\t\t\tline_termination = '\\0';\n+\t\t\tdiff_output_format = DIFF_FORMAT_MACHINE;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"-m\")) {\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -13,7 +13,9 @@ static const char *diff_opts = \"-pu\";\n static unsigned char null_sha1[20] = { 0, };\n \n static int reverse_diff;\n-static int diff_raw_output = -1;\n+static int generate_patch;\n+static int line_termination = '\\n';\n+static int inter_name_termination = '\\t';\n static const char **pathspec;\n static int speccnt;\n \n@@ -163,20 +165,23 @@ struct diff_filespec *alloc_filespec(con\n \tstruct diff_filespec *spec = xmalloc(sizeof(*spec) + namelen + 1);\n \tspec->path = (char *)(spec + 1);\n \tstrcpy(spec->path, path);\n-\tspec->should_free = spec->should_munmap = spec->file_valid = 0;\n+\tspec->should_free = spec->should_munmap = 0;\n \tspec->xfrm_flags = 0;\n \tspec->size = 0;\n \tspec->data = 0;\n+\tspec->mode = 0;\n+\tmemset(spec->sha1, 0, 20);\n \treturn spec;\n }\n \n void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,\n \t\t   unsigned short mode)\n {\n-\tspec->mode = mode;\n-\tmemcpy(spec->sha1, sha1, 20);\n-\tspec->sha1_valid = !!memcmp(sha1, null_sha1, 20);\n-\tspec->file_valid = 1;\n+\tif (mode) { /* just playing defensive */\n+\t\tspec->mode = mode;\n+\t\tmemcpy(spec->sha1, sha1, 20);\n+\t\tspec->sha1_valid = !!memcmp(sha1, null_sha1, 20);\n+\t}\n }\n \n /*\n@@ -231,7 +236,7 @@ static int work_tree_matches(const char \n int diff_populate_filespec(struct diff_filespec *s)\n {\n \tint err = 0;\n-\tif (!s->file_valid)\n+\tif (!DIFF_FILE_VALID(s))\n \t\tdie(\"internal error: asking to populate invalid file.\");\n \tif (S_ISDIR(s->mode))\n \t\treturn -1;\n@@ -316,7 +321,7 @@ static void prepare_temp_file(const char\n \t\t\t      struct diff_tempfile *temp,\n \t\t\t      struct diff_filespec *one)\n {\n-\tif (!one->file_valid) {\n+\tif (!DIFF_FILE_VALID(one)) {\n \tnot_a_valid_file:\n \t\t/* A '-' entry produces this for file-2, and\n \t\t * a '+' entry produces this for file-1.\n@@ -509,10 +514,22 @@ int diff_scoreopt_parse(const char *opt)\n \treturn MAX_SCORE * num / scale;\n }\n \n-void diff_setup(int reverse_diff_, int diff_raw_output_)\n+void diff_setup(int reverse_diff_, int diff_output_style)\n {\n \treverse_diff = reverse_diff_;\n-\tdiff_raw_output = diff_raw_output_;\n+\tgenerate_patch = 0;\n+\tswitch (diff_output_style) {\n+\tcase DIFF_FORMAT_HUMAN:\n+\t\tline_termination = '\\n';\n+\t\tinter_name_termination = '\\t';\n+\t\tbreak;\n+\tcase DIFF_FORMAT_MACHINE:\n+\t\tline_termination = inter_name_termination = 0;\n+\t\tbreak;\n+\tcase DIFF_FORMAT_PATCH:\n+\t\tgenerate_patch = 1;\n+\t\tbreak;\n+\t}\n }\n \n struct diff_queue_struct diff_queued_diff;\n@@ -536,43 +553,17 @@ struct diff_filepair *diff_queue(struct \n \treturn dp;\n }\n \n-static const char *git_object_type(unsigned mode)\n-{\n-\treturn S_ISDIR(mode) ? \"tree\" : \"blob\";\n-}\n-\n static void diff_flush_raw(struct diff_filepair *p)\n {\n-\tstruct diff_filespec *it;\n-\tint addremove;\n-\n-\t/* raw output does not have a way to express rename nor copy */\n-\tif (strcmp(p->one->path, p->two->path))\n-\t\treturn;\n-\n-\tif (p->one->file_valid && p->two->file_valid) {\n-\t\tchar hex[41];\n-\t\tstrcpy(hex, sha1_to_hex(p->one->sha1));\n-\t\tprintf(\"*%06o->%06o %s %s->%s %s%c\",\n-\t\t       p->one->mode, p->two->mode,\n-\t\t       git_object_type(p->one->mode),\n-\t\t       hex, sha1_to_hex(p->two->sha1),\n-\t\t       p->one->path, diff_raw_output);\n-\t\treturn;\n-\t}\n-\n-\tif (p->one->file_valid) {\n-\t\tit = p->one;\n-\t\taddremove = '-';\n-\t} else {\n-\t\tit = p->two;\n-\t\taddremove = '+';\n-\t}\n-\n-\tprintf(\"%c%06o %s %s %s%c\",\n-\t       addremove,\n-\t       it->mode, git_object_type(it->mode),\n-\t       sha1_to_hex(it->sha1), it->path, diff_raw_output);\n+\t/*\n+\t * We used to reject rename/copy but new diff-raw can express them.\n+\t */\n+\tprintf(\":%06o %06o %s \",\n+\t       p->one->mode, p->two->mode, sha1_to_hex(p->one->sha1));\n+\tprintf(\"%s%c%s%c%s%c\",\n+\t       sha1_to_hex(p->two->sha1), inter_name_termination,\n+\t       p->one->path, inter_name_termination,\n+\t       p->two->path, line_termination);\n }\n \n static void diff_flush_patch(struct diff_filepair *p)\n@@ -581,8 +572,8 @@ static void diff_flush_patch(struct diff\n \n \tname = p->one->path;\n \tother = (strcmp(name, p->two->path) ? p->two->path : NULL);\n-\tif ((p->one->file_valid && S_ISDIR(p->one->mode)) ||\n-\t    (p->two->file_valid && S_ISDIR(p->two->mode)))\n+\tif ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||\n+\t    (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))\n \t\treturn; /* no tree diffs in patch format */ \n \n \trun_external_diff(name, other, p->one, p->two, p->xfrm_msg);\n@@ -596,11 +587,12 @@ static int identical(struct diff_filespe\n \t * and filter and clean them up here before producing the output.\n \t */\n \n-\tif (!one->file_valid && !two->file_valid)\n+\tif (!DIFF_FILE_VALID(one) && !DIFF_FILE_VALID(two))\n \t\treturn 1; /* not interesting */\n \n \t/* deletion, addition, mode change and renames are all interesting. */\n-\tif ((one->file_valid != two->file_valid) || (one->mode != two->mode) ||\n+\tif (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) ||\n+\t    (one->mode != two->mode) ||\n \t    strcmp(one->path, two->path))\n \t\treturn 0;\n \n@@ -619,10 +611,10 @@ static void diff_flush_one(struct diff_f\n {\n \tif (identical(p->one, p->two))\n \t\treturn;\n-\tif (0 <= diff_raw_output)\n-\t\tdiff_flush_raw(p);\n-\telse\n+\tif (generate_patch)\n \t\tdiff_flush_patch(p);\n+\telse\n+\t\tdiff_flush_raw(p);\n }\n \n int diff_queue_is_empty(void)\n@@ -697,10 +689,35 @@ void diff_addremove(int addremove, unsig\n \tdiff_queue(&diff_queued_diff, one, two);\n }\n \n+void diff_guif(unsigned old_mode,\n+\t       unsigned new_mode,\n+\t       const unsigned char *old_sha1,\n+\t       const unsigned char *new_sha1,\n+\t       const char *old_path,\n+\t       const char *new_path)\n+{\n+\tstruct diff_filespec *one, *two;\n+\n+\tif (reverse_diff) {\n+\t\tunsigned tmp;\n+\t\tconst unsigned char *tmp_c;\n+\t\ttmp = old_mode; old_mode = new_mode; new_mode = tmp;\n+\t\ttmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;\n+\t}\n+\tone = alloc_filespec(old_path);\n+\ttwo = alloc_filespec(new_path);\n+\tif (old_mode)\n+\t\tfill_filespec(one, old_sha1, old_mode);\n+\tif (new_mode)\n+\t\tfill_filespec(two, new_sha1, new_mode);\n+\tdiff_queue(&diff_queued_diff, one, two);\n+}\n+\n void diff_change(unsigned old_mode, unsigned new_mode,\n \t\t const unsigned char *old_sha1,\n \t\t const unsigned char *new_sha1,\n-\t\t const char *base, const char *path) {\n+\t\t const char *base, const char *path) \n+{\n \tchar concatpath[PATH_MAX];\n \tstruct diff_filespec *one, *two;\n \n@@ -722,9 +739,8 @@ void diff_change(unsigned old_mode, unsi\n \n void diff_unmerge(const char *path)\n {\n-\tif (0 <= diff_raw_output) {\n-\t\tprintf(\"U %s%c\", path, diff_raw_output);\n-\t\treturn;\n-\t}\n-\trun_external_diff(path, NULL, NULL, NULL, NULL);\n+\tif (generate_patch)\n+\t\trun_external_diff(path, NULL, NULL, NULL, NULL);\n+\telse\n+\t\tprintf(\"U %s%c\", path, line_termination);\n }\ndiff --git a/diff.h b/diff.h\n--- a/diff.h\n+++ b/diff.h\n@@ -15,11 +15,21 @@ extern void diff_change(unsigned mode1, \n \t\t\t     const unsigned char *sha2,\n \t\t\t     const char *base, const char *path);\n \n+extern void diff_guif(unsigned mode1,\n+\t\t      unsigned mode2,\n+\t\t      const unsigned char *sha1,\n+\t\t      const unsigned char *sha2,\n+\t\t      const char *path1,\n+\t\t      const char *path2);\n+\n extern void diff_unmerge(const char *path);\n \n extern int diff_scoreopt_parse(const char *opt);\n \n-extern void diff_setup(int reverse, int diff_raw_output);\n+#define DIFF_FORMAT_HUMAN   0\n+#define DIFF_FORMAT_MACHINE 1\n+#define DIFF_FORMAT_PATCH   2\n+extern void diff_setup(int reverse, int diff_output_style);\n \n extern void diff_detect_rename(int, int);\n extern void diff_pickaxe(const char *);\ndiff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c\n--- a/diffcore-pickaxe.c\n+++ b/diffcore-pickaxe.c\n@@ -32,14 +32,14 @@ void diff_pickaxe(const char *needle)\n \n \tfor (i = 0; i < q->nr; i++) {\n \t\tstruct diff_filepair *p = q->queue[i];\n-\t\tif (!p->one->file_valid) {\n-\t\t\tif (!p->two->file_valid)\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_queue(&outq, p->one, p->two);\n \t\t}\n-\t\telse if (!p->two->file_valid) {\n+\t\telse if (!DIFF_FILE_VALID(p->two)) {\n \t\t\tif (contains(p->one, needle, len))\n \t\t\t\tdiff_queue(&outq, p->one, p->two);\n \t\t}\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -142,7 +142,7 @@ static void debug_filespec(struct diff_f\n \tfprintf(stderr, \"queue[%d] %s (%s) %s %06o %s\\n\",\n \t\tx, one,\n \t\ts->path,\n-\t\ts->file_valid ? \"valid\" : \"invalid\",\n+\t\tDIFF_FILE_VALID(s) ? \"valid\" : \"invalid\",\n \t\ts->mode,\n \t\ts->sha1_valid ? sha1_to_hex(s->sha1) : \"\");\n \tfprintf(stderr, \"queue[%d] %s size %lu flags %d\\n\",\n@@ -210,7 +210,7 @@ static int needs_to_stay(struct diff_que\n \t */\n \twhile (i < q->nr) {\n \t\tstruct diff_filepair *p = q->queue[i++];\n-\t\tif (!p->two->file_valid)\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@@ -247,12 +247,12 @@ void diff_detect_rename(int detect_renam\n \n \tfor (i = 0; i < q->nr; i++) {\n \t\tstruct diff_filepair *p = q->queue[i];\n-\t\tif (!p->one->file_valid)\n-\t\t\tif (!p->two->file_valid)\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\telse\n \t\t\t\tdiff_rename_pool_add(&created, p->two);\n-\t\telse if (!p->two->file_valid)\n+\t\telse if (!DIFF_FILE_VALID(p->two))\n \t\t\tdiff_rename_pool_add(&deleted, p->one);\n \t\telse if (1 < detect_rename) /* find copy, too */\n \t\t\tdiff_rename_pool_add(&stay, p->one);\n@@ -340,15 +340,15 @@ void diff_detect_rename(int detect_renam\n \t */\n \tfor (i = 0; i < q->nr; i++) {\n \t\tstruct diff_filepair *dp, *p = q->queue[i];\n-\t\tif (!p->one->file_valid) {\n-\t\t\tif (p->two->file_valid) {\n+\t\tif (!DIFF_FILE_VALID(p->one)) {\n+\t\t\tif (DIFF_FILE_VALID(p->two)) {\n \t\t\t\t/* creation */\n \t\t\t\tdp = diff_queue(&outq, p->one, p->two);\n \t\t\t\tdp->xfrm_work = 4;\n \t\t\t}\n \t\t\t/* otherwise it is a nonsense; just ignore it */\n \t\t}\n-\t\telse if (!p->two->file_valid) {\n+\t\telse if (!DIFF_FILE_VALID(p->two)) {\n \t\t\t/* deletion */\n \t\t\tdp = diff_queue(&outq, p->one, p->two);\n \t\t\tdp->xfrm_work = 2;\n@@ -374,14 +374,14 @@ void diff_detect_rename(int detect_renam\n \t/* Copy it out to q, removing duplicates. */\n \tfor (i = 0; i < outq.nr; i++) {\n \t\tstruct diff_filepair *p = outq.queue[i];\n-\t\tif (!p->one->file_valid) {\n+\t\tif (!DIFF_FILE_VALID(p->one)) {\n \t\t\t/* created */\n \t\t\tif (p->two->xfrm_flags & RENAME_DST_MATCHED)\n \t\t\t\t; /* rename/copy created it already */\n \t\t\telse\n \t\t\t\tdiff_queue(q, p->one, p->two);\n \t\t}\n-\t\telse if (!p->two->file_valid) {\n+\t\telse if (!DIFF_FILE_VALID(p->two)) {\n \t\t\t/* deleted */\n \t\t\tif (p->one->xfrm_flags & RENAME_SRC_GONE)\n \t\t\t\t; /* rename/copy deleted it already */\ndiff --git a/diffcore.h b/diffcore.h\n--- a/diffcore.h\n+++ b/diffcore.h\n@@ -26,7 +26,7 @@ struct diff_filespec {\n \t\t\t\t  * if false, use the name and read from\n \t\t\t\t  * the filesystem.\n \t\t\t\t  */\n-\tunsigned file_valid : 1; /* if false the file does not exist */\n+#define DIFF_FILE_VALID(spec) (((spec)->mode) != 0)\n \tunsigned should_free : 1; /* data should be free()'ed */\n \tunsigned should_munmap : 1; /* data should be munmap()'ed */\n };\ndiff --git a/t/t0000-basic.sh b/t/t0000-basic.sh\n--- a/t/t0000-basic.sh\n+++ b/t/t0000-basic.sh\n@@ -155,14 +155,14 @@ test_expect_success \\\n      test \"$newtree\" = \"$tree\"'\n \n cat >expected <<\\EOF\n-*100644->100644\tblob\tf87290f8eb2cbbea7857214459a0739927eab154->0000000000000000000000000000000000000000\tpath0\n-*120000->120000\tblob\t15a98433ae33114b085f3eb3bb03b832b3180a01->0000000000000000000000000000000000000000\tpath0sym\n-*100644->100644\tblob\t3feff949ed00a62d9f7af97c15cd8a30595e7ac7->0000000000000000000000000000000000000000\tpath2/file2\n-*120000->120000\tblob\td8ce161addc5173867a3c3c730924388daedbc38->0000000000000000000000000000000000000000\tpath2/file2sym\n-*100644->100644\tblob\t0aa34cae68d0878578ad119c86ca2b5ed5b28376->0000000000000000000000000000000000000000\tpath3/file3\n-*120000->120000\tblob\t8599103969b43aff7e430efea79ca4636466794f->0000000000000000000000000000000000000000\tpath3/file3sym\n-*100644->100644\tblob\t00fb5908cb97c2564a9783c0c64087333b3b464f->0000000000000000000000000000000000000000\tpath3/subp3/file3\n-*120000->120000\tblob\t6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c->0000000000000000000000000000000000000000\tpath3/subp3/file3sym\n+:100644 100644 f87290f8eb2cbbea7857214459a0739927eab154 0000000000000000000000000000000000000000\tpath0\tpath0\n+:120000 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0000000000000000000000000000000000000000\tpath0sym\tpath0sym\n+:100644 100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0000000000000000000000000000000000000000\tpath2/file2\tpath2/file2\n+:120000 120000 d8ce161addc5173867a3c3c730924388daedbc38 0000000000000000000000000000000000000000\tpath2/file2sym\tpath2/file2sym\n+:100644 100644 0aa34cae68d0878578ad119c86ca2b5ed5b28376 0000000000000000000000000000000000000000\tpath3/file3\tpath3/file3\n+:120000 120000 8599103969b43aff7e430efea79ca4636466794f 0000000000000000000000000000000000000000\tpath3/file3sym\tpath3/file3sym\n+:100644 100644 00fb5908cb97c2564a9783c0c64087333b3b464f 0000000000000000000000000000000000000000\tpath3/subp3/file3\tpath3/subp3/file3\n+:120000 120000 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c 0000000000000000000000000000000000000000\tpath3/subp3/file3sym\tpath3/subp3/file3sym\n EOF\n test_expect_success \\\n     'validate git-diff-files output for a know cache/work tree state.' \\\ndiff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh\n--- a/t/t4002-diff-basic.sh\n+++ b/t/t4002-diff-basic.sh\n@@ -10,123 +10,122 @@ test_description='Test diff raw-output.\n . ../lib-read-tree-m-3way.sh\n \n cat >.test-plain-OA <<\\EOF\n-+100644 blob ccba72ad3888a3520b39efcf780b9ee64167535d AA\n-+100644 blob 7e426fb079479fd67f6d81f984e4ec649a44bc25 AN\n--100644 blob bcc68ef997017466d5c9094bcf7692295f588c9a DD\n-+040000 tree 6d50f65d3bdab91c63444294d38f08aeff328e42 DF\n--100644 blob 141c1f1642328e4bc46a7d801a71da392e66791e DM\n--100644 blob 35abde1506ddf806572ff4d407bd06885d0f8ee9 DN\n-+100644 blob 1d41122ebdd7a640f29d3c9cc4f9d70094374762 LL\n-*100644->100644 blob 03f24c8c4700babccfd28b654e7e8eac402ad6cd->103d9f89b50b9aad03054b579be5e7aa665f2d57 MD\n-*100644->100644 blob b258508afb7ceb449981bd9d63d2d3e971bf8d34->b431b272d829ff3aa4d1a5085f4394ab4d3305b6 MM\n-*100644->100644 blob bd084b0c27c7b6cc34f11d6d0509a29be3caf970->a716d58de4a570e0038f5c307bd8db34daea021f MN\n-*100644->100644 blob 40c959f984c8b89a2b02520d17f00d717f024397->2ac547ae9614a00d1b28275de608131f7a0e259f SS\n-*100644->100644 blob 4ac13458899ab908ef3b1128fa378daefc88d356->4c86f9a85fbc5e6804ee2e17a797538fbe785bca TT\n-*040000->040000 tree 7d670fdcdb9929f6c7dac196ff78689cd1c566a1->5e5f22072bb39f6e12cf663a57cb634c76eefb49 Z\n+:000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d\tAA\tAA\n+:000000 100644 0000000000000000000000000000000000000000 7e426fb079479fd67f6d81f984e4ec649a44bc25\tAN\tAN\n+:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000\tDD\tDD\n+:000000 040000 0000000000000000000000000000000000000000 6d50f65d3bdab91c63444294d38f08aeff328e42\tDF\tDF\n+:100644 000000 141c1f1642328e4bc46a7d801a71da392e66791e 0000000000000000000000000000000000000000\tDM\tDM\n+:100644 000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 0000000000000000000000000000000000000000\tDN\tDN\n+:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762\tLL\tLL\n+:100644 100644 03f24c8c4700babccfd28b654e7e8eac402ad6cd 103d9f89b50b9aad03054b579be5e7aa665f2d57\tMD\tMD\n+:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 b431b272d829ff3aa4d1a5085f4394ab4d3305b6\tMM\tMM\n+:100644 100644 bd084b0c27c7b6cc34f11d6d0509a29be3caf970 a716d58de4a570e0038f5c307bd8db34daea021f\tMN\tMN\n+:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f\tSS\tSS\n+:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 4c86f9a85fbc5e6804ee2e17a797538fbe785bca\tTT\tTT\n+:040000 040000 7d670fdcdb9929f6c7dac196ff78689cd1c566a1 5e5f22072bb39f6e12cf663a57cb634c76eefb49\tZ\tZ\n EOF\n \n cat >.test-recursive-OA <<\\EOF\n-+100644 blob ccba72ad3888a3520b39efcf780b9ee64167535d AA\n-+100644 blob 7e426fb079479fd67f6d81f984e4ec649a44bc25 AN\n--100644 blob bcc68ef997017466d5c9094bcf7692295f588c9a DD\n-+100644 blob 68a6d8b91da11045cf4aa3a5ab9f2a781c701249 DF/DF\n--100644 blob 141c1f1642328e4bc46a7d801a71da392e66791e DM\n--100644 blob 35abde1506ddf806572ff4d407bd06885d0f8ee9 DN\n-+100644 blob 1d41122ebdd7a640f29d3c9cc4f9d70094374762 LL\n-*100644->100644 blob 03f24c8c4700babccfd28b654e7e8eac402ad6cd->103d9f89b50b9aad03054b579be5e7aa665f2d57 MD\n-*100644->100644 blob b258508afb7ceb449981bd9d63d2d3e971bf8d34->b431b272d829ff3aa4d1a5085f4394ab4d3305b6 MM\n-*100644->100644 blob bd084b0c27c7b6cc34f11d6d0509a29be3caf970->a716d58de4a570e0038f5c307bd8db34daea021f MN\n-*100644->100644 blob 40c959f984c8b89a2b02520d17f00d717f024397->2ac547ae9614a00d1b28275de608131f7a0e259f SS\n-*100644->100644 blob 4ac13458899ab908ef3b1128fa378daefc88d356->4c86f9a85fbc5e6804ee2e17a797538fbe785bca TT\n-+100644 blob 8acb8e9750e3f644bf323fcf3d338849db106c77 Z/AA\n-+100644 blob 087494262084cefee7ed484d20c8dc0580791272 Z/AN\n--100644 blob 879007efae624d2b1307214b24a956f0a8d686a8 Z/DD\n--100644 blob 9b541b2275c06e3a7b13f28badf5294e2ae63df4 Z/DM\n--100644 blob beb5d38c55283d280685ea21a0e50cfcc0ca064a Z/DN\n-*100644->100644 blob d41fda41b7ec4de46b43cb7ea42a45001ae393d5->a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9 Z/MD\n-*100644->100644 blob 4ca22bae2527d3d9e1676498a0fba3b355bd1278->61422ba9c2c873416061a88cd40a59a35b576474 Z/MM\n-*100644->100644 blob b16d7b25b869f2beb124efa53467d8a1550ad694->a5c544c21cfcb07eb80a4d89a5b7d1570002edfd Z/MN\n+:000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d\tAA\tAA\n+:000000 100644 0000000000000000000000000000000000000000 7e426fb079479fd67f6d81f984e4ec649a44bc25\tAN\tAN\n+:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000\tDD\tDD\n+:000000 100644 0000000000000000000000000000000000000000 68a6d8b91da11045cf4aa3a5ab9f2a781c701249\tDF/DF\tDF/DF\n+:100644 000000 141c1f1642328e4bc46a7d801a71da392e66791e 0000000000000000000000000000000000000000\tDM\tDM\n+:100644 000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 0000000000000000000000000000000000000000\tDN\tDN\n+:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762\tLL\tLL\n+:100644 100644 03f24c8c4700babccfd28b654e7e8eac402ad6cd 103d9f89b50b9aad03054b579be5e7aa665f2d57\tMD\tMD\n+:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 b431b272d829ff3aa4d1a5085f4394ab4d3305b6\tMM\tMM\n+:100644 100644 bd084b0c27c7b6cc34f11d6d0509a29be3caf970 a716d58de4a570e0038f5c307bd8db34daea021f\tMN\tMN\n+:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f\tSS\tSS\n+:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 4c86f9a85fbc5e6804ee2e17a797538fbe785bca\tTT\tTT\n+:000000 100644 0000000000000000000000000000000000000000 8acb8e9750e3f644bf323fcf3d338849db106c77\tZ/AA\tZ/AA\n+:000000 100644 0000000000000000000000000000000000000000 087494262084cefee7ed484d20c8dc0580791272\tZ/AN\tZ/AN\n+:100644 000000 879007efae624d2b1307214b24a956f0a8d686a8 0000000000000000000000000000000000000000\tZ/DD\tZ/DD\n+:100644 000000 9b541b2275c06e3a7b13f28badf5294e2ae63df4 0000000000000000000000000000000000000000\tZ/DM\tZ/DM\n+:100644 000000 beb5d38c55283d280685ea21a0e50cfcc0ca064a 0000000000000000000000000000000000000000\tZ/DN\tZ/DN\n+:100644 100644 d41fda41b7ec4de46b43cb7ea42a45001ae393d5 a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9\tZ/MD\tZ/MD\n+:100644 100644 4ca22bae2527d3d9e1676498a0fba3b355bd1278 61422ba9c2c873416061a88cd40a59a35b576474\tZ/MM\tZ/MM\n+:100644 100644 b16d7b25b869f2beb124efa53467d8a1550ad694 a5c544c21cfcb07eb80a4d89a5b7d1570002edfd\tZ/MN\tZ/MN\n EOF\n cat >.test-plain-OB <<\\EOF\n-+100644 blob 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 AA\n--100644 blob bcc68ef997017466d5c9094bcf7692295f588c9a DD\n-+100644 blob 71420ab81e254145d26d6fc0cddee64c1acd4787 DF\n-*100644->100644 blob 141c1f1642328e4bc46a7d801a71da392e66791e->3c4d8de5fbad08572bab8e10eef8dbb264cf0231 DM\n-+100644 blob 1d41122ebdd7a640f29d3c9cc4f9d70094374762 LL\n--100644 blob 03f24c8c4700babccfd28b654e7e8eac402ad6cd MD\n-*100644->100644 blob b258508afb7ceb449981bd9d63d2d3e971bf8d34->19989d4559aae417fedee240ccf2ba315ea4dc2b MM\n-+100644 blob 15885881ea69115351c09b38371f0348a3fb8c67 NA\n--100644 blob a4e179e4291e5536a5e1c82e091052772d2c5a93 ND\n-*100644->100644 blob c8f25781e8f1792e3e40b74225e20553041b5226->cdb9a8c3da571502ac30225e9c17beccb8387983 NM\n-*100644->100644 blob 40c959f984c8b89a2b02520d17f00d717f024397->2ac547ae9614a00d1b28275de608131f7a0e259f SS\n-*100644->100644 blob 4ac13458899ab908ef3b1128fa378daefc88d356->c4e4a12231b9fa79a0053cb6077fcb21bb5b135a TT\n-*040000->040000 tree 7d670fdcdb9929f6c7dac196ff78689cd1c566a1->1ba523955d5160681af65cb776411f574c1e8155 Z\n+:000000 100644 0000000000000000000000000000000000000000 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2\tAA\tAA\n+:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000\tDD\tDD\n+:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787\tDF\tDF\n+:100644 100644 141c1f1642328e4bc46a7d801a71da392e66791e 3c4d8de5fbad08572bab8e10eef8dbb264cf0231\tDM\tDM\n+:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762\tLL\tLL\n+:100644 000000 03f24c8c4700babccfd28b654e7e8eac402ad6cd 0000000000000000000000000000000000000000\tMD\tMD\n+:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 19989d4559aae417fedee240ccf2ba315ea4dc2b\tMM\tMM\n+:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67\tNA\tNA\n+:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000\tND\tND\n+:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983\tNM\tNM\n+:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f\tSS\tSS\n+:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 c4e4a12231b9fa79a0053cb6077fcb21bb5b135a\tTT\tTT\n+:040000 040000 7d670fdcdb9929f6c7dac196ff78689cd1c566a1 1ba523955d5160681af65cb776411f574c1e8155\tZ\tZ\n EOF\n cat >.test-recursive-OB <<\\EOF\n-+100644 blob 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 AA\n--100644 blob bcc68ef997017466d5c9094bcf7692295f588c9a DD\n-+100644 blob 71420ab81e254145d26d6fc0cddee64c1acd4787 DF\n-*100644->100644 blob 141c1f1642328e4bc46a7d801a71da392e66791e->3c4d8de5fbad08572bab8e10eef8dbb264cf0231 DM\n-+100644 blob 1d41122ebdd7a640f29d3c9cc4f9d70094374762 LL\n--100644 blob 03f24c8c4700babccfd28b654e7e8eac402ad6cd MD\n-*100644->100644 blob b258508afb7ceb449981bd9d63d2d3e971bf8d34->19989d4559aae417fedee240ccf2ba315ea4dc2b MM\n-+100644 blob 15885881ea69115351c09b38371f0348a3fb8c67 NA\n--100644 blob a4e179e4291e5536a5e1c82e091052772d2c5a93 ND\n-*100644->100644 blob c8f25781e8f1792e3e40b74225e20553041b5226->cdb9a8c3da571502ac30225e9c17beccb8387983 NM\n-*100644->100644 blob 40c959f984c8b89a2b02520d17f00d717f024397->2ac547ae9614a00d1b28275de608131f7a0e259f SS\n-*100644->100644 blob 4ac13458899ab908ef3b1128fa378daefc88d356->c4e4a12231b9fa79a0053cb6077fcb21bb5b135a TT\n-+100644 blob 6c0b99286d0bce551ac4a7b3dff8b706edff3715 Z/AA\n--100644 blob 879007efae624d2b1307214b24a956f0a8d686a8 Z/DD\n-*100644->100644 blob 9b541b2275c06e3a7b13f28badf5294e2ae63df4->d77371d15817fcaa57eeec27f770c505ba974ec1 Z/DM\n--100644 blob d41fda41b7ec4de46b43cb7ea42a45001ae393d5 Z/MD\n-*100644->100644 blob 4ca22bae2527d3d9e1676498a0fba3b355bd1278->697aad7715a1e7306ca76290a3dd4208fbaeddfa Z/MM\n-+100644 blob d12979c22fff69c59ca9409e7a8fe3ee25eaee80 Z/NA\n--100644 blob a18393c636b98e9bd7296b8b437ea4992b72440c Z/ND\n-*100644->100644 blob 3fdbe17fd013303a2e981e1ca1c6cd6e72789087->7e09d6a3a14bd630913e8c75693cea32157b606d Z/NM\n+:000000 100644 0000000000000000000000000000000000000000 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2\tAA\tAA\n+:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000\tDD\tDD\n+:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787\tDF\tDF\n+:100644 100644 141c1f1642328e4bc46a7d801a71da392e66791e 3c4d8de5fbad08572bab8e10eef8dbb264cf0231\tDM\tDM\n+:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762\tLL\tLL\n+:100644 000000 03f24c8c4700babccfd28b654e7e8eac402ad6cd 0000000000000000000000000000000000000000\tMD\tMD\n+:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 19989d4559aae417fedee240ccf2ba315ea4dc2b\tMM\tMM\n+:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67\tNA\tNA\n+:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000\tND\tND\n+:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983\tNM\tNM\n+:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f\tSS\tSS\n+:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 c4e4a12231b9fa79a0053cb6077fcb21bb5b135a\tTT\tTT\n+:000000 100644 0000000000000000000000000000000000000000 6c0b99286d0bce551ac4a7b3dff8b706edff3715\tZ/AA\tZ/AA\n+:100644 000000 879007efae624d2b1307214b24a956f0a8d686a8 0000000000000000000000000000000000000000\tZ/DD\tZ/DD\n+:100644 100644 9b541b2275c06e3a7b13f28badf5294e2ae63df4 d77371d15817fcaa57eeec27f770c505ba974ec1\tZ/DM\tZ/DM\n+:100644 000000 d41fda41b7ec4de46b43cb7ea42a45001ae393d5 0000000000000000000000000000000000000000\tZ/MD\tZ/MD\n+:100644 100644 4ca22bae2527d3d9e1676498a0fba3b355bd1278 697aad7715a1e7306ca76290a3dd4208fbaeddfa\tZ/MM\tZ/MM\n+:000000 100644 0000000000000000000000000000000000000000 d12979c22fff69c59ca9409e7a8fe3ee25eaee80\tZ/NA\tZ/NA\n+:100644 000000 a18393c636b98e9bd7296b8b437ea4992b72440c 0000000000000000000000000000000000000000\tZ/ND\tZ/ND\n+:100644 100644 3fdbe17fd013303a2e981e1ca1c6cd6e72789087 7e09d6a3a14bd630913e8c75693cea32157b606d\tZ/NM\tZ/NM\n EOF\n cat >.test-plain-AB <<\\EOF\n-*100644->100644 blob ccba72ad3888a3520b39efcf780b9ee64167535d->6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 AA\n--100644 blob 7e426fb079479fd67f6d81f984e4ec649a44bc25 AN\n-+100644 blob 71420ab81e254145d26d6fc0cddee64c1acd4787 DF\n--040000 tree 6d50f65d3bdab91c63444294d38f08aeff328e42 DF\n-+100644 blob 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 DM\n-+100644 blob 35abde1506ddf806572ff4d407bd06885d0f8ee9 DN\n--100644 blob 103d9f89b50b9aad03054b579be5e7aa665f2d57 MD\n-*100644->100644 blob b431b272d829ff3aa4d1a5085f4394ab4d3305b6->19989d4559aae417fedee240ccf2ba315ea4dc2b MM\n-*100644->100644 blob a716d58de4a570e0038f5c307bd8db34daea021f->bd084b0c27c7b6cc34f11d6d0509a29be3caf970 MN\n-+100644 blob 15885881ea69115351c09b38371f0348a3fb8c67 NA\n--100644 blob a4e179e4291e5536a5e1c82e091052772d2c5a93 ND\n-*100644->100644 blob c8f25781e8f1792e3e40b74225e20553041b5226->cdb9a8c3da571502ac30225e9c17beccb8387983 NM\n-*100644->100644 blob 4c86f9a85fbc5e6804ee2e17a797538fbe785bca->c4e4a12231b9fa79a0053cb6077fcb21bb5b135a TT\n-*040000->040000 tree 5e5f22072bb39f6e12cf663a57cb634c76eefb49->1ba523955d5160681af65cb776411f574c1e8155 Z\n+:100644 100644 ccba72ad3888a3520b39efcf780b9ee64167535d 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2\tAA\tAA\n+:100644 000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 0000000000000000000000000000000000000000\tAN\tAN\n+:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787\tDF\tDF\n+:040000 000000 6d50f65d3bdab91c63444294d38f08aeff328e42 0000000000000000000000000000000000000000\tDF\tDF\n+:000000 100644 0000000000000000000000000000000000000000 3c4d8de5fbad08572bab8e10eef8dbb264cf0231\tDM\tDM\n+:000000 100644 0000000000000000000000000000000000000000 35abde1506ddf806572ff4d407bd06885d0f8ee9\tDN\tDN\n+:100644 000000 103d9f89b50b9aad03054b579be5e7aa665f2d57 0000000000000000000000000000000000000000\tMD\tMD\n+:100644 100644 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 19989d4559aae417fedee240ccf2ba315ea4dc2b\tMM\tMM\n+:100644 100644 a716d58de4a570e0038f5c307bd8db34daea021f bd084b0c27c7b6cc34f11d6d0509a29be3caf970\tMN\tMN\n+:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67\tNA\tNA\n+:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000\tND\tND\n+:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983\tNM\tNM\n+:100644 100644 4c86f9a85fbc5e6804ee2e17a797538fbe785bca c4e4a12231b9fa79a0053cb6077fcb21bb5b135a\tTT\tTT\n+:040000 040000 5e5f22072bb39f6e12cf663a57cb634c76eefb49 1ba523955d5160681af65cb776411f574c1e8155\tZ\tZ\n EOF\n cat >.test-recursive-AB <<\\EOF\n-*100644->100644 blob ccba72ad3888a3520b39efcf780b9ee64167535d->6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 AA\n--100644 blob 7e426fb079479fd67f6d81f984e4ec649a44bc25 AN\n-+100644 blob 71420ab81e254145d26d6fc0cddee64c1acd4787 DF\n--100644 blob 68a6d8b91da11045cf4aa3a5ab9f2a781c701249 DF/DF\n-+100644 blob 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 DM\n-+100644 blob 35abde1506ddf806572ff4d407bd06885d0f8ee9 DN\n--100644 blob 103d9f89b50b9aad03054b579be5e7aa665f2d57 MD\n-*100644->100644 blob b431b272d829ff3aa4d1a5085f4394ab4d3305b6->19989d4559aae417fedee240ccf2ba315ea4dc2b MM\n-*100644->100644 blob a716d58de4a570e0038f5c307bd8db34daea021f->bd084b0c27c7b6cc34f11d6d0509a29be3caf970 MN\n-+100644 blob 15885881ea69115351c09b38371f0348a3fb8c67 NA\n--100644 blob a4e179e4291e5536a5e1c82e091052772d2c5a93 ND\n-*100644->100644 blob c8f25781e8f1792e3e40b74225e20553041b5226->cdb9a8c3da571502ac30225e9c17beccb8387983 NM\n-*100644->100644 blob 4c86f9a85fbc5e6804ee2e17a797538fbe785bca->c4e4a12231b9fa79a0053cb6077fcb21bb5b135a TT\n-*100644->100644 blob 8acb8e9750e3f644bf323fcf3d338849db106c77->6c0b99286d0bce551ac4a7b3dff8b706edff3715 Z/AA\n--100644 blob 087494262084cefee7ed484d20c8dc0580791272 Z/AN\n-+100644 blob d77371d15817fcaa57eeec27f770c505ba974ec1 Z/DM\n-+100644 blob beb5d38c55283d280685ea21a0e50cfcc0ca064a Z/DN\n--100644 blob a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9 Z/MD\n-*100644->100644 blob 61422ba9c2c873416061a88cd40a59a35b576474->697aad7715a1e7306ca76290a3dd4208fbaeddfa Z/MM\n-*100644->100644 blob a5c544c21cfcb07eb80a4d89a5b7d1570002edfd->b16d7b25b869f2beb124efa53467d8a1550ad694 Z/MN\n-+100644 blob d12979c22fff69c59ca9409e7a8fe3ee25eaee80 Z/NA\n--100644 blob a18393c636b98e9bd7296b8b437ea4992b72440c Z/ND\n-*100644->100644 blob 3fdbe17fd013303a2e981e1ca1c6cd6e72789087->7e09d6a3a14bd630913e8c75693cea32157b606d Z/NM\n+:100644 100644 ccba72ad3888a3520b39efcf780b9ee64167535d 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2\tAA\tAA\n+:100644 000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 0000000000000000000000000000000000000000\tAN\tAN\n+:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787\tDF\tDF\n+:100644 000000 68a6d8b91da11045cf4aa3a5ab9f2a781c701249 0000000000000000000000000000000000000000\tDF/DF\tDF/DF\n+:000000 100644 0000000000000000000000000000000000000000 3c4d8de5fbad08572bab8e10eef8dbb264cf0231\tDM\tDM\n+:000000 100644 0000000000000000000000000000000000000000 35abde1506ddf806572ff4d407bd06885d0f8ee9\tDN\tDN\n+:100644 000000 103d9f89b50b9aad03054b579be5e7aa665f2d57 0000000000000000000000000000000000000000\tMD\tMD\n+:100644 100644 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 19989d4559aae417fedee240ccf2ba315ea4dc2b\tMM\tMM\n+:100644 100644 a716d58de4a570e0038f5c307bd8db34daea021f bd084b0c27c7b6cc34f11d6d0509a29be3caf970\tMN\tMN\n+:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67\tNA\tNA\n+:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000\tND\tND\n+:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983\tNM\tNM\n+:100644 100644 4c86f9a85fbc5e6804ee2e17a797538fbe785bca c4e4a12231b9fa79a0053cb6077fcb21bb5b135a\tTT\tTT\n+:100644 100644 8acb8e9750e3f644bf323fcf3d338849db106c77 6c0b99286d0bce551ac4a7b3dff8b706edff3715\tZ/AA\tZ/AA\n+:100644 000000 087494262084cefee7ed484d20c8dc0580791272 0000000000000000000000000000000000000000\tZ/AN\tZ/AN\n+:000000 100644 0000000000000000000000000000000000000000 d77371d15817fcaa57eeec27f770c505ba974ec1\tZ/DM\tZ/DM\n+:000000 100644 0000000000000000000000000000000000000000 beb5d38c55283d280685ea21a0e50cfcc0ca064a\tZ/DN\tZ/DN\n+:100644 000000 a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9 0000000000000000000000000000000000000000\tZ/MD\tZ/MD\n+:100644 100644 61422ba9c2c873416061a88cd40a59a35b576474 697aad7715a1e7306ca76290a3dd4208fbaeddfa\tZ/MM\tZ/MM\n+:100644 100644 a5c544c21cfcb07eb80a4d89a5b7d1570002edfd b16d7b25b869f2beb124efa53467d8a1550ad694\tZ/MN\tZ/MN\n+:000000 100644 0000000000000000000000000000000000000000 d12979c22fff69c59ca9409e7a8fe3ee25eaee80\tZ/NA\tZ/NA\n+:100644 000000 a18393c636b98e9bd7296b8b437ea4992b72440c 0000000000000000000000000000000000000000\tZ/ND\tZ/ND\n+:100644 100644 3fdbe17fd013303a2e981e1ca1c6cd6e72789087 7e09d6a3a14bd630913e8c75693cea32157b606d\tZ/NM\tZ/NM\n EOF\n \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 z40='0000000000000000000000000000000000000000'\n@@ -135,7 +134,7 @@ cmp_diff_files_output () {\n     # object ID for the changed files because it wants you to look at the\n     # filesystem.\n     sed <\"$2\" >.test-tmp \\\n-\t-e '/^+/d;/\\^*/s/\\( '$x40'->\\)'$x40' /\\1'$z40' /' &&\n+\t-e '/^:000000 /d;s/'$x40'\t/'$z40'\t/' &&\n     diff \"$1\" .test-tmp\n }\n \ndiff --git a/t/t4003-diff-rename-1.sh b/t/t4003-diff-rename-1.sh\n--- a/t/t4003-diff-rename-1.sh\n+++ b/t/t4003-diff-rename-1.sh\n@@ -26,7 +26,7 @@ test_expect_success \\\n # both are slightly edited.  So we say you copy-and-edit one,\n # and rename-and-edit the other.\n \n-GIT_DIFF_OPTS=--unified=0 git-diff-cache -M $tree |\n+GIT_DIFF_OPTS=--unified=0 git-diff-cache -M -p $tree |\n sed -e 's/\\([0-9][0-9]*\\)/#/g' >current &&\n cat >expected <<\\EOF\n diff --git a/COPYING b/COPYING.#\n@@ -68,7 +68,7 @@ test_expect_success \\\n # both are slightly edited.  So we say you edited one,\n # and copy-and-edit the other.\n \n-GIT_DIFF_OPTS=--unified=0 git-diff-cache -C $tree |\n+GIT_DIFF_OPTS=--unified=0 git-diff-cache -C -p $tree |\n sed -e 's/\\([0-9][0-9]*\\)/#/g' >current\n cat >expected <<\\EOF\n diff --git a/COPYING b/COPYING.#\n@@ -108,7 +108,7 @@ test_expect_success \\\n # this is only possible because -C mode now reports the unmodified\n # file to the diff-core.\n \n-GIT_DIFF_OPTS=--unified=0 git-diff-cache -C $tree |\n+GIT_DIFF_OPTS=--unified=0 git-diff-cache -C -p $tree |\n sed -e 's/\\([0-9][0-9]*\\)/#/g' >current\n cat >expected <<\\EOF\n diff --git a/COPYING b/COPYING.#\n------------------------------------------------\n\n"},{"id":"3738","messageId":"Pine.LNX.4.58.0505212253350.2206@ppc970.osdl.org","threadId":"688","inReplyTo":"7vvf5cc779.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-05-22T06:01:27Z","receivedAt":"2005-05-22T06:01:27Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 21 May 2005, Junio C Hamano wrote:\n>\n> Update the diff-raw format as Linus and I discussed, except that\n> it does not use sequence of underscore '_' letters to express\n> nonexistence.  All '0' mode is used for that purpose instead.\n\nOk, it all looks very nice, but it looks like -C is still broken ;)\n\nDo \n\n\tgit-whatchanged -C -p\n\nand you'll see this hilarious entry as the first \"copy\" entry:\n\n\tdiff --git a/Documentation/git-diff-cache.txt b/diffcore-pickaxe.c\n\tsimilarity index 0%\n\tcopy from Documentation/git-diff-cache.txt\n\tcopy to diffcore-pickaxe.c\n\t--- a/Documentation/git-diff-cache.txt\n\t+++ b/diffcore-pickaxe.c\n\t@@ -1,150 +1,56 @@\n\t..\n\nit even says \"similarity index 0%\", adn that sure is accurate ;)\n\nDoing \"-C50\" to ask for at least 50% similarity makes it ok (and it finds\nthe \"git-pull-script\" -> \"git-resolve-script\" thing), but there's\nsomething strange going on, becuase -C10 and -C90 don't work, even if -C40\nand -C50 _do_ work.\n\n\t\tLinus\n"},{"id":"3739","messageId":"7voeb3db2b.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"Pine.LNX.4.58.0505212253350.2206@ppc970.osdl.org","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-22T06:33:32Z","receivedAt":"2005-05-22T06:33:32Z","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 Sat, 21 May 2005, Junio C Hamano wrote:\n>> \n>> Update the diff-raw format as Linus and I discussed, except that\n>> it does not use sequence of underscore '_' letters to express\n>> nonexistence.  All '0' mode is used for that purpose instead.\n\nLT> Ok, it all looks very nice, but it looks like -C is still broken ;)\n\nWell it is not _still_ broken, but rather was introduced by the\ncode reorganization to expose diff_rename_detect().  Sorry about\nthe lack of testing.\n\n------------\nAdd the code to set default minimum score back in.\n\nWhen the minimum score is specified as 0 (meaning \"use default\nvalue\"), set it to the default as we are told.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n$ jit-diff\n# - linus: [PATCH] The diff-raw format updates.\n# + (working tree)\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -235,6 +235,8 @@ void diff_detect_rename(int detect_renam\n \tint h, i, j;\n \tint num_create, num_src, dst_cnt, src_cnt;\n \n+\tif (!minimum_score)\n+\t\tminimum_score = DEFAULT_MINIMUM_SCORE;\n \toutq.queue = NULL;\n \toutq.nr = outq.alloc = 0;\n \n\n\n"},{"id":"3740","messageId":"7vis1bd9y4.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"Pine.LNX.4.58.0505212253350.2206@ppc970.osdl.org","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-22T06:57:39Z","receivedAt":"2005-05-22T06:57:39Z","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> Doing \"-C50\" to ask for at least 50% similarity makes it ok (and it finds\nLT> the \"git-pull-script\" -> \"git-resolve-script\" thing), but there's\nLT> something strange going on, becuase -C10 and -C90 don't work, even if -C40\nLT> and -C50 _do_ work.\n\nPlain -C (or -M) not working was a bug I now understand why,\nand I would not be surprised that -C10 may give ridiculous\nor hilarious results (false hits), but I am having trouble\nreproducing the -C90 case.  Will do some more digging later.\n\n\n"},{"id":"3743","messageId":"7vacmnd5lr.fsf_-_@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"7vis1bd9y4.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] Fix tweak in similarity estimator.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-22T08:31:28Z","receivedAt":"2005-05-22T08:31:28Z","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> Plain -C (or -M) not working was a bug I now understand why,\nJCH> and I would not be surprised that -C10 may give ridiculous\nJCH> or hilarious results (false hits), but I am having trouble\nJCH> reproducing the -C90 case.  Will do some more digging later.\n\nAn embarrasing math thinko was causing this confusion.\n\n------------\nFix tweak in similarity estimator.\n\nThere was a screwy math bug in the estimator that confused what\n-C1 meant and what -C9 meant, only in one of the early \"cheap\"\ncheck, which resulted in quite confusing behaviour.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiffcore-rename.c |    4 ++--\n1 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -77,12 +77,12 @@ static int estimate_similarity(struct di\n \n \t/* We would not consider edits that change the file size so\n \t * drastically.  delta_size must be smaller than\n-\t * minimum_score/MAX_SCORE * min(src->size, dst->size).\n+\t * (MAX_SCORE-minimum_score)/MAX_SCORE * min(src->size, dst->size).\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 \t */\n-\tif (base_size * minimum_score < delta_size * MAX_SCORE)\n+\tif (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)\n \t\treturn 0;\n \n \tdelta = diff_delta(src->data, src->size,\n\n"},{"id":"3744","messageId":"7vbr73ha2c.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"7v1x80dluj.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] Diffcore updates.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-22T09:41:31Z","receivedAt":"2005-05-22T09:41:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus, this truly makes diff-tree with the pickaxe very pleasant to\nuse.  Please give it a try.\n\n------------\nThis patch moves the path selection logic from individual\nprograms to a new diffcore transformer (diff-tree still needs\nto have its own for performance reasons).  Also the header\nprinting code in diff-tree was tweaked not to produce anything\nwhen pickaxe is in effect and there is nothing interesting to\nreport.  An interesting example is the following in the GIT\narchive itself:\n\n    $ git-whatchanged -p -C -S'or something in a real script'\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\nMakefile           |    9 +--\ndiff-cache.c       |   26 ++++++----\ndiff-files.c       |   37 +++-----------\ndiff-helper.c      |   16 +++---\ndiff-tree.c        |   43 ++++++++--------\ndiff.c             |  137 +++++++++++++++++++++--------------------------------\ndiff.h             |   20 ++++---\ndiffcore-pickaxe.c |   15 ++---\ndiffcore-rename.c  |   23 ++++++++\ndiffcore.h         |    4 +\n10 files changed, 162 insertions(+), 168 deletions(-)\n\ndiff --git a/Makefile b/Makefile\n--- a/Makefile\n+++ b/Makefile\n@@ -45,7 +45,7 @@ LIB_H += strbuf.h\n LIB_OBJS += strbuf.o\n \n LIB_H += diff.h\n-LIB_OBJS += diff.o diffcore-rename.o diffcore-pickaxe.o\n+LIB_OBJS += diff.o diffcore-rename.o diffcore-pickaxe.o diffcore-pathspec.o\n \n LIB_OBJS += gitenv.o\n \n@@ -123,9 +123,10 @@ sha1_file.o: $(LIB_H)\n usage.o: $(LIB_H)\n strbuf.o: $(LIB_H)\n gitenv.o: $(LIB_H)\n-diff.o: $(LIB_H)\n-diffcore-rename.o : $(LIB_H)\n-diffcore-pickaxe.o : $(LIB_H)\n+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 \n test: all\n \tmake -C t/ all\ndiff --git a/diff-cache.c b/diff-cache.c\n--- a/diff-cache.c\n+++ b/diff-cache.c\n@@ -71,7 +71,7 @@ static int show_modified(struct cache_en\n \n \toldmode = old->ce_mode;\n \tif (mode == oldmode && !memcmp(sha1, old->sha1, 20) &&\n-\t    detect_rename < 2)\n+\t    detect_rename < DIFF_DETECT_COPY)\n \t\treturn 0;\n \n \tmode = ntohl(mode);\n@@ -156,7 +156,7 @@ static void mark_merge_entries(void)\n static char *diff_cache_usage =\n \"git-diff-cache [-p] [-r] [-z] [-m] [-M] [-C] [-R] [-S<string>] [--cached] <tree-ish>\";\n \n-int main(int argc, char **argv)\n+int main(int argc, const char **argv)\n {\n \tunsigned char tree_sha1[20];\n \tvoid *tree;\n@@ -165,7 +165,7 @@ int main(int argc, char **argv)\n \n \tread_cache();\n \twhile (argc > 2) {\n-\t\tchar *arg = argv[1];\n+\t\tconst char *arg = argv[1];\n \t\targv++;\n \t\targc--;\n \t\tif (!strcmp(arg, \"-r\")) {\n@@ -177,12 +177,12 @@ int main(int argc, char **argv)\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strncmp(arg, \"-M\", 2)) {\n-\t\t\tdetect_rename = 1;\n+\t\t\tdetect_rename = DIFF_DETECT_RENAME;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(arg);\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strncmp(arg, \"-C\", 2)) {\n-\t\t\tdetect_rename = 2;\n+\t\t\tdetect_rename = DIFF_DETECT_COPY;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(arg);\n \t\t\tcontinue;\n \t\t}\n@@ -209,10 +209,14 @@ int main(int argc, char **argv)\n \t\tusage(diff_cache_usage);\n \t}\n \n-\tif (argc != 2 || get_sha1(argv[1], tree_sha1))\n+\tif (argc < 2 || get_sha1(argv[1], tree_sha1))\n \t\tusage(diff_cache_usage);\n+\targv++;\n+\targc--;\n \n-\tdiff_setup(reverse_diff, diff_output_format);\n+\t/* The rest is for paths restriction. */\n+\n+\tdiff_setup(reverse_diff);\n \n \tmark_merge_entries();\n \n@@ -224,9 +228,11 @@ int main(int argc, char **argv)\n \n \tret = diff_cache(active_cache, active_nr);\n \tif (detect_rename)\n-\t\tdiff_detect_rename(detect_rename, diff_score_opt);\n+\t\tdiffcore_rename(detect_rename, diff_score_opt);\n \tif (pickaxe)\n-\t\tdiff_pickaxe(pickaxe);\n-\tdiff_flush(NULL, 0);\n+\t\tdiffcore_pickaxe(pickaxe);\n+\tif (2 <= argc)\n+\t\tdiffcore_pathspec(argv + 1);\n+\tdiff_flush(diff_output_format);\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,21 +16,6 @@ static int diff_score_opt = 0;\n static const char *pickaxe = NULL;\n static int silent = 0;\n \n-static int matches_pathspec(struct cache_entry *ce, char **spec, int cnt)\n-{\n-\tint i;\n-\tint namelen = ce_namelen(ce);\n-\tfor (i = 0; i < cnt; i++) {\n-\t\tint speclen = strlen(spec[i]);\n-\t\tif (! strncmp(spec[i], ce->name, speclen) &&\n-\t\t    speclen <= namelen &&\n-\t\t    (ce->name[speclen] == 0 ||\n-\t\t     ce->name[speclen] == '/'))\n-\t\t\treturn 1;\n-\t}\n-\treturn 0;\n-}\n-\n static void show_unmerge(const char *path)\n {\n \tdiff_unmerge(path);\n@@ -48,7 +33,7 @@ static void show_modified(int oldmode, i\n \tdiff_change(oldmode, mode, old_sha1, sha1, path, NULL);\n }\n \n-int main(int argc, char **argv)\n+int main(int argc, const char **argv)\n {\n \tstatic const unsigned char null_sha1[20] = { 0, };\n \tint entries = read_cache();\n@@ -71,11 +56,11 @@ int main(int argc, char **argv)\n \t\t\tpickaxe = argv[1] + 2;\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 = 1;\n+\t\t\tdetect_rename = DIFF_DETECT_RENAME;\n \t\t}\n \t\telse if (!strncmp(argv[1], \"-C\", 2)) {\n \t\t\tdiff_score_opt = diff_scoreopt_parse(argv[1]);\n-\t\t\tdetect_rename = 2;\n+\t\t\tdetect_rename = DIFF_DETECT_COPY;\n \t\t}\n \t\telse\n \t\t\tusage(diff_files_usage);\n@@ -90,7 +75,7 @@ int main(int argc, char **argv)\n \t\texit(1);\n \t}\n \n-\tdiff_setup(reverse_diff, diff_output_format);\n+\tdiff_setup(reverse_diff);\n \n \tfor (i = 0; i < entries; i++) {\n \t\tstruct stat st;\n@@ -98,10 +83,6 @@ int main(int argc, char **argv)\n \t\tstruct cache_entry *ce = active_cache[i];\n \t\tint changed;\n \n-\t\tif (1 < argc &&\n-\t\t    ! matches_pathspec(ce, argv+1, argc-1))\n-\t\t\tcontinue;\n-\n \t\tif (ce_stage(ce)) {\n \t\t\tshow_unmerge(ce->name);\n \t\t\twhile (i < entries &&\n@@ -122,7 +103,7 @@ int main(int argc, char **argv)\n \t\t\tcontinue;\n \t\t}\n \t\tchanged = ce_match_stat(ce, &st);\n-\t\tif (!changed && detect_rename < 2)\n+\t\tif (!changed && detect_rename < DIFF_DETECT_COPY)\n \t\t\tcontinue;\n \n \t\toldmode = ntohl(ce->ce_mode);\n@@ -133,9 +114,11 @@ int main(int argc, char **argv)\n \t\t\t      ce->name);\n \t}\n \tif (detect_rename)\n-\t\tdiff_detect_rename(detect_rename, diff_score_opt);\n+\t\tdiffcore_rename(detect_rename, diff_score_opt);\n \tif (pickaxe)\n-\t\tdiff_pickaxe(pickaxe);\n-\tdiff_flush(NULL, 0);\n+\t\tdiffcore_pickaxe(pickaxe);\n+\tif (1 < argc)\n+\t\tdiffcore_pathspec(argv + 1);\n+\tdiff_flush(diff_output_format);\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@@ -77,11 +77,11 @@ int main(int ac, const char **av) {\n \t\telse if (av[1][1] == 'P') /* hidden from the help */\n \t\t\tdiff_output_style = DIFF_FORMAT_MACHINE;\n \t\telse if (av[1][1] == 'M') {\n-\t\t\tdetect_rename = 1;\n+\t\t\tdetect_rename = DIFF_DETECT_RENAME;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(av[1]);\n \t\t}\n \t\telse if (av[1][1] == 'C') {\n-\t\t\tdetect_rename = 2;\n+\t\t\tdetect_rename = DIFF_DETECT_COPY;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(av[1]);\n \t\t}\n \t\telse if (av[1][1] == 'S') {\n@@ -93,7 +93,7 @@ int main(int ac, const char **av) {\n \t}\n \t/* the remaining parameters are paths patterns */\n \n-\tdiff_setup(reverse_diff, diff_output_style);\n+\tdiff_setup(reverse_diff);\n \twhile (1) {\n \t\tint status;\n \t\tread_line(&sb1, stdin, line_termination);\n@@ -121,14 +121,16 @@ int main(int ac, const char **av) {\n \t\t\tstatus = parse_diff_raw(sb1.buf+1, NULL, NULL);\n \t\tif (status) {\n \t\tunrecognized:\n-\t\t\tdiff_flush(av+1, ac-1);\n+\t\t\tdiff_flush(diff_output_style);\n \t\t\tprintf(\"%s%c\", sb1.buf, line_termination);\n \t\t}\n \t}\n \tif (detect_rename)\n-\t\tdiff_detect_rename(detect_rename, diff_score_opt);\n+\t\tdiffcore_rename(detect_rename, diff_score_opt);\n \tif (pickaxe)\n-\t\tdiff_pickaxe(pickaxe);\n-\tdiff_flush(av+1, ac-1);\n+\t\tdiffcore_pickaxe(pickaxe);\n+\tif (ac)\n+\t\tdiffcore_pathspec(av + 1);\n+\tdiff_flush(diff_output_style);\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@@ -18,7 +18,7 @@ static const char *header_prefix = \"\";\n \n // What paths are we interested in?\n static int nr_paths = 0;\n-static char **paths = NULL;\n+static const char **paths = NULL;\n static int *pathlens = NULL;\n \n static int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base);\n@@ -67,11 +67,6 @@ static void show_file(const char *prefix\n \tconst char *path;\n \tconst unsigned char *sha1 = extract(tree, size, &path, &mode);\n \n-\tif (header) {\n-\t\tprintf(\"%s\", header);\n-\t\theader = NULL;\n-\t}\n-\n \tif (silent)\n \t\treturn;\n \n@@ -137,10 +132,6 @@ static int compare_tree_entry(void *tree\n \t\treturn retval;\n \t}\n \n-\tif (header) {\n-\t\tprintf(\"%s\", header);\n-\t\theader = NULL;\n-\t}\n \tif (silent)\n \t\treturn 0;\n \n@@ -268,16 +259,28 @@ static int diff_tree_sha1(const unsigned\n \n static void call_diff_setup(void)\n {\n-\tdiff_setup(reverse_diff, diff_output_format);\n+\tdiff_setup(reverse_diff);\n }\n \n-static void call_diff_flush(void)\n+static int call_diff_flush()\n {\n \tif (detect_rename)\n-\t\tdiff_detect_rename(detect_rename, diff_score_opt);\n-\tif (pickaxe)\n-\t\tdiff_pickaxe(pickaxe);\n-\tdiff_flush(NULL, 0);\n+\t\tdiffcore_rename(detect_rename, diff_score_opt);\n+\tif (pickaxe) {\n+\t\tdiffcore_pickaxe(pickaxe);\n+\t\tif (diff_queue_is_empty()) {\n+\t\t\tdiff_flush(DIFF_FORMAT_NO_OUTPUT);\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\tif (nr_paths)\n+\t\tdiffcore_pathspec(paths);\n+\tif (header) {\n+\t\tprintf(\"%s\", header);\n+\t\theader = NULL;\n+\t}\n+\tdiff_flush(diff_output_format);\n+\treturn 1;\n }\n \n static int diff_tree_sha1_top(const unsigned char *old,\n@@ -462,7 +465,7 @@ static int diff_tree_stdin(char *line)\n static char *diff_tree_usage =\n \"git-diff-tree [-p] [-r] [-z] [--stdin] [-M] [-C] [-R] [-S<string>] [-m] [-s] [-v] <tree-ish> <tree-ish>\";\n \n-int main(int argc, char **argv)\n+int main(int argc, const char **argv)\n {\n \tint nr_sha1;\n \tchar line[1000];\n@@ -470,7 +473,7 @@ int main(int argc, char **argv)\n \n \tnr_sha1 = 0;\n \tfor (;;) {\n-\t\tchar *arg;\n+\t\tconst char *arg;\n \n \t\targv++;\n \t\targc--;\n@@ -509,12 +512,12 @@ int main(int argc, char **argv)\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strncmp(arg, \"-M\", 2)) {\n-\t\t\tdetect_rename = 1;\n+\t\t\tdetect_rename = DIFF_DETECT_RENAME;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(arg);\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strncmp(arg, \"-C\", 2)) {\n-\t\t\tdetect_rename = 2;\n+\t\t\tdetect_rename = DIFF_DETECT_COPY;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(arg);\n \t\t\tcontinue;\n \t\t}\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -16,8 +16,6 @@ static int reverse_diff;\n static int generate_patch;\n static int line_termination = '\\n';\n static int inter_name_termination = '\\t';\n-static const char **pathspec;\n-static int speccnt;\n \n static const char *external_diff(void)\n {\n@@ -286,6 +284,12 @@ int diff_populate_filespec(struct diff_f\n \treturn 0;\n }\n \n+void diff_free_filepair(struct diff_filepair *p)\n+{\n+\tfree(p->xfrm_msg);\n+\tfree(p);\n+}\n+\n void diff_free_filespec_data(struct diff_filespec *s)\n {\n \tif (s->should_free)\n@@ -390,25 +394,6 @@ static void remove_tempfile_on_signal(in\n \tremove_tempfile();\n }\n \n-static int matches_pathspec(const char *name)\n-{\n-\tint i;\n-\tint namelen;\n-\n-\tif (speccnt == 0)\n-\t\treturn 1;\n-\n-\tnamelen = strlen(name);\n-\tfor (i = 0; i < speccnt; i++) {\n-\t\tint speclen = strlen(pathspec[i]);\n-\t\tif (! strncmp(pathspec[i], name, speclen) &&\n-\t\t    speclen <= namelen &&\n-\t\t    (name[speclen] == 0 || name[speclen] == '/'))\n-\t\t\treturn 1;\n-\t}\n-\treturn 0;\n-}\n-\n /* An external diff command takes:\n  *\n  * diff-cmd name infile1 infile1-sha1 infile1-mode \\\n@@ -426,9 +411,6 @@ static void run_external_diff(const char\n \tint status;\n \tstatic int atexit_asked = 0;\n \n-\tif (!matches_pathspec(name) && (!other || !matches_pathspec(other)))\n-\t\treturn;\n-\n \tif (one && two) {\n \t\tprepare_temp_file(name, &temp[0], one);\n \t\tprepare_temp_file(other ? : name, &temp[1], two);\n@@ -496,47 +478,26 @@ static void run_external_diff(const char\n \tremove_tempfile();\n }\n \n-int diff_scoreopt_parse(const char *opt)\n+void diff_setup(int reverse_diff_)\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-\tdiglen = strspn(opt+2, \"0123456789\");\n-\tif (diglen == 0 || strlen(opt+2) != diglen)\n-\t\treturn 0; /* use default */\n-\tsscanf(opt+2, \"%d\", &num);\n-\tfor (i = 0, scale = 1; i < diglen; i++)\n-\t\tscale *= 10;\n-\n-\t/* user says num divided by scale and we say internally that\n-\t * is MAX_SCORE * num / scale.\n-\t */\n-\treturn MAX_SCORE * num / scale;\n+\treverse_diff = reverse_diff_;\n }\n \n-void diff_setup(int reverse_diff_, int diff_output_style)\n+struct diff_queue_struct diff_queued_diff;\n+\n+void diff_q(struct diff_queue_struct *queue, struct diff_filepair *dp)\n {\n-\treverse_diff = reverse_diff_;\n-\tgenerate_patch = 0;\n-\tswitch (diff_output_style) {\n-\tcase DIFF_FORMAT_HUMAN:\n-\t\tline_termination = '\\n';\n-\t\tinter_name_termination = '\\t';\n-\t\tbreak;\n-\tcase DIFF_FORMAT_MACHINE:\n-\t\tline_termination = inter_name_termination = 0;\n-\t\tbreak;\n-\tcase DIFF_FORMAT_PATCH:\n-\t\tgenerate_patch = 1;\n-\t\tbreak;\n+\tif (queue->alloc <= queue->nr) {\n+\t\tqueue->alloc = alloc_nr(queue->alloc);\n+\t\tqueue->queue = xrealloc(queue->queue,\n+\t\t\t\t\tsizeof(dp) * queue->alloc);\n \t}\n+\tqueue->queue[queue->nr++] = dp;\n }\n \n-struct diff_queue_struct diff_queued_diff;\n-\n struct diff_filepair *diff_queue(struct diff_queue_struct *queue,\n-\t\t\t\t  struct diff_filespec *one,\n-\t\t\t\t  struct diff_filespec *two)\n+\t\t\t\t struct diff_filespec *one,\n+\t\t\t\t struct diff_filespec *two)\n {\n \tstruct diff_filepair *dp = xmalloc(sizeof(*dp));\n \tdp->one = one;\n@@ -544,20 +505,16 @@ struct diff_filepair *diff_queue(struct \n \tdp->xfrm_msg = 0;\n \tdp->orig_order = queue->nr;\n \tdp->xfrm_work = 0;\n-\tif (queue->alloc <= queue->nr) {\n-\t\tqueue->alloc = alloc_nr(queue->alloc);\n-\t\tqueue->queue = xrealloc(queue->queue,\n-\t\t\t\t       sizeof(dp) * queue->alloc);\n-\t}\n-\tqueue->queue[queue->nr++] = dp;\n+\tdiff_q(queue, dp);\n \treturn dp;\n }\n \n static void diff_flush_raw(struct diff_filepair *p)\n {\n-\t/*\n-\t * We used to reject rename/copy but new diff-raw can express them.\n-\t */\n+\tif (DIFF_PAIR_UNMERGED(p)) {\n+\t\tprintf(\"U %s%c\", p->one->path, line_termination);\n+\t\treturn;\n+\t}\n \tprintf(\":%06o %06o %s \",\n \t       p->one->mode, p->two->mode, sha1_to_hex(p->one->sha1));\n \tprintf(\"%s%c%s%c%s%c\",\n@@ -576,19 +533,26 @@ static void diff_flush_patch(struct diff\n \t    (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))\n \t\treturn; /* no tree diffs in patch format */ \n \n-\trun_external_diff(name, other, p->one, p->two, p->xfrm_msg);\n+\tif (DIFF_PAIR_UNMERGED(p))\n+\t\trun_external_diff(name, NULL, NULL, NULL, NULL);\n+\telse\n+\t\trun_external_diff(name, other, p->one, p->two, p->xfrm_msg);\n }\n \n-static int identical(struct diff_filespec *one, struct diff_filespec *two)\n+static int interesting(struct diff_filepair *p)\n {\n \t/* This function is written stricter than necessary to support\n \t * the currently implemented transformers, but the idea is to\n \t * let transformers to produce diff_filepairs any way they want,\n \t * and filter and clean them up here before producing the output.\n \t */\n+\tstruct diff_filespec *one, *two;\n+\n+\tif (DIFF_PAIR_UNMERGED(p))\n+\t\treturn 0; /* unmerged is interesting */\n \n-\tif (!DIFF_FILE_VALID(one) && !DIFF_FILE_VALID(two))\n-\t\treturn 1; /* not interesting */\n+\tone = p->one;\n+\ttwo = p->two;\n \n \t/* deletion, addition, mode change and renames are all interesting. */\n \tif (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) ||\n@@ -609,7 +573,7 @@ static int identical(struct diff_filespe\n \n static void diff_flush_one(struct diff_filepair *p)\n {\n-\tif (identical(p->one, p->two))\n+\tif (interesting(p))\n \t\treturn;\n \tif (generate_patch)\n \t\tdiff_flush_patch(p);\n@@ -624,23 +588,32 @@ int diff_queue_is_empty(void)\n \n \tfor (i = 0; i < q->nr; i++) {\n \t\tstruct diff_filepair *p = q->queue[i];\n-\t\tif (!identical(p->one, p->two))\n+\t\tif (!interesting(p))\n \t\t\treturn 0;\n \t}\n \treturn 1;\n }\n \n-void diff_flush(const char **pathspec_, int speccnt_)\n+void diff_flush(int diff_output_style)\n {\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \tint i;\n \n-\tpathspec = pathspec_;\n-\tspeccnt = speccnt_;\n-\n+\tgenerate_patch = 0;\n+\tswitch (diff_output_style) {\n+\tcase DIFF_FORMAT_HUMAN:\n+\t\tline_termination = '\\n';\n+\t\tinter_name_termination = '\\t';\n+\t\tbreak;\n+\tcase DIFF_FORMAT_MACHINE:\n+\t\tline_termination = inter_name_termination = 0;\n+\t\tbreak;\n+\tcase DIFF_FORMAT_PATCH:\n+\t\tgenerate_patch = 1;\n+\t\tbreak;\n+\t}\n \tfor (i = 0; i < q->nr; i++)\n \t\tdiff_flush_one(q->queue[i]);\n-\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@@ -669,7 +642,7 @@ void diff_addremove(int addremove, unsig\n \t * which but should not make any difference).\n \t * Feeding the same new and old to diff_change() should\n \t * also have the same effect.  diff_flush() should\n-\t * filter the identical ones out at the final output\n+\t * filter uninteresting ones out at the final output\n \t * stage.\n \t */\n \tif (reverse_diff)\n@@ -739,8 +712,8 @@ void diff_change(unsigned old_mode, unsi\n \n void diff_unmerge(const char *path)\n {\n-\tif (generate_patch)\n-\t\trun_external_diff(path, NULL, NULL, NULL, NULL);\n-\telse\n-\t\tprintf(\"U %s%c\", path, line_termination);\n+\tstruct diff_filespec *one, *two;\n+\tone = alloc_filespec(path);\n+\ttwo = alloc_filespec(path);\n+\tdiff_queue(&diff_queued_diff, one, two);\n }\ndiff --git a/diff.h b/diff.h\n--- a/diff.h\n+++ b/diff.h\n@@ -26,16 +26,20 @@ extern void diff_unmerge(const char *pat\n \n extern int diff_scoreopt_parse(const char *opt);\n \n-#define DIFF_FORMAT_HUMAN   0\n-#define DIFF_FORMAT_MACHINE 1\n-#define DIFF_FORMAT_PATCH   2\n-extern void diff_setup(int reverse, int diff_output_style);\n-\n-extern void diff_detect_rename(int, int);\n-extern void diff_pickaxe(const char *);\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+\n+#define DIFF_DETECT_RENAME\t1\n+#define DIFF_DETECT_COPY\t2\n+extern void diffcore_rename(int rename_copy, int minimum_score);\n+extern void diffcore_pickaxe(const char *needle);\n+extern void diffcore_pathspec(const char **pathspec);\n \n extern int diff_queue_is_empty(void);\n \n-extern void diff_flush(const char **, int);\n+extern void diff_flush(int output_style);\n \n #endif /* DIFF_H */\ndiff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c\n--- a/diffcore-pickaxe.c\n+++ b/diffcore-pickaxe.c\n@@ -21,7 +21,7 @@ static int contains(struct diff_filespec\n \treturn 0;\n }\n \n-void diff_pickaxe(const char *needle)\n+void diffcore_pickaxe(const char *needle)\n {\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \tunsigned long len = strlen(needle);\n@@ -32,24 +32,23 @@ void diff_pickaxe(const char *needle)\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_queue(&outq, p->one, p->two);\n+\t\t\t\tdiff_q(&outq, p);\n \t\t}\n \t\telse if (!DIFF_FILE_VALID(p->two)) {\n \t\t\tif (contains(p->one, needle, len))\n-\t\t\t\tdiff_queue(&outq, p->one, p->two);\n+\t\t\t\tdiff_q(&outq, p);\n \t\t}\n \t\telse if (contains(p->one, needle, len) !=\n \t\t\t contains(p->two, needle, len))\n-\t\t\tdiff_queue(&outq, p->one, p->two);\n-\t}\n-\tfor (i = 0; i < q->nr; i++) {\n-\t\tstruct diff_filepair *p = q->queue[i];\n-\t\tfree(p);\n+\t\t\tdiff_q(&outq, p);\n+\t\tif (onum == outq.nr)\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@@ -224,8 +224,25 @@ static int needs_to_stay(struct diff_que\n \treturn 0;\n }\n \n-void diff_detect_rename(int detect_rename,\n-\t\t\tint minimum_score)\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+\tdiglen = strspn(opt+2, \"0123456789\");\n+\tif (diglen == 0 || strlen(opt+2) != diglen)\n+\t\treturn 0; /* use default */\n+\tsscanf(opt+2, \"%d\", &num);\n+\tfor (i = 0, scale = 1; i < diglen; i++)\n+\t\tscale *= 10;\n+\n+\t/* user says num divided by scale and we say internally that\n+\t * is MAX_SCORE * num / scale.\n+\t */\n+\treturn MAX_SCORE * num / scale;\n+}\n+\n+void diffcore_rename(int detect_rename, int minimum_score)\n {\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \tstruct diff_queue_struct outq;\n@@ -235,6 +252,8 @@ void diff_detect_rename(int detect_renam\n \tint h, i, j;\n \tint num_create, num_src, dst_cnt, src_cnt;\n \n+\tif (!minimum_score)\n+\t\tminimum_score = DEFAULT_MINIMUM_SCORE;\n \toutq.queue = NULL;\n \toutq.nr = outq.alloc = 0;\n \ndiff --git a/diffcore.h b/diffcore.h\n--- a/diffcore.h\n+++ b/diffcore.h\n@@ -45,6 +45,8 @@ struct diff_filepair {\n \tint orig_order; /* the original order of insertion into the queue */\n \tint xfrm_work; /* for use by tramsformers, not by diffcore */\n };\n+#define DIFF_PAIR_UNMERGED(p) \\\n+\t(!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))\n \n struct diff_queue_struct {\n \tstruct diff_filepair **queue;\n@@ -56,5 +58,7 @@ extern struct diff_queue_struct diff_que\n extern struct diff_filepair *diff_queue(struct diff_queue_struct *,\n \t\t\t\t\tstruct diff_filespec *,\n \t\t\t\t\tstruct diff_filespec *);\n+extern void diff_q(struct diff_queue_struct *, struct diff_filepair *);\n+extern void diff_free_filepair(struct diff_filepair *);\n \n #endif\n------------------------------------------------\n\n"},{"id":"3750","messageId":"Pine.LNX.4.58.0505220939390.2307@ppc970.osdl.org","threadId":"688","inReplyTo":"7vbr73ha2c.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Diffcore updates.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-05-22T16:40:47Z","receivedAt":"2005-05-22T16:40:47Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 22 May 2005, Junio C Hamano wrote:\n>\n> Linus, this truly makes diff-tree with the pickaxe very pleasant to\n> use.  Please give it a try.\n\nI can't. You split \"diffcore_pathspec()\" into a new common library file, \nbut you didn't add that to the archive, so your patch doesn't contain it.\n\nMaybe you should make \"jit\" warn about unadded files ;)\n\n\t\tLinus\n"},{"id":"3751","messageId":"7vvf5bfbrr.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"Pine.LNX.4.58.0505220939390.2307@ppc970.osdl.org","subject":"Re: [PATCH] Diffcore updates.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-22T16:47:36Z","receivedAt":"2005-05-22T16:47:36Z","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> Maybe you should make \"jit\" warn about unadded files ;)\n\nSorry my bad.  I do not run jit-status often enough.\n\n"},{"id":"3754","messageId":"7vekbzfaze.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"7vbr73ha2c.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] Diffcore updates.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-22T17:04:37Z","receivedAt":"2005-05-22T17:04:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This applies on top of the \"math thinko\" fix; would not conflict\nif you are missing it, but you would see offsets.  I added the\nfile missing from the previous patch.  Sorry about the screwup.\n\n------------\nThis patch moves the path selection logic from individual\nprograms to a new diffcore transformer (diff-tree still needs\nto have its own for performance reasons).  Also the header\nprinting code in diff-tree was tweaked not to produce anything\nwhen pickaxe is in effect and there is nothing interesting to\nreport.  An interesting example is the following in the GIT\narchive itself:\n\n    $ git-whatchanged -p -C -S'or something in a real script'\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\nMakefile            |    9 +-\ndiff-cache.c        |   27 +++++---\ndiff-files.c        |   38 +++--------\ndiff-helper.c       |   17 +++--\ndiff-tree.c         |   44 +++++++------\ndiff.c              |  172 +++++++++++++++++++++++++++-------------------------\ndiff.h              |   22 ++++--\ndiffcore-pathspec.c |   63 +++++++++++++++++++\ndiffcore-pickaxe.c  |   15 ++--\ndiffcore-rename.c   |   23 ++++++\ndiffcore.h          |    4 +\n11 files changed, 267 insertions(+), 167 deletions(-)\nnew file (100644): diffcore-pathspec.c\n\ndiff --git a/Makefile b/Makefile\n--- a/Makefile\n+++ b/Makefile\n@@ -45,7 +45,7 @@ LIB_H += strbuf.h\n LIB_OBJS += strbuf.o\n \n LIB_H += diff.h\n-LIB_OBJS += diff.o diffcore-rename.o diffcore-pickaxe.o\n+LIB_OBJS += diff.o diffcore-rename.o diffcore-pickaxe.o diffcore-pathspec.o\n \n LIB_OBJS += gitenv.o\n \n@@ -123,9 +123,10 @@ sha1_file.o: $(LIB_H)\n usage.o: $(LIB_H)\n strbuf.o: $(LIB_H)\n gitenv.o: $(LIB_H)\n-diff.o: $(LIB_H)\n-diffcore-rename.o : $(LIB_H)\n-diffcore-pickaxe.o : $(LIB_H)\n+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 \n test: all\n \tmake -C t/ all\ndiff --git a/diff-cache.c b/diff-cache.c\n--- a/diff-cache.c\n+++ b/diff-cache.c\n@@ -71,7 +71,7 @@ static int show_modified(struct cache_en\n \n \toldmode = old->ce_mode;\n \tif (mode == oldmode && !memcmp(sha1, old->sha1, 20) &&\n-\t    detect_rename < 2)\n+\t    detect_rename < DIFF_DETECT_COPY)\n \t\treturn 0;\n \n \tmode = ntohl(mode);\n@@ -156,7 +156,7 @@ static void mark_merge_entries(void)\n static char *diff_cache_usage =\n \"git-diff-cache [-p] [-r] [-z] [-m] [-M] [-C] [-R] [-S<string>] [--cached] <tree-ish>\";\n \n-int main(int argc, char **argv)\n+int main(int argc, const char **argv)\n {\n \tunsigned char tree_sha1[20];\n \tvoid *tree;\n@@ -165,7 +165,7 @@ int main(int argc, char **argv)\n \n \tread_cache();\n \twhile (argc > 2) {\n-\t\tchar *arg = argv[1];\n+\t\tconst char *arg = argv[1];\n \t\targv++;\n \t\targc--;\n \t\tif (!strcmp(arg, \"-r\")) {\n@@ -177,12 +177,12 @@ int main(int argc, char **argv)\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strncmp(arg, \"-M\", 2)) {\n-\t\t\tdetect_rename = 1;\n+\t\t\tdetect_rename = DIFF_DETECT_RENAME;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(arg);\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strncmp(arg, \"-C\", 2)) {\n-\t\t\tdetect_rename = 2;\n+\t\t\tdetect_rename = DIFF_DETECT_COPY;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(arg);\n \t\t\tcontinue;\n \t\t}\n@@ -209,10 +209,14 @@ int main(int argc, char **argv)\n \t\tusage(diff_cache_usage);\n \t}\n \n-\tif (argc != 2 || get_sha1(argv[1], tree_sha1))\n+\tif (argc < 2 || get_sha1(argv[1], tree_sha1))\n \t\tusage(diff_cache_usage);\n+\targv++;\n+\targc--;\n \n-\tdiff_setup(reverse_diff, diff_output_format);\n+\t/* The rest is for paths restriction. */\n+\n+\tdiff_setup(reverse_diff);\n \n \tmark_merge_entries();\n \n@@ -224,9 +228,12 @@ int main(int argc, char **argv)\n \n \tret = diff_cache(active_cache, active_nr);\n \tif (detect_rename)\n-\t\tdiff_detect_rename(detect_rename, diff_score_opt);\n+\t\tdiffcore_rename(detect_rename, diff_score_opt);\n+\tdiffcore_prune();\n \tif (pickaxe)\n-\t\tdiff_pickaxe(pickaxe);\n-\tdiff_flush(NULL, 0);\n+\t\tdiffcore_pickaxe(pickaxe);\n+\tif (2 <= argc)\n+\t\tdiffcore_pathspec(argv + 1);\n+\tdiff_flush(diff_output_format);\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,21 +16,6 @@ static int diff_score_opt = 0;\n static const char *pickaxe = NULL;\n static int silent = 0;\n \n-static int matches_pathspec(struct cache_entry *ce, char **spec, int cnt)\n-{\n-\tint i;\n-\tint namelen = ce_namelen(ce);\n-\tfor (i = 0; i < cnt; i++) {\n-\t\tint speclen = strlen(spec[i]);\n-\t\tif (! strncmp(spec[i], ce->name, speclen) &&\n-\t\t    speclen <= namelen &&\n-\t\t    (ce->name[speclen] == 0 ||\n-\t\t     ce->name[speclen] == '/'))\n-\t\t\treturn 1;\n-\t}\n-\treturn 0;\n-}\n-\n static void show_unmerge(const char *path)\n {\n \tdiff_unmerge(path);\n@@ -48,7 +33,7 @@ static void show_modified(int oldmode, i\n \tdiff_change(oldmode, mode, old_sha1, sha1, path, NULL);\n }\n \n-int main(int argc, char **argv)\n+int main(int argc, const char **argv)\n {\n \tstatic const unsigned char null_sha1[20] = { 0, };\n \tint entries = read_cache();\n@@ -71,11 +56,11 @@ int main(int argc, char **argv)\n \t\t\tpickaxe = argv[1] + 2;\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 = 1;\n+\t\t\tdetect_rename = DIFF_DETECT_RENAME;\n \t\t}\n \t\telse if (!strncmp(argv[1], \"-C\", 2)) {\n \t\t\tdiff_score_opt = diff_scoreopt_parse(argv[1]);\n-\t\t\tdetect_rename = 2;\n+\t\t\tdetect_rename = DIFF_DETECT_COPY;\n \t\t}\n \t\telse\n \t\t\tusage(diff_files_usage);\n@@ -90,7 +75,7 @@ int main(int argc, char **argv)\n \t\texit(1);\n \t}\n \n-\tdiff_setup(reverse_diff, diff_output_format);\n+\tdiff_setup(reverse_diff);\n \n \tfor (i = 0; i < entries; i++) {\n \t\tstruct stat st;\n@@ -98,10 +83,6 @@ int main(int argc, char **argv)\n \t\tstruct cache_entry *ce = active_cache[i];\n \t\tint changed;\n \n-\t\tif (1 < argc &&\n-\t\t    ! matches_pathspec(ce, argv+1, argc-1))\n-\t\t\tcontinue;\n-\n \t\tif (ce_stage(ce)) {\n \t\t\tshow_unmerge(ce->name);\n \t\t\twhile (i < entries &&\n@@ -122,7 +103,7 @@ int main(int argc, char **argv)\n \t\t\tcontinue;\n \t\t}\n \t\tchanged = ce_match_stat(ce, &st);\n-\t\tif (!changed && detect_rename < 2)\n+\t\tif (!changed && detect_rename < DIFF_DETECT_COPY)\n \t\t\tcontinue;\n \n \t\toldmode = ntohl(ce->ce_mode);\n@@ -133,9 +114,12 @@ int main(int argc, char **argv)\n \t\t\t      ce->name);\n \t}\n \tif (detect_rename)\n-\t\tdiff_detect_rename(detect_rename, diff_score_opt);\n+\t\tdiffcore_rename(detect_rename, diff_score_opt);\n+\tdiffcore_prune();\n \tif (pickaxe)\n-\t\tdiff_pickaxe(pickaxe);\n-\tdiff_flush(NULL, 0);\n+\t\tdiffcore_pickaxe(pickaxe);\n+\tif (1 < argc)\n+\t\tdiffcore_pathspec(argv + 1);\n+\tdiff_flush(diff_output_format);\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@@ -77,11 +77,11 @@ int main(int ac, const char **av) {\n \t\telse if (av[1][1] == 'P') /* hidden from the help */\n \t\t\tdiff_output_style = DIFF_FORMAT_MACHINE;\n \t\telse if (av[1][1] == 'M') {\n-\t\t\tdetect_rename = 1;\n+\t\t\tdetect_rename = DIFF_DETECT_RENAME;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(av[1]);\n \t\t}\n \t\telse if (av[1][1] == 'C') {\n-\t\t\tdetect_rename = 2;\n+\t\t\tdetect_rename = DIFF_DETECT_COPY;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(av[1]);\n \t\t}\n \t\telse if (av[1][1] == 'S') {\n@@ -93,7 +93,7 @@ int main(int ac, const char **av) {\n \t}\n \t/* the remaining parameters are paths patterns */\n \n-\tdiff_setup(reverse_diff, diff_output_style);\n+\tdiff_setup(reverse_diff);\n \twhile (1) {\n \t\tint status;\n \t\tread_line(&sb1, stdin, line_termination);\n@@ -121,14 +121,17 @@ int main(int ac, const char **av) {\n \t\t\tstatus = parse_diff_raw(sb1.buf+1, NULL, NULL);\n \t\tif (status) {\n \t\tunrecognized:\n-\t\t\tdiff_flush(av+1, ac-1);\n+\t\t\tdiff_flush(diff_output_style);\n \t\t\tprintf(\"%s%c\", sb1.buf, line_termination);\n \t\t}\n \t}\n \tif (detect_rename)\n-\t\tdiff_detect_rename(detect_rename, diff_score_opt);\n+\t\tdiffcore_rename(detect_rename, diff_score_opt);\n+\tdiffcore_prune();\n \tif (pickaxe)\n-\t\tdiff_pickaxe(pickaxe);\n-\tdiff_flush(av+1, ac-1);\n+\t\tdiffcore_pickaxe(pickaxe);\n+\tif (ac)\n+\t\tdiffcore_pathspec(av + 1);\n+\tdiff_flush(diff_output_style);\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@@ -18,7 +18,7 @@ static const char *header_prefix = \"\";\n \n // What paths are we interested in?\n static int nr_paths = 0;\n-static char **paths = NULL;\n+static const char **paths = NULL;\n static int *pathlens = NULL;\n \n static int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base);\n@@ -67,11 +67,6 @@ static void show_file(const char *prefix\n \tconst char *path;\n \tconst unsigned char *sha1 = extract(tree, size, &path, &mode);\n \n-\tif (header) {\n-\t\tprintf(\"%s\", header);\n-\t\theader = NULL;\n-\t}\n-\n \tif (silent)\n \t\treturn;\n \n@@ -137,10 +132,6 @@ static int compare_tree_entry(void *tree\n \t\treturn retval;\n \t}\n \n-\tif (header) {\n-\t\tprintf(\"%s\", header);\n-\t\theader = NULL;\n-\t}\n \tif (silent)\n \t\treturn 0;\n \n@@ -268,16 +259,29 @@ static int diff_tree_sha1(const unsigned\n \n static void call_diff_setup(void)\n {\n-\tdiff_setup(reverse_diff, diff_output_format);\n+\tdiff_setup(reverse_diff);\n }\n \n-static void call_diff_flush(void)\n+static int call_diff_flush()\n {\n \tif (detect_rename)\n-\t\tdiff_detect_rename(detect_rename, diff_score_opt);\n-\tif (pickaxe)\n-\t\tdiff_pickaxe(pickaxe);\n-\tdiff_flush(NULL, 0);\n+\t\tdiffcore_rename(detect_rename, diff_score_opt);\n+\tdiffcore_prune();\n+\tif (pickaxe) {\n+\t\tdiffcore_pickaxe(pickaxe);\n+\t\tif (diff_queue_is_empty()) {\n+\t\t\tdiff_flush(DIFF_FORMAT_NO_OUTPUT);\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\tif (nr_paths)\n+\t\tdiffcore_pathspec(paths);\n+\tif (header) {\n+\t\tprintf(\"%s\", header);\n+\t\theader = NULL;\n+\t}\n+\tdiff_flush(diff_output_format);\n+\treturn 1;\n }\n \n static int diff_tree_sha1_top(const unsigned char *old,\n@@ -462,7 +466,7 @@ static int diff_tree_stdin(char *line)\n static char *diff_tree_usage =\n \"git-diff-tree [-p] [-r] [-z] [--stdin] [-M] [-C] [-R] [-S<string>] [-m] [-s] [-v] <tree-ish> <tree-ish>\";\n \n-int main(int argc, char **argv)\n+int main(int argc, const char **argv)\n {\n \tint nr_sha1;\n \tchar line[1000];\n@@ -470,7 +474,7 @@ int main(int argc, char **argv)\n \n \tnr_sha1 = 0;\n \tfor (;;) {\n-\t\tchar *arg;\n+\t\tconst char *arg;\n \n \t\targv++;\n \t\targc--;\n@@ -509,12 +513,12 @@ int main(int argc, char **argv)\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strncmp(arg, \"-M\", 2)) {\n-\t\t\tdetect_rename = 1;\n+\t\t\tdetect_rename = DIFF_DETECT_RENAME;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(arg);\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strncmp(arg, \"-C\", 2)) {\n-\t\t\tdetect_rename = 2;\n+\t\t\tdetect_rename = DIFF_DETECT_COPY;\n \t\t\tdiff_score_opt = diff_scoreopt_parse(arg);\n \t\t\tcontinue;\n \t\t}\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -16,8 +16,6 @@ static int reverse_diff;\n static int generate_patch;\n static int line_termination = '\\n';\n static int inter_name_termination = '\\t';\n-static const char **pathspec;\n-static int speccnt;\n \n static const char *external_diff(void)\n {\n@@ -286,6 +284,12 @@ int diff_populate_filespec(struct diff_f\n \treturn 0;\n }\n \n+void diff_free_filepair(struct diff_filepair *p)\n+{\n+\tfree(p->xfrm_msg);\n+\tfree(p);\n+}\n+\n void diff_free_filespec_data(struct diff_filespec *s)\n {\n \tif (s->should_free)\n@@ -390,25 +394,6 @@ static void remove_tempfile_on_signal(in\n \tremove_tempfile();\n }\n \n-static int matches_pathspec(const char *name)\n-{\n-\tint i;\n-\tint namelen;\n-\n-\tif (speccnt == 0)\n-\t\treturn 1;\n-\n-\tnamelen = strlen(name);\n-\tfor (i = 0; i < speccnt; i++) {\n-\t\tint speclen = strlen(pathspec[i]);\n-\t\tif (! strncmp(pathspec[i], name, speclen) &&\n-\t\t    speclen <= namelen &&\n-\t\t    (name[speclen] == 0 || name[speclen] == '/'))\n-\t\t\treturn 1;\n-\t}\n-\treturn 0;\n-}\n-\n /* An external diff command takes:\n  *\n  * diff-cmd name infile1 infile1-sha1 infile1-mode \\\n@@ -426,9 +411,6 @@ static void run_external_diff(const char\n \tint status;\n \tstatic int atexit_asked = 0;\n \n-\tif (!matches_pathspec(name) && (!other || !matches_pathspec(other)))\n-\t\treturn;\n-\n \tif (one && two) {\n \t\tprepare_temp_file(name, &temp[0], one);\n \t\tprepare_temp_file(other ? : name, &temp[1], two);\n@@ -496,47 +478,26 @@ static void run_external_diff(const char\n \tremove_tempfile();\n }\n \n-int diff_scoreopt_parse(const char *opt)\n+void diff_setup(int reverse_diff_)\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-\tdiglen = strspn(opt+2, \"0123456789\");\n-\tif (diglen == 0 || strlen(opt+2) != diglen)\n-\t\treturn 0; /* use default */\n-\tsscanf(opt+2, \"%d\", &num);\n-\tfor (i = 0, scale = 1; i < diglen; i++)\n-\t\tscale *= 10;\n-\n-\t/* user says num divided by scale and we say internally that\n-\t * is MAX_SCORE * num / scale.\n-\t */\n-\treturn MAX_SCORE * num / scale;\n+\treverse_diff = reverse_diff_;\n }\n \n-void diff_setup(int reverse_diff_, int diff_output_style)\n+struct diff_queue_struct diff_queued_diff;\n+\n+void diff_q(struct diff_queue_struct *queue, struct diff_filepair *dp)\n {\n-\treverse_diff = reverse_diff_;\n-\tgenerate_patch = 0;\n-\tswitch (diff_output_style) {\n-\tcase DIFF_FORMAT_HUMAN:\n-\t\tline_termination = '\\n';\n-\t\tinter_name_termination = '\\t';\n-\t\tbreak;\n-\tcase DIFF_FORMAT_MACHINE:\n-\t\tline_termination = inter_name_termination = 0;\n-\t\tbreak;\n-\tcase DIFF_FORMAT_PATCH:\n-\t\tgenerate_patch = 1;\n-\t\tbreak;\n+\tif (queue->alloc <= queue->nr) {\n+\t\tqueue->alloc = alloc_nr(queue->alloc);\n+\t\tqueue->queue = xrealloc(queue->queue,\n+\t\t\t\t\tsizeof(dp) * queue->alloc);\n \t}\n+\tqueue->queue[queue->nr++] = dp;\n }\n \n-struct diff_queue_struct diff_queued_diff;\n-\n struct diff_filepair *diff_queue(struct diff_queue_struct *queue,\n-\t\t\t\t  struct diff_filespec *one,\n-\t\t\t\t  struct diff_filespec *two)\n+\t\t\t\t struct diff_filespec *one,\n+\t\t\t\t struct diff_filespec *two)\n {\n \tstruct diff_filepair *dp = xmalloc(sizeof(*dp));\n \tdp->one = one;\n@@ -544,20 +505,16 @@ struct diff_filepair *diff_queue(struct \n \tdp->xfrm_msg = 0;\n \tdp->orig_order = queue->nr;\n \tdp->xfrm_work = 0;\n-\tif (queue->alloc <= queue->nr) {\n-\t\tqueue->alloc = alloc_nr(queue->alloc);\n-\t\tqueue->queue = xrealloc(queue->queue,\n-\t\t\t\t       sizeof(dp) * queue->alloc);\n-\t}\n-\tqueue->queue[queue->nr++] = dp;\n+\tdiff_q(queue, dp);\n \treturn dp;\n }\n \n static void diff_flush_raw(struct diff_filepair *p)\n {\n-\t/*\n-\t * We used to reject rename/copy but new diff-raw can express them.\n-\t */\n+\tif (DIFF_PAIR_UNMERGED(p)) {\n+\t\tprintf(\"U %s%c\", p->one->path, line_termination);\n+\t\treturn;\n+\t}\n \tprintf(\":%06o %06o %s \",\n \t       p->one->mode, p->two->mode, sha1_to_hex(p->one->sha1));\n \tprintf(\"%s%c%s%c%s%c\",\n@@ -576,19 +533,26 @@ static void diff_flush_patch(struct diff\n \t    (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))\n \t\treturn; /* no tree diffs in patch format */ \n \n-\trun_external_diff(name, other, p->one, p->two, p->xfrm_msg);\n+\tif (DIFF_PAIR_UNMERGED(p))\n+\t\trun_external_diff(name, NULL, NULL, NULL, NULL);\n+\telse\n+\t\trun_external_diff(name, other, p->one, p->two, p->xfrm_msg);\n }\n \n-static int identical(struct diff_filespec *one, struct diff_filespec *two)\n+static int uninteresting(struct diff_filepair *p)\n {\n \t/* This function is written stricter than necessary to support\n \t * the currently implemented transformers, but the idea is to\n \t * let transformers to produce diff_filepairs any way they want,\n \t * and filter and clean them up here before producing the output.\n \t */\n+\tstruct diff_filespec *one, *two;\n \n-\tif (!DIFF_FILE_VALID(one) && !DIFF_FILE_VALID(two))\n-\t\treturn 1; /* not interesting */\n+\tif (DIFF_PAIR_UNMERGED(p))\n+\t\treturn 0; /* unmerged is interesting */\n+\n+\tone = p->one;\n+\ttwo = p->two;\n \n \t/* deletion, addition, mode change and renames are all interesting. */\n \tif (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) ||\n@@ -607,9 +571,44 @@ static int identical(struct diff_filespe\n \treturn 0;\n }\n \n+void diffcore_prune(void)\n+{\n+\t/*\n+\t * Although rename/copy detection wants to have \"no-change\"\n+\t * entries fed into them, the downstream do not need to see\n+\t * them.  This function removes such entries.\n+\t *\n+\t * The applications that use rename/copy should:\n+\t *\n+\t * (1) feed change and \"no-change\" entries via diff_queue().\n+\t * (2) call diffcore_rename, and any other future diffcore_xxx\n+\t *     that would benefit by still having \"no-change\" entries.\n+\t * (3) call diffcore_prune\n+\t * (4) call other diffcore_xxx that do not need to see\n+\t *     \"no-change\" entries.\n+\t */\n+\tstruct diff_queue_struct *q = &diff_queued_diff;\n+\tstruct diff_queue_struct outq;\n+\tint i;\n+\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\tif (!uninteresting(p))\n+\t\t\tdiff_q(&outq, p);\n+\t\telse\n+\t\t\tdiff_free_filepair(p);\n+\t}\n+\tfree(q->queue);\n+\t*q = outq;\n+\treturn;\n+}\n+\n static void diff_flush_one(struct diff_filepair *p)\n {\n-\tif (identical(p->one, p->two))\n+\tif (uninteresting(p))\n \t\treturn;\n \tif (generate_patch)\n \t\tdiff_flush_patch(p);\n@@ -624,23 +623,32 @@ int diff_queue_is_empty(void)\n \n \tfor (i = 0; i < q->nr; i++) {\n \t\tstruct diff_filepair *p = q->queue[i];\n-\t\tif (!identical(p->one, p->two))\n+\t\tif (!uninteresting(p))\n \t\t\treturn 0;\n \t}\n \treturn 1;\n }\n \n-void diff_flush(const char **pathspec_, int speccnt_)\n+void diff_flush(int diff_output_style)\n {\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \tint i;\n \n-\tpathspec = pathspec_;\n-\tspeccnt = speccnt_;\n-\n+\tgenerate_patch = 0;\n+\tswitch (diff_output_style) {\n+\tcase DIFF_FORMAT_HUMAN:\n+\t\tline_termination = '\\n';\n+\t\tinter_name_termination = '\\t';\n+\t\tbreak;\n+\tcase DIFF_FORMAT_MACHINE:\n+\t\tline_termination = inter_name_termination = 0;\n+\t\tbreak;\n+\tcase DIFF_FORMAT_PATCH:\n+\t\tgenerate_patch = 1;\n+\t\tbreak;\n+\t}\n \tfor (i = 0; i < q->nr; i++)\n \t\tdiff_flush_one(q->queue[i]);\n-\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@@ -669,7 +677,7 @@ void diff_addremove(int addremove, unsig\n \t * which but should not make any difference).\n \t * Feeding the same new and old to diff_change() should\n \t * also have the same effect.  diff_flush() should\n-\t * filter the identical ones out at the final output\n+\t * filter uninteresting ones out at the final output\n \t * stage.\n \t */\n \tif (reverse_diff)\n@@ -739,8 +747,8 @@ void diff_change(unsigned old_mode, unsi\n \n void diff_unmerge(const char *path)\n {\n-\tif (generate_patch)\n-\t\trun_external_diff(path, NULL, NULL, NULL, NULL);\n-\telse\n-\t\tprintf(\"U %s%c\", path, line_termination);\n+\tstruct diff_filespec *one, *two;\n+\tone = alloc_filespec(path);\n+\ttwo = alloc_filespec(path);\n+\tdiff_queue(&diff_queued_diff, one, two);\n }\ndiff --git a/diff.h b/diff.h\n--- a/diff.h\n+++ b/diff.h\n@@ -26,16 +26,24 @@ extern void diff_unmerge(const char *pat\n \n extern int diff_scoreopt_parse(const char *opt);\n \n-#define DIFF_FORMAT_HUMAN   0\n-#define DIFF_FORMAT_MACHINE 1\n-#define DIFF_FORMAT_PATCH   2\n-extern void diff_setup(int reverse, int diff_output_style);\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 \n-extern void diff_detect_rename(int, int);\n-extern void diff_pickaxe(const char *);\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_prune(void);\n+\n+extern void diffcore_pickaxe(const char *needle);\n+extern void diffcore_pathspec(const char **pathspec);\n \n extern int diff_queue_is_empty(void);\n \n-extern void diff_flush(const char **, int);\n+extern void diff_flush(int output_style);\n \n #endif /* DIFF_H */\ndiff --git a/diffcore-pathspec.c b/diffcore-pathspec.c\nnew file mode 100644\n--- /dev/null\n+++ b/diffcore-pathspec.c\n@@ -0,0 +1,63 @@\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+\n+struct path_spec {\n+\tconst char *spec;\n+\tint len;\n+};\n+\n+static int matches_pathspec(const char *name, struct path_spec *s, int cnt)\n+{\n+\tint i;\n+\tint namelen;\n+\n+\tif (cnt == 0)\n+\t\treturn 1;\n+\n+\tnamelen = strlen(name);\n+\tfor (i = 0; i < cnt; i++) {\n+\t\tint len = s->len;\n+\t\tif (! strncmp(s->spec, name, len) &&\n+\t\t    len <= namelen &&\n+\t\t    (name[len] == 0 || name[len] == '/'))\n+\t\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+void diffcore_pathspec(const char **pathspec)\n+{\n+\tstruct diff_queue_struct *q = &diff_queued_diff;\n+\tint i, speccnt;\n+\tstruct diff_queue_struct outq;\n+\tstruct path_spec *spec;\n+\n+\toutq.queue = NULL;\n+\toutq.nr = outq.alloc = 0;\n+\n+\tfor (i = 0; pathspec[i]; i++)\n+\t\t;\n+\tspeccnt = i;\n+\tspec = xmalloc(sizeof(*spec) * speccnt);\n+\tfor (i = 0; pathspec[i]; i++) {\n+\t\tspec[i].spec = pathspec[i];\n+\t\tspec[i].len = strlen(pathspec[i]);\n+\t}\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\t\tdiff_q(&outq, p);\n+\t\telse\n+\t\t\tdiff_free_filepair(p);\n+\t}\n+\tfree(q->queue);\n+\t*q = outq;\n+\treturn;\n+}\ndiff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c\n--- a/diffcore-pickaxe.c\n+++ b/diffcore-pickaxe.c\n@@ -21,7 +21,7 @@ static int contains(struct diff_filespec\n \treturn 0;\n }\n \n-void diff_pickaxe(const char *needle)\n+void diffcore_pickaxe(const char *needle)\n {\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \tunsigned long len = strlen(needle);\n@@ -32,24 +32,23 @@ void diff_pickaxe(const char *needle)\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_queue(&outq, p->one, p->two);\n+\t\t\t\tdiff_q(&outq, p);\n \t\t}\n \t\telse if (!DIFF_FILE_VALID(p->two)) {\n \t\t\tif (contains(p->one, needle, len))\n-\t\t\t\tdiff_queue(&outq, p->one, p->two);\n+\t\t\t\tdiff_q(&outq, p);\n \t\t}\n \t\telse if (contains(p->one, needle, len) !=\n \t\t\t contains(p->two, needle, len))\n-\t\t\tdiff_queue(&outq, p->one, p->two);\n-\t}\n-\tfor (i = 0; i < q->nr; i++) {\n-\t\tstruct diff_filepair *p = q->queue[i];\n-\t\tfree(p);\n+\t\t\tdiff_q(&outq, p);\n+\t\tif (onum == outq.nr)\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@@ -224,8 +224,25 @@ static int needs_to_stay(struct diff_que\n \treturn 0;\n }\n \n-void diff_detect_rename(int detect_rename,\n-\t\t\tint minimum_score)\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+\tdiglen = strspn(opt+2, \"0123456789\");\n+\tif (diglen == 0 || strlen(opt+2) != diglen)\n+\t\treturn 0; /* use default */\n+\tsscanf(opt+2, \"%d\", &num);\n+\tfor (i = 0, scale = 1; i < diglen; i++)\n+\t\tscale *= 10;\n+\n+\t/* user says num divided by scale and we say internally that\n+\t * is MAX_SCORE * num / scale.\n+\t */\n+\treturn MAX_SCORE * num / scale;\n+}\n+\n+void diffcore_rename(int detect_rename, int minimum_score)\n {\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \tstruct diff_queue_struct outq;\n@@ -235,6 +252,8 @@ void diff_detect_rename(int detect_renam\n \tint h, i, j;\n \tint num_create, num_src, dst_cnt, src_cnt;\n \n+\tif (!minimum_score)\n+\t\tminimum_score = DEFAULT_MINIMUM_SCORE;\n \toutq.queue = NULL;\n \toutq.nr = outq.alloc = 0;\n \ndiff --git a/diffcore.h b/diffcore.h\n--- a/diffcore.h\n+++ b/diffcore.h\n@@ -45,6 +45,8 @@ struct diff_filepair {\n \tint orig_order; /* the original order of insertion into the queue */\n \tint xfrm_work; /* for use by tramsformers, not by diffcore */\n };\n+#define DIFF_PAIR_UNMERGED(p) \\\n+\t(!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))\n \n struct diff_queue_struct {\n \tstruct diff_filepair **queue;\n@@ -56,5 +58,7 @@ extern struct diff_queue_struct diff_que\n extern struct diff_filepair *diff_queue(struct diff_queue_struct *,\n \t\t\t\t\tstruct diff_filespec *,\n \t\t\t\t\tstruct diff_filespec *);\n+extern void diff_q(struct diff_queue_struct *, struct diff_filepair *);\n+extern void diff_free_filepair(struct diff_filepair *);\n \n #endif\n------------------------------------------------\n\n"},{"id":"3770","messageId":"Pine.LNX.4.58.0505221108000.2307@ppc970.osdl.org","threadId":"688","inReplyTo":"7vvf5cc779.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-05-22T18:35:34Z","receivedAt":"2005-05-22T18:35:34Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 21 May 2005, Junio C Hamano wrote:\n>\n> Update the diff-raw format as Linus and I discussed, except that\n> it does not use sequence of underscore '_' letters to express\n> nonexistence.  All '0' mode is used for that purpose instead.\n> \n> The new diff-raw format can express rename/copy\n\nHaving looked at this, I have to disagree.\n\nIt can _almost_ express rename/copy, but you can't tell the two apart. In \nboth cases you have two different modes, two different SHA1's, and two \ndifferent filenames.\n\nAlso, while you can trivially tell whether a file is deleted or new (look \nat the 000000... SHA1), it is pretty illogical to give a \"filename\" for \nthe non-existent side, as in the line\n\n:000000 100644 0000000000000000000000000000000000000000 25ab9eda939ad92bb746c2419d083b1e52117a56\tdiffcore-pathspec.c\tdiffcore-pathspec.c\n\nFinally, having now looked at it some more, I've come to realize that it's \nactually pretty hard to tell the different cases apart visually (is it a \nrename or just a change), because the full pathnames can be so long that \nit's not at all immediately obvious.\n\nAnyway, I think we can trivially tweak the filename output to handle all \nthese problems.\n\nI'd suggest:\n\n - we'd continue to have two \"filename\" fields, with the existing\n   termination, but they aren't pure filenames any more, they are just\n   tab-separated (or zero-separated) \"source\" and \"destination\" fields.\n\n - if no filename exists (ie the source side of a new file, or the \n   destination side of a deleted file), output \"/dev/null\". In other\n   words, a nonexistent file is _always_ associated with mode 000000, SHA1\n   00000..  and a \"name field\" of \"/dev/null\".\n\n - ONLY IF HUMAN-READABLE: if the destination filename is the same as the\n   source, drop it (and the tab) completely. This just makes things so\n   much more readable, and it's still parseable, because the \n   line-termination is different from the inter-file termination.\n\n   NOTE! In the zero-terminated format, you can't do this, since you \n   wouldn't know where the line ended. You might drop the name completely, \n   but you'd have to have two NUL bytes. I'd argue that since this format \n   isn't human-readable anyway, you might just want to keep the filename \n   the same.\n\n - in all other cases: if the file is new, prepend a \"+\", if the file is \n   old, prepend a \"*\", and if the file goes away, prepend a \"-\". In other \n   words, the actual pathname (if it exists) always starts at the second\n   character and is always prepended by _something_ (ie there is no \n   ambiguoity with pathnames that start in -/+/*).\n\nThe above hass a few nice properties, notably you can parse the first\ncharacter of the name field, and you always know what's up:\n\n - '/' is always \"/dev/null\" aka \"no file\"\n - '+' is always \"added file\"\n - '-' is always \"removed file\"\n - '*' is always \"existing file\"\n - '\\0' (ie empty) is always \"same filename as source\"\n\nSo for the above \"create\" event, it would look like\n\n\t:000000 100644 0000.. 25ab..\t/dev/null\t+diffcore-pathspec.c\n\nwhich is visually quite obviously a create. Similarly, deletes are also \nvisually pretty obvious:\n\n\t:100644 000000 25ab.. 0000..\t-diffcore-pathspec.c\t/dev/null\n\nwhile a \"copy\" would be (git-pull-script stays around, so it gets a \"*\"):\n\n\t:100755 100755 bd89.. 17f2..\t*git-pull-script\t+git-fetch-script\n\nand a \"rename\" would be:\n\n\t:100644 100644 51bb.. 51bb..\t-diff-tree-helper.c\t+diff-helper.c\n\n(ie the difference is in the source file having a \"-\" in front of it \ninstead of a \"*\").\n\nA regular modification would be\n\n\t:100644 100644 bcd3.. c05b..\t*Documentation/git-fsck-cache.txt\n\nwhich is also very visually distinct from the other cases.\n\nWhat do you think?\n\n\t\tLinus\n"},{"id":"3772","messageId":"20050522183645.GA1866@localhost.localdomain","threadId":"688","inReplyTo":"Pine.LNX.4.58.0505221108000.2307@ppc970.osdl.org","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Niklas Hoglund","fromEmail":"nhoglund@gmail.com","sentAt":"2005-05-22T18:36:45Z","receivedAt":"2005-05-22T18:36:45Z","isPatch":true,"sender":{"key":"nhoglund@gmail.com","avatar":"https://gravatar.com/avatar/7bacd9636e9e8e382a629679b90867195d3827a68acfe3094e59788afc1df9ac?d=mp&s=160"},"body":"On Sun, May 22, 2005 at 11:35:34AM -0700, Linus Torvalds wrote:\n> :000000 100644 0000000000000000000000000000000000000000 25ab9eda939ad92bb746c2419d083b1e52117a56\tdiffcore-pathspec.c\tdiffcore-pathspec.c\n\nDon't you think it would be easier to read if the SHA1 field was, say, a\ndash and 19 spaces, instead of an obviously bogus SHA1?\n\nThis feels a bit like using 0 where NULL would be more appropriate...\n\nBest regards,\nNiklas\n"},{"id":"3771","messageId":"20050522184237.GG18500@cip.informatik.uni-erlangen.de","threadId":"688","inReplyTo":"Pine.LNX.4.58.0505221108000.2307@ppc970.osdl.org","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Thomas Glanzmann","fromEmail":"sithglan@stud.uni-erlangen.de","sentAt":"2005-05-22T18:42:37Z","receivedAt":"2005-05-22T18:42:37Z","isPatch":true,"sender":{"key":"sithglan@stud.uni-erlangen.de","avatar":null},"body":"Hello,\n\n>  - in all other cases: if the file is new, prepend a \"+\", if the file is \n>    old, prepend a \"*\", and if the file goes away, prepend a \"-\". In other \n>    words, the actual pathname (if it exists) always starts at the second\n>    character and is always prepended by _something_ (ie there is no \n>    ambiguoity with pathnames that start in -/+/*).\n\nI guess that this is only on human readable but not on the machine\nformat, right?\n\n> What do you think?\n\nSounds good. Especially that we keep the fixed fields for the machine\nparsable stuff.\n\n\tThomas\n"},{"id":"3773","messageId":"Pine.LNX.4.58.0505221203410.2307@ppc970.osdl.org","threadId":"688","inReplyTo":"20050522184237.GG18500@cip.informatik.uni-erlangen.de","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-05-22T19:05:24Z","receivedAt":"2005-05-22T19:05:24Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 22 May 2005, Thomas Glanzmann wrote:\n> \n> >  - in all other cases: if the file is new, prepend a \"+\", if the file is \n> >    old, prepend a \"*\", and if the file goes away, prepend a \"-\". In other \n> >    words, the actual pathname (if it exists) always starts at the second\n> >    character and is always prepended by _something_ (ie there is no \n> >    ambiguoity with pathnames that start in -/+/*).\n> \n> I guess that this is only on human readable but not on the machine\n> format, right?\n\nThe machine readable format has the same issue: it needs to be able to \ndistinguish between a \"copy\" (where the source remains) and a \"rename\" \n(where the source is removed). So you still need the \"*/-\" thing, and then \nyou're better off doing \"+\" and \"/\" too in the machine-readable format, to \nmake the differences be as small as possible _and_ to avoid confusion if a \npathname starts with '*' or '-'.\n\n\t\tLinus\n"},{"id":"3774","messageId":"20050522190554.GA23388@cip.informatik.uni-erlangen.de","threadId":"688","inReplyTo":"Pine.LNX.4.58.0505221203410.2307@ppc970.osdl.org","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Thomas Glanzmann","fromEmail":"sithglan@stud.uni-erlangen.de","sentAt":"2005-05-22T19:05:54Z","receivedAt":"2005-05-22T19:05:54Z","isPatch":true,"sender":{"key":"sithglan@stud.uni-erlangen.de","avatar":null},"body":"Hello,\n\n> The machine readable format has the same issue: it needs to be able to \n> distinguish between a \"copy\" (where the source remains) and a \"rename\" \n> (where the source is removed). So you still need the \"*/-\" thing, and then \n> you're better off doing \"+\" and \"/\" too in the machine-readable format, to \n> make the differences be as small as possible _and_ to avoid confusion if a \n> pathname starts with '*' or '-'.\n\nthanks, I see the point now.\n\n\tThomas\n"},{"id":"3776","messageId":"7vhdgvdqgc.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"Pine.LNX.4.58.0505221108000.2307@ppc970.osdl.org","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-22T19:13:23Z","receivedAt":"2005-05-22T19:13:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"I have to have some time to ruminate about this, but I am\nleaving for the day and won't be back till late afternoon (PDT).\n\nMy feeling, knee-jerk reaction, to what you are proposing is\nthat you are trying to go half-way back to the earlier format\nthat had addremove/change distinction.\n\nI have to disagree that having path for the non-existent side is\n_illogical_.  I ask \"what is different between tree A and tree\nB\", and the diff-raw answers \"there was no path0 in tree A but\npath0 exists in tree B; there was path1 in tree A but now path2\nis there instead in tree B\".  To me, the answer would be more\nillogical if the diff-raw said \"there was no /dev/null in tree\nA, and instead path0 exists in tree B.\", which is what your\nsuggestion makes it say if I am reading you right.  Also, again\nthis is my knee-jerk reaction without thinking things through,\nyou are hiding (not losing, because having a rename/copy entry\nthat removes a path _is_ illogical) information by not saying\nwhat no longer exists by replacing the right hand side with\n/dev/null and having the reader to infer what is removed.  I\nwant the reader to be able to say \"I can look at only right hand\nside to see what is in the right hand side tree\".  With\n/dev/null munging, it becomes necessary to sometimes look at the\nother side.\n\n"},{"id":"3777","messageId":"7vbr73dqdi.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"20050522183645.GA1866@localhost.localdomain","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-22T19:15:05Z","receivedAt":"2005-05-22T19:15:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":">>>>> \"NH\" == Niklas Hoglund <nhoglund@gmail.com> writes:\n\nNH> On Sun, May 22, 2005 at 11:35:34AM -0700, Linus Torvalds wrote:\n>> :000000 100644 0000000000000000000000000000000000000000 25ab9eda939ad92bb746c2419d083b1e52117a56\tdiffcore-pathspec.c\tdiffcore-pathspec.c\n\nNH> Don't you think it would be easier to read if the SHA1 field was, say, a\nNH> dash and 19 spaces, instead of an obviously bogus SHA1?\n\nMy understanding of the purpose of the diff-raw format is\nprimarily about machine readablility; 0{40} was far easier to\ncode cleanly---please see why in diff-helper.c.\n\n\n"},{"id":"3778","messageId":"7v64xbdq4y.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"Pine.LNX.4.58.0505221203410.2307@ppc970.osdl.org","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-22T19:20:13Z","receivedAt":"2005-05-22T19:20:13Z","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> The machine readable format has the same issue: it needs to be able to \nLT> distinguish between a \"copy\" (where the source remains) and a \"rename\" \nLT> (where the source is removed).\n\nWhy?  If the same path appears later as the left hand side then\nit is a copy otherwise it is a rename.  Please see what\ndiffcore-rename does when assigning to dp->xfrm_msg.  \n\nWhat I've been trying hard so far was to keep diff_filepair not\nto be too specific to rename/copy.  What I do not like about\nwhat is being proposed is that it would force me to introduce\nthis \"is this a copy or a rename\" flag to that structure.\n\n"},{"id":"3780","messageId":"7vy8a7cavf.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"7v64xbdq4y.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-22T19:35:16Z","receivedAt":"2005-05-22T19:35:16Z","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>>>>> \"LT\" == Linus Torvalds <torvalds@osdl.org> writes:\nLT> The machine readable format has the same issue: it needs to be able to \nLT> distinguish between a \"copy\" (where the source remains) and a \"rename\" \nLT> (where the source is removed).\n\nJCH> Why?  If the same path appears later as the left hand side then\nJCH> it is a copy otherwise it is a rename.  Please see what\nJCH> diffcore-rename does when assigning to dp->xfrm_msg.  \n\nJCH> What I've been trying hard so far was to keep diff_filepair not\nJCH> to be too specific to rename/copy.  What I do not like about\nJCH> what is being proposed is that it would force me to introduce\nJCH> this \"is this a copy or a rename\" flag to that structure.\n\nLinus, another way of saying the above is this.\n\nThink about the example in test that munge COPYING file.  What\nwas recorded in the two trees as the result of\n\n    sed $munge_1 <COPYING >COPYING.1\n    sed $munge_2 <COPYING COPYING.2\n    rm COPYING\n\nwill be expressed as copy-edit of COPYING.1 and rename-edit of\nCOPYING.2 (both from COPYING).  But that is just _one_\ninterpretation diffcore-rename _arbitrarily_ makes.\n\nIt could have been copy-edit of COPYING.2 and rename-edit of\nCOPYING.1 (both from COPYING).  If you make the change you are\nproposing to diff-raw format, reordering diff-raw entries stops\nmaking sense, while the current format allows us to.  \n\nIt is similar to your argument of not _recording_ renames on\ncommit but _tracking_ it upon inspection.  I deliberatly chose\nnot to record rename/copy distinction in diff-raw --- it is to\nbe inferred from which entry touches the src _last_.  Everything\nbut the last one to touch the same path is copy, and the last\none is rename.\n\n"},{"id":"3781","messageId":"Pine.LNX.4.58.0505221259110.2307@ppc970.osdl.org","threadId":"688","inReplyTo":"7vy8a7cavf.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-05-22T20:24:17Z","receivedAt":"2005-05-22T20:24:17Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 22 May 2005, Junio C Hamano wrote:\n>\n> I deliberatly chose not to record rename/copy distinction in diff-raw\n> --- it is to be inferred from which entry touches the src _last_.  \n> Everything but the last one to touch the same path is copy, and the last\n> one is rename.\n\nMy counter-example: there is no rename at all, just a copy.\n\nThe current raw-diff output simply _cannot_ distinguish this from the case\nwhere there is a rename, as far as I can tell. \n\nBasically, imagine doing something like this:\n\n\tmkdir test-copy\n\tcd test-copy\n\tgit-init-db \n\tcp ~/v2.6/linux/kernel/sched.c .\n\tgit-update-cache --add sched.c \n\techo duh | git-commit-tree $(git-write-tree) > .git/HEAD\n\tsed 's/sched\\.c/other\\.c/g' < sched.c > other.c\n\tgit-update-cache --add other.c \n\techo duh2 | git-commit-tree $(git-write-tree) -p HEAD\n\nand dammit, if \"other.c\" doesn't show up as a copy (with small\ndifferences) of sched.c, then the whole diff algorithm is broken and\nclearly isn't doing copy-diffs right at all (right now it doesn't do it\nsince diff-tree doesn't even do the \"check copies against old\", but that's\na different issue).\n\nIn other words, how do you propose to _describe_ that copy?\n\nThe whole point to the \"raw diff\" format is that you should be able to \nget the same output with\n\n\tgit-diff-tree -C HEAD | git-diff-helper\n\tgit-diff-tree -C -p HEAD\n\nand right now I don't see how you can do that.. And if you can't do that,\nthen the raw diff format is pointless. It's not \"raw diff\" at all, it's a\n\"less capable diff\" format.\n\nTHAT is why I think the raw diff output is broken right now. All the rest \nwas about just trying to also make it a bit more readable while at it.\n\nTry it. I added in the \"&& detect_rename < 2\" thing to diff-tree.c, and I \nget:\n\n\tgit-whatchanged -C --root | git-diff-helper | less -S\n\nresulting in:\n\n\tdiff-tree eb773598aa29bb642d6016a0d5961c80628ce490 (from 8c63fcad1f27ceed6825110aa806349dad99d89f)\n\tAuthor: Linus Torvalds <torvalds@ppc970.osdl.org>\n\tDate:   Sun May 22 13:17:40 2005 -0700\n\t    \n\t    duh2\n\t\n\tdiff --git a/sched.c b/other.c\n\t--- a/sched.c\n\t+++ b/other.c\n\t@@ -1,5 +1,5 @@\n\t /*\n\t- *  kernel/sched.c\n\t+ *  kernel/other.c\n\t  *\n\t  *  Kernel scheduler and related syscalls\n\t  *\n\nie it does NOT say it is a copy, because it _cannot_ say it is a copy. It \njust doesn't know. A copy and a rename look exactly the same in the raw \ndiff output, and that's a BUG.\n\n\t\tLinus\n\n"},{"id":"3787","messageId":"7vis1adfvq.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"Pine.LNX.4.58.0505221259110.2307@ppc970.osdl.org","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-22T23:01:45Z","receivedAt":"2005-05-22T23:01:45Z","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, 22 May 2005, Junio C Hamano wrote:\n>> \n>> I deliberatly chose not to record rename/copy distinction in diff-raw\n>> --- it is to be inferred from which entry touches the src _last_.  \n>> Everything but the last one to touch the same path is copy, and the last\n>> one is rename.\n\nLT> My counter-example: there is no rename at all, just a copy.\n\nMy wording was wrong.  If you do not use the helper, you should\nbe able to get copy and in-place edit or no-modification (I\ncollectively call them \"stay\" in diffcore-rename.c).  If it does\nnot work then you have spotted a bug in the implementation but\nnot the design.  Anyway, I should have said:\n\n    Everything but the last are copies.  If the last one have\n    different src and dst, then it is a rename.  Otherwise it\n    is a \"stay\".\n\nLT> Try it. I added in the \"&& detect_rename < 2\" thing to diff-tree.c, and I \nLT> get:\n\nLT> \tgit-whatchanged -C --root | git-diff-helper | less -S\n\nLT> resulting in:\n\nThat's not a counter-example.  You are agreeing to what I said in\nthis message:\n\n    To: Linus Torvalds <torvalds@osdl.org>\n    Cc: git@vger.kernel.org\n    Subject: [PATCH] Teach diff-tree to report unmodified paths for -C option.\n    Date: Sat, 21 May 2005 03:11:49 -0700\n    Message-ID: <7vpsvkj3bu.fsf_-_@assigned-by-dhcp.cox.net>\n\n    ...\n\n    Another useless comment.  For obvious reasons, there is nothing\n    we can do about the diff-helper to add \"the other half of copy\n    detection information\", because what it can tell diff-core is\n    limited to its input, which usually is just differences prepared\n    by somebody else, and it cannot know anything about unchanged\n    files.  When I started pushing '-p' flag to diff-tree family, I\n    remember that your reaction was neutral to moderately negative\n    (\"I'd tolerate, although I think it is redundant and you are not\n    even generating diff yourself anyway\" as opposed to \"That's just\n    great\").  I think now you would thank me for shoving the diff\n    interface into them ;-).\n\nIf you want the diff-helper to be able to the full scale copy\ndetection, you must _feed_ the full information including \"stay\"\nentries to it.  Unfortunately, the current diffcore interface\ndoes not let the callers (diff-tree family) to tell it to keep\nthe \"stay\" entries in its output.  I've been working on that\npart this morning before this discussion started, so that\nelectively they can tell the diffcore layer not to do the \"stay\"\npruning (\"stay pruning\" will simply become another diffcore\ntransformation).\n\nI have to leave again now, but I promise you'll hear back from\nme on this one later tonight (or tomorrow evening at the latest\nif things do not work out).\n\n"},{"id":"3788","messageId":"Pine.LNX.4.58.0505221611020.2307@ppc970.osdl.org","threadId":"688","inReplyTo":"7vis1adfvq.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-05-22T23:14:23Z","receivedAt":"2005-05-22T23:14:23Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 22 May 2005, Junio C Hamano wrote:\n> \n>     Everything but the last are copies.  If the last one have\n>     different src and dst, then it is a rename.  Otherwise it\n>     is a \"stay\".\n\nIf so, I disagree. Totally.\n\nYou seem to think that it's a feature that you can't get the same output \nout of git-diff-helper, and I think that's not a feature, but a total bug.\n\n> LT> resulting in:\n> \n> That's not a counter-example.  You are agreeing to what I said in\n> this message:\n\nNo, I'm not agreeing at all. I'm saying that this is unacceptable, and if \nthis was intentional, as you seem to be saying, then it was in my opinion \na bad idea. We might as well go back to the original diff format, which \nhad other problems, but they were no worse than the new one.\n\nBasically, with the new format as-is, renames and copies cannot be \ndescribed sanely. That was exactly the same problem as the old format had, \nexcept the old format was less verbose. So why do the new format at all?\n\nI'm arguing that we should consider it a _requirement_ that \"raw diffs\" \ncan be translated into te same thing the \"-p\" flag internally does.\n\n\t\tLinus\n"},{"id":"3789","messageId":"7vu0kubwyg.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"Pine.LNX.4.58.0505221611020.2307@ppc970.osdl.org","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-23T00:35:51Z","receivedAt":"2005-05-23T00:35:51Z","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> You seem to think that it's a feature that you can't get the same output \nLT> out of git-diff-helper, and I think that's not a feature, but a total bug.\n\nI do agree that it is a real problem.\n\nAnd I think you've filled some holes in the idea I originally\nhad while working on the following (the part you omitted from\nthe quote):\n\n    ...  Unfortunately, the current diffcore interface does not\n    let the callers (diff-tree family) to tell it to keep the\n    \"stay\" entries in its output.  I've been working on that\n    part this morning before this discussion started, so that\n    electively they can tell the diffcore layer not to do the\n    \"stay\" pruning (\"stay pruning\" will simply become another\n    diffcore transformation).\n\nThe change I am making right now is essentially what the above\nparagraph says, with your \"hole-filling\", to do something like\nthis:\n\n (1) diff-tree family feeds unmodified entries to the diffcore,\n     in addition to changes.  diffcore-rename matches and merges\n     them into rename/copy; diffcore-pickaxe filters; other\n     diffcore transformations may later be written (e.g\n     reordering the patch so that .h files come first before .c\n     files).  The result of the sequence of these\n     transformations is fed to the diff_flush() for output.\n     Essentially this is the same architecture as we already\n     have.\n\n (2) diff_flush() currently prunes _all_ the unmodified entries\n     just before output happens.  This is a _bug_ as you said.\n     To fix this, first, separate that pruning part (which is\n     currently embedded in diff_flush_one as the first\n     statement) out, so this pruning happens independently and\n     before diff_flush() happens; and make the pruning more\n     careful, not to prune entries that we _need_ to express\n     rename/copy distinction in diff-raw output.  Namely, it\n     should keep the unmodified entry for a path _if_ the path\n     is to be used as the source of a rename or copy entry.\n     Also diff-patch output needs to be taught to take notice\n     and ignore such entries.\n\n     Optionally, the callers may want to tell diffcore not to do\n     _any_ pruning, making diff-raw output similar to a merge of\n     ls-files --stage outputs of two trees.\n\nLT> I'm arguing that we should consider it a _requirement_ that \"raw diffs\" \nLT> can be translated into te same thing the \"-p\" flag internally does.\n\nTo this, I 100% agree, and I think it is doable.  Give me a bit\nmore time for testing and tweaking.\n\n"},{"id":"3790","messageId":"Pine.LNX.4.58.0505221751070.2307@ppc970.osdl.org","threadId":"688","inReplyTo":"7vu0kubwyg.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-05-23T01:07:48Z","receivedAt":"2005-05-23T01:07:48Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 22 May 2005, Junio C Hamano wrote:\n> \n>  (2) diff_flush() currently prunes _all_ the unmodified entries\n>      just before output happens.  This is a _bug_ as you said.\n>      To fix this, first, separate that pruning part\n\n\nI don't understand why this is so hard.\n\nThe built-in diff knows to do the right thing. That means that the \ninformation is there, in xfrm_msg. \n\nInstead of making a big deal out of this, why not admit that \"xfrm_msg\" \nwas a mistake, and it should be replaced with just a flag about what that \nthing is. Make the flag be \"diff\", \"copy\" or \"rename\". Add another field \nto say how good a match it was, and then let the people who now use \nxfrm_msg generate the string instead. \n\nThe fact is, we alreadt _have_ the information, but it's been corrupted\ninto a string too early, making it hard to get at. No?\n\n\t\tLinus\n"},{"id":"3791","messageId":"7vpsvibuag.fsf@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"Pine.LNX.4.58.0505221751070.2307@ppc970.osdl.org","subject":"Re: [PATCH] The diff-raw format updates.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-23T01:33:27Z","receivedAt":"2005-05-23T01:33:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Delaying the message generation to just before output is part of\nthe fix I've been working on, yes.  But there are some other\nissues.  Give me some more time as I asked.\n\n"},{"id":"3792","messageId":"7vhdgubmcu.fsf_-_@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"7vekbzfaze.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] Be careful with symlinks when detecting renames and copies.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-23T04:24:49Z","receivedAt":"2005-05-23T04:24:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus, this is *not* based on the fixed rename/copy fix I am\ngoing to send out shortly, but comes before that due to patch\ndependency (the order I happened to have done things, that is).\n\nPlease apply this one before the rename/copy fix.\n\n------------\nEarlier round was not treating symbolic links carefully enough,\nand would have produced diff output that renamed/copied then\nedited the contents of a symbolic link, which made no practical\nsense.  Change it to detect only pure renames.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiffcore-rename.c              |   24 ++++++++------\nt/t4004-diff-rename-symlink.sh |   66 +++++++++++++++++++++++++++++++++++++++++\n2 files changed, 80 insertions(+), 10 deletions(-)\nnew file (100755): t/t4004-diff-rename-symlink.sh\n\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -20,7 +20,7 @@ static void diff_rename_pool_add(struct \n \t\t\t\t struct diff_filespec *s)\n {\n \tif (S_ISDIR(s->mode))\n-\t\treturn;  /* rename/copy patch for tree does not make sense. */\n+\t\treturn;  /* no trees, please */\n \n \tif (pool->alloc <= pool->nr) {\n \t\tpool->alloc = alloc_nr(pool->alloc);\n@@ -71,6 +71,13 @@ static int estimate_similarity(struct di\n \tunsigned long delta_size, base_size;\n \tint score;\n \n+\t/* We deal only with regular files.  Symlink renames are handled\n+\t * only when they are exact matches --- in other words, no edits\n+\t * after renaming.\n+\t */\n+\tif (!S_ISREG(src->mode) || !S_ISREG(dst->mode))\n+\t\treturn 0;\n+\n \tdelta_size = ((src->size < dst->size) ?\n \t\t      (dst->size - src->size) : (src->size - dst->size));\n \tbase_size = ((src->size < dst->size) ? src->size : dst->size);\n@@ -268,7 +275,7 @@ void diffcore_rename(int detect_rename, \n \t\tstruct diff_filepair *p = q->queue[i];\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\tcontinue; /* unmerged */\n \t\t\telse\n \t\t\t\tdiff_rename_pool_add(&created, p->two);\n \t\telse if (!DIFF_FILE_VALID(p->two))\n@@ -360,12 +367,9 @@ void diffcore_rename(int detect_rename, \n \tfor (i = 0; i < q->nr; i++) {\n \t\tstruct diff_filepair *dp, *p = q->queue[i];\n \t\tif (!DIFF_FILE_VALID(p->one)) {\n-\t\t\tif (DIFF_FILE_VALID(p->two)) {\n-\t\t\t\t/* creation */\n-\t\t\t\tdp = diff_queue(&outq, p->one, p->two);\n-\t\t\t\tdp->xfrm_work = 4;\n-\t\t\t}\n-\t\t\t/* otherwise it is a nonsense; just ignore it */\n+\t\t\t/* creation or unmerged entries */\n+\t\t\tdp = diff_queue(&outq, p->one, p->two);\n+\t\t\tdp->xfrm_work = 4;\n \t\t}\n \t\telse if (!DIFF_FILE_VALID(p->two)) {\n \t\t\t/* deletion */\n@@ -394,7 +398,7 @@ void diffcore_rename(int detect_rename, \n \tfor (i = 0; i < outq.nr; i++) {\n \t\tstruct diff_filepair *p = outq.queue[i];\n \t\tif (!DIFF_FILE_VALID(p->one)) {\n-\t\t\t/* created */\n+\t\t\t/* created or unmerged */\n \t\t\tif (p->two->xfrm_flags & RENAME_DST_MATCHED)\n \t\t\t\t; /* rename/copy created it already */\n \t\t\telse\n@@ -443,7 +447,7 @@ void diffcore_rename(int detect_rename, \n \t\telse\n \t\t\t/* otherwise it is a modified (or stayed) entry */\n \t\t\tdiff_queue(q, p->one, p->two);\n-\t\tfree(p);\n+\t\tdiff_free_filepair(p);\n \t}\n \n \tfree(outq.queue);\ndiff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh\nnew file mode 100755\n--- /dev/null\n+++ b/t/t4004-diff-rename-symlink.sh\n@@ -0,0 +1,66 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2005 Junio C Hamano\n+#\n+\n+test_description='More rename detection tests.\n+\n+The rename detection logic should be able to detect pure rename or\n+copy of symbolic links, but should not produce rename/copy followed\n+by an edit for them.\n+'\n+. ./test-lib.sh\n+\n+test_expect_success \\\n+    'prepare reference tree' \\\n+    'echo xyzzy | tr -d '\\\\\\\\'012 >yomin &&\n+     ln -s xyzzy frotz &&\n+    git-update-cache --add frotz yomin &&\n+    tree=$(git-write-tree) &&\n+    echo $tree'\n+\n+test_expect_success \\\n+    'prepare work tree' \\\n+    'mv frotz rezrov &&\n+     rm -f yomin &&\n+     ln -s xyzzy nitfol &&\n+     ln -s xzzzy bozbar &&\n+    git-update-cache --add --remove frotz rezrov nitfol bozbar yomin'\n+\n+# tree has frotz pointing at xyzzy, and yomin that contains xyzzy to\n+# confuse things.  work tree has rezrov (xyzzy) nitfol (xyzzy) and\n+# bozbar (xzzzy).\n+# rezrov and nitfol are rename/copy of frotz and bozbar should be\n+# a new creation.\n+\n+GIT_DIFF_OPTS=--unified=0 git-diff-cache -M -p $tree >current\n+cat >expected <<\\EOF\n+diff --git a/frotz b/nitfol\n+similarity index 100%\n+copy from frotz\n+copy to nitfol\n+diff --git a/frotz b/rezrov\n+similarity index 100%\n+rename old frotz\n+rename new rezrov\n+diff --git a/yomin b/yomin\n+deleted file mode 100644\n+--- a/yomin\n++++ /dev/null\n+@@ -1 +0,0 @@\n+-xyzzy\n+\\ No newline at end of file\n+diff --git a/bozbar b/bozbar\n+new file mode 120000\n+--- /dev/null\n++++ b/bozbar\n+@@ -0,0 +1 @@\n++xzzzy\n+\\ No newline at end of file\n+EOF\n+\n+test_expect_success \\\n+    'validate diff output' \\\n+    'diff -u current expected'\n+\n+test_done\n------------------------------------------------\n\n"},{"id":"3793","messageId":"7vd5ribmam.fsf_-_@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"7vy8a7cavf.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] Rename/copy detection fix.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-23T04:26:09Z","receivedAt":"2005-05-23T04:26:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The rename/copy detection logic in earlier round was only good\nenough to show patch output and discussion on the mailing list\nabout the diff-raw format updates revealed many problems with\nit.  This patch fixes all the ones known to me, without making\nthings I want to do later impossible, mostly related to patch\nreordering.\n\n (1) Earlier rename/copy detector determined which one is rename\n     and which one is copy too early, which made it impossible\n     to later introduce diffcore transformers to reorder\n     patches.  This patch fixes it by moving that logic to the\n     very end of the processing.\n\n (2) Earlier output routine diff_flush() was pruning all the\n     \"no-change\" entries indiscriminatingly.  This was done due\n     to my false assumption that one of the requirements in the\n     diff-raw output was not to show such an entry (which\n     resulted in my incorrect comment about \"diff-helper never\n     being able to be equivalent to built-in diff driver\").  My\n     special thanks go to Linus for correcting me about this.\n     When we produce diff-raw output, for the downstream to be\n     able to tell renames from copies, sometimes it _is_\n     necessary to output \"no-change\" entries, and this patch\n     adds diffcore_prune() function for doing it.\n\n (3) Earlier diff_filepair structure was trying to be not too\n     specific about rename/copy operations, but the purpose of\n     the structure was to record one or two paths, which _was_\n     indeed about rename/copy.  This patch discards xfrm_msg\n     field which was trying to be generic for this wrong reason,\n     and introduces a couple of fields (rename_score and\n     rename_rank) that are explicitly specific to rename/copy\n     logic.  One thing to note is that the information in a\n     single diff_filepair structure _still_ does not distinguish\n     renames from copies, and it is deliberately so.  This is to\n     allow patches to be reordered in later stages.\n\n (4) This patch also adds some tests about diff-raw format\n     output and makes sure that necessary \"no-change\" entries\n     appear on the output.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff.c                   |  145 +++++++++++++++++++++++++++++++----------------\ndiffcore-pathspec.c      |    2 \ndiffcore-pickaxe.c       |    2 \ndiffcore-rename.c        |  112 +++++++++++-------------------------\ndiffcore.h               |   13 +++-\nt/t4003-diff-rename-1.sh |   26 ++++----\nt/t4005-diff-rename-2.sh |   82 ++++++++++++++++++++++++++\n7 files changed, 244 insertions(+), 138 deletions(-)\nnew file (100644): t/t4005-diff-rename-2.sh\n\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -283,12 +283,6 @@ int diff_populate_filespec(struct diff_f\n \treturn 0;\n }\n \n-void diff_free_filepair(struct diff_filepair *p)\n-{\n-\tfree(p->xfrm_msg);\n-\tfree(p);\n-}\n-\n void diff_free_filespec_data(struct diff_filespec *s)\n {\n \tif (s->should_free)\n@@ -501,9 +495,9 @@ struct diff_filepair *diff_queue(struct \n \tstruct diff_filepair *dp = xmalloc(sizeof(*dp));\n \tdp->one = one;\n \tdp->two = two;\n-\tdp->xfrm_msg = NULL;\n+\tdp->score = 0;\n \tdp->orig_order = queue->nr;\n-\tdp->xfrm_work = 0;\n+\tdp->rename_rank = 0;\n \tdiff_q(queue, dp);\n \treturn dp;\n }\n@@ -522,23 +516,7 @@ static void diff_flush_raw(struct diff_f\n \t       p->two->path, line_termination);\n }\n \n-static void diff_flush_patch(struct diff_filepair *p)\n-{\n-\tconst char *name, *other;\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)) ||\n-\t    (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))\n-\t\treturn; /* no tree diffs in patch format */ \n-\n-\tif (DIFF_PAIR_UNMERGED(p))\n-\t\trun_external_diff(name, NULL, NULL, NULL, NULL);\n-\telse\n-\t\trun_external_diff(name, other, p->one, p->two, p->xfrm_msg);\n-}\n-\n-static int uninteresting(struct diff_filepair *p)\n+int diff_unmodified_pair(struct diff_filepair *p)\n {\n \t/* This function is written stricter than necessary to support\n \t * the currently implemented transformers, but the idea is to\n@@ -570,12 +548,70 @@ static int uninteresting(struct diff_fil\n \treturn 0;\n }\n \n+static void diff_flush_patch(struct diff_filepair *p, const char *msg)\n+{\n+\tconst char *name, *other;\n+\n+\t/* diffcore_prune() keeps \"stay\" entries for diff-raw\n+\t * copy/rename detection, but when we are generating\n+\t * patches we do not need them.\n+\t */\n+\tif (diff_unmodified_pair(p))\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)) ||\n+\t    (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))\n+\t\treturn; /* no tree diffs in patch format */ \n+\n+\tif (DIFF_PAIR_UNMERGED(p))\n+\t\trun_external_diff(name, NULL, NULL, NULL, NULL);\n+\telse\n+\t\trun_external_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+static int diff_used_as_source(struct diff_queue_struct *q, int lim,\n+\t\t\t       struct diff_filespec *it)\n+{\n+\tint i;\n+\tfor (i = 0; i < lim; i++) {\n+\t\tstruct diff_filepair *p = q->queue[i++];\n+\t\tif (!strcmp(p->one->path, it->path))\n+\t\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n void diffcore_prune(void)\n {\n \t/*\n \t * Although rename/copy detection wants to have \"no-change\"\n \t * entries fed into them, the downstream do not need to see\n-\t * them.  This function removes such entries.\n+\t * them, unless we had rename/copy for the same path earlier.\n+\t * This function removes such entries.\n \t *\n \t * The applications that use rename/copy should:\n \t *\n@@ -585,6 +621,7 @@ void diffcore_prune(void)\n \t * (3) call diffcore_prune\n \t * (4) call other diffcore_xxx that do not need to see\n \t *     \"no-change\" entries.\n+\t * (5) call diff_flush().\n \t */\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \tstruct diff_queue_struct outq;\n@@ -595,22 +632,21 @@ void diffcore_prune(void)\n \n \tfor (i = 0; i < q->nr; i++) {\n \t\tstruct diff_filepair *p = q->queue[i];\n-\t\tif (!uninteresting(p))\n+\t\tif (!diff_unmodified_pair(p) ||\n+\t\t    diff_used_as_source(q, i, p->one))\n \t\t\tdiff_q(&outq, p);\n \t\telse\n-\t\t\tdiff_free_filepair(p);\n+\t\t\tfree(p);\n \t}\n \tfree(q->queue);\n \t*q = outq;\n \treturn;\n }\n \n-static void diff_flush_one(struct diff_filepair *p)\n+static void diff_flush_one(struct diff_filepair *p, const char *msg)\n {\n-\tif (uninteresting(p))\n-\t\treturn;\n \tif (generate_patch)\n-\t\tdiff_flush_patch(p);\n+\t\tdiff_flush_patch(p, msg);\n \telse\n \t\tdiff_flush_raw(p);\n }\n@@ -618,14 +654,7 @@ static void diff_flush_one(struct diff_f\n int diff_queue_is_empty(void)\n {\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n-\tint i;\n-\n-\tfor (i = 0; i < q->nr; i++) {\n-\t\tstruct diff_filepair *p = q->queue[i];\n-\t\tif (!uninteresting(p))\n-\t\t\treturn 0;\n-\t}\n-\treturn 1;\n+\treturn q->nr == 0;\n }\n \n void diff_flush(int diff_output_style)\n@@ -646,13 +675,35 @@ void diff_flush(int diff_output_style)\n \t\tgenerate_patch = 1;\n \t\tbreak;\n \t}\n-\tfor (i = 0; i < q->nr; i++)\n-\t\tdiff_flush_one(q->queue[i]);\n+\tfor (i = 0; i < q->nr; i++) {\n+\t\tchar msg_[PATH_MAX*2+200], *msg = NULL;\n+\t\tstruct diff_filepair *p = q->queue[i];\n+\t\tif (strcmp(p->one->path, p->two->path)) {\n+\t\t\t/* This is rename or copy.  Which one is it? */\n+\t\t\tif (diff_needs_to_stay(q, i+1, p->one)) {\n+\t\t\t\tsprintf(msg_,\n+\t\t\t\t\t\"similarity index %d%%\\n\"\n+\t\t\t\t\t\"copy from %s\\n\"\n+\t\t\t\t\t\"copy to %s\\n\",\n+\t\t\t\t\t(int)(0.5 + p->score * 100/MAX_SCORE),\n+\t\t\t\t\tp->one->path, p->two->path);\n+\t\t\t}\n+\t\t\telse\n+\t\t\t\tsprintf(msg_,\n+\t\t\t\t\t\"similarity index %d%%\\n\"\n+\t\t\t\t\t\"rename old %s\\n\"\n+\t\t\t\t\t\"rename new %s\\n\",\n+\t\t\t\t\t(int)(0.5 + p->score * 100/MAX_SCORE),\n+\t\t\t\t\tp->one->path, p->two->path);\n+\t\t\tmsg = msg_;\n+\t\t}\n+\t\tdiff_flush_one(p, msg);\n+\t}\n+\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->xfrm_msg);\n \t\tfree(p);\n \t}\n \tfree(q->queue);\n@@ -674,10 +725,10 @@ void diff_addremove(int addremove, unsig\n \t * entries to the diff-core.  They will be prefixed\n \t * with something like '=' or '*' (I haven't decided\n \t * which but should not make any difference).\n-\t * Feeding the same new and old to diff_change() should\n-\t * also have the same effect.  diff_flush() should\n-\t * filter uninteresting ones out at the final output\n-\t * stage.\n+\t * Feeding the same new and old to diff_change() \n+\t * also has the same effect.  diffcore_prune() should\n+\t * be used to filter uninteresting ones out before the\n+\t * final output happens.\n \t */\n \tif (reverse_diff)\n \t\taddremove = (addremove == '+' ? '-' :\ndiff --git a/diffcore-pathspec.c b/diffcore-pathspec.c\n--- a/diffcore-pathspec.c\n+++ b/diffcore-pathspec.c\n@@ -55,7 +55,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\tdiff_free_filepair(p);\n+\t\t\tfree(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@@ -48,7 +48,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\tdiff_free_filepair(p);\n+\t\t\tfree(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@@ -119,27 +119,34 @@ static void record_rename_pair(struct di\n \t\t\t       int rank,\n \t\t\t       int score)\n {\n-\t/* The rank is used to sort the final output, because there\n-\t * are certain dependencies.\n+\t/*\n+\t * These ranks are used to sort the final output, because there\n+\t * are certain dependencies:\n \t *\n-\t *  - rank #0 depends on deleted ones.\n-\t *  - rank #1 depends on kept files before they are modified.\n-\t *  - rank #2 depends on kept files after they are modified;\n-\t *    currently not used.\n-\t *\n-\t * Therefore, the final output order should be:\n-\t *\n-\t *  1. rank #0 rename/copy diffs.\n+\t *  1. rename/copy that depends on deleted ones.\n \t *  2. deletions in the original.\n-\t *  3. rank #1 rename/copy diffs.\n-\t *  4. additions and modifications in the original.\n-\t *  5. rank #2 rename/copy diffs; currently not used.\n+\t *  3. rename/copy that depends on the pre-edit image of kept files.\n+\t *  4. additions, modifications and no-modifications in the original.\n+\t *  5. rename/copy that depends on the post-edit image of kept files\n+\t *     (note that we currently do not detect such rename/copy).\n \t *\n-\t * To achieve this sort order, we give xform_work the number\n-\t * above.\n+\t * The downstream diffcore transformers are free to reorder\n+\t * the entries as long as they keep file pairs that has the\n+\t * same p->one->path in earlier rename_rank to appear before\n+\t * later ones.  This ordering is used by the diff_flush()\n+\t * logic to tell renames from copies, and also used by the\n+\t * diffcore_prune() logic to omit unnecessary\n+\t * \"no-modification\" entries.\n+\t *\n+\t * To the final output routine, and in the diff-raw format\n+\t * output, a rename/copy that is based on a path that has a\n+\t * later entry that shares the same p->one->path and is not a\n+\t * deletion is a copy.  Otherwise it is a rename.\n \t */\n+\n \tstruct diff_filepair *dp = diff_queue(outq, src, dst);\n-\tdp->xfrm_work = (rank * 2 + 1) | (score<<RENAME_SCORE_SHIFT);\n+\tdp->rename_rank = rank * 2 + 1;\n+\tdp->score = score;\n \tdst->xfrm_flags |= RENAME_DST_MATCHED;\n }\n \n@@ -161,10 +168,8 @@ static void debug_filepair(const struct \n {\n \tdebug_filespec(p->one, i, \"one\");\n \tdebug_filespec(p->two, i, \"two\");\n-\tfprintf(stderr, \"pair flags %d, orig order %d, score %d\\n\",\n-\t\t(p->xfrm_work & ((1<<RENAME_SCORE_SHIFT) - 1)),\n-\t\tp->orig_order,\n-\t\t(p->xfrm_work >> RENAME_SCORE_SHIFT));\n+\tfprintf(stderr, \"pair rank %d, orig order %d, score %d\\n\",\n+\t\tp->rename_rank, p->orig_order, p->score);\n }\n \n static void debug_queue(const char *msg, struct diff_queue_struct *q)\n@@ -191,8 +196,8 @@ static int rank_compare(const void *a_, \n {\n \tconst struct diff_filepair *a = *(const struct diff_filepair **)a_;\n \tconst struct diff_filepair *b = *(const struct diff_filepair **)b_;\n-\tint a_rank = a->xfrm_work & ((1<<RENAME_SCORE_SHIFT) - 1);\n-\tint b_rank = b->xfrm_work & ((1<<RENAME_SCORE_SHIFT) - 1);\n+\tint a_rank = a->rename_rank;\n+\tint b_rank = b->rename_rank;\n \n \tif (a_rank != b_rank)\n \t\treturn a_rank - b_rank;\n@@ -209,28 +214,6 @@ static int score_compare(const void *a_,\n \treturn b->score - a->score;\n }\n \n-static int needs_to_stay(struct diff_queue_struct *q, int i,\n-\t\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 or rename/copy,\n-\t\t * so we cannot rename it out.\n-\t\t */\n-\t\treturn 1;\n-\t}\n-\treturn 0;\n-}\n-\n int diff_scoreopt_parse(const char *opt)\n {\n \tint diglen, num, scale, i;\n@@ -359,27 +342,24 @@ void diffcore_rename(int detect_rename, \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 xfrm_work.\n-\t *\n-\t * Note that we have not annotated the diff_filepair with any comment\n-\t * so there is nothing other than p to free.\n+\t * to assign rename_rank.\n \t */\n \tfor (i = 0; i < q->nr; i++) {\n \t\tstruct diff_filepair *dp, *p = q->queue[i];\n \t\tif (!DIFF_FILE_VALID(p->one)) {\n \t\t\t/* creation or unmerged entries */\n \t\t\tdp = diff_queue(&outq, p->one, p->two);\n-\t\t\tdp->xfrm_work = 4;\n+\t\t\tdp->rename_rank = 4;\n \t\t}\n \t\telse if (!DIFF_FILE_VALID(p->two)) {\n \t\t\t/* deletion */\n \t\t\tdp = diff_queue(&outq, p->one, p->two);\n-\t\t\tdp->xfrm_work = 2;\n+\t\t\tdp->rename_rank = 2;\n \t\t}\n \t\telse {\n \t\t\t/* modification, or stay as is */\n \t\t\tdp = diff_queue(&outq, p->one, p->two);\n-\t\t\tdp->xfrm_work = 4;\n+\t\t\tdp->rename_rank = 4;\n \t\t}\n \t\tfree(p);\n \t}\n@@ -415,39 +395,21 @@ void diffcore_rename(int detect_rename, \n \t\t\t/* rename or copy */\n \t\t\tstruct diff_filepair *dp =\n \t\t\t\tdiff_queue(q, p->one, p->two);\n-\t\t\tint msglen = (strlen(p->one->path) +\n-\t\t\t\t      strlen(p->two->path) + 100);\n-\t\t\tint score = (p->xfrm_work >> RENAME_SCORE_SHIFT);\n-\t\t\tdp->xfrm_msg = xmalloc(msglen);\n+\t\t\tdp->score = p->score;\n \n \t\t\t/* if we have a later entry that is a rename/copy\n \t\t\t * that depends on p->one, then we copy here.\n \t\t\t * otherwise we rename it.\n \t\t\t */\n-\t\t\tif (needs_to_stay(&outq, i+1, p->one)) {\n-\t\t\t\t/* copy it */\n-\t\t\t\tsprintf(dp->xfrm_msg,\n-\t\t\t\t\t\"similarity index %d%%\\n\"\n-\t\t\t\t\t\"copy from %s\\n\"\n-\t\t\t\t\t\"copy to %s\\n\",\n-\t\t\t\t\t(int)(0.5 + score * 100 / MAX_SCORE),\n-\t\t\t\t\tp->one->path, p->two->path);\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\t/* rename it, and mark it as gone. */\n+\t\t\tif (!diff_needs_to_stay(&outq, i+1, p->one))\n+\t\t\t\t/* this is the last one, so mark it as gone.\n+\t\t\t\t */\n \t\t\t\tp->one->xfrm_flags |= RENAME_SRC_GONE;\n-\t\t\t\tsprintf(dp->xfrm_msg,\n-\t\t\t\t\t\"similarity index %d%%\\n\"\n-\t\t\t\t\t\"rename old %s\\n\"\n-\t\t\t\t\t\"rename new %s\\n\",\n-\t\t\t\t\t(int)(0.5 + score * 100 / MAX_SCORE),\n-\t\t\t\t\tp->one->path, p->two->path);\n-\t\t\t}\n \t\t}\n \t\telse\n-\t\t\t/* otherwise it is a modified (or stayed) entry */\n+\t\t\t/* otherwise it is a modified (or \"stay\") entry */\n \t\t\tdiff_queue(q, p->one, p->two);\n-\t\tdiff_free_filepair(p);\n+\t\tfree(p);\n \t}\n \n \tfree(outq.queue);\ndiff --git a/diffcore.h b/diffcore.h\n--- a/diffcore.h\n+++ b/diffcore.h\n@@ -41,13 +41,18 @@ extern void diff_free_filespec_data(stru\n struct diff_filepair {\n \tstruct diff_filespec *one;\n \tstruct diff_filespec *two;\n-\tchar *xfrm_msg;\n+\tint score; /* only valid when one and two are different paths */\n \tint orig_order; /* the original order of insertion into the queue */\n-\tint xfrm_work; /* for use by tramsformers, not by diffcore */\n+\tint rename_rank; /* rename/copy dependency needs to enforce\n+\t\t\t  * certain ordering of patches that later\n+\t\t\t  * diffcore transformations should not break.\n+\t\t\t  */\n };\n #define DIFF_PAIR_UNMERGED(p) \\\n \t(!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))\n \n+extern int diff_unmodified_pair(struct diff_filepair *);\n+\n struct diff_queue_struct {\n \tstruct diff_filepair **queue;\n \tint alloc;\n@@ -59,6 +64,8 @@ extern struct diff_filepair *diff_queue(\n \t\t\t\t\tstruct diff_filespec *,\n \t\t\t\t\tstruct diff_filespec *);\n extern void diff_q(struct diff_queue_struct *, struct diff_filepair *);\n-extern void diff_free_filepair(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 #endif\ndiff --git a/t/t4003-diff-rename-1.sh b/t/t4003-diff-rename-1.sh\n--- a/t/t4003-diff-rename-1.sh\n+++ b/t/t4003-diff-rename-1.sh\n@@ -11,7 +11,8 @@ test_description='More rename detection\n test_expect_success \\\n     'prepare reference tree' \\\n     'cat ../../COPYING >COPYING &&\n-    git-update-cache --add COPYING &&\n+     echo frotz >rezrov &&\n+    git-update-cache --add COPYING rezrov &&\n     tree=$(git-write-tree) &&\n     echo $tree'\n \n@@ -22,9 +23,10 @@ test_expect_success \\\n     rm -f COPYING &&\n     git-update-cache --add --remove COPYING COPYING.?'\n \n-# tree has COPYING.  work tree has COPYING.1 and COPYING.2,\n-# both are slightly edited.  So we say you copy-and-edit one,\n-# and rename-and-edit the other.\n+# tree has COPYING and rezrov.  work tree has COPYING.1 and COPYING.2,\n+# both are slightly edited, and unchanged rezrov.  So we say you\n+# copy-and-edit one, and rename-and-edit the other.  We do not say\n+# anything about rezrov.\n \n GIT_DIFF_OPTS=--unified=0 git-diff-cache -M -p $tree |\n sed -e 's/\\([0-9][0-9]*\\)/#/g' >current &&\n@@ -64,9 +66,10 @@ test_expect_success \\\n     'mv COPYING.2 COPYING &&\n      git-update-cache --add --remove COPYING COPYING.1 COPYING.2'\n \n-# tree has COPYING.  work tree has COPYING and COPYING.1,\n-# both are slightly edited.  So we say you edited one,\n-# and copy-and-edit the other.\n+# tree has COPYING and rezrov.  work tree has COPYING and COPYING.1,\n+# both are slightly edited, and unchanged rezrov.  So we say you\n+# edited one, and copy-and-edit the other.  We do not say\n+# anything about rezrov.\n \n GIT_DIFF_OPTS=--unified=0 git-diff-cache -C -p $tree |\n sed -e 's/\\([0-9][0-9]*\\)/#/g' >current\n@@ -103,10 +106,11 @@ test_expect_success \\\n     'cat ../../COPYING >COPYING &&\n      git-update-cache --add --remove COPYING COPYING.1'\n \n-# tree has COPYING.  work tree has the same COPYING and COPYING.1,\n-# but COPYING is not edited.  We say you copy-and-edit COPYING.1;\n-# this is only possible because -C mode now reports the unmodified\n-# file to the diff-core.\n+# tree has COPYING and rezrov.  work tree has COPYING and COPYING.1,\n+# but COPYING is not edited.  We say you copy-and-edit COPYING.1; this\n+# is only possible because -C mode now reports the unmodified file to\n+# the diff-core.  Unchanged rezrov, although being fed to\n+# git-diff-cache as well, should not be mentioned.\n \n GIT_DIFF_OPTS=--unified=0 git-diff-cache -C -p $tree |\n sed -e 's/\\([0-9][0-9]*\\)/#/g' >current\ndiff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh\nnew file mode 100644\n--- /dev/null\n+++ b/t/t4005-diff-rename-2.sh\n@@ -0,0 +1,82 @@\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.\n+\n+'\n+. ./test-lib.sh\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 -M $tree >current\n+\n+cat >expected <<\\EOF\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178\tCOPYING\tCOPYING.1\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471\tCOPYING\tCOPYING.2\n+EOF\n+\n+test_expect_success \\\n+    'validate output from rename/copy detection' \\\n+    'diff -u current expected'\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 -C $tree >current\n+cat >expected <<\\EOF\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178\tCOPYING\tCOPYING.1\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471\tCOPYING\tCOPYING\n+EOF\n+\n+test_expect_success \\\n+    'validate output from rename/copy detection' \\\n+    'diff -u 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+# tree has COPYING and rezrov.  work tree has the same COPYING and\n+# copy-edited COPYING.1, and unchanged rezrov.  We should see\n+# unmodified COPYING in the output, so that downstream diff-helper can\n+# notice.  We should not say anything about rezrov.\n+\n+git-diff-cache -C $tree >current\n+cat >expected <<\\EOF\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178\tCOPYING\tCOPYING.1\n+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3\tCOPYING\tCOPYING\n+EOF\n+\n+test_expect_success \\\n+    'validate output from rename/copy detection' \\\n+    'diff -u current expected'\n+\n+test_done\n------------------------------------------------\n\n"},{"id":"3794","messageId":"7vvf5aa74x.fsf_-_@assigned-by-dhcp.cox.net","threadId":"688","inReplyTo":"7vd5ribmam.fsf_-_@assigned-by-dhcp.cox.net","subject":"Comments on \"Rename/copy detection fix.\"","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-05-23T04:38:54Z","receivedAt":"2005-05-23T04:38:54Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"I talked about patch reordering in the commit log without giving\nspecifics, because I did not think it is good to cast specifics\nof uncoded ideas into stone in a commit log message.\n\nBut to throw it out open in the public, here are two of the\nobvious ones.\n\n (1) Give users a way to specify patches about which files\n     should come before which other files.  Things like \"*.h\n     files before *.c files\", or \"README and Documentation first\n     before anything else\".\n\n (2) Currently when pickaxe is used, it shows only diffs about\n     the files involved in the change we looked for.  The user\n     may sometimes want to see such a diff in the context of\n     whole patches and I plan to add an option for doing so.  It\n     may be useful in such a case to show the patch about the\n     file that triggered the commit to be selected by the\n     pickaxe first before patches for all other files.\n\nThese may or may not be useful, but this kind of reordering\nwould make it pretty much pointless to record which is a rename\nand which is a copy in the diff_filepair structure in the\ndiff_queue.  It all depends on which one comes first and which\none comes later in the final ordering, which is something\ndiffcore-rename cannot (and should not) know about.\n\n\n"}]}