{"thread":{"id":"19419","subject":"Reverting an uncommitted revert","startedAt":"2009-05-20T02:34:40Z","lastAt":"2009-05-21T06:16:17Z","messageCount":28,"participants":["Joshua Jensen","Nicolas Pitre","Jeff King","Ping Yin","Wincent Colaiuta","Jakub Narebski","Shawn O. Pearce","Sverre Rabbelier","Eric Raible","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"114314","messageId":"4A136C40.6020808@workspacewhiz.com","threadId":"19419","inReplyTo":null,"subject":"Reverting an uncommitted revert","fromName":"Joshua Jensen","fromEmail":"jjensen@workspacewhiz.com","sentAt":"2009-05-20T02:34:40Z","receivedAt":"2009-05-20T02:34:40Z","isPatch":false,"sender":{"key":"jjensen@workspacewhiz.com","avatar":"https://avatars.githubusercontent.com/u/111687?v=4"},"body":"I had an interesting experience the other day I'd like to share.  It is \na story of my stupidity plus a bug in the latest version of the Mac OS X \n'git gui'.\n\nI had spent several evenings working on a piece of source code.  It was \nbasically working, but I hadn't committed anything yet. Yes, I know I \nshould have made a branch and performed incremental commits, but I \ndidn't.  This is why I was stupid.\n\nBecause of a busy week, it was many days before I could get back to what \nI was working on.  By then, I had forgotten what I was doing, and so I \npulled up the handy 'git gui' to show me my diffs.\n\n'git gui' showed me a single whitespace change in the diff.  The scroll \nbar extended from top to bottom in the diff window and wouldn't move.  \n(This is the bug, which is easily reproducible.)  I thought, \"Gee, \nthat's weird.  I thought I'd made more changes.\"  Not wanting the silly \nwhitespace change, I chose Commit->Revert Changes from the menu.\n\nA couple hours later, I was pulling my hair out trying to find the \nchange that I knew, deep down, I had reverted.\n\nI also knew there was no support for putting a reverted change in the \nreflog, but hey, the reflog has saved me before.  I looked.  It wasn't \nthere.\n\nSo here's the idea: What if Git, upon a revert change (or git reset \n--hard HEAD), \"committed\" the changes to be reverted and then did the \nrevert with a 'git reset --hard HEAD^'?  The reverted files would be \ndisconnected from a branch, but they would be available in the reflog to \nretrieve.\n\nOr do I just not 'get it'?\n\nThanks!\n\nJosh\n"},{"id":"114315","messageId":"alpine.LFD.2.00.0905192300070.3906@xanadu.home","threadId":"19419","inReplyTo":"4A136C40.6020808@workspacewhiz.com","subject":"Re: Reverting an uncommitted revert","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2009-05-20T03:10:14Z","receivedAt":"2009-05-20T03:10:14Z","isPatch":false,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"On Tue, 19 May 2009, Joshua Jensen wrote:\n\n> So here's the idea: What if Git, upon a revert change (or git reset --hard\n> HEAD), \"committed\" the changes to be reverted and then did the revert with a\n> 'git reset --hard HEAD^'?  The reverted files would be disconnected from a\n> branch, but they would be available in the reflog to retrieve.\n\nI think there is indeed some value in having a commit of the work \ndirectory dirty state automatically made before this state is discarded, \nand stuff a reference to that commit in the HEAD reflog.  I think such a \nfeature would need to be made configurable and active by default.\n\n\nNicolas\n"},{"id":"114316","messageId":"20090520032139.GB10212@coredump.intra.peff.net","threadId":"19419","inReplyTo":"alpine.LFD.2.00.0905192300070.3906@xanadu.home","subject":"Re: Reverting an uncommitted revert","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-05-20T03:21:39Z","receivedAt":"2009-05-20T03:21:39Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, May 19, 2009 at 11:10:14PM -0400, Nicolas Pitre wrote:\n\n> > So here's the idea: What if Git, upon a revert change (or git reset --hard\n> > HEAD), \"committed\" the changes to be reverted and then did the revert with a\n> > 'git reset --hard HEAD^'?  The reverted files would be disconnected from a\n> > branch, but they would be available in the reflog to retrieve.\n> \n> I think there is indeed some value in having a commit of the work \n> directory dirty state automatically made before this state is discarded, \n\nRelated to this, I have wondered if it might be useful to have an \"index\nreflog\". If I do something like this:\n\n  $ git add foo\n  $ hack hack hack\n  $ git add foo\n\nThen the first added state of \"foo\" is available in the object database,\nbut it is not connected to the name \"foo\" in any way, which makes it\nmuch harder to find. If we had a reflog pointing to trees representing\nthe index state after each change, then it would be simple (you could\nlook at \"INDEX@{1}:foo\" or similar).\n\nI don't know if the performance is an issue. We are writing an extra\ntree every time we touch the index, but in many cases you are already\nwriting a blob.\n\n> and stuff a reference to that commit in the HEAD reflog.  I think such a \n> feature would need to be made configurable and active by default.\n\nI'm not sure if putting it in the HEAD reflog is a good idea. The\nreflogs and what goes in them are very user-visible. Do users make the\nassumption that HEAD@{1} will be where they expect (e.g., because they\njust did a reset, they expect HEAD@{1} to be the prevoius commit, but\nwith this patch it would actually be HEAD@{2}, with HEAD@{1} as the\nworktree pseudo-commit)?\n\n-Peff\n"},{"id":"114317","messageId":"alpine.LFD.2.00.0905192328310.3906@xanadu.home","threadId":"19419","inReplyTo":"20090520032139.GB10212@coredump.intra.peff.net","subject":"Re: Reverting an uncommitted revert","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2009-05-20T03:35:45Z","receivedAt":"2009-05-20T03:35:45Z","isPatch":false,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"On Tue, 19 May 2009, Jeff King wrote:\n\n> On Tue, May 19, 2009 at 11:10:14PM -0400, Nicolas Pitre wrote:\n> \n> > > So here's the idea: What if Git, upon a revert change (or git reset --hard\n> > > HEAD), \"committed\" the changes to be reverted and then did the revert with a\n> > > 'git reset --hard HEAD^'?  The reverted files would be disconnected from a\n> > > branch, but they would be available in the reflog to retrieve.\n> > \n> > I think there is indeed some value in having a commit of the work \n> > directory dirty state automatically made before this state is discarded, \n> \n> Related to this, I have wondered if it might be useful to have an \"index\n> reflog\". If I do something like this:\n> \n>   $ git add foo\n>   $ hack hack hack\n>   $ git add foo\n> \n> Then the first added state of \"foo\" is available in the object database,\n> but it is not connected to the name \"foo\" in any way, which makes it\n> much harder to find. If we had a reflog pointing to trees representing\n> the index state after each change, then it would be simple (you could\n> look at \"INDEX@{1}:foo\" or similar).\n> \n> I don't know if the performance is an issue. We are writing an extra\n> tree every time we touch the index, but in many cases you are already\n> writing a blob.\n\nWell... Actually I think having a reflog dedicated to discarded state \nwould probably be a better idea. And...\n\n> > and stuff a reference to that commit in the HEAD reflog.  I think such a \n> > feature would need to be made configurable and active by default.\n> \n> I'm not sure if putting it in the HEAD reflog is a good idea. The\n> reflogs and what goes in them are very user-visible. Do users make the\n> assumption that HEAD@{1} will be where they expect (e.g., because they\n> just did a reset, they expect HEAD@{1} to be the prevoius commit, but\n> with this patch it would actually be HEAD@{2}, with HEAD@{1} as the\n> worktree pseudo-commit)?\n\nHaving a \"trash\" reflog would solve this unambiguously.  That could also \ninclude your index example above.  However, in the index case, I'd \nrecord a reflog entry only if you're about to discard a previously non \ncommitted entry.  If you do:\n\n\t$ git add foo\n\t$ git add bar\n\t$ git commit\n\t$ hack hack hack\n\t$ git add foo\n\nthen in this case there is nothing to be lost hence no additional entry \nin the \"trash\" reflog.\n\n\nNicolas\n"},{"id":"114318","messageId":"20090520033848.GA10581@coredump.intra.peff.net","threadId":"19419","inReplyTo":"alpine.LFD.2.00.0905192328310.3906@xanadu.home","subject":"Re: Reverting an uncommitted revert","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-05-20T03:38:48Z","receivedAt":"2009-05-20T03:38:48Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, May 19, 2009 at 11:35:45PM -0400, Nicolas Pitre wrote:\n\n> Having a \"trash\" reflog would solve this unambiguously.  That could also \n> include your index example above.\n\n\nYeah, I think combining the two in a single log makes sense.\n\n> However, in the index case, I'd record a reflog entry only if you're\n> about to discard a previously non committed entry.  If you do:\n> \n> \t$ git add foo\n> \t$ git add bar\n> \t$ git commit\n> \t$ hack hack hack\n> \t$ git add foo\n> \n> then in this case there is nothing to be lost hence no additional entry \n> in the \"trash\" reflog.\n\nGood point. That both helps the performance and keeps the log less\ncluttered.\n\n-Peff\n"},{"id":"114319","messageId":"46dff0320905192158k340ece90xd88af53792056b67@mail.gmail.com","threadId":"19419","inReplyTo":"alpine.LFD.2.00.0905192328310.3906@xanadu.home","subject":"Re: Reverting an uncommitted revert","fromName":"Ping Yin","fromEmail":"pkufranky@gmail.com","sentAt":"2009-05-20T04:58:05Z","receivedAt":"2009-05-20T04:58:05Z","isPatch":false,"sender":{"key":"pkufranky@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5346?v=4"},"body":"On Wed, May 20, 2009 at 11:35 AM, Nicolas Pitre <nico@cam.org> wrote:\n> On Tue, 19 May 2009, Jeff King wrote:\n>\n>> On Tue, May 19, 2009 at 11:10:14PM -0400, Nicolas Pitre wrote:\n>>\n>> > > So here's the idea: What if Git, upon a revert change (or git reset --hard\n>> > > HEAD), \"committed\" the changes to be reverted and then did the revert with a\n>> > > 'git reset --hard HEAD^'?  The reverted files would be disconnected from a\n>> > > branch, but they would be available in the reflog to retrieve.\n>> >\n>> > I think there is indeed some value in having a commit of the work\n>> > directory dirty state automatically made before this state is discarded,\n>>\n>> Related to this, I have wondered if it might be useful to have an \"index\n>> reflog\". If I do something like this:\n>>\n>>   $ git add foo\n>>   $ hack hack hack\n>>   $ git add foo\n>>\n>> Then the first added state of \"foo\" is available in the object database,\n>> but it is not connected to the name \"foo\" in any way, which makes it\n>> much harder to find. If we had a reflog pointing to trees representing\n>> the index state after each change, then it would be simple (you could\n>> look at \"INDEX@{1}:foo\" or similar).\n>>\n>> I don't know if the performance is an issue. We are writing an extra\n>> tree every time we touch the index, but in many cases you are already\n>> writing a blob.\n>\n> Well... Actually I think having a reflog dedicated to discarded state\n> would probably be a better idea. And...\n>\n\nAgree. The issues about losing changes due to wrong operation have\nraised many times in the list, such as\n\ngit add .\ngit reset --hard\n\nWith reflog for discarded changes,  this can be easily recovered.\n"},{"id":"114327","messageId":"025225A0-FACC-4A29-A747-40201A7FBA19@wincent.com","threadId":"19419","inReplyTo":"alpine.LFD.2.00.0905192328310.3906@xanadu.home","subject":"Re: Reverting an uncommitted revert","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2009-05-20T09:15:31Z","receivedAt":"2009-05-20T09:15:31Z","isPatch":false,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"El 20/5/2009, a las 5:35, Nicolas Pitre escribió:\n\n> Having a \"trash\" reflog would solve this unambiguously.  That could  \n> also\n> include your index example above.  However, in the index case, I'd\n> record a reflog entry only if you're about to discard a previously non\n> committed entry.  If you do:\n>\n> \t$ git add foo\n> \t$ git add bar\n> \t$ git commit\n> \t$ hack hack hack\n> \t$ git add foo\n>\n> then in this case there is nothing to be lost hence no additional  \n> entry\n> in the \"trash\" reflog.\n\nThat's true in the example you provide, but it doesn't really handle  \nJeff's initial example (\"git add\" twice on the same file), where it is  \npossible to throw away intermediate state (by overwriting).\n\nI personally haven't been bitten by a mistaken use of \"git reset\" and  \nfriends for a long time now, but I really like the ideas that have  \nbeen mentioned so far in this thread. This \"trash\" or \"index\" or  \n\"reset\" reflog would be a huge step forward in \"idiot-proofing\" Git.\n\nCheers,\nWincent\n"},{"id":"114331","messageId":"m3iqjwjd01.fsf@localhost.localdomain","threadId":"19419","inReplyTo":"025225A0-FACC-4A29-A747-40201A7FBA19@wincent.com","subject":"Re: Reverting an uncommitted revert","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2009-05-20T10:16:27Z","receivedAt":"2009-05-20T10:16:27Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Wincent Colaiuta <win@wincent.com> writes:\n> El 20/5/2009, a las 5:35, Nicolas Pitre escribió:\n> \n> > Having a \"trash\" reflog would solve this unambiguously.\n[...]\n\n> I personally haven't been bitten by a mistaken use of \"git reset\" and  \n> friends for a long time now, but I really like the ideas that have  \n> been mentioned so far in this thread. This \"trash\" or \"index\" or  \n> \"reset\" reflog would be a huge step forward in \"idiot-proofing\" Git.\n\nWell, having a kind of 'Attic' for reflogs for deleted branches would\nhelp here in \"mistake-proofing\" Git (there were even IIRC some\npreliminary test implementation), although it is not as necessary\nnowadays because of reflog for HEAD, and ability to set longer expiry\ntime for it (IIRC).\n\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"114344","messageId":"alpine.LFD.2.00.0905200853010.3906@xanadu.home","threadId":"19419","inReplyTo":"025225A0-FACC-4A29-A747-40201A7FBA19@wincent.com","subject":"Re: Reverting an uncommitted revert","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2009-05-20T12:53:51Z","receivedAt":"2009-05-20T12:53:51Z","isPatch":false,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"On Wed, 20 May 2009, Wincent Colaiuta wrote:\n\n> El 20/5/2009, a las 5:35, Nicolas Pitre escribió:\n> \n> > Having a \"trash\" reflog would solve this unambiguously.  That could also\n> > include your index example above.  However, in the index case, I'd\n> > record a reflog entry only if you're about to discard a previously non\n> > committed entry.  If you do:\n> > \n> > \t$ git add foo\n> > \t$ git add bar\n> > \t$ git commit\n> > \t$ hack hack hack\n> > \t$ git add foo\n> > \n> > then in this case there is nothing to be lost hence no additional entry\n> > in the \"trash\" reflog.\n> \n> That's true in the example you provide, but it doesn't really handle Jeff's\n> initial example (\"git add\" twice on the same file), where it is possible to\n> throw away intermediate state (by overwriting).\n\nDid I disagree with Jeff's original example?\n\n\nNicolas\n"},{"id":"114346","messageId":"20090520141709.GO30527@spearce.org","threadId":"19419","inReplyTo":"alpine.LFD.2.00.0905200853010.3906@xanadu.home","subject":"Re: Reverting an uncommitted revert","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-05-20T14:17:09Z","receivedAt":"2009-05-20T14:17:09Z","isPatch":false,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Nicolas Pitre <nico@cam.org> wrote:\n> On Wed, 20 May 2009, Wincent Colaiuta wrote:\n> > El 20/5/2009, a las 5:35, Nicolas Pitre escribi?:\n> > \n> > > Having a \"trash\" reflog would solve this unambiguously.  That could also\n> > > include your index example above.  However, in the index case, I'd\n> > > record a reflog entry only if you're about to discard a previously non\n> > > committed entry.  If you do:\n> > > \n> > > \t$ git add foo\n> > > \t$ git add bar\n> > > \t$ git commit\n> > > \t$ hack hack hack\n> > > \t$ git add foo\n> > > \n> > > then in this case there is nothing to be lost hence no additional entry\n> > > in the \"trash\" reflog.\n> > \n> > That's true in the example you provide, but it doesn't really handle Jeff's\n> > initial example (\"git add\" twice on the same file), where it is possible to\n> > throw away intermediate state (by overwriting).\n> \n> Did I disagree with Jeff's original example?\n\nNo, but if you don't read closely enough to notice that the first\nadd affects path \"foo\" and the second add \"bar\", you might miss\nwhat you were trying to say.  I almost thought you disagreed with\nPeff until I read your example a 2nd time.  :-)\n\nYou did say \"uncommitted entry causes reflog append\", so in Peff's\noriginal example of \"git add a; vi a; git add a\", we should be\ncreating a reflog entry for that first added state, which is clearly\nnot a disagreement.\n\nFWIW, I think this is a great idea, but lack the time to code it\nmyself, otherwise I probably would start hacking on it right now.\n\n-- \nShawn.\n"},{"id":"114352","messageId":"96BC1064-EEEF-48BC-B79A-9D15C517CF47@wincent.com","threadId":"19419","inReplyTo":"alpine.LFD.2.00.0905200853010.3906@xanadu.home","subject":"Re: Reverting an uncommitted revert","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2009-05-20T15:23:17Z","receivedAt":"2009-05-20T15:23:17Z","isPatch":false,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"El 20/5/2009, a las 14:53, Nicolas Pitre escribió:\n\n> On Wed, 20 May 2009, Wincent Colaiuta wrote:\n>\n>> El 20/5/2009, a las 5:35, Nicolas Pitre escribió:\n>>\n>>> Having a \"trash\" reflog would solve this unambiguously.  That  \n>>> could also\n>>> include your index example above.  However, in the index case, I'd\n>>> record a reflog entry only if you're about to discard a previously  \n>>> non\n>>> committed entry.  If you do:\n>>>\n>>> \t$ git add foo\n>>> \t$ git add bar\n>>> \t$ git commit\n>>> \t$ hack hack hack\n>>> \t$ git add foo\n>>>\n>>> then in this case there is nothing to be lost hence no additional  \n>>> entry\n>>> in the \"trash\" reflog.\n>>\n>> That's true in the example you provide, but it doesn't really  \n>> handle Jeff's\n>> initial example (\"git add\" twice on the same file), where it is  \n>> possible to\n>> throw away intermediate state (by overwriting).\n>\n> Did I disagree with Jeff's original example?\n\nNo, but I may have misinterpreted you; I understood that you said you  \nwouldn't store intermediate index state after successive \"git add\"  \nexecutions.\n\nCheers,\nWincent\n"},{"id":"114353","messageId":"alpine.LFD.2.00.0905201144040.3906@xanadu.home","threadId":"19419","inReplyTo":"96BC1064-EEEF-48BC-B79A-9D15C517CF47@wincent.com","subject":"Re: Reverting an uncommitted revert","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2009-05-20T15:47:49Z","receivedAt":"2009-05-20T15:47:49Z","isPatch":false,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"On Wed, 20 May 2009, Wincent Colaiuta wrote:\n\n> El 20/5/2009, a las 14:53, Nicolas Pitre escribió:\n> \n> > On Wed, 20 May 2009, Wincent Colaiuta wrote:\n> > \n> > > El 20/5/2009, a las 5:35, Nicolas Pitre escribió:\n> > > \n> > > > Having a \"trash\" reflog would solve this unambiguously.  That could also\n> > > > include your index example above.  However, in the index case, I'd\n> > > > record a reflog entry only if you're about to discard a previously non\n> > > > committed entry.  If you do:\n> > > > \n> > > > \t$ git add foo\n> > > > \t$ git add bar\n> > > > \t$ git commit\n> > > > \t$ hack hack hack\n> > > > \t$ git add foo\n> > > > \n> > > > then in this case there is nothing to be lost hence no additional entry\n> > > > in the \"trash\" reflog.\n> > > \n> > > That's true in the example you provide, but it doesn't really handle\n> > > Jeff's\n> > > initial example (\"git add\" twice on the same file), where it is possible\n> > > to\n> > > throw away intermediate state (by overwriting).\n> > \n> > Did I disagree with Jeff's original example?\n> \n> No, but I may have misinterpreted you; I understood that you said you wouldn't\n> store intermediate index state after successive \"git add\" executions.\n\nI wouldn't if those states don't discard data.  If you 'git add foo' and \nthen 'git add bar' you don't lose anything.  If you do two 'git add foo' \nand the second one would discard the previous state of foo which is \ndifferent from the new one _then_ it is worth adding a reflog entry for \nthe otherwise about to be lost state. \n\n\nNicolas\n"},{"id":"114354","messageId":"fabb9a1e0905200913u5252973aia9a20983b20243d3@mail.gmail.com","threadId":"19419","inReplyTo":"alpine.LFD.2.00.0905201144040.3906@xanadu.home","subject":"Re: Reverting an uncommitted revert","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2009-05-20T16:13:55Z","receivedAt":"2009-05-20T16:13:55Z","isPatch":false,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Heya,\n\nOn Wed, May 20, 2009 at 17:47, Nicolas Pitre <nico@cam.org> wrote:\n> [...] _then_ it is worth adding a reflog entry for the otherwise about to\n> be lost state.\n\nOn that note, the usefulness of such a feature is dependant on the\nsupport we have for actually restoring an entry from this new reflog.\nThe current reflog is so amazingly useful because git has\nawesome-cherry-pick-and-the-like-commit-handling powers that make it\neasy to restore the otherwise lost state. But as far as I know,\nthere's no nice 'n easy support for restoring state to the information\ncontained in this new reflog, is there?\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"114356","messageId":"loom.20090520T165201-657@post.gmane.org","threadId":"19419","inReplyTo":"20090520141709.GO30527@spearce.org","subject":"Re: Reverting an uncommitted revert","fromName":"Eric Raible","fromEmail":"raible@gmail.com","sentAt":"2009-05-20T16:55:25Z","receivedAt":"2009-05-20T16:55:25Z","isPatch":false,"sender":{"key":"raible@gmail.com","avatar":null},"body":"Shawn O. Pearce <spearce <at> spearce.org> writes:\n\n> You did say \"uncommitted entry causes reflog append\", so in Peff's\n> original example of \"git add a; vi a; git add a\", we should be\n> creating a reflog entry for that first added state, which is clearly\n> not a disagreement.\n> \n> FWIW, I think this is a great idea, but lack the time to code it\n> myself, otherwise I probably would start hacking on it right now.\n> \n\nI for one am very happy to hear to say this.\n\nGit is generally quite safe, but still, there are holes for the unwary.\nI've fallen into those holes (embarrassingly, more than once), and so\nany movement towards filling them is quite welcome.\n"},{"id":"114357","messageId":"20090520165854.GA23031@coredump.intra.peff.net","threadId":"19419","inReplyTo":"fabb9a1e0905200913u5252973aia9a20983b20243d3@mail.gmail.com","subject":"Re: Reverting an uncommitted revert","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-05-20T16:58:54Z","receivedAt":"2009-05-20T16:58:54Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, May 20, 2009 at 06:13:55PM +0200, Sverre Rabbelier wrote:\n\n> On that note, the usefulness of such a feature is dependant on the\n> support we have for actually restoring an entry from this new reflog.\n> The current reflog is so amazingly useful because git has\n> awesome-cherry-pick-and-the-like-commit-handling powers that make it\n> easy to restore the otherwise lost state. But as far as I know,\n> there's no nice 'n easy support for restoring state to the information\n> contained in this new reflog, is there?\n\nI was envisioning a reflog of tree objects, so you could do:\n\n  $ git reflog show TRASH ;# show the reflog message\n  $ git show TRASH@{1} ;# show what's in the tree\n  $ git show TRASH@{1}:path/to/file ;# see a file\n  $ git checkout TRASH@{1} path/to/file ;# restore a file\n\nwhich should all work as-is.\n\nI suspect \"git log -g\" might need some tweaking to get a tree rather\nthan a commit (but in theory we should just show the \"Reflog *:\" headers\nand not the commit headers).\n\n-Peff\n"},{"id":"114358","messageId":"7vfxezzms0.fsf@alter.siamese.dyndns.org","threadId":"19419","inReplyTo":"20090520032139.GB10212@coredump.intra.peff.net","subject":"Re: Reverting an uncommitted revert","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-05-20T17:50:39Z","receivedAt":"2009-05-20T17:50:39Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> Related to this, I have wondered if it might be useful to have an \"index\n> reflog\". If I do something like this:\n>\n>   $ git add foo\n>   $ hack hack hack\n>   $ git add foo\n>\n> Then the first added state of \"foo\" is available in the object database,\n> but it is not connected to the name \"foo\" in any way, which makes it\n> much harder to find. If we had a reflog pointing to trees representing\n> the index state after each change, then it would be simple (you could\n> look at \"INDEX@{1}:foo\" or similar).\n>\n> I don't know if the performance is an issue. We are writing an extra\n> tree every time we touch the index, but in many cases you are already\n> writing a blob.\n\nIt is not just \"an extra tree every time\".  For example, in the kernel\nrepository, one of the path that is deepest [*1*] (i.e. whose modification\naffects the most number of trees) is:\n\n    arch/cris/include/arch-v32/mach-a3/mach/hwregs/iop/asm/iop_reg_space_asm.h\n\nIf you modify this file and then \"git add\", and if you write-tree the\nindex at that point, you need to write a tree object for \".\", arch/,\narch/cris, ..., arch/cris/include/arch-v32/mach-a3/mach/hwregs/iop/asm, 10\ntrees in total (if I am counting them right ;-).\n\nIf your cache-tree is fresh (and if you \"git write-tree\" every time you\n\"git add\", that will make it stay fresh), you do not have to recompute\nobject names of other 1728 tree objects (they are unchanged) [*2*], which\nshould help somewhat, but the majority of time is spent in the I/O (and\nperhaps slow fsync on ext3 ;-) of writing these 10 tree objects [*3*].\n\nPeople like Shawn who work with Java projects, where the tree hierarchy\ntends to be (unnecessary) deep with prefixes like org/spearce/jgit due to\nthe namespace issues will have bigger overhead than a relatively shallow\nproject like git.git itself.\n\n[Footnotes]\n\n*1* You can find it out yourself with...\n\ngit ls-files \"$(\n    git ls-files |\n    sed -e 's|[^/]||g' |\n    sort -u |\n    tail -n 1 |\n    sed -e 's|/|*/|g' -e 's/$/*/'\n)\" | head -n 1\n\n*2* The total number of tree objects in a commit is...\n\necho $(git ls-tree -r -d HEAD | wc -l) 1 + p | dc\n\n*3* write-tree with or without help from cache-tree in the kernel\nrepository with a hot cache (we are talking about running \"git write-tree\"\nevery time you do \"git add\" so the cold cache case does not matter) looks\nlike this:\n\n$ l=arch/cris/include/arch-v32/mach-a3/mach/hwregs/iop/asm/iop_reg_space_asm.h\n$ echo >>$l && git add $l\n$ /usr/bin/time git write-tree\n04bc92c40a5d0f0d44e162e140cb00964a52046b\n0.02user 0.01system 0:00.03elapsed 102%CPU (0avgtext+0avgdata 0maxresident)k\n0inputs+0outputs (0major+6387minor)pagefaults 0swaps\n\n$ git reset --hard\n\n$ echo >>$l && git add $l\n$ /usr/bin/time git write-tree --ignore-cache-tree\n04bc92c40a5d0f0d44e162e140cb00964a52046b\n0.13user 0.04system 0:00.17elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k\n0inputs+5336outputs (0major+17141minor)pagefaults 0swaps\n\n(The numbers are from my Athlon(tm) 64 X2 3800+ with slow IDE disks).\n"},{"id":"114359","messageId":"7vab57zmd8.fsf@alter.siamese.dyndns.org","threadId":"19419","inReplyTo":"20090520141709.GO30527@spearce.org","subject":"Re: Reverting an uncommitted revert","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-05-20T17:59:31Z","receivedAt":"2009-05-20T17:59:31Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Shawn O. Pearce\" <spearce@spearce.org> writes:\n\n> You did say \"uncommitted entry causes reflog append\", so in Peff's\n> original example of \"git add a; vi a; git add a\", we should be\n> creating a reflog entry for that first added state, which is clearly\n> not a disagreement.\n>\n> FWIW, I think this is a great idea, but lack the time to code it\n> myself, otherwise I probably would start hacking on it right now.\n\nThe devil's in the details.  There are at least four things you would need\nto design before start hacking.\n\n (0) Do you want this to apply only to Porcelains, or do you want to use\n     this for plumbing operations as well?\n\n (1) When would you \"auto\" write-tree?  When you do \"git add\" or anything\n     that adds new contents to the index?  Or immediately before you do\n     something destructive like \"git reset\"?  Or perhaps both?\n\n (2) Enumerate the operations that falls into the category you decided in\n     the above question.  For example, \"git apply --index\" and \"git apply\n     --cached\" would fall into the same category as \"git add\".  If you\n     cover plumbing, you would also need to cover \"git update-index\".\n\n (3) What should happen when you cannot write the index out as a tree?  I\n     think it is easier to make mistakes during a conflicted merge\n     resolution than during a straight linear development of your own, and\n     one of the cases that would benefit most would be that you have\n     resolved a path to your satisfaction but then later you screw up\n     while resolving some other paths, losing an earlier resolution.\n"},{"id":"114360","messageId":"alpine.LFD.2.00.0905201358000.3906@xanadu.home","threadId":"19419","inReplyTo":"20090520165854.GA23031@coredump.intra.peff.net","subject":"Re: Reverting an uncommitted revert","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2009-05-20T18:04:58Z","receivedAt":"2009-05-20T18:04:58Z","isPatch":false,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"On Wed, 20 May 2009, Jeff King wrote:\n\n> On Wed, May 20, 2009 at 06:13:55PM +0200, Sverre Rabbelier wrote:\n> \n> > On that note, the usefulness of such a feature is dependant on the\n> > support we have for actually restoring an entry from this new reflog.\n> > The current reflog is so amazingly useful because git has\n> > awesome-cherry-pick-and-the-like-commit-handling powers that make it\n> > easy to restore the otherwise lost state. But as far as I know,\n> > there's no nice 'n easy support for restoring state to the information\n> > contained in this new reflog, is there?\n> \n> I was envisioning a reflog of tree objects, so you could do:\n> \n>   $ git reflog show TRASH ;# show the reflog message\n>   $ git show TRASH@{1} ;# show what's in the tree\n>   $ git show TRASH@{1}:path/to/file ;# see a file\n>   $ git checkout TRASH@{1} path/to/file ;# restore a file\n> \n> which should all work as-is.\n> \n> I suspect \"git log -g\" might need some tweaking to get a tree rather\n> than a commit (but in theory we should just show the \"Reflog *:\" headers\n> and not the commit headers).\n\nEven simpler (for the user) would be to create a full commit with a \nsynthetic message.  The advantage is that the commit would have for \nparent the HEAD commit at the moment the operation leading to the reflog \nentry was made, with the date tag, etc.  The message could even contain \ninformation about what exactly was discarded by the operation.  This \ncommit would be referenced only by the trash reflog and not be part of \nany branch. And all your examples above should still work of course.\n\n\nNicolas\n"},{"id":"114361","messageId":"fabb9a1e0905201108q3939ebdm9bf60a344319daef@mail.gmail.com","threadId":"19419","inReplyTo":"alpine.LFD.2.00.0905201358000.3906@xanadu.home","subject":"Re: Reverting an uncommitted revert","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2009-05-20T18:08:19Z","receivedAt":"2009-05-20T18:08:19Z","isPatch":false,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Heya,\n\nOn Wed, May 20, 2009 at 20:04, Nicolas Pitre <nico@cam.org> wrote:\n> Even simpler (for the user) would be to create a full commit with a\n> synthetic message.\n\nFWIW, I really like this idea, and it would automagically put the\n'trash reflog' into the reach of git's\nawesome-cherry-pick-and-the-like-commit-handling powers.\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"114362","messageId":"alpine.LFD.2.00.0905201406280.3906@xanadu.home","threadId":"19419","inReplyTo":"7vab57zmd8.fsf@alter.siamese.dyndns.org","subject":"Re: Reverting an uncommitted revert","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2009-05-20T18:19:09Z","receivedAt":"2009-05-20T18:19:09Z","isPatch":false,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"On Wed, 20 May 2009, Junio C Hamano wrote:\n\n> \"Shawn O. Pearce\" <spearce@spearce.org> writes:\n> \n> > You did say \"uncommitted entry causes reflog append\", so in Peff's\n> > original example of \"git add a; vi a; git add a\", we should be\n> > creating a reflog entry for that first added state, which is clearly\n> > not a disagreement.\n> >\n> > FWIW, I think this is a great idea, but lack the time to code it\n> > myself, otherwise I probably would start hacking on it right now.\n> \n> The devil's in the details.  There are at least four things you would need\n> to design before start hacking.\n> \n>  (0) Do you want this to apply only to Porcelains, or do you want to use\n>      this for plumbing operations as well?\n\nDepends if current plumbing already takes care of reflogs for normal \noperations.\n\n>  (1) When would you \"auto\" write-tree?  When you do \"git add\" or anything\n>      that adds new contents to the index?  Or immediately before you do\n>      something destructive like \"git reset\"?  Or perhaps both?\n\nDelaying any IO until it is clear that something is to be discarded is \nthe best approach performance wise.  So perhaps not on the first 'git \nadd' but certainly on the second one with an already cached path for \nwhich new (different) content is going to replace previous content.\n\n>  (2) Enumerate the operations that falls into the category you decided in\n>      the above question.  For example, \"git apply --index\" and \"git apply\n>      --cached\" would fall into the same category as \"git add\".  If you\n>      cover plumbing, you would also need to cover \"git update-index\".\n\nPlumbing should probably have the mechanism to create those trash \nreflogs if desired, but not active by default.  Plumbing is normally for \nscripts, and once a script is debugged it shouldn't discard data by \nmistake.  Let's not forget that this is a feature for mistake inducing \nhumans.\n\n>  (3) What should happen when you cannot write the index out as a tree?  I\n>      think it is easier to make mistakes during a conflicted merge\n>      resolution than during a straight linear development of your own, and\n>      one of the cases that would benefit most would be that you have\n>      resolved a path to your satisfaction but then later you screw up\n>      while resolving some other paths, losing an earlier resolution.\n\nThis one is tricky.  Maybe storing two reflog entries corresponding to \nthe unresolved stages?\n\n\nNicolas\n"},{"id":"114363","messageId":"m3eiujk54b.fsf@localhost.localdomain","threadId":"19419","inReplyTo":"7vab57zmd8.fsf@alter.siamese.dyndns.org","subject":"Re: Reverting an uncommitted revert","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2009-05-20T18:21:19Z","receivedAt":"2009-05-20T18:21:19Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n> \"Shawn O. Pearce\" <spearce@spearce.org> writes:\n> \n> > You did say \"uncommitted entry causes reflog append\", so in Peff's\n> > original example of \"git add a; vi a; git add a\", we should be\n> > creating a reflog entry for that first added state, which is clearly\n> > not a disagreement.\n> >\n> > FWIW, I think this is a great idea, but lack the time to code it\n> > myself, otherwise I probably would start hacking on it right now.\n> \n> The devil's in the details.  There are at least four things you would need\n> to design before start hacking.\n> \n>  (0) Do you want this to apply only to Porcelains, or do you want to use\n>      this for plumbing operations as well?\n> \n>  (1) When would you \"auto\" write-tree?  When you do \"git add\" or anything\n>      that adds new contents to the index?  Or immediately before you do\n>      something destructive like \"git reset\"?  Or perhaps both?\n> \n>  (2) Enumerate the operations that falls into the category you decided in\n>      the above question.  For example, \"git apply --index\" and \"git apply\n>      --cached\" would fall into the same category as \"git add\".  If you\n>      cover plumbing, you would also need to cover \"git update-index\".\n\nI guess that if we will not be able to resolve performance issues,\nthen such safety net would be of necessity limited only to highly\ndestructive commands that are more likely to destroy huge amount of\nwork if handled not carefully.\n\n> \n>  (3) What should happen when you cannot write the index out as a tree?  I\n>      think it is easier to make mistakes during a conflicted merge\n>      resolution than during a straight linear development of your own, and\n>      one of the cases that would benefit most would be that you have\n>      resolved a path to your satisfaction but then later you screw up\n>      while resolving some other paths, losing an earlier resolution.\n\nHmmm... because of (3), and because of performance concerns,\nespecially for the deep hierarchy, perhaps it would be better to use\nhere backup copy of index, or to save disk space just a delta to index.\nOn the other hand this would introduce yet another mechanism...\n\nBTW., ad (3): how stash deals with conflicted merge? Hmmm... it doesn't\n\n $ git stash\n foo: needs merge\n foo: needs merge\n foo: unmerged (257cc5642cb1a054f08cc83f2d943e56fd3ebe99)\n foo: unmerged (b7d6715e2df11b9c32b2341423273c6b3ad9ae8a)\n foo: unmerged (5716ca5987cbf97d6bb54920bea6adde242d87e6)\n fatal: git-write-tree: error building trees\n Cannot save the current index state\n\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"114364","messageId":"alpine.LFD.2.00.0905201421440.3906@xanadu.home","threadId":"19419","inReplyTo":"alpine.LFD.2.00.0905201406280.3906@xanadu.home","subject":"Re: Reverting an uncommitted revert","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2009-05-20T18:25:20Z","receivedAt":"2009-05-20T18:25:20Z","isPatch":false,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"On Wed, 20 May 2009, Nicolas Pitre wrote:\n\n> On Wed, 20 May 2009, Junio C Hamano wrote:\n> \n> >  (3) What should happen when you cannot write the index out as a tree?  I\n> >      think it is easier to make mistakes during a conflicted merge\n> >      resolution than during a straight linear development of your own, and\n> >      one of the cases that would benefit most would be that you have\n> >      resolved a path to your satisfaction but then later you screw up\n> >      while resolving some other paths, losing an earlier resolution.\n> \n> This one is tricky.  Maybe storing two reflog entries corresponding to \n> the unresolved stages?\n\nBTW, what happens if one tries to 'git stash' an unresolved index?  So \nfar this trash reflog and git stash have quite in common, and stashing \nan unresolved index certainly has value too.\n\n\nNicolas\n"},{"id":"114365","messageId":"7vws8by6iz.fsf_-_@alter.siamese.dyndns.org","threadId":"19419","inReplyTo":"7vfxezzms0.fsf@alter.siamese.dyndns.org","subject":"[PATCH] write-tree --ignore-cache-tree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-05-20T18:27:00Z","receivedAt":"2009-05-20T18:27:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This allows you to discard the cache-tree information before writing the\ntree out of the index (i.e. it always recomputes the tree object names for\nall the subtrees).\n\nThis is only useful as a debug option, so I did not bother documenting it.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-write-tree.c |   12 +++++++++---\n cache-tree.c         |   10 +++++++---\n cache-tree.h         |    7 ++++++-\n 3 files changed, 22 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin-write-tree.c b/builtin-write-tree.c\nindex 9d64050..3a24ce8 100644\n--- a/builtin-write-tree.c\n+++ b/builtin-write-tree.c\n@@ -13,7 +13,7 @@ static const char write_tree_usage[] =\n \n int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)\n {\n-\tint missing_ok = 0, ret;\n+\tint flags = 0, ret;\n \tconst char *prefix = NULL;\n \tunsigned char sha1[20];\n \tconst char *me = \"git-write-tree\";\n@@ -22,9 +22,15 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)\n \twhile (1 < argc) {\n \t\tconst char *arg = argv[1];\n \t\tif (!strcmp(arg, \"--missing-ok\"))\n-\t\t\tmissing_ok = 1;\n+\t\t\tflags |= WRITE_TREE_MISSING_OK;\n \t\telse if (!prefixcmp(arg, \"--prefix=\"))\n \t\t\tprefix = arg + 9;\n+\t\telse if (!prefixcmp(arg, \"--ignore-cache-tree\"))\n+\t\t\t/*\n+\t\t\t * This is only useful for debugging, so I\n+\t\t\t * do not bother documenting it.\n+\t\t\t */\n+\t\t\tflags |= WRITE_TREE_IGNORE_CACHE_TREE;\n \t\telse\n \t\t\tusage(write_tree_usage);\n \t\targc--; argv++;\n@@ -33,7 +39,7 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)\n \tif (argc > 2)\n \t\tdie(\"too many options\");\n \n-\tret = write_cache_as_tree(sha1, missing_ok, prefix);\n+\tret = write_cache_as_tree(sha1, flags, prefix);\n \tswitch (ret) {\n \tcase 0:\n \t\tprintf(\"%s\\n\", sha1_to_hex(sha1));\ndiff --git a/cache-tree.c b/cache-tree.c\nindex 37bf35e..6dd8411 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -538,28 +538,32 @@ static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *pat\n \treturn it;\n }\n \n-int write_cache_as_tree(unsigned char *sha1, int missing_ok, const char *prefix)\n+int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)\n {\n \tint entries, was_valid, newfd;\n+\tstruct lock_file *lock_file;\n \n \t/*\n \t * We can't free this memory, it becomes part of a linked list\n \t * parsed atexit()\n \t */\n-\tstruct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));\n+\tlock_file = xcalloc(1, sizeof(struct lock_file));\n \n \tnewfd = hold_locked_index(lock_file, 1);\n \n \tentries = read_cache();\n \tif (entries < 0)\n \t\treturn WRITE_TREE_UNREADABLE_INDEX;\n+\tif (flags & WRITE_TREE_IGNORE_CACHE_TREE)\n+\t\tcache_tree_free(&(active_cache_tree));\n \n \tif (!active_cache_tree)\n \t\tactive_cache_tree = cache_tree();\n \n \twas_valid = cache_tree_fully_valid(active_cache_tree);\n-\n \tif (!was_valid) {\n+\t\tint missing_ok = flags & WRITE_TREE_MISSING_OK;\n+\n \t\tif (cache_tree_update(active_cache_tree,\n \t\t\t\t      active_cache, active_nr,\n \t\t\t\t      missing_ok, 0) < 0)\ndiff --git a/cache-tree.h b/cache-tree.h\nindex e958835..eadcad8 100644\n--- a/cache-tree.h\n+++ b/cache-tree.h\n@@ -30,11 +30,16 @@ struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);\n int cache_tree_fully_valid(struct cache_tree *);\n int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int, int);\n \n+/* bitmasks to write_cache_as_tree flags */\n+#define WRITE_TREE_MISSING_OK 1\n+#define WRITE_TREE_IGNORE_CACHE_TREE 2\n+\n+/* error return codes */\n #define WRITE_TREE_UNREADABLE_INDEX (-1)\n #define WRITE_TREE_UNMERGED_INDEX (-2)\n #define WRITE_TREE_PREFIX_ERROR (-3)\n \n-int write_cache_as_tree(unsigned char *sha1, int missing_ok, const char *prefix);\n+int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix);\n void prime_cache_tree(struct cache_tree **, struct tree *);\n \n #endif\n-- \n1.6.3.1.56.gd00e3.dirty\n"},{"id":"114367","messageId":"20090520185740.GP30527@spearce.org","threadId":"19419","inReplyTo":"alpine.LFD.2.00.0905201406280.3906@xanadu.home","subject":"Re: Reverting an uncommitted revert","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-05-20T18:57:40Z","receivedAt":"2009-05-20T18:57:40Z","isPatch":false,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Nicolas Pitre <nico@cam.org> wrote:\n> On Wed, 20 May 2009, Junio C Hamano wrote:\n> > \"Shawn O. Pearce\" <spearce@spearce.org> writes:\n> > \n> > > You did say \"uncommitted entry causes reflog append\", so in Peff's\n> > > original example of \"git add a; vi a; git add a\", we should be\n> > > creating a reflog entry for that first added state, which is clearly\n> > > not a disagreement.\n> > >\n> > > FWIW, I think this is a great idea, but lack the time to code it\n> > > myself, otherwise I probably would start hacking on it right now.\n> > \n> > The devil's in the details.  There are at least four things you would need\n> > to design before start hacking.\n> > \n> >  (0) Do you want this to apply only to Porcelains, or do you want to use\n> >      this for plumbing operations as well?\n> \n> Depends if current plumbing already takes care of reflogs for normal \n> operations.\n\ngit update-ref and git symbolic-ref, both plumbing, update the\nreflog automatically.\n\nWhy?  Because that was the best way to ensure we always caught the\nchange, so we didn't miss updates and have a missing but important\nstate.\n\nHowever, git rebase was moved from operating on its branch to work\non detached HEAD to avoid polluting the branch reflog with all of\nthe intermediate states.  So, here is a case where porcelain had to\nbe changed after the fact to work around the log-by-default nature\nof the plumbing.\n \n> >  (2) Enumerate the operations that falls into the category you decided in\n> >      the above question.  For example, \"git apply --index\" and \"git apply\n> >      --cached\" would fall into the same category as \"git add\".  If you\n> >      cover plumbing, you would also need to cover \"git update-index\".\n> \n> Plumbing should probably have the mechanism to create those trash \n> reflogs if desired, but not active by default.  Plumbing is normally for \n> scripts, and once a script is debugged it shouldn't discard data by \n> mistake.  Let's not forget that this is a feature for mistake inducing \n> humans.\n\ngit gui calls plumbing.  It uses git update-index directly.  IMHO,\ngit gui is porcelain, and wants this log, if its available.  But,\nby the same token, git filter-branch is porcelain, and probably\ndoes *not* want this logging on the index updates it makes.\n\nSo, if its off by default, we really need a way to allow git gui\nto force it on.\n\nBut... given that it is a safety measure, I would prefer to make it\non by default, and permit scripts to disable it, and update tools\nlike git filter-branch to disable it, because that fits with our\nother rules to try and err on the side of caution for the user's\ndata when given the choice between raw speed and some minor safety.\n(E.g. we fsync objects to disk for safety, not because its fast.)\n \n> >  (3) What should happen when you cannot write the index out as a tree?  I\n> >      think it is easier to make mistakes during a conflicted merge\n> >      resolution than during a straight linear development of your own, and\n> >      one of the cases that would benefit most would be that you have\n> >      resolved a path to your satisfaction but then later you screw up\n> >      while resolving some other paths, losing an earlier resolution.\n> \n> This one is tricky.  Maybe storing two reflog entries corresponding to \n> the unresolved stages?\n\nYea, and as was pointed out, git stash doesn't support this, but\nit probably should be able to do it, assuming the index could write\nout 3 (or maybe 4) trees in this case, one for each stage.\n\n-- \nShawn.\n"},{"id":"114378","messageId":"7vab57waok.fsf_-_@alter.siamese.dyndns.org","threadId":"19419","inReplyTo":"7vws8by6iz.fsf_-_@alter.siamese.dyndns.org","subject":"[PATCH 1/2] cache-tree.c::cache_tree_find(): simplify inernal API","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-05-21T00:40:11Z","receivedAt":"2009-05-21T00:40:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Earlier cache_tree_find() needs to be called with a valid cache_tree,\nbut repeated look-up may find an invalid or missing cache_tree in between.\nHelp simplify the callers by returning NULL to mean \"nothing appropriate\nfound\" when the input is NULL.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * This is a preliminary clean-up patch.  What comes next is much bigger.\n\n cache-tree.c |    2 ++\n 1 files changed, 2 insertions(+), 0 deletions(-)\n\ndiff --git a/cache-tree.c b/cache-tree.c\nindex 6dd8411..5481e43 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -514,6 +514,8 @@ struct cache_tree *cache_tree_read(const char *buffer, unsigned long size)\n \n static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *path)\n {\n+\tif (!it)\n+\t\treturn NULL;\n \twhile (*path) {\n \t\tconst char *slash;\n \t\tstruct cache_tree_sub *sub;\n-- \n1.6.3.1.56.gd00e3.dirty\n"},{"id":"114379","messageId":"7v4ovfwahj.fsf_-_@alter.siamese.dyndns.org","threadId":"19419","inReplyTo":"7vws8by6iz.fsf_-_@alter.siamese.dyndns.org","subject":"[PATCH 2/2] Optimize \"diff-index --cached\" using cache-tree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-05-21T00:44:24Z","receivedAt":"2009-05-21T00:44:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When running \"diff-index --cached\" after making a change to only a small\nportion of the index, there is no point unpacking unchanged subtrees into\nthe index recursively, only to find that all entries match anyway.  Tweak\nunpack_trees() logic that is used to read in the tree object to catch the\ncase where the tree entry we are looking at matches the index as a whole\nby looking at the cache-tree.\n\nAs an exercise, after modifying a few paths in the kernel tree, here are\na few numbers on my Athlon 64X2 3800+:\n\n    (without patch, hot cache)\n    $ /usr/bin/time git diff --cached --raw\n    :100644 100644 b57e1f5... e69de29... M  Makefile\n    :100644 000000 8c86b72... 0000000... D  arch/x86/Makefile\n    :000000 100644 0000000... e69de29... A  arche\n    0.07user 0.02system 0:00.09elapsed 102%CPU (0avgtext+0avgdata 0maxresident)k\n    0inputs+0outputs (0major+9407minor)pagefaults 0swaps\n\n    (with patch, hot cache)\n    $ /usr/bin/time ../git.git/git-diff --cached --raw\n    :100644 100644 b57e1f5... e69de29... M  Makefile\n    :100644 000000 8c86b72... 0000000... D  arch/x86/Makefile\n    :000000 100644 0000000... e69de29... A  arche\n    0.02user 0.00system 0:00.02elapsed 103%CPU (0avgtext+0avgdata 0maxresident)k\n    0inputs+0outputs (0major+2446minor)pagefaults 0swaps\n\nCold cache numbers are very impressive, but it does not matter very much\nin practice:\n\n    (without patch, cold cache)\n    $ su root sh -c 'echo 3 >/proc/sys/vm/drop_caches'\n    $ /usr/bin/time git diff --cached --raw\n    :100644 100644 b57e1f5... e69de29... M  Makefile\n    :100644 000000 8c86b72... 0000000... D  arch/x86/Makefile\n    :000000 100644 0000000... e69de29... A  arche\n    0.06user 0.17system 0:10.26elapsed 2%CPU (0avgtext+0avgdata 0maxresident)k\n    247032inputs+0outputs (1172major+8237minor)pagefaults 0swaps\n\n    (with patch, cold cache)\n    $ su root sh -c 'echo 3 >/proc/sys/vm/drop_caches'\n    $ /usr/bin/time ../git.git/git-diff --cached --raw\n    :100644 100644 b57e1f5... e69de29... M  Makefile\n    :100644 000000 8c86b72... 0000000... D  arch/x86/Makefile\n    :000000 100644 0000000... e69de29... A  arche\n    0.02user 0.01system 0:01.01elapsed 3%CPU (0avgtext+0avgdata 0maxresident)k\n    18440inputs+0outputs (79major+2369minor)pagefaults 0swaps\n\nThis of course helps \"git status\" as well.\n\n    (without patch, hot cache)\n    $ /usr/bin/time ../git.git/git-status >/dev/null\n    0.17user 0.18system 0:00.35elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k\n    0inputs+5336outputs (0major+10970minor)pagefaults 0swaps\n\n    (with patch, hot cache)\n    $ /usr/bin/time ../git.git/git-status >/dev/null\n    0.10user 0.16system 0:00.27elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k\n    0inputs+5336outputs (0major+3921minor)pagefaults 0swaps\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * This is what I've been wanting to do for a loooooooong time, ever since\n   I introduced the cache-tree extension to the index data structure.\n   Even though \"diff --cached HEAD\" is probably one of the least often\n   used form of the diff from end users' point of view (you would run at\n   most once before making an actual commit to give it a final review), it\n   is more often used than a casual observer realizes, because it is the\n   way Porcelains check what changes, if any, are staged.\n\n cache-tree.c   |   32 ++++++++++++++++++++++++++++++++\n cache-tree.h   |    3 +++\n diff-lib.c     |    2 ++\n unpack-trees.c |   17 +++++++++++++++++\n unpack-trees.h |    1 +\n 5 files changed, 55 insertions(+), 0 deletions(-)\n\ndiff --git a/cache-tree.c b/cache-tree.c\nindex 5481e43..16a65df 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -631,3 +631,35 @@ void prime_cache_tree(struct cache_tree **it, struct tree *tree)\n \t*it = cache_tree();\n \tprime_cache_tree_rec(*it, tree);\n }\n+\n+/*\n+ * find the cache_tree that corresponds to the current level without\n+ * exploding the full path into textual form.  The root of the\n+ * cache tree is given as \"root\", and our current level is \"info\".\n+ * (1) When at root level, info->prev is NULL, so it is \"root\" itself.\n+ * (2) Otherwise, find the cache_tree that corresponds to one level\n+ *     above us, and find ourselves in there.\n+ */\n+static struct cache_tree *find_cache_tree_from_traversal(struct cache_tree *root,\n+\t\t\t\t\t\t\t struct traverse_info *info)\n+{\n+\tstruct cache_tree *our_parent;\n+\n+\tif (!info->prev)\n+\t\treturn root;\n+\tour_parent = find_cache_tree_from_traversal(root, info->prev);\n+\treturn cache_tree_find(our_parent, info->name.path);\n+}\n+\n+int cache_tree_matches_traversal(struct cache_tree *root,\n+\t\t\t\t struct name_entry *ent,\n+\t\t\t\t struct traverse_info *info)\n+{\n+\tstruct cache_tree *it;\n+\n+\tit = find_cache_tree_from_traversal(root, info);\n+\tit = cache_tree_find(it, ent->path);\n+\tif (it && it->entry_count > 0 && !hashcmp(ent->sha1, it->sha1))\n+\t\treturn it->entry_count;\n+\treturn 0;\n+}\ndiff --git a/cache-tree.h b/cache-tree.h\nindex eadcad8..3df641f 100644\n--- a/cache-tree.h\n+++ b/cache-tree.h\n@@ -2,6 +2,7 @@\n #define CACHE_TREE_H\n \n #include \"tree.h\"\n+#include \"tree-walk.h\"\n \n struct cache_tree;\n struct cache_tree_sub {\n@@ -42,4 +43,6 @@ int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int, int)\n int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix);\n void prime_cache_tree(struct cache_tree **, struct tree *);\n \n+extern int cache_tree_matches_traversal(struct cache_tree *, struct name_entry *ent, struct traverse_info *info);\n+\n #endif\ndiff --git a/diff-lib.c b/diff-lib.c\nindex a310fb2..1cb97af 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -446,6 +446,7 @@ int run_diff_index(struct rev_info *revs, int cached)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.head_idx = 1;\n \topts.index_only = cached;\n+\topts.diff_index_cached = cached;\n \topts.merge = 1;\n \topts.fn = oneway_diff;\n \topts.unpack_data = revs;\n@@ -502,6 +503,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.head_idx = 1;\n \topts.index_only = 1;\n+\topts.diff_index_cached = 1;\n \topts.merge = 1;\n \topts.fn = oneway_diff;\n \topts.unpack_data = &revs;\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex aaacaf1..8eb3ddb 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -326,6 +326,23 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str\n \t\t\tif (src[0])\n \t\t\t\tconflicts |= 1;\n \t\t}\n+\n+\t\t/* special case: \"diff-index --cached\" looking at a tree */\n+\t\tif (o->diff_index_cached &&\n+\t\t    n == 1 && dirmask == 1 && S_ISDIR(names->mode)) {\n+\t\t\tint matches;\n+\t\t\tmatches = cache_tree_matches_traversal(o->src_index->cache_tree,\n+\t\t\t\t\t\t\t       names, info);\n+\t\t\t/*\n+\t\t\t * Everything under the name matches.  Adjust o->pos to\n+\t\t\t * skip the entire hierarchy.\n+\t\t\t */\n+\t\t\tif (matches) {\n+\t\t\t\to->pos += matches;\n+\t\t\t\treturn mask;\n+\t\t\t}\n+\t\t}\n+\n \t\tif (traverse_trees_recursive(n, dirmask, conflicts,\n \t\t\t\t\t     names, info) < 0)\n \t\t\treturn -1;\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex 0d26f3d..1e0e232 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -27,6 +27,7 @@ struct unpack_trees_options {\n \t\t     aggressive:1,\n \t\t     skip_unmerged:1,\n \t\t     initial_checkout:1,\n+\t\t     diff_index_cached:1,\n \t\t     gently:1;\n \tconst char *prefix;\n \tint pos;\n-- \n1.6.3.1.56.gd00e3.dirty\n"},{"id":"114386","messageId":"20090521034739.GA8091@sigill.intra.peff.net","threadId":"19419","inReplyTo":"alpine.LFD.2.00.0905201358000.3906@xanadu.home","subject":"Re: Reverting an uncommitted revert","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-05-21T03:47:40Z","receivedAt":"2009-05-21T03:47:40Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, May 20, 2009 at 02:04:58PM -0400, Nicolas Pitre wrote:\n\n> > I was envisioning a reflog of tree objects, so you could do:\n> [...]\n> Even simpler (for the user) would be to create a full commit with a \n> synthetic message.  The advantage is that the commit would have for \n> parent the HEAD commit at the moment the operation leading to the reflog \n> entry was made, with the date tag, etc.  The message could even contain \n\nI think you are right that this is better. I was trying to save the\ncreation of an extra commit object, but it probably is not that big a\ndeal (as Junio mentioned, we may already be creating multiple tree\nobjects). And having parent information is much richer for doing actual\nmerges of the changes introduced by that reflog commit, not just pulling\nout individual pieces of its state.\n\nSo forget my idea of storing trees; it should definitely be commits.\n\n-Peff\n"},{"id":"114393","messageId":"7vk54bc766.fsf@alter.siamese.dyndns.org","threadId":"19419","inReplyTo":"alpine.LFD.2.00.0905201406280.3906@xanadu.home","subject":"Re: Reverting an uncommitted revert","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-05-21T06:16:17Z","receivedAt":"2009-05-21T06:16:17Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nicolas Pitre <nico@cam.org> writes:\n\n>>  (1) When would you \"auto\" write-tree?  When you do \"git add\" or anything\n>>      that adds new contents to the index?  Or immediately before you do\n>>      something destructive like \"git reset\"?  Or perhaps both?\n>\n> Delaying any IO until it is clear that something is to be discarded is \n> the best approach performance wise.  So perhaps not on the first 'git \n> add' but certainly on the second one with an already cached path for \n> which new (different) content is going to replace previous content.\n\nI hope that you are not forgetting that it costs cycles to determine \"it\nis clear that something is to be discarded\".\n\nThe \"diff-index --cached\" optimization I did today may help, as it would\nallow you to check with the last trash-log more cheaply, but it certainly\nis not free.\n"}]}