{"thread":{"id":"41146","subject":"Should notes handle replace commits?","startedAt":"2016-01-08T01:28:30Z","lastAt":"2016-01-11T16:50:24Z","messageCount":12,"participants":["Mike Hommey","Junio C Hamano","Philip Oakley"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"275520","messageId":"20160108012830.GA2110@glandium.org","threadId":"41146","inReplyTo":null,"subject":"Should notes handle replace commits?","fromName":"Mike Hommey","fromEmail":"mh@glandium.org","sentAt":"2016-01-08T01:28:30Z","receivedAt":"2016-01-08T01:28:30Z","isPatch":false,"sender":{"key":"mh@glandium.org","avatar":"https://avatars.githubusercontent.com/u/1038527?v=4"},"body":"Hi,\n\nTake the following dummy repo:\n\n  $ git init\n  $ git commit --allow-empty -m 'first'\n  $ git notes add -m 'first note'\n  $ git commit --allow-empty -m 'second'\n  $ git notes add -m 'second note'\n  $ git commit --allow-empty -m 'third'\n  $ git notes add -m 'third note'\n\n  $ git log --format='%s - %N'\n  third - third note\n\n  second - second note\n\n  first - first note\n\nNow, let's say you have some other history that you want to graft:\n\n  $ git checkout --orphan old\n  $ git commit --allow-empty -m 'real first'\n  $ git notes add -m 'real first note'\n  $ git commit --allow-empty -m 'real second'\n  $ git notes add -m 'real second note'\n  $ git commit --allow-empty -m 'real third'\n  $ git notes add -m 'real third note'\n  $ git log --format='%s - %N'\n  real third - real third note\n\n  real second - real second note\n\n  real first - real first note\n\nAssuming that the \"first\" commit on master is the same as the \"real\nthird\" on old, you can graft with:\n\n  $ git rev-parse master~ old | xargs > .git/info/grafts\n\nAnd then:\n  $ git log master --format='%s - %N'\n  third - third note\n\n  second - second note\n\n  real third - real third note\n\n  real second - real second note\n\n  real first - real first note\n\nNow, if you try to do the same with replace:\n\n  $ rm .git/info/grafts\n  $ git replace master~2 old\n  $ git log master --format='%s - %N'\n  third - third note\n\n  second - second note\n\n  real third - first note\n\n  real second - real second note\n\n  real first - real first note\n\nNote how \"real third\" now has \"first note\", instead of \"real third\nnote\".\n\nSo the question is, is this the behavior this should have?\n\nCheers,\n\nMike\n"},{"id":"275575","messageId":"xmqqh9in25py.fsf@gitster.mtv.corp.google.com","threadId":"41146","inReplyTo":"20160108012830.GA2110@glandium.org","subject":"Re: Should notes handle replace commits?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-01-08T20:09:45Z","receivedAt":"2016-01-08T20:09:45Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Mike Hommey <mh@glandium.org> writes:\n\n> Assuming that the \"first\" commit on master is the same as the \"real\n> third\" on old, you can graft with:\n>\n>   $ git rev-parse master~ old | xargs > .git/info/grafts\n\nWith \"graft\", you told Git that the parent of master~1 (\"second\") is\nnot master~2 (\"third\") but is old (\"real third\").\n\n> And then:\n>   $ git log master --format='%s - %N'\n>   third - third note\n>\n>   second - second note\n>\n>   real third - real third note\n\nSo it is expected that after \"second\" you see \"real third\", as you\nare seeing the \"real third\" with its notes.\n\n> Now, if you try to do the same with replace:\n>\n>   $ rm .git/info/grafts\n>   $ git replace master~2 old\n\nWith \"replace\", you told Git that the contents (i.e. message, tree,\netc.) of master~2 (\"first\") is not what it really is but is what\nappears in old (\"real third\").\n\n>   $ git log master --format='%s - %N'\n>   third - third note\n>\n>   second - second note\n>\n>   real third - first note\n\nHence Git tries to show master~2, i.e. \"first\", here; its contents\nis replaced with that of \"real third\", but the object name of the\ncommit shown after \"second\" is shown, as far as Git is concerned, is\nstill that of \"first\", so it is not surprising that the note that is\nassociated with it is shown here, as the whole point of \"notes\" is\nthat it is kept outside the contents recorded _in_ the commit.\n\n>   real second - real second note\n>\n>   real first - real first note\n>\n> Note how \"real third\" now has \"first note\", instead of \"real third\n> note\".\n\nSo it is not a correct observation that '\"real third\" now has \"first\nnote\"'.  You are still seeing \"first\", but its message, together\nwith its tree and its parents, are replaced by those of \"real\nthird\".\n\n> So the question is, is this the behavior this should have?\n\nThe behaviour is a natural consequence of what graft and replace are\nabout (i.e. \"telling Git what the parents of a commit are\" vs\n\"telling Git what the contents of a commit are\"), so the answer to\nthe \"should\" question is a resounding \"yes\".\n\nIt is a separate issue if the behaviour is useful for the purpose\nyou wanted to use \"replace\" for.\n"},{"id":"275579","messageId":"20160108214939.GA22801@glandium.org","threadId":"41146","inReplyTo":"xmqqh9in25py.fsf@gitster.mtv.corp.google.com","subject":"Re: Should notes handle replace commits?","fromName":"Mike Hommey","fromEmail":"mh@glandium.org","sentAt":"2016-01-08T21:49:39Z","receivedAt":"2016-01-08T21:49:39Z","isPatch":false,"sender":{"key":"mh@glandium.org","avatar":"https://avatars.githubusercontent.com/u/1038527?v=4"},"body":"On Fri, Jan 08, 2016 at 12:09:45PM -0800, Junio C Hamano wrote:\n> > So the question is, is this the behavior this should have?\n> \n> The behaviour is a natural consequence of what graft and replace are\n> about (i.e. \"telling Git what the parents of a commit are\" vs\n> \"telling Git what the contents of a commit are\"), so the answer to\n> the \"should\" question is a resounding \"yes\".\n\nIt's not only about contents, except for a very broad definition of\ncontents that includes ancestry. From my POV, replace is more about\n\"telling Git that this commit (and its parents) is really that one (and\nits parents)\".\n\nAnyways, the dummy example is using different commit messages, and notes\neverywhere, to make what happens to commits and notes obvious.  In the\nreal world, \"first\" would be \"real third\", so the commit messages would\nbe as expected in all cases. The question is whether users expect the\nnote of the non-replaced commit to show up. Would they expect no note at\nall in the case there is a note on the replacee, but not on the\nreplaced?  Maybe the right thing to do would be for *both* notes to be\nconcatenated? I don't know.\n\nMike\n"},{"id":"275583","messageId":"xmqqziwfzl2s.fsf@gitster.mtv.corp.google.com","threadId":"41146","inReplyTo":"20160108214939.GA22801@glandium.org","subject":"Re: Should notes handle replace commits?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-01-08T23:51:39Z","receivedAt":"2016-01-08T23:51:39Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Mike Hommey <mh@glandium.org> writes:\n\n> On Fri, Jan 08, 2016 at 12:09:45PM -0800, Junio C Hamano wrote:\n>> > So the question is, is this the behavior this should have?\n>> \n>> The behaviour is a natural consequence of what graft and replace are\n>> about (i.e. \"telling Git what the parents of a commit are\" vs\n>> \"telling Git what the contents of a commit are\"), so the answer to\n>> the \"should\" question is a resounding \"yes\".\n>\n> It's not only about contents, except for a very broad definition of\n> contents that includes ancestry.\n\nThat is not broad at all.  A Git commit knows about its parents in\nexactly the same way as it knows about its tree and its own log\nmessage.  Hashing all of them together, without considering which\npart is \"broad\", gives us the content-addressible filesystem, which\nis \"stupid content tracker\", aka Git.\n\nPerhaps you would see what is going on more clearly if you replace\nyour \"git log\" with \"git rev-list\".\n\nIf your pre-graft/pre-replace histories were\n\n\tA (first)  <--- B (second)  <--- C (third)\tmaster\n\tX (rFirst) <--- Y (rSecond) <--- Z (rThird)\told\n\nthen your \"graft\" tells Git \"B's parent is Z, not A.  If you run\n\"rev-list master\", it will give you \"C B Z Y X\".  The discrepancy\n(relative to the true history) brought in by \"grafting\" is that\nnowhere in \"cat-file commit B\" you would find Z, even though \"log\"\nand \"rev-list\" pretends as if Z is a (and the) parent of B.\n\nYour \"replace\" tells Git \"A records what Z records\".  If you run\n\"rev-list master\", it will give you \"C B A Y X\".\n\nA fake history made by \"replace\" does not have the same discrepancy\nas \"grafting\"; \"cat-file commit B\" names A as its parent, and asking\nwhat A is gives what actually is in Z, i.e. \"cat-file commit A\"\nshows what \"cat-file commit Z\" would give you.  The discrepancy with\nthe reality \"replacing\" gives you is that hashing what you got from\n\"cat-file commit A\" does not hash to A (it obviously has to hash to\nZ).\n\n> From my POV, replace is more about\n> \"telling Git that this commit (and its parents) is really that one (and\n> its parents)\".\n\nYour \"POV\" does not match reality; replace is about telling Git to\ngive contents recorded for object Z when anybody asks the contents\nrecorded for object A.\n"},{"id":"275584","messageId":"xmqqvb73zk35.fsf@gitster.mtv.corp.google.com","threadId":"41146","inReplyTo":"xmqqziwfzl2s.fsf@gitster.mtv.corp.google.com","subject":"Re: Should notes handle replace commits?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-01-09T00:13:02Z","receivedAt":"2016-01-09T00:13:02Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Perhaps you would see what is going on more clearly if you replace\n> your \"git log\" with \"git rev-list\".\n>\n> If your pre-graft/pre-replace histories were\n>\n> \tA (first)  <--- B (second)  <--- C (third)\tmaster\n> \tX (rFirst) <--- Y (rSecond) <--- Z (rThird)\told\n>\n> then your \"graft\" tells Git \"B's parent is Z, not A.  If you run\n> \"rev-list master\", it will give you \"C B Z Y X\".  The discrepancy\n> (relative to the true history) brought in by \"grafting\" is that\n> nowhere in \"cat-file commit B\" you would find Z, even though \"log\"\n> and \"rev-list\" pretends as if Z is a (and the) parent of B.\n>\n> Your \"replace\" tells Git \"A records what Z records\".  If you run\n> \"rev-list master\", it will give you \"C B A Y X\".\n>\n> A fake history made by \"replace\" does not have the same discrepancy\n> as \"grafting\"; \"cat-file commit B\" names A as its parent, and asking\n> what A is gives what actually is in Z, i.e. \"cat-file commit A\"\n> shows what \"cat-file commit Z\" would give you.  The discrepancy with\n> the reality \"replacing\" gives you is that hashing what you got from\n> \"cat-file commit A\" does not hash to A (it obviously has to hash to\n> Z).\n>\n>> From my POV, replace is more about\n>> \"telling Git that this commit (and its parents) is really that one (and\n>> its parents)\".\n>\n> Your \"POV\" does not match reality; replace is about telling Git to\n> give contents recorded for object Z when anybody asks the contents\n> recorded for object A.\n\nTo put it differently, what you did in your two examples with graft\nand replace are not equivalent.  With graft, you told commit B that\nits parent is not commit A but commit Z.  If you wanted to do the\nequivalent with replace, you would have replaced commit B with an\notherwise identical commit B' that records Z as its parent.  But you\ndidn't; instead, you replaced commit A with Z.\n\nAnd if you did the equivalent with \"replace\", your \"git rev-list\"\nwould have shown \"C B Z Y X\" (instead of \"C B A Y X\"), and when \"git\nlog\" showed the second commit, it would have shown the contents of B'\n_and_ because Git still thinks it is showing the original B, it\nwould have shown the notes for B.\n\nSomething like this (totally untested) would let you replace B with\nan otherwise identical B' that has Z instead of A as its parent:\n\n    $ Bprime=$(git cat-file commit master~ |\n             sed -e \"s/^parent .*/parent $(git rev-parse old)/\" |\n             git hash-object -w --stdin -t commit)\n\n    $ git update-ref refs/replace/$(git rev-parse master~) $Bprime\n"},{"id":"275585","messageId":"20160109002510.GA30050@glandium.org","threadId":"41146","inReplyTo":"xmqqziwfzl2s.fsf@gitster.mtv.corp.google.com","subject":"Re: Should notes handle replace commits?","fromName":"Mike Hommey","fromEmail":"mh@glandium.org","sentAt":"2016-01-09T00:25:10Z","receivedAt":"2016-01-09T00:25:10Z","isPatch":false,"sender":{"key":"mh@glandium.org","avatar":"https://avatars.githubusercontent.com/u/1038527?v=4"},"body":"On Fri, Jan 08, 2016 at 03:51:39PM -0800, Junio C Hamano wrote:\n> Mike Hommey <mh@glandium.org> writes:\n> \n> > On Fri, Jan 08, 2016 at 12:09:45PM -0800, Junio C Hamano wrote:\n> >> > So the question is, is this the behavior this should have?\n> >> \n> >> The behaviour is a natural consequence of what graft and replace are\n> >> about (i.e. \"telling Git what the parents of a commit are\" vs\n> >> \"telling Git what the contents of a commit are\"), so the answer to\n> >> the \"should\" question is a resounding \"yes\".\n> >\n> > It's not only about contents, except for a very broad definition of\n> > contents that includes ancestry.\n> \n> That is not broad at all.  A Git commit knows about its parents in\n> exactly the same way as it knows about its tree and its own log\n> message.  Hashing all of them together, without considering which\n> part is \"broad\", gives us the content-addressible filesystem, which\n> is \"stupid content tracker\", aka Git.\n> \n> Perhaps you would see what is going on more clearly if you replace\n> your \"git log\" with \"git rev-list\".\n> \n> If your pre-graft/pre-replace histories were\n> \n> \tA (first)  <--- B (second)  <--- C (third)\tmaster\n> \tX (rFirst) <--- Y (rSecond) <--- Z (rThird)\told\n> \n> then your \"graft\" tells Git \"B's parent is Z, not A.  If you run\n> \"rev-list master\", it will give you \"C B Z Y X\".  The discrepancy\n> (relative to the true history) brought in by \"grafting\" is that\n> nowhere in \"cat-file commit B\" you would find Z, even though \"log\"\n> and \"rev-list\" pretends as if Z is a (and the) parent of B.\n> \n> Your \"replace\" tells Git \"A records what Z records\".  If you run\n> \"rev-list master\", it will give you \"C B A Y X\".\n> \n> A fake history made by \"replace\" does not have the same discrepancy\n> as \"grafting\"; \"cat-file commit B\" names A as its parent, and asking\n> what A is gives what actually is in Z, i.e. \"cat-file commit A\"\n> shows what \"cat-file commit Z\" would give you.  The discrepancy with\n> the reality \"replacing\" gives you is that hashing what you got from\n> \"cat-file commit A\" does not hash to A (it obviously has to hash to\n> Z).\n> \n> > From my POV, replace is more about\n> > \"telling Git that this commit (and its parents) is really that one (and\n> > its parents)\".\n> \n> Your \"POV\" does not match reality; replace is about telling Git to\n> give contents recorded for object Z when anybody asks the contents\n> recorded for object A.\n\nIt's not that different to me, but my point is that (almost) everything\nabout A redirects to Z, as you point out, _except_ notes.\n\nSo while `cat-file commit A` gives you what `cat-file commit Z` would,\n`notes show A` doesn't give you what `notes show Z` would. And that's\nthis \"inconsistency\" that bothers me.\n\nMike\n"},{"id":"275586","messageId":"20160109003258.GA31415@glandium.org","threadId":"41146","inReplyTo":"xmqqvb73zk35.fsf@gitster.mtv.corp.google.com","subject":"Re: Should notes handle replace commits?","fromName":"Mike Hommey","fromEmail":"mh@glandium.org","sentAt":"2016-01-09T00:32:58Z","receivedAt":"2016-01-09T00:32:58Z","isPatch":false,"sender":{"key":"mh@glandium.org","avatar":"https://avatars.githubusercontent.com/u/1038527?v=4"},"body":"On Fri, Jan 08, 2016 at 04:13:02PM -0800, Junio C Hamano wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n> > Perhaps you would see what is going on more clearly if you replace\n> > your \"git log\" with \"git rev-list\".\n> >\n> > If your pre-graft/pre-replace histories were\n> >\n> > \tA (first)  <--- B (second)  <--- C (third)\tmaster\n> > \tX (rFirst) <--- Y (rSecond) <--- Z (rThird)\told\n> >\n> > then your \"graft\" tells Git \"B's parent is Z, not A.  If you run\n> > \"rev-list master\", it will give you \"C B Z Y X\".  The discrepancy\n> > (relative to the true history) brought in by \"grafting\" is that\n> > nowhere in \"cat-file commit B\" you would find Z, even though \"log\"\n> > and \"rev-list\" pretends as if Z is a (and the) parent of B.\n> >\n> > Your \"replace\" tells Git \"A records what Z records\".  If you run\n> > \"rev-list master\", it will give you \"C B A Y X\".\n> >\n> > A fake history made by \"replace\" does not have the same discrepancy\n> > as \"grafting\"; \"cat-file commit B\" names A as its parent, and asking\n> > what A is gives what actually is in Z, i.e. \"cat-file commit A\"\n> > shows what \"cat-file commit Z\" would give you.  The discrepancy with\n> > the reality \"replacing\" gives you is that hashing what you got from\n> > \"cat-file commit A\" does not hash to A (it obviously has to hash to\n> > Z).\n> >\n> >> From my POV, replace is more about\n> >> \"telling Git that this commit (and its parents) is really that one (and\n> >> its parents)\".\n> >\n> > Your \"POV\" does not match reality; replace is about telling Git to\n> > give contents recorded for object Z when anybody asks the contents\n> > recorded for object A.\n> \n> To put it differently, what you did in your two examples with graft\n> and replace are not equivalent.  With graft, you told commit B that\n> its parent is not commit A but commit Z.  If you wanted to do the\n> equivalent with replace, you would have replaced commit B with an\n> otherwise identical commit B' that records Z as its parent.  But you\n> didn't; instead, you replaced commit A with Z.\n> \n> And if you did the equivalent with \"replace\", your \"git rev-list\"\n> would have shown \"C B Z Y X\" (instead of \"C B A Y X\"), and when \"git\n> log\" showed the second commit, it would have shown the contents of B'\n> _and_ because Git still thinks it is showing the original B, it\n> would have shown the notes for B.\n> \n> Something like this (totally untested) would let you replace B with\n> an otherwise identical B' that has Z instead of A as its parent:\n> \n>     $ Bprime=$(git cat-file commit master~ |\n>              sed -e \"s/^parent .*/parent $(git rev-parse old)/\" |\n>              git hash-object -w --stdin -t commit)\n> \n>     $ git update-ref refs/replace/$(git rev-parse master~) $Bprime\n\ngit replace --graft does that automatically. But my contention is not\nreally about graft vs. replace. I should just have skipped that part,\nit's largely irrelevant.\n\nMike\n"},{"id":"275587","messageId":"xmqqmvsfzhq4.fsf@gitster.mtv.corp.google.com","threadId":"41146","inReplyTo":"20160109002510.GA30050@glandium.org","subject":"Re: Should notes handle replace commits?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-01-09T01:04:03Z","receivedAt":"2016-01-09T01:04:03Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Mike Hommey <mh@glandium.org> writes:\n\n> So while `cat-file commit A` gives you what `cat-file commit Z` would,\n> `notes show A` doesn't give you what `notes show Z` would. And that's\n> this \"inconsistency\" that bothers me.\n\nIn any case, 'notes' being a way to add extra information to an\nexisting object means that with your original \"replace\" that tells\nGit to keep A in the history (and give Z's contents when contents of\nA was asked), it is absolutely correct that notes for A is shown.\nIt would make no sense if notes for Z \"followed\", because as long as\nGit is concerned, you told Git to show A when your \"git log master\"\nfollowed the history down thru B to its parent, which you did not\nrewrite to be Z but kept to be A.  With the approach to replace B\nwith B' that has Z as its parent, when \"git log master\" follows the\nhistory down thru C to its parent, Git thinks it is showing B but\nreads B', and finds out its parent is Z and goes down to Z, and\nnotes for these two commits B and Z (not B' and Z) would be shown;\nthere is no need to \"follow\".\n\nThe true source of your confusion, I think, is that there is a\nmisunderstanding of what \"replace A with Z\" does.\n\nIt is not \"whenever somebody refers to A, pretend as if it is\nreferring to Z\".  If that _were_ the case, then I'd agree that\n\"whenever somebody else asks notes attached to A, pretend as if\nnotes attached to Z were asked\" might make sense, but that does not\nmatch the reality.\n\nIt is not graft vs replace.  It is about what you replace with what\nother thing: \"replace\" is a content replacement mechanism, not\nidentity replacement mechanism.\n"},{"id":"275589","messageId":"20160109012542.GA2445@glandium.org","threadId":"41146","inReplyTo":"xmqqmvsfzhq4.fsf@gitster.mtv.corp.google.com","subject":"Re: Should notes handle replace commits?","fromName":"Mike Hommey","fromEmail":"mh@glandium.org","sentAt":"2016-01-09T01:25:42Z","receivedAt":"2016-01-09T01:25:42Z","isPatch":false,"sender":{"key":"mh@glandium.org","avatar":"https://avatars.githubusercontent.com/u/1038527?v=4"},"body":"On Fri, Jan 08, 2016 at 05:04:03PM -0800, Junio C Hamano wrote:\n> It is not graft vs replace.  It is about what you replace with what\n> other thing: \"replace\" is a content replacement mechanism, not\n> identity replacement mechanism.\n\nI guess this is where opinions diverge: in what notes are.\nI'd argue notes are content just as much as the tree, except they're not\nstored inside the commit.\n\nMike\n"},{"id":"275597","messageId":"181337AE5CFC4AF09B82187B8E97095D@PhilipOakley","threadId":"41146","inReplyTo":"xmqqmvsfzhq4.fsf@gitster.mtv.corp.google.com","subject":"Re: Should notes handle replace commits?","fromName":"Philip Oakley","fromEmail":"philipoakley@iee.org","sentAt":null,"receivedAt":"2016-01-09T17:21:03Z","isPatch":false,"sender":{"key":"philipoakley@iee.email","avatar":"https://avatars.githubusercontent.com/u/914343?v=4"},"body":"\nFrom: \"Junio C Hamano\" <gitster@pobox.com>\nSent: Saturday, January 09, 2016 1:04 AM\n> Mike Hommey <mh@glandium.org> writes:\n>\n>> So while `cat-file commit A` gives you what `cat-file commit Z` would,\n>> `notes show A` doesn't give you what `notes show Z` would. And that's\n>> this \"inconsistency\" that bothers me.\n>\n> In any case, 'notes' being a way to add extra information to an\n> existing object means that with your original \"replace\" that tells\n> Git to keep A in the history (and give Z's contents when contents of\n> A was asked),\n>     it is absolutely correct that notes for A is shown.\n\nThis seems very wrong to me. Surely we have asked that _all_ references to A \nbe replaced by a reference to Z and thence onward to Z's contents.\n\nThat is, unless 'git --no-replace-objects' was given as an option.\n\nIt is as if everything about the old 'A' has been deleted, and new content \n'Z' replaces it.\n\nIn this case surely we want the notes about the contents of the new Z. To \nmix up the old and new will cause confusion, as evidenced by Mike's post.\n\n> It would make no sense if notes for Z \"followed\", because as long as\n> Git is concerned, you told Git to show A when your \"git log master\"\n> followed the history down thru B to its parent, which you did not\n> rewrite to be Z but kept to be A.  With the approach to replace B\n> with B' that has Z as its parent, when \"git log master\" follows the\n> history down thru C to its parent, Git thinks it is showing B but\n> reads B', and finds out its parent is Z and goes down to Z, and\n> notes for these two commits B and Z (not B' and Z) would be shown;\n> there is no need to \"follow\".\n>\n> The true source of your confusion, I think, is that there is a\n> misunderstanding of what \"replace A with Z\" does.\n>\n> It is not \"whenever somebody refers to A, pretend as if it is\n> referring to Z\".  If that _were_ the case, then I'd agree that\n> \"whenever somebody else asks notes attached to A, pretend as if\n> notes attached to Z were asked\" might make sense, but that does not\n> match the reality.\n\nI think Mike is pointing out that it is only for notes that this alternate \nreality unexpectedly happens.\n\n>\n> It is not graft vs replace.  It is about what you replace with what\n> other thing: \"replace\" is a content replacement mechanism, not\n> identity replacement mechanism.\n\nPart of the graft vs replace difficulty (historically) was that a graft \n(easy to use) will keep all the commits, and add a traversal link, while \nreplace would essentially 'delete' one of the commits (users did not have a \nspare commit to delete if linking histories). The newer --graft option is \nnow the appropriate method of creating an extra commit (that has the extra \ntraverse linkage) that can then be 'deleted' and replaced by the commit on \nthe other traverse chain.\n\nThere is the case where we have a string of commits (A-B-C-D-E) and we \nsimply want to swap out commit C and replace it with a new commit C' to \ncreate (A-B-C'-D-E). A new C' would be prepared, and then upon replacement \nof C by C' we have to ask \"What should be done about the old notes that were \nattached to C?\".\n\nI think that it maybe here that the issue lies. Some will think that the \nnotes of the old C should still be attached to that location in the string, \nwhile others may think that it's for the user to both notice, and check the \nveracity, and only then carry those old notes across to the new C' \n(manually).\n\n--\nPhilip\n\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n> \n"},{"id":"275654","messageId":"xmqq60z0yu60.fsf@gitster.mtv.corp.google.com","threadId":"41146","inReplyTo":"20160109012542.GA2445@glandium.org","subject":"Re: Should notes handle replace commits?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-01-11T16:09:43Z","receivedAt":"2016-01-11T16:09:43Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Mike Hommey <mh@glandium.org> writes:\n\n> On Fri, Jan 08, 2016 at 05:04:03PM -0800, Junio C Hamano wrote:\n>> It is not graft vs replace.  It is about what you replace with what\n>> other thing: \"replace\" is a content replacement mechanism, not\n>> identity replacement mechanism.\n>\n> I guess this is where opinions diverge: in what notes are.\n> I'd argue notes are content just as much as the tree, except they're not\n> stored inside the commit.\n\nThere is no room for any divergence in opinion around what\n\"contents\" are in the context of Git, though.\n\nIff the name of the object would change if you change something in\nit, then that something is \"contents\" of the object.  So the list of\nparents of a commit is part of the contents of the commit object.\n\nNotes are not.\n\nIn fact, that is the primary reason notes exist---the whole point of\nnotes is that with them you can add pieces of information ABOUT an\nexisting object without changing the contents of the object.\n"},{"id":"275659","messageId":"xmqqy4bwxdpr.fsf@gitster.mtv.corp.google.com","threadId":"41146","inReplyTo":"181337AE5CFC4AF09B82187B8E97095D@PhilipOakley","subject":"Re: Should notes handle replace commits?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-01-11T16:50:24Z","receivedAt":"2016-01-11T16:50:24Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Philip Oakley\" <philipoakley@iee.org> writes:\n\n> From: \"Junio C Hamano\" <gitster@pobox.com>\n> Sent: Saturday, January 09, 2016 1:04 AM\n>> Mike Hommey <mh@glandium.org> writes:\n>>\n>>> So while `cat-file commit A` gives you what `cat-file commit Z` would,\n>>> `notes show A` doesn't give you what `notes show Z` would. And that's\n>>> this \"inconsistency\" that bothers me.\n>>\n>> In any case, 'notes' being a way to add extra information to an\n>> existing object means that with your original \"replace\" that tells\n>> Git to keep A in the history (and give Z's contents when contents of\n>> A was asked),\n>>     it is absolutely correct that notes for A is shown.\n>\n> This seems very wrong to me. Surely we have asked that _all_\n> references to A be replaced by a reference to Z and thence onward to\n> Z's contents.\n\nYou didn't ask any such thing.\n\nI already said:\n\n    The true source of your confusion, I think, is that there is a\n    misunderstanding of what \"replace A with Z\" does.\n\n    It is NOT \"whenever somebody refers to A, pretend as if it is\n    referring to Z\".  If that _were_ the case, then I'd agree that\n    \"whenever somebody else asks notes attached to A, pretend as if\n    notes attached to Z were asked\" might make sense, but that does not\n    match the reality.\n\nbut then didn't say what \"the reality\" is (because I already said it\nelsewhere upthread).\n\nWhat you asked is that whenever information in A is asked, a tweaked\nversion of the original information in A is returned.  The tweaked\nversion happens to match what is in Z, but after the replacement,\nthe object Z does not directly get into the picture---as you can see\nin the result of log (or rev-list) from C that shows \"C B A Y X\" (in\ntip-to-root order), the end user does not even have to be aware of\nZ's existence.\n\nLet's think about the example of grafting \"old\" history that starts\nat X, resurrected from archive since the history of the current\nproject has already started at A and leading to C in real-world\nterms.  A would be like Linus's v2.6.12-rc2 in the Linux kernel\nproject that does not have any parent.  Then you rebuild the \"old\"\nhistory from the archive that ends at the same v2.6.12-rc2, so your\nX Y Z history also ends with v2.6.12-rc2.  And you would \"graft\" it\nbehind the true history.\n\nCommits A and Z would both have the same log message, record the\nsame tree, made by the same author and committer with same\ntimestamps.\n\nNow, think which one of A and Z do you think people would want to\n\"survive\" in the graft operation?  Everybody has built (and will\ncontinue to build) on the history that started at A whose tip is\ncurrently at C.\n\nIf you have a way to tell Git \"Earlier A was recorded as having NO\nparent, but I wish we had recorded Y as its parent.  So create a\nfake reality where A's parent is Y.\", then you can keep the\nresulting world view consistent with the \"recent-only\" real history,\ni.e. B's parent is still A.  Some might even have notes on A, or\ncommits made since A may reference the commit object A by name in\nits log, or another superproject may bind A as its subproject, but\nbecause we are not changing the identity/name of A with Z, all of\nthese \"external\" references survive the grafting/replacing.\n\nPerhaps it would help if you stop thinking that you are somehow\nlosing \"Z\" and \"Z\" is somehow precious.  It is not, at least in the\n\"history grafting\" context, at all precious.\n\nWhen you have the true history starting from v2.6.12-rc2 (that is \"A\nB C\"), the thing you REALLY want to do is to resurrect history that\nends at one commit before v2.6.12-rc2 (that is \"Y\" in the example),\nand somehow tweak \"A\" so that its parent is \"Y\".  And the way you\ntweak \"A\" to make that happen is to replace its contents with the\ncontents of a commit that records all the same information as \"A\"\nexcept that it has a parent that is \"Y\".  We could do \"cat-file\ncommit A\" piped to sed to add a \"parent Y\" line to it and then\n\"hash-object\" to come up with such a replacement commit, and replace\nA with it, but \"Z\" happens to be such a commit already, so that is\nwhy replacing A with Z would graft Y just behind A.  The resulting\nhistory would show Y as the parent of A and Z won't even be in the\nconsciousness of the end users.  The \"old history resurrection\"\nproject does not even have to create \"Z\" in the first place.\n\nIf the contents of \"Z\" is different from contents of \"A\" in some\nother way, other than their set of parents, or if \"Z\" has other\nexternal references that are precious (e.g. a note may refer to it,\nor a superproject may bind Z to its tree), then either replacing A\nwith Z or replacing Z with A would not be a good way to connect the\ntwo histories.  As you would want to keep both, and the \"external\"\nreferences, such as notes and submodule gitlink bindings, want to\nkeep refering to both.\n\nIn such a case, the best you can do is to tweak A to have Z (instead\nof Y) as its fake parent, to make the resulting fake history read\nlike \"C B A Z Y X\" (in tip-to-root order), having two copies of\nv2.6.12-rc2.  That way you would be able to see both notes for A and\nZ, and can tell which note was attached to the true history and\nwhich note was attached to the history resurrected from the archive.\n"}]}