{"thread":{"id":"91","subject":"[PATCH] \"checkout-cache -m\" writes unmerged contents for each stage.","startedAt":"2005-04-17T23:03:55Z","lastAt":"2005-04-18T03:05:16Z","messageCount":6,"participants":["Junio C Hamano","Linus Torvalds"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"563","messageId":"7v1x99vwdg.fsf@assigned-by-dhcp.cox.net","threadId":"91","inReplyTo":null,"subject":"[PATCH] \"checkout-cache -m\" writes unmerged contents for each stage.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-04-17T23:03:55Z","receivedAt":"2005-04-17T23:03:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This is the alternative strategy I mentioned in my previous\nmessage.  A new -m option to checkout-cache causes it to store\ncontents of unmerged paths in path~1~, path~2~, and path~3~.\n\nTo be applied on top of the previous patch I re-sent:\n\n    [PATCH] checkout-cache -a should not extract unmerged stages.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\n checkout-cache.c |   93 ++++++++++++++++++++++++++++++++++++++++++++----------\n 1 files changed, 76 insertions(+), 17 deletions(-)\n\ncheckout-cache.c: 596471a2f98b80a622488cf04edf9e95ce8666b1\n--- checkout-cache.c\n+++ checkout-cache.c\t2005-04-17 15:49:15.000000000 -0700\n@@ -34,7 +34,7 @@\n  */\n #include \"cache.h\"\n \n-static int force = 0, quiet = 0;\n+static int force = 0, merge = 0, quiet = 0;\n \n static void create_directories(const char *path)\n {\n@@ -65,6 +65,31 @@ static int create_file(const char *path,\n \treturn fd;\n }\n \n+/* Returns the pathname itself for a merged entry\n+ * and pathname~N~ for an unmerged one.\n+ * Do not free the value you get from this function.\n+ */\n+static char *ce_name_with_stage(struct cache_entry *ce)\n+{\n+\t/* (CE_NAMEMASK+1) is the max length of a name.\n+\t * We are adding 3 bytes for ~N~ and we need a terminating NUL\n+\t * hence +5.\n+\t */\n+\tstatic char name[CE_NAMEMASK + 5];\n+\tint stage = ce_stage(ce);\n+\tif (! stage)\n+\t\treturn ce->name;\n+\telse {\n+\t\tint pos = ce_namelen(ce);\n+\t\tstrcpy(name, ce->name);\n+\t\tname[pos++] = '~';\n+\t\tname[pos++] = '0' + stage;\n+\t\tname[pos++] = '~';\n+\t\tname[pos] = 0;\n+\t\treturn name;\n+\t}\n+}\n+\n static int write_entry(struct cache_entry *ce)\n {\n \tint fd;\n@@ -72,13 +97,20 @@ static int write_entry(struct cache_entr\n \tunsigned long size;\n \tlong wrote;\n \tchar type[20];\n+\tchar *name;\n \n \tnew = read_sha1_file(ce->sha1, type, &size);\n \tif (!new || strcmp(type, \"blob\")) {\n \t\treturn error(\"checkout-cache: unable to read sha1 file of %s (%s)\",\n \t\t\tce->name, sha1_to_hex(ce->sha1));\n \t}\n-\tfd = create_file(ce->name, ntohl(ce->ce_mode));\n+\tname = ce_name_with_stage(ce);\n+\tif (!quiet && name != ce->name)\n+\t\tfprintf(stderr,\n+\t\t\t\"checkout-cache: storing stage %d of %s in %s\\n\",\n+\t\t\tce_stage(ce), ce->name, name);\n+\n+\tfd = create_file(name, ntohl(ce->ce_mode));\n \tif (fd < 0) {\n \t\tfree(new);\n \t\treturn error(\"checkout-cache: unable to create %s (%s)\",\n@@ -117,19 +149,39 @@ static int checkout_entry(struct cache_e\n \treturn write_entry(ce);\n }\n \n+static int checkout_unmerged(int pos)\n+{\n+\tint i, err;\n+\tstruct cache_entry *ce = active_cache[pos];\n+\n+\tfor (err = 0, i = pos;\n+\t     (i < active_nr &&\n+\t      !strcmp(active_cache[i]->name, ce->name));\n+\t     i++)\n+\t\terr |= checkout_entry(active_cache[i]);\n+\treturn err;\n+}\n+\n static int checkout_file(const char *name)\n {\n \tint pos = cache_name_pos(name, strlen(name));\n \tif (pos < 0) {\n-\t\tif (!quiet) {\n-\t\t\tpos = -pos - 1;\n-\t\t\tfprintf(stderr,\n-\t\t\t\t\"checkout-cache: %s is %s.\\n\",\n-\t\t\t\tname,\n-\t\t\t\t(pos < active_nr &&\n-\t\t\t\t !strcmp(active_cache[pos]->name, name)) ?\n-\t\t\t\t\"unmerged\" : \"not in the cache\");\n+\t\tpos = -pos - 1;\n+\t\tif (pos < active_nr &&\n+\t\t    !strcmp(active_cache[pos]->name, name)) {\n+\t\t\tif (merge)\n+\t\t\t\treturn checkout_unmerged(pos);\n+\t\t\telse if (! quiet) {\n+\t\t\t\tfprintf(stderr,\n+\t\t\t\t\t\"checkout-cache: %s is unmerged.\\n\",\n+\t\t\t\t\tname);\n+\t\t\t\treturn -1;\n+\t\t\t}\n \t\t}\n+\t\telse if (! quiet)\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"checkout-cache: %s is not in the cache.\\n\",\n+\t\t\t\tname);\n \t\treturn -1;\n \t}\n \treturn checkout_entry(active_cache[pos]);\n@@ -137,22 +189,25 @@ static int checkout_file(const char *nam\n \n static int checkout_all(void)\n {\n-\tstruct cache_entry *unmerge_skipping = NULL;\n \tint i;\n \n \tfor (i = 0; i < active_nr ; i++) {\n \t\tstruct cache_entry *ce = active_cache[i];\n \t\tif (ce_stage(ce)) {\n-\t\t\tif (!unmerge_skipping ||\n-\t\t\t    strcmp(unmerge_skipping->name, ce->name))\n+\t\t\tif (!merge)\n \t\t\t\tfprintf(stderr,\n \t\t\t\t\t\"checkout-cache: needs merge %s\\n\",\n \t\t\t\t\tce->name);\n-\t\t\tunmerge_skipping = ce;\n-\t\t\tcontinue;\n+\t\t\twhile (i < active_nr &&\n+\t\t\t       !strcmp(ce->name, active_cache[i]->name)) {\n+\t\t\t\tif (merge) {\n+\t\t\t\t\tcheckout_entry(active_cache[i]);\n+\t\t\t\t}\n+\t\t\t\ti++;\n+\t\t\t}\n+\t\t\ti--;\n \t\t}\n-\t\tunmerge_skipping = NULL;\n-\t\tif (checkout_entry(ce) < 0)\n+\t\telse if (checkout_entry(ce) < 0)\n \t\t\treturn -1;\n \t}\n \treturn 0;\n@@ -181,6 +236,10 @@ int main(int argc, char **argv)\n \t\t\t\tforce = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (!strcmp(arg, \"-m\")) {\n+\t\t\t\tmerge = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (!strcmp(arg, \"-q\")) {\n \t\t\t\tquiet = 1;\n \t\t\t\tcontinue;\n\n"},{"id":"602","messageId":"Pine.LNX.4.58.0504171833450.7211@ppc970.osdl.org","threadId":"91","inReplyTo":"7v1x99vwdg.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] \"checkout-cache -m\" writes unmerged contents for each stage.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-04-18T02:05:53Z","receivedAt":"2005-04-18T02:05:53Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 17 Apr 2005, Junio C Hamano wrote:\n>\n> This is the alternative strategy I mentioned in my previous\n> message.  A new -m option to checkout-cache causes it to store\n> contents of unmerged paths in path~1~, path~2~, and path~3~.\n\nI don't think this is wrong, but it all seems a bit hacky. The reason I \ndidn't apply your other patch was that I just don't feel quite comfortable \nwith what is the RightThing(tm) to do yet.\n\nI'm actually fairly certain that the right thing to do is to silently \nignore all unmerged entries. They just aren't \"interesting\" from a \ncheckout-cache perspective, least of all when given the \"-a\" flag. So even \nwarning about them seems to be pointless.\n\nThe other issue is that obviously we _do_ need to have some way to merge\nthem, but as far as I can tell, we've really got that already in\n\"show-files --unmerged\"  together with \"cat-file blob xxxx\". I do believe \nthat it's a damn ugly script, and that maybe we could create some helper \nplumbing to make it more obvious, but I haven't quite decided what the \nright helper interface would be.\n\nWriting files to the current working directory I don't like, especially as\nwe don't even necessarily write all three files (so with your example\ncode, if there was a left-over xxx~1~ file from a previous merge, your\nmerger would really get confused now since it thinks it has an original\nfile that didn't actually really exist). So I'd much rather write them to\na /tmp directory instead, let the user merge them some way.\n\nI'm actually thinking that maybe the _right_ interface is to do something \nlike this:\n\n\tmerge-cache <program> <filename>\n\nand what that does is to look up the <filename> in the cache, and if it \nhas any merge entries, unpack all of them (which may be just one file, of \ncourse) into up to three separate files (mkstemp()), and then execve the \nsupplied program name with those three files as arguments 1,2,3 (empty \nargument if no file), and \"filename\" as argument 4.\n\nSo now the program could be a simple script:\n\n\t#!/bin/sh\n\t#\n\t# This is the git merge script, called with\n\t#\n\t#   $1 - original file (or empty string)\n\t#   $2 - file in branch1 (or empty string)\n\t#   $3 - file in branch2 (or empty string)\n\t#   $4 - pathname in repository\n\t#\n\t#\n\t# Case 1: file removed in both\n\t#\n\tif [ -z \"$2\" && -z \"$3\" ]; then\n\t\trm -- \"$4\"\n\t\tupdate-cache --remove -- \"$4\"\n\t\texit 0\n\tfi\n\t#\n\t# Case 2: file exists in just one\n\t#\n\tif [ -z \"$2\" || -z \"$3\" ] then\n\t\tcat \"$2\"\"$3\" > \"$4\"\n\t\tupdate-cache --add -- \"$4\"\n\t\texit 0\n\tfi\n\t#\n\t# Case 3: file exists in both\n\t#\n\tsrc=\"$1\"\n\tif [ -z \"$1\" ]; then\n\t\tsrc=/den/null\n\tfi\t\n\tmerge \"$3\" \"$src\" \"$2\" && cp \"$3\" \"$4\" && update-cache --add -- \"$4\"\n\nor you could so something fancier.\n\nThis would _seem_ to be a very simple way to generate a nice merge (the \nmerge script can obviously be a lot smarter than the above thing, \nincluding doing nice graphical merge interfaces).\n\nAn extension of this might be to make \"merge-cache\" take several \nfilenames, and merge them one at a time (stopping if the merge program \nreturns a failure code). And obviously this also expands trivially to the \nnormal \"-a\" case, which would do the same, except it would just call the \nmerge program for any file it finds that needs merging)\n\nWhat do you think? I can whip up a \"merge-cache\" program like that in five \nminutes, and it _seems_ like the right interface..\n\n\t\tLinus\n"},{"id":"605","messageId":"7vk6n0vnbq.fsf@assigned-by-dhcp.cox.net","threadId":"91","inReplyTo":"Pine.LNX.4.58.0504171833450.7211@ppc970.osdl.org","subject":"Re: [PATCH] \"checkout-cache -m\" writes unmerged contents for each stage.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-04-18T02:19:21Z","receivedAt":"2005-04-18T02:19:21Z","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, 17 Apr 2005, Junio C Hamano wrote:\n>> \n>> This is the alternative strategy I mentioned in my previous\n>> message.  A new -m option to checkout-cache causes it to store\n>> contents of unmerged paths in path~1~, path~2~, and path~3~.\n\nLT> I don't think this is wrong, but it all seems a bit hacky. The reason I \nLT> didn't apply your other patch was that I just don't feel quite comfortable \nLT> with what is the RightThing(tm) to do yet.\n\nI do not personally like that '-m' thing either so please feel\nfree to drop it.  I was just toying with the idea.  I knew you\nwould not use \"-m\" yourself (and I would not myself either).\nThe patch was primarily meant for Pasky to raise his opinion.\nIf having something like this helps Cogito then he can keep it.\nIf Cogito will be doing the \"show-files --unmerged\" with\n\"cat-file blob\" itself and this \"checkout-cache -m\" does not\nhelp it that much then there is no point having this hack.\n\nLT> I'm actually fairly certain that the right thing to do is to silently \nLT> ignore all unmerged entries. They just aren't \"interesting\" from a \nLT> checkout-cache perspective, least of all when given the \"-a\" flag. So even \nLT> warning about them seems to be pointless.\n\nI do not mind your dropping the \"warning\" bit, but please keep\nthe skipping bit for \"-a\".  Dropping that TT from stage 1 into\nthe working directory and then complaining that TT exists when\nit then tries to extract stage 2 and stage 3 is simply madness.\n\nLT> What do you think? I can whip up a \"merge-cache\" program like that in five \nLT> minutes, and it _seems_ like the right interface..\n\nYes.  I think that is the right thing to do.  In fact the idea\nis quite similar to what I've been working on, which is a\nrewrite of that perl thing to use \"read-tree -m O A B\".\n\n"},{"id":"613","messageId":"7vfyxovm6j.fsf@assigned-by-dhcp.cox.net","threadId":"91","inReplyTo":"Pine.LNX.4.58.0504171833450.7211@ppc970.osdl.org","subject":"Re: [PATCH] \"checkout-cache -m\" writes unmerged contents for each stage.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-04-18T02:44:04Z","receivedAt":"2005-04-18T02:44:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":">>>>> \"LT\" == Linus Torvalds <torvalds@osdl.org> writes:\n\nLT> I'm actually thinking that maybe the _right_ interface is to do something \nLT> like this:\n\nLT> \tmerge-cache <program> <filename>\n\nLT> and what that does is to look up the <filename> in the cache, and if it \nLT> has any merge entries, unpack all of them (which may be just one file, of \nLT> course) into up to three separate files (mkstemp()), and then execve the \nLT> supplied program name with those three files as arguments 1,2,3 (empty \nLT> argument if no file), and \"filename\" as argument 4.\n\nLT> What do you think? I can whip up a \"merge-cache\" program like that in five \nLT> minutes, and it _seems_ like the right interface..\n\nOne small detail.  What about the \"-x\" bit?\n\nIn case 2 and 3 in your sample merge script, the \"merge script\"\nneeds to know what the preferred mode bits are before running\n\"update-cache --add\".\n\nYes, it can figure that out by running \"show-files --unmerged\"\nand grepping for \"$4\" by itself, but then it can figure out the\ninformation in \"$1\" through \"$3\" by itself as well, so that\nmakes having merge-cache wrapper less useful to begin with.\n\nI do not think it is realistic for these three related trees to\nhave files that differ in -x bit, so it would not be that useful\nto give the \"merge script\" the flexibility to pick -x bit value\namong three parents --- it would be fine for the merge-cache\nwrapper to dictate the value of the -x bit for the resulting\nfile.  So I'd suggest to add an extra parameter to the \"merge\nscript\" when merge-cache wrapper calls it.\n\n"},{"id":"614","messageId":"Pine.LNX.4.58.0504171954410.7211@ppc970.osdl.org","threadId":"91","inReplyTo":"7vfyxovm6j.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] \"checkout-cache -m\" writes unmerged contents for each stage.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-04-18T02:56:28Z","receivedAt":"2005-04-18T02:56:28Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 17 Apr 2005, Junio C Hamano wrote:\n> \n> One small detail.  What about the \"-x\" bit?\n\nYou'll need to merge those kinds of changes separately for now. We'll have \nto resolve that too, right now I think the -x bit gets lost on a merge \nsimply because we just write the temp-files, something merges them, and \ndoes an \"update-cache\" on the merged result, and we'll pick up the -x bit \n(or rather, lack of one) at that point.\n\n\t\tLinus\n"},{"id":"618","messageId":"Pine.LNX.4.58.0504171957210.7211@ppc970.osdl.org","threadId":"91","inReplyTo":"7vk6n0vnbq.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] \"checkout-cache -m\" writes unmerged contents for each stage.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-04-18T03:05:16Z","receivedAt":"2005-04-18T03:05:16Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 17 Apr 2005, Junio C Hamano wrote:\n> \n> LT> What do you think? I can whip up a \"merge-cache\" program like that in five \n> LT> minutes, and it _seems_ like the right interface..\n> \n> Yes.  I think that is the right thing to do.  In fact the idea\n> is quite similar to what I've been working on, which is a\n> rewrite of that perl thing to use \"read-tree -m O A B\".\n\nI pushed the thing out. It was indeed pretty trivial.\n\nJust as an example, on your merge-test, I can then do\n\n\tmerge-cache echo -a\n\nand the output is\n\n\t .merge_file_Raanu4 .merge_file_q7ZoLY AA\n\t .merge_file_zMXLAW .merge_file_SOugrU AN\n\t.merge_file_tWf5zS   DD\n\t.merge_file_YmGzgR   DM\n\t.merge_file_N1M8oO   DN\n\t.merge_file_eAC5WL .merge_file_ROg7mM  MD\n\t.merge_file_G1AWSM .merge_file_LMCruN .merge_file_ucVx7N MM\n\t  .merge_file_3YxD2O NA\n\t.merge_file_2SJa6P .merge_file_3V5g6Q  ND\n\t.merge_file_SIo4nQ   S/DD\n\t.merge_file_dO8AOP   S/DM\n\t.merge_file_wOO6iP   S/DN\n\t.merge_file_RrgnYR .merge_file_KQxqHU  S/MD\n\t.merge_file_F0sQRX .merge_file_E4DC00 .merge_file_HQkyn2 S/MM\n\t.merge_file_od0mf4 .merge_file_Xvv035  S/ND\n\t.merge_file_oEtq17 .merge_file_TwkT5c .merge_file_aqx58h Trivial\n\n(Note that the spaces signify an empty argument in those places, since the\nfile in question didn't always exist). It executed the \"echo\" thing 16 \ntimes (once for each object that your script-from-hell had caused a clash \nwith).\n\nIn all cases you have $1 being \"original\", $2 being \"branch 1\", $3 being\n\"branch 2\", and $4 being \"name in cache\".\n\nALERT ALERT ALERT! The git \"merge object order\" is different from the \n\"merge\" program merge object order. In the above ordering, the original is \nfirst. But the argument order to the 3-way merge program \"merge\" is to \nhave the original in the middle. Don't ask me why.\n\nAnyway, another example:\n\n\ttorvalds@ppc970:~/merge-test> merge-cache cat MM\n\tThis is MM from the original tree.\t\t\t# original\n\tThis is modified MM in the branch A.\t\t\t# merge1\n\tThis is modified MM in the branch B.\t\t\t# merge2\n\tThis is modified MM in the branch B.\t\t\t# current contents\n\nor \n\n\ttorvalds@ppc970:~/merge-test> merge-cache cat AA MM\n\tcat: : No such file or directory\n\tThis is added AA in the branch A.\n\tThis is added AA in the branch B.\n\tThis is added AA in the branch B.\n\tfatal: merge program failed\n\nwhere the latter example shows how \"merge-cache\" will stop trying to merge \nonce anything has returned an error (ie \"cat\" returned an error for the AA \nfile, because it didn't exist in the original, and thus \"merge-cache\" \ndidn't even try to merge the MM thing).\n\n\t\tLinus\n"}]}