{"thread":{"id":"47950","subject":"The case for two trees in a commit (\"How to make rebase less modal\")","startedAt":"2018-02-28T23:30:35Z","lastAt":"2018-03-01T19:05:27Z","messageCount":8,"participants":["Stefan Beller","Ramsay Jones","Brandon Williams","Jeff King","Jacob Keller","Junio C Hamano","Jonathan Nieder"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"340677","messageId":"CAGZ79ka6PXgs+JDicaQYWYSKgEthj0A-2bBaRcdp_0T2H+sREA@mail.gmail.com","threadId":"47950","inReplyTo":null,"subject":"The case for two trees in a commit (\"How to make rebase less modal\")","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-02-28T23:30:27Z","receivedAt":"2018-02-28T23:30:35Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"$ git hash-object --stdin -w -t commit <<EOF\ntree c70b4a33a0089f15eb3b38092832388d75293e86\nparent 105d5b91138ced892765a84e771a061ede8d63b8\nauthor Stefan Beller <sbeller@google.com> 1519859216 -0800\ncommitter Stefan Beller <sbeller@google.com> 1519859216 -0800\ntree 5495266479afc9a4bd9560e9feac465ed43fa63a\ntest commit\nEOF\n19abfc3bf1c5d782045acf23abdf7eed81e16669\n$ git fsck |grep 19abfc3bf1c5d782045acf23abdf7eed81e16669\n$\n\nSo it is technically possible to create a commit with two tree entries\nand fsck is not complaining.\n\nBut why would I want to do that?\n\nThere are multiple abstraction levels in Git, I think of them as follows:\n* data structures / object model\n* plumbing\n* porcelain commands to manipulate the repo \"at small scale\", e.g.\ncreate a commit/tag\n* porcelain to modify the repo \"at larger scale\", such as rebase,\ncherrypicking, reverting\n  involving more than 1 commit.\n\nThese large scale operations involving multiple commits however\nare all modal in its nature. Before doing anything else, you have to\nfinish or abort the rebase or you need expert knowledge how to\ngo otherwise.\n\nDuring the rebase there might be a hard to resolve conflict, which\nyou may not want to resolve right now, but defer to later.  Deferring a\nconflict is currently impossible, because precisely one tree is recorded.\n\nIf we had multiple trees possible in a commit, then all these large scale\noperations would stop being modal and you could just record the unresolved\nmerge conflict instead; to come back later and fix it up later.\n\nI'd be advocating for having multiple trees in a commit\npossible locally; it might be a bad idea to publish such trees.\n\nOpinions or other use cases?\n\nThanks,\nStefan\n"},{"id":"340679","messageId":"a2c44a68-9fc6-5998-6d84-aba73ea65a61@ramsayjones.plus.com","threadId":"47950","inReplyTo":"CAGZ79ka6PXgs+JDicaQYWYSKgEthj0A-2bBaRcdp_0T2H+sREA@mail.gmail.com","subject":"Re: The case for two trees in a commit (\"How to make rebase less modal\")","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2018-03-01T01:15:10Z","receivedAt":"2018-03-01T01:15:19Z","isPatch":false,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 28/02/18 23:30, Stefan Beller wrote:\n> $ git hash-object --stdin -w -t commit <<EOF\n> tree c70b4a33a0089f15eb3b38092832388d75293e86\n> parent 105d5b91138ced892765a84e771a061ede8d63b8\n> author Stefan Beller <sbeller@google.com> 1519859216 -0800\n> committer Stefan Beller <sbeller@google.com> 1519859216 -0800\n> tree 5495266479afc9a4bd9560e9feac465ed43fa63a\n> test commit\n> EOF\n> 19abfc3bf1c5d782045acf23abdf7eed81e16669\n> $ git fsck |grep 19abfc3bf1c5d782045acf23abdf7eed81e16669\n> $\n> \n> So it is technically possible to create a commit with two tree entries\n> and fsck is not complaining.\n\nHmm, it's a while since I looked at that code, but I don't think\nyou have a commit with two trees - the second 'tree <sha1>' line\nis just part of the commit message, isn't it?\n\nATB,\nRamsay Jones\n\n"},{"id":"340680","messageId":"20180301014256.GD87122@google.com","threadId":"47950","inReplyTo":"a2c44a68-9fc6-5998-6d84-aba73ea65a61@ramsayjones.plus.com","subject":"Re: The case for two trees in a commit (\"How to make rebase less modal\")","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-03-01T01:42:56Z","receivedAt":"2018-03-01T01:43:06Z","isPatch":false,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 03/01, Ramsay Jones wrote:\n>\n>\n> On 28/02/18 23:30, Stefan Beller wrote:\n> > $ git hash-object --stdin -w -t commit <<EOF\n> > tree c70b4a33a0089f15eb3b38092832388d75293e86\n> > parent 105d5b91138ced892765a84e771a061ede8d63b8\n> > author Stefan Beller <sbeller@google.com> 1519859216 -0800\n> > committer Stefan Beller <sbeller@google.com> 1519859216 -0800\n> > tree 5495266479afc9a4bd9560e9feac465ed43fa63a\n> > test commit\n> > EOF\n> > 19abfc3bf1c5d782045acf23abdf7eed81e16669\n> > $ git fsck |grep 19abfc3bf1c5d782045acf23abdf7eed81e16669\n> > $\n> >\n> > So it is technically possible to create a commit with two tree entries\n> > and fsck is not complaining.\n>\n> Hmm, it's a while since I looked at that code, but I don't think\n> you have a commit with two trees - the second 'tree <sha1>' line\n> is just part of the commit message, isn't it?\n>\n> ATB,\n> Ramsay Jones\n>\n\nActually it doesn't look like it.  The commit msg doesn't start till\nafter an empty newline so that commit has an empty commit msg.  Here's\none which you can see the msg when passed to show:\n\ngit hash-object --stdin -w -t commit <<EOF\ntree 76d269b57d3c4283922216f84a2850e99f561ccc\nparent fa0624f79f9d5765d09598b003124b3cf0b9acdb\nauthor Brandon Williams <bmwill@google.com> 1519859216 -0800\ncommitter Brandon Williams <bmwill@google.com> 1519859216 -0800\ntree 76d269b57d3c4283922216f84a2850e99f561ccc\n\nThis is a test commit with multiple trees\nEOF\n\nOf course the extra tree is ignored, but fsck doesn't complain and show\nhappily shows what it knows about.\n\n--\nBrandon Williams\n"},{"id":"340681","messageId":"20180301042620.GA21310@sigill.intra.peff.net","threadId":"47950","inReplyTo":"CAGZ79ka6PXgs+JDicaQYWYSKgEthj0A-2bBaRcdp_0T2H+sREA@mail.gmail.com","subject":"Re: The case for two trees in a commit (\"How to make rebase less modal\")","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-03-01T04:26:20Z","receivedAt":"2018-03-01T04:26:27Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Feb 28, 2018 at 03:30:27PM -0800, Stefan Beller wrote:\n\n> During the rebase there might be a hard to resolve conflict, which\n> you may not want to resolve right now, but defer to later.  Deferring a\n> conflict is currently impossible, because precisely one tree is recorded.\n> \n> If we had multiple trees possible in a commit, then all these large scale\n> operations would stop being modal and you could just record the unresolved\n> merge conflict instead; to come back later and fix it up later.\n> \n> I'd be advocating for having multiple trees in a commit\n> possible locally; it might be a bad idea to publish such trees.\n> \n> Opinions or other use cases?\n\nWhat benefit does it have over adding a new header \"unresolved-tree\" or\nsimilar? I do not think you are getting any backwards compatibility\nhere. For instance, \"prune\" will not traverse it with existing versions\nof git, nor \"pack-objects\" include it in a pack (I didn't actually test\nit, so I could be wrong; but those are all based around parse_commit,\nwhich should look at only the first tree).\n\n-Peff\n"},{"id":"340686","messageId":"CA+P7+xpB36t7WHpYOa+8wc7kLY7ES_bVEnSHPYHJ50gJ03uQBw@mail.gmail.com","threadId":"47950","inReplyTo":"CAGZ79ka6PXgs+JDicaQYWYSKgEthj0A-2bBaRcdp_0T2H+sREA@mail.gmail.com","subject":"Re: The case for two trees in a commit (\"How to make rebase less modal\")","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2018-03-01T07:25:07Z","receivedAt":"2018-03-01T07:25:35Z","isPatch":false,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Wed, Feb 28, 2018 at 3:30 PM, Stefan Beller <sbeller@google.com> wrote:\n> $ git hash-object --stdin -w -t commit <<EOF\n> tree c70b4a33a0089f15eb3b38092832388d75293e86\n> parent 105d5b91138ced892765a84e771a061ede8d63b8\n> author Stefan Beller <sbeller@google.com> 1519859216 -0800\n> committer Stefan Beller <sbeller@google.com> 1519859216 -0800\n> tree 5495266479afc9a4bd9560e9feac465ed43fa63a\n> test commit\n> EOF\n> 19abfc3bf1c5d782045acf23abdf7eed81e16669\n> $ git fsck |grep 19abfc3bf1c5d782045acf23abdf7eed81e16669\n> $\n>\n> So it is technically possible to create a commit with two tree entries\n> and fsck is not complaining.\n>\n> But why would I want to do that?\n>\n> There are multiple abstraction levels in Git, I think of them as follows:\n> * data structures / object model\n> * plumbing\n> * porcelain commands to manipulate the repo \"at small scale\", e.g.\n> create a commit/tag\n> * porcelain to modify the repo \"at larger scale\", such as rebase,\n> cherrypicking, reverting\n>   involving more than 1 commit.\n>\n> These large scale operations involving multiple commits however\n> are all modal in its nature. Before doing anything else, you have to\n> finish or abort the rebase or you need expert knowledge how to\n> go otherwise.\n>\n> During the rebase there might be a hard to resolve conflict, which\n> you may not want to resolve right now, but defer to later.  Deferring a\n> conflict is currently impossible, because precisely one tree is recorded.\n>\n\nHow does this let you defer a conflict? A future commit which modified\nblobs in that tree wouldn't know what version of the trees/blobs to\nactually use? Clearly future commits could record their own trees, but\nhow would they generate the \"correct\" tree?\n\nMaybe I am missing something here?\n\nThanks,\nJake\n\n> If we had multiple trees possible in a commit, then all these large scale\n> operations would stop being modal and you could just record the unresolved\n> merge conflict instead; to come back later and fix it up later.\n>\n> I'd be advocating for having multiple trees in a commit\n> possible locally; it might be a bad idea to publish such trees.\n>\n> Opinions or other use cases?\n>\n> Thanks,\n> Stefan\n"},{"id":"340746","messageId":"xmqqh8pzhbz3.fsf@gitster-ct.c.googlers.com","threadId":"47950","inReplyTo":"CAGZ79ka6PXgs+JDicaQYWYSKgEthj0A-2bBaRcdp_0T2H+sREA@mail.gmail.com","subject":"Re: The case for two trees in a commit (\"How to make rebase less modal\")","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-03-01T18:44:48Z","receivedAt":"2018-03-01T18:44:58Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Beller <sbeller@google.com> writes:\n\n> $ git hash-object --stdin -w -t commit <<EOF\n> tree c70b4a33a0089f15eb3b38092832388d75293e86\n> parent 105d5b91138ced892765a84e771a061ede8d63b8\n> author Stefan Beller <sbeller@google.com> 1519859216 -0800\n> committer Stefan Beller <sbeller@google.com> 1519859216 -0800\n> tree 5495266479afc9a4bd9560e9feac465ed43fa63a\n> test commit\n> EOF\n> 19abfc3bf1c5d782045acf23abdf7eed81e16669\n> $ git fsck |grep 19abfc3bf1c5d782045acf23abdf7eed81e16669\n> $\n>\n> So it is technically possible to create a commit with two tree entries\n> and fsck is not complaining.\n\nThe second one is merely a random unauthorized header that is not\ninterpreted in any way by Git.  It is merely being confusing by\nstarting with \"tree \" and having 40-hex after it, but the 40-hex\ndoes not get interpreted as an object name, and does not participate\nin reachability computation (i.e. packing, pruning and fsck).\n\nThere is not much difference between that and a line of trailer in\nthe commit log message (other than this one is less discoverable).\n"},{"id":"340747","messageId":"xmqqd10nhbq6.fsf@gitster-ct.c.googlers.com","threadId":"47950","inReplyTo":"CA+P7+xpB36t7WHpYOa+8wc7kLY7ES_bVEnSHPYHJ50gJ03uQBw@mail.gmail.com","subject":"Re: The case for two trees in a commit (\"How to make rebase less modal\")","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-03-01T18:50:09Z","receivedAt":"2018-03-01T18:50:29Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Keller <jacob.keller@gmail.com> writes:\n\n> How does this let you defer a conflict? A future commit which modified\n> blobs in that tree wouldn't know what version of the trees/blobs to\n> actually use? Clearly future commits could record their own trees, but\n> how would they generate the \"correct\" tree?\n>\n> Maybe I am missing something here?\n\nIf you write four trees out of each stage in the index and record\nthem, you could in theory have a new command that reads them and\nrecreate the conflicted index.  Oh, and then you would need the\nfifth tree that records what the working-tree files (with conflict\nmarkers) looked like, in order to reproduce the state seen by the\nperson who ran \"git merge\", attempted to resolve and gave up halfway\nin the middle.\n\nAs a local operation, extending \"git stash\" somehow so that it can\nstash even in a conflicted working tree may be a better approach,\nand it does not need cruft headers in commit objects, I would think.\n"},{"id":"340748","messageId":"20180301190507.GA165392@aiede.svl.corp.google.com","threadId":"47950","inReplyTo":"CAGZ79ka6PXgs+JDicaQYWYSKgEthj0A-2bBaRcdp_0T2H+sREA@mail.gmail.com","subject":"Re: The case for two trees in a commit (\"How to make rebase less modal\")","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2018-03-01T19:05:07Z","receivedAt":"2018-03-01T19:05:27Z","isPatch":false,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi,\n\nStefan Beller wrote:\n\n> $ git hash-object --stdin -w -t commit <<EOF\n> tree c70b4a33a0089f15eb3b38092832388d75293e86\n> parent 105d5b91138ced892765a84e771a061ede8d63b8\n> author Stefan Beller <sbeller@google.com> 1519859216 -0800\n> committer Stefan Beller <sbeller@google.com> 1519859216 -0800\n> tree 5495266479afc9a4bd9560e9feac465ed43fa63a\n> test commit\n> EOF\n> 19abfc3bf1c5d782045acf23abdf7eed81e16669\n> $ git fsck |grep 19abfc3bf1c5d782045acf23abdf7eed81e16669\n> $\n>\n> So it is technically possible to create a commit with two tree entries\n> and fsck is not complaining.\n\nAs others mentioned, this is essentially a fancy way to experiment\nwith adding a new header (with the same name as an existing header) to\na commit.  It is kind of a scary thing to do because anyone trying to\nparse commits, including old versions of git, is likely to get\nconfused by the multiple trees.  It doesn't affect the reachability\ncalculation in the way that it should so this ends up being something\nthat should be straightforward to do with a message in the commit body\ninstead.\n\nTo affect reachability, you could use multiple parent lines instead.\nYou'd need synthetic commits to hang the trees on.  This is similar to\nhow \"git stash\" stores the index state.\n\nIn other words, I think what you are trying to do is feasible, but not\nin the exact way you described.\n\n[...]\n> * porcelain to modify the repo \"at larger scale\", such as rebase,\n> cherrypicking, reverting\n>   involving more than 1 commit.\n>\n> These large scale operations involving multiple commits however\n> are all modal in its nature. Before doing anything else, you have to\n> finish or abort the rebase or you need expert knowledge how to\n> go otherwise.\n>\n> During the rebase there might be a hard to resolve conflict, which\n> you may not want to resolve right now, but defer to later.  Deferring a\n> conflict is currently impossible, because precisely one tree is recorded.\n\nJunio mentions you'd want to record:\n - stages of the index, to re-create a conflicted index\n - working tree files, with conflict markers\n\nIn addition you may also want to record:\n - state (todo list) from .git/rebase-merge, to allow picking up where\n   you left off in such a larger operation\n - similar state for other commands --- e.g. MERGE_MSG\n\nRecording this work-in-progress state is in the spirit of \"git stash\"\ndoes.  People also sometimes like to record their state in progress with\na \"wip commit\" at the tip of a branch.  Both of those workflows would\nbenefit from something like this, I'd think.\n\nSo I kind of like this.  Maybe a \"git save-wip\" command that is like\n\"git stash\" but records state to the current branch?  And similarly\nimproving \"git stash\" to record the same richer state.\n\nAnd in the spirit of \"git stash\" I think it is possible without\neven modifying the commit object format.\n\n[...]\n> I'd be advocating for having multiple trees in a commit\n> possible locally; it might be a bad idea to publish such trees.\n\nI think such \"WIP state\" may also be useful for publishing, to allow\ncollaborating on a thorny rebase or merge.\n\nThanks and hope that helps,\nJonathan\n"}]}