{"thread":{"id":"8057","subject":"[RFC] Second parent for reverts","startedAt":"2007-05-09T19:20:05Z","lastAt":"2007-05-29T11:12:03Z","messageCount":53,"participants":["Daniel Barkalow","Johannes Schindelin","Shawn O. Pearce","Junio C Hamano","Linus Torvalds","Johan Herland","Martin Langhoff","Jakub Narebski","Michael S. Tsirkin","Jeff King","Andy Parkins"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"41645","messageId":"Pine.LNX.4.64.0705091406350.18541@iabervon.org","threadId":"8057","inReplyTo":null,"subject":"[RFC] Second parent for reverts","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2007-05-09T19:20:05Z","receivedAt":"2007-05-09T19:20:05Z","isPatch":false,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"The discussion about having a header to specify, for a revert commit, what \nit reverts made me realize that this header *would* be useful, but that we \ndon't need a *new* header for it. I think that the right method is to add \nthe parent of the reverted commit as a second parent for the revert.\n\nIf you have:\n\na -> b -> c -> d\n\nAnd you want to revert b, the most exact flow would be:\n\na -> b -> c -> d -> e\n       \\         /\n        -> a' ---\n\nI.e., you exactly remove the effects of b to generate a commit that has \nthe same tree as a, and then you merge.\n\nBut a' doesn't actually take anything from b, since it's reverting all of \nb (unless it's only reverting part of b), and, if b isn't there, it \ndoesn't need a commit message, either, so it's not different from a. So \nthe flow should be:\n\na -> b -> c -> d -> e\n  \\              /\n   --------------\n\nAnd this means blame work correctly: lines that b changed will be blamed \non a (or an ancestor), because e will match a there and be different from \nd. So I think git-revert should simply add in the reverted patch's parent. \nDoes this analysis make sense to other people?\n\n\t-Daniel\n*This .sig left intentionally blank*\n"},{"id":"41651","messageId":"Pine.LNX.4.64.0705092206540.4167@racer.site","threadId":"8057","inReplyTo":"Pine.LNX.4.64.0705091406350.18541@iabervon.org","subject":"Re: [RFC] Second parent for reverts","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-05-09T20:07:59Z","receivedAt":"2007-05-09T20:07:59Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 9 May 2007, Daniel Barkalow wrote:\n\n> The discussion about having a header to specify, for a revert commit, \n> what it reverts made me realize that this header *would* be useful, but \n> that we don't need a *new* header for it. I think that the right method \n> is to add the parent of the reverted commit as a second parent for the \n> revert.\n\nI am not so sure. In a sense, you are correct. But everybody who does \"git \nlog --no-merges\" would no longer see reverts. Which is somewhat incorrect.\n\nCiao,\nDscho\n"},{"id":"41657","messageId":"20070509202224.GG3141@spearce.org","threadId":"8057","inReplyTo":"Pine.LNX.4.64.0705092206540.4167@racer.site","subject":"Re: [RFC] Second parent for reverts","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-05-09T20:22:24Z","receivedAt":"2007-05-09T20:22:24Z","isPatch":false,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> On Wed, 9 May 2007, Daniel Barkalow wrote:\n> \n> > The discussion about having a header to specify, for a revert commit, \n> > what it reverts made me realize that this header *would* be useful, but \n> > that we don't need a *new* header for it. I think that the right method \n> > is to add the parent of the reverted commit as a second parent for the \n> > revert.\n> \n> I am not so sure. In a sense, you are correct. But everybody who does \"git \n> log --no-merges\" would no longer see reverts. Which is somewhat incorrect.\n\nRight.\n\nI've actually done what Daniel just talked about doing in one of my\n\"production\" repositories.  I did it by hand as a developer had\ncreated a bad merge and accidentially reverted 800 files during\nthat merge.  80 or so commits later along a public non-rewinding\nbranch coworkers realized things weren't right, and asked me\nto fix the mess.  As I wanted to save the blame data when I\nreverted-the-revert I did what Daniel suggests.\n\nBut since the revert-the-revert wasn't really an interesting point\nin history, and neither was the bad merge, I don't really care that\nneither shows up with --no-merges.  The original bad merge was a\nsimple honest mistake made by a developer who was new to Git, and\nwas only caused because merge-recursive wasn't installed properly\non that system.\n\n\nAs Dscho says, most reverts are interesting points in time.  *Why*\na particular revert was done is important.\n\nAnd so I have to disagree quite a bit with Daniel's idea, for exactly\nthat reason.  If I'm looking at a block of code in a file I want to\nknow why its there.  If blame tells me its a revert of something,\nthat tells me we tried another path and it didn't work out.  I might\nbe sitting here looking at this line because I'm thinking of redoing\nwhatever it was that wasn't good!\n\nSo that revert commit message better say why that thing didn't\nwork out.\n\nIf I really do care about the source of that line, I can always\nre-run blame on the parent of the reverted commit (hence why ^\nis so nice as a suffix on a commit-ish!) and examine the line again.\n\nHmm.  I should teach git-gui to parse out the revert message and\nlet you click into its parent.  Simple enough.  Maybe it will be\nin 0.7.0.  Maybe it won't be.  ;-)\n\n-- \nShawn.\n"},{"id":"41671","messageId":"7v7irhslx1.fsf@assigned-by-dhcp.cox.net","threadId":"8057","inReplyTo":"Pine.LNX.4.64.0705091406350.18541@iabervon.org","subject":"Re: [RFC] Second parent for reverts","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-05-09T21:54:18Z","receivedAt":"2007-05-09T21:54:18Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Daniel Barkalow <barkalow@iabervon.org> writes:\n\n> The discussion about having a header to specify, for a revert commit, what \n> it reverts made me realize that this header *would* be useful, but that we \n> don't need a *new* header for it. I think that the right method is to add \n> the parent of the reverted commit as a second parent for the revert.\n>\n> If you have:\n>\n> a -> b -> c -> d\n>\n> And you want to revert b, the most exact flow would be:\n>\n> a -> b -> c -> d -> e\n>        \\         /\n>         -> a' ---\n>\n> I.e., you exactly remove the effects of b to generate a commit that has \n> the same tree as a, and then you merge.\n>\n> But a' doesn't actually take anything from b, since it's reverting all of \n> b (unless it's only reverting part of b), and, if b isn't there, it \n> doesn't need a commit message, either, so it's not different from a. So \n> the flow should be:\n>\n> a -> b -> c -> d -> e\n>   \\              /\n>    --------------\n>\n> And this means blame work correctly: lines that b changed will be blamed \n> on a (or an ancestor), because e will match a there and be different from \n> d. So I think git-revert should simply add in the reverted patch's parent. \n> Does this analysis make sense to other people?\n\nThe revert operation at the tree level (not commit level) treats\nAS IF b is a common ancestor between a and d and computes a\nmerge between a and d using that fake common ancestor to reach\nat e.  So it is understandable that you are confused that the\nresult somehow has something to do with a merge between a and d.\n\nBut other than that, the \"analysis\" does not make any sense to\nme.\n\nWhen you have a merge commit somebody else made, you should be\nable to reproduce it yourself, with the help from the intent of\nthe merge explained in the commit log of the merge commit\n(usually it says \"I am merging this development line with that\none\").  With a->b->c->d history, what is the sane merge result\nbetween a and d?  It won't be e.  The only clue that you did a\nrevert of b is contained in the message \"Revert b\", because b is\nnot a common ancestor between a and d.\n\nAn interesting tangent is that you can revert both b and c with\na single tree-level operation by pretending as if c is a common\nancestor between a and d and run tree-level 3-way merge to apply\ndifference between c and d on top of a to come up with e.  It\nwould not make sense to record neither one of a b c as the fake\nsecond parent.\n\nI would suggest to leave a revert as a revert.  It is not a\nmerge.\n\nIn general, you should think of an act of making a commit (not\nlimited to a merge but a single parent) to mean that you are\nmaking this statement:\n\n\tI have considered the development history that leads to\n\tthe parent commits this commit records, and the tree\n\trecorded with this commit suits the purpose of my branch\n\tbetter than all of them.\n\nObviously, if you considered the history that leads to 'd', you\nhave considered the hsitory that leads to 'a' as well, so\nrecording both 'a' and 'd' as parents does not make much sense.\nYou could have recorded 'e' as an Octopus of 'a', 'b', ... 'd',\nand the statement does not change.\n"},{"id":"41674","messageId":"alpine.LFD.0.98.0705091513050.4062@woody.linux-foundation.org","threadId":"8057","inReplyTo":"7v7irhslx1.fsf@assigned-by-dhcp.cox.net","subject":"Re: [RFC] Second parent for reverts","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-05-09T22:16:36Z","receivedAt":"2007-05-09T22:16:36Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 9 May 2007, Junio C Hamano wrote:\n> \n> I would suggest to leave a revert as a revert.  It is not a\n> merge.\n\nYes, please.\n\nPeople can do fancy things if they want, but quite frankly, if you start \nplaying games with the \"parent\" pointer, it starts losing its meaning, and \nbecomes a random \"pointer to another related commit\". \n\nThen, you might as well say \"oh, this commit is related to that other \ncommit\", and decide to call the other commit \"related\" too, and now a \nrandom commit just looks like a strange merge.\n\nThat's simply not what \"parenthood\" is about. Parenthood is about \nnonlinear development, and parents should not be reachable from each other \n(which such a bogus revert or \"related\" parent would almost always be: it \nwould be reachable from the *real* parent.\n\nIf you want a \"related to that commit\" field, it should be a separate \nfield in the commit object. But since it doesn't really have any real \n*semantic* meaning to git itself, it shouldn't be in the header. We \ncould, for example, make it be in the free-form section, and teach our \ngraphical visualization tools to automatically turn it into a hyperlink.\n\n.. which we already do.\n\n\t\tLinus\n"},{"id":"41678","messageId":"200705100027.06087.johan@herland.net","threadId":"8057","inReplyTo":"20070509202224.GG3141@spearce.org","subject":"Re: [RFC] Second parent for reverts","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-09T22:26:52Z","receivedAt":"2007-05-09T22:26:52Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Wednesday 09 May 2007, Shawn O. Pearce wrote:\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> > On Wed, 9 May 2007, Daniel Barkalow wrote:\n> > > The discussion about having a header to specify, for a revert\n> > > commit, what it reverts made me realize that this header *would*\n> > > be useful, but that we don't need a *new* header for it. I think\n> > > that the right method is to add the parent of the reverted commit\n> > > as a second parent for the revert.\n> >\n> > I am not so sure. In a sense, you are correct. But everybody who\n> > does \"git log --no-merges\" would no longer see reverts. Which is\n> > somewhat incorrect.\n>\n> Right.\n>\n> I've actually done what Daniel just talked about doing in one of my\n> \"production\" repositories.  I did it by hand as a developer had\n> created a bad merge and accidentially reverted 800 files during\n> that merge.  80 or so commits later along a public non-rewinding\n> branch coworkers realized things weren't right, and asked me\n> to fix the mess.  As I wanted to save the blame data when I\n> reverted-the-revert I did what Daniel suggests.\n>\n> But since the revert-the-revert wasn't really an interesting point\n> in history, and neither was the bad merge, I don't really care that\n> neither shows up with --no-merges.  The original bad merge was a\n> simple honest mistake made by a developer who was new to Git, and\n> was only caused because merge-recursive wasn't installed properly\n> on that system.\n>\n>\n> As Dscho says, most reverts are interesting points in time.  *Why*\n> a particular revert was done is important.\n>\n> And so I have to disagree quite a bit with Daniel's idea, for exactly\n> that reason.  If I'm looking at a block of code in a file I want to\n> know why its there.  If blame tells me its a revert of something,\n> that tells me we tried another path and it didn't work out.  I might\n> be sitting here looking at this line because I'm thinking of redoing\n> whatever it was that wasn't good!\n\nSorry for butting in with pretty much the same arguments as I had in the \nprevious thread on the \"Reverts\" header field, but...:\n\nIsn't this exactly why we could use the \"Reverts\" header field?\n\n1. It would enable Daniel (and me) to get the \"correct\" blame data (for \nsome version of \"correct\" that at least seems to make sense to us).\n\n2. It would make it trivial for git-log (and other porcelains) to detect \nreverts and color them bright red in Shawn's and Dscho's logs, so that \nthey can easily see when and why things were reverted.\n\n3. It wouldn't screw up \"git log --no-merges\" since it doesn't interfere \nwith the \"real\" parent data.\n\nWhat am I still missing here?\n\n> Hmm.  I should teach git-gui to parse out the revert message and\n> let you click into its parent.  Simple enough.  Maybe it will be\n> in 0.7.0.  Maybe it won't be.  ;-)\n\nIsn't it better/cleaner to get this info from a (strict-format) header \nfield, rather than parsing the commit message? (or worse, detecting \nreverse diffs/patches?)\n\n\nHave fun!\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"41694","messageId":"7vzm4do3m2.fsf@assigned-by-dhcp.cox.net","threadId":"8057","inReplyTo":"7v7irhslx1.fsf@assigned-by-dhcp.cox.net","subject":"Re: [RFC] Second parent for reverts","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-05-10T01:43:17Z","receivedAt":"2007-05-10T01:43:17Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <junkio@cox.net> writes:\n\n>> But a' doesn't actually take anything from b, since it's reverting all of \n>> b (unless it's only reverting part of b), and, if b isn't there, it \n>> doesn't need a commit message, either, so it's not different from a. So \n>> the flow should be:\n>>\n>> a -> b -> c -> d -> e\n>>   \\              /\n>>    --------------\n>>\n>> And this means blame work correctly: lines that b changed will be blamed \n>> on a (or an ancestor), because e will match a there and be different from \n>> d. So I think git-revert should simply add in the reverted patch's parent. \n>> Does this analysis make sense to other people?\n>\n> The revert operation at the tree level (not commit level) treats\n> AS IF b is a common ancestor between a and d and computes a\n> merge between a and d using that fake common ancestor to reach\n> at e.  So it is understandable that you are confused that the\n> result somehow has something to do with a merge between a and d.\n>\n> But other than that, the \"analysis\" does not make any sense to\n> me.\n\nSide note.\n\nIn the same spirit as gitk and history browsers pay attention to\nthe in-body SHA-1 of reverted commits, you could make git-blame\npay attention to the revert message.  I think the rough outline\nwould go like this.\n\n (1) you run pass_blame_to_parent() on 'e' as usual and give as\n     much blame as you can to 'd'; the remainder are attributed\n     to 'e' but it is actually a revert of what 'b' did.\n\n (2) you notice that 'e' is a revert of 'b';\n\n (3) 'b' always has only one parent, as we do not revert a\n     merge; so you can find 'a' easily.\n\n (4) instead of \"take the responsibility for the remaining\n     entries\" as usual in assign_blame() while drilling down\n     'e', you find the matching blob using find_origin and\n     find_rename between 'a' and 'e'.  And pass blames that are\n     attributed to 'e' down to 'a'.\n\n (5) then you keep going, digging 'd' and 'a'.\n \n"},{"id":"41750","messageId":"alpine.LFD.0.98.0705100927340.3986@woody.linux-foundation.org","threadId":"8057","inReplyTo":"alpine.LFD.0.98.0705091513050.4062@woody.linux-foundation.org","subject":"Re: [RFC] Second parent for reverts","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-05-10T16:35:45Z","receivedAt":"2007-05-10T16:35:45Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 9 May 2007, Linus Torvalds wrote:\n> \n> If you want a \"related to that commit\" field, it should be a separate \n> field in the commit object. But since it doesn't really have any real \n> *semantic* meaning to git itself, it shouldn't be in the header. We \n> could, for example, make it be in the free-form section, and teach our \n> graphical visualization tools to automatically turn it into a hyperlink.\n> \n> .. which we already do.\n\nBtw, sorry for harping on this issue, but one of the really *great* things \nabout putting things in the free-form section is a somewhat unanticipated \nhuge advantage:\n\n - we've had much better integration with non-git users than any other SCM \n   I've ever seen!\n\nNow, a lot of that was by design (ie one of the primary design goals for \ngit was to work well with patches), and it's one of the reasons that I \ntotally dismiss the whole \"track file ID's\" idiocy: anything but \"content\" \nwill pretty much by definition not be tracked by any other source control \nsystem.\n\nBut it turns out that the whole \"you can point to commits in the free-form \ncommit message\" thing has worked out really well. It means, for example, \nthat people can do \"git revert\" operations in their own local repository, \nAND THEY TRANSLATE BEAUTIFULLY EVEN AS EMAILED PATCHES!\n\nI'm shouting, because it's easy to overlook these kinds of issues, but \nthey are really really important. Designing your SCM around the notion \nthat everybody will use a totally integrated system is a mistake! It's a \n*huge* mistake. Even a lot of git users end up sending patches back and \nforth, just because for many things it's actually more appropriate.\n\nSo I'm literally getting patches that refer to commits that I've \nintegrated (\"Commit xyz introduced a nasty bug\", or \"Revert commit abc\") \nand they work very well even when I'm not merging with those people \nnatively through git. \n\nAnd yes, maybe the kernel is a bit unusual in this, but I really don't \nthink it should be. In many ways, emailing patches around is a much better \nworkflow for actual *development* than doing git merges. The git merges \nare wonderful, but they are kind of a \"the development is done, let's \nmerge it\" operation - they are not good for sending stuff out for comments \nor discussion!\n\nSo in general, putting things into the headers and having git semantic \nmeaning should be discouraged. The \"parents\" thing is special, because \nthe whole \"history\" thing is very deeply integrated in git, and obviously \nhas to be (any SCM that does _not_ have parenthood information is totally \nbroken *cough*CVS/SVN*cough*), but other than that we should actually \nstrive to _avoid_ anything with deep git semantics.\n\n\t\tLinus\n"},{"id":"41758","messageId":"200705102006.08624.johan@herland.net","threadId":"8057","inReplyTo":"alpine.LFD.0.98.0705100927340.3986@woody.linux-foundation.org","subject":"Re: [RFC] Second parent for reverts","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-10T18:06:05Z","receivedAt":"2007-05-10T18:06:05Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Thursday 10 May 2007, Linus Torvalds wrote:\n> On Wed, 9 May 2007, Linus Torvalds wrote:\n> > If you want a \"related to that commit\" field, it should be a\n> > separate field in the commit object. But since it doesn't really\n> > have any real *semantic* meaning to git itself, it shouldn't be in\n> > the header. We could, for example, make it be in the free-form\n> > section, and teach our graphical visualization tools to\n> > automatically turn it into a hyperlink.\n> >\n> > .. which we already do.\n>\n> Btw, sorry for harping on this issue, but one of the really *great*\n> things about putting things in the free-form section is a somewhat\n> unanticipated huge advantage:\n>\n>  - we've had much better integration with non-git users than any\n> other SCM I've ever seen!\n>\n> [...]\n>\n> So in general, putting things into the headers and having git\n> semantic meaning should be discouraged. The \"parents\" thing is\n> special, because the whole \"history\" thing is very deeply integrated\n> in git, and obviously has to be (any SCM that does _not_ have\n> parenthood information is totally broken *cough*CVS/SVN*cough*), but\n> other than that we should actually strive to _avoid_ anything with\n> deep git semantics.\n\nOk. I'm sold. I will take my header fields and go away before y'all \nreplace me with a very small shell script. :)\n\n\nBTW, I'm wondering whether anybody has ever thought about allowing \nafter-the-fact annotations on commits. Kinda like free-form \ncontinuations on the commit message. It would allow people to make \nnotes on previous commits that were either forgotten at commit-time, or \nonly became apparent after the commit was done.\n\nFurthermore, if we make git-blame pay attention to hints in the commit \nmessage (like Junio suggested somewhere else in this thread) - \nincluding the annotations - we can then add annotations to guide \ngit-blame whenever it gets the blame wrong.\n\nThere's probably other things we could use this for.\n\nObviously we can't store the annotations in the commit object itself \n(because commit objects are immutable). I'm thinking annotations could \nbe stored as simple (compressed) text files in .git/annotations/, under \nthe same sha1/filename as the corresponding commit object is stored \nunder .git/objects/. That would make them easy to retrieve from their \ncorresponding commit.\n\n\nAnyway, it's just an idea that struck me. Feel free to tell me why this \nis the worst idea since, oh, I don't know, say, my header fields \nidea...\n\n\nHave fun! :)\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"41759","messageId":"alpine.LFD.0.98.0705101116480.3986@woody.linux-foundation.org","threadId":"8057","inReplyTo":"200705102006.08624.johan@herland.net","subject":"Re: [RFC] Second parent for reverts","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-05-10T18:22:22Z","receivedAt":"2007-05-10T18:22:22Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Thu, 10 May 2007, Johan Herland wrote:\n> \n> BTW, I'm wondering whether anybody has ever thought about allowing \n> after-the-fact annotations on commits. Kinda like free-form \n> continuations on the commit message. It would allow people to make \n> notes on previous commits that were either forgotten at commit-time, or \n> only became apparent after the commit was done.\n\nWe kind of have some of that.\n\nTag objects can be used that way, and the \"grafts\" file is a very special \ncase. \n\nBut if you want to do it on a larger scale, you'd need something that is \nreally optimized for that. For example, git internally now has a notion of \n\"decorating\" arbitrary objects with arbitrary data, and if you just had an \nefficient file format to create such decorations (for blame or other \nspecial ops), the *code* is easy to write. It's how\n\n\tgit log --decorate\n\nworks right now (the \"data\" is just the tag names, but you could make it \nread other decorations, and the git data structures are very efficient, \nand allow different types of decorations to be used independently of \neach other).\n\n\t\tLinus\n"},{"id":"41790","messageId":"46a038f90705101533m12957692lea94cce2a416f788@mail.gmail.com","threadId":"8057","inReplyTo":"alpine.LFD.0.98.0705100927340.3986@woody.linux-foundation.org","subject":"Re: [RFC] Second parent for reverts","fromName":"Martin Langhoff","fromEmail":"martin.langhoff@gmail.com","sentAt":"2007-05-10T22:33:15Z","receivedAt":"2007-05-10T22:33:15Z","isPatch":false,"sender":{"key":"martin.langhoff@gmail.com","avatar":"https://gravatar.com/avatar/1e3f311b6c4c15836501901ca58f8c0b0667246488084ba524d8bc9867e22fd9?d=mp&s=160"},"body":"On 5/11/07, Linus Torvalds <torvalds@linux-foundation.org> wrote:\n> Designing your SCM around the notion\n> that everybody will use a totally integrated system is a mistake! It's a\n> *huge* mistake.\n\nA huge mistake _everyone else_ makes. git is the only scm that works\ntransparently with everything else -- witness the myriad of\nimporters/exporters/gateways we have, all depending on the fact that\ngit tracks content, and does not get distracted with fancy metadata.\n\ncheers\n\n\n\nmartin\n"},{"id":"43415","messageId":"200705271608.02122.johan@herland.net","threadId":"8057","inReplyTo":"alpine.LFD.0.98.0705101116480.3986@woody.linux-foundation.org","subject":"[PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-27T14:08:01Z","receivedAt":"2007-05-27T14:08:01Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Thursday 10 May 2007, Linus Torvalds wrote:\n> On Thu, 10 May 2007, Johan Herland wrote:\n> > \n> > BTW, I'm wondering whether anybody has ever thought about allowing \n> > after-the-fact annotations on commits. Kinda like free-form \n> > continuations on the commit message. It would allow people to make \n> > notes on previous commits that were either forgotten at commit-time, or \n> > only became apparent after the commit was done.\n> \n> We kind of have some of that.\n> \n> Tag objects can be used that way, and the \"grafts\" file is a very special \n> case. \n> \n> But if you want to do it on a larger scale, you'd need something that is \n> really optimized for that. For example, git internally now has a notion of \n> \"decorating\" arbitrary objects with arbitrary data, and if you just had an \n> efficient file format to create such decorations (for blame or other \n> special ops), the *code* is easy to write. It's how\n> \n> \tgit log --decorate\n> \n> works right now (the \"data\" is just the tag names, but you could make it \n> read other decorations, and the git data structures are very efficient, \n> and allow different types of decorations to be used independently of \n> each other).\n\nI've been working on combining tag objects and --decorate into a useful\nproof-of-concept that provides the after-the-fact commit annotations I\nrequested above, and here's the result:\n\n<quote src=\"Documentation/git-note.txt\">\nA git note provides an after-the-fact text annotation associated with an\nexiting object in the git object database. The note itself is also stored\nin the database (as a special case of a tag object). Note object are therefore\n-- as all other objects in the database -- immutable, i.e. once added they\ncannot be edited, although they can be deleted.\n\nAs with regular tag objects, notes are useless unless they have a\ncorresponding reference stored in `.git/refs`. For notes, these references\nare stored in a hierarchy under `.git/refs/notes`. For each object that has\none or more associated notes, there is a subdirectory under\n`.git/refs/notes` named after the object identifier (SHA1 sum). The\nobject's subdirectory contains one file per note associated with that object.\nThe end result is that notes are easily accessible from the name/identifier\nof the object they are associated with.\n\nNote that deleting a note with `git-note -d` only deletes the reference to\nthe note. The note itself is still present in the object database, but will\nbe considered unreachable by gitlink:git-fsck[1], and may be removed with\ngitlink:git-prune[1].\n</quote>\n\n\nHere are some more details from the design notes I based my work on:\n\n- Notes must not run counter to the existing design (and design goals) of git\n\n- A note is itself stored as a git tag object:\n\t- \"object\" has SHA1 of this note's object (as with regular tag objects)\n\t- \"type\" has type of this note's object (as with regular tag objects)\n\t- \"tag\" is fundamentally unimportant to the note. However, in order\n\t  to provide a unique tag name (so as not to be confused with any\n\t  other tag object) this is automatically set to \"note-{$sha1}\",\n\t  where {$sha1} is the SHA1 sum of the \"object\" and \"tagger\" fields\n\t  and the note message. If uniqueness of tag names within tag objects\n\t  is not important, we may consider dropping this\n\t- \"tagger\" has the note author and date (same as regular tag objects)\n\t- The rest of the tag object contains the note message, similar in\n\t  form to a commit message\n\n- External mapping of objects to associated notes is kept below .git/refs/notes:\n\t- One file per note object (as with tag refs in .git/refs/tags)\n\t- Located at .git/refs/notes/{$object-sha1}/{$note-sha1}\n\t- File contains SHA1 of the note object (same as refs in .git/refs/tags)\n\t- Yes, this means that the filename (minus directory part) is identical\n\t  to the contents of the file\n\nSome of the properties that result from the above decisions:\n\n- A note is uniquely identified by its SHA1 sum (as are all other git objects)\n\n- Notes are trivially packable\n\n- The entire set of note refs can be fully recreated by trawling the object db\n  for tag objects with the tag name field matching the \"note-{$sha1}\" pattern\n\n- Notes behave like regular tags under clone/push/fetch\n\n- Notes are automatically checked by fsck (although some extra checking has\n  has been added)\n\n- Little impact to existing git architecture\n\n\nHowever, there are still some remaining questions:\n\n- Is the creation of a unique tag name (i.e. the 'tag' field _inside_ the\n  object) for note objects really necessary? Which parts of the system rely\n  on these names to be unique across tag objects?\n\n- Should notes have their own object type instead of piggy-backing on \"tag\"?\n\n- What about having a note object type with minimal header fields, and make\n  tags just a special case of a note object with an extra tag name header?\n\n- What about notes on notes? How are tags on tags treated? How should they be\n  treated?\n\n- Currently noted objects (notees?) are treated as reachable from their\n  associated notes, i.e. like tags. This means that an otherwise dangling\n  object will not be detected and removed if it has associated notes.\n  This may not be what we want. If not, the following needs to be done:\n\t- Notes should probably have their own object type at this point\n\t- An object is no longer considered reachable from the associated notes\n\t  This mostly affects git-fsck and git-prune, which should now be able\n\t  to remove dangling objects with notes\n\n\nFinally, here's the shortlog of the patches that will follow this email:\n\nJohan Herland (15):\n      git-note: Add git-note command for adding/listing/deleting git notes\n      git-note: (Documentation) Add git-note manual page\n      git-note: (Administrivia) Add git-note to Makefile, .gitignore, etc.\n      git-note: (Plumbing) Add plumbing-level support for git notes\n      git-note: (Plumbing) Add support for git notes to git-rev-parse and git-show-ref\n      git-note: (Documentation) Explain the new '--notes' option to git-rev-parse and git-show-ref\n      git-note: (Almost plumbing) Add support for git notes to git-pack-refs and git-fsck\n      git-note: (Decorations) Add note decorations to \"git-{log,show,whatchanged} --decorate\"\n      git-note: (Documentation) Explain new behaviour of --decorate in git-{log,show,whatchanged}\n      git-note: (Transfer) Teach git-clone how to clone notes\n      git-note: (Transfer) Teach git-fetch to auto-follow notes\n      git-note: (Transfer) Teach git-push to push notes when --all or --notes is given\n      git-note: (Documentation) Explain the new --notes option to git-push\n      git-note: (Tests) Add tests for git-note and associated functionality\n      git-note: Add display of notes to gitk\n\n .gitignore                        |    3 +-\n Documentation/cmd-list.perl       |    5 +-\n Documentation/git-log.txt         |    3 +-\n Documentation/git-note.txt        |   95 ++++++++++++\n Documentation/git-push.txt        |    7 +-\n Documentation/git-rev-parse.txt   |    5 +-\n Documentation/git-show-ref.txt    |   14 +-\n Documentation/git-show.txt        |    4 +\n Documentation/git-whatchanged.txt |    4 +\n Makefile                          |    2 +-\n builtin-fetch--tool.c             |   11 ++\n builtin-fsck.c                    |   61 ++++++++\n builtin-log.c                     |  108 +++++++++++++-\n builtin-pack-refs.c               |    5 +-\n builtin-push.c                    |   20 ++-\n builtin-rev-parse.c               |    5 +\n builtin-show-ref.c                |   16 ++-\n commit.h                          |    7 +\n generate-cmdlist.sh               |    1 +\n git-clone.sh                      |    5 +-\n git-fetch.sh                      |   27 ++++\n git-note.sh                       |  227 +++++++++++++++++++++++++++\n git-parse-remote.sh               |    4 +-\n gitk                              |   38 +++++-\n log-tree.c                        |   67 ++++++++-\n refs.c                            |   20 +++\n refs.h                            |    4 +\n t/t3850-note.sh                   |  303 +++++++++++++++++++++++++++++++++++++\n 28 files changed, 1033 insertions(+), 38 deletions(-)\n create mode 100644 Documentation/git-note.txt\n create mode 100755 git-note.sh\n create mode 100755 t/t3850-note.sh\n\n\nHave fun!\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"43416","messageId":"200705271609.33033.johan@herland.net","threadId":"8057","inReplyTo":"200705271608.02122.johan@herland.net","subject":"[PATCH 01/15] git-note: Add git-note command for adding/listing/deleting git notes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-27T14:09:32Z","receivedAt":"2007-05-27T14:09:32Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Synopsis:\n'git-note' [-m <msg> | -F <file>] [<head>]\n'git-note' -l [<name>...]\n'git-note' -d <name>...\n\n'git-note' without any parameters creates 'note' associated with HEAD.\nSpecifying an object name/ref to 'git-note' will cause the note to\nbe associated with that object instead.\n\n`git-note -l [<name>...]` lists notes associated with the given <name>s.\nIf no <name> is given, all notes are listed. The given <name> may itself\nbe a note object, in which case it is listed along with its associated\nnotes (if any).\n\n`git-note -d <name>...` deletes the notes associated with the given\n<name>s. The given <name> may itself be a note object, in which case it is\ndeleted along with its associated notes (if any).\n\ngit-note.sh is partly based on git-tag.sh\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n git-note.sh |  227 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 227 insertions(+), 0 deletions(-)\n create mode 100755 git-note.sh\n\ndiff --git a/git-note.sh b/git-note.sh\nnew file mode 100755\nindex 0000000..4f539e2\n--- /dev/null\n+++ b/git-note.sh\n@@ -0,0 +1,227 @@\n+#!/bin/sh\n+\n+USAGE='-l [<name>...] | -d <name>... | [-m <msg> | -F <file>] [<head>]'\n+LONG_USAGE='Add, delete and show git object notes\n+\t-l\tlist/show notes associated with the given object.\n+\t-d\tdelete the notes associated with the given object.\n+\t-m\tuse the given note message (instead of prompting).\n+\t-F \tTake the note message from the given file (instead of prompting).\n+For -l and -d, the given <name> may be a note object itself, in which case\n+it will be included in the listing/deletion.\n+When <head> is not given, it defaults to HEAD.'\n+SUBDIRECTORY_OK='Yes'\n+. git-sh-setup\n+\n+\n+# Show the note identified by $1\n+show_note () {\n+\tauthordate=$(git-cat-file -p $1 | grep -m1 '^tagger ' | cut -c8-)\n+\techo\n+\techo \"--- by $authordate\"\n+\tgit-cat-file -p $1 | tail -n+6\n+\treturn 0\n+}\n+\n+# Show notes below the refs/notes directory given in $1\n+show_notes_below_dir () {\n+\tprev_object=\n+\tfor note_data in $(git-for-each-ref --shell \\\n+\t\t\t--sort=-taggerdate --sort=*authordate \\\n+\t\t\t--format='note=%(objectname);object=%(*objectname);object_type=%(*objecttype);object_tag=%(*tag);' \\\n+\t\t\t$1)\n+\tdo\n+\t\teval \"$note_data\"\n+\n+\t\t# Separate note objects from regular tags\n+\t\tif test \"$object_type\" = \"tag\" -a -n \"$(echo \"$object_tag\" | grep '^note-')\"; then\n+\t\t\tobject_type=\"note\"\n+\t\t\tobject_tag=\"\"\n+\t\tfi\n+\n+\t\tdescription=\n+\t\t# Create description based on object type\n+\t\tcase \"$object_type\" in\n+\t\tcommit)\n+\t\t\tdescription=\"$(git-show --pretty='format:%h (%s) by %an <%ae> on %aD' $object | head -n1)\"\n+\t\t\t;;\n+\t\ttag)\n+\t\t\tdescription=\"$object ($object_tag)\"\n+\t\t\t;;\n+\t\t*)\n+\t\t\tdescription=\"$object\"\n+\t\t\t;;\n+\t\tesac\n+\n+\t\tif test \"$object\" != \"$prev_object\"; then\n+\t\t\ttest -z \"$prev_object\" || echo\n+\t\t\techo \"=== Notes on $object_type $description\"\n+\t\tfi\n+\t\tshow_note $note\n+\t\tprev_object=\"$object\"\n+\tdone\n+\treturn 0\n+}\n+\n+# Show the notes associated with the objects in $@\n+show_associated_notes () {\n+\tif test \"$#\" = \"0\"; then\n+\t\tshow_notes_below_dir \"refs/notes\"\n+\t\treturn 0\n+\tfi\n+\n+\twhile case \"$#\" in 0) break ;; esac\n+\tdo\n+\t\tobj=$(git-rev-parse --verify \"$1\") || \\\n+\t\t\t\tdie \"error: failed to find object named '$1'\"\n+\n+\t\t# If $obj is a note itself, handle it first\n+\t\tif test \"$(git-cat-file -t $obj)\" = \"tag\"; then\n+\t\t\tif test -n \"$(git-cat-file -p $obj | grep '^tag note-')\"; then\n+\t\t\t\techo\n+\t\t\t\techo \"=== Note $obj\"\n+\t\t\t\tshow_note \"$obj\"\n+\t\t\tfi\n+\t\tfi\n+\n+\t\t# List all notes associated with $obj.\n+\t\tshow_notes_below_dir \"refs/notes/$obj\"\n+\n+\t\tshift\n+\tdone\n+\treturn 0\n+}\n+\n+# Delete the noteref in $1 pointing to the note identified by the SHA1 in $2\n+delete_note () {\n+\tgit-update-ref -m \"note: delete\" -d \"$1\" \"$2\"\n+\treturn 0\n+}\n+\n+# Delete all notes associated with object in $1\n+delete_object_notes () {\n+\tfor note_data in $(git-for-each-ref --shell \\\n+\t\t\t--sort=taggerdate --sort=*authordate \\\n+\t\t\t--format='note=%(objectname);object=%(*objectname);object_type=%(*objecttype);noteref=%(refname)' \\\n+\t\t\trefs/notes/$1)\n+\tdo\n+\t\teval \"$note_data\"\n+\t\techo \"Deleting note $note associated with $object_type $object\"\n+\t\tdelete_note \"$noteref\" \"$note\"\n+\tdone\n+\n+\t# Try to get rid of $object's directory below refs/notes as well\n+\tif test -d \"$GIT_DIR/refs/notes/$1\"; then\n+\t\trmdir \"$GIT_DIR/refs/notes/$1\"\n+\tfi\n+\treturn 0\n+}\n+\n+# Delete the notes associated with the objects in $@\n+delete_associated_notes () {\n+\twhile case \"$#\" in 0) break ;; esac\n+\tdo\n+\t\tobj=$(git-rev-parse --verify \"$1\") || \\\n+\t\t\t\tdie \"error: failed to find object named '$1'\"\n+\n+\t\t# If $obj is a note itself, handle it first\n+\t\tif test \"$(git-cat-file -t $obj)\" = \"tag\"; then\n+\t\t\tobjref=\"$(git-cat-file -p $obj | grep -m1 '^object ' | cut -c8-)\"\n+\t\t\tif test -n \"$objref\"; then\n+\t\t\t\techo \"Deleting note $obj\"\n+\t\t\t\tdelete_note \"refs/notes/$objref/$obj\" \"$obj\"\n+\t\t\tfi\n+\t\tfi\n+\n+\t\t# Delete all notes associated with $obj\n+\t\tdelete_object_notes \"$obj\"\n+\n+\t\tshift\n+\tdone\n+\treturn 0\n+}\n+\n+message_given=\n+message=\n+while case \"$#\" in 0) break ;; esac\n+do\n+\tcase \"$1\" in\n+\t-l)\n+\t\tshift\n+\t\tshow_associated_notes \"$@\"\n+\t\texit $?\n+\t\t;;\n+\t-d)\n+\t\tshift\n+\t\tif test \"$#\" = \"0\"; then\n+\t\t\tdie \"error: option -d needs one or more objects\"\n+\t\tfi\n+\t\tdelete_associated_notes \"$@\"\n+\t\texit $?\n+\t\t;;\n+\t-m)\n+\t\tshift\n+\t\tmessage=\"$1\"\n+\t\tif test \"$#\" = \"0\"; then\n+\t\t\tdie \"error: option -m needs an argument\"\n+\t\telse\n+\t\t\tmessage_given=1\n+\t\tfi\n+\t\t;;\n+\t-F)\n+\t\tshift\n+\t\tif test \"$#\" = \"0\"; then\n+\t\t\tdie \"error: option -F needs an argument\"\n+\t\telse\n+\t\t\tmessage=\"$(cat \"$1\")\"\n+\t\t\tmessage_given=1\n+\t\tfi\n+\t\t;;\n+\t-*)\n+\t\tusage\n+\t\t;;\n+\t*)\n+\t\tbreak\n+\t\t;;\n+\tesac\n+\tshift\n+done\n+\n+object=$(git-rev-parse --verify --default HEAD \"$@\") || exit 1\n+type=$(git-cat-file -t $object) || exit 1\n+tagger=$(git-var GIT_COMMITTER_IDENT) || exit 1\n+\n+# Process note message\n+trap 'rm -f \"$GIT_DIR\"/NOTE_TMP* \"$GIT_DIR\"/NOTE_FINALMSG \"$GIT_DIR\"/NOTE_EDITMSG' 0\n+\n+if test -z \"$message_given\"; then\n+\t( echo \"\"\n+\t  echo \"#\"\n+\t  echo \"# Write a note on $type $object\"\n+\t  echo \"#\" ) > \"$GIT_DIR/NOTE_EDITMSG\"\n+\t${VISUAL:-${EDITOR:-vi}} \"$GIT_DIR/NOTE_EDITMSG\" || exit\n+else\n+\tprintf '%s\\n' \"$message\" >\"$GIT_DIR/NOTE_EDITMSG\"\n+fi\n+\n+grep -v '^#' <\"$GIT_DIR/NOTE_EDITMSG\" |\n+git-stripspace >\"$GIT_DIR/NOTE_FINALMSG\"\n+\n+test -s \"$GIT_DIR/NOTE_FINALMSG\" -o -n \"$message_given\" || {\n+\techo >&2 \"No note?\"\n+\texit 1\n+}\n+\n+name=\"note-`( printf 'object %s\\ntagger %s\\n\\n' \"$tagger\"; \\\n+              cat \"$GIT_DIR/NOTE_FINALMSG\" ) \\\n+            | sha1sum | cut -d\" \" -f1`\"\n+\n+git-check-ref-format \"notes/$object/$name\" ||\n+\tdie \"Failed to create a valid note name. (internal git-note error)\"\n+\n+( printf 'object %s\\ntype %s\\ntag %s\\ntagger %s\\n\\n' \\\n+\t\t\"$object\"  \"$type\"  \"$name\" \"$tagger\";\n+\t\tcat \"$GIT_DIR/NOTE_FINALMSG\" ) >\"$GIT_DIR/NOTE_TMP\"\n+rm -f \"$GIT_DIR/NOTE_FINALMSG\"\n+note=$(git-mktag < \"$GIT_DIR/NOTE_TMP\")\n+\n+git-update-ref \"refs/notes/$object/$note\" \"$note\"\n-- \n1.5.2.101.gee49f\n"},{"id":"43417","messageId":"200705271610.20927.johan@herland.net","threadId":"8057","inReplyTo":"200705271608.02122.johan@herland.net","subject":"[PATCH 02/15] git-note: (Documentation) Add git-note manual page","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-27T14:10:20Z","receivedAt":"2007-05-27T14:10:20Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"The manual page describes the operation of the git-note command, as well\nas some discussion around the design and usage of git notes.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/git-note.txt |   95 ++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 95 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/git-note.txt\n\ndiff --git a/Documentation/git-note.txt b/Documentation/git-note.txt\nnew file mode 100644\nindex 0000000..077d526\n--- /dev/null\n+++ b/Documentation/git-note.txt\n@@ -0,0 +1,95 @@\n+git-note(1)\n+===========\n+\n+NAME\n+----\n+git-note - Create, list or delete git note objects\n+\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git-note' [-m <msg> | -F <file>] [<head>]\n+'git-note' -l [<name>...]\n+'git-note' -d <name>...\n+\n+\n+DESCRIPTION\n+-----------\n+Adds a 'note' in `.git/refs/notes/` on an existing git object.\n+\n+A 'note' is an annotation associated with an existing git object.\n+The note is similar in form to a commit message.\n+\n+Unless `-l` or `-d` is passed, the command creates a 'note' object,\n+and requires the note message.  Unless `-m <msg>` or `-F <file>` is\n+given, an editor is started for the user to type in the note message.\n+\n+`-l [<name>...]` lists notes associated with the given <name>s. If no\n+<name> is given, all notes are listed. The given <name> may itself be\n+a note object, in which case it is listed along with its associated\n+notes (if any).\n+\n+`-d <name>...` deletes the notes associated with the given <name>s.\n+The given <name> may itself be a note object, in which case it is\n+deleted along with its associated notes (if any).\n+\n+\n+OPTIONS\n+-------\n+-d <name>...::\n+\tDelete existing notes associated with the given name(s).\n+\n+-l [<name>...]::\n+\tList notes associated with the given name(s). List all notes id no\n+\tname is given.\n+\n+-m <msg>::\n+\tUse the given note message (instead of prompting)\n+\n+-F <file>::\n+\tTake the note message from the given file (instead of prompting).\n+\tUse '-' to read the message from the standard input.\n+\n+\n+DISCUSSION\n+----------\n+A git note provides an after-the-fact text annotation associated with an\n+exiting object in the git object database. The note itself is also stored\n+in the database (as a special case of a tag object). Note object are therefore\n+-- as all other objects in the database -- immutable, i.e. once added they\n+cannot be edited, although they can be deleted.\n+\n+As with regular tag objects, notes are useless unless they have a\n+corresponding reference stored in `.git/refs`. For notes, these references\n+are stored in a hierarchy under `.git/refs/notes`. For each object that has\n+one or more associated notes, there is a subdirectory under\n+`.git/refs/notes` named after the object identifier (SHA1 sum). The\n+object's subdirectory contains one file per note associated with that object.\n+The end result is that notes are easily accessible from the name/identifier\n+of the object they are associated with.\n+\n+Note that deleting a note with `git-note -d` only deletes the reference to\n+the note. The note itself is still present in the object database, but will\n+be considered unreachable by gitlink:git-fsck[1], and may be removed with\n+gitlink:git-prune[1].\n+\n+\n+USE CASE\n+--------\n+Notes may be used to add any kind of extra information to existing git\n+objects that may not become apparent until 'after' the original object is\n+created. A common use case is to add -- for example -- information on bugs\n+to existing commit objects. E.g. making notes like \"This commit closes\n+bug #123\", or \"This commit opens bug #456\". These messages may then be\n+parsed by custom tools that interoperate with a bug tracker, etc.\n+\n+\n+Author\n+------\n+Written by Johan Herland <johan@herland.net>.\n+\n+\n+GIT\n+---\n+Part of the gitlink:git[7] suite\n-- \n1.5.2.101.gee49f\n"},{"id":"43418","messageId":"200705271611.02280.johan@herland.net","threadId":"8057","inReplyTo":"200705271608.02122.johan@herland.net","subject":"[PATCH 03/15] git-note: (Administrivia) Add git-note to Makefile, .gitignore, etc.","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-27T14:11:02Z","receivedAt":"2007-05-27T14:11:02Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Teach Makefile, .gitignore, Documentation/cmd-list.perl and\ngenerate-cmdlist.sh on the existence of the git-note command.\n\nThe patch also does some unrelated whitespace cleanup in\nDocumentation/cmd-list.perl and an alphabetizing fix in .gitignore,\njust to keep things neat and tidy.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n .gitignore                  |    3 ++-\n Documentation/cmd-list.perl |    5 +++--\n Makefile                    |    2 +-\n generate-cmdlist.sh         |    1 +\n 4 files changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex 4dc0c39..0f34b94 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -82,8 +82,9 @@ git-merge-subtree\n git-mergetool\n git-mktag\n git-mktree\n-git-name-rev\n git-mv\n+git-name-rev\n+git-note\n git-pack-redundant\n git-pack-objects\n git-pack-refs\ndiff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl\nindex 443802a..dc7e8b3 100755\n--- a/Documentation/cmd-list.perl\n+++ b/Documentation/cmd-list.perl\n@@ -103,13 +103,13 @@ git-diff-files                          plumbinginterrogators\n git-diff-index                          plumbinginterrogators\n git-diff                                mainporcelain\n git-diff-tree                           plumbinginterrogators\n-git-fast-import\t\t\t\tancillarymanipulators\n+git-fast-import                         ancillarymanipulators\n git-fetch                               mainporcelain\n git-fetch-pack                          synchingrepositories\n git-fmt-merge-msg                       purehelpers\n git-for-each-ref                        plumbinginterrogators\n git-format-patch                        mainporcelain\n-git-fsck\t                        ancillaryinterrogators\n+git-fsck                                ancillaryinterrogators\n git-gc                                  mainporcelain\n git-get-tar-commit-id                   ancillaryinterrogators\n git-grep                                mainporcelain\n@@ -140,6 +140,7 @@ git-mktag                               plumbingmanipulators\n git-mktree                              plumbingmanipulators\n git-mv                                  mainporcelain\n git-name-rev                            plumbinginterrogators\n+git-note                                mainporcelain\n git-pack-objects                        plumbingmanipulators\n git-pack-redundant                      plumbinginterrogators\n git-pack-refs                           ancillarymanipulators\ndiff --git a/Makefile b/Makefile\nindex 29243c6..c2f9f22 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -209,7 +209,7 @@ SCRIPT_SH = \\\n \tgit-applymbox.sh git-applypatch.sh git-am.sh \\\n \tgit-merge.sh git-merge-stupid.sh git-merge-octopus.sh \\\n \tgit-merge-resolve.sh git-merge-ours.sh \\\n-\tgit-lost-found.sh git-quiltimport.sh\n+\tgit-lost-found.sh git-quiltimport.sh git-note.sh\n \n SCRIPT_PERL = \\\n \tgit-add--interactive.perl \\\ndiff --git a/generate-cmdlist.sh b/generate-cmdlist.sh\nindex 975777f..0a37fd0 100755\n--- a/generate-cmdlist.sh\n+++ b/generate-cmdlist.sh\n@@ -26,6 +26,7 @@ init\n log\n merge\n mv\n+note\n prune\n pull\n push\n-- \n1.5.2.101.gee49f\n"},{"id":"43419","messageId":"200705271611.36457.johan@herland.net","threadId":"8057","inReplyTo":"200705271608.02122.johan@herland.net","subject":"[PATCH 04/15] git-note: (Plumbing) Add plumbing-level support for git notes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-27T14:11:36Z","receivedAt":"2007-05-27T14:11:36Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"This patch declares and implements two functions:\n\n1. for_each_note_ref() - for iterating over note references in refs/notes.\n   This is analogous to for_each_tag_ref(), for_each_branch_ref() and\n   for_each_remote_ref(), which is already present.\n\n2. cleanup_notes_subdirs() - a convenience function for removing empty\n   subdirectories from refs/notes. These subdirectories would otherwise be\n   left behind after removing notes, or after packing note refs.\n\nThe above functions will be used by subsequent git-note patches.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n refs.c |   20 ++++++++++++++++++++\n refs.h |    4 ++++\n 2 files changed, 24 insertions(+), 0 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 2ae3235..f0abfcd 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -569,6 +569,11 @@ int for_each_tag_ref(each_ref_fn fn, void *cb_data)\n \treturn do_for_each_ref(\"refs/tags/\", fn, 10, cb_data);\n }\n \n+int for_each_note_ref(each_ref_fn fn, void *cb_data)\n+{\n+\treturn do_for_each_ref(\"refs/notes/\", fn, 11, cb_data);\n+}\n+\n int for_each_branch_ref(each_ref_fn fn, void *cb_data)\n {\n \treturn do_for_each_ref(\"refs/heads/\", fn, 11, cb_data);\n@@ -1433,3 +1438,18 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)\n {\n \treturn do_for_each_reflog(\"\", fn, cb_data);\n }\n+\n+void cleanup_notes_subdirs ()\n+{\n+\tDIR *dir = opendir(git_path(\"refs/notes/\"));\n+\n+\tif (dir) {\n+\t\tstruct dirent *de;\n+\t\twhile ((de = readdir(dir)) != NULL) {\n+\t\t\tif (de->d_name[0] == '.')\n+\t\t\t\tcontinue;\n+\t\t\trmdir(git_path(\"refs/notes/%s\", de->d_name));\n+\t\t}\n+\t\tclosedir(dir);\n+\t}\n+}\ndiff --git a/refs.h b/refs.h\nindex f234eb7..6175bfb 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -21,6 +21,7 @@ typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flag\n extern int head_ref(each_ref_fn, void *);\n extern int for_each_ref(each_ref_fn, void *);\n extern int for_each_tag_ref(each_ref_fn, void *);\n+extern int for_each_note_ref(each_ref_fn, void *);\n extern int for_each_branch_ref(each_ref_fn, void *);\n extern int for_each_remote_ref(each_ref_fn, void *);\n \n@@ -64,4 +65,7 @@ extern int rename_ref(const char *oldref, const char *newref, const char *logmsg\n /** resolve ref in nested \"gitlink\" repository */\n extern int resolve_gitlink_ref(const char *name, const char *refname, unsigned char *result);\n \n+/** clean up empty subdirs below \"refs/notes/\" */\n+extern void cleanup_notes_subdirs();\n+\n #endif /* REFS_H */\n-- \n1.5.2.101.gee49f\n"},{"id":"43420","messageId":"200705271612.13612.johan@herland.net","threadId":"8057","inReplyTo":"200705271608.02122.johan@herland.net","subject":"[PATCH 05/15] git-note: (Plumbing) Add support for git notes to git-rev-parse and git-show-ref","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-27T14:12:13Z","receivedAt":"2007-05-27T14:12:13Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Teach git-rev-parse and git-show-ref to show notes by adding a '--notes'\noption. The '--notes' option is analogous to the '--tags' option for the\nrespective commands.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n builtin-rev-parse.c |    5 +++++\n builtin-show-ref.c  |   16 +++++++++++-----\n 2 files changed, 16 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin-rev-parse.c b/builtin-rev-parse.c\nindex 37addb2..4ab88af 100644\n--- a/builtin-rev-parse.c\n+++ b/builtin-rev-parse.c\n@@ -43,6 +43,7 @@ static int is_rev_argument(const char *arg)\n \t\t\"--max-count=\",\n \t\t\"--min-age=\",\n \t\t\"--no-merges\",\n+\t\t\"--notes\",\n \t\t\"--objects\",\n \t\t\"--objects-edge\",\n \t\t\"--parents\",\n@@ -310,6 +311,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)\n \t\t\t\tfor_each_tag_ref(show_reference, NULL);\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (!strcmp(arg, \"--notes\")) {\n+\t\t\t\tfor_each_note_ref(show_reference, NULL);\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (!strcmp(arg, \"--remotes\")) {\n \t\t\t\tfor_each_remote_ref(show_reference, NULL);\n \t\t\t\tcontinue;\ndiff --git a/builtin-show-ref.c b/builtin-show-ref.c\nindex 9463ff0..8a7d0e7 100644\n--- a/builtin-show-ref.c\n+++ b/builtin-show-ref.c\n@@ -4,10 +4,11 @@\n #include \"tag.h\"\n #include \"path-list.h\"\n \n-static const char show_ref_usage[] = \"git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<length>]] [--abbrev[=<length>]] [--tags] [--heads] [--] [pattern*] < ref-list\";\n+static const char show_ref_usage[] = \"git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<length>]] [--abbrev[=<length>]] [--tags] [--heads] [--notes] [--] [pattern*] < ref-list\";\n \n static int deref_tags = 0, show_head = 0, tags_only = 0, heads_only = 0,\n-\tfound_match = 0, verify = 0, quiet = 0, hash_only = 0, abbrev = 0;\n+\tnotes_only = 0, found_match = 0, verify = 0, quiet = 0, hash_only = 0,\n+\tabbrev = 0;\n static const char **pattern;\n \n static void show_one(const char *refname, const unsigned char *sha1)\n@@ -25,11 +26,12 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo\n \tconst char *hex;\n \tunsigned char peeled[20];\n \n-\tif (tags_only || heads_only) {\n+\tif (tags_only || heads_only || notes_only) {\n \t\tint match;\n \n-\t\tmatch = heads_only && !prefixcmp(refname, \"refs/heads/\");\n-\t\tmatch |= tags_only && !prefixcmp(refname, \"refs/tags/\");\n+\t\tmatch  = heads_only && !prefixcmp(refname, \"refs/heads/\");\n+\t\tmatch |= tags_only  && !prefixcmp(refname, \"refs/tags/\");\n+\t\tmatch |= notes_only && !prefixcmp(refname, \"refs/notes/\");\n \t\tif (!match)\n \t\t\treturn 0;\n \t}\n@@ -213,6 +215,10 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)\n \t\t\theads_only = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(arg, \"--notes\")) {\n+\t\t\tnotes_only = 1;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(arg, \"--exclude-existing\"))\n \t\t\treturn exclude_existing(NULL);\n \t\tif (!prefixcmp(arg, \"--exclude-existing=\"))\n-- \n1.5.2.101.gee49f\n"},{"id":"43421","messageId":"200705271613.09919.johan@herland.net","threadId":"8057","inReplyTo":"200705271608.02122.johan@herland.net","subject":"[PATCH 06/15] git-note: (Documentation) Explain the new '--notes' option to git-rev-parse and git-show-ref","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-27T14:13:09Z","receivedAt":"2007-05-27T14:13:09Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Signed-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/git-rev-parse.txt |    5 ++++-\n Documentation/git-show-ref.txt  |   14 +++++++-------\n 2 files changed, 11 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt\nindex 7757abe..7d36011 100644\n--- a/Documentation/git-rev-parse.txt\n+++ b/Documentation/git-rev-parse.txt\n@@ -73,8 +73,11 @@ OPTIONS\n --tags::\n \tShow tag refs found in `$GIT_DIR/refs/tags`.\n \n+--notes::\n+\tShow note refs found in `$GIT_DIR/refs/notes`.\n+\n --remotes::\n-\tShow tag refs found in `$GIT_DIR/refs/remotes`.\n+\tShow refs found in `$GIT_DIR/refs/remotes`.\n \n --show-prefix::\n \tWhen the command is invoked from a subdirectory, show the\ndiff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt\nindex 2355aa5..7e204f0 100644\n--- a/Documentation/git-show-ref.txt\n+++ b/Documentation/git-show-ref.txt\n@@ -9,7 +9,7 @@ SYNOPSIS\n --------\n [verse]\n 'git-show-ref' [-q|--quiet] [--verify] [-h|--head] [-d|--dereference]\n-\t     [-s|--hash] [--abbrev] [--tags] [--heads] [--] <pattern>...\n+\t     [-s|--hash] [--abbrev] [--tags] [--heads] [--notes] [--] <pattern>...\n 'git-show-ref' --exclude-existing[=pattern]\n \n DESCRIPTION\n@@ -33,16 +33,16 @@ OPTIONS\n \n \tShow the HEAD reference.\n \n---tags, --heads::\n+--heads, --tags, --notes::\n \n-\tLimit to only \"refs/heads\" and \"refs/tags\", respectively.  These\n-\toptions are not mutually exclusive; when given both, references stored\n-\tin \"refs/heads\" and \"refs/tags\" are displayed.\n+\tLimit to only \"refs/heads\", \"refs/tags\" and \"refs/notes\", respectively.\n+\tThese options are not mutually exclusive; when given more than one,\n+\treferences classified as any of them are displayed.\n \n -d, --dereference::\n \n-\tDereference tags into object IDs as well. They will be shown with \"^{}\"\n-\tappended.\n+\tDereference tags and notes into object IDs as well. They will be shown\n+\twith \"^{}\" appended.\n \n -s, --hash::\n \n-- \n1.5.2.101.gee49f\n"},{"id":"43422","messageId":"200705271613.36648.johan@herland.net","threadId":"8057","inReplyTo":"200705271608.02122.johan@herland.net","subject":"[PATCH 07/15] git-note: (Almost plumbing) Add support for git notes to git-pack-refs and git-fsck","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-27T14:13:36Z","receivedAt":"2007-05-27T14:13:36Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Teach git-pack-refs to pack note refs in the same way that regular\ntag refs are packed. Also, make sure to clean up empty subdirs in\nrefs/notes after packing note refs.\n\nTeach git-fsck some extra checking of note refs.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n builtin-fsck.c      |   61 +++++++++++++++++++++++++++++++++++++++++++++++++++\n builtin-pack-refs.c |    5 +++-\n 2 files changed, 65 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-fsck.c b/builtin-fsck.c\nindex cbbcaf0..edbb976 100644\n--- a/builtin-fsck.c\n+++ b/builtin-fsck.c\n@@ -522,9 +522,70 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int f\n \treturn 0;\n }\n \n+static int fsck_handle_note_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)\n+{\n+\t/*\n+\t * - Verify that refname has format $object/$note\n+\t * - Verify that contents of $object/$note == $note\n+\t * - Verify that $object/$note references a tag object\n+\t * - Verify that the tag object points to $object\n+\t * - Verify that $object exists. (already done by fsck_tag())\n+\t */\n+\t /* Even on error, we still return 0 to keep for_each_ref() going. */\n+\n+\tconst char *object_name = refname;\n+\tconst char *note_name = strchr(object_name, '/');\n+\tsize_t object_name_len, note_name_len;\n+\tunsigned char object_sha1[20], note_sha1[20];\n+\tstruct object *note_obj;\n+\tstruct tag *note;\n+\n+\tif (!note_name) {\n+\t\terror(\"%s: invalid note refname; missing note part\", refname);\n+\t\treturn 0;\n+\t}\n+\tobject_name_len = note_name - object_name;\n+\tnote_name_len = strlen(++note_name);\n+\tif (object_name_len != 40 || get_sha1_hex(object_name, object_sha1)) {\n+\t\terror(\"%s: invalid note refname; object part not valid SHA1 sum\", refname);\n+\t\treturn 0;\n+\t}\n+\tif (note_name_len != 40 || get_sha1_hex(note_name, note_sha1)) {\n+\t\terror(\"%s: invalid note refname; note part not valid SHA1 sum\", refname);\n+\t\treturn 0;\n+\t}\n+\tif (hashcmp(note_sha1, sha1)) {\n+\t\terror(\"%s: invalid note ref; note part not identical to note's SHA1 sum (%s)\",\n+\t\t\trefname, sha1_to_hex(sha1));\n+\t\treturn 0;\n+\t}\n+\tnote_obj = lookup_object(sha1);\n+\tif (!note_obj) { /* Couldn't find note object... */\n+\t\tif (!has_sha1_file(sha1)) /* ...and it's not hidden in a pack */\n+\t\t\terror(\"%s: invalid note ref; must point at a valid object\",\n+\t\t\t\trefname);\n+\t\treturn 0; /* Return even if in a pack. */\n+\t}\n+\tif (note_obj->type != OBJ_TAG) {\n+\t\terror(\"%s: invalid note ref; must point at tag object (type == %s)\",\n+\t\t\trefname, typename(note_obj->type));\n+\t\treturn 0;\n+\t}\n+\tnote = (struct tag *) parse_object(sha1);\n+\tif (hashcmp(note->tagged->sha1, object_sha1)) {\n+\t\terror(\"%s: invalid note ref; \"\n+\t\t\t\"object part not identical to tagged object (%s)\",\n+\t\t\trefname, sha1_to_hex(note->tagged->sha1));\n+\t\treturn 0;\n+\t}\n+\n+\treturn 0;\n+}\n+\n static void get_default_heads(void)\n {\n \tfor_each_ref(fsck_handle_ref, NULL);\n+\tfor_each_note_ref(fsck_handle_note_ref, NULL);\n \tif (include_reflogs)\n \t\tfor_each_reflog(fsck_handle_reflog, NULL);\n \ndiff --git a/builtin-pack-refs.c b/builtin-pack-refs.c\nindex 1952950..57b9fee 100644\n--- a/builtin-pack-refs.c\n+++ b/builtin-pack-refs.c\n@@ -39,6 +39,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1,\n \tif ((flags & REF_ISSYMREF))\n \t\treturn 0;\n \tis_tag_ref = !prefixcmp(path, \"refs/tags/\");\n+\tis_tag_ref |= !prefixcmp(path, \"refs/notes/\");\n \n \t/* ALWAYS pack refs that were already packed or are tags */\n \tif (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref && !(flags & REF_ISPACKED))\n@@ -109,8 +110,10 @@ static int pack_refs(unsigned int flags)\n \t\tdie(\"failed to write ref-pack file (%s)\", strerror(errno));\n \tif (commit_lock_file(&packed) < 0)\n \t\tdie(\"unable to overwrite old ref-pack file (%s)\", strerror(errno));\n-\tif (cbdata.flags & PACK_REFS_PRUNE)\n+\tif (cbdata.flags & PACK_REFS_PRUNE) {\n \t\tprune_refs(cbdata.ref_to_prune);\n+\t\tcleanup_notes_subdirs();\n+\t}\n \treturn 0;\n }\n \n-- \n1.5.2.101.gee49f\n"},{"id":"43423","messageId":"200705271614.01462.johan@herland.net","threadId":"8057","inReplyTo":"200705271608.02122.johan@herland.net","subject":"[PATCH 08/15] git-note: (Decorations) Add note decorations to \"git-{log,show,whatchanged} --decorate\"","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-27T14:14:01Z","receivedAt":"2007-05-27T14:14:01Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Introduce the concept of note decorations (supplementing the existing\nname decorations), and teach the code handling the --decorate option\nhow to display note decorations.\n\nThe result is that notes are displayed below the commit message for\nthese commands:\n- git-log --decorate\n- git-show --decorate\n- git-whatchanged --decorate\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n builtin-log.c |  108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n commit.h      |    7 ++++\n log-tree.c    |   67 +++++++++++++++++++++++++++++++++--\n 3 files changed, 178 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin-log.c b/builtin-log.c\nindex 3744712..ee10f2a 100644\n--- a/builtin-log.c\n+++ b/builtin-log.c\n@@ -20,6 +20,108 @@ static int default_show_root = 1;\n /* this is in builtin-diff.c */\n void add_head(struct rev_info *revs);\n \n+static int get_note_decoration_data(const char *data, unsigned long data_len, const char **author, unsigned long *author_len, const char **date, unsigned long *date_len, const char **msg, unsigned long *msg_len)\n+{\n+\t/*\n+\t * Set author, date and msg pointers to their appropriate positions\n+\t * within the given data buffer (tag object data). Also set the\n+\t * appropriate lengths, respectively.\n+\t */\n+\tconst char *p, *q, *end = data + data_len;\n+\tif (!data || !data_len)\n+\t\treturn 1;\n+\tp = data;\n+\tend = data + data_len;\n+\tdo {\n+\t\tif (end - p >= 7 && !memcmp(\"tagger \", p, 7)) {\n+\t\t\t*author = p + 7;\n+\t\t\tif ((q = memchr(*author, '>', end - *author))) {\n+\t\t\t\t*author_len = ++q - *author;\n+\t\t\t\t*date = ++q;\n+\t\t\t\tq = memchr(*date, '\\n', end - *date);\n+\t\t\t\t*date_len = q ? q - *date : 0;\n+\t\t\t}\n+\t\t\telse if ((q = memchr(*author, '\\n', end - *author)))\n+\t\t\t\t*author_len = q - *author;\n+\t\t\telse\n+\t\t\t\t*author_len = 0;\n+\t\t}\n+\t\telse if (end - p >= 1 && !memcmp(\"\\n\", p, 1)) {\n+\t\t\t*msg = ++p;\n+\t\t\t*msg_len = end - *msg;\n+\t\t\tbreak;\n+\t\t}\n+\t\tp = memchr(p, '\\n', end - p);\n+\t} while (p++);\n+\treturn 0;\n+}\n+\n+static int add_note_decoration(const char *refname, const unsigned char *sha1, int flags, void *cb_data)\n+{\n+\tstruct object *obj;\n+\tstruct tag *note;\n+\tstruct note_decoration *res;\n+\tenum object_type data_type;\n+\tchar *data;\n+\tconst char   *author = \"\",   *date = \"\",   *msg = \"\";\n+\tunsigned long author_len = 0, date_len = 0, msg_len = 0;\n+\tunsigned long note_len, data_len;\n+\tunsigned long time = 0;\n+\n+\t/* Get object pointed to by note */\n+\tobj = parse_object(sha1);\n+\tif (!obj)\n+\t\treturn 0;\n+\tif (obj->type != OBJ_TAG)\n+\t\treturn 0;\n+\n+\tnote = (struct tag *)obj;\n+\tobj = note->tagged;\n+\tif (!obj)\n+\t\treturn 0;\n+\n+\t/* Generate note decoration from note data */\n+\tdata = read_sha1_file(note->object.sha1, &data_type, &data_len);\n+\tif (!data || !data_len)\n+\t\treturn 0;\n+\tif (data_type != OBJ_TAG) {\n+\t\tfree(data);\n+\t\treturn 0; /* should not happen */\n+\t}\n+\n+\tif (get_note_decoration_data(data,  data_len,\n+\t\t\t&author, &author_len, &date, &date_len, &msg, &msg_len))\n+\t{\n+\t\tfree(data);\n+\t\treturn 0; /* should not happen */\n+\t}\n+\tif (date && *date && date_len) { /* Found date; prettify it */\n+\t\tchar *tz_p, *end;\n+\t\tint tz;\n+\t\ttime = strtoul(date, &tz_p, 10);\n+\t\ttz = strtol(tz_p, &end, 10);\n+\t\tif (end - date > date_len) {\n+\t\t\tfree(data);\n+\t\t\treturn 0; /* overflowed; should not happen */\n+\t\t}\n+\t\tdate = show_date(time, tz, DATE_NORMAL);\n+\t\tdate_len = strlen(date);\n+\t}\n+\tnote_len = 10 + author_len + date_len + msg_len;\n+\tres = xmalloc(sizeof(struct note_decoration) + note_len);\n+\tsnprintf(res->note, note_len + 1, \"\\n--- by %.*s %.*s\\n%.*s\",\n+\t\t\t(int) author_len, author,\n+\t\t\t(int)   date_len,   date,\n+\t\t\t(int)    msg_len,    msg);\n+\tres->sort_hint = time;\n+\n+\t/* Add note decoration to object that note points to */\n+\tres->next = add_decoration(&note_decoration, obj, res);\n+\n+\tfree(data);\n+\treturn 0;\n+}\n+\n static void add_name_decoration(const char *prefix, const char *name, struct object *obj)\n {\n \tint plen = strlen(prefix);\n@@ -35,6 +137,8 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in\n \tstruct object *obj = parse_object(sha1);\n \tif (!obj)\n \t\treturn 0;\n+\tif (!prefixcmp(refname, \"refs/notes/\"))\n+\t\treturn 0; /* notes shouldn't show up here */\n \tadd_name_decoration(\"\", refname, obj);\n \twhile (obj->type == OBJ_TAG) {\n \t\tobj = ((struct tag *)obj)->tagged;\n@@ -67,8 +171,10 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,\n \t\t\telse\n \t\t\t\tgit_log_output_encoding = \"\";\n \t\t} else if (!strcmp(arg, \"--decorate\")) {\n-\t\t\tif (!decorate)\n+\t\t\tif (!decorate) {\n \t\t\t\tfor_each_ref(add_ref_decoration, NULL);\n+\t\t\t\tfor_each_note_ref(add_note_decoration, NULL);\n+\t\t\t}\n \t\t\tdecorate = 1;\n \t\t} else\n \t\t\tdie(\"unrecognized argument: %s\", arg);\ndiff --git a/commit.h b/commit.h\nindex 86e8dca..93cac79 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -28,6 +28,13 @@ struct name_decoration {\n \tstruct name_decoration *next;\n \tchar name[1];\n };\n+/* While we can decorate any object with a note, it's only used for commits.. */\n+extern struct decoration note_decoration;\n+struct note_decoration {\n+\tstruct note_decoration *next;\n+\tunsigned long sort_hint;\n+\tchar note[1];\n+};\n \n struct commit *lookup_commit(const unsigned char *sha1);\n struct commit *lookup_commit_reference(const unsigned char *sha1);\ndiff --git a/log-tree.c b/log-tree.c\nindex 4bef909..dccaca2 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -5,6 +5,7 @@\n #include \"reflog-walk.h\"\n \n struct decoration name_decoration = { \"object names\" };\n+struct decoration note_decoration = { \"object notes\" };\n \n static void show_parents(struct commit *commit, int abbrev)\n {\n@@ -15,7 +16,7 @@ static void show_parents(struct commit *commit, int abbrev)\n \t}\n }\n \n-static void show_decorations(struct commit *commit)\n+static void show_name_decorations(struct commit *commit)\n {\n \tconst char *prefix;\n \tstruct name_decoration *decoration;\n@@ -32,6 +33,65 @@ static void show_decorations(struct commit *commit)\n \tputchar(')');\n }\n \n+static int compare_note_decorations(const void *a, const void *b)\n+{\n+\tconst struct note_decoration *na = *((const struct note_decoration **) a);\n+\tconst struct note_decoration *nb = *((const struct note_decoration **) b);\n+\tif (na->sort_hint < nb->sort_hint)\n+\t\treturn -1;\n+\telse if (na->sort_hint == nb->sort_hint)\n+\t\treturn 0;\n+\treturn 1;\n+}\n+\n+static void show_note_decorations(struct commit *commit)\n+{\n+\tstruct note_decoration *decoration;\n+\tstruct note_decoration **decoArray;\n+\tunsigned int numDecorations = 0, i;\n+\n+\tdecoration = lookup_decoration(&note_decoration, &commit->object);\n+\tif (!decoration)\n+\t\treturn;\n+\n+\t/* Sort notes */\n+\twhile (decoration) {\n+\t\t++numDecorations;\n+\t\tdecoration = decoration->next;\n+\t}\n+\tdecoration = lookup_decoration(&note_decoration, &commit->object);\n+\tdecoArray = xmalloc(sizeof(struct note_decoration *) * numDecorations);\n+\ti = 0;\n+\twhile (decoration) {\n+\t\tdecoArray[i++] = decoration;\n+\t\tdecoration = decoration->next;\n+\t}\n+\tqsort(decoArray, numDecorations, sizeof(struct note_decoration *), compare_note_decorations);\n+\n+\t/* Print notes */\n+\tputs(\"\\nNotes:\");\n+\tfor (i = 0; i < numDecorations; ++i) {\n+\t\tconst char *p, *q;\n+\t\tdecoration = decoArray[i];\n+\t\tp = decoration->note;\n+\t\twhile (1) {\n+\t\t\t/* print everything indented by 4 spaces */\n+\t\t\tq = strchr(p, '\\n');\n+\t\t\tif (!q)\n+\t\t\t\tq = p + strlen(p);\n+\t\t\tif (q - p)\n+\t\t\t\tprintf(\"    %.*s\\n\", q - p, p);\n+\t\t\telse\n+\t\t\t\tprintf(\"\\n\");\n+\t\t\tif (!*q || !*(q + 1))\n+\t\t\t\tbreak;\n+\t\t\tp = q + 1;\n+\t\t}\n+\t}\n+\n+\tfree(decoArray);\n+}\n+\n /*\n  * Search for \"^[-A-Za-z]+: [^@]+@\" pattern. It usually matches\n  * Signed-off-by: and Acked-by: lines.\n@@ -155,7 +215,7 @@ void show_log(struct rev_info *opt, const char *sep)\n \t\tfputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);\n \t\tif (opt->parents)\n \t\t\tshow_parents(commit, abbrev_commit);\n-\t\tshow_decorations(commit);\n+\t\tshow_name_decorations(commit);\n \t\tputchar(opt->diffopt.line_termination);\n \t\treturn;\n \t}\n@@ -260,7 +320,7 @@ void show_log(struct rev_info *opt, const char *sep)\n \t\t\tprintf(\" (from %s)\",\n \t\t\t       diff_unique_abbrev(parent->object.sha1,\n \t\t\t\t\t\t  abbrev_commit));\n-\t\tshow_decorations(commit);\n+\t\tshow_name_decorations(commit);\n \t\tprintf(\"%s\",\n \t\t       diff_get_color(opt->diffopt.color_diff, DIFF_RESET));\n \t\tputchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\\n');\n@@ -286,6 +346,7 @@ void show_log(struct rev_info *opt, const char *sep)\n \t\tlen = append_signoff(this_header, sizeof(this_header), len,\n \t\t\t\t     opt->add_signoff);\n \tprintf(\"%s%s%s\", this_header, extra, sep);\n+\tshow_note_decorations(commit);\n }\n \n int log_tree_diff_flush(struct rev_info *opt)\n-- \n1.5.2.101.gee49f\n"},{"id":"43424","messageId":"200705271614.31849.johan@herland.net","threadId":"8057","inReplyTo":"200705271608.02122.johan@herland.net","subject":"[PATCH 09/15] git-note: (Documentation) Explain new behaviour of --decorate in git-{log,show,whatchanged}","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-27T14:14:31Z","receivedAt":"2007-05-27T14:14:31Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"The addition of git notes to --decorate output is noted on the git-log\nmanual page. In addition, the --decorate documentation is copied to the\ngit-show and git-whatchanged manual pages, where any mention of --decorate\nhave been missing until now.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/git-log.txt         |    3 ++-\n Documentation/git-show.txt        |    4 ++++\n Documentation/git-whatchanged.txt |    4 ++++\n 3 files changed, 10 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/git-log.txt b/Documentation/git-log.txt\nindex 0f353f6..1168372 100644\n--- a/Documentation/git-log.txt\n+++ b/Documentation/git-log.txt\n@@ -52,7 +52,8 @@ include::pretty-options.txt[]\n \tSee also gitlink:git-reflog[1].\n \n --decorate::\n-    Print out the ref names of any commits that are shown.\n+\tPrint out the ref names and notes associated with any commits that are\n+\tshown.\n \n <paths>...::\n \tShow only commits that affect the specified paths.\ndiff --git a/Documentation/git-show.txt b/Documentation/git-show.txt\nindex 34c5caf..a31ba6b 100644\n--- a/Documentation/git-show.txt\n+++ b/Documentation/git-show.txt\n@@ -38,6 +38,10 @@ OPTIONS\n \tFor a more complete list of ways to spell object names, see\n \t\"SPECIFYING REVISIONS\" section in gitlink:git-rev-parse[1].\n \n+--decorate::\n+\tPrint out the ref names and notes associated with any commits that are\n+\tshown.\n+\n include::pretty-options.txt[]\n \n \ndiff --git a/Documentation/git-whatchanged.txt b/Documentation/git-whatchanged.txt\nindex 399bff3..3f77b3d 100644\n--- a/Documentation/git-whatchanged.txt\n+++ b/Documentation/git-whatchanged.txt\n@@ -43,6 +43,10 @@ OPTIONS\n \t<format> can be one of 'raw', 'medium', 'short', 'full',\n \tand 'oneline'.\n \n+--decorate::\n+\tPrint out the ref names and notes associated with any commits that are\n+\tshown.\n+\n -m::\n \tBy default, differences for merge commits are not shown.\n \tWith this flag, show differences to that commit from all\n-- \n1.5.2.101.gee49f\n"},{"id":"43425","messageId":"200705271615.01210.johan@herland.net","threadId":"8057","inReplyTo":"200705271608.02122.johan@herland.net","subject":"[PATCH 10/15] git-note: (Transfer) Teach git-clone how to clone notes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-27T14:15:01Z","receivedAt":"2007-05-27T14:15:01Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"This patch causes git notes to be cloned in the same way that tags are\ncloned.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n git-clone.sh |    5 ++++-\n 1 files changed, 4 insertions(+), 1 deletions(-)\n\ndiff --git a/git-clone.sh b/git-clone.sh\nindex fdd354f..6bb2fd5 100755\n--- a/git-clone.sh\n+++ b/git-clone.sh\n@@ -57,7 +57,7 @@ Perhaps git-update-server-info needs to be run there?\"\n \t\t*^*)\tcontinue;;\n \t\tesac\n \t\tcase \"$bare,$name\" in\n-\t\tyes,* | ,heads/* | ,tags/*) ;;\n+\t\tyes,* | ,heads/* | ,tags/* | ,notes/*) ;;\n \t\t*)\tcontinue ;;\n \t\tesac\n \t\tif test -n \"$use_separate_remote\" &&\n@@ -320,6 +320,7 @@ then\n \t\tbranch_top=\"heads\"\n \tfi\n \ttag_top=\"tags\"\n+\tnote_top=\"notes\"\n \twhile read sha1 name\n \tdo\n \t\tcase \"$name\" in\n@@ -331,6 +332,8 @@ then\n \t\t\tdestname=\"refs/$branch_top/${name#refs/heads/}\" ;;\n \t\trefs/tags/*)\n \t\t\tdestname=\"refs/$tag_top/${name#refs/tags/}\" ;;\n+\t\trefs/notes/*)\n+\t\t\tdestname=\"refs/$note_top/${name#refs/notes/}\" ;;\n \t\t*)\n \t\t\tcontinue ;;\n \t\tesac\n-- \n1.5.2.101.gee49f\n"},{"id":"43426","messageId":"200705271615.29286.johan@herland.net","threadId":"8057","inReplyTo":"200705271608.02122.johan@herland.net","subject":"[PATCH 11/15] git-note: (Transfer) Teach git-fetch to auto-follow notes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-27T14:15:29Z","receivedAt":"2007-05-27T14:15:29Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"This causes git-fetch to auto-follow notes in the same fashion that tags\nare auto-followed by default.\n\nPretty much all the code in this patch is about doing the same thing for\nnotes as what is already done for tags.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n builtin-fetch--tool.c |   11 +++++++++++\n git-fetch.sh          |   27 +++++++++++++++++++++++++++\n git-parse-remote.sh   |    4 ++--\n 3 files changed, 40 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c\nindex ed4d5de..71ddc6d 100644\n--- a/builtin-fetch--tool.c\n+++ b/builtin-fetch--tool.c\n@@ -82,6 +82,8 @@ static int update_local_ref(const char *name,\n \t\t/* new ref */\n \t\tif (!strncmp(name, \"refs/tags/\", 10))\n \t\t\tmsg = \"storing tag\";\n+\t\telse if (!strncmp(name, \"refs/notes/\", 11))\n+\t\t\tmsg = \"storing note\";\n \t\telse\n \t\t\tmsg = \"storing head\";\n \t\tfprintf(stderr, \"* %s: storing %s\\n\",\n@@ -103,6 +105,11 @@ static int update_local_ref(const char *name,\n \t\tshow_new(type, sha1_new);\n \t\treturn update_ref(\"updating tag\", name, sha1_new, NULL);\n \t}\n+\telse if (!strncmp(name, \"refs/notes/\", 11)) {\n+\t\tfprintf(stderr, \"* %s: updating with %s\\n\", name, note);\n+\t\tshow_new(type, sha1_new);\n+\t\treturn update_ref(\"updating note\", name, sha1_new, NULL);\n+\t}\n \n \tcurrent = lookup_commit_reference(sha1_old);\n \tupdated = lookup_commit_reference(sha1_new);\n@@ -163,6 +170,10 @@ static int append_fetch_head(FILE *fp,\n \t\tkind = \"tag\";\n \t\twhat = remote_name + 10;\n \t}\n+\telse if (!strncmp(remote_name, \"refs/notes/\", 11)) {\n+\t\tkind = \"note\";\n+\t\twhat = remote_name + 11;\n+\t}\n \telse if (!strncmp(remote_name, \"refs/remotes/\", 13)) {\n \t\tkind = \"remote branch\";\n \t\twhat = remote_name + 13;\ndiff --git a/git-fetch.sh b/git-fetch.sh\nindex 0e05cf1..a2a7874 100755\n--- a/git-fetch.sh\n+++ b/git-fetch.sh\n@@ -353,6 +353,33 @@ case \"$no_tags$tags\" in\n \tesac\n esac\n \n+# automated note following\n+case \"\" in # --notes and --no-notes options?\n+'')\n+\tcase \"$reflist\" in\n+\t*:refs/*)\n+\t\t# effective only when we are following remote branch\n+\t\t# using local tracking branch.\n+\t\tnotelist=$(IFS='\t' &&\n+\t\techo \"$ls_remote_result\" |\n+\t\tgit-show-ref --exclude-existing=refs/notes/ |\n+\t\twhile read sha1 name\n+\t\tdo\n+\t\t\tgit-cat-file -t \"$sha1\" >/dev/null 2>&1 || continue\n+\t\t\techo >&2 \"Auto-following $name\"\n+\t\t\techo \".${name}:${name}\"\n+\t\tdone)\n+\tesac\n+\tcase \"$notelist\" in\n+\t'') ;;\n+\t?*)\n+\t\t# do not deepen a shallow tree when following notes\n+\t\tshallow_depth=\n+\t\tfetch_main \"$notelist\" || exit ;;\n+\tesac\n+esac\n+\n+\n # If the original head was empty (i.e. no \"master\" yet), or\n # if we were told not to worry, we do not have to check.\n case \"$orig_head\" in\ndiff --git a/git-parse-remote.sh b/git-parse-remote.sh\nindex 0506b12..95dcc10 100755\n--- a/git-parse-remote.sh\n+++ b/git-parse-remote.sh\n@@ -144,13 +144,13 @@ canon_refs_list_for_fetch () {\n \t\tcase \"$remote\" in\n \t\t'' | HEAD ) remote=HEAD ;;\n \t\trefs/*) ;;\n-\t\theads/* | tags/* | remotes/* ) remote=\"refs/$remote\" ;;\n+\t\theads/* | tags/* | notes/* | remotes/* ) remote=\"refs/$remote\" ;;\n \t\t*) remote=\"refs/heads/$remote\" ;;\n \t\tesac\n \t\tcase \"$local\" in\n \t\t'') local= ;;\n \t\trefs/*) ;;\n-\t\theads/* | tags/* | remotes/* ) local=\"refs/$local\" ;;\n+\t\theads/* | tags/* | notes/* | remotes/* ) local=\"refs/$local\" ;;\n \t\t*) local=\"refs/heads/$local\" ;;\n \t\tesac\n \n-- \n1.5.2.101.gee49f\n"},{"id":"43427","messageId":"200705271615.55294.johan@herland.net","threadId":"8057","inReplyTo":"200705271608.02122.johan@herland.net","subject":"[PATCH 12/15] git-note: (Transfer) Teach git-push to push notes when --all or --notes is given","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-27T14:15:55Z","receivedAt":"2007-05-27T14:15:55Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"As with tags, git-push will not push notes by default. However, if --all\nor --notes (new) is given, git-push will now push notes (similary to tags,\nif --all or --tags is given).\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n builtin-push.c |   20 ++++++++++++--------\n 1 files changed, 12 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin-push.c b/builtin-push.c\nindex cb78401..e3d6a21 100644\n--- a/builtin-push.c\n+++ b/builtin-push.c\n@@ -8,9 +8,9 @@\n \n #define MAX_URI (16)\n \n-static const char push_usage[] = \"git-push [--all] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]\";\n+static const char push_usage[] = \"git-push [--all] [--tags] [--notes] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]\";\n \n-static int all, tags, force, thin = 1, verbose;\n+static int all, tags, notes, force, thin = 1, verbose;\n static const char *receivepack;\n \n #define BUF_SIZE (2084)\n@@ -32,7 +32,8 @@ static int expand_one_ref(const char *ref, const unsigned char *sha1, int flag,\n \t/* Ignore the \"refs/\" at the beginning of the refname */\n \tref += 5;\n \n-\tif (!prefixcmp(ref, \"tags/\"))\n+\tif ((tags  && !prefixcmp(ref, \"tags/\")) ||\n+\t    (notes && !prefixcmp(ref, \"notes/\")))\n \t\tadd_refspec(xstrdup(ref));\n \treturn 0;\n }\n@@ -49,9 +50,8 @@ static void expand_refspecs(void)\n \t\t */\n \t\treturn;\n \t}\n-\tif (!tags)\n-\t\treturn;\n-\tfor_each_ref(expand_one_ref, NULL);\n+\tif (tags || notes)\n+\t\tfor_each_ref(expand_one_ref, NULL);\n }\n \n struct wildcard_cb {\n@@ -141,7 +141,7 @@ static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])\n {\n \tint n = 0;\n \tFILE *f = fopen(git_path(\"remotes/%s\", repo), \"r\");\n-\tint has_explicit_refspec = refspec_nr || all || tags;\n+\tint has_explicit_refspec = refspec_nr || all || tags || notes;\n \n \tif (!f)\n \t\treturn -1;\n@@ -227,7 +227,7 @@ static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI])\n \tconfig_repo = repo;\n \tconfig_current_uri = 0;\n \tconfig_uri = uri;\n-\tconfig_get_refspecs = !(refspec_nr || all || tags);\n+\tconfig_get_refspecs = !(refspec_nr || all || tags || notes);\n \tconfig_get_receivepack = (receivepack == NULL);\n \n \tgit_config(get_remote_config);\n@@ -388,6 +388,10 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\t\ttags = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(arg, \"--notes\")) {\n+\t\t\tnotes = 1;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(arg, \"--force\") || !strcmp(arg, \"-f\")) {\n \t\t\tforce = 1;\n \t\t\tcontinue;\n-- \n1.5.2.101.gee49f\n"},{"id":"43428","messageId":"200705271616.20882.johan@herland.net","threadId":"8057","inReplyTo":"200705271608.02122.johan@herland.net","subject":"[PATCH 13/15] git-note: (Documentation) Explain the new --notes option to git-push","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-27T14:16:20Z","receivedAt":"2007-05-27T14:16:20Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Signed-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/git-push.txt |    7 ++++++-\n 1 files changed, 6 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex e9ad106..2e98844 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -9,7 +9,7 @@ git-push - Update remote refs along with associated objects\n SYNOPSIS\n --------\n [verse]\n-'git-push' [--all] [--tags] [--receive-pack=<git-receive-pack>]\n+'git-push' [--all] [--tags] [--notes] [--receive-pack=<git-receive-pack>]\n            [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]\n \n DESCRIPTION\n@@ -69,6 +69,11 @@ the remote repository.\n \taddition to refspecs explicitly listed on the command\n \tline.\n \n+\\--notes::\n+\tAll refs under `$GIT_DIR/refs/notes` are pushed, in\n+\taddition to refspecs explicitly listed on the command\n+\tline.\n+\n \\--receive-pack=<git-receive-pack>::\n \tPath to the 'git-receive-pack' program on the remote\n \tend.  Sometimes useful when pushing to a remote\n-- \n1.5.2.101.gee49f\n"},{"id":"43429","messageId":"200705271616.53832.johan@herland.net","threadId":"8057","inReplyTo":"200705271608.02122.johan@herland.net","subject":"[PATCH 14/15] git-note: (Tests) Add tests for git-note and associated functionality","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-27T14:16:53Z","receivedAt":"2007-05-27T14:16:53Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"This test script add tests for:\n- git-note (including -l and -d options)\n- State of note/tag objects\n- git-show-ref --notes\n- git-for-each-ref refs/notes\n- git-{log,show,whatchanged} --decorate\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n t/t3850-note.sh |  303 +++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 303 insertions(+), 0 deletions(-)\n create mode 100755 t/t3850-note.sh\n\ndiff --git a/t/t3850-note.sh b/t/t3850-note.sh\nnew file mode 100755\nindex 0000000..f737f7c\n--- /dev/null\n+++ b/t/t3850-note.sh\n@@ -0,0 +1,303 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2007 Johan Herland\n+#\n+\n+test_description='Test of git-note'\n+\n+. ./test-lib.sh\n+\n+\n+# Prepare repo and create some notes\n+\n+test_expect_success 'Preparing for test' '\n+\techo \"foo\" > foo &&\n+\tgit-add foo &&\n+\ttest_tick &&\n+\tgit-commit -m \"Initial commit\" &&\n+\techo \"bar\" >> foo\n+\ttest_tick &&\n+\tgit-commit -m \"Second commit\" foo\n+'\n+test_expect_success 'Testing git-note -m' '\n+\ttest_tick &&\n+\tgit-note -m \"spam\"\n+'\n+test_expect_success 'Testing git-note -F' '\n+\techo \"spam spam\" > note.msg &&\n+\ttest_tick &&\n+\tgit-note -F note.msg\n+'\n+test_expect_success 'Testing git-note <commit>' '\n+\ttest_tick &&\n+\tgit-note -m \"spam spam spam\" 301711b66fe71164f646b798706a2c1f7024da8d\n+'\n+\n+# At this point we should have:\n+# - commit @ 301711b66fe71164f646b798706a2c1f7024da8d (\"Initial commit\")\n+#   - note @ 93bbbc95a42852552494327537263d475aac1789 (\"spam spam spam\")\n+# - commit @ 9671cbee7ad26528645b2665c8f74d39a6288864 (\"Second commit\")\n+#   - note @ 1245491fece5910086bc1870a4a746256f8a7ecf (\"spam\")\n+#   - note @ 466f861dde008d3905d320225eebef727665a3f0 (\"spam spam\")\n+\n+# Verify note objects\n+\n+test_expect_success 'Testing git-cat-file tag <note>' '\n+\tcat > note.expect << EOF &&\n+object 9671cbee7ad26528645b2665c8f74d39a6288864\n+type commit\n+tag note-14669883eb004f12636307269f537d59eb143125\n+tagger C O Mitter <committer@example.com> 1112912113 -0700\n+\n+spam\n+EOF\n+\tgit-cat-file tag 1245491fece5910086bc1870a4a746256f8a7ecf > note.output &&\n+\tcmp note.expect note.output &&\n+\tcat > note.expect << EOF &&\n+object 9671cbee7ad26528645b2665c8f74d39a6288864\n+type commit\n+tag note-ad2099b641be660c4e616bf0b46dcaca391764af\n+tagger C O Mitter <committer@example.com> 1112912173 -0700\n+\n+spam spam\n+EOF\n+\tgit-cat-file tag 466f861dde008d3905d320225eebef727665a3f0 > note.output &&\n+\tcmp note.expect note.output &&\n+\tcat > note.expect << EOF &&\n+object 301711b66fe71164f646b798706a2c1f7024da8d\n+type commit\n+tag note-c1afd1d9a4e2df7efbb29ff88e50e866e88eb108\n+tagger C O Mitter <committer@example.com> 1112912233 -0700\n+\n+spam spam spam\n+EOF\n+\tgit-cat-file tag 93bbbc95a42852552494327537263d475aac1789 > note.output &&\n+\tcmp note.expect note.output\n+'\n+\n+# Verify output from git-note -l\n+\n+test_expect_success 'Testing git-note -l' '\n+\tcat > note.expect << EOF &&\n+=== Notes on commit 9671cbe (Second commit) by A U Thor <author@example.com> on Thu, 7 Apr 2005 15:14:13 -0700\n+\n+--- by C O Mitter <committer@example.com> Thu Apr 7 15:15:13 2005 -0700\n+spam\n+\n+--- by C O Mitter <committer@example.com> Thu Apr 7 15:16:13 2005 -0700\n+spam spam\n+\n+=== Notes on commit 301711b (Initial commit) by A U Thor <author@example.com> on Thu, 7 Apr 2005 15:13:13 -0700\n+\n+--- by C O Mitter <committer@example.com> Thu Apr 7 15:17:13 2005 -0700\n+spam spam spam\n+EOF\n+\tgit-note -l > note.output &&\n+\tcmp note.expect note.output\n+'\n+test_expect_success 'Testing git-note -l <commit>' '\n+\tcat > note.expect << EOF &&\n+=== Notes on commit 9671cbe (Second commit) by A U Thor <author@example.com> on Thu, 7 Apr 2005 15:14:13 -0700\n+\n+--- by C O Mitter <committer@example.com> Thu Apr 7 15:15:13 2005 -0700\n+spam\n+\n+--- by C O Mitter <committer@example.com> Thu Apr 7 15:16:13 2005 -0700\n+spam spam\n+EOF\n+\tgit-note -l 9671cbee7ad26528645b2665c8f74d39a6288864 > note.output &&\n+\tcmp note.expect note.output &&\n+\tcat > note.expect << EOF &&\n+=== Notes on commit 301711b (Initial commit) by A U Thor <author@example.com> on Thu, 7 Apr 2005 15:13:13 -0700\n+\n+--- by C O Mitter <committer@example.com> Thu Apr 7 15:17:13 2005 -0700\n+spam spam spam\n+EOF\n+\tgit-note -l 301711b66fe71164f646b798706a2c1f7024da8d > note.output &&\n+\tcmp note.expect note.output\n+'\n+test_expect_success 'Testing git-note -l <note> <note> <note>' '\n+\tcat > note.expect << EOF &&\n+\n+=== Note 93bbbc95a42852552494327537263d475aac1789\n+\n+--- by C O Mitter <committer@example.com> Thu Apr 7 15:17:13 2005 -0700\n+spam spam spam\n+\n+=== Note 466f861dde008d3905d320225eebef727665a3f0\n+\n+--- by C O Mitter <committer@example.com> Thu Apr 7 15:16:13 2005 -0700\n+spam spam\n+\n+=== Note 1245491fece5910086bc1870a4a746256f8a7ecf\n+\n+--- by C O Mitter <committer@example.com> Thu Apr 7 15:15:13 2005 -0700\n+spam\n+EOF\n+\tgit-note -l 93bbbc95a42852552494327537263d475aac1789 \\\n+\t            466f861dde008d3905d320225eebef727665a3f0 \\\n+\t            1245491fece5910086bc1870a4a746256f8a7ecf > note.output &&\n+\tcmp note.expect note.output\n+'\n+\n+# Verify that notes show up in git-show-ref and git-for-each-ref\n+\n+test_expect_success 'Testing git-show-ref with notes' '\n+\tcat > note.expect << EOF &&\n+9671cbee7ad26528645b2665c8f74d39a6288864 refs/heads/master\n+93bbbc95a42852552494327537263d475aac1789 refs/notes/301711b66fe71164f646b798706a2c1f7024da8d/93bbbc95a42852552494327537263d475aac1789\n+1245491fece5910086bc1870a4a746256f8a7ecf refs/notes/9671cbee7ad26528645b2665c8f74d39a6288864/1245491fece5910086bc1870a4a746256f8a7ecf\n+466f861dde008d3905d320225eebef727665a3f0 refs/notes/9671cbee7ad26528645b2665c8f74d39a6288864/466f861dde008d3905d320225eebef727665a3f0\n+EOF\n+\tgit-show-ref > note.output &&\n+\tcmp note.expect note.output\n+'\n+test_expect_success 'Testing git-show-ref --notes' '\n+\tcat > note.expect << EOF &&\n+93bbbc95a42852552494327537263d475aac1789 refs/notes/301711b66fe71164f646b798706a2c1f7024da8d/93bbbc95a42852552494327537263d475aac1789\n+1245491fece5910086bc1870a4a746256f8a7ecf refs/notes/9671cbee7ad26528645b2665c8f74d39a6288864/1245491fece5910086bc1870a4a746256f8a7ecf\n+466f861dde008d3905d320225eebef727665a3f0 refs/notes/9671cbee7ad26528645b2665c8f74d39a6288864/466f861dde008d3905d320225eebef727665a3f0\n+EOF\n+\tgit-show-ref --notes > note.output &&\n+\tcmp note.expect note.output\n+'\n+test_expect_success 'Testing git-for-each-ref refs/notes' '\n+\tcat > note.expect << EOF &&\n+93bbbc95a42852552494327537263d475aac1789 tag\trefs/notes/301711b66fe71164f646b798706a2c1f7024da8d/93bbbc95a42852552494327537263d475aac1789\n+1245491fece5910086bc1870a4a746256f8a7ecf tag\trefs/notes/9671cbee7ad26528645b2665c8f74d39a6288864/1245491fece5910086bc1870a4a746256f8a7ecf\n+466f861dde008d3905d320225eebef727665a3f0 tag\trefs/notes/9671cbee7ad26528645b2665c8f74d39a6288864/466f861dde008d3905d320225eebef727665a3f0\n+EOF\n+\tgit-for-each-ref refs/notes > note.output &&\n+\tcmp note.expect note.output\n+'\n+\n+# Verify output from git-log/git-show/git-whatchanged --decorate\n+\n+test_expect_success 'Testing git-log --decorate' '\n+\tcat > note.expect << EOF &&\n+commit 9671cbee7ad26528645b2665c8f74d39a6288864 (refs/heads/master)\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:14:13 2005 -0700\n+\n+    Second commit\n+\n+Notes:\n+\n+    --- by C O Mitter <committer@example.com> Thu Apr 7 15:15:13 2005 -0700\n+    spam\n+\n+    --- by C O Mitter <committer@example.com> Thu Apr 7 15:16:13 2005 -0700\n+    spam spam\n+\n+commit 301711b66fe71164f646b798706a2c1f7024da8d\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:13:13 2005 -0700\n+\n+    Initial commit\n+\n+Notes:\n+\n+    --- by C O Mitter <committer@example.com> Thu Apr 7 15:17:13 2005 -0700\n+    spam spam spam\n+EOF\n+\tgit-log --decorate > note.output &&\n+\tcmp note.expect note.output\n+'\n+test_expect_success 'Testing git-show --decorate' '\n+\tcat > note.expect << EOF &&\n+commit 9671cbee7ad26528645b2665c8f74d39a6288864 (refs/heads/master)\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:14:13 2005 -0700\n+\n+    Second commit\n+\n+Notes:\n+\n+    --- by C O Mitter <committer@example.com> Thu Apr 7 15:15:13 2005 -0700\n+    spam\n+\n+    --- by C O Mitter <committer@example.com> Thu Apr 7 15:16:13 2005 -0700\n+    spam spam\n+\n+diff --git a/foo b/foo\n+index 257cc56..3bd1f0e 100644\n+--- a/foo\n++++ b/foo\n+@@ -1 +1,2 @@\n+ foo\n++bar\n+EOF\n+\tgit-show --decorate > note.output &&\n+\tcmp note.expect note.output\n+'\n+test_expect_success 'Testing git-whatchanged --decorate' '\n+\tcat > note.expect << EOF &&\n+commit 9671cbee7ad26528645b2665c8f74d39a6288864 (refs/heads/master)\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:14:13 2005 -0700\n+\n+    Second commit\n+\n+Notes:\n+\n+    --- by C O Mitter <committer@example.com> Thu Apr 7 15:15:13 2005 -0700\n+    spam\n+\n+    --- by C O Mitter <committer@example.com> Thu Apr 7 15:16:13 2005 -0700\n+    spam spam\n+\n+:100644 100644 257cc56... 3bd1f0e... M\tfoo\n+\n+commit 301711b66fe71164f646b798706a2c1f7024da8d\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:13:13 2005 -0700\n+\n+    Initial commit\n+\n+Notes:\n+\n+    --- by C O Mitter <committer@example.com> Thu Apr 7 15:17:13 2005 -0700\n+    spam spam spam\n+\n+:000000 100644 0000000... 257cc56... A\tfoo\n+EOF\n+\tgit-whatchanged --decorate > note.output &&\n+\tcmp note.expect note.output\n+'\n+\n+# Verify successful removal of notes\n+\n+test_expect_success 'Testing git-note -d <note>' '\n+\tcat > note.expect << EOF &&\n+Deleting note 93bbbc95a42852552494327537263d475aac1789\n+EOF\n+\tgit-note -d 93bbbc95a42852552494327537263d475aac1789 > note.output &&\n+\tcmp note.expect note.output\n+'\n+test_expect_success 'Verify repo state after git-note -d <note>' '\n+\tcat > note.expect << EOF &&\n+1245491fece5910086bc1870a4a746256f8a7ecf refs/notes/9671cbee7ad26528645b2665c8f74d39a6288864/1245491fece5910086bc1870a4a746256f8a7ecf\n+466f861dde008d3905d320225eebef727665a3f0 refs/notes/9671cbee7ad26528645b2665c8f74d39a6288864/466f861dde008d3905d320225eebef727665a3f0\n+EOF\n+\tgit-show-ref --notes > note.output &&\n+\tcmp note.expect note.output\n+'\n+test_expect_success 'Testing git-note -d <commit>' '\n+\tcat > note.expect << EOF &&\n+Deleting note 1245491fece5910086bc1870a4a746256f8a7ecf associated with commit 9671cbee7ad26528645b2665c8f74d39a6288864\n+Deleting note 466f861dde008d3905d320225eebef727665a3f0 associated with commit 9671cbee7ad26528645b2665c8f74d39a6288864\n+EOF\n+\tgit-note -d 9671cbee7ad26528645b2665c8f74d39a6288864 > note.output &&\n+\tcmp note.expect note.output\n+'\n+test_expect_success 'Verify repo state after git-note -d <commit>' '\n+\tcat > note.expect << EOF &&\n+EOF\n+\tgit-show-ref --notes > note.output\n+\ttest \"$?\" == \"1\" &&\n+\tcmp note.expect note.output\n+'\n+\n+# Testing of clone/fetch/push?\n+\n+test_done\n-- \n1.5.2.101.gee49f\n"},{"id":"43430","messageId":"200705271617.19097.johan@herland.net","threadId":"8057","inReplyTo":"200705271608.02122.johan@herland.net","subject":"[PATCH 15/15] git-note: Add display of notes to gitk","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-27T14:17:18Z","receivedAt":"2007-05-27T14:17:18Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Teach gitk to display notes below the commit message similarly to how\nnotes are displayed when using --decorate with git-{log,show,whatchanged}.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n gitk |   38 +++++++++++++++++++++++++++++++++++---\n 1 files changed, 35 insertions(+), 3 deletions(-)\n\ndiff --git a/gitk b/gitk\nindex a57e84c..7bed56b 100755\n--- a/gitk\n+++ b/gitk\n@@ -303,7 +303,7 @@ proc getcommit {id} {\n }\n \n proc readrefs {} {\n-    global tagids idtags headids idheads tagcontents\n+    global tagids idtags idnotes headids idheads tagcontents\n     global otherrefids idotherrefs mainhead\n \n     foreach v {tagids idtags headids idheads otherrefids idotherrefs} {\n@@ -318,7 +318,7 @@ proc readrefs {} {\n \tif {[regexp {^remotes/.*/HEAD$} $path match]} {\n \t    continue\n \t}\n-\tif {![regexp {^(tags|heads)/(.*)$} $path match type name]} {\n+\tif {![regexp {^(tags|notes|heads)/(.*)$} $path match type name]} {\n \t    set type others\n \t    set name $path\n \t}\n@@ -341,6 +341,18 @@ proc readrefs {} {\n \t    catch {\n \t        set tagcontents($name) [exec git cat-file tag $id]\n \t    }\n+\t} elseif { $type == \"notes\" } {\n+\t    puts stderr \"note name = '$name', id = '$id'\"\n+\t    lappend idnotes($id) $name\n+\t    set obj {}\n+\t    set type {}\n+\t    set tag {}\n+\t    catch {\n+\t\tset commit [exec git rev-parse \"$id^0\"]\n+\t\tif {$commit != $id} {\n+\t\t    lappend idnotes($commit) $id\n+\t\t}\n+\t    }\n \t} elseif { $type == \"heads\" } {\n \t    set headids($name) $id\n \t    lappend idheads($id) $name\n@@ -3864,7 +3876,7 @@ proc selectline {l isnew} {\n     global displayorder linehtag linentag linedtag\n     global canvy0 linespc parentlist childlist\n     global currentid sha1entry\n-    global commentend idtags linknum\n+    global commentend idtags idnotes linknum\n     global mergemax numcommits pending_select\n     global cmitmode desc_tags anc_tags showneartags allcommits desc_heads\n \n@@ -4009,6 +4021,26 @@ proc selectline {l isnew} {\n     $ctext insert end \"\\n\"\n     appendwithlinks [lindex $info 5] {comment}\n \n+    if {[info exists idnotes($id)]} {\n+\t$ctext insert end \"\\nNotes:\\n\"\n+\tset notes [exec git note -l \"$id\"]\n+\t# strip first line.\n+\tset firstLineEnd [string first \"\\n\" $notes]\n+\tif {$firstLineEnd < 0} {\n+\t    # should never happen...\n+\t    set hdrend 0\n+\t}\n+\tset newnotes [string range $notes [expr {$firstLineEnd + 1}] end]\n+\t# Indent everything by 4 spaces\n+\tset notes {}\n+\tforeach line [split $newnotes \"\\n\"] {\n+\t    append notes \"    $line\\n\"\n+\t}\n+\t# Append to $ctext with auto-highlighting of SHA1 IDs.\n+\tappendwithlinks $notes {comment}\n+\t$ctext insert end \"\\n\"\n+    }\n+\n     $ctext tag delete Comments\n     $ctext tag remove found 1.0 end\n     $ctext conf -state disabled\n-- \n1.5.2.101.gee49f\n"},{"id":"43449","messageId":"7vejl2do5w.fsf@assigned-by-dhcp.cox.net","threadId":"8057","inReplyTo":"200705271608.02122.johan@herland.net","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-05-27T20:09:15Z","receivedAt":"2007-05-27T20:09:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> I've been working on combining tag objects and --decorate into a useful\n> proof-of-concept that provides the after-the-fact commit annotations I\n> requested above, and here's the result:\n> ...\n\nVery nicely presented. I appreciate [PATCH 0/N] summary for a\nseries like this that talks about not just what and how it does,\nbut focus more on why it does it in a particular way, on design\nissues, and decisions.  It makes it very pleasant to comment on\nthe series to have a well written summary like this, because in\nthe early stage of a review cycle, we would want to talk about\nthe design, ignoring implementation details.\n\n> However, there are still some remaining questions:\n>\n> - Is the creation of a unique tag name (i.e. the 'tag' field _inside_ the\n>   object) for note objects really necessary? Which parts of the system rely\n>   on these names to be unique across tag objects?\n\nNone.  Although your use of note-X{40} would make \"recovering\"\neasier, I would imagine.\n\n> - Should notes have their own object type instead of piggy-backing on \"tag\"?\n>\n> - What about having a note object type with minimal header fields, and make\n>   tags just a special case of a note object with an extra tag name header?\n\nTwo things struck me:\n\n - The \"reverse\" mapping 'note' tag uses uses FS as a database.\n   Clever but would not scale well.\n\n - *BUT* the reverse mapping 'note' tag uses is exactly what we\n    would want to use for tags that are not notes, for tools\n    like gitk to attach little flags to commits.\n\nSo maybe we _should_ not even need to call this a new 'notes'\nsystem.  I am just saying your second point above in a different\nway, but I suspect all what is needed is to make the filtering\nand presentation of tag objects a bit more flexible, and making\nuse of the 'peeled' (see .git/packed-refs, for example) mapping\nultra cheap for ordinary tags.\n\nWhat you would need are:\n\n - Ultra-cheap reverse lookup: \"I have an object; which tags\n   point at this?\"\n\n   You solved it with refs/notes/<sha1-of-tagged>/, but it would\n   be useful to extend this to any tag in general.\n\n - Easy way to filter the above question: \"I have an object;\n   which tags of this particlar class point at this?\"\n\n   You are essentially prefiltering by only making notes tags\n   available via refs/notes/ hierarchy, but you can also filter\n   with your naming convention \"tag notes-<sha1>\".  We _could_\n   (I am not seriously suggesting this yet as I haven't thought\n   through the issues yet) allow \"keyword\" header to tag\n   objects, and allow --decorate='kwd1,kwd2,...' to limit the\n   tags we take object decorations from the ones that has one of\n   the specified keywords.  If we do that, I suspect your\n   'notes' system would naturally fall out as regular tags that\n   happen to have a keyword header with 'note' on it.\n\n> - What about notes on notes? How are tags on tags treated? How should they be\n>   treated?\n\nYou can create a tag object that points at another tag.  When we\npeel a tag using \"^{}\" notation, currently it is peeled all the\nway until we hit a non tag, so if you need to look at\nintermediate tags, you need to parse them yourself.  IOW, there\nis no Porcelain feature to let you take advantage of this\ncapability, easily.  But we could use this to say \"I attest that\nhe tagged that object\".\n\n> - Currently noted objects (notees?) are treated as reachable from their\n>   associated notes, i.e. like tags. This means that an otherwise dangling\n>   object will not be detected and removed if it has associated notes.\n\nI think it largely depends on how you intend to use 'notes' if\nthis is a problem.\n\nThe reachability issue is not limited to cruft removal; it also\naffects the fetching.\n\nI would imagine the intended use of a 'note' is to annotate\nobjects (mostly commits, but not necessarily) after the fact,\ne.g. \"this is reported to fix the issue 35833\".  You may or may\nnot want to propagate this across repositories, and the way you\nimplemented it is to have a ref pointing at the note -- then it\nwould make it fetchable by other people.  Otherwise it would not.\nWith your implementation, unfortunately, not having a ref under\nrefs/notes/ would also make the information unavailable to\nyourself.\n\nI think the semantics you would want, when a 'note' object that\npoints at another object (pointee) exists, is to allow creating\na fake (reverse) reachability that says \"pointee points at the\nnote\".  When such a reachability exists:\n\n - A fetch from such a repository that transfers out the pointee\n   will drag the note attached to it along with it (so you would\n   teach rev-list/upload-pack about the extended reachability).\n\n - Incidentally, having a ref that points at the pointee is\n   enough to protect the note that points at the pointee from\n   getting pruned (so you would teach fsck/prune about the\n   extended reachability).\n\nOthewise, the 'note' is subject to garbage collection.  This is\npretty much an extension to the existing 'grafts' mechanism,\nwhich can only override commit and its parents; what is done in\nthe above is to add (not override) to existing reachability to\nany object (not just commit, but anything that can get 'noted').\n"},{"id":"43476","messageId":"200705280229.08870.johan@herland.net","threadId":"8057","inReplyTo":"7vejl2do5w.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-28T00:29:08Z","receivedAt":"2007-05-28T00:29:08Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Sunday 27 May 2007, Junio C Hamano wrote:\n> Johan Herland <johan@herland.net> writes:\n> \n> > I've been working on combining tag objects and --decorate into a useful\n> > proof-of-concept that provides the after-the-fact commit annotations I\n> > requested above, and here's the result:\n> > ...\n> \n> Very nicely presented. I appreciate [PATCH 0/N] summary for a\n> series like this that talks about not just what and how it does,\n> but focus more on why it does it in a particular way, on design\n> issues, and decisions.  It makes it very pleasant to comment on\n> the series to have a well written summary like this, because in\n> the early stage of a review cycle, we would want to talk about\n> the design, ignoring implementation details.\n\nThanks, no problem. You're doing a really good job keeping all this crazy\ndevelopment on track. The very least I can do is make your job easier. :)\n\n> > However, there are still some remaining questions:\n> >\n> > - Is the creation of a unique tag name (i.e. the 'tag' field _inside_ the\n> >   object) for note objects really necessary? Which parts of the system rely\n> >   on these names to be unique across tag objects?\n> \n> None.  Although your use of note-X{40} would make \"recovering\"\n> easier, I would imagine.\n\nYeah, I'd rather just use _one_ identifier for all notes, instead of\ngenerating yet another SHA1 sum, just to make each note (unecessarily)\nunique.\n\n> > - Should notes have their own object type instead of piggy-backing on \"tag\"?\n> >\n> > - What about having a note object type with minimal header fields, and make\n> >   tags just a special case of a note object with an extra tag name header?\n> \n> Two things struck me:\n> \n>  - The \"reverse\" mapping 'note' tag uses uses FS as a database.\n>    Clever but would not scale well.\n\nAgreed.\n\n>  - *BUT* the reverse mapping 'note' tag uses is exactly what we\n>     would want to use for tags that are not notes, for tools\n>     like gitk to attach little flags to commits.\n\nFor notes, it's obvious, as they aren't very useful unless we have a fast\nreverse mapping, but it makes sense that other types of tag objects would\nwant this as well.\n\n> So maybe we _should_ not even need to call this a new 'notes'\n> system.  I am just saying your second point above in a different\n> way, but I suspect all what is needed is to make the filtering\n> and presentation of tag objects a bit more flexible, and making\n> use of the 'peeled' (see .git/packed-refs, for example) mapping\n> ultra cheap for ordinary tags.\n\nAgreed. The peeled mapping makes a lot of sense here. I didn't know\nabout it when I first designed this, and I somehow managed to miss it\nwhen I made the patches to pack-refs and show-refs.\n\nActually, I thought about something similar to the peeled mapping when\nI designed the note refs. I basically had 3 reverse mapping options lined\nup:\n\n1. Put _all_ note refs in a single two-column file, noted objects\n   (\"notees\") in the first column, and note objects in the second.\n   Keep the file sorted by object name (first column), and keep the\n   notes per object sorted chronologically. This would provide one of\n   the fastest possible lookups for \"git-note -l\". However, the file\n   would probably grow extremely large, slowing down insertion (and\n   lookup) considerably.\n\n2. One file per object with notes. Notes sorted chronologically within\n   that file. This is sort of an intermediate between (1) and (3).\n\n3. Keep a two-level hierarchy with one subdir per noted object, and one\n   file per note. This is what I ended up with, mostly because it fit\n   perfectly into the existing layout of refs/, and required very little\n   code to implement.\n\nOf course, now that I know about the peeled mapping, it makes more sense\nto use a variation on that (maybe together with (1) or (2)).\n\n> What you would need are:\n> \n>  - Ultra-cheap reverse lookup: \"I have an object; which tags\n>    point at this?\"\n> \n>    You solved it with refs/notes/<sha1-of-tagged>/, but it would\n>    be useful to extend this to any tag in general.\n> \n>  - Easy way to filter the above question: \"I have an object;\n>    which tags of this particlar class point at this?\"\n> \n>    You are essentially prefiltering by only making notes tags\n>    available via refs/notes/ hierarchy, but you can also filter\n>    with your naming convention \"tag notes-<sha1>\".  We _could_\n>    (I am not seriously suggesting this yet as I haven't thought\n>    through the issues yet) allow \"keyword\" header to tag\n>    objects, and allow --decorate='kwd1,kwd2,...' to limit the\n>    tags we take object decorations from the ones that has one of\n>    the specified keywords.  If we do that, I suspect your\n>    'notes' system would naturally fall out as regular tags that\n>    happen to have a keyword header with 'note' on it.\n\nYeah, I was thinking somewhat similarly; basically making tag objects\nsimpler and more versatile by replacing the \"tag\" header (which is\nfundamentally unimportant to my notes) with a \"tagtype\" header which\ncould be set to \"tag\" for tag objects, \"note\" for note objects, etc.\nEach tagtype could then further define headers specific to that tagtype.\nFor example could \"tagtype == tag\" objects reintroduce the \"tag\" header.\n\nThe tagtype field could then serve pretty much the same purpose as the\nkeyword header you suggest above.\n\n> > - What about notes on notes? How are tags on tags treated? How should they be\n> >   treated?\n> \n> You can create a tag object that points at another tag.  When we\n> peel a tag using \"^{}\" notation, currently it is peeled all the\n> way until we hit a non tag, so if you need to look at\n> intermediate tags, you need to parse them yourself.  IOW, there\n> is no Porcelain feature to let you take advantage of this\n> capability, easily.  But we could use this to say \"I attest that\n> he tagged that object\".\n\nYeah, I'm still undecided on whether we want to allow notes on notes.\nSome might argue that a note on a note should really be just another note\non the original noted object, and that nested notes doesn't make sense.\nBut then, on the other hand, there might be a use for nested notes.\nSay, what about allowing threaded discussion on commits represented as\nnested note objects? ;-P\n\nIn my current implementation, \"git-note <tag>\" will create a note on the\ntag object, and not on the commit pointed to by the tag object. I'm not\nsure that's the default behaviour I want; at least I would probably want\nthe note to show up when looking at the commit pointed to by that tag\n\n> > - Currently noted objects (notees?) are treated as reachable from their\n> >   associated notes, i.e. like tags. This means that an otherwise dangling\n> >   object will not be detected and removed if it has associated notes.\n> \n> I think it largely depends on how you intend to use 'notes' if\n> this is a problem.\n> \n> The reachability issue is not limited to cruft removal; it also\n> affects the fetching.\n> \n> I would imagine the intended use of a 'note' is to annotate\n> objects (mostly commits, but not necessarily) after the fact,\n> e.g. \"this is reported to fix the issue 35833\".\n\nYep, sounds about right, although other people might have other,\njust as valid, ideas.\n\n> You may or may \n> not want to propagate this across repositories, and the way you\n> implemented it is to have a ref pointing at the note -- then it\n> would make it fetchable by other people.  Otherwise it would not.\n> With your implementation, unfortunately, not having a ref under\n> refs/notes/ would also make the information unavailable to\n> yourself.\n> \n> I think the semantics you would want, when a 'note' object that\n> points at another object (pointee) exists, is to allow creating\n> a fake (reverse) reachability that says \"pointee points at the\n> note\".  When such a reachability exists:\n> \n>  - A fetch from such a repository that transfers out the pointee\n>    will drag the note attached to it along with it (so you would\n>    teach rev-list/upload-pack about the extended reachability).\n> \n>  - Incidentally, having a ref that points at the pointee is\n>    enough to protect the note that points at the pointee from\n>    getting pruned (so you would teach fsck/prune about the\n>    extended reachability).\n> \n> Othewise, the 'note' is subject to garbage collection.\n\nYeah, the current implementation does not allow for this reverse/weaker\nrelationship at all.\n\nI agree that fetchability and prunability of a note depends on the\nfetch-/prunability of its pointee. Furthermore, a note on an otherwise\ndangling object should not keep that object alive, but rather allow that\nobject to be pruned (automatically causing the note to be pruned as well).\n\n> This is \n> pretty much an extension to the existing 'grafts' mechanism,\n> which can only override commit and its parents; what is done in\n> the above is to add (not override) to existing reachability to\n> any object (not just commit, but anything that can get 'noted').\n\nI'll have to read up on the 'grafts' mechanism, but it already sounds\nlike a much better way to encode these relationships than my crude\nattempts in 'refs/notes'.\n\nThanks a lot for the feedback.\n\n\nHave fun!\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"43479","messageId":"f3d97g$rkk$1@sea.gmane.org","threadId":"8057","inReplyTo":"7vejl2do5w.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-05-28T00:59:16Z","receivedAt":"2007-05-28T00:59:16Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n\n> You can create a tag object that points at another tag.  When we\n> peel a tag using \"^{}\" notation, currently it is peeled all the\n> way until we hit a non tag, so if you need to look at\n> intermediate tags, you need to parse them yourself.  IOW, there\n> is no Porcelain feature to let you take advantage of this\n> capability, easily.  But we could use this to say \"I attest that\n> he tagged that object\".\n\nHmmm... I wonder why \"git show exaples/tag\" (where 'examples/tag' is\ntag to tag) does show empty output, and why \"git show examples/tag^{tag}\"\ndoes not work...\n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"43484","messageId":"alpine.LFD.0.98.0705272131370.26602@woody.linux-foundation.org","threadId":"8057","inReplyTo":"200705271608.02122.johan@herland.net","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-05-28T04:37:15Z","receivedAt":"2007-05-28T04:37:15Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 27 May 2007, Johan Herland wrote:\n> \n> I've been working on combining tag objects and --decorate into a useful\n> proof-of-concept that provides the after-the-fact commit annotations I\n> requested above, and here's the result:\n\nOk, looks fine to me. I do have a few questions:\n - why don't you just let people name their notes, the same way we name \n   tags (and then actually using it as the note name?)\n\n   Putting them in the refs/notes/ filesystem by their SHA1 seems a bit \n   wasteful, and it would seem that it could be quite nice to name the \n   notes some way?\n\n - This will probably scale horribly badly if you have tens of thousands \n   of notes, even when they are packed. Do we care?\n\nOther than that, it looks straightforward and sane.\n\n\t\tLinus\n"},{"id":"43489","messageId":"200705281254.23297.johan@herland.net","threadId":"8057","inReplyTo":"alpine.LFD.0.98.0705272131370.26602@woody.linux-foundation.org","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-28T10:54:23Z","receivedAt":"2007-05-28T10:54:23Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Monday 28 May 2007, Linus Torvalds wrote:\n> \n> On Sun, 27 May 2007, Johan Herland wrote:\n> > \n> > I've been working on combining tag objects and --decorate into a useful\n> > proof-of-concept that provides the after-the-fact commit annotations I\n> > requested above, and here's the result:\n> \n> Ok, looks fine to me. I do have a few questions:\n>  - why don't you just let people name their notes, the same way we name \n>    tags (and then actually using it as the note name?)\n> \n>    Putting them in the refs/notes/ filesystem by their SHA1 seems a bit \n>    wasteful, and it would seem that it could be quite nice to name the \n>    notes some way?\n\nWell, when I first designed them, I thought of notes more as an extension\nto the commit message than as a special case of tags. Therefore naming\nthem didn't seem natural at that point. But now that it all seems to\nblend together (cf. Junio's answer), I'm not opposed to putting names\non notes at all. But I don't want to make naming mandatory. It should\nstill be possible to add a note like \"Fixes bug #12345\" in a jiffy,\nwithout having to think up a note name as well (or having a badly\nthought-up name pollute some namespace).\n\nSo I guess we end up with two kinds of objects/entities:\n1. tags, with mandatory naming and optional comment/message\n2. notes, with optional naming and mandatory comment/message\n\nTo some degree we can say that the name is the ref, and the\ncomment/message is the object. We therefore get:\n\n1. tags, with mandatory ref and optional tag object (we have this today)\n2. notes, with optional ref and mandatory object\n\nToday, a note needs its ref in order to stay alive, but this is purely a\n(badly designed) technical measure. When we get the proper reverse mapping\ndone (as discussed in Junio's answer), notes will stop polluting refs/ and\nwe can instead allow naming of notes by adding simple (tag-type) refs to\nnote objects.\n\nAlso, as mentioned by Junio, we want to do the reverse mapping for regular\ntags as well.\n\nAt this point, there's no real difference between tags and notes (some\nwould say there never were a difference), and we can stop caring about\nwhether a note is a note or a tag, etc, at least in the plumbing.\n\nWe may still want to enforce a difference in the procelain, though.\nWhen presenting tags/notes (e.g. with --decorate), users might be\ninterested in classifying and filtering their tags/notes in different\ncategories. Categories might be (\"note\", \"tag\"), or we can make it\nuser-extensible, like Junio's \"keyword\" idea). Either way, we'll have\nto make room for this classification in the tag object by adding another\nheader (\"tagtype\", \"keyword\", whatever...).\n\nAnd while we're on the subject of changing the tag object, I'd like for\nthe \"tag\" header (the one holding the tag name) to become optional.\nWhen doing my ref <-> name trick above, I conveniently forgot this little\nbugger. Basically the only reason for this one to exist is to include the\nname of the tag in the data passed to gpg for signing. This is of course\nnecesssary in order to make renaming a signed tag impossible. (Allowing\nrenaming would make it possible to replace it with a malicious tag with\nthe original name.) Therefore the \"tag\" header must be mandatory for\nsigned tags. But for all other tags (including notes) this header is\npretty much useless.\n\n>  - This will probably scale horribly badly if you have tens of thousands \n>    of notes, even when they are packed. Do we care?\n\nI can't see why the current implementation would scale any worse than an\nequivalent number of (annotated/signed) tags. But then again, the tag\nsystem might not have been designed with tens of thousands of tag objects\nin mind. :)\n\n> Other than that, it looks straightforward and sane.\n\nThanks for the feedback\n\n\nHave fun!\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"43497","messageId":"alpine.LFD.0.98.0705280924410.26602@woody.linux-foundation.org","threadId":"8057","inReplyTo":"200705281254.23297.johan@herland.net","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-05-28T16:28:22Z","receivedAt":"2007-05-28T16:28:22Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 28 May 2007, Johan Herland wrote:\n> \n> >  - This will probably scale horribly badly if you have tens of thousands \n> >    of notes, even when they are packed. Do we care?\n> \n> I can't see why the current implementation would scale any worse than an\n> equivalent number of (annotated/signed) tags. But then again, the tag\n> system might not have been designed with tens of thousands of tag objects\n> in mind. :)\n\nRight. I was more thinking that this \"notes\" thing could potentially be a \nvery useful thing for some random workflow - using notes to indicate that \nsome commit has been vetted by somebody, for example (ie adding things \nlike \"Acked-by:\" after-the-fact, which happens for the kernel).\n\nAnd once you start using notes for something like that, I think you're \ngoing to end up with a set of notes that grows with history, and \npotentially grows quite quickly.\n\nSo I can see people having thousands of tags, but usually you only tag \nreleases. In contrast, I can see notes being used not as a \"per release\" \nthing, but closer to a \"per commit\" thing. And that kind of worries me, I \ncan see workflows where you end up having tons and tons of notes.\n\nBut hey, maybe I just worry unnecessarily.\n\n\t\tLinus\n"},{"id":"43499","messageId":"200705281840.50814.johan@herland.net","threadId":"8057","inReplyTo":"alpine.LFD.0.98.0705280924410.26602@woody.linux-foundation.org","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-28T16:40:50Z","receivedAt":"2007-05-28T16:40:50Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Monday 28 May 2007, Linus Torvalds wrote:\n> > I can't see why the current implementation would scale any worse than an\n> > equivalent number of (annotated/signed) tags. But then again, the tag\n> > system might not have been designed with tens of thousands of tag objects\n> > in mind. :)\n> \n> Right. I was more thinking that this \"notes\" thing could potentially be a \n> very useful thing for some random workflow - using notes to indicate that \n> some commit has been vetted by somebody, for example (ie adding things \n> like \"Acked-by:\" after-the-fact, which happens for the kernel).\n> \n> And once you start using notes for something like that, I think you're \n> going to end up with a set of notes that grows with history, and \n> potentially grows quite quickly.\n> \n> So I can see people having thousands of tags, but usually you only tag \n> releases. In contrast, I can see notes being used not as a \"per release\" \n> thing, but closer to a \"per commit\" thing. And that kind of worries me, I \n> can see workflows where you end up having tons and tons of notes.\n> \n> But hey, maybe I just worry unnecessarily.\n\nI still don't see what makes note objects inherently more expensive than\ncommit objects. Except for the refs, of course, but we're getting rid\nof those (at least replacing them with a more efficient reverse mapping).\n\nAnd even if we _do_ end up with 10 notes per commit, we could always\ndesign some kind of \"supernote\" that lets \"git-gc\" pack all the notes\nrelated to a commit into _one_ object.\n\n\nHave fun!\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"43501","messageId":"alpine.LFD.0.98.0705280957070.26602@woody.linux-foundation.org","threadId":"8057","inReplyTo":"200705281840.50814.johan@herland.net","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-05-28T16:58:02Z","receivedAt":"2007-05-28T16:58:02Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 28 May 2007, Johan Herland wrote:\n> \n> I still don't see what makes note objects inherently more expensive than\n> commit objects. Except for the refs, of course, but we're getting rid\n> of those (at least replacing them with a more efficient reverse mapping).\n\nIt's exactly the refs that I worry about.\n\nAnything that needs to read in all notes at startup is going to be _slow_.\n\nIn contrast, commits we read when (and only when) we need them.\n\n\t\tLinus\n"},{"id":"43504","messageId":"20070528172949.GA7295@mellanox.co.il","threadId":"8057","inReplyTo":"alpine.LFD.0.98.0705272131370.26602@woody.linux-foundation.org","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Michael S. Tsirkin","fromEmail":"mst@dev.mellanox.co.il","sentAt":"2007-05-28T17:29:49Z","receivedAt":"2007-05-28T17:29:49Z","isPatch":true,"sender":{"key":"mst@kernel.org","avatar":null},"body":"> Quoting Linus Torvalds <torvalds@linux-foundation.org>:\n> Subject: Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits\n> \n> \n> \n> On Sun, 27 May 2007, Johan Herland wrote:\n> > \n> > I've been working on combining tag objects and --decorate into a useful\n> > proof-of-concept that provides the after-the-fact commit annotations I\n> > requested above, and here's the result:\n> \n> Ok, looks fine to me. I do have a few questions:\n>  - why don't you just let people name their notes, the same way we name \n>    tags (and then actually using it as the note name?)\n> \n>    Putting them in the refs/notes/ filesystem by their SHA1 seems a bit \n>    wasteful, and it would seem that it could be quite nice to name the \n>    notes some way?\n> \n>  - This will probably scale horribly badly if you have tens of thousands \n>    of notes, even when they are packed. Do we care?\n\nMaybe note names could include the object they point to?\nWould this help with the scalability too (i.e. only get\nnames for objects we want)?\n\n\n-- \nMST\n"},{"id":"43505","messageId":"20070528174205.GB7295@mellanox.co.il","threadId":"8057","inReplyTo":"20070528172949.GA7295@mellanox.co.il","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Michael S. Tsirkin","fromEmail":"mst@dev.mellanox.co.il","sentAt":"2007-05-28T17:42:05Z","receivedAt":"2007-05-28T17:42:05Z","isPatch":true,"sender":{"key":"mst@kernel.org","avatar":null},"body":"> Quoting Michael S. Tsirkin <mst@dev.mellanox.co.il>:\n> Subject: Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits\n> \n> > Quoting Linus Torvalds <torvalds@linux-foundation.org>:\n> > Subject: Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits\n> > \n> > \n> > \n> > On Sun, 27 May 2007, Johan Herland wrote:\n> > > \n> > > I've been working on combining tag objects and --decorate into a useful\n> > > proof-of-concept that provides the after-the-fact commit annotations I\n> > > requested above, and here's the result:\n> > \n> > Ok, looks fine to me. I do have a few questions:\n> >  - why don't you just let people name their notes, the same way we name \n> >    tags (and then actually using it as the note name?)\n> > \n> >    Putting them in the refs/notes/ filesystem by their SHA1 seems a bit \n> >    wasteful, and it would seem that it could be quite nice to name the \n> >    notes some way?\n> > \n> >  - This will probably scale horribly badly if you have tens of thousands \n> >    of notes, even when they are packed. Do we care?\n> \n> Maybe note names could include the object they point to?\n> Would this help with the scalability too (i.e. only get\n> names for objects we want)?\n\nHmm, I just noticed there's refs/notes/{object-sha}/{note-sha}\nthat does exactly that. So - this seems to be clear to everyone\nelse, but I seem to miss the reason why does this not scale well?\nWe can just get the notes for objects we pull, right?\n\n-- \nMST\n"},{"id":"43506","messageId":"200705281948.27329.johan@herland.net","threadId":"8057","inReplyTo":"alpine.LFD.0.98.0705280957070.26602@woody.linux-foundation.org","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-28T17:48:27Z","receivedAt":"2007-05-28T17:48:27Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Monday 28 May 2007, Linus Torvalds wrote:\n> \n> On Mon, 28 May 2007, Johan Herland wrote:\n> > \n> > I still don't see what makes note objects inherently more expensive than\n> > commit objects. Except for the refs, of course, but we're getting rid\n> > of those (at least replacing them with a more efficient reverse mapping).\n> \n> It's exactly the refs that I worry about.\n> \n> Anything that needs to read in all notes at startup is going to be _slow_.\n> \n> In contrast, commits we read when (and only when) we need them.\n\nOk. But the reverse mapping will help with this, won't it?\nWe'll look up the interesting commits and find their associated\nnote objects directly.\n\nBTW, if you use \"git-note -l <commit>\" it'll go directly to <commit>'s\nsubdir in refs/notes and never look at any other notes.\n\nOf course, this currently does not help the plumbing much, which uses\nfor_each_note_ref() instead...\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"43507","messageId":"200705281958.51079.johan@herland.net","threadId":"8057","inReplyTo":"20070528174205.GB7295@mellanox.co.il","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-28T17:58:50Z","receivedAt":"2007-05-28T17:58:50Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Monday 28 May 2007, Michael S. Tsirkin wrote:\n> Quoting Michael S. Tsirkin <mst@dev.mellanox.co.il>:\n> > Maybe note names could include the object they point to?\n> > Would this help with the scalability too (i.e. only get\n> > names for objects we want)?\n> \n> Hmm, I just noticed there's refs/notes/{object-sha}/{note-sha}\n> that does exactly that. So - this seems to be clear to everyone\n> else, but I seem to miss the reason why does this not scale well?\n> We can just get the notes for objects we pull, right?\n\nCurrently, only \"git-note -l\" (and by extension gitk) uses the shortcut\nthrough refs/notes/{object-sha}/...\n\nThe --decorate code and everybody else goes through for_each_note_ref()\nwhich looks up every note ref in the repo.\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"43515","messageId":"7vwsysbrtg.fsf@assigned-by-dhcp.cox.net","threadId":"8057","inReplyTo":"200705281948.27329.johan@herland.net","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-05-28T20:45:31Z","receivedAt":"2007-05-28T20:45:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> On Monday 28 May 2007, Linus Torvalds wrote:\n>> \n>> On Mon, 28 May 2007, Johan Herland wrote:\n>> > \n>> > I still don't see what makes note objects inherently more expensive than\n>> > commit objects. Except for the refs, of course, but we're getting rid\n>> > of those (at least replacing them with a more efficient reverse mapping).\n>> \n>> It's exactly the refs that I worry about.\n>> \n>> Anything that needs to read in all notes at startup is going to be _slow_.\n>> \n>> In contrast, commits we read when (and only when) we need them.\n>\n> Ok. But the reverse mapping will help with this, won't it?\n> We'll look up the interesting commits and find their associated\n> note objects directly.\n\nThe issue Linus brought up worries me, too.\n\nThe \"efficient reverse mapping\" is still handwaving at this\nstage.  What it needs to do is an equivalent to your\nimplementation with \"refs/notes/<a dir per commit>/<note>\".  The\n\"efficient\" one might do a flat file that says \"notee note\" per\nline sorted by notee, or it might use BDB or sqlite, but the\namount of the data and complexity of the look-up is really the\nsame.  A handful notes per each commit in the history (I think\nLinus's \"Acked-by after the fact\" example a very sensible thing\nto want from this subsystem).\n\nI am not saying that it is impossible to make the set-up cost\nfor the \"efficient lookup\" almost zero, and to make it lazy and\non-demand.  The concern above just adds one design constraints\nto that \"efficient reverse mapping\" code yet to come.\n"},{"id":"43516","messageId":"7vps4kbrtb.fsf@assigned-by-dhcp.cox.net","threadId":"8057","inReplyTo":"200705281254.23297.johan@herland.net","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-05-28T20:45:36Z","receivedAt":"2007-05-28T20:45:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> And while we're on the subject of changing the tag object, I'd like for\n> the \"tag\" header (the one holding the tag name) to become optional.\n> When doing my ref <-> name trick above, I conveniently forgot this little\n> bugger. Basically the only reason for this one to exist is to include the\n> name of the tag in the data passed to gpg for signing. This is of course\n> necesssary in order to make renaming a signed tag impossible. (Allowing\n> renaming would make it possible to replace it with a malicious tag with\n> the original name.) Therefore the \"tag\" header must be mandatory for\n> signed tags. But for all other tags (including notes) this header is\n> pretty much useless.\n\nThis brings up an interesting point, by the way.\n\nCurrently when you say\n\n\t$ git show v2.6.22\n\nwe \"dwim_ref\" v2.6.22.  That is, we try to see if \".git/v2.6.22\"\nis there (it isn't), check \".git/refs/v2.6.22\" (no, again), then\nhappily find that \".git/refs/tags/v2.6.22\" is there and use it.\n\nDuring this process we _never_ check if the tag object pointed\nat by the ref v2.6.22 has tagname v2.6.22 at all.\n\nI do not think it is a problem at all that we do not use \"tag\"\nname during the look-up process.  However it would be a good\nidea to add logic to fsck to warn upon inconsistencis (perhaps\nby mistake) between refname and tag's true name.\n\nThe check would say something like:\n\n\tIf an annotated (signed or unsigned) tag has a \"tag\"\n\tline to give it the official $name, and if it is pointed\n\tat by a ref, the refname must end with \"/$name\".\n\tOtherwise we warn.\n\nTrivially, the above rule says that having v2.6.22 tag under\nrefs/tags/v2.6.20 is a mistake we would want to be warned upon.\n\nThe \"/$name\" part is to support merging a separate project into\nanother project after the fact better.  Shawn graciously names\nhis tags in git-gui project \"gitgui-0.7.1\", to avoid tag name\ncollisions with \"v1.5.0\" style tag names in git.git project, but\nwe could have stored his v0.7.1 tag as refs/tags/gitgui/v0.7.1\nto differenciate the tag namespace (this needs an update to the\n\"tag following\" code in git-fetch with a new configuration).\n"},{"id":"43519","messageId":"20070528211956.GA7044@spearce.org","threadId":"8057","inReplyTo":"7vps4kbrtb.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-05-28T21:19:56Z","receivedAt":"2007-05-28T21:19:56Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Junio C Hamano <junkio@cox.net> wrote:\n> Shawn graciously names\n> his tags in git-gui project \"gitgui-0.7.1\", to avoid tag name\n> collisions with \"v1.5.0\" style tag names in git.git project, but\n> we could have stored his v0.7.1 tag as refs/tags/gitgui/v0.7.1\n> to differenciate the tag namespace (this needs an update to the\n> \"tag following\" code in git-fetch with a new configuration).\n\nActually I use that same naming convention everywhere else except\nin egit.  I've found it easier to have the \"major project name\"\nprefixed in front of the version number as it sort of reminds me\nof what I'm looking at...  yes, I get confused easily.  There are\nfar too many \"v1.0.0\", \"v1.0.1\" around...  ;-)\n\n-- \nShawn.\n"},{"id":"43524","messageId":"20070528213511.GB7044@spearce.org","threadId":"8057","inReplyTo":"7vwsysbrtg.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-05-28T21:35:11Z","receivedAt":"2007-05-28T21:35:11Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Junio C Hamano <junkio@cox.net> wrote:\n> Johan Herland <johan@herland.net> writes:\n> > Ok. But the reverse mapping will help with this, won't it?\n> > We'll look up the interesting commits and find their associated\n> > note objects directly.\n> \n> The issue Linus brought up worries me, too.\n> \n> The \"efficient reverse mapping\" is still handwaving at this\n> stage.  What it needs to do is an equivalent to your\n> implementation with \"refs/notes/<a dir per commit>/<note>\".  The\n> \"efficient\" one might do a flat file that says \"notee note\" per\n> line sorted by notee, or it might use BDB or sqlite, but the\n> amount of the data and complexity of the look-up is really the\n> same.  A handful notes per each commit in the history (I think\n> Linus's \"Acked-by after the fact\" example a very sensible thing\n> to want from this subsystem).\n\nPlease, don't use BDB or sqllite.  I really don't trust either.\nI've lost data to both.  I've *never* lost data to a Git packfile.\n;-)\n\nI'm actually thinking pack v4.  OK, I know its just a virtual hand\nwaving thing still, but there's really no reason Nico and I cannot\nget the damn thing finished before we both wind up buying the farm.\n\nWhat if we use a \"slow\" storage by \"refs/notes/$objname/$notename\",\nand we also allow them to appear in the packed-refs file.  But during\na repack we instead stick the annotations into the same packfile as\n$objname, and we also include a list of $notename after $objname's\nother data.\n\nThis way we have quick access to the $notename(s) of all notes of\n$objname through the pack, and we can lazily go get the notes raw\ndata if we need them.  This isn't too different from what we do\nwith parent fields.  We initialize the commit_list when we parse\nthe commit but we don't parse the parents until we really need them.\n\nOnce packed we delete the note ref (if loose) and during a repack\nof the packed-refs file we delete the note if $notename exists in\nthe packfile.\n\nIf someone wants notes we can check to see if refs/notes exists; if\nit does then we enumerate all refs and catalog the notes we found\nin memory.  Note search then works off the in-memory list and off\nthe packfiles.  If refs/notes doesn't exist (and we should delete it\nwhen we prune away those ref files or prune them out of packed-refs)\nthen we can skip the ref enumeration and just go straight to the\npackfile(s).  Most notes will be in the packfiles.  I think most\npeople repack often enough that the handful of unpacked notes before\nthe next repack won't be a major bottleneck.  Especially since we\ncan get the target $objname directly from a readdir() call, or by\nsplitting the string in the packed-refs file.\n\n>From an object enumeration standpoint during packfile generation\nthe notes for a given object are treated like the parent fields in\na commit; they come after the object itself, but unlike the parent\nfields they are always output if the object itself was output.\n(Hence an --objects-edge enumeration would include the notes only\nif the commit itself had been included.)\n\nUnfortunately that doesn't cover the case of a note being added\nmonths later and needing to distribute it to clients that already\nhave the object the note is attached to.\n\n\nI haven't been following this discussion very closely, but I'd also\nlike to suggest that if annotated tags are being used for notes\nthat the \"tag <name>\" field be left out of them.  I don't see why\na note should be given a specific name that sits in a (roughly)\nglobal namespace.\n\n-- \nShawn.\n"},{"id":"43528","messageId":"Pine.LNX.4.64.0705290029420.4648@racer.site","threadId":"8057","inReplyTo":"20070528213511.GB7044@spearce.org","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-05-28T23:37:42Z","receivedAt":"2007-05-28T23:37:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 28 May 2007, Shawn O. Pearce wrote:\n\n> Junio C Hamano <junkio@cox.net> wrote:\n> > Johan Herland <johan@herland.net> writes:\n> > > Ok. But the reverse mapping will help with this, won't it?\n> > > We'll look up the interesting commits and find their associated\n> > > note objects directly.\n> > \n> > The issue Linus brought up worries me, too.\n> > \n> > The \"efficient reverse mapping\" is still handwaving at this\n> > stage.  What it needs to do is an equivalent to your\n> > implementation with \"refs/notes/<a dir per commit>/<note>\".  The\n> > \"efficient\" one might do a flat file that says \"notee note\" per\n> > line sorted by notee, or it might use BDB or sqlite, but the\n> > amount of the data and complexity of the look-up is really the\n> > same.  A handful notes per each commit in the history (I think\n> > Linus's \"Acked-by after the fact\" example a very sensible thing\n> > to want from this subsystem).\n> \n> Please, don't use BDB or sqllite.  I really don't trust either.\n> I've lost data to both.  I've *never* lost data to a Git packfile.\n> ;-)\n\nAlso, there is the question of dependencies. We don't have to repeat SVN's \nerrors.\n\nAs for sqllite: the only reason I was _not_ horrified when I realized that \ncvsserver needs sqllite was that it is not really important for the \ncommon user.\n\n> I'm actually thinking pack v4.  OK, I know its just a virtual hand\n> waving thing still, but there's really no reason Nico and I cannot\n> get the damn thing finished before we both wind up buying the farm.\n\nMaybe I am missing something important here, but I think we do not at all \nneed a generic database here. Just a key/value store.\n\nAs it happens, we already have such a beast. It is called object store \nhere.\n\nNow, the only reason we cannot do something like \"SHA-1 ^ 0xff[...]ff\" is \nthe SHA-1 for the note for that commit, is that you could possibly have \nmore than one note for the commit.\n\nOf course, without that restriction, we could reuse our object store logic \nfor notes.\n\nSide note: the more I think about this notes thing, the more I get \ndisgusted by the deep changes we'd have to do just to accomodate for them. \nAre they really worth it? Or would something like a pseudo-branch suffice, \nbeing one strand of notes (commits), where the second commit parent is \nactually the commit the note is for?\n\nCiao,\nDscho\n"},{"id":"43530","messageId":"200705290146.44914.johan@herland.net","threadId":"8057","inReplyTo":"7vps4kbrtb.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] Add fsck_verify_ref_to_tag_object() to verify that refname matches name stored in tag object","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-28T23:46:44Z","receivedAt":"2007-05-28T23:46:44Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Monday 28 May 2007, Junio C Hamano wrote:\n> However it would be a good\n> idea to add logic to fsck to warn upon inconsistencis (perhaps\n> by mistake) between refname and tag's true name.\n>\n> The check would say something like:\n>\n> \tIf an annotated (signed or unsigned) tag has a \"tag\"\n> \tline to give it the official $name, and if it is pointed\n> \tat by a ref, the refname must end with \"/$name\".\n> \tOtherwise we warn.\n>\n> Trivially, the above rule says that having v2.6.22 tag under\n> refs/tags/v2.6.20 is a mistake we would want to be warned upon.\n\nThis patch adds the check described by Junio.\n\nIt is assumed that the \"tag\" header is mandatory for all tag objects.\nThis might change in the future, at which point this patch should be\nrevised.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n\nI hope this is what you had in mind :)\n\n\nHave fun!\n\n...Johan\n\n builtin-fsck.c |   19 +++++++++++++++++++\n 1 files changed, 19 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-fsck.c b/builtin-fsck.c\nindex cbbcaf0..3594bd3 100644\n--- a/builtin-fsck.c\n+++ b/builtin-fsck.c\n@@ -501,6 +501,23 @@ static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, in\n \treturn 0;\n }\n \n+static void fsck_verify_ref_to_tag_object(const char *refname, struct object *obj)\n+{\n+\t/* Verify that refname matches the name stored in obj's \"tag\" header */\n+\tstruct tag *tagobj = (struct tag *) parse_object(obj->sha1);\n+\tsize_t tagname_len = strlen(tagobj->tag);\n+\tsize_t refname_len = strlen(refname);\n+\n+\tif (tagname_len < refname_len &&\n+\t    !memcmp(tagobj->tag, refname + (refname_len - tagname_len), tagname_len) &&\n+\t    refname[(refname_len - tagname_len) - 1] == '/') {\n+\t\t/* OK: tag name is \"$name\", and refname ends with \"/$name\" */\n+\t\treturn;\n+\t}\n+\telse\n+\t\terror(\"%s: Mismatch between tag ref and tag object's name %s\", refname, tagobj->tag);\n+}\n+\n static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tstruct object *obj;\n@@ -515,6 +532,8 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int f\n \t\t/* We'll continue with the rest despite the error.. */\n \t\treturn 0;\n \t}\n+\tif (obj->type == OBJ_TAG) /* ref to tag object */\n+\t\tfsck_verify_ref_to_tag_object(refname, obj);\n \tdefault_refs++;\n \tobj->used = 1;\n \tmark_reachable(obj, REACHABLE);\n-- \n1.5.2.87.g875de-dirty\n"},{"id":"43537","messageId":"alpine.LFD.0.98.0705282008400.26602@woody.linux-foundation.org","threadId":"8057","inReplyTo":"20070528213511.GB7044@spearce.org","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-05-29T03:12:29Z","receivedAt":"2007-05-29T03:12:29Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 28 May 2007, Shawn O. Pearce wrote:\n> \n> What if we use a \"slow\" storage by \"refs/notes/$objname/$notename\",\n\nThis _really_ won't scale. Even if the notes don't exist, just doing the \nlookup (which will fail for most commits) will be horribly slow, and will \npopulate the dentry cache with negative entries.\n\nTo get good filesystem performance, you have to\n - have reasonable hit-rates (and looking it up for each commit is _not_ \n   going to do that)\n - not have millions of objects.\n\nSo you'd have to have a separate database. You could do it with a separate \nindex file (or mixing it up with the \"index v4\" and doing it with a single \nindex file that also contains normal objects), but the point is, it's \ngoing to be a real separate database.\n\nThe current \"decorations\" approach is actually much more efficient with \npacked refs (dense in the filesystem), and probably not very bad at all \nfor a smallish set (ie a few hundred to a few thousand) objects, but just \nreading all the decorations ends up being an overhead at some point.\n\n\t\tLinus\n"},{"id":"43538","messageId":"20070529032221.GE7044@spearce.org","threadId":"8057","inReplyTo":"alpine.LFD.0.98.0705282008400.26602@woody.linux-foundation.org","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-05-29T03:22:21Z","receivedAt":"2007-05-29T03:22:21Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> wrote:\n> \n> On Mon, 28 May 2007, Shawn O. Pearce wrote:\n> > \n> > What if we use a \"slow\" storage by \"refs/notes/$objname/$notename\",\n> \n> This _really_ won't scale. Even if the notes don't exist, just doing the \n> lookup (which will fail for most commits) will be horribly slow, and will \n> populate the dentry cache with negative entries.\n\nYes.\n\nI think you missed what I was trying to say.  We *definately* do not\nwant to probe the OS and ask \"do you have $objname1? $objname2?\"\nfor exactly the reason you just stated.  (Though you being some\nsort of kernel guru means you know a hellva lot more about that\ndentry cache thing than I do!)\n\nWhat I meant was something more like:\n\n\tDIR *d = opendir(\"refs/notes\");\n\tif (d) {\n\t\t... notes can exist in both packfiles and \"loose\" ...\n\t\tonly_packed_notes = 0;\n\n\t\twhile (readdir(d)) {\n\t\t\t... the entry name here is the name of an object ...\n\t\t\t... stuff into a hash table, we can come back later ...\n\t\t\t... for the subdirectory contents if we need it ...\n\t\t}\n\t\tclosedir(d);\n\t\t... also examine \"packed-refs\", in case any are there ...\n\t} else {\n\t\tonly_packed_notes = 1;\n\t\t... we *only* have notes in packfiles, if we have any at all...\n\t}\n\nThen looking up a note is just a probe into our in-memory hash\n(if only_packed_notes is false) and a probe into the packfile(s) to\nfind the notes for the object.  Not very expensive if the packfiles\nhave the reverse obj->tag mappings indexed within them.\n \n> To get good filesystem performance, you have to\n>  - have reasonable hit-rates (and looking it up for each commit is _not_ \n>    going to do that)\n>  - not have millions of objects.\n\nWhich is why we:\n\n  a) allow these things to migrate into packed-refs, because\n  getting into there is a hellva lot cheaper than getting into\n  a packfile;\n\n  b) move them into a packfile when we repack loose objects,\n  because then we have really good access.\n\n  c) take them out of packed-refs once they are into a packfile,\n  and get them out of the loose refs/notes directory as early as\n  possible.\n\n-- \nShawn.\n"},{"id":"43543","messageId":"f3givk$1sn$1@sea.gmane.org","threadId":"8057","inReplyTo":"20070529032221.GE7044@spearce.org","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-05-29T07:04:09Z","receivedAt":"2007-05-29T07:04:09Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Shawn O. Pearce wrote:\n\n> Which is why we:\n> \n>   a) allow these things to migrate into packed-refs, because\n>   getting into there is a hellva lot cheaper than getting into\n>   a packfile;\n> \n>   b) move them into a packfile when we repack loose objects,\n>   because then we have really good access.\n> \n>   c) take them out of packed-refs once they are into a packfile,\n>   and get them out of the loose refs/notes directory as early as\n>   possible.\n\nThree-tier layout? Loose note refs -> packed refs -> packfile? \n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"44458","messageId":"200705290906.29328.johan@herland.net","threadId":"8057","inReplyTo":"7vwsysbrtg.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-29T07:06:29Z","receivedAt":"2007-05-29T07:06:29Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Monday 28 May 2007, Junio C Hamano wrote:\n> Johan Herland <johan@herland.net> writes:\n> > On Monday 28 May 2007, Linus Torvalds wrote:\n> >> On Mon, 28 May 2007, Johan Herland wrote:\n> >> > I still don't see what makes note objects inherently more expensive than\n> >> > commit objects. Except for the refs, of course, but we're getting rid\n> >> > of those (at least replacing them with a more efficient reverse mapping).\n> >> \n> >> It's exactly the refs that I worry about.\n> >> \n> >> Anything that needs to read in all notes at startup is going to be _slow_.\n> >> \n> >> In contrast, commits we read when (and only when) we need them.\n> >\n> > Ok. But the reverse mapping will help with this, won't it?\n> > We'll look up the interesting commits and find their associated\n> > note objects directly.\n> \n> The issue Linus brought up worries me, too.\n> \n> The \"efficient reverse mapping\" is still handwaving at this\n> stage.  What it needs to do is an equivalent to your\n> implementation with \"refs/notes/<a dir per commit>/<note>\".  The\n> \"efficient\" one might do a flat file that says \"notee note\" per\n> line sorted by notee, or it might use BDB or sqlite, but the\n> amount of the data and complexity of the look-up is really the\n> same.  A handful notes per each commit in the history (I think\n> Linus's \"Acked-by after the fact\" example a very sensible thing\n> to want from this subsystem).\n> \n> I am not saying that it is impossible to make the set-up cost\n> for the \"efficient lookup\" almost zero, and to make it lazy and\n> on-demand.  The concern above just adds one design constraints\n> to that \"efficient reverse mapping\" code yet to come.\n\nOk, here's what I'm thinking so far on that reverse mapping:\n\n1. Keep a file, \".git/reverse_tagmap_sorted\" with one entry of the form\n\"pointee pointer\" per line. The file is sorted on \"pointee\", so we can\neasily do the radix-256-fan-out-followed-by-binary-search trick that\nLinus mentioned in another thread. This should hopefully make lookup\nfairly cheap. BTW, if there is a similar \"pointee pointer\"-type format\nalready being used in git, I'd be happy to use that instead. I looked\nat the \"peeled\" format being used by packed-refs, but using that\ndirectly doesn't sound like a good idea, since the refname causes the\nentries to be of variable length, and the refnames are not interesting\nto me at all.\n\n2. Keep another file, \".git/reverse_tagmap_unsorted\" in front of (1).\nThis file has exactly the same format, minus the sorting. It exists just\nto make insertion cheap. Once this file reaches a certain size (i.e.\nwhen trawling it on lookup becomes slightly painful), we shuffle the\nentries into the sorted file (this would happen automatically on\ninsertion of an entry, and should _not_ have to be triggered by 'git-gc'\netc.).\n\n\nOf course, if we think insertion directly into (1) will never be too\nexpensive, we can drop (2) altogether.\n\nI don't know enough about packing to have a good idea on how to pack\nthese reverse tagmaps, but Shawn's thoughts about keeping associated\ntags/notes and objects close together makes a lot of sense. I'm just\nnot sure yet where these reverse tagmaps fit into the whole picture.\n\nCurrently, AFAICS, the packed-refs file is never propagated into the\npacks, but stays separate for the lifetime of the repo, but then it\nseems we're designing these reverse tagmaps for managing a handful of\nnotes per commit, i.e. to hold a couple of orders of magnitude more\nentries than the packed-refs file.\n\nMaybe each pack should keep the reverse tagmap for all the object->note\nrelationships internal to that pack? Everything else (unpacked notes,\nand object->note relationships spanning packs) would be kept in (1).\nOf course, when repacking, we'd try to keep objects and their notes\ntogether as much as possible, to maximize the in-pack reverse tagmap,\nand minimize the number of entries left behind in (1).\n\n\nHave fun!\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"43548","messageId":"20070529082245.GA15788@coredump.intra.peff.net","threadId":"8057","inReplyTo":"200705290906.29328.johan@herland.net","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2007-05-29T08:22:46Z","receivedAt":"2007-05-29T08:22:46Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, May 29, 2007 at 09:06:29AM +0200, Johan Herland wrote:\n\n> 1. Keep a file, \".git/reverse_tagmap_sorted\" with one entry of the form\n> \"pointee pointer\" per line. The file is sorted on \"pointee\", so we can\n> easily do the radix-256-fan-out-followed-by-binary-search trick that\n> Linus mentioned in another thread. This should hopefully make lookup\n> fairly cheap. BTW, if there is a similar \"pointee pointer\"-type format\n> already being used in git, I'd be happy to use that instead. I looked\n\nI did a similar thing (though rather than having \"lines\" at all, they\nwere fixed-length pairs of binary sha1 hashes) to implement a negative\ndelta cache (which turned out to be a stupid idea, but the\nimplementation worked):\n\nhttp://www.gelato.unsw.edu.au/archives/git/0606/23229.html\n\n> 2. Keep another file, \".git/reverse_tagmap_unsorted\" in front of (1).\n> This file has exactly the same format, minus the sorting. It exists just\n> to make insertion cheap. Once this file reaches a certain size (i.e.\n> when trawling it on lookup becomes slightly painful), we shuffle the\n> entries into the sorted file (this would happen automatically on\n> insertion of an entry, and should _not_ have to be triggered by 'git-gc'\n> etc.).\n\nThe implementation I mentioned above collects several \"to be inserted\"\nentries (in memory) and then merge sorts the two lists into a new file.\nSo it was fast in terms of comparisons, but it involved writing O(n)\nentries, which is probably bad for creating a single note.\n\n> Of course, if we think insertion directly into (1) will never be too\n> expensive, we can drop (2) altogether.\n\nYou can always find the right spot, memmove everything down one slot,\nand insert. But that means:\n  - the cost of insertion will be proportional to the number of items,\n    whereas using an unsorted journal you get to amortize that cost over\n    many insertions\n  - if you update in place, you have to lock the db for reading while\n    you are moving around. If you are always either appending to the\n    journal or merging into a new file, you can avoid this.\n\n-Peff\n"},{"id":"43550","messageId":"200705291123.54607.johan@herland.net","threadId":"8057","inReplyTo":"20070529082245.GA15788@coredump.intra.peff.net","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2007-05-29T09:23:54Z","receivedAt":"2007-05-29T09:23:54Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Tuesday 29 May 2007, Jeff King wrote:\n> On Tue, May 29, 2007 at 09:06:29AM +0200, Johan Herland wrote:\n> \n> > 1. Keep a file, \".git/reverse_tagmap_sorted\" with one entry of the form\n> > \"pointee pointer\" per line. The file is sorted on \"pointee\", so we can\n> > easily do the radix-256-fan-out-followed-by-binary-search trick that\n> > Linus mentioned in another thread. This should hopefully make lookup\n> > fairly cheap. BTW, if there is a similar \"pointee pointer\"-type format\n> > already being used in git, I'd be happy to use that instead. I looked\n> \n> I did a similar thing (though rather than having \"lines\" at all, they\n> were fixed-length pairs of binary sha1 hashes) to implement a negative\n> delta cache (which turned out to be a stupid idea, but the\n> implementation worked):\n> \n> http://www.gelato.unsw.edu.au/archives/git/0606/23229.html\n\nCool. It won't give a big-Oh improvement, but it might give a worthwile\nboost anyway. I guess it's the usual tradeoff between speed and\nmaintainability/transparency/readability.\n\n> > 2. Keep another file, \".git/reverse_tagmap_unsorted\" in front of (1).\n> > This file has exactly the same format, minus the sorting. It exists just\n> > to make insertion cheap. Once this file reaches a certain size (i.e.\n> > when trawling it on lookup becomes slightly painful), we shuffle the\n> > entries into the sorted file (this would happen automatically on\n> > insertion of an entry, and should _not_ have to be triggered by 'git-gc'\n> > etc.).\n> \n> The implementation I mentioned above collects several \"to be inserted\"\n> entries (in memory) and then merge sorts the two lists into a new file.\n> So it was fast in terms of comparisons, but it involved writing O(n)\n> entries, which is probably bad for creating a single note.\n> \n> > Of course, if we think insertion directly into (1) will never be too\n> > expensive, we can drop (2) altogether.\n> \n> You can always find the right spot, memmove everything down one slot,\n> and insert. But that means:\n>   - the cost of insertion will be proportional to the number of items,\n>     whereas using an unsorted journal you get to amortize that cost over\n>     many insertions\n>   - if you update in place, you have to lock the db for reading while\n>     you are moving around. If you are always either appending to the\n>     journal or merging into a new file, you can avoid this.\n\nYep, I'm thinking (2) will be worth it in the end. O(#notes) per note\ninsertion doesn't sound appealing at all, especially not if we're\nexpecting a handful of notes per commit.\n\n\nHave fun!\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"43557","messageId":"200705291204.10606.andyparkins@gmail.com","threadId":"8057","inReplyTo":"alpine.LFD.0.98.0705282008400.26602@woody.linux-foundation.org","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-05-29T11:04:06Z","receivedAt":"2007-05-29T11:04:06Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"On Tuesday 2007 May 29, Linus Torvalds wrote:\n\n> So you'd have to have a separate database. You could do it with a separate\n> index file (or mixing it up with the \"index v4\" and doing it with a single\n> index file that also contains normal objects), but the point is, it's\n> going to be a real separate database.\n\nThis is an off-the-wall suggestion; but why not use the object database a bit \nmore strongly than just for storing the notes?  The original method was to \nuse the filesystem to store refs/notes/$SHA1, which was essentially annotated \ntags.  The object database has been designed for storing thousands of \nhierarchically organised files, so why not use that instead of the refs/ \ntree?\n\nThat is to say, make a \"private\" branch, independent of any real repository \nbranches.  I'll call it \"notes\".  Then each commit in that branch represents \na change to a note (or multiple notes).  The tree attached to each commit \nprovides the reverse lookup that's wanted, and also maps to the object \nitself.  So where a normal tree has\n\n100644 blob 156e952df8603c72532bcda95ddcd3bcb16ec5fd somefile\n\nA \"notes\" tree would have\n\n100644 blob 156e952df8603c72532bcda95ddcd3bcb16ec5fd $SHA1_OF_TARGET_COMMIT1\n100644 blob 97e08b0ab483146cb8fff31138eaa828c24ac84f $SHA1_OF_TARGET_COMMIT2\n\nAll the \"notes\" tools could now use the standard git tools to look up a note.  \nThe notes would be version controlled (which is impressive in itself), and \nreverse lookup is the same as path-limited git-rev-list.\n\nThe tree objects are (I guess) stored alphabetically, so the reverse lookup \ncould be done with a binary search.  If a user really wanted to, they could \ncheck out the \"notes\" branch and edit the files directly.\n\nMadness?  I'm sure.\n\n\nAndy\n\n-- \nDr Andy Parkins, M Eng (hons), MIET\nandyparkins@gmail.com\n"},{"id":"43558","messageId":"Pine.LNX.4.64.0705291209510.4648@racer.site","threadId":"8057","inReplyTo":"200705291204.10606.andyparkins@gmail.com","subject":"Re: [PATCH 00/15] git-note: A mechanisim for providing free-form after-the-fact annotations on commits","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-05-29T11:12:03Z","receivedAt":"2007-05-29T11:12:03Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 29 May 2007, Andy Parkins wrote:\n\n> On Tuesday 2007 May 29, Linus Torvalds wrote:\n> \n> > So you'd have to have a separate database. You could do it with a \n> > separate index file (or mixing it up with the \"index v4\" and doing it \n> > with a single index file that also contains normal objects), but the \n> > point is, it's going to be a real separate database.\n> \n> This is an off-the-wall suggestion; but why not use the object database \n> a bit more strongly than just for storing the notes?\n\nI alluded to the same yesterday.\n\nHowever, just before waking up, I had the same idea as you, using a \nbranch.\n\n> So where a normal tree has\n> \n> 100644 blob 156e952df8603c72532bcda95ddcd3bcb16ec5fd somefile\n> \n> A \"notes\" tree would have\n> \n> 100644 blob 156e952df8603c72532bcda95ddcd3bcb16ec5fd $SHA1_OF_TARGET_COMMIT1\n> 100644 blob 97e08b0ab483146cb8fff31138eaa828c24ac84f $SHA1_OF_TARGET_COMMIT2\n\nActually, just throw in one fan-out stage, and you should be fine, \nperformance-wise.\n\nAnd what is best: you could merge notes from somebody else easily, \n_exactly_ because it is a proper branch now.\n\nCiao,\nDscho\n"}]}