{"thread":{"id":"19641","subject":"Am able to delete a file with no trace in the log","startedAt":"2009-06-02T19:33:14Z","lastAt":"2009-06-04T06:57:05Z","messageCount":26,"participants":["Graham Perks","Tony Finch","Jeff King","Linus Torvalds","Sitaram Chamarty","Junio C Hamano","Avery Pennarun"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"115271","messageId":"EB2D87B9-3B03-431A-B204-C7799F9BA291@ausperks.net","threadId":"19641","inReplyTo":null,"subject":"Am able to delete a file with no trace in the log","fromName":"Graham Perks","fromEmail":"gperks@ausperks.net","sentAt":"2009-06-02T19:33:14Z","receivedAt":"2009-06-02T19:33:14Z","isPatch":false,"sender":{"key":"gperks@ausperks.net","avatar":null},"body":"We just had this come up. A file was accidentally removed during a  \nmerge operation, and we could find no clue in the gitk and git log  \noutput as to which change the deletion occurred in. We found the  \noriginal file addition in a change; but no other mention of this file.\n\nThis seems like a bug. The log should show the file being deleted.\n\nThis is pretty easy to reproduce. Just run the script below. Here,  \npretend \"c\" is the central repo to which two users are pushing/ \npulling. The users are using repositories r1 and r2.\n\n# create and init repos\nmd r1\nmd r2\nmd c\ncd c\ngit init --bare\ncd ..\ncd r1 && git init && cd ..\ncd r2 && git init && cd ..\n\n# create a file to conflict later\ncd r1\necho hello > a.txt\ngit add .\ngit commit -m \"add file\"\ngit push ../c master\n\n# sync other repo\ncd ../r2\ngit pull ../c\n\n# make conflicting change\necho helloworld > a.txt\ngit commit -a -m \"change file in r2\"\n\n# and add a file\necho bye > b.txt\ngit add .\ngit commit -m \"add file b\"\ngit push ../c\n\n# Meanwhile, r1 has been working\ncd ../r1\necho hellogit > a.txt\ngit commit -a -m \"change file in r1\"\ngit pull ../c\n\n# edit a.txt to resolve merge\nvi a.txt\n\n# User in r1 erroneously thinks \"ooh, I didn't edit b.txt, I don't  \nwant that in my commit!\"\n# This is especially easy to do in git gui. That's what our user did.\n# Or, the user could legitimately remove the file as part of the  \nmerge. Probably\n# they should git rm, but our user didn't.\ngit reset HEAD b.txt\n\n# But he thinks, \"I did merge a.txt\"\ngit add a.txt\ngit commit -m \"Merged\"\ngit push ../c\n\n\n# Now user in r2 wants to pull r1's changes\ncd ../r2\ngit pull ../c\n\n# File b.txt deleted! Woah! How did that happen?\n# gitk and git log show nothing about the deletion.\n# There seems to be no evidence about who, how, why, or when the file  \ngot deleted.\n# So it's hard to track down which user mis-used the system and  \neducate them.\n\nCheers,\nGraham Perks.\n"},{"id":"115278","messageId":"alpine.LSU.2.00.0906022128280.9226@hermes-2.csi.cam.ac.uk","threadId":"19641","inReplyTo":"EB2D87B9-3B03-431A-B204-C7799F9BA291@ausperks.net","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Tony Finch","fromEmail":"dot@dotat.at","sentAt":"2009-06-02T20:29:45Z","receivedAt":"2009-06-02T20:29:45Z","isPatch":false,"sender":{"key":"dot@dotat.at","avatar":"https://avatars.githubusercontent.com/u/68429?v=4"},"body":"On Tue, 2 Jun 2009, Graham Perks wrote:\n\n> We just had this come up. A file was accidentally removed during a merge\n> operation, and we could find no clue in the gitk and git log output as to\n> which change the deletion occurred in. We found the original file addition in\n> a change; but no other mention of this file.\n>\n> This seems like a bug. The log should show the file being deleted.\n\nThe manual for git log suggests the -c or --cc options ought to display\nthe information you want, but it doesn't seem to work in practice.\n\nTony.\n-- \nf.anthony.n.finch  <dot@dotat.at>  http://dotat.at/\nGERMAN BIGHT HUMBER: SOUTHWEST 5 TO 7. MODERATE OR ROUGH. SQUALLY SHOWERS.\nMODERATE OR GOOD.\n"},{"id":"115283","messageId":"20090602213439.GA7584@coredump.intra.peff.net","threadId":"19641","inReplyTo":"EB2D87B9-3B03-431A-B204-C7799F9BA291@ausperks.net","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-06-02T21:34:39Z","receivedAt":"2009-06-02T21:34:39Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Jun 02, 2009 at 02:33:14PM -0500, Graham Perks wrote:\n\n> # File b.txt deleted! Woah! How did that happen?\n> # gitk and git log show nothing about the deletion.\n> # There seems to be no evidence about who, how, why, or when the file got \n> deleted.\n> # So it's hard to track down which user mis-used the system and educate \n> them.\n\nI think this is a funny interaction with the way the diffs for merge\ncommits are shown. The diff you are looking for is definitely available.\nIn your example:\n\n  # compare the second parent of the merge to the merge result\n  $ git diff-tree HEAD^2 HEAD\n  :100644 100644 31e0fce560e96c8b357f5b8630c7d8fbeb0a3ec8 2dc6d98afe942589307d7c0166971b3a2ec8706d M      a.txt\n  :100644 000000 b023018cabc396e7692c70bbf5784a93d3f738ab 0000000000000000000000000000000000000000 D      b.txt\n\nBut it doesn't show up in \"git log\". I believe this is because the rule\nfor what to show in a merge commit is \"if content is exactly the same as\none of the parents, it's not interesting\". That is, deleting \"b.txt\"\nfrom the second parent ends up being exactly as it is in the first\nparent -- nonexistent. So git has no idea that you deleted \"b.txt\"\naccidentally, and it was not simply part of the conflict resolution.\n\nSo I think this is working as intended, and is not a bug exactly. But\ncertainly the behavior leaves something to be desired for actually\ntracking down the source of the change later on. I don't think there is\na way to get \"git show $merge\" to show the deletion, and nor does it\nshow up under \"git log -- b.txt\". Even worse, the latter produces no\noutput at all for your example (you need \"--full-history\" to tell it to\nfollow both parents of a merge).\n\nI wonder if we need some kind of \"--verbose-merges\" option to tell the\ndiff engine that we really are interested in all of the changes that\nhappened in a merge. But maybe we even have something and I don't know\nabout it.\n\n-Peff\n"},{"id":"115286","messageId":"alpine.LFD.2.01.0906021439030.4880@localhost.localdomain","threadId":"19641","inReplyTo":"20090602213439.GA7584@coredump.intra.peff.net","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2009-06-02T21:55:24Z","receivedAt":"2009-06-02T21:55:24Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 2 Jun 2009, Jeff King wrote:\n> \n> But it doesn't show up in \"git log\". I believe this is because the rule\n> for what to show in a merge commit is \"if content is exactly the same as\n> one of the parents, it's not interesting\".\n\nCorrect.\n\nWhat happens is that \"git log\" with a filename will always simplify the \nhistory to the side that matches. And yes, \"matching\" can and does include \n\"doesn't exist in child, doesn't exist in parent\"\n\nNow, I admit that in this case the matching heuristic is dubious, and \nmaybe we should consider \"does not exist in result\" to not match any \nparent. We already think that \"all new\" is special (\"REV_TREE_NEW\" vs \n\"REV_TREE_DIFFERENT\"), so maybe we should think that \"all deleted\" is also \nspecial (\"REV_TREE_DEL\")\n\n\t\tLinus\n"},{"id":"115298","messageId":"2e24e5b90906021747v327c9db1o16d03af5dcf50546@mail.gmail.com","threadId":"19641","inReplyTo":"alpine.LFD.2.01.0906021439030.4880@localhost.localdomain","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Sitaram Chamarty","fromEmail":"sitaramc@gmail.com","sentAt":"2009-06-03T00:47:17Z","receivedAt":"2009-06-03T00:47:17Z","isPatch":false,"sender":{"key":"sitaramc@gmail.com","avatar":"https://avatars.githubusercontent.com/u/43316?v=4"},"body":"On Wed, Jun 3, 2009 at 3:25 AM, Linus Torvalds\n<torvalds@linux-foundation.org> wrote:\n>\n>\n> On Tue, 2 Jun 2009, Jeff King wrote:\n>>\n>> But it doesn't show up in \"git log\". I believe this is because the rule\n>> for what to show in a merge commit is \"if content is exactly the same as\n>> one of the parents, it's not interesting\".\n>\n> Correct.\n>\n> What happens is that \"git log\" with a filename will always simplify the\n> history to the side that matches. And yes, \"matching\" can and does include\n> \"doesn't exist in child, doesn't exist in parent\"\n>\n> Now, I admit that in this case the matching heuristic is dubious, and\n> maybe we should consider \"does not exist in result\" to not match any\n> parent. We already think that \"all new\" is special (\"REV_TREE_NEW\" vs\n> \"REV_TREE_DIFFERENT\"), so maybe we should think that \"all deleted\" is also\n> special (\"REV_TREE_DEL\")\n\n\"git merge -s ours\" would do precisely the same thing, wouldn't it?\nThat has happened to me before, and I noticed that git log does not\nshow the deletion, but rationalised it as being because I had\nexplicitly done a \"-s ours\".\n\nFixing this would fix that (maybe more common) case too, and show that\nthe merge commit removed the file.\n"},{"id":"115302","messageId":"alpine.LFD.2.01.0906021811440.4880@localhost.localdomain","threadId":"19641","inReplyTo":"2e24e5b90906021747v327c9db1o16d03af5dcf50546@mail.gmail.com","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2009-06-03T01:20:44Z","receivedAt":"2009-06-03T01:20:44Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 3 Jun 2009, Sitaram Chamarty wrote:\n> \n> \"git merge -s ours\" would do precisely the same thing, wouldn't it?\n\nYes. It doesn't matter which way it goes - if a file is seen as being \nidentical (and \"none\" very much counts) in one parent, it's not judged \n\"interesting\" from a merge patch standpoint, or even from a \"git log\" \n(aka \"revision history\") standpoint.\n\n> That has happened to me before, and I noticed that git log does not\n> show the deletion, but rationalised it as being because I had\n> explicitly done a \"-s ours\".\n> \n> Fixing this would fix that (maybe more common) case too, and show that\n> the merge commit removed the file.\n\nThe problem is that quite often, a merge that removes a file _is_ the \ncorrect thing when it was removed in one branch, and a merge that adds a \nfile is even more common, and in no way special. We don't show the whole \ndiff in a merge, because the whole diff is often nonsensical (ie so \ntrivial that showing it all just hides the parts that are actually \nrelevant).\n\nSo I'll have to think about it a bit more. We clearly don't generate good \ndiffs for file deletion/creation in merges, and we should improve on it, \nbut it's definitely not a trivial issue either.\n\n\t\tLinus\n"},{"id":"115304","messageId":"alpine.LFD.2.01.0906021820570.4880@localhost.localdomain","threadId":"19641","inReplyTo":"alpine.LFD.2.01.0906021439030.4880@localhost.localdomain","subject":"Clean up and simplify rev_compare_tree()","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2009-06-03T01:34:01Z","receivedAt":"2009-06-03T01:34:01Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThis simplifies the logic of rev_compare_tree() by removing a special \ncase. \n\nIt does so by turning the special case of finding a diff to be \"all new \nfiles\" into a more generic case of \"all new\" vs \"all removed\" vs \"mixed \nchanges\", so now the code is actually more powerful and more generic, and \nthe added symmetry actually makes it simpler too.\n\nThis makes no changes to any existing behavior, but apart from the \nsimplification it does make it possible to some day care about whether all \nchanges were just deletions if we want to. Which we may well want to for \nmerge handling.\n\nSigned-off-by: Linus Torvalds <torvalds@linux-foundation.org>\n---\n\nThis is just a cleanup while I'm looking at the code. There's two things \nthat are relevant to merges - the \"TREESAME\" logic that determines whether \nwe should simplify the merge away and pick just one parent, and the actual \ndiff creation logic that then creates diffs of the merges that we don't \nsimplify away.\n\nThe two are totally independent, and this patch just cleans up the helper \nfunction that we use for the commit simplification logic. \n\nThe only half-way subtle part here (and it really isn't that subtle) is \nthat \"REV_TREE_NEW | REV_TREE_OLD == REV_TREE_DIFFERENT\", which makes \nsense and just simplifies the logic in general. It used to be that mixing \nREV_TREE_NEW state with REV_TREE_DIFFERENT was complicated. Now it's \ntrivial, thanks to REV_TREE_OLD that is currently otherwise unused (we \ntreat it the same way as REV_TREE_DIFFERENT, which is what that case used \nto result in).\n\nThere's an unrelated cleanup which is to just move the \"can't happen\" \nspecial case code of a missing commit tree up to the same point as the \n\"can't happen\" missing parent tree.\n\nOn Tue, 2 Jun 2009, Linus Torvalds wrote:\n> \n> Now, I admit that in this case the matching heuristic is dubious, and \n> maybe we should consider \"does not exist in result\" to not match any \n> parent. We already think that \"all new\" is special (\"REV_TREE_NEW\" vs \n> \"REV_TREE_DIFFERENT\"), so maybe we should think that \"all deleted\" is also \n> special (\"REV_TREE_DEL\")\n> \n> \t\tLinus\n> \n\n revision.c |   33 ++++++++++++---------------------\n revision.h |    5 +++--\n 2 files changed, 15 insertions(+), 23 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 18b7ebb..bf58448 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -256,10 +256,12 @@ static int everybody_uninteresting(struct commit_list *orig)\n \n /*\n  * The goal is to get REV_TREE_NEW as the result only if the\n- * diff consists of all '+' (and no other changes), and\n- * REV_TREE_DIFFERENT otherwise (of course if the trees are\n- * the same we want REV_TREE_SAME).  That means that once we\n- * get to REV_TREE_DIFFERENT, we do not have to look any further.\n+ * diff consists of all '+' (and no other changes), REV_TREE_OLD\n+ * if the whole diff is removal of old data, and otherwise\n+ * REV_TREE_DIFFERENT (of course if the trees are the same we\n+ * want REV_TREE_SAME).\n+ * That means that once we get to REV_TREE_DIFFERENT, we do not\n+ * have to look any further.\n  */\n static int tree_difference = REV_TREE_SAME;\n \n@@ -268,22 +270,9 @@ static void file_add_remove(struct diff_options *options,\n \t\t    const unsigned char *sha1,\n \t\t    const char *fullpath)\n {\n-\tint diff = REV_TREE_DIFFERENT;\n+\tint diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;\n \n-\t/*\n-\t * Is it an add of a new file? It means that the old tree\n-\t * didn't have it at all, so we will turn \"REV_TREE_SAME\" ->\n-\t * \"REV_TREE_NEW\", but leave any \"REV_TREE_DIFFERENT\" alone\n-\t * (and if it already was \"REV_TREE_NEW\", we'll keep it\n-\t * \"REV_TREE_NEW\" of course).\n-\t */\n-\tif (addremove == '+') {\n-\t\tdiff = tree_difference;\n-\t\tif (diff != REV_TREE_SAME)\n-\t\t\treturn;\n-\t\tdiff = REV_TREE_NEW;\n-\t}\n-\ttree_difference = diff;\n+\ttree_difference |= diff;\n \tif (tree_difference == REV_TREE_DIFFERENT)\n \t\tDIFF_OPT_SET(options, HAS_CHANGES);\n }\n@@ -305,6 +294,8 @@ static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct\n \n \tif (!t1)\n \t\treturn REV_TREE_NEW;\n+\tif (!t2)\n+\t\treturn REV_TREE_OLD;\n \n \tif (revs->simplify_by_decoration) {\n \t\t/*\n@@ -323,8 +314,7 @@ static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct\n \t\tif (!revs->prune_data)\n \t\t\treturn REV_TREE_SAME;\n \t}\n-\tif (!t2)\n-\t\treturn REV_TREE_DIFFERENT;\n+\n \ttree_difference = REV_TREE_SAME;\n \tDIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);\n \tif (diff_tree_sha1(t1->object.sha1, t2->object.sha1, \"\",\n@@ -429,6 +419,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)\n \t\t\t\tp->parents = NULL;\n \t\t\t}\n \t\t/* fallthrough */\n+\t\tcase REV_TREE_OLD:\n \t\tcase REV_TREE_DIFFERENT:\n \t\t\ttree_changed = 1;\n \t\t\tpp = &parent->next;\ndiff --git a/revision.h b/revision.h\nindex be39e7d..227164c 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -118,8 +118,9 @@ struct rev_info {\n };\n \n #define REV_TREE_SAME\t\t0\n-#define REV_TREE_NEW\t\t1\n-#define REV_TREE_DIFFERENT\t2\n+#define REV_TREE_NEW\t\t1\t/* Only new files */\n+#define REV_TREE_OLD\t\t2\t/* Only files removed */\n+#define REV_TREE_DIFFERENT\t3\t/* Mixed changes */\n \n /* revision.c */\n void read_revisions_from_stdin(struct rev_info *revs);\n"},{"id":"115306","messageId":"7vfxeidqoz.fsf@alter.siamese.dyndns.org","threadId":"19641","inReplyTo":"alpine.LFD.2.01.0906021439030.4880@localhost.localdomain","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-06-03T01:57:48Z","receivedAt":"2009-06-03T01:57:48Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> What happens is that \"git log\" with a filename will always simplify the \n> history to the side that matches. And yes, \"matching\" can and does include \n> \"doesn't exist in child, doesn't exist in parent\"\n>\n> Now, I admit that in this case the matching heuristic is dubious, and \n> maybe we should consider \"does not exist in result\" to not match any \n> parent. We already think that \"all new\" is special (\"REV_TREE_NEW\" vs \n> \"REV_TREE_DIFFERENT\"), so maybe we should think that \"all deleted\" is also \n> special (\"REV_TREE_DEL\")\n\nSorry, but I do not quite understand this comment.  REV_TREE_NEW can be\ntreated differently from REV_TREE_DIFFERENT but that only happens if you\nknow about --remove-empty option, and no scripted (and later converted to\nC) Porcelain uses that option by default.\n\nAlso, and more on the topic of simplification, if one parent matches the\nchild (i.e. REV_TREE_SAME), the merge history is simplified by discarding\nother parents, regardless of the nature of their differences, be they\nREV_TREE_NEW or REV_TREE_DIFFERENT, so in that sense we do not even\nspecial case REV_TREE_NEW at all for the purpose of merge simplification.\n"},{"id":"115311","messageId":"alpine.LFD.2.01.0906022000040.4880@localhost.localdomain","threadId":"19641","inReplyTo":"7vfxeidqoz.fsf@alter.siamese.dyndns.org","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2009-06-03T03:03:31Z","receivedAt":"2009-06-03T03:03:31Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 2 Jun 2009, Junio C Hamano wrote:\n> \n> Sorry, but I do not quite understand this comment.  REV_TREE_NEW can be\n> treated differently from REV_TREE_DIFFERENT but that only happens if you\n> know about --remove-empty option, and no scripted (and later converted to\n> C) Porcelain uses that option by default.\n\nIt's not REV_TREE_NEW, but the other way around, ie when the commit has no \ncontents but the parent _does_ have contents, maybe we shouldn't then look \nat another parent and say \"no content\", and then match that other parent \n(resulting in no difference).\n\nIOW, we are in the situation where one parent gets REV_TREE_SAME, but gets \nit for a totally pointless reason, namely that neither that parent nor the \neventual merge has anything at all in that path. In that case, we simplify \ntowards the parent that results in the smallest diff - which in this case \nis \"nothing there at all\".\n\nNow, that is often the _right_ thing to do, since if it was meant to be \ndeleted in that branch, then we'll eventually hit the delete commit, and \nsee it as a nice removal. But it does make it really hard to see this case \nof \"unintentional delete\"\n\n\t\tLinus\n"},{"id":"115377","messageId":"7vtz2x6mor.fsf@alter.siamese.dyndns.org","threadId":"19641","inReplyTo":"alpine.LFD.2.01.0906022000040.4880@localhost.localdomain","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-06-03T21:18:28Z","receivedAt":"2009-06-03T21:18:28Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> On Tue, 2 Jun 2009, Junio C Hamano wrote:\n>> \n>> Sorry, but I do not quite understand this comment.  REV_TREE_NEW can be\n>> treated differently from REV_TREE_DIFFERENT but that only happens if you\n>> know about --remove-empty option, and no scripted (and later converted to\n>> C) Porcelain uses that option by default.\n>\n> It's not REV_TREE_NEW, but the other way around, ie when the commit has no \n> contents but the parent _does_ have contents, maybe we shouldn't then look \n> at another parent and say \"no content\", and then match that other parent \n> (resulting in no difference).\n>\n> IOW, we are in the situation where one parent gets REV_TREE_SAME, but gets \n> it for a totally pointless reason, namely that neither that parent nor the \n> eventual merge has anything at all in that path. In that case, we simplify \n> towards the parent that results in the smallest diff - which in this case \n> is \"nothing there at all\".\n>\n> Now, that is often the _right_ thing to do, since if it was meant to be \n> deleted in that branch, then we'll eventually hit the delete commit, and \n> see it as a nice removal. But it does make it really hard to see this case \n> of \"unintentional delete\"\n\nI realize that \"--simplify-merges\" would show that kind of deletion.\n\nE.g. \"git log --graph --oneline -- git-clone.sh\" shows that the scripted\nversion ceased to exist at 8434c2f (Build in clone, 2008-04-27), but with\nthe option, b84c343 (Merge branch 'db/clone-in-c', 2008-05-25) merged the\ndeletion to the mainline, but while doing so we lost two updates to the\nscripted version since the \"Build in clone\" topic forked.\n"},{"id":"115378","messageId":"alpine.LFD.2.01.0906031424590.4880@localhost.localdomain","threadId":"19641","inReplyTo":"7vtz2x6mor.fsf@alter.siamese.dyndns.org","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2009-06-03T21:26:42Z","receivedAt":"2009-06-03T21:26:42Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 3 Jun 2009, Junio C Hamano wrote:\n> \n> I realize that \"--simplify-merges\" would show that kind of deletion.\n\nYes. Or just \"--full-history\".\n\nOf course, it would then not show any _diff_, so even then you're still \nkind of in the dark, if what you were looking for was \"when did this file \ngo away\". Doing a \"git log -c --stat\" would show the creation, but not the \ndeletion of the file.\n\n\t\tLinus\n"},{"id":"115379","messageId":"alpine.LFD.2.01.0906031431100.4880@localhost.localdomain","threadId":"19641","inReplyTo":"7vtz2x6mor.fsf@alter.siamese.dyndns.org","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2009-06-03T21:33:39Z","receivedAt":"2009-06-03T21:33:39Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 3 Jun 2009, Junio C Hamano wrote:\n> \n> E.g. \"git log --graph --oneline -- git-clone.sh\" shows that the scripted\n> version ceased to exist at 8434c2f\n\nBtw, this example misses the whole point of the original problem.\n\nThe original problem was:\n\n - create new file 'x' in branch 'a'\n\n - merge branch 'a' into branch 'b', but because of a merge conflict and \n   confurion in the merge, the merge result doesn't contain 'x' any more.\n\n - try to find out what happened to 'x' after-the-fact.\n\nTry it. Git really doesn't make it very easy, because git will notice that \n'x' didn't exist before the branch either (in branch 'b'), so there will \nbe _no_ sign of 'x' actually going away.\n\n\t\t\tLinus\n"},{"id":"115382","messageId":"7vhbyx6kzl.fsf@alter.siamese.dyndns.org","threadId":"19641","inReplyTo":"alpine.LFD.2.01.0906022000040.4880@localhost.localdomain","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-06-03T21:55:10Z","receivedAt":"2009-06-03T21:55:10Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> On Tue, 2 Jun 2009, Junio C Hamano wrote:\n>> \n>> Sorry, but I do not quite understand this comment.  REV_TREE_NEW can be\n>> treated differently from REV_TREE_DIFFERENT but that only happens if you\n>> know about --remove-empty option, and no scripted (and later converted to\n>> C) Porcelain uses that option by default.\n>\n> It's not REV_TREE_NEW, but the other way around, ie when the commit has no \n> contents but the parent _does_ have contents, maybe we shouldn't then look \n> at another parent and say \"no content\", and then match that other parent \n> (resulting in no difference).\n>\n> IOW, we are in the situation where one parent gets REV_TREE_SAME, but gets \n> it for a totally pointless reason, namely that neither that parent nor the \n> eventual merge has anything at all in that path. In that case, we simplify \n> towards the parent that results in the smallest diff - which in this case \n> is \"nothing there at all\".\n\nHere is a crude attempt to do this.  It introduces an diff optflag that\nrecords if we checked any changes at the path level (if left unset, that\nmeans everything was pruned away by pathspec) and teaches the internal\ndiff-tree logic to set it, so that the caller can tell between \"no\nchanges\" and \"there was no interesting path that matches the pathspec, so\ncomparison between parent and child yielded nothing\".\n\n\tSide note. I didn't bother touching the diff-files and diff-index\n\tcodepaths in this patch; if the distinction turns out to be useful\n\twe should teach the flag to them as well (but I'll say it is\n\tprobably not very useful shortly).\n\nAfter looking at its output, I have to say that I do not like it very\nmuch.  Compared to \"--simplify-merges\", it shows too many uninteresting\nmerges.  E.g.\n\n    $ git log --oneline -- git-clone.sh\n\nstarts like this.\n\n    17d778e Merge branch 'dr/ceiling'\n    159e639 Merge branch 'lt/racy-empty'\n    bc9c3e0 Merge branch 'jc/maint-combine-diff-pre-context'\n    ...\n\nAll of them are resolving a merge with a revision that has a scripted \"git\nclone\" (i.e. \"maint\" track before built-in git-clone) into a newer\nrevision after \"git clone\" has become built-in.\n\nAfter a score or so of such uninteresting merges, we finally see\nwhat matters.\n\n    b84c343 Merge branch 'db/clone-in-c'\n    6d9878c clone: bsd shell portability fix\n    c904bf3 Be more careful with objects directory permissions on clone\n    8434c2f Build in clone\n    e42251a Use \"=\" instead of \"==\" in condition as it is more portable\n    ...\n\nThis part is actually somewhat interesting.\n\n    $ git log --oneline --graph b84c343 -- git-clone.sh\n\nlooks like this:\n\n    *   b84c343 Merge branch 'db/clone-in-c'\n    |\\\n    | * 8434c2f Build in clone\n    * | 6d9878c clone: bsd shell portability fix\n    * | c904bf3 Be more careful with objects dir...\n    |/\n    * e42251a Use \"=\" instead of \"==\" in conditi...\n    * a2b26ac clone: detect and fail on excess p...\n    * c20711d Silence cpio's \"N blocks\" output w...\n    ...\n\nBut then that was what \"--simplify-merges\" gave us without the patch\nanyway.\n\n\n\n diff.h      |    1 +\n revision.c  |   32 ++++++++++++++++++++++++++++++--\n revision.h  |    1 +\n tree-diff.c |    1 +\n 4 files changed, 33 insertions(+), 2 deletions(-)\n\ndiff --git a/diff.h b/diff.h\nindex 6616877..fcf1d52 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -66,6 +66,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,\n #define DIFF_OPT_DIRSTAT_CUMULATIVE  (1 << 19)\n #define DIFF_OPT_DIRSTAT_BY_FILE     (1 << 20)\n #define DIFF_OPT_ALLOW_TEXTCONV      (1 << 21)\n+#define DIFF_OPT_CHECKED_CHANGES     (1 << 22)\n #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)\n #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)\n #define DIFF_OPT_CLR(opts, flag)    ((opts)->flags &= ~DIFF_OPT_##flag)\ndiff --git a/revision.c b/revision.c\nindex bf58448..7440f59 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -273,7 +273,7 @@ static void file_add_remove(struct diff_options *options,\n \tint diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;\n \n \ttree_difference |= diff;\n-\tif (tree_difference == REV_TREE_DIFFERENT)\n+\tif ((tree_difference & REV_TREE_DIFFERENT) == REV_TREE_DIFFERENT)\n \t\tDIFF_OPT_SET(options, HAS_CHANGES);\n }\n \n@@ -317,9 +317,13 @@ static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct\n \n \ttree_difference = REV_TREE_SAME;\n \tDIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);\n+\tDIFF_OPT_CLR(&revs->pruning, CHECKED_CHANGES);\n \tif (diff_tree_sha1(t1->object.sha1, t2->object.sha1, \"\",\n \t\t\t   &revs->pruning) < 0)\n \t\treturn REV_TREE_DIFFERENT;\n+\tif (tree_difference == REV_TREE_SAME &&\n+\t    !DIFF_OPT_TST(&revs->pruning, CHECKED_CHANGES))\n+\t\ttree_difference = REV_TREE_EMPTY;\n \treturn tree_difference;\n }\n \n@@ -351,7 +355,7 @@ static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)\n static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)\n {\n \tstruct commit_list **pp, *parent;\n-\tint tree_changed = 0, tree_same = 0;\n+\tint tree_changed = 0, tree_same = 0, all_empty = 1;\n \n \t/*\n \t * If we don't do pruning, everything is interesting\n@@ -384,7 +388,17 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)\n \t\t\t    sha1_to_hex(commit->object.sha1),\n \t\t\t    sha1_to_hex(p->object.sha1));\n \t\tswitch (rev_compare_tree(revs, p, commit)) {\n+\t\tcase REV_TREE_EMPTY:\n+\t\t\t/*\n+\t\t\t * This parent is the same as the child but that is\n+\t\t\t * only because no path we are interested in appears\n+\t\t\t * in either of them.  Do not cull other parents (yet).\n+\t\t\t */\n+\t\t\tpp = &parent->next;\n+\t\t\tcontinue;\n+\n \t\tcase REV_TREE_SAME:\n+\t\t\tall_empty = 0;\n \t\t\ttree_same = 1;\n \t\t\tif (!revs->simplify_history || (p->object.flags & UNINTERESTING)) {\n \t\t\t\t/* Even if a merge with an uninteresting\n@@ -421,12 +435,26 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)\n \t\t/* fallthrough */\n \t\tcase REV_TREE_OLD:\n \t\tcase REV_TREE_DIFFERENT:\n+\t\t\tall_empty = 0;\n \t\t\ttree_changed = 1;\n \t\t\tpp = &parent->next;\n \t\t\tcontinue;\n+\n \t\t}\n \t\tdie(\"bad tree compare for commit %s\", sha1_to_hex(commit->object.sha1));\n \t}\n+\n+\tif (all_empty && revs->simplify_history) {\n+\t\t/*\n+\t\t * No path we are interested in appears in any of the\n+\t\t * parents nor in this commit.  Just simplify the merge away.\n+\t\t */\n+\t\tparent = commit->parents;\n+\t\tparent->next = NULL;\n+\t\tcommit->object.flags |= TREESAME;\n+\t\treturn;\n+\t}\n+\n \tif (tree_changed && !tree_same)\n \t\treturn;\n \tcommit->object.flags |= TREESAME;\ndiff --git a/revision.h b/revision.h\nindex 227164c..7064d35 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -121,6 +121,7 @@ struct rev_info {\n #define REV_TREE_NEW\t\t1\t/* Only new files */\n #define REV_TREE_OLD\t\t2\t/* Only files removed */\n #define REV_TREE_DIFFERENT\t3\t/* Mixed changes */\n+#define REV_TREE_EMPTY\t\t4\t/* Same but only because both are empty */\n \n /* revision.c */\n void read_revisions_from_stdin(struct rev_info *revs);\ndiff --git a/tree-diff.c b/tree-diff.c\nindex edd8394..692d1cb 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -298,6 +298,7 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, stru\n \t\t\tupdate_tree_entry(t1);\n \t\t\tcontinue;\n \t\t}\n+\t\tDIFF_OPT_SET(opt, CHECKED_CHANGES);\n \t\tswitch (compare_tree_entry(t1, t2, base, baselen, opt)) {\n \t\tcase -1:\n \t\t\tupdate_tree_entry(t1);\n"},{"id":"115383","messageId":"32541b130906031459p32640f4dte5e2e079cf4093d7@mail.gmail.com","threadId":"19641","inReplyTo":"alpine.LFD.2.01.0906031431100.4880@localhost.localdomain","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Avery Pennarun","fromEmail":"apenwarr@gmail.com","sentAt":"2009-06-03T21:59:07Z","receivedAt":"2009-06-03T21:59:07Z","isPatch":false,"sender":{"key":"apenwarr@gmail.com","avatar":"https://avatars.githubusercontent.com/u/20592?v=4"},"body":"On Wed, Jun 3, 2009 at 5:33 PM, Linus Torvalds\n<torvalds@linux-foundation.org> wrote:\n> On Wed, 3 Jun 2009, Junio C Hamano wrote:\n>> E.g. \"git log --graph --oneline -- git-clone.sh\" shows that the scripted\n>> version ceased to exist at 8434c2f\n>\n> Btw, this example misses the whole point of the original problem.\n>\n> The original problem was:\n>\n>  - create new file 'x' in branch 'a'\n>\n>  - merge branch 'a' into branch 'b', but because of a merge conflict and\n>   confurion in the merge, the merge result doesn't contain 'x' any more.\n>\n>  - try to find out what happened to 'x' after-the-fact.\n>\n> Try it. Git really doesn't make it very easy, because git will notice that\n> 'x' didn't exist before the branch either (in branch 'b'), so there will\n> be _no_ sign of 'x' actually going away.\n\nNote that full-file deletion is only one particular case of a general\nproblem.  Consider this script:\n\n===\n\n#!/bin/bash\nrm -rf testb\nmkdir testb\ncd testb\ngit init\necho 'initial a' >a\necho 'initial b' >b\ngit add a b\ngit commit -m 'initial commit'\n\ngit branch sub\n\necho 'first a change' >>a\ngit add a\ngit commit -m 'modify a'\n\ngit checkout sub\necho 'second a change' >>a\ngit add a\ngit commit -m 'modify a differently'\n\ngit checkout master\ngit merge -s ours sub\n\n===\n\n\"git log -p\" will show the addition of the line \"second a change\" to\na, but it doesn't show that, in fact, this line was later deleted\nbecause of a botched merge.  There is no obvious way to find out in\nwhich commit the line was lost.  If someone doing a code review is\nreviewing all the patches from \"git log -p\", then they'll miss the\nfact that this patch was lost.\n\n('git log -p -- a\" doesn't show that the line was added at all - which\nis true, in some sense, but it's equally true that, in the example you\ngave, 'x' was never added.)\n\nHave fun,\n\nAvery\n"},{"id":"115385","messageId":"7v8wk96knh.fsf@alter.siamese.dyndns.org","threadId":"19641","inReplyTo":"alpine.LFD.2.01.0906031431100.4880@localhost.localdomain","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-06-03T22:02:26Z","receivedAt":"2009-06-03T22:02:26Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> The original problem was:\n>\n>  - create new file 'x' in branch 'a'\n>\n>  - merge branch 'a' into branch 'b', but because of a merge conflict and \n>    confurion in the merge, the merge result doesn't contain 'x' any more.\n>\n>  - try to find out what happened to 'x' after-the-fact.\n>\n> Try it. Git really doesn't make it very easy, because git will notice that \n> 'x' didn't exist before the branch either (in branch 'b'), so there will \n> be _no_ sign of 'x' actually going away.\n\nThat is true.  The \"crude attempt\" patch I just sent actually catches\nthis, but it does not show the lossage of \"new\" in the \"diff/diffstat\"\npart of the merge, when run with \"git log --stat -- x\".  Besides, it shows\ntoo many other uninteresting \"merged two branches, resolving to lossage of\nthe path the same way as all the previous merges\" to be really useful.\n"},{"id":"115388","messageId":"alpine.LFD.2.01.0906031504080.4880@localhost.localdomain","threadId":"19641","inReplyTo":"7v8wk96knh.fsf@alter.siamese.dyndns.org","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2009-06-03T22:17:49Z","receivedAt":"2009-06-03T22:17:49Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 3 Jun 2009, Junio C Hamano wrote:\n\n> Linus Torvalds <torvalds@linux-foundation.org> writes:\n> \n> > The original problem was:\n> >\n> >  - create new file 'x' in branch 'a'\n> >\n> >  - merge branch 'a' into branch 'b', but because of a merge conflict and \n> >    confurion in the merge, the merge result doesn't contain 'x' any more.\n> >\n> >  - try to find out what happened to 'x' after-the-fact.\n> >\n> > Try it. Git really doesn't make it very easy, because git will notice that \n> > 'x' didn't exist before the branch either (in branch 'b'), so there will \n> > be _no_ sign of 'x' actually going away.\n> \n> That is true.  The \"crude attempt\" patch I just sent actually catches\n> this, but it does not show the lossage of \"new\" in the \"diff/diffstat\"\n> part of the merge, when run with \"git log --stat -- x\".  Besides, it shows\n> too many other uninteresting \"merged two branches, resolving to lossage of\n> the path the same way as all the previous merges\" to be really useful.\n\nYes.\n\nThinking more about it, we always did have fairly good workarounds for the \n\"we optimized away the history too aggressively\" (ie the original \n--full-history, and then the newer and nicer --simplify-merges).\n\nSo I'm starting to suspect that I was just wrong in looking at the \nrevision history simplification. Yes, that can cause simplification that \nwe don't want, but on the other hand, it's reasonably easy to work around.\n\nMaybe what we want is a better model for showing diffs from merges.\n\nFor example, right now there is _no_ way to get even a \"show diff relative \nto first parent\". You can do \"-m\", which will show it relative to _both_ \nparents, but nobody ever wants that. And you can do \"-c\" or \"--cc\", but \nthat simplifies away all the paths that match in one. \n\nSo here's a challenge: in the git repository, get a nice view of what your \nmerges looked like. The closest I can get is\n\n\tgit log -c --stat --grep=\"Merge branch '\"\n\nwhich is actually very non-intuitive (\"-c\" on its own gives no useful \noutput, but \"-c --stat\" gives nice diffstat against the first parent, \nwhich in this case is what we want).\n\nBut I can't actually get git to generate the _patch_ that the --stat \ndescribes. You'd have to do something like\n\n\tgit rev-list --parents --grep=\"Merge branch '\" HEAD |\n\t\twhile read a b c; do git show -s $a ; git diff $b..$a; done | less -S\n\nwhich is pretty ugly.\n\n\t\t\tLinus\n"},{"id":"115389","messageId":"7vmy8p54fr.fsf@alter.siamese.dyndns.org","threadId":"19641","inReplyTo":"alpine.LFD.2.01.0906031504080.4880@localhost.localdomain","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-06-03T22:38:00Z","receivedAt":"2009-06-03T22:38:00Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> For example, right now there is _no_ way to get even a \"show diff relative \n> to first parent\". You can do \"-m\", which will show it relative to _both_ \n> parents, but nobody ever wants that. And you can do \"-c\" or \"--cc\", but \n> that simplifies away all the paths that match in one. \n\nActually for that \"Where did my file 'x' go across the merge chain\", I was\ngoing to suggest something like\n\n\tgit log --simplify-merges -m --raw -- x\n\n> So here's a challenge: in the git repository, get a nice view of what your \n> merges looked like. The closest I can get is\n>\n> \tgit log -c --stat --grep=\"Merge branch '\"\n>\n> which is actually very non-intuitive (\"-c\" on its own gives no useful \n> output, but \"-c --stat\" gives nice diffstat against the first parent, \n> which in this case is what we want).\n\nI think the logical place to hook that into is the --first-parent option.\n\nI actually have very hard resisted so far the temptation to do so because\nyour mantra has always been \"in a merge, all parents are equal.\"  If you\ntreat the first parent specially too heavily, it would go against the \"I\ngot a pull request from you, but the resulting conflicts are too much for\nme; you know the area much better than I do, so could you do the merge for\nme and I'll fast forward to you later\" workflow.  The \"first parent is the\nmainline\" and \"I am important, so I'll merge with --no-ff to pee in the\nsnow\" mentality problems will become worse.\n"},{"id":"115391","messageId":"20090603224413.GA2902@coredump.intra.peff.net","threadId":"19641","inReplyTo":"7vmy8p54fr.fsf@alter.siamese.dyndns.org","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-06-03T22:44:13Z","receivedAt":"2009-06-03T22:44:13Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jun 03, 2009 at 03:38:00PM -0700, Junio C Hamano wrote:\n\n> Actually for that \"Where did my file 'x' go across the merge chain\", I was\n> going to suggest something like\n> \n> \tgit log --simplify-merges -m --raw -- x\n\nBut in the original example, the merge commit where 'x' is deleted isn't\nshown _at all_ when path limiting is used. You end up either with\n\"git log -m\" showing the two sides of the merge separately, \"git log\n--simplify-merges -- x\" showing stuff that happened on the side branch\nbut _not_ the actual merge that made a change, or of course \"git log --\nx\" showing nothing (because we don't traverse the changing side of the\nmerge).\n\nIs there a way to say \"show me everything that touched x, _including_\nmerges\"?\n\n-Peff\n"},{"id":"115392","messageId":"alpine.LFD.2.01.0906031544170.4880@localhost.localdomain","threadId":"19641","inReplyTo":"7vmy8p54fr.fsf@alter.siamese.dyndns.org","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2009-06-03T22:47:13Z","receivedAt":"2009-06-03T22:47:13Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 3 Jun 2009, Junio C Hamano wrote:\n\n> Linus Torvalds <torvalds@linux-foundation.org> writes:\n> \n> > For example, right now there is _no_ way to get even a \"show diff relative \n> > to first parent\". You can do \"-m\", which will show it relative to _both_ \n> > parents, but nobody ever wants that. And you can do \"-c\" or \"--cc\", but \n> > that simplifies away all the paths that match in one. \n> \n> Actually for that \"Where did my file 'x' go across the merge chain\", I was\n> going to suggest something like\n> \n> \tgit log --simplify-merges -m --raw -- x\n\nOk. Not very readable, but it's certainly getting closer.\n\n> > So here's a challenge: in the git repository, get a nice view of what your \n> > merges looked like. The closest I can get is\n> >\n> > \tgit log -c --stat --grep=\"Merge branch '\"\n> >\n> > which is actually very non-intuitive (\"-c\" on its own gives no useful \n> > output, but \"-c --stat\" gives nice diffstat against the first parent, \n> > which in this case is what we want).\n> \n> I think the logical place to hook that into is the --first-parent option.\n> \n> I actually have very hard resisted so far the temptation to do so because\n> your mantra has always been \"in a merge, all parents are equal.\" \n\nOh, I agree. The challenge was just the first step - how to make it do \nmerges in general sanely would be the issue.\n\nBecause if you do just --first-parent, then that won't even show the \ntest-case that Graham actually had - because the missing file didn't come \nin from the first parent of a merge. \n\nThe challenge was mainly as a way to point out that even some fairly \nsimple cases aren't all that simple.\n\n\t\tLinus\n"},{"id":"115393","messageId":"alpine.LFD.2.01.0906031547480.4880@localhost.localdomain","threadId":"19641","inReplyTo":"20090603224413.GA2902@coredump.intra.peff.net","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2009-06-03T22:56:01Z","receivedAt":"2009-06-03T22:56:01Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 3 Jun 2009, Jeff King wrote:\n> \n> Is there a way to say \"show me everything that touched x, _including_\n> merges\"?\n\nWell, that's the \"--simplify-merges\" part. \n\nIt's just that our diff generation isn't very smart. We do show the \ncommit, we just don't show a meaningful diff in that case.\n\nAnd doing good diffs for a merge is _hard_. The \"--cc\" thing is supremely \nuseful - it's just that it's useful for data conflicts, not for metadata \nissues.\n\nIt's in fact somewhat dubious if you actually want to see the file removal \nas a _diff_ in a merge, exactly because it's so verbose and yet often so \nuninteresting (ie the removal may well be intentional). \n\nIt might be that the right thing to do is to expand on \"-c\" and '--cc\" to \njust give a summary of metadata changes.\n\nRight now, \"-c\" and \"--cc\" ignore files that didn't change from one of the \nparents.\n\nBut maybe the right thing to do is to entirely ignore files only if they \nexist in all parents, but didn't change in one - and for things that have \nactual metadata changes, just say\n\n - exists in merge result, but not in parent 2:\n\n   File 'x' was created in parent 1\n\n - does not exist in merge result, but exists in parent 1:\n\n   File 'y' was deleted by parent 2\n\nor similar (with perhaps even rename detection some day if -M is \nspecified, although n-way rename detection is likely pretty painful).\n\nIOW, do the whole \"extended diff\", but not actually show any diffs, just \nsummary information, for new/deleted files in merges.\n\nThat would be enough of a hint to then use other tools to see the exact \ndetails of what happened..\n\n\t\t\tLinus\n"},{"id":"115394","messageId":"7vtz2x3ozt.fsf@alter.siamese.dyndns.org","threadId":"19641","inReplyTo":"alpine.LFD.2.01.0906031504080.4880@localhost.localdomain","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-06-03T22:56:54Z","receivedAt":"2009-06-03T22:56:54Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> Thinking more about it, we always did have fairly good workarounds for the \n> \"we optimized away the history too aggressively\" (ie the original \n> --full-history, and then the newer and nicer --simplify-merges).\n\nThis is offtopic, but there is a rather funny hidden feature in that\n\"newer and nicer\" option.\n\n    $ git log --simplify-merges -- no-no-no-such-path-ever-existed\n\nwill lists the merges that do not have any common ancestors.\n\nI do not know where it comes from, or if it is worth fixing.\n"},{"id":"115395","messageId":"20090603230606.GA5462@coredump.intra.peff.net","threadId":"19641","inReplyTo":"alpine.LFD.2.01.0906031547480.4880@localhost.localdomain","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-06-03T23:06:06Z","receivedAt":"2009-06-03T23:06:06Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jun 03, 2009 at 03:56:01PM -0700, Linus Torvalds wrote:\n\n> > Is there a way to say \"show me everything that touched x, _including_\n> > merges\"?\n> \n> Well, that's the \"--simplify-merges\" part. \n> \n> It's just that our diff generation isn't very smart. We do show the \n> commit, we just don't show a meaningful diff in that case.\n\nNo, --simplify-merges doesn't show the merge, unless I am doing\nsomething very wrong. Try (and this is a simplified version of the\noriginal example):\n\n  mkdir repo && cd repo && git init &&\n  echo content >base && git add base && git commit -m base &&\n  echo context >a.txt && git add a.txt && git commit -m 'master 1' &&\n  git checkout -b other HEAD^ &&\n  echo content >b.txt && git add b.txt && git commit -m 'other 1' &&\n  echo conflict >a.txt && git add a.txt && git commit -m 'other 2' &&\n  git checkout master &&\n  git merge other ;# conflicts\n\n  rm b.txt && git add b.txt &&\n  echo resolve >a.txt && git add a.txt &&\n  git commit -m merged\n\nNow try running git log on that. I can see the merge diff if I use \"-m\",\nwhich is obviously too verbose, but at least works. But if I give\n\"b.txt\" as a path limiter, I can't get the merge commit to display at\nall. Doing \"git log -m --simplify-merges --stat -- b.txt\" yields only\nthe commit \"other 1\" in which b.txt was added.\n\n-Peff\n"},{"id":"115396","messageId":"alpine.LFD.2.01.0906031619150.4880@localhost.localdomain","threadId":"19641","inReplyTo":"20090603230606.GA5462@coredump.intra.peff.net","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2009-06-03T23:27:39Z","receivedAt":"2009-06-03T23:27:39Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 3 Jun 2009, Jeff King wrote:\n>\n> Try (and this is a simplified version of the original example):\n> \n>   mkdir repo && cd repo && git init &&\n>   echo content >base && git add base && git commit -m base &&\n>   echo context >a.txt && git add a.txt && git commit -m 'master 1' &&\n>   git checkout -b other HEAD^ &&\n>   echo content >b.txt && git add b.txt && git commit -m 'other 1' &&\n>   echo conflict >a.txt && git add a.txt && git commit -m 'other 2' &&\n>   git checkout master &&\n>   git merge other ;# conflicts\n> \n>   rm b.txt && git add b.txt &&\n>   echo resolve >a.txt && git add a.txt &&\n>   git commit -m merged\n\nThis doesn't work at all for me.\n\nDo\n\n\tgit show HEAD:b.txt\n\nand it still shows b.txt in the commit. You should have used\n\n\tgit rm b.txt\n\nrather than \"git add b.txt\" (or you use use \"-u\" or \"-a\" to git add).\n\nThat looks like a bug, btw, but whatever. It seems intentional (we do the \nwhole \"ADD_CACHE_IGNORE_REMOVAL\" flag thing).\n\nBut you're right. Even when fixed, it does seem to need \"--full-history\" \nto stay around, and --simplify-merges is insufficient. Bug in merge \nsimplification?\n\n\t\t\tLinus\n"},{"id":"115398","messageId":"20090603233710.GA10578@coredump.intra.peff.net","threadId":"19641","inReplyTo":"alpine.LFD.2.01.0906031619150.4880@localhost.localdomain","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-06-03T23:37:10Z","receivedAt":"2009-06-03T23:37:10Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jun 03, 2009 at 04:27:39PM -0700, Linus Torvalds wrote:\n\n> >   rm b.txt && git add b.txt &&\n> >   echo resolve >a.txt && git add a.txt &&\n> >   git commit -m merged\n> \n> This doesn't work at all for me.\n> \n> Do\n> \n> \tgit show HEAD:b.txt\n> \n> and it still shows b.txt in the commit. You should have used\n> \n> \tgit rm b.txt\n> \n> rather than \"git add b.txt\" (or you use use \"-u\" or \"-a\" to git add).\n\nEr, sorry, yeah, I botched the recipe (I initially used \"git rm\" by\nitself, but it complains about \"changes staged in the index\", so I fixed\nit up manually and then botched writing out the automated version).\n\nBut I see you figured out what I meant, so...\n\n> But you're right. Even when fixed, it does seem to need \"--full-history\" \n> to stay around, and --simplify-merges is insufficient. Bug in merge \n> simplification?\n\nI don't even see it with --full-history. I get:\n\n  $ git log -m --stat --oneline | head\n  b1a38ec (from 2671fa7) merged\n   a.txt |    2 +-\n   1 files changed, 1 insertions(+), 1 deletions(-)\n  b1a38ec (from d0bac65) merged\n   a.txt |    2 +-\n   b.txt |    1 -\n   2 files changed, 1 insertions(+), 2 deletions(-)\n\n  $ git log -m --stat --oneline -- b.txt\n  (no output)\n\n  $ git log --simplify-merges -m --stat --oneline -- b.txt\n  912ac84 other 1\n   b.txt |    1 +\n   1 files changed, 1 insertions(+), 0 deletions(-)\n\n  $ git log --full-history -m --stat --oneline -- b.txt\n  912ac84 other 1\n   b.txt |    1 +\n   1 files changed, 1 insertions(+), 0 deletions(-)\n\nIs there some trick to enabling both path limiting _and_ still showing\nthe merge commit? Or is this a bug?\n\n-Peff\n"},{"id":"115404","messageId":"alpine.LFD.2.01.0906031855460.4880@localhost.localdomain","threadId":"19641","inReplyTo":"20090603233710.GA10578@coredump.intra.peff.net","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2009-06-04T01:58:21Z","receivedAt":"2009-06-04T01:58:21Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 3 Jun 2009, Jeff King wrote:\n>\n> I don't even see it with --full-history. \n\nOh, that's because I used \"gitk\" rather than \"git log\", and that adds \n--parents, which in turn means that it actually keeps the merges. \n\nSo with\n\n\tgit log --full-history --parents --stat -- b.txt\n\nyou actually finally get a visible removal.\n\n\t\tLinus\n"},{"id":"115413","messageId":"7v63fc1o72.fsf@alter.siamese.dyndns.org","threadId":"19641","inReplyTo":"alpine.LFD.2.01.0906031547480.4880@localhost.localdomain","subject":"Re: Am able to delete a file with no trace in the log","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-06-04T06:57:05Z","receivedAt":"2009-06-04T06:57:05Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> And doing good diffs for a merge is _hard_. The \"--cc\" thing is supremely \n> useful - it's just that it's useful for data conflicts, not for metadata \n> issues.\n>\n> It's in fact somewhat dubious if you actually want to see the file removal \n> as a _diff_ in a merge, exactly because it's so verbose and yet often so \n> uninteresting (ie the removal may well be intentional). \n\nThinking about this a bit more, a \"good\" diff for merge that catches this\nkind of \"broken merge\" would likely require redoing a merge when you ask\nfor a diff.\n\nEarlier, in my \"Here is a crude attempt\" patch, I mentioned that the\nmerges in the beginning part of the output are uninteresting while the one\nthat merges \"Build-in git-clone\" is interesting (and \"crude attempt\" was\nnot useful because it shows both).\n\nThe reason?  The uninteresting ones match mechanical merge result, while\nthe interesting one does not.  For example, 17d778e (the first one in the\noutput --- an uninteresting one) is a merge between 5e97f46 and 450f437;\nthe former branch removes the path in question (git-clone.sh) compared to\ntheir merge base, while the latter does not change it, hence the\nmechanical resolution to remove the path matches what the final merge\nrecords.  On the other hand, b84c343 (Merge branch 'db/clone-in-c') merges \n0dbaa5b (modifies \"git-clone.sh\" since the merge base) and b50c846\n(removes) and the mechanical merge results in a conflict.\n\nI think the original example can be handled in the same way.  A side\nbranch created a new file since the merge base, but a merge lost the file\nby mistake.  The recorded result does not resemble the mechanical merge\nresult and we can flag it by detecting this condition (that is, if we\nwanted to --- I do not think we want to spend cycles to recreate a merge\nwhile traversing the history with \"log --stat\" by default).\n"}]}