{"thread":{"id":"6545","subject":"More precise tag following","startedAt":"2007-01-26T11:07:19Z","lastAt":"2007-02-09T07:41:14Z","messageCount":92,"participants":["Junio C Hamano","Shawn O. Pearce","Simon 'corecode' Schubert","Johannes Schindelin","Jeff King","Jakub Narebski","Linus Torvalds","Nicolas Pitre","Chris Lee","Theodore Tso","Randal L. Schwartz","René Scharfe","David Lang","Alex Riesen","Matthias Lederhofer","Eric Wong","David Kågedal","Peter Eriksen"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"32719","messageId":"7vy7nqxd08.fsf@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":null,"subject":"More precise tag following","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-26T11:07:19Z","receivedAt":"2007-01-26T11:07:19Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"What if (I know, this discussion does not belong here until\n1.5.0 final) we had a \"reverse\" database that keeps track of\nwhat tag references which object, and \"git rev-list\" knows how\nto exploit it?  That is, just like generating a list of objects\nthat are reachable with --objects option, if we can add a new\noption --with-tag very cheaply to list tag objects that would\nreach what are in the generated list of objects?\n\nThe way current git-fetch \"follows\" tags is very imprecise,\nalthough it is good enough in practice.  If you happen to\nlocally have an object that is tagged (and currently we get the\nlist of non-tag objects that tags eventually refer to in an\nout-of-band-ish way), then we fetch the tag and everything it\nreaches.  This means if you copied a single commit that is tagged\nfrom somewhere without objects that it refers to, we would end\nup fetching beyond that commit to complete it.  Which would not\nresult in a corrupted repository, but ideally we should not be\nfetching the tag in such a case.  And with something like\nenhanced rev-list that knows --with-tag it might be possible (I\nneed to think a bit more about have/want exchange and what\nshould happen later in fetch-pack and push-pack protocol,\nthough).\n\nThe application of this actually may not be limited to tag\nfollowing.  We could define a tag-like objects that attaches to\nother objects and enhance its meanings (annotates them) and\ntreat them the same way as tags for objects traversal and\ntransfer purposes, so if we were to do this, the option to\nexploit reverse database would be called --with-annotation and\nnot --with-tag.\n\n - If a single-path following turns out to be too expensive\n   (there was a longstanding talk about \"git log --single-follow\n   $path\"; \"git blame\" also follows a single path although the\n   target it follows can fork into two or more when following\n   cut&pastes) because we need to explode multi-level trees for\n   each commit while traversing commit ancestry, we could define\n   an annotation to a commit that lists the set of paths the\n   commit touches relative to each of its parents (so the object\n   contains N lists of paths), so that pathspec limiting needs\n   to open and read only one object to figure out that the trees\n   do not have to be opened to skip the commit and/or a merge\n   can be simplified.\n\n - We could define an annotation to a commit that describes what\n   fake parents it should have instead of the real ones\n   (i.e. grafts implemented in the object database).\n\nJust an idle thought.\n"},{"id":"32726","messageId":"7v8xfqxaur.fsf@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":"7vy7nqxd08.fsf@assigned-by-dhcp.cox.net","subject":"Re: More precise tag following","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-26T11:53:48Z","receivedAt":"2007-01-26T11:53:48Z","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> What if (I know, this discussion does not belong here until\n> 1.5.0 final) we had a \"reverse\" database that keeps track of\n> what tag references which object, and \"git rev-list\" knows how\n> to exploit it?  That is, just like generating a list of objects\n> that are reachable with --objects option, if we can add a new\n> option --with-tag very cheaply to list tag objects that would\n> reach what are in the generated list of objects?\n> ...\n> Just an idle thought.\n\nNah, that would not work.  Please disregard.\n\nThe thing is, you cannot sanely traverse and transfer objects\nthat have such reverse connectivity.  The other end can annotate\na commit long after you acquired it, and trying to make\ngit-fetch to retrieve newly created annotations would mean\nbreaking \"things reachable from your local refs are complete and\nthere is nothing missing from them\" invariant.  Pushing into\nsomebody else after annotating an old commit that already exists\nat the other end has the same issue.\n"},{"id":"32810","messageId":"20070127080126.GC9966@spearce.org","threadId":"6545","inReplyTo":"7vy7nqxd08.fsf@assigned-by-dhcp.cox.net","subject":"Re: More precise tag following","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-01-27T08:01:26Z","receivedAt":"2007-01-27T08:01:26Z","isPatch":false,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Junio C Hamano <junkio@cox.net> wrote:\n> What if (I know, this discussion does not belong here until\n> 1.5.0 final) we had a \"reverse\" database that keeps track of\n> what tag references which object, and \"git rev-list\" knows how\n> to exploit it?\n\nIt'd be useful.  In as many ways as you suggest.  But it also\nwould be downright difficult to transfer between repositories,\nas you have already pointed out in a public forum to yourself. :-)\n\n>  - If a single-path following turns out to be too expensive\n>    (there was a longstanding talk about \"git log --single-follow\n>    $path\"; \"git blame\" also follows a single path although the\n>    target it follows can fork into two or more when following\n>    cut&pastes) because we need to explode multi-level trees for\n>    each commit while traversing commit ancestry, we could define\n>    an annotation to a commit that lists the set of paths the\n>    commit touches relative to each of its parents (so the object\n>    contains N lists of paths), so that pathspec limiting needs\n>    to open and read only one object to figure out that the trees\n>    do not have to be opened to skip the commit and/or a merge\n>    can be simplified.\n\n_THIS_ is worth doing.  I've been having a lot of discussion on\n#git with Simon 'corecode' Schubert and Chris Lee about how poorly\ngit-blame performs compared to its counterpart in Subversion.\n\nBasically Subversion is storing file-level revision ancestry data\nwithin its commit data, allowing it to quickly skip back through\nthe commits which modified that file.\n\nGit doesn't have this information and must instead traverse the\nentire DAG, at least until the file was initially added to the tree.\nWith 440,000+ revisions and a file which exists in nearly all\nof them, getting anything from \"git-blame\" or \"git-log -- foo.c\"\ntakes ages.\n\nBased on some (limited) profiling with Shark it seems we spend about\n50% of our CPU time doing zlib decompression of objects and almost\nanother 14% parsing the tree objects to apply the path limiter.\nThe only way to speed up blame/log operations is to reduce the number\nof decompressions we need to do to the bare minium, and maybe also\nreduce the tree parsing overheads.  Do that and we can maybe drop\nthe running time to 1/4th the current time.\n\n\nOne idea Simon and I were talking about was to store a reverse\nfile/tree-level DAG in the header of each tree/blob object in the\npack file.  This additional header would be something like a list\nof triplets:\n\n  (descendant-commit, ancestor-commit, ancestor-object)\n\nwhere:\n\n  descendant-commit: the \"current\" commit being looked at.\n  ancestor-commit:   the commit which descendant-commit\n                     derives from (directly or indirectly)\n  ancestor-object:   prior version (descendant-commit^:path)\n\nThis triplet would probably be encoded with descendant-commit using\nOBJ_REF, ancestor-commit being an OBJ_OFS style back reference within\nthe pack (or OBJ_REF if not in this pack) and ancestor-object would\nalso be an OBJ_REF.  So a triplet probably would wind up costing\n~60 bytes.\n\nTriplets would only be stored if ancestor-object != this-object, so\nbasically only for the commits which changed the path the tree/blob\nis occupying in descendant-commit.\n\nFinding the prior revision of a tree or file would be a matter of\nfinding the triplet which matches the current commit, then jumping\nthrough to the ancestor-* values.  If no triplet matches the current\ncommit then we peel back the parents of the current commit and try\nagain with those.  Worst case we do what we do now, which is walk\nthe DAG.  ;-)\n\nThis of course penalizes objects which don't ever change, as we'd\nhave to walk back a good chunk of the DAG before we find a matching\ntriplet.  But I would suspect that files which never change are\nalso not given to log/blame very often either.  And once we do find\na triplet, we can skip through the DAG in time proportional to the\nrate of change for the path, rather than to the entire repository.\n\n\nThoughts?\n\n-- \nShawn.\n"},{"id":"32814","messageId":"7vbqkklv3h.fsf@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":"20070127080126.GC9966@spearce.org","subject":"Re: More precise tag following","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-27T08:41:54Z","receivedAt":"2007-01-27T08:41:54Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Shawn O. Pearce\" <spearce@spearce.org> writes:\n\n> Based on some (limited) profiling with Shark it seems we spend about\n> 50% of our CPU time doing zlib decompression of objects and almost\n> another 14% parsing the tree objects to apply the path limiter.\n\nI once tried to use zlib compression level 0 for tree objects\nand did not see much difference -- maybe I should dig it up and\nfind out why.\n\n> One idea Simon and I were talking about was to store a reverse\n> file/tree-level DAG in the header of each tree/blob object in the\n> pack file.\n> ...\n> Thoughts?\n\nAnything you would do, storing that in tree is wrong.  Tree\nobject only represents just the contents of a single state and\nin itself there should not be any information that describes its\nrelation with other trees [*1*].\n\nAnd of course making it pack-only is doubly wrong.\n\n\n*1* That's why my thinking-aloud talked about \"N list of changed\npaths recorded in a commit object with N parents\".  A commit is\nto talk about one particular state (i.e. tree) and its relation\nto other commits (and by indirection, other trees), so logically\nthe information could belong there --- that is merely a \"could\",\nsince that is strictly caching for performance.  After finding\nwhere the bottleneck is, obviously finding a way to optimize the\ntree pathlimiting with the currently available data without\nhaving such redundant data is more preferable.\n"},{"id":"32818","messageId":"45BB15B4.7030009@fs.ei.tum.de","threadId":"6545","inReplyTo":"20070127080126.GC9966@spearce.org","subject":"Re: More precise tag following","fromName":"Simon 'corecode' Schubert","fromEmail":"corecode@fs.ei.tum.de","sentAt":"2007-01-27T09:04:52Z","receivedAt":"2007-01-27T09:04:52Z","isPatch":false,"sender":{"key":"corecode@fs.ei.tum.de","avatar":"https://gravatar.com/avatar/eff9dbf0cdac0d1e6a6cd7ed0e50763edcb376b493b5253a35ff167918ad79e1?d=mp&s=160"},"body":"Shawn O. Pearce wrote:\n> This triplet would probably be encoded with descendant-commit using\n> OBJ_REF, ancestor-commit being an OBJ_OFS style back reference within\n> the pack (or OBJ_REF if not in this pack) and ancestor-object would\n> also be an OBJ_REF.  So a triplet probably would wind up costing\n> ~60 bytes.\n\nI'd propose the following:\n\nHave a \"shadow\" tree which is storing DAG information per entry of the original tree.  keep this shadow tree sorted in the same order the original tree object is.  for simplicity i will now add the new information to the tree examples, but of course this new data cannot be stored in the tree itself and can't be hashed.\n\ntree\n<filename> <mode> <object> (<new: ancestor commit> <new: ancestor object>)*\n\nthe ancestor object isn't necessary, it would just speed up annotations.  considering that we want to look only at the path, it is superfluous.  if we want to traverse copies/moves as well, this could be helpful.\n\nnow, this information allows us to build a object-level (read: path identifies object) DAG, by starting out on the current tree and retrieving the associated information.  using this it is possible to jump to the next commit/tree which changed the object and start over.\n\nexpense:  8 or 40 bytes per parent, per object, per commit, for each tree modified.\n\nper parent:  we of course store information about all parents\nper object:  as this is a \"shadow\" tree, we need to annotate all entries and not just changed ones\nfor each tree modified:  (sub)trees not being modified of course do not need annotation *again*\n\nbut:  this shadow tree can be deltified quite tightly, i'd say.  possibly with a different, specialized tree delta method.  then this boils down to 8 or 40 bytes per *changed* object, plus delta overhead.\n\n> This of course penalizes objects which don't ever change, as we'd\n> have to walk back a good chunk of the DAG before we find a matching\n> triplet.  But I would suspect that files which never change are\n> also not given to log/blame very often either.  And once we do find\n> a triplet, we can skip through the DAG in time proportional to the\n> rate of change for the path, rather than to the entire repository.\n\nusing my proposal this penalty does not exist.  i think it would be really awkward to have the annotation of a never-changed Makefile to take way longer than the operation on a recently/often changed file.\n\nwe'd have to compare the space requirements of both approaches.\n\ncheers\n  simon\n\n-- \nServe - BSD     +++  RENT this banner advert  +++    ASCII Ribbon   /\"\\\nWork - Mac      +++  space for low €€€ NOW!1  +++      Campaign     \\ /\nParty Enjoy Relax   |   http://dragonflybsd.org      Against  HTML   \\\nDude 2c 2 the max   !   http://golden-apple.biz       Mail + News   / \\\n\n"},{"id":"32832","messageId":"Pine.LNX.4.63.0701271352170.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6545","inReplyTo":"20070127080126.GC9966@spearce.org","subject":"Re: More precise tag following","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-01-27T12:58:37Z","receivedAt":"2007-01-27T12:58:37Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 27 Jan 2007, Shawn O. Pearce wrote:\n\n> I've been having a lot of discussion on #git with Simon 'corecode' \n> Schubert and Chris Lee about how poorly git-blame performs compared to \n> its counterpart in Subversion.\n\nWell, I don't run git-blame too often, and then it is mostly confined to a \ncertain file / lines combo. So git-blame is fast enough for me.\n\nIt is slower than Subversion's counterpart, just because SVN's blame \nsucks. You cannot find out the _relevant_ information easily, i.e. once \nyou merged something, the _merge_ gets attributed for the change (at least \nthe last time I tried it).\n\nSo, don't blame blame for being useful in git.\n\nOf course, you could introduce a cache, but then, I don't run blame _that_ \noften.\n\nBesides, we already introduced an orthogonal historisation by reflogs, and \nyour method would not cope gracefully with that, would it?\n\nCiao,\nDscho\n"},{"id":"32836","messageId":"20070127133352.GB2417@coredump.intra.peff.net","threadId":"6545","inReplyTo":"7vbqkklv3h.fsf@assigned-by-dhcp.cox.net","subject":"Re: More precise tag following","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2007-01-27T13:33:53Z","receivedAt":"2007-01-27T13:33:53Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Jan 27, 2007 at 12:41:54AM -0800, Junio C Hamano wrote:\n\n> > Based on some (limited) profiling with Shark it seems we spend about\n> > 50% of our CPU time doing zlib decompression of objects and almost\n> > another 14% parsing the tree objects to apply the path limiter.\n> \n> I once tried to use zlib compression level 0 for tree objects\n> and did not see much difference -- maybe I should dig it up and\n> find out why.\n\nI don't know exactly what Shawn meant, but a considerable amount of time\nin a blame is spent decompressing the blobs. Just for fun, some numbers:\n\nFully packed, warm cache, core.compression = -1:\n$ time git blame Makefile >/dev/null\nreal    0m5.537s\nuser    0m5.500s\nsys     0m0.032s\n\nFully packed, warm cache, core.compression = 0:\n$ time git blame Makefile >/dev/null\nreal    0m3.001s\nuser    0m2.984s\nsys     0m0.012s\n\nThat's 45% savings. The resulting pack sizes are 11932K compressed and\n22308 uncompressed.\n\n-Peff\n"},{"id":"32838","messageId":"45BB5888.9020608@fs.ei.tum.de","threadId":"6545","inReplyTo":"Pine.LNX.4.63.0701271352170.22628@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: More precise tag following","fromName":"Simon 'corecode' Schubert","fromEmail":"corecode@fs.ei.tum.de","sentAt":"2007-01-27T13:50:00Z","receivedAt":"2007-01-27T13:50:00Z","isPatch":false,"sender":{"key":"corecode@fs.ei.tum.de","avatar":"https://gravatar.com/avatar/eff9dbf0cdac0d1e6a6cd7ed0e50763edcb376b493b5253a35ff167918ad79e1?d=mp&s=160"},"body":"Johannes Schindelin wrote:\n> It is slower than Subversion's counterpart, just because SVN's blame \n> sucks. You cannot find out the _relevant_ information easily, i.e. once \n> you merged something, the _merge_ gets attributed for the change (at least \n> the last time I tried it).\n> \n> So, don't blame blame for being useful in git.\n\nYour reasoning is backwards.  Git's blame (or fwiw, rev-list path.name) is not slower because it is doing a better job (I can't tell, I don't use svn), but because it uses an algorithm which doesn't scale.  rev-list and blame are O(number of commits between HEAD and root) and not O(number of commits affecting path).  It might be sufficient for git.git, but certainly not for projects with a long history.  we are talking KDE, FreeBSD, OOo, something like this.  They each got about 400k commits.  It takes literally *minutes* to get a rev-list or a blame for a certain path.  The algorithm simply does not scale.  And this has nothing to do with superior output, because hg does it in O(num_of_file_revs), so it *can* be done.\n\n> Of course, you could introduce a cache, but then, I don't run blame _that_ \n> often.\n\nI don't think a cache is the right way.  I'd call the right idea \"auxillary information\".\n\n> Besides, we already introduced an orthogonal historisation by reflogs, and \n> your method would not cope gracefully with that, would it?\n\nI don't see how reflogs can play into this.  After all we're talking about the series of commits the blob experienced to get into its current state, not the series of actions it took this repo to contain this blob.\n\ncheers\n  simon\n\n-- \nServe - BSD     +++  RENT this banner advert  +++    ASCII Ribbon   /\"\\\nWork - Mac      +++  space for low €€€ NOW!1  +++      Campaign     \\ /\nParty Enjoy Relax   |   http://dragonflybsd.org      Against  HTML   \\\nDude 2c 2 the max   !   http://golden-apple.biz       Mail + News   / \\\n\n"},{"id":"32840","messageId":"epfulr$787$1@sea.gmane.org","threadId":"6545","inReplyTo":"45BB5888.9020608@fs.ei.tum.de","subject":"Re: More precise tag following","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-01-27T16:30:59Z","receivedAt":"2007-01-27T16:30:59Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Simon 'corecode' Schubert wrote:\n> Johannes Schindelin wrote:\n\n>> It is slower than Subversion's counterpart, just because SVN's blame \n>> sucks. You cannot find out the _relevant_ information easily, i.e. once \n>> you merged something, the _merge_ gets attributed for the change (at\n>> least the last time I tried it).\n>> \n>> So, don't blame blame for being useful in git.\n> \n> Your reasoning is backwards.  Git's blame (or fwiw, rev-list path.name)\n> is not slower because it is doing a better job (I can't tell, I don't\n> use svn), but because it uses an algorithm which doesn't scale.\n> rev-list and blame are O(number of commits between HEAD and root) and not\n> O(number of commits affecting path).  It might be sufficient for git.git,\n> but certainly not for projects with a long history.  we are talking KDE,\n> FreeBSD, OOo, something like this.  They each got about 400k commits.\n> It takes literally *minutes* to get a rev-list or a blame for a certain\n> path.  The algorithm simply does not scale.  And this has nothing to do\n> with superior output, because hg does it in O(num_of_file_revs), so it\n> *can* be done.          \n\nMercurial (hg) has different repository structure, with changesets in\nper filename \"buckets\", tied together with mainfest file and changelog\nfile. So it is easy to get per file history in hg, while it is harder\nto get per commit (general) history; contrary to git where it is easy\nto get per commit (general) history, and it is harder to get per file\nhistory.\n\nOn the other hand IIRC Mercurial, due to its repository structure, has some\nproblems with file copying and renames, not to mention more complicated \ncontents movement (of which git-blame is aware of). Perhaps this structure\nis/was also the cause that Mercurial is geared towards one branch per\nrepository workflow.\n \n>> Of course, you could introduce a cache, but then, I don't run blame\n>> _that_ often.\n> \n> I don't think a cache is the right way.  I'd call the right idea\n> \"auxillary information\". \n\nIf the information can be regenerated, this is cache. (Well, this is\none point of view).\n\nP.S. In git we can use so called pickaxe (options to git-diff/git-log)\nbesides using annotate/blame.\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"32842","messageId":"Pine.LNX.4.63.0701271728020.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6545","inReplyTo":"45BB5888.9020608@fs.ei.tum.de","subject":"Re: More precise tag following","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-01-27T16:46:14Z","receivedAt":"2007-01-27T16:46:14Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 27 Jan 2007, Simon 'corecode' Schubert wrote:\n\n> Johannes Schindelin wrote:\n> > It is slower than Subversion's counterpart, just because SVN's blame sucks.\n> > You cannot find out the _relevant_ information easily, i.e. once you merged\n> > something, the _merge_ gets attributed for the change (at least the last\n> > time I tried it).\n> > \n> > So, don't blame blame for being useful in git.\n> \n> Your reasoning is backwards.  Git's blame (or fwiw, rev-list path.name) \n> is not slower because it is doing a better job (I can't tell, I don't \n> use svn), but because it uses an algorithm which doesn't scale.  \n> rev-list and blame are O(number of commits between HEAD and root) and \n> not O(number of commits affecting path).\n\nAh, I think you fall in the \"files matter\" trap.\n\nMy point is: for what git does it does not need information which might or \nmight not be present, but it derives that information which was there from \nthe beginning: the ancestry path.\n\nMany people don't use or even need blame. And what you want to introduce \nwould affect them, too.\n\nThat is why I proposed a cache (of precomputed data): you don't have to \nchange _anything_ in the file format, but you can speed the processes up \n-- locally! -- if they matter to you.\n\nWhich means it works on old repositories, too.\n\n> It might be sufficient for git.git, but certainly not for projects with \n> a long history.  we are talking KDE, FreeBSD, OOo, something like this.  \n> They each got about 400k commits.  It takes literally *minutes* to get a \n> rev-list or a blame for a certain path.  The algorithm simply does not \n> scale.  And this has nothing to do with superior output, because hg does \n> it in O(num_of_file_revs), so it *can* be done.\n\nBut can hg do it that fast, if you track code _movement_ between files? I \ndoubt so.\n\nI don't know if git can, at the moment, but even if it cannot, in future \nversions this may well be possible, exactly because we do _not_ rely on \nmetadata to be stored in the objects, which can be derived from the \nhistory as-is anyway.\n\n> > Of course, you could introduce a cache, but then, I don't run blame \n> > _that_ often.\n> \n> I don't think a cache is the right way.  I'd call the right idea \n> \"auxillary information\".\n\nYou can name it \"Dirty Harry\" if you want.\n\nThe important part is that you should not change the file format when you \ndo not have to.\n\nRather, calculate the information you need from the existing data, and if \nyou can reuse it, store it locally. _That_ is flexibility.\n\nIt also gives me a warm fuzzy feeling that no bogus \"auxillary \ninformation\" can be introduced by fetching from somewhere else. (It does \nnot matter if intended or unintended.)\n\nAnd if something is wrong with that \"auxillary information\", it can be \nregenerated correctly, without touching the real data -- the commit \nancestry.\n\nJust think of .git/info/refs: this data is derived from the repository, \nbut because you need it so often (or it would be prohibitively expensive \nto do otherwise), it is derived only when needed, then stored, and \nretrieved quite often.\n\n> > Besides, we already introduced an orthogonal historisation by reflogs, \n> > and your method would not cope gracefully with that, would it?\n> \n> I don't see how reflogs can play into this.  After all we're talking \n> about the series of commits the blob experienced to get into its current \n> state, not the series of actions it took this repo to contain this blob.\n\nMy point was that you want to introduce a reverse mapping onto the history \nDAG. But this claims that there is only one history you can possibly look \nat. This assumption is wrong.\n\nIt can make a lot of sense to git-blame a change on a pull, maybe because \nyou don't want to fix it yourself, but throw it all back to the lieutnant \nwhom you pulled that part from.\n\nYou could find that pull (in theory; I don't think it works right now) \nwith git-blame walking the _reflogs_ instead of the _commit history_.\n\nIn this case, your reverse mapping would be wrong.\n\nSee?\n\nCiao,\nDscho\n"},{"id":"32845","messageId":"45BB87EB.7010200@fs.ei.tum.de","threadId":"6545","inReplyTo":"Pine.LNX.4.63.0701271728020.22628@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: More precise tag following","fromName":"Simon 'corecode' Schubert","fromEmail":"corecode@fs.ei.tum.de","sentAt":"2007-01-27T17:12:11Z","receivedAt":"2007-01-27T17:12:11Z","isPatch":false,"sender":{"key":"corecode@fs.ei.tum.de","avatar":"https://gravatar.com/avatar/eff9dbf0cdac0d1e6a6cd7ed0e50763edcb376b493b5253a35ff167918ad79e1?d=mp&s=160"},"body":"Johannes Schindelin wrote:\n> Ah, I think you fall in the \"files matter\" trap.\n> \n> My point is: for what git does it does not need information which might or \n> might not be present, but it derives that information which was there from \n> the beginning: the ancestry path.\n> \n> Many people don't use or even need blame. And what you want to introduce \n> would affect them, too.\n\nMany people do not use colored diffs.  Introducing colored diff support affects them, too.  In which way?  Additional command line switches, for example.  I don't think that's a big deal, and neither is a reverse map to create object-level DAGs.\n\n> That is why I proposed a cache (of precomputed data): you don't have to \n> change _anything_ in the file format, but you can speed the processes up \n> -- locally! -- if they matter to you.\n> \n> Which means it works on old repositories, too.\n\nMaybe I was not clear enough.  I do not propose to change the file format, but to extend the information stored.  In which way whatsoever.  However I think that keeping this information along with trees in pack files seems very sensible.  Or along pack files, whatever.\n\n>> It might be sufficient for git.git, but certainly not for projects with \n>> a long history.  we are talking KDE, FreeBSD, OOo, something like this.  \n>> They each got about 400k commits.  It takes literally *minutes* to get a \n>> rev-list or a blame for a certain path.  The algorithm simply does not \n>> scale.  And this has nothing to do with superior output, because hg does \n>> it in O(num_of_file_revs), so it *can* be done.\n> \n> But can hg do it that fast, if you track code _movement_ between files? I \n> doubt so.\n> \n> I don't know if git can, at the moment, but even if it cannot, in future \n> versions this may well be possible, exactly because we do _not_ rely on \n> metadata to be stored in the objects, which can be derived from the \n> history as-is anyway.\n\nPlease don't take the mentioning of hg as an attack on git.  You don't have to shoot back.  It was just to illustrate that this information can be used to speed up certain operations considerably.  Besides, I don't think that hg's repo format prevents it to do things which git can do.  Just some things might be less elegant or easy.\n\n> The important part is that you should not change the file format when you \n> do not have to.\n\nDo doubt.  Especially not in a way which breaks backwards compatibility.\n\n> Rather, calculate the information you need from the existing data, and if \n> you can reuse it, store it locally. _That_ is flexibility.\n\nOf course this is flexibility.  But this also means that every consumer has to do this for every repo.  Wouldn't it be nice to have it done one time and then stored in a pack?\n\n> It also gives me a warm fuzzy feeling that no bogus \"auxillary \n> information\" can be introduced by fetching from somewhere else. (It does \n> not matter if intended or unintended.)\n\nI agree on that.\n\n> And if something is wrong with that \"auxillary information\", it can be \n> regenerated correctly, without touching the real data -- the commit \n> ancestry.\n\nYes, it always can be regenerated.  I never said it should be made part of the core structure.\n\n>>> Besides, we already introduced an orthogonal historisation by reflogs, \n>>> and your method would not cope gracefully with that, would it?\n>> I don't see how reflogs can play into this.  After all we're talking \n>> about the series of commits the blob experienced to get into its current \n>> state, not the series of actions it took this repo to contain this blob.\n> My point was that you want to introduce a reverse mapping onto the history \n> DAG. But this claims that there is only one history you can possibly look \n> at. This assumption is wrong.\n\nThen you are reading it wrong.  It is just a way to speed up the common way of operation.  That doesn't mean that other ways stop working.  git-rev-list does one thing and you wouldn't call it not being gracefull, just because it doesn't operate on reflogs?\n\n> It can make a lot of sense to git-blame a change on a pull, maybe because \n> you don't want to fix it yourself, but throw it all back to the lieutnant \n> whom you pulled that part from.\n> \n> You could find that pull (in theory; I don't think it works right now) \n> with git-blame walking the _reflogs_ instead of the _commit history_.\n\nFair enough.  Nobody said that this wouldn't work anymore.  I just said that working on commit history could be sped up considerably.\n\ncheers\n  simon\n\n-- \nServe - BSD     +++  RENT this banner advert  +++    ASCII Ribbon   /\"\\\nWork - Mac      +++  space for low €€€ NOW!1  +++      Campaign     \\ /\nParty Enjoy Relax   |   http://dragonflybsd.org      Against  HTML   \\\nDude 2c 2 the max   !   http://golden-apple.biz       Mail + News   / \\\n\n"},{"id":"32848","messageId":"Pine.LNX.4.64.0701270837170.25027@woody.linux-foundation.org","threadId":"6545","inReplyTo":"20070127080126.GC9966@spearce.org","subject":"Re: More precise tag following","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-01-27T17:22:50Z","receivedAt":"2007-01-27T17:22:50Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 27 Jan 2007, Shawn O. Pearce wrote:\n> \n> _THIS_ is worth doing.  I've been having a lot of discussion on\n> #git with Simon 'corecode' Schubert and Chris Lee about how poorly\n> git-blame performs compared to its counterpart in Subversion.\n\nI think we're *much* better off trying to get people off the \"git-blame\" \nmentality entirely.\n\nDon't screw up git in trying to make \"git blame\" performance better.\n\nBecause you *will* screw it up. No ifs, buts, maybes or anything else \nabout it. The only way to make \"git blame\" perform better is to do a \nreally really crappy job.\n\nYou really have to fundamnetally realize that the reason SVN and CVS can \ndo \"annotate\" really cheaply is BECAUSE THEY ARE BROKEN. Trying to emulate \nthem is only going to break git too.\n\nReally. Let that thought sink in, and let it fester in your brain until \nyou really really get it.\n\nThere simply is no way to get \"git blame\" to be faster, without screwing \nthings up royally in any number of ways:\n\n - extra (redundant) on-the-fly-generated metadata\n\n   Yes, you can generate caches etc. Now you need to have a way to check \n   the caches, and to make sure that they are in sync. You also need to \n   update them constantly - you can make them look great in *benchmarks*, \n   but I bet that once you actually start developing, and really want to \n   _use_ them, you'll just curse the whole thing, because the caches won't \n   be there for the things you want. People normally don't do a million \n   \"blame\" operations on the same tree - they do *one*. Caches don't work.\n\n - extra (redundant) metadata generated at commit time\n\n   Instead of doing caches, you can do it statically at commit time, and \n   now you will screw up all the *other* things, like finding content \n   movement between files, and a dense and efficient repository encoding. \n   You'll need to add tons of crap to the commits (that most ops won't \n   find *any* use for), and you'll also make the operation a lot less \n   useful because it's now static rather than dynamic.\n\n> Thoughts?\n\nHere's a really fundamental suggestion:\n\nInstead of trying to do \"git blame\" faster, which is a totally broken \nnotion, just face the fact that emulating a broken environment will be \nslower. CPU's will get faster and help you in the long run, but more \nimportantly, if you just \"accept git\".\n\nSo instead, aim for:\n\n - \"yes, we can do blame, but yes, it will take five seconds for a biggish \n    archive on a reasonable CPU\".\n\n   Which implies that with a slow CPU, and a really humongous archive, it \n   will take much more. Is five seconds slow enough that people think it \n   is slow? Yes. Is 30 seconds approaching painful? Yes. But you should \n   try to aim for really just telling people that it's not a common \n   operation.\n\n   For example, I think it is a mistake to expose blame in \"gitweb\". It's \n   simply not a natural operation for git to do. Don't do it.\n\n   (Side note: for the kernel - which is certainly not a \"small\" project, \n   even if it's not a humongous one either, and the kinds of machines I \n   work with, git blame usually takes about 1-2 seconds for most files. \n   That is *not* excessive for a developer. It's excessive if you try to \n   do it on a web-server where you've made everybody and his dog press \n   \"history\" by putting a big red blinking button there..\n\n   In other words, aim for \"git blame\" being something that you run once a \n   week (which is about as often as I do it) or maybe a couple of times a \n   day if you're really obsessed. At which point a few seconds isn't that \n   horrid.\n\n - teach people about alternatives. For example, \"git log -p filename\" is \n   actually a hell of a lot more useful for most things. Yes, it's \n   *different*, but git makes it really easy, and it has the added \n   advantage that you see things in time order, and can very naturally \n   search back through time.\n\n   In a very similar vein, the real problem with \"git blame\" is not that \n   git cannot do it, but the fact that it's a \"whole history in one go\" \n   operation. Again, you can actually do a \"git blame\" that people would \n   probably find much less annoying, if you just did things \n   *incrementally*.\n\n   The reason \"git log -p filename\" doesn't perform badly is exactly that \n   it is incremental. Try it some time. The cost of\n\n\ttime git log -p mm/memory.c > /dev/null\n\ttime git blame mm/memory.c > /dev/null\n\n   is almost 100% identical when you run them that way. So why is it that \n   just about everybody would always consider \"git log -p\" to be \n   instantaneous with git, but \"git blame\" is slow?\n\nI'd really like people to think about that difference between \"git log -p \nfilename\" and \"git blame filename\". Because it tells you a lot about the \n*psychology* of the thing. They both take the same amount of time, but one \nis slow as hell, and the other one is so fast that anybody coming from the \nCVS world will just go \"Whoah! Magic!\".\n\nReally. Think about it.\n\nNow, think about what would happen if you had a graphical \"git blame\" that \nwas a tcl/tk thing (and slowed things down even more), but basically \nfilled in the \"git blame\" information incrementally - the exact same way \nthat \"git blame\" actually calculates it internally?\n\nYou know what? I bet that people would LIKE it. They could open up the \nfile in that nice graphical interface, and scroll down/search to the part \nthey care about, and see how git populates the blame. They'd think it's \n*cool*. And it would feel fast, because there wouldn't be any need to wait \nfor *all* the information before it's done.\n\nHere's a patch. Use \"git blame --incremental filename\" to get the blame \noutput in a nicely parseable format that you can now write a simple \ngraphical viewer for. \n\nPlease.\n\n\t\tLinus\n---\ndiff --git a/builtin-blame.c b/builtin-blame.c\nindex 4a1accf..7d97ae9 100644\n--- a/builtin-blame.c\n+++ b/builtin-blame.c\n@@ -27,6 +27,7 @@ static char blame_usage[] =\n \"  -p, --porcelain     Show in a format designed for machine consumption\\n\"\n \"  -L n,m              Process only line range n,m, counting from 1\\n\"\n \"  -M, -C              Find line movements within and across files\\n\"\n+\"  --incremental       Show blame entries as we find them, incrementally\\n\"\n \"  -S revs-file        Use revisions from revs-file instead of calling git-rev-list\\n\";\n \n static int longest_file;\n@@ -36,6 +37,7 @@ static int max_digits;\n static int max_score_digits;\n static int show_root;\n static int blank_boundary;\n+static int incremental;\n \n #ifndef DEBUG\n #define DEBUG 0\n@@ -1069,6 +1071,21 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \t\torigin_decref(parent_origin[i]);\n }\n \n+static void found_guilty_entry(struct blame_entry *ent)\n+{\n+\tif (ent->guilty)\n+\t\treturn;\n+\tent->guilty = 1;\n+\tif (incremental) {\n+\t\tstruct origin *origin = ent->suspect;\n+\t\tprintf(\"%d %d %s:%s:%d\\n\",\n+\t\t\tent->lno, ent->num_lines,\n+\t\t\tsha1_to_hex(origin->commit->object.sha1),\n+\t\t\torigin->path,\n+\t\t\tent->s_lno);\n+\t}\n+}\n+\n static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)\n {\n \twhile (1) {\n@@ -1102,7 +1119,7 @@ static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)\n \t\t/* Take responsibility for the remaining entries */\n \t\tfor (ent = sb->ent; ent; ent = ent->next)\n \t\t\tif (!cmp_suspect(ent->suspect, suspect))\n-\t\t\t\tent->guilty = 1;\n+\t\t\t\tfound_guilty_entry(ent);\n \t\torigin_decref(suspect);\n \n \t\tif (DEBUG) /* sanity */\n@@ -1717,6 +1734,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\t\t\tdie(\"More than one '-L n,m' option given\");\n \t\t\tbottomtop = arg;\n \t\t}\n+\t\telse if (!strcmp(\"--incremental\", arg))\n+\t\t\tincremental = 1;\n \t\telse if (!strcmp(\"--score-debug\", arg))\n \t\t\toutput_option |= OUTPUT_SHOW_SCORE;\n \t\telse if (!strcmp(\"-f\", arg) ||\n@@ -1907,6 +1926,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n \tassign_blame(&sb, &revs, opt);\n \n+\tif (incremental)\n+\t\treturn 0;\n+\n \tcoalesce(&sb);\n \n \tif (!(output_option & OUTPUT_PORCELAIN))\n"},{"id":"32856","messageId":"Pine.LNX.4.64.0701270925080.25027@woody.linux-foundation.org","threadId":"6545","inReplyTo":"epfulr$787$1@sea.gmane.org","subject":"Re: More precise tag following","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-01-27T17:36:39Z","receivedAt":"2007-01-27T17:36:39Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 27 Jan 2007, Jakub Narebski wrote:\n> \n> On the other hand IIRC Mercurial, due to its repository structure, has some\n> problems with file copying and renames\n\nThis is not a hg-only problem.\n\nThis is the SAME and FUNDAMENTAL problem that you have any time you think \n\"file identity\" matters.\n\nYes, it's what makes \"blame/annotate\" fast. But I have tried, over and \nover again, to explain why it's fundamentally broken (regardless of any \nblame thing).\n\nSo I will  just say once again: don't try to make \"blame\" faster. You can \nonly do so by introducing MUCH MORE serious problems in other parts.\n\nThis was a very early design decision in git. It was discussed within \nhours of me releasing the first version of git. Yes, \"git blame\" is \nrelatively slow, and it is very very fundamental. It's fundamental exactly \nbecause git avoids the mistake that *everybody* else has done.\n\nThe upside? I just sent a patch that should make it possible to do a \"cool \nblame\" that doesn't feel slow, and in fact allows some nice eyecandy \neffects (well, it would allow it, if I just knew tcl/tk or something like \nthat: it needs a canvas to draw the existing file into, and then filling \nin the incremental data as git-blame finds it).\n\nIt's going to be tons more fun to watch than any CVS/SVN annotate has ever \nbeen. Trust me. I bet that you'll feel that \"git blame\" is *too* fast, and \nyou'll want the graphical viewer to have a \"slow down\" flag, just so that \nyou can appreciate the blame building up!\n\n[ Ok, that may not be true for everybody, but having played with \"git \n   blame --incremental\" a bit I really think it would be a bit cool to \n   have that \"slow down\" mode, and start things with a really tiny font \n   so you could see the blame build up over a file!\n\n   I'm not kidding you. Eyecandy! And it literally would need git to slow \n   down to make it more human-friendly! ]\n\nThe other upside? EVERYTHING ELSE IS FASTER. And I really mean \n*everything*. Yes, \"git blame\" is slower than SVN. It will remain so, \nunless somebody either overrides my objections, or some alien intelligence \ncomes up with something _really_ clever. But look at it this way: blame \nmay take a few seconds, but that's a big part of why you can do merges of \nthings that have tens of thousands of files in half a second.\n\nThings that you would need to go brew a cup of coffee for in some other \nenvironments are basically _instantaneous_. \n\n\t\t\tLinus\n"},{"id":"32857","messageId":"Pine.LNX.4.64.0701271228270.3021@xanadu.home","threadId":"6545","inReplyTo":"7vbqkklv3h.fsf@assigned-by-dhcp.cox.net","subject":"Re: More precise tag following","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2007-01-27T17:47:44Z","receivedAt":"2007-01-27T17:47:44Z","isPatch":false,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"On Sat, 27 Jan 2007, Junio C Hamano wrote:\n\n> Anything you would do, storing that in tree is wrong.  Tree\n> object only represents just the contents of a single state and\n> in itself there should not be any information that describes its\n> relation with other trees [*1*].\n> \n> And of course making it pack-only is doubly wrong.\n> \n> \n> *1* That's why my thinking-aloud talked about \"N list of changed\n> paths recorded in a commit object with N parents\".  A commit is\n> to talk about one particular state (i.e. tree) and its relation\n> to other commits (and by indirection, other trees), so logically\n> the information could belong there --- that is merely a \"could\",\n> since that is strictly caching for performance.  After finding\n> where the bottleneck is, obviously finding a way to optimize the\n> tree pathlimiting with the currently available data without\n> having such redundant data is more preferable.\n\nI do think, too, that such data is not desirable in the object database.\n\nHowever there is nothing wrong with a separate \"cache\", just like the \npack index, that can be discarded and recreated at any time.  \nEspecially since this \"cache data\" might change with time as new tricks \nto speed up things are found.  OTOH it is preferable to keep the object \ndatabase as slick and stable as possible.\n\n\nNicolas\n"},{"id":"32858","messageId":"Pine.LNX.4.64.0701270945260.25027@woody.linux-foundation.org","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701270837170.25027@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-01-27T17:56:17Z","receivedAt":"2007-01-27T17:56:17Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 27 Jan 2007, Linus Torvalds wrote:\n> \n> Here's a patch. Use \"git blame --incremental filename\" to get the blame \n> output in a nicely parseable format that you can now write a simple \n> graphical viewer for. \n\nBtw, the more I play with this, the more I curse the fact that I don't \nknow tcltk enough to actually enjoy the full effect.\n\nBut in the kernel, with the patch I just sent out, try\n\n\tPAGER= git blame -C --incremental block/ll_rw_blk.c\n\n(because using a pager for the output just detracts from the whole \nexperience.\n\nI think it's really cool how it notices the chunks coming in from the \nrename, and starts giving different names - and for some reason I didn't \ndelve into it actually seems to find some of the changes from the old \nlocation before it have pinpointed all of the changes from the new one \netc..\n\n(That's a fairly expensive file to check, so it takes almost ten seconds \nfor me to generate blame for. But with the incremental output, you really \ndon't mind, because it finds the \"new changes\" immediately, so it's really \nthe old and relatively uninteresting stuff that will only be found at the \nend).\n\nThis should be an example of how important interfaces can be to what feels \n\"slow\".\n\nBtw, Junio - even if nobody writes a graphical front-end for that \n\"--incremental\" flag, this should be in 1.5.0. If only so that people \ncould write those front-ends and not have to patch git to get the blame \ninformation out of it.\n\nThe exact format for the output of this thing is obviously very debatable: \nit might well be worthwhile to use a more verbose thing that also gives \nthe commit name and date etc information (since git-blame knows it), so \nthat any graphical front-end doesn't need to look up every commit as it \ncomes out of the incremntal blame engine. I just wrote it as a quick \n\"proof of concept\" thing, and the output is obviously pretty minimalistic \nright now.\n\n\t\tLinus\n"},{"id":"32861","messageId":"45BB9C8B.8020907@fs.ei.tum.de","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701270837170.25027@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Simon 'corecode' Schubert","fromEmail":"corecode@fs.ei.tum.de","sentAt":"2007-01-27T18:40:11Z","receivedAt":"2007-01-27T18:40:11Z","isPatch":false,"sender":{"key":"corecode@fs.ei.tum.de","avatar":"https://gravatar.com/avatar/eff9dbf0cdac0d1e6a6cd7ed0e50763edcb376b493b5253a35ff167918ad79e1?d=mp&s=160"},"body":"Linus Torvalds wrote:\n> On Sat, 27 Jan 2007, Shawn O. Pearce wrote:\n>> _THIS_ is worth doing.  I've been having a lot of discussion on\n>> #git with Simon 'corecode' Schubert and Chris Lee about how poorly\n>> git-blame performs compared to its counterpart in Subversion.\n> I think we're *much* better off trying to get people off the \"git-blame\" \n> mentality entirely.\n> \n> Don't screw up git in trying to make \"git blame\" performance better.\n\nOkay, let's try to assume for now that nobody said \"git blame\".  Instead let's say:\n\ngit rev-list and git log (with or without -p) perform poorly when invoked with a pathspec.\n\n>    Which implies that with a slow CPU, and a really humongous archive, it \n>    will take much more. Is five seconds slow enough that people think it \n>    is slow? Yes. Is 30 seconds approaching painful? Yes. But you should \n>    try to aim for really just telling people that it's not a common \n>    operation.\n\nI agreee with those numbers.  However, on a converted KDE repo, they are *completely* different:\n\ngit log kdelibs/README takes 1:18.  One minute, eighteen seconds.\ngit rev-list and git blame take roughly the same time.\n\nThis particular file has 64 revisions.  However there are ~ 375000 revisions in the converted repo.\n\nMy and also Shawn's point was not about the speed of git blame itself.  It is about pathspec/rev operations.  The operation time does not scale with the number of changes to the file/object/call-it-whatever, but with the number of total commits in the branch.\n\nThat's what we were getting at.  Not the superiority of git blame (no irony) and thus reduced speed, but the algorithmic deficiency of any operation on a pathspec/object, which can be easily fixed.\n\ncheers\n  simon\n\n-- \nServe - BSD     +++  RENT this banner advert  +++    ASCII Ribbon   /\"\\\nWork - Mac      +++  space for low €€€ NOW!1  +++      Campaign     \\ /\nParty Enjoy Relax   |   http://dragonflybsd.org      Against  HTML   \\\nDude 2c 2 the max   !   http://golden-apple.biz       Mail + News   / \\\n\n"},{"id":"32862","messageId":"epg6vk$van$1@sea.gmane.org","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701270837170.25027@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-01-27T18:52:44Z","receivedAt":"2007-01-27T18:52:44Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Linus Torvalds wrote:\n\n> - teach people about alternatives.\n\nActually, \"git log -p -S'<string>'\" is usually much better alternative\nto blame / annotate to find who introduced given change and what for;\nmoreover you can find the <string> which vanished (find also deleted\ncontents).\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"32864","messageId":"Pine.LNX.4.63.0701271959000.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6545","inReplyTo":"45BB9C8B.8020907@fs.ei.tum.de","subject":"Re: More precise tag following","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-01-27T19:02:25Z","receivedAt":"2007-01-27T19:02:25Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 27 Jan 2007, Simon 'corecode' Schubert wrote:\n\n> Okay, let's try to assume for now that nobody said \"git blame\".  \n> Instead let's say:\n> \n> git rev-list and git log (with or without -p) perform poorly when \n> invoked with a pathspec.\n\nSo what? You _will_ be interested in the _newest_ changes _99%_ of the \ntime. And for these you don't need to wait 1:18, but 0:00.01 or so.\n\n> This particular file has 64 revisions.  However there are ~ 375000 \n> revisions in the converted repo.\n\n\"file version\" trap! \"file version\" trap! \"file version\" trap!\n\nCiao,\nDscho\n"},{"id":"32865","messageId":"45BBA405.6050409@fs.ei.tum.de","threadId":"6545","inReplyTo":"Pine.LNX.4.63.0701271959000.22628@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: More precise tag following","fromName":"Simon 'corecode' Schubert","fromEmail":"corecode@fs.ei.tum.de","sentAt":"2007-01-27T19:12:05Z","receivedAt":"2007-01-27T19:12:05Z","isPatch":false,"sender":{"key":"corecode@fs.ei.tum.de","avatar":"https://gravatar.com/avatar/eff9dbf0cdac0d1e6a6cd7ed0e50763edcb376b493b5253a35ff167918ad79e1?d=mp&s=160"},"body":"Johannes Schindelin wrote:\n>> Okay, let's try to assume for now that nobody said \"git blame\".  \n>> Instead let's say:\n>>\n>> git rev-list and git log (with or without -p) perform poorly when \n>> invoked with a pathspec.\n> \n> So what? You _will_ be interested in the _newest_ changes _99%_ of the \n> time. And for these you don't need to wait 1:18, but 0:00.01 or so.\n\nnot if you are interested which commit introduced/changed a particular line.\n\n>> This particular file has 64 revisions.  However there are ~ 375000 \n>> revisions in the converted repo.\n> \n> \"file version\" trap! \"file version\" trap! \"file version\" trap!\n\ncall it path and retry.\n\n-- \nServe - BSD     +++  RENT this banner advert  +++    ASCII Ribbon   /\"\\\nWork - Mac      +++  space for low €€€ NOW!1  +++      Campaign     \\ /\nParty Enjoy Relax   |   http://dragonflybsd.org      Against  HTML   \\\nDude 2c 2 the max   !   http://golden-apple.biz       Mail + News   / \\\n\n"},{"id":"32866","messageId":"Pine.LNX.4.63.0701272004250.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6545","inReplyTo":"45BB87EB.7010200@fs.ei.tum.de","subject":"Re: More precise tag following","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-01-27T19:13:29Z","receivedAt":"2007-01-27T19:13:29Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 27 Jan 2007, Simon 'corecode' Schubert wrote:\n\n> Johannes Schindelin wrote:\n> \n> > Many people don't use or even need blame. And what you want to \n> > introduce would affect them, too.\n> \n> Many people do not use colored diffs.  Introducing colored diff support \n> affects them, too.  In which way?  Additional command line switches, for \n> example.  I don't think that's a big deal, and neither is a reverse map \n> to create object-level DAGs.\n\nDo colored diffs need additional data?\n\nNo.\n\nThere's the principal difference between rev-pathspec-speed-up and \ncolored-diffs.\n\n> Please don't take the mentioning of hg as an attack on git.  You don't \n> have to shoot back.\n\nI did not take it as an attack on git! If it sounded like that, sorry.\n\n(Side note: If there is _any_ feature in other versioning systems I would \nlike to have in git, I'll try to implement it. If there is a VCS that is \nbetter than git, and which is free, I'll use it. Sorry, but that's the \nway it is.)\n\nI only tried to make it clear why we do not do things as Mercurial does it \n(in this particular case at least), and why I think that git's way is \nbetter.\n\n> > Rather, calculate the information you need from the existing data, and \n> > if you can reuse it, store it locally. _That_ is flexibility.\n> \n> Of course this is flexibility.  But this also means that every consumer \n> has to do this for every repo.  Wouldn't it be nice to have it done one \n> time and then stored in a pack?\n\nSo you want to store it in a pack, fetchable?\n\n> > It also gives me a warm fuzzy feeling that no bogus \"auxillary \n> > information\" can be introduced by fetching from somewhere else. (It \n> > does not matter if intended or unintended.)\n> \n> I agree on that.\n\nSo you agree we should _not_ store it in a pack, fetchable?\n\n> > And if something is wrong with that \"auxillary information\", it can be \n> > regenerated correctly, without touching the real data -- the commit \n> > ancestry.\n> \n> Yes, it always can be regenerated.  I never said it should be made part \n> of the core structure.\n\nSo, if you _do_ have it in a pack, fetchable, what happens if you \nregenerated it locally, fixing a flaw, but then fetch it from somewhere \nelse, where the flaw possibly still exists, what do you do?\n\nCiao,\nDscho\n"},{"id":"32867","messageId":"Pine.LNX.4.64.0701271103520.25027@woody.linux-foundation.org","threadId":"6545","inReplyTo":"45BB9C8B.8020907@fs.ei.tum.de","subject":"Re: More precise tag following","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-01-27T19:15:00Z","receivedAt":"2007-01-27T19:15:00Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 27 Jan 2007, Simon 'corecode' Schubert wrote:\n> \n> git rev-list and git log (with or without -p) perform poorly when invoked with\n> a pathspec.\n\nReally? I would say exactly the opposite. They _smoke_ when invoked with a \npathspec.\n\nShow me *one* other SCM that even comes close..\n\nAnd please, realize that git does arbitrary combinations of directories, \nand not just single files. AND THAT IS IMPORTANT!\n\nAny SCM that can't do\n\n\tgit log drivers/scsi/ include/scsi/\n\nand have it be a sane log of the changes to the _union_ of those two \ndirectories is strictly inferior to what git can do.\n\nUsually this is something that others CANNOT DO AT ALL.\n\nEven your 1:18 number is a hell of a lot faster than \"can't do it\", which \nis what you have for everything else I can imagine.\n\nMaybe you just do single files, but my pathspecs tend to be directories or \nmultiple files more often than single ones.\n\nHow the heck did you intend to cache that?\n\n> I agreee with those numbers.  However, on a converted KDE repo, they are\n> *completely* different:\n> \n> git log kdelibs/README takes 1:18.  One minute, eighteen seconds.\n> git rev-list and git blame take roughly the same time.\n\nDo you have the converted repo somewhere to be cloned for? It's going to \nbe a lot more interesting for scalability testing than anything else.\n\nIt is possible, for example, that the real issue is that we shouldn't \ncompress delta objects in a pack.\n\n> That's what we were getting at.  Not the superiority of git blame (no irony)\n> and thus reduced speed, but the algorithmic deficiency of any operation on a\n> pathspec/object, which can be easily fixed.\n\nThe thing is, one of the reasons the git object database is small is that \nit compresses really well, and I suspect that for the KDE repo, what \nyou're seeing is really a combination of:\n\n - the KDE people were idiots in the first place to make it into one big \n   repo\n\n - we've consciously made repo size be a major goal, and yes, we spend a \n   lot of CPU as a result, following delta chains etc. The zlib overhead \n   is more visible, because once you've uncompressed the delta the delta \n   itself is really quick to apply, but the whole \"trees compress really \n   well\" all boils down to the same thing: we create lots of small \n   objects, and we have tons of deltas, and the hierarchical nature of the \n   data structures (ie saving the trees not as one big manifest but as \n   a more complex hierarchial datastructure) is what allows us to do tons \n   of the path-based optimizations.\n\nBut they all do end up boiling down to \"we use lots of CPU\".\n\nAnd I suspect tweaking the existing stuff is quite reasonable. But we need \nto have a public repo that people who want to tweak can play with (for \nexample, the old \"linux-history\" archive was what made us tweak things \nlike gitk, which was horribly horribly bad).\n\nSo please point to a kde conversion archive to play with (maybe you have, \nI missed it).\n\n\t\tLinus\n"},{"id":"32868","messageId":"Pine.LNX.4.63.0701272017110.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6545","inReplyTo":"45BBA405.6050409@fs.ei.tum.de","subject":"Re: More precise tag following","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-01-27T19:19:12Z","receivedAt":"2007-01-27T19:19:12Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 27 Jan 2007, Simon 'corecode' Schubert wrote:\n\n> Johannes Schindelin wrote:\n> > > Okay, let's try to assume for now that nobody said \"git blame\".  \n> > > Instead let's say:\n> > > \n> > > git rev-list and git log (with or without -p) perform poorly when \n> > > invoked with a pathspec.\n> > \n> > So what? You _will_ be interested in the _newest_ changes _99%_ of the \n> > time. And for these you don't need to wait 1:18, but 0:00.01 or so.\n> \n> not if you are interested which commit introduced/changed a particular \n> line.\n\nWonderful. Say, you want to know who last changed the beginning of the \nfunction main() in git.c:\n\n\t$  git blame -L '/main(/,+20' git.c\n\nWhat was your point again?\n\n> > > This particular file has 64 revisions.  However there are ~ 375000 \n> > > revisions in the converted repo.\n> > \n> > \"file version\" trap! \"file version\" trap! \"file version\" trap!\n> \n> call it path and retry.\n\nDoes not matter. Not one wit. Your reasoning is still harping on \"file \nversions\".\n\nCiao,\nDscho\n"},{"id":"32870","messageId":"Pine.LNX.4.64.0701271119300.25027@woody.linux-foundation.org","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701271103520.25027@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-01-27T19:25:38Z","receivedAt":"2007-01-27T19:25:38Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 27 Jan 2007, Linus Torvalds wrote:\n>\n> Quoth Simon 'corecode' Schubert:\n> > git log kdelibs/README takes 1:18.  One minute, eighteen seconds.\n> > git rev-list and git blame take roughly the same time.\n\nBtw, why do people even think this is \"slow\"?\n\nYeah, we should speed it up, just because I think having that large a repo \nwill make it more obvious what we can do even better. No question about \nthat.\n\nBut I actually think that it's perfectly ok for \"whole-history\" operations \nto be slow. If you want the whole history of a big repository, you \nshouldn't expect it to be totally fast. And you should NOT expect the \nhistory for \"one file\" to be any faster than the history for \"the whole \nrepo\".\n\nBecause if you do, you have totally missed the whole point of git. It's \nnot a \"file tracker\". It's a *content* tracker. A file doesn't have \nhistory.\n\nSo what you're basically saying is that getting the whole history of KDE \ntakes just over a minute. That's pretty damned *fast*. And yes, we can \nmake it faster still.\n\nThe operations that git has been optimized for is that the size of the \nhistory shouldn't affect *new* stuff. \n\nBasically, asking for \"git log --since=1.week.ago\" should be \nconstant-time, regardless of how big the history is (well, it obviously \ndepends on how many changes there have been in the last week, but the \npoint is that it shouldn't get slower over time). And the git log output \nshould \"stream\", so that you can do \n\n\tgit log ..randomfile..\n\nand you'll always get speedy access to the stuff that happened recently, \nand we should *never* have to do the whole history just to get the recent \nchanges.\n\nThat's why \"git blame\" is so horrible. It's fundamentally an operation \nthat depends on \"whole history\" and thus cannot scale.\n\n\t\tLinus\n"},{"id":"32871","messageId":"204011cb0701271136m655815f6o1501de2bf699b362@mail.gmail.com","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701271103520.25027@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Chris Lee","fromEmail":"clee@kde.org","sentAt":"2007-01-27T19:36:50Z","receivedAt":"2007-01-27T19:36:50Z","isPatch":false,"sender":{"key":"clee@kde.org","avatar":"https://gravatar.com/avatar/c930bdc8cc6465094a5722188409ecb8955e0da2b188d7340137074b08f857e3?d=mp&s=160"},"body":"On 1/27/07, Linus Torvalds <torvalds@linux-foundation.org> wrote:\n> Do you have the converted repo somewhere to be cloned for? It's going to\n> be a lot more interesting for scalability testing than anything else.\n\nI don't have access to any servers that I could drop a 3GB packfile\nonto and expect them to serve it. And I don't have a connection at\nhome that I could use to upload the 3GB pack from quickly - it would\ntake days, at least. If anybody wants to hook me up with a hosting\nprovider or a machine that just the git devs can access, I'd be\nwilling to tie up my upstream bandwidth for a few days so you all can\nhave access to it.\n\n> The thing is, one of the reasons the git object database is small is that\n> it compresses really well, and I suspect that for the KDE repo, what\n> you're seeing is really a combination of:\n>\n>  - the KDE people were idiots in the first place to make it into one big\n>    repo\n\nNo argument from me about this one. The only defense I can really\nthink of is that, in KDE, we *have* moved entire applications and\nlibraries around between modules, and it is really nice to be able to\nhave the full history for them.\n\n> So please point to a kde conversion archive to play with (maybe you have,\n> I missed it).\n\nI can provide you with instructions on how to reconstruct one, but\nyou'll have to rsync over about 38GB of KDE's Subversion archive to do\nit. (Not fun.) If someone else wants to give me a dumping ground where\nI can upload my 3GB converted repo, I'd be happy to start pushing it.\n\nAlso, please note, the 3GB packed repo is only about 2/3 of the full\nKDE repo - I cut off the import at revision 409202, because that was\nwhen the KDE svn admins decided to move a bunch of modules from\n/trunk/ to /trunk/KDE/ and it screws up everything. So a *full* KDE\nhistory import would definitely be more than 4GB, packed.\n\n-clee\n"},{"id":"32872","messageId":"Pine.LNX.4.64.0701271430310.3021@xanadu.home","threadId":"6545","inReplyTo":"45BB87EB.7010200@fs.ei.tum.de","subject":"Re: More precise tag following","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2007-01-27T19:41:06Z","receivedAt":"2007-01-27T19:41:06Z","isPatch":false,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"On Sat, 27 Jan 2007, Simon 'corecode' Schubert wrote:\n\n> Maybe I was not clear enough.  I do not propose to change the file format, but\n> to extend the information stored.  In which way whatsoever.  However I think\n> that keeping this information along with trees in pack files seems very\n> sensible.  Or along pack files, whatever.\n\nAlong pack files please.\n\n> > Rather, calculate the information you need from the existing data, and if\n> > you can reuse it, store it locally. _That_ is flexibility.\n> \n> Of course this is flexibility.  But this also means that every consumer has to\n> do this for every repo.  Wouldn't it be nice to have it done one time and then\n> stored in a pack?\n\nNO!  That would mean that this extra information is now tied to the pack \nformat and this is not a good thing to depend on.\n\nEvery consumer is already recomputing the pack index locally for every \nrepo.  This has the advantage that we can change the pack index format \nas we so choose without having to bother with backward compatibility in \nthe pack transfer protocol.\n\n> > And if something is wrong with that \"auxillary information\", it can be\n> > regenerated correctly, without touching the real data -- the commit\n> > ancestry.\n> \n> Yes, it always can be regenerated.  I never said it should be made part of the\n> core structure.\n\nBut the pack format is pretty much part of the core structure.  If \nthings can be deduced from the pack without adding to it then they \nshould.  This way you have the freedom to experiment with any ancillary \nformat you wish.\n\n\nNicolas\n"},{"id":"32873","messageId":"epgaj2$bn9$1@sea.gmane.org","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701271119300.25027@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-01-27T19:54:11Z","receivedAt":"2007-01-27T19:54:11Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Linus Torvalds wrote:\n\n> On Sat, 27 Jan 2007, Linus Torvalds wrote:\n\n>> Quoth Simon 'corecode' Schubert:\n\n>>> git log kdelibs/README takes 1:18.  One minute, eighteen seconds.\n>>> git rev-list and git blame take roughly the same time.\n> \n> Btw, why do people even think this is \"slow\"?\n> \n> Yeah, we should speed it up, just because I think having that large a repo \n> will make it more obvious what we can do even better. No question about \n> that.\n[...]\n> Basically, asking for \"git log --since=1.week.ago\" should be \n> constant-time, regardless of how big the history is (well, it obviously \n> depends on how many changes there have been in the last week, but the \n> point is that it shouldn't get slower over time).\n[...]\n> That's why \"git blame\" is so horrible. It's fundamentally an operation \n> that depends on \"whole history\" and thus cannot scale.\n\nBy the way, in git-blame you can also give the cutoff like in git-log;\nthe lines which come from outside given revision range either get blamed\non boundary, or are shown \"unblamed\".\n\nI wonder if any other SCM's blame/annotate has that...\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"32874","messageId":"45BBAE3D.6000805@fs.ei.tum.de","threadId":"6545","inReplyTo":"Pine.LNX.4.63.0701272004250.22628@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: More precise tag following","fromName":"Simon 'corecode' Schubert","fromEmail":"corecode@fs.ei.tum.de","sentAt":"2007-01-27T19:55:41Z","receivedAt":"2007-01-27T19:55:41Z","isPatch":false,"sender":{"key":"corecode@fs.ei.tum.de","avatar":"https://gravatar.com/avatar/eff9dbf0cdac0d1e6a6cd7ed0e50763edcb376b493b5253a35ff167918ad79e1?d=mp&s=160"},"body":"Johannes Schindelin wrote:\n> So you want to store it in a pack, fetchable?\n\nOr wherever.  Main point was \"reusable\", but actually that depends on how long it takes to build the cache (okay, i'll call it cache).\n\n>>> It also gives me a warm fuzzy feeling that no bogus \"auxillary \n>>> information\" can be introduced by fetching from somewhere else. (It \n>>> does not matter if intended or unintended.)\n>> I agree on that.\n> \n> So you agree we should _not_ store it in a pack, fetchable?\n\nI agree that it had advantages if you can opt out.\n\n> So, if you _do_ have it in a pack, fetchable, what happens if you \n> regenerated it locally, fixing a flaw, but then fetch it from somewhere \n> else, where the flaw possibly still exists, what do you do?\n\nthe same what happens if you repack a pack locally.  the pack won't be re-fetched, thus your data won't be overwritten.\n\ncheers\n  simon\n\n-- \nServe - BSD     +++  RENT this banner advert  +++    ASCII Ribbon   /\"\\\nWork - Mac      +++  space for low €€€ NOW!1  +++      Campaign     \\ /\nParty Enjoy Relax   |   http://dragonflybsd.org      Against  HTML   \\\nDude 2c 2 the max   !   http://golden-apple.biz       Mail + News   / \\\n\n"},{"id":"32875","messageId":"epgask$bn9$2@sea.gmane.org","threadId":"6545","inReplyTo":"45BBA405.6050409@fs.ei.tum.de","subject":"Re: More precise tag following","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-01-27T19:59:25Z","receivedAt":"2007-01-27T19:59:25Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Simon 'corecode' Schubert wrote:\n> Johannes Schindelin wrote:\n\n>>> This particular file has 64 revisions.  However there are ~ 375000 \n>>> revisions in the converted repo.\n>> \n>> \"file version\" trap! \"file version\" trap! \"file version\" trap!\n> \n> call it path and retry.\n\nBy the way, if you don't mind be wrong in rare situation (file\nresurrecting), \"git log -p --remove-empty -- <filename>\" should\nspeed up things for new files at least.\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"32876","messageId":"Pine.LNX.4.64.0701271156260.25027@woody.linux-foundation.org","threadId":"6545","inReplyTo":"epgaj2$bn9$1@sea.gmane.org","subject":"Re: More precise tag following","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-01-27T20:13:08Z","receivedAt":"2007-01-27T20:13:08Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 27 Jan 2007, Jakub Narebski wrote:\n> \n> By the way, in git-blame you can also give the cutoff like in git-log;\n> the lines which come from outside given revision range either get blamed\n> on boundary, or are shown \"unblamed\".\n\nWell, that's not actually all that useful. It's ok when nothing else \nworks, but it would be nicer if it just acted well \"by default\".\n\nBecause You generally don't know a priori where your point of interest \nlies.\n\nThis is why \"git log -p\" (or \"git whatchanged\" before it) is so nice. A \nstreaming format means that you get the stuff you likely care about soon, \nbut if you aren't quite sure about where it was, it will come _eventually_ \nas you page down. And you can decide at any point in the middle that \"ok, \nthe thing I was looking for is obviously ancient\", which may end up \nchanging your whole outlook on a problem.\n\nWhich is why I think \"incremental\" things are so important.\n\nIn Sydney at Linux.conf.au I talked a bit to Paul Mackerras about gitk, \nand gitk is _fairly_ good at doing things incrementally (and apparently it \nis internally better at it than I have realized), but by default it still \npasses \"--topo-order\" to git-rev-list.\n\nWhich turns git-rev-list totally non-incremental, and makes gitk horrible \nto start up with default arguments (ie none) on a huge repository. If it \ntakes 1 minute to walk the whole history, then gitk will take a minute \nbefore it shows the first commit.\n\nPaulus was saying that it should be easily fixable, and that gitk \n*already* internally has a reorder buffer for commits out of topological \norder (for the \"--date-order\" thing, aka \"gitk -d\"), so gitk too should be \nable to stream perfectly well.\n\nAnd once you can stream, who cares how big the history is? The part that \nis old will take a long time, but people won't even see it, because \nthey'll be busy looking at the new parts that they saw immediately.\n\nSo this is why I tend to think that doing\n\n\ttime <fundamental git operation>\n\nis actually not all that interesting. It's a *lot* more interesting in \nmany cases to do\n\n\n\ttime <fundamental git operation> | head\n\nbecause that gives a much more accurate view of what the user experience \nis like.\n\nTo get back to the patch I sent out to \"git blame\", just to illustrate \nthis issue:\n\n\t[torvalds@woody linux]$ time git blame --incremental -C block/ll_rw_blk.c > /dev/null\n\treal    0m8.540s\n\tuser    0m8.109s\n\tsys     0m0.432s\n\nvs\n\n\t[torvalds@woody linux]$ time git blame --incremental -C block/ll_rw_blk.c | head > /dev/null\n\treal    0m0.238s\n\tuser    0m0.240s\n\tsys     0m0.004s\n\nand 8.5 seconds is a _loong_ time even for a human, but 0.24 seconds is \n\"instant\". THAT is the difference between \"streaming\" and \"non-streaming\".\n\nFor a similar example, and seeing why \"topo-order\" is problematic, just \ntry this:\n\n\t[torvalds@woody linux]$ time git rev-list --all | head > /dev/null\n\treal    0m0.007s\n\tuser    0m0.000s\n\tsys     0m0.012s\n\nvs\n\n\t[torvalds@woody linux]$ time git rev-list --topo-order --all | head > /dev/null\n\treal    0m1.058s\n\tuser    0m1.028s\n\tsys     0m0.036s\n\nand note how they both just time the first few lines: one takes basically \nno time at all (it's fast *and* streaming) and the other one takes over a \nsecond (it gets the whole kernel history and then sorts it - so it can't \nstream. A second is still fast for \"whole history\", but the lack of \nstreaming means that it's two orders of magnitude slower IN PRACTICE).\n\nSo it's really the *second* case we want to avoid. We want to avoid \nteaching people bad manners, and here \"bad manners\" is not \"having large \nrepositories with lots of history\", but simply means \"do operations that \nfundamentally depend on all of history\".\n\nThis is why I would much prefer the \"--incremental\" blame. Suddenly, that \nturns \"git blame\" from a non-streaming (and thus fundamentally broken) \noperation into something that streams and can thus have a nice user \nexperience.\n\n\t\t\tLinus\n"},{"id":"32877","messageId":"20070127201640.GA25637@coredump.intra.peff.net","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701270837170.25027@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2007-01-27T20:16:41Z","receivedAt":"2007-01-27T20:16:41Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Jan 27, 2007 at 09:22:50AM -0800, Linus Torvalds wrote:\n\n> Here's a patch. Use \"git blame --incremental filename\" to get the blame \n> output in a nicely parseable format that you can now write a simple \n> graphical viewer for. \n\nAnd here's a (very hackish) incremental viewer using perl/gtk (you will\nneed the Gtk2 perl module installed). It doesn't take any options, and it\njust shows the blame output (no lookup of committer/date).\n\nIt needs much work (and cleanup) to be useful, but I think it proves\nyour point: in the time it takes me to actually start looking through\nthe output, the blame has finished without me noticing!\n\n-Peff\n\n-- >8 --\n#!/usr/bin/perl\n\nuse Gtk2 -init;\nuse Gtk2::SimpleList;\n\nmy $fn = shift or die \"require filename to blame\";\n\nmy $window = Gtk2::Window->new('toplevel');\n$window->signal_connect(destroy => sub { Gtk2->main_quit });\nmy $scrolled_window = Gtk2::ScrolledWindow->new;\n$window->add($scrolled_window);\nmy $fileview = Gtk2::SimpleList->new(\n    'Commit' => 'text',\n    'FileLine' => 'text',\n    'Data' => 'text'\n);\n$scrolled_window->add($fileview);\n$fileview->get_column(0)->set_spacing(0);\n$fileview->set_size_request(1024, 768);\n\nopen(my $fh, '<', $fn)\n  or die \"unable to open $fn: $!\";\nwhile(<$fh>) {\n  chomp;\n  $fileview->{data}->[$.] = ['HEAD', \"$fn:$.\", $_];\n}\n\nopen(my $blame, '-|', qw(git blame --incremental), $fn)\n  or die \"unable to open git blame: $!\";\nGlib::IO->add_watch(fileno($blame), 'in', \\&read_blame_line);\n\n$window->show_all;\nGtk2->main;\nexit 0;\n\nmy $buf;\nsub read_blame_line {\n  my $r = sysread($blame, $buf, 1024, length($buf));\n  return 0 unless $r;\n  while($buf =~ s/([^\\n]*)\\n//) {\n    my $line = $1;\n    $line =~ /^(\\d+) (\\d+) ([0-9a-f]+):(.*):(\\d+)$/\n      or die \"bad blame output: $line\";\n    for(my $i = 0; $i < $2; $i++) {\n      @{$fileview->{data}->[$1+$i]}[0,1] =\n        (substr($3, 0, 8), $4 . ':' . ($5+$i+1));\n    }\n  }\n  return 1;\n}\n"},{"id":"32880","messageId":"7vzm84gmei.fsf@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701270945260.25027@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-27T22:00:53Z","receivedAt":"2007-01-27T22:00:53Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> This should be an example of how important interfaces can be to what feels \n> \"slow\".\n\nI learn new thing (or relearn things I knew but did not apply in\nmy argument myself) every day from you, and this is a prime\nexample ;-).  I agree that latency in giving feedback helps the\nfeel of speed, and the feel is the most important thing in UI.\n\n> Btw, Junio - even if nobody writes a graphical front-end for that \n> \"--incremental\" flag, this should be in 1.5.0. If only so that people \n> could write those front-ends and not have to patch git to get the blame \n> information out of it.\n>\n> The exact format for the output of this thing is obviously very debatable: \n> it might well be worthwhile to use a more verbose thing that also gives \n> the commit name and date etc information (since git-blame knows it), so \n> that any graphical front-end doesn't need to look up every commit as it \n> comes out of the incremntal blame engine. I just wrote it as a quick \n> \"proof of concept\" thing, and the output is obviously pretty minimalistic \n> right now.\n\nI would think we probably should reuse the --porcelain output,\nperhaps enhancing it even more.\n"},{"id":"32885","messageId":"Pine.LNX.4.64.0701271432450.25027@woody.linux-foundation.org","threadId":"6545","inReplyTo":"20070127201640.GA25637@coredump.intra.peff.net","subject":"Re: More precise tag following","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-01-27T22:39:30Z","receivedAt":"2007-01-27T22:39:30Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 27 Jan 2007, Jeff King wrote:\n> \n> And here's a (very hackish) incremental viewer using perl/gtk (you will\n> need the Gtk2 perl module installed). It doesn't take any options, and it\n> just shows the blame output (no lookup of committer/date).\n\nHeh. Ok, this is absolutely the ugliest thing I have ever seen, but after \ndoing a simple \"yum install perl-Gtk2\" it clearly does work ;)\n\n(For some reason I have never fathomed, gtk apps seem to always like \nadding about a mile of empty space around lines. I want my text nice and \ntight - gtk menus and text always look like it's 1½ line spacing to me. \nWhich may be ok when you're writing a paper and want space to add your \nscribbles and underlining etc, but not when you're looking at the screen, \nand it just means that there's *less* space for commentary).\n\n> It needs much work (and cleanup) to be useful, but I think it proves\n> your point: in the time it takes me to actually start looking through\n> the output, the blame has finished without me noticing!\n\nYeah. It needs the logic to coalesce consecutive file-name/line-nr \nentries, but even after you add a \"-C\" to the arguments (which really \nmakes 'git-blame' quite a bit more expensive), it doesn't feel \"slow\". \n\nJust ugly ;)\n\n\t\tLinus \"shouldn't throw stones when anything\n\t\t\tI would have done would have been\n\t\t\tuglier still\" Torvalds\n\t\t\t"},{"id":"32886","messageId":"Pine.LNX.4.64.0701271439340.25027@woody.linux-foundation.org","threadId":"6545","inReplyTo":"7vzm84gmei.fsf@assigned-by-dhcp.cox.net","subject":"Re: More precise tag following","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-01-27T22:54:10Z","receivedAt":"2007-01-27T22:54:10Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 27 Jan 2007, Junio C Hamano wrote:\n> \n> I would think we probably should reuse the --porcelain output,\n> perhaps enhancing it even more.\n\nI looked at using \"emit_porcelain()\" directly, but that format doesn't \nseem to actually be usable for incremental blame.\n\nFor example: the porcelain depends on things like the MORE_THAN_ONE_PATH \nflag having been computed, which simply isn't known incrementally.\n\nAlso, for the incremental blame, it makes no sense to actually print out \nthe actual blame buffer: anybody who uses the incremental blame thing \nreally needs to get the original buffer separately set up anyway.\n\nSo one one hand, I agree: the output really should probably share a lot of \nthe ideas with --porcelain. At the same time, the porcelain output as it \nis now is actually very non-sensible for the incremental case.\n\n(The \"METAINFO_SHOWN\" kind of logic works fine for --incremental, though. \nIt's only the MORE_THAN_ONE_PATH things that don't really make sense until \nthe end, since they are part of the discovery logic rather than part of \nthe actual print-out logic. I guess it _works_, but still).\n\nI think the people who will care are the people who actually write some \nnice gui around it..\n\n\t\tLinus\n"},{"id":"32887","messageId":"20070127235238.GA28706@coredump.intra.peff.net","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701271432450.25027@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2007-01-27T23:52:38Z","receivedAt":"2007-01-27T23:52:38Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Jan 27, 2007 at 02:39:30PM -0800, Linus Torvalds wrote:\n\n> (For some reason I have never fathomed, gtk apps seem to always like \n> adding about a mile of empty space around lines. I want my text nice and \n> tight - gtk menus and text always look like it's 1½ line spacing to me. \n> Which may be ok when you're writing a paper and want space to add your \n> scribbles and underlining etc, but not when you're looking at the screen, \n> and it just means that there's *less* space for commentary).\n\nYes, I spent quite a bit of time trying to correct this, but it seems to\nbe an artifact of the GtkTreeView widget (which maybe I am abusing, but\nit seems like the right thing conceptually). As the vertical spacing is\na \"style\" attribute, I'm not as a developer allowed to change it. You\ncan try this, which helps a bit (the default is '2'); put it before any\nwidgets are created:\n\nGtk2::Rc->parse_string(<<'EOS');\nstyle \"treeview_style\"\n{\n  GtkTreeView::vertical-separator = 0\n}\nclass \"GtkTreeView\" style \"treeview_style\"\nEOS\n\nBut it's still pretty ugly. I'm not inclined to hack on it much more --\nIMHO, a nice curses interface like tig would be much more sensible.\n\n> Yeah. It needs the logic to coalesce consecutive file-name/line-nr \n> entries, but even after you add a \"-C\" to the arguments (which really \n> makes 'git-blame' quite a bit more expensive), it doesn't feel \"slow\". \n\nYes, it would ideally color the blobs (try adding\n$fileview->set_rules_hint(1) to get alternating line colors!). But I\ndon't think I can do anything that magical with their widget, which\nmeans I get to write my own widget. Bleh.\n\n> Just ugly ;)\n\n:)\n\nHere's my \"final\" version that looks up the committer name. It also\ntakes blame output on the command line:\n  git-blame --incremental -C file | perl foo.pl file\nI won't be working on it anymore, but if somebody wants to see an\nexample of some really ugly perl/gtk code, here it is.\n\n-Peff\n\n-- >8 --\n#!/usr/bin/perl\n\nuse Gtk2 -init;\nuse Gtk2::SimpleList;\n\nmy $fn = shift or die \"require filename to blame\";\n\nGtk2::Rc->parse_string(<<'EOS');\nstyle \"treeview_style\"\n{\n  GtkTreeView::vertical-separator = 0\n}\nclass \"GtkTreeView\" style \"treeview_style\"\nEOS\n\nmy $window = Gtk2::Window->new('toplevel');\n$window->signal_connect(destroy => sub { Gtk2->main_quit });\nmy $scrolled_window = Gtk2::ScrolledWindow->new;\n$window->add($scrolled_window);\nmy $fileview = Gtk2::SimpleList->new(\n    'Commit' => 'text',\n    'CommitInfo' => 'text',\n    'FileLine' => 'text',\n    'Data' => 'text'\n);\n$scrolled_window->add($fileview);\n$fileview->get_column(0)->set_spacing(0);\n$fileview->set_size_request(1024, 768);\n$fileview->set_rules_hint(1);\n\nopen(my $fh, '<', $fn)\n  or die \"unable to open $fn: $!\";\nwhile(<$fh>) {\n  chomp;\n  $fileview->{data}->[$.] = ['HEAD', '?', \"$fn:$.\", $_];\n}\n\nGlib::IO->add_watch(fileno(STDIN), 'in', \\&read_blame_line);\n\n$window->show_all;\nGtk2->main;\nexit 0;\n\nmy $buf;\nsub read_blame_line {\n  my $r = sysread(STDIN, $buf, 1024, length($buf));\n  return 0 unless $r;\n  while($buf =~ s/([^\\n]*)\\n//) {\n    my $line = $1;\n    $line =~ /^(\\d+) (\\d+) ([0-9a-f]+):(.*):(\\d+)$/\n      or die \"bad blame output: $line\";\n    my $info = commitinfo($3);\n    for(my $i = 0; $i < $2; $i++) {\n      @{$fileview->{data}->[$1+$i]}[0,1] =\n        (substr($3, 0, 8), $info, $4 . ':' . ($5+$i+1));\n    }\n  }\n  return 1;\n}\n\nsub commitinfo {\n  my $hash = shift;\n  open(my $fh, '-|', qw(git rev-list -1 --pretty=raw), $hash)\n    or die \"unable to open git-rev-list: $!\";\n  while(<$fh>) {\n    chomp;\n    next unless /^author (.*) <.*> (\\d+) ([+-]\\d+)/;\n    return $1 . ' ' . format_time($2, $3);\n  }\n}\n\nsub format_time {\n  my $time = shift;\n  my $tz = shift;\n\n  my $minutes = $tz < 0 ? 0-$tz : $tz;\n  $minutes = ($minutes / 100)*60 + ($minutes % 100);\n  $minutes = $tz < 0 ? 0-$minutes : $minutes;\n  $time += $minutes * 60;\n  my @t = gmtime($time);\n  return sprintf('%04d-%02d-%02d %02d:%02d:%02d %s', @t[5,4,3,2,1,0], $tz);\n}\n"},{"id":"32893","messageId":"20070128023958.GF9897@thunk.org","threadId":"6545","inReplyTo":"20070127235238.GA28706@coredump.intra.peff.net","subject":"Re: More precise tag following","fromName":"Theodore Tso","fromEmail":"tytso@mit.edu","sentAt":"2007-01-28T02:39:58Z","receivedAt":"2007-01-28T02:39:58Z","isPatch":false,"sender":{"key":"tytso@mit.edu","avatar":"https://avatars.githubusercontent.com/u/51416?v=4"},"body":"On Sat, Jan 27, 2007 at 06:52:38PM -0500, Jeff King wrote:\n> But it's still pretty ugly. I'm not inclined to hack on it much more --\n> IMHO, a nice curses interface like tig would be much more sensible.\n\nFor emacs users, it would even be better to tie it into emacs.  That\nway you're already at the line number looking at the source code when\nyou start wondering, \"who the f*ck created this mess?\".  The way I'd\ndesign it would to have emacs split the window vertically, and place\nthe blame information in a buffer whose scrolling was synchronized\nwith the main window.  \n\nOne of the things that I noticed myself doing (and I'm guessing many\nother people would do it as well), is that you have a tendency to wait\nuntil the attribution information has started filling in before you\nstart scrolling to the part of the code you were interested in --- and\nsince the beginning of the fle often hasn't changed much until the\nearliest beginnings of the project, that can be quite a while.  And of\ncourse, scrolling to the right part of the file is a pain.  So\nbuilding it into the editor is not only convenient, but it avoids the\npsychological effects that could make it seem slow because how long it\ntakes to fill the attribution for these first bits:\n\n/*\n * Copyright (C) 1991, 1992 Linus Torvalds\n * Copyright (C) 1994,      Karl Keyte: Added support for disk statistics\n * Elevator latency, (C) 2000  Andrea Arcangeli <andrea@suse.de> SuSE\n * Queue request tables / lock, selectable elevator, Jens Axboe <axboe@suse.de>\n * kernel-doc documentation started by NeilBrown <neilb@cse.unsw.edu.au> -  July2000\n * bio rewrite, highmem i/o, etc, Jens Axboe <axboe@suse.de> - may 2001\n */\n\n\nAnyway, this should be relatively easily for emacs, and for eclipse\n(although I don't think anyone is using eclipse to do Kernel hacking,\nis there?).  I have no idea how you would hack this into Vim, other\nthan passing the line number into the GUI so it can open right into\nthe function that the developer was looking at.\n\n\t\t\t\t\t\t- Ted\n"},{"id":"32896","messageId":"86mz43hmar.fsf@blue.stonehenge.com","threadId":"6545","inReplyTo":"20070128023958.GF9897@thunk.org","subject":"Re: More precise tag following","fromName":"Randal L. Schwartz","fromEmail":"merlyn@stonehenge.com","sentAt":"2007-01-28T03:17:48Z","receivedAt":"2007-01-28T03:17:48Z","isPatch":false,"sender":{"key":"merlyn@stonehenge.com","avatar":"https://gravatar.com/avatar/dc528d210743ff0333e6213f9ee7b33b23f1b7bc1f3c5a8c2d819074ecd7ab19?d=mp&s=160"},"body":">>>>> \"Theodore\" == Theodore Tso <tytso@mit.edu> writes:\n\nTheodore> For emacs users, it would even be better to tie it into emacs.  That\nTheodore> way you're already at the line number looking at the source code\nTheodore> when you start wondering, \"who the f*ck created this mess?\".  The\nTheodore> way I'd design it would to have emacs split the window vertically,\nTheodore> and place the blame information in a buffer whose scrolling was\nTheodore> synchronized with the main window.\n\nvc-annotate can do the window already, although not the rest.\n\n-- \nRandal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095\n<merlyn@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>\nPerl/Unix/security consulting, Technical writing, Comedy, etc. etc.\nSee PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!\n"},{"id":"32902","messageId":"20070128074027.GB9781@spearce.org","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701270837170.25027@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-01-28T07:40:27Z","receivedAt":"2007-01-28T07:40:27Z","isPatch":false,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> wrote:\n> Here's a patch. Use \"git blame --incremental filename\" to get the blame \n> output in a nicely parseable format that you can now write a simple \n> graphical viewer for. \n\nHeh.  Nice timing.  I was starting to think about adding blame output\nto git-gui.  Having an incremental backend would certainly be nice.\n\n-- \nShawn.\n"},{"id":"32907","messageId":"7vps8zfqlx.fsf@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701271439340.25027@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-28T09:27:38Z","receivedAt":"2007-01-28T09:27:38Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> On Sat, 27 Jan 2007, Junio C Hamano wrote:\n>> \n>> I would think we probably should reuse the --porcelain output,\n>> perhaps enhancing it even more.\n>\n> I looked at using \"emit_porcelain()\" directly, but that format doesn't \n> seem to actually be usable for incremental blame.\n\nI agree the code itself wouldn't be for the reasons you stated.\n\n> Also, for the incremental blame, it makes no sense to actually print out \n> the actual blame buffer: anybody who uses the incremental blame thing \n> really needs to get the original buffer separately set up anyway.\n\nYes and no -- it might be interesting to start from a blank\ncanvas, and insert the lines as they are received at appropriate\nplaces (recorded as ent->lno), although in general I agree the\nGUI would have the way and the need to grab the blob contents\nwithout us giving it in the --incremental output.\n\nI think it is sensible to do the attached on top of your patch.\n\n-- >8 --\n[PATCH] Update blame --incremental output format.\n\nIt makes the output show the origin information in the same\nformat as the porcelain format.  The first line has commit\nobject name, the line number of the first line in the group in\nthe original file, the line number of that file in the final\nimage, and number of lines in the group.  Then subsequent lines\nshow the metainformation for the commit when the commit is shown\nfor the first time, except the filename information is always\nshown (we cannot even make it conditional to -C option as blame\nalways follows the renaming of the file wholesale).\n\nTwo things I updated are (1) line numbers start at 1, not 0, to\nmake it consistent with other formats, (2) filename is C-quoted\nif needed.\n\nThe latter should be done to fix the original porcelain output;\nit was an oversight.\n\n builtin-blame.c |   67 +++++++++++++++++++++++++++++++++++++------------------\n 1 files changed, 45 insertions(+), 22 deletions(-)\n\ndiff --git a/builtin-blame.c b/builtin-blame.c\nindex 7d97ae9..967e30d 100644\n--- a/builtin-blame.c\n+++ b/builtin-blame.c\n@@ -13,6 +13,7 @@\n #include \"diff.h\"\n #include \"diffcore.h\"\n #include \"revision.h\"\n+#include \"quote.h\"\n #include \"xdiff-interface.h\"\n \n static char blame_usage[] =\n@@ -1071,18 +1072,56 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \t\torigin_decref(parent_origin[i]);\n }\n \n+struct commit_info\n+{\n+\tchar *author;\n+\tchar *author_mail;\n+\tunsigned long author_time;\n+\tchar *author_tz;\n+\n+\t/* filled only when asked for details */\n+\tchar *committer;\n+\tchar *committer_mail;\n+\tunsigned long committer_time;\n+\tchar *committer_tz;\n+\n+\tchar *summary;\n+};\n+\n+static void get_commit_info(struct commit *commit,\n+\t\t\t    struct commit_info *ret,\n+\t\t\t    int detailed);\n+\n static void found_guilty_entry(struct blame_entry *ent)\n {\n \tif (ent->guilty)\n \t\treturn;\n \tent->guilty = 1;\n \tif (incremental) {\n-\t\tstruct origin *origin = ent->suspect;\n-\t\tprintf(\"%d %d %s:%s:%d\\n\",\n-\t\t\tent->lno, ent->num_lines,\n-\t\t\tsha1_to_hex(origin->commit->object.sha1),\n-\t\t\torigin->path,\n-\t\t\tent->s_lno);\n+\t\tstruct origin *suspect = ent->suspect;\n+\n+\t\tprintf(\"%s %d %d %d\\n\",\n+\t\t       sha1_to_hex(suspect->commit->object.sha1),\n+\t\t       ent->s_lno + 1, ent->lno + 1, ent->num_lines);\n+\t\tif (!(suspect->commit->object.flags & METAINFO_SHOWN)) {\n+\t\t\tstruct commit_info ci;\n+\t\t\tsuspect->commit->object.flags |= METAINFO_SHOWN;\n+\t\t\tget_commit_info(suspect->commit, &ci, 1);\n+\t\t\tprintf(\"author %s\\n\", ci.author);\n+\t\t\tprintf(\"author-mail %s\\n\", ci.author_mail);\n+\t\t\tprintf(\"author-time %lu\\n\", ci.author_time);\n+\t\t\tprintf(\"author-tz %s\\n\", ci.author_tz);\n+\t\t\tprintf(\"committer %s\\n\", ci.committer);\n+\t\t\tprintf(\"committer-mail %s\\n\", ci.committer_mail);\n+\t\t\tprintf(\"committer-time %lu\\n\", ci.committer_time);\n+\t\t\tprintf(\"committer-tz %s\\n\", ci.committer_tz);\n+\t\t\tprintf(\"summary %s\\n\", ci.summary);\n+\t\t\tif (suspect->commit->object.flags & UNINTERESTING)\n+\t\t\t\tprintf(\"boundary\\n\");\n+\t\t}\n+\t\tprintf(\"filename \");\n+\t\twrite_name_quoted(NULL, 0, suspect->path, 1, stdout);\n+\t\tputchar('\\n');\n \t}\n }\n \n@@ -1152,22 +1191,6 @@ static const char *format_time(unsigned long time, const char *tz_str,\n \treturn time_buf;\n }\n \n-struct commit_info\n-{\n-\tchar *author;\n-\tchar *author_mail;\n-\tunsigned long author_time;\n-\tchar *author_tz;\n-\n-\t/* filled only when asked for details */\n-\tchar *committer;\n-\tchar *committer_mail;\n-\tunsigned long committer_time;\n-\tchar *committer_tz;\n-\n-\tchar *summary;\n-};\n-\n static void get_ac_line(const char *inbuf, const char *what,\n \t\t\tint bufsz, char *person, char **mail,\n \t\t\tunsigned long *time, char **tz)\n"},{"id":"32908","messageId":"7virerfptl.fsf_-_@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":"7vps8zfqlx.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] git-blame --porcelain: quote filename in c-style when needed.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-28T09:44:38Z","receivedAt":"2007-01-28T09:44:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Otherwise a pathname that has funny characters such as LF would\nscrew up the parsing programs of the output.\n\nStrictly speaking, this is not backward compatible, but the\ncurrent output for pathnames that have embedded LF and such\ncannot be sanely parsed anyway, and pathnames that only use\ncharacters from the portable pathname character set won't be\naffected.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n builtin-blame.c |   15 ++++++++++-----\n 1 files changed, 10 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin-blame.c b/builtin-blame.c\nindex 54ab675..7a58ee3 100644\n--- a/builtin-blame.c\n+++ b/builtin-blame.c\n@@ -1182,6 +1182,13 @@ static void get_commit_info(struct commit *commit,\n \tsummary_buf[len] = 0;\n }\n \n+static void write_filename_info(const char *path)\n+{\n+\tprintf(\"filename \");\n+\twrite_name_quoted(NULL, 0, path, 1, stdout);\n+\tputchar('\\n');\n+}\n+\n static void found_guilty_entry(struct blame_entry *ent)\n {\n \tif (ent->guilty)\n@@ -1209,9 +1216,7 @@ static void found_guilty_entry(struct blame_entry *ent)\n \t\t\tif (suspect->commit->object.flags & UNINTERESTING)\n \t\t\t\tprintf(\"boundary\\n\");\n \t\t}\n-\t\tprintf(\"filename \");\n-\t\twrite_name_quoted(NULL, 0, suspect->path, 1, stdout);\n-\t\tputchar('\\n');\n+\t\twrite_filename_info(suspect->path);\n \t}\n }\n \n@@ -1315,13 +1320,13 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)\n \t\tprintf(\"committer-mail %s\\n\", ci.committer_mail);\n \t\tprintf(\"committer-time %lu\\n\", ci.committer_time);\n \t\tprintf(\"committer-tz %s\\n\", ci.committer_tz);\n-\t\tprintf(\"filename %s\\n\", suspect->path);\n+\t\twrite_filename_info(suspect->path);\n \t\tprintf(\"summary %s\\n\", ci.summary);\n \t\tif (suspect->commit->object.flags & UNINTERESTING)\n \t\t\tprintf(\"boundary\\n\");\n \t}\n \telse if (suspect->commit->object.flags & MORE_THAN_ONE_PATH)\n-\t\tprintf(\"filename %s\\n\", suspect->path);\n+\t\twrite_filename_info(suspect->path);\n \n \tcp = nth_line(sb, ent->lno);\n \tfor (cnt = 0; cnt < ent->num_lines; cnt++) {\n-- \n1.5.0.rc2.g1650-dirty\n"},{"id":"32913","messageId":"20070128131559.GA31217@coredump.intra.peff.net","threadId":"6545","inReplyTo":"20070128023958.GF9897@thunk.org","subject":"Re: More precise tag following","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2007-01-28T13:15:59Z","receivedAt":"2007-01-28T13:15:59Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Jan 27, 2007 at 09:39:58PM -0500, Theodore Tso wrote:\n\n> For emacs users, it would even be better to tie it into emacs.  That\n> way you're already at the line number looking at the source code when\n\nI agree that connecting it with the editor might be sensible; I'm not\nlikely to work on an emacs version, though. :)\n\nOne of my other long-standing annoyances with using \"raw\" git-blame is\nthat it shows me a bunch of commits, but then I have to open a new\nwindow and cut and paste the hash to actually _see_ the commit. That's\nwhy I think something like tig makes sense, where you can jump between\ndifferent views very easily. An editor extension should be able to do\nthe same thing.\n\n> course, scrolling to the right part of the file is a pain.  So\n> building it into the editor is not only convenient, but it avoids the\n> psychological effects that could make it seem slow because how long it\n> takes to fill the attribution for these first bits:\n\nAgreed, I noticed that as well (especially because the enormous font and\nspacing choices of GTK made sure you could only see the first couple of\nlines :) ).\n\n> is there?).  I have no idea how you would hack this into Vim, other\n> than passing the line number into the GUI so it can open right into\n> the function that the developer was looking at.\n\nI'll look into what's out there for vim; there's quite a bit of\nextensibility if you buy into things like the perl support.\n\n-Peff\n"},{"id":"32914","messageId":"45BCB273.7010601@lsrfire.ath.cx","threadId":"6545","inReplyTo":"7vps8zfqlx.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] git-blame --incremental: don't use pager","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2007-01-28T14:25:55Z","receivedAt":"2007-01-28T14:25:55Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Starting a pager defeats the purpose of the incremental output\nmode.  This changes git-blame to only paginate if --incremental\nwas not given.\n\ngit -p blame --incremental still starts the pager, though.\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n\n builtin-blame.c |    3 +++\n git.c           |    2 +-\n 2 files changed, 4 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-blame.c b/builtin-blame.c\nindex 7a58ee3..02bda5e 100644\n--- a/builtin-blame.c\n+++ b/builtin-blame.c\n@@ -1780,6 +1780,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\t\targv[unk++] = arg;\n \t}\n \n+\tif (!incremental)\n+\t\tsetup_pager();\n+\n \tif (!blame_move_score)\n \t\tblame_move_score = BLAME_DEFAULT_MOVE_SCORE;\n \tif (!blame_copy_score)\ndiff --git a/git.c b/git.c\nindex 530e99f..e9febc3 100644\n--- a/git.c\n+++ b/git.c\n@@ -217,7 +217,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)\n \t\t{ \"annotate\", cmd_annotate, USE_PAGER },\n \t\t{ \"apply\", cmd_apply },\n \t\t{ \"archive\", cmd_archive },\n-\t\t{ \"blame\", cmd_blame, RUN_SETUP | USE_PAGER },\n+\t\t{ \"blame\", cmd_blame, RUN_SETUP },\n \t\t{ \"branch\", cmd_branch, RUN_SETUP },\n \t\t{ \"cat-file\", cmd_cat_file, RUN_SETUP },\n \t\t{ \"checkout-index\", cmd_checkout_index, RUN_SETUP },\n"},{"id":"32917","messageId":"20070128181015.GA25600@thunk.org","threadId":"6545","inReplyTo":"204011cb0701271136m655815f6o1501de2bf699b362@mail.gmail.com","subject":"Re: More precise tag following","fromName":"Theodore Tso","fromEmail":"tytso@mit.edu","sentAt":"2007-01-28T18:10:15Z","receivedAt":"2007-01-28T18:10:15Z","isPatch":false,"sender":{"key":"tytso@mit.edu","avatar":"https://avatars.githubusercontent.com/u/51416?v=4"},"body":"On Sat, Jan 27, 2007 at 11:36:50AM -0800, Chris Lee wrote:\n> I don't have access to any servers that I could drop a 3GB packfile\n> onto and expect them to serve it. And I don't have a connection at\n> home that I could use to upload the 3GB pack from quickly - it would\n> take days, at least. If anybody wants to hook me up with a hosting\n> provider or a machine that just the git devs can access, I'd be\n> willing to tie up my upstream bandwidth for a few days so you all can\n> have access to it.\n\nHmm, maybe the right answer is to send a DVD out to someone who is\nwilling make copies through a distribution tree to those that want it;\nmy guess it will probably be a relatively small set of folks.  \n\n\t\t\t\t\t\t- Ted\n"},{"id":"32918","messageId":"Pine.LNX.4.64.0701281023500.25027@woody.linux-foundation.org","threadId":"6545","inReplyTo":"20070128181015.GA25600@thunk.org","subject":"Re: More precise tag following","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-01-28T18:27:15Z","receivedAt":"2007-01-28T18:27:15Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 28 Jan 2007, Theodore Tso wrote:\n> \n> Hmm, maybe the right answer is to send a DVD out to someone who is\n> willing make copies through a distribution tree to those that want it;\n> my guess it will probably be a relatively small set of folks.  \n\nHeh. Sneakernet. \"The throughput of a truck-full of tapes...\"\n\nBut it probably would work in this case. Chris - where in the world are \nyou? Maybe somebody with bandwidth can indeed just pick up a DVD, and then \nmake it available to the rest of us through the net (possibly even though \na non-public URL - I suspect that Ted is right, and there's only a few \npeople who would actually want to download 3GB to play with).\n\nI was *not* planning on downloading a 48GB KDE archive to then run it \nthrough some strange contortions, but I'd love to just download 3GB \novernight..\n\n\t\tLinus\n"},{"id":"32919","messageId":"Pine.LNX.4.64.0701281107050.25027@woody.linux-foundation.org","threadId":"6545","inReplyTo":"7vps8zfqlx.fsf@assigned-by-dhcp.cox.net","subject":"Re: More precise tag following","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-01-28T19:08:06Z","receivedAt":"2007-01-28T19:08:06Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 28 Jan 2007, Junio C Hamano wrote:\n> \n> I think it is sensible to do the attached on top of your patch.\n\nAck.\n\nI see you committed this, which is nice, but now Shawn's butt-ugly thing \ndoesn't work any more, and my mad perl skillz are sadly lacking.\n\n\t\tLinus\n"},{"id":"32920","messageId":"7v4pqbezo9.fsf@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":"45BCB273.7010601@lsrfire.ath.cx","subject":"Re: [PATCH] git-blame --incremental: don't use pager","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-28T19:09:26Z","receivedAt":"2007-01-28T19:09:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"René Scharfe <rene.scharfe@lsrfire.ath.cx> writes:\n\n> Starting a pager defeats the purpose of the incremental output\n> mode.  This changes git-blame to only paginate if --incremental\n> was not given.\n\nI should have done this myself when I applied Linus's patch.\nThanks for catching.\n"},{"id":"32921","messageId":"7vzm83dkw4.fsf@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":"7v4pqbezo9.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git-blame --incremental: don't use pager","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-28T19:14:03Z","receivedAt":"2007-01-28T19:14:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <junkio@cox.net> writes:\n\n> René Scharfe <rene.scharfe@lsrfire.ath.cx> writes:\n>\n>> Starting a pager defeats the purpose of the incremental output\n>> mode.  This changes git-blame to only paginate if --incremental\n>> was not given.\n>\n> I should have done this myself when I applied Linus's patch.\n> Thanks for catching.\n\nAlthough I'd apply it anyway, strictly speaking, I think this\npatch should not matter because any real Porcelain would be\nusing this as an upstream of a pipe to its drawing engine.\n\nWell, unless that Porcelain drives --incremental through a pair\nof ptys, but I do not think it is likely ;-).\n"},{"id":"32922","messageId":"7vveirdkpb.fsf@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701281107050.25027@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-28T19:18:08Z","receivedAt":"2007-01-28T19:18:08Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> On Sun, 28 Jan 2007, Junio C Hamano wrote:\n>> \n>> I think it is sensible to do the attached on top of your patch.\n>\n> Ack.\n>\n> I see you committed this, which is nice, but now Shawn's butt-ugly thing \n> doesn't work any more, and my mad perl skillz are sadly lacking.\n\nDo you mean the perl-Gtk one by Jeff King?\n\nI was hoping to take a look at Shawn's git-gui and also perhaps\nlooking into adding blame --incremental support to gitk myself\nwhen I have time, but unfortunately my day-job deadline is\nspilling into this weekend.\n"},{"id":"32924","messageId":"Pine.LNX.4.64.0701281143190.25027@woody.linux-foundation.org","threadId":"6545","inReplyTo":"7vveirdkpb.fsf@assigned-by-dhcp.cox.net","subject":"Re: More precise tag following","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-01-28T19:57:33Z","receivedAt":"2007-01-28T19:57:33Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 28 Jan 2007, Junio C Hamano wrote:\n> \n> Do you mean the perl-Gtk one by Jeff King?\n\nSorry, yeah, I'm just confused.\n\nWhere are my meds again?\n\n> I was hoping to take a look at Shawn's git-gui and also perhaps\n> looking into adding blame --incremental support to gitk myself\n> when I have time, but unfortunately my day-job deadline is\n> spilling into this weekend.\n\nI think the nice thing about the new \"git-blame --incremental\" is that it \nallows people who really don't know (or care) anything at all about git \ninternals to do the viewer. So you shouldn't need to care.\n\nSo I don't think you should do it, we should encourage others (who may not \nbe comfy with writing hard-core C that touches subtle internal git issues) \nto just do it.\n\nOne thing I looked at, which *should* be easy to do inside \"git-blame\", is \nto make the case where you do *not* give a head to start with, default to \n\"current working tree\" instead of HEAD.\n\nFor example, say that I have changes in my working tree, and I do\n\n\tgit blame-viewer <filename-that-is-dirty>\n\nI think it would be nice if the *dirty* lines would actually get blamed to \na fake commit (SHA-1 \"00000000..\") that is the \"current working tree. \nRight now, it always starts from HEAD:filename, which may be how CVS/SVN \nannotate and friends work, but I actually think we could do better.\n\nIf you really want the annotation for the _committed_ state, you can \nalways just say so explicitly:\n\n\tgit blame-viewer HEAD <filename-that-may-be-dirty-but-who-cares>\n\nNo?\n\nBut for the actual viewer parts, which don't need internal git knowledge, \nlet's just document the blame format, so that others can do it:\n\nThe new format is fairly easy to parse: each blame entry is always\n\n - starts with a line of\n\n\t<40-byte hex sha1> <sourceline> <resultline> <num_lines>\n\n - the first time that commit shows up in the stream, it has various\n   other information about it printed out with a one-word tag at the \n   beginning of each line about that \"extended commit info\" (author, \n   email, committer, dates, summary etc)\n\n - each entry is _always_ finished by a\n\n\t\"filename\" <whitespace-quoted-filename-goes-here>\n\nand thus it's really quite easy to parse for some line- and word-oriented \nparser (which should be quite natural for most scripting languages).\n\nNOTE! For people who do parsing: to make it more robust, just ignore any \nlines in between the first and last one (\"<sha1>\" and \"filename\" lines) \nwhere you don't recognize the tag-words (or care about that particular \none) at the beginning of the \"extended information\" lines. That way, if \nthere is ever added information (like the commit encoding or extended \ncommit commentary), a blame viewer won't ever care.\n\n\t\tLinus\n"},{"id":"32925","messageId":"7vlkjmexel.fsf@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":"7vveirdkpb.fsf@assigned-by-dhcp.cox.net","subject":"Re: More precise tag following","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-28T19:58:26Z","receivedAt":"2007-01-28T19:58:26Z","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> Linus Torvalds <torvalds@linux-foundation.org> writes:\n>\n>> On Sun, 28 Jan 2007, Junio C Hamano wrote:\n>>> \n>>> I think it is sensible to do the attached on top of your patch.\n>>\n>> Ack.\n>>\n>> I see you committed this, which is nice, but now Shawn's butt-ugly thing \n>> doesn't work any more, and my mad perl skillz are sadly lacking.\n>\n> Do you mean the perl-Gtk one by Jeff King?\n\nThis on top of the \"final\" by Jeff should minimally restore it,\nin addition to fixing a few problems.\n\n * filename/linenumber is updated from the blame.\n * gmtime output is relative to year 1900.\n\nI've annotated revision.c with -C and it was fun to watch;-).\n\n---\ndiff --git a/jk.perl b/jk.perl\nindex 6a4ac9f..c4d9d57 100644\n--- a/jk.perl\n+++ b/jk.perl\n@@ -41,32 +41,63 @@ $window->show_all;\n Gtk2->main;\n exit 0;\n \n+my %commitinfo = ();\n+\n+sub flush_blame_line {\n+\tmy ($attr) = @_;\n+\n+\treturn unless defined $attr;\n+\n+\tmy ($commit, $s_lno, $lno, $cnt) =\n+\t    @{$attr}{qw(COMMIT S_LNO LNO CNT)};\n+\n+\tmy ($filename, $author, $author_time, $author_tz) =\n+\t    @{$commitinfo{$commit}}{qw(FILENAME AUTHOR AUTHOR-TIME AUTHOR-TZ)};\n+\tmy $info = $author . ' ' . format_time($author_time, $author_tz);\n+\n+\tfor(my $i = 0; $i < $cnt; $i++) {\n+\t\t@{$fileview->{data}->[$lno+$i-1]}[0,1,2] =\n+\t\t    (substr($commit, 0, 8), $info,\n+\t\t     $filename . ':' . ($s_lno+$i));\n+\t}\n+}\n+\n my $buf;\n+my $current;\n sub read_blame_line {\n-  my $r = sysread(STDIN, $buf, 1024, length($buf));\n-  return 0 unless $r;\n-  while($buf =~ s/([^\\n]*)\\n//) {\n-    my $line = $1;\n-    $line =~ /^(\\d+) (\\d+) ([0-9a-f]+):(.*):(\\d+)$/\n-      or die \"bad blame output: $line\";\n-    my $info = commitinfo($3);\n-    for(my $i = 0; $i < $2; $i++) {\n-      @{$fileview->{data}->[$1+$i]}[0,1] =\n-        (substr($3, 0, 8), $info, $4 . ':' . ($5+$i+1));\n-    }\n-  }\n-  return 1;\n-}\n \n-sub commitinfo {\n-  my $hash = shift;\n-  open(my $fh, '-|', qw(git rev-list -1 --pretty=raw), $hash)\n-    or die \"unable to open git-rev-list: $!\";\n-  while(<$fh>) {\n-    chomp;\n-    next unless /^author (.*) <.*> (\\d+) ([+-]\\d+)/;\n-    return $1 . ' ' . format_time($2, $3);\n-  }\n+\tmy $r = sysread(STDIN, $buf, 1024, length($buf));\n+\tdie \"I/O error\" unless defined $r;\n+\n+\tif ($r == 0) {\n+\t\tflush_blame_line($current);\n+\t\t$current = undef;\n+\t\treturn 0;\n+\t}\n+\n+\twhile ($buf =~ s/([^\\n]*)\\n//) {\n+\t\tmy $line = $1;\n+\n+\t\tif (($commit, $s_lno, $lno, $cnt) =\n+\t\t    ($line =~ /^([0-9a-f]{40}) (\\d+) (\\d+) (\\d+)$/)) {\n+\t\t\tflush_blame_line($current);\n+\t\t\t$current = +{\n+\t\t\t\tCOMMIT => $1,\n+\t\t\t\tS_LNO => $2,\n+\t\t\t\tLNO => $3,\n+\t\t\t\tCNT => $4,\n+\t\t\t};\n+\t\t\tnext;\n+\t\t}\n+\n+\t\t# extended attribute values\n+\t\tif ($line =~ /^(author|author-mail|author-time|author-tz|committer|committer-mail|committer-time|committer-tz|summary|filename) (.*)$/) {\n+\t\t\tmy $commit = $current->{COMMIT};\n+\t\t\t$commitinfo{$commit}{uc($1)} = $2;\n+\t\t\tnext;\n+\t\t}\n+\t}\n+\treturn 1;\n }\n \n sub format_time {\n@@ -78,5 +109,6 @@ sub format_time {\n   $minutes = $tz < 0 ? 0-$minutes : $minutes;\n   $time += $minutes * 60;\n   my @t = gmtime($time);\n-  return sprintf('%04d-%02d-%02d %02d:%02d:%02d %s', @t[5,4,3,2,1,0], $tz);\n+  return sprintf('%04d-%02d-%02d %02d:%02d:%02d %s',\n+\t\t $t[5] + 1900, @t[4,3,2,1,0], $tz);\n }\n"},{"id":"32926","messageId":"7vhcuaex9k.fsf@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701281143190.25027@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-28T20:01:27Z","receivedAt":"2007-01-28T20:01:27Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> But for the actual viewer parts, which don't need internal git knowledge, \n> let's just document the blame format, so that others can do it:\n>\n> The new format is fairly easy to parse: each blame entry is always\n>\n>  - starts with a line of\n>\n> \t<40-byte hex sha1> <sourceline> <resultline> <num_lines>\n>\n>  - the first time that commit shows up in the stream, it has various\n>    other information about it printed out with a one-word tag at the \n>    beginning of each line about that \"extended commit info\" (author, \n>    email, committer, dates, summary etc)\n>\n>  - each entry is _always_ finished by a\n>\n> \t\"filename\" <whitespace-quoted-filename-goes-here>\n>\n> and thus it's really quite easy to parse for some line- and word-oriented \n> parser (which should be quite natural for most scripting languages).\n>\n> NOTE! For people who do parsing: to make it more robust, just ignore any \n> lines in between the first and last one (\"<sha1>\" and \"filename\" lines) \n> where you don't recognize the tag-words (or care about that particular \n> one) at the beginning of the \"extended information\" lines. That way, if \n> there is ever added information (like the commit encoding or extended \n> commit commentary), a blame viewer won't ever care.\n\nThanks for these notes, which I should have written.  I would\nalso caution them to ignore if there is anything they do not\nunderstand between \"filename\" and <sha1>.\n\nA sample code to parse it in Perl was just posted by me ;-).\n"},{"id":"32928","messageId":"7v8xfmewdm.fsf_-_@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701281143190.25027@woody.linux-foundation.org","subject":"[PATCH] document 'blame --incremental'","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-28T20:20:37Z","receivedAt":"2007-01-28T20:20:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt\nindex 5dd8e36..a4e4bee 100644\n--- a/Documentation/git-blame.txt\n+++ b/Documentation/git-blame.txt\n@@ -8,7 +8,7 @@ git-blame - Show what revision and author last modified each line of a file\n SYNOPSIS\n --------\n [verse]\n-'git-blame' [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>]\n+'git-blame' [-c] [-l] [-t] [-f] [-n] [-p] [--incremental] [-L n,m] [-S <revs-file>]\n             [-M] [-C] [-C] [--since=<date>] [<rev>] [--] <file>\n \n DESCRIPTION\n@@ -63,6 +63,10 @@ OPTIONS\n -p, --porcelain::\n \tShow in a format designed for machine consumption.\n \n+--incremental::\n+\tShow the result incrementally in a format designed for\n+\tmachine consumption.\n+\n -M::\n \tDetect moving lines in the file as well.  When a commit\n \tmoves a block of lines in a file (e.g. the original file\n@@ -158,6 +162,46 @@ parents, using `commit{caret}!` notation:\n \tgit blame -C -C -f $commit^! -- foo\n \n \n+INCREMENTAL OUTPUT\n+------------------\n+\n+When called with `--incremental` option, the command outputs the\n+result as it is built.  The output generally will talk about\n+lines touched by more recent commits first and is meant to be\n+used by interactive viewers.\n+\n+The output format is similar to the Porcelain format, but it\n+does not contain the actual lines from the file that is being\n+annotated.  \n+\n+. Each blame entry always starts with a line of:\n+\n+\t<40-byte hex sha1> <sourceline> <resultline> <num_lines>\n++\n+Line numbers count from 1.\n+\n+. The first time that commit shows up in the stream, it has various\n+  other information about it printed out with a one-word tag at the \n+  beginning of each line about that \"extended commit info\" (author, \n+  email, committer, dates, summary etc).\n+\n+. Unlike Porcelain format, the filename information is always\n+  given and terminates the entry:\n+\n+\t\"filename\" <whitespace-quoted-filename-goes-here>\n++\n+and thus it's really quite easy to parse for some line- and word-oriented\n+parser (which should be quite natural for most scripting languages).\n++\n+[NOTE]\n+For people who do parsing: to make it more robust, just ignore any \n+lines in between the first and last one (\"<sha1>\" and \"filename\" lines) \n+where you don't recognize the tag-words (or care about that particular \n+one) at the beginning of the \"extended information\" lines. That way, if \n+there is ever added information (like the commit encoding or extended \n+commit commentary), a blame viewer won't ever care.\n+\n+\n SEE ALSO\n --------\n gitlink:git-annotate[1]\n"},{"id":"32931","messageId":"7vveiqdfpj.fsf@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701281143190.25027@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-28T21:06:00Z","receivedAt":"2007-01-28T21:06:00Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> On Sun, 28 Jan 2007, Junio C Hamano wrote:\n> ...\n>> I was hoping to take a look at Shawn's git-gui and also perhaps\n>> looking into adding blame --incremental support to gitk myself\n>> when I have time, but unfortunately my day-job deadline is\n>> spilling into this weekend.\n>\n> I think the nice thing about the new \"git-blame --incremental\" is that it \n> allows people who really don't know (or care) anything at all about git \n> internals to do the viewer. So you shouldn't need to care.\n>\n> So I don't think you should do it, we should encourage others (who may not \n> be comfy with writing hard-core C that touches subtle internal git issues) \n> to just do it.\n\nGood points.\n\nI won't, although I've added fixed-up version of Jeff's as an\nexample under contrib/ -- I hope Jeff does not mind.\n\n> ...\n> I think it would be nice if the *dirty* lines would actually get blamed to \n> a fake commit (SHA-1 \"00000000..\") that is the \"current working tree. \n> ...\n> No?\n\nYeah.  That sounds sensible.\n"},{"id":"32933","messageId":"Pine.LNX.4.63.0701281425270.26863@qynat.qvtvafvgr.pbz","threadId":"6545","inReplyTo":"204011cb0701271136m655815f6o1501de2bf699b362@mail.gmail.com","subject":"Re: More precise tag following","fromName":"David Lang","fromEmail":"david.lang@digitalinsight.com","sentAt":"2007-01-28T22:26:16Z","receivedAt":"2007-01-28T22:26:16Z","isPatch":false,"sender":{"key":"david.lang@digitalinsight.com","avatar":null},"body":"if nobody else steps forward I can arrange something like this on my home server \n(only 768K updtream bandwidth, but it's better then nothing)\n\nDaivd Lang\n\nOn Sat, 27 Jan 2007, Chris Lee wrote:\n\n> Date: Sat, 27 Jan 2007 11:36:50 -0800\n> From: Chris Lee <clee@kde.org>\n> To: Linus Torvalds <torvalds@linux-foundation.org>\n> Cc: Simon 'corecode' Schubert <corecode@fs.ei.tum.de>,\n>     Shawn O. Pearce <spearce@spearce.org>, Junio C Hamano <junkio@cox.net>,\n>     git@vger.kernel.org\n> Subject: Re: More precise tag following\n> \n> On 1/27/07, Linus Torvalds <torvalds@linux-foundation.org> wrote:\n>> Do you have the converted repo somewhere to be cloned for? It's going to\n>> be a lot more interesting for scalability testing than anything else.\n>\n> I don't have access to any servers that I could drop a 3GB packfile\n> onto and expect them to serve it. And I don't have a connection at\n> home that I could use to upload the 3GB pack from quickly - it would\n> take days, at least. If anybody wants to hook me up with a hosting\n> provider or a machine that just the git devs can access, I'd be\n> willing to tie up my upstream bandwidth for a few days so you all can\n> have access to it.\n>\n>> The thing is, one of the reasons the git object database is small is that\n>> it compresses really well, and I suspect that for the KDE repo, what\n>> you're seeing is really a combination of:\n>>\n>>  - the KDE people were idiots in the first place to make it into one big\n>>    repo\n>\n> No argument from me about this one. The only defense I can really\n> think of is that, in KDE, we *have* moved entire applications and\n> libraries around between modules, and it is really nice to be able to\n> have the full history for them.\n>\n>> So please point to a kde conversion archive to play with (maybe you have,\n>> I missed it).\n>\n> I can provide you with instructions on how to reconstruct one, but\n> you'll have to rsync over about 38GB of KDE's Subversion archive to do\n> it. (Not fun.) If someone else wants to give me a dumping ground where\n> I can upload my 3GB converted repo, I'd be happy to start pushing it.\n>\n> Also, please note, the 3GB packed repo is only about 2/3 of the full\n> KDE repo - I cut off the import at revision 409202, because that was\n> when the KDE svn admins decided to move a bunch of modules from\n> /trunk/ to /trunk/KDE/ and it screws up everything. So a *full* KDE\n> history import would definitely be more than 4GB, packed.\n>\n> -clee\n> -\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n>\n"},{"id":"32934","messageId":"20070128230119.GA28367@coredump.intra.peff.net","threadId":"6545","inReplyTo":"7vveiqdfpj.fsf@assigned-by-dhcp.cox.net","subject":"Re: More precise tag following","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2007-01-28T23:01:20Z","receivedAt":"2007-01-28T23:01:20Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Jan 28, 2007 at 01:06:00PM -0800, Junio C Hamano wrote:\n\n> I won't, although I've added fixed-up version of Jeff's as an\n> example under contrib/ -- I hope Jeff does not mind.\n\nNot at all; thanks for updating it.\n\n-Peff\n"},{"id":"32939","messageId":"45BD40AE.9020603@lsrfire.ath.cx","threadId":"6545","inReplyTo":"7vzm83dkw4.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git-blame --incremental: don't use pager","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2007-01-29T00:32:46Z","receivedAt":"2007-01-29T00:32:46Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Junio C Hamano schrieb:\n> Although I'd apply it anyway, strictly speaking, I think this\n> patch should not matter because any real Porcelain would be\n> using this as an upstream of a pipe to its drawing engine.\n> \n> Well, unless that Porcelain drives --incremental through a pair\n> of ptys, but I do not think it is likely ;-).\n\nHa!, didn't think of that.  I still like it more without a pager\neven if run on a terminal, because then you can *see* that it's\nreally incremental (without needing to unset PAGER).  I'm a\nnon-believer. ;-)\n\nRené\n"},{"id":"32942","messageId":"7vfy9ublvj.fsf_-_@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":"45BD40AE.9020603@lsrfire.ath.cx","subject":"[PATCH] git blame --progress","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-29T02:35:44Z","receivedAt":"2007-01-29T02:35:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"[PATCH] git blame --progress\n\nWith  --progress option, the command shows a fairly useless but\namusing eye-candy while making the user wait.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n René Scharfe <rene.scharfe@lsrfire.ath.cx> writes:\n\n > Junio C Hamano schrieb:\n >> Although I'd apply it anyway, strictly speaking, I think this\n >> patch should not matter because any real Porcelain would be\n >> using this as an upstream of a pipe to its drawing engine.\n >> \n >> Well, unless that Porcelain drives --incremental through a pair\n >> of ptys, but I do not think it is likely ;-).\n >\n > Ha!, didn't think of that.  I still like it more without a pager\n > even if run on a terminal, because then you can *see* that it's\n > really incremental (without needing to unset PAGER).  I'm a\n > non-believer. ;-)\n\n builtin-blame.c |   87 +++++++++++++++++++++++++++++++++++++++++++++++++++++--\n 1 files changed, 84 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin-blame.c b/builtin-blame.c\nindex 02bda5e..cd54acf 100644\n--- a/builtin-blame.c\n+++ b/builtin-blame.c\n@@ -17,7 +17,7 @@\n #include \"xdiff-interface.h\"\n \n static char blame_usage[] =\n-\"git-blame [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [commit] [--] file\\n\"\n+\"git-blame [-c] [-l] [-t] [-f] [-n] [-p] [--progress] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [commit] [--] file\\n\"\n \"  -c, --compatibility Use the same output mode as git-annotate (Default: off)\\n\"\n \"  -b                  Show blank SHA-1 for boundary commits (Default: off)\\n\"\n \"  -l, --long          Show long commit SHA1 (Default: off)\\n\"\n@@ -29,6 +29,7 @@ static char blame_usage[] =\n \"  -L n,m              Process only line range n,m, counting from 1\\n\"\n \"  -M, -C              Find line movements within and across files\\n\"\n \"  --incremental       Show blame entries as we find them, incrementally\\n\"\n+\"  --progress          Show fairly useless progress display\\n\"\n \"  -S revs-file        Use revisions from revs-file instead of calling git-rev-list\\n\";\n \n static int longest_file;\n@@ -39,6 +40,7 @@ static int max_score_digits;\n static int show_root;\n static int blank_boundary;\n static int incremental;\n+static int eye_candy;\n \n #ifndef DEBUG\n #define DEBUG 0\n@@ -1189,7 +1191,80 @@ static void write_filename_info(const char *path)\n \tputchar('\\n');\n }\n \n-static void found_guilty_entry(struct blame_entry *ent)\n+#define NUM_EC_SPOT 500\n+#define NUM_EC_SPOT_PER_GROUP 10\n+#define NUM_EC_SPOT_PER_ROW 50\n+\n+static int eye_candy_spots(struct scoreboard *sb)\n+{\n+\tint num_lines = sb->num_lines;\n+\tif (NUM_EC_SPOT < num_lines)\n+\t\treturn NUM_EC_SPOT;\n+\treturn num_lines;\n+}\n+\n+static void initialize_eye_candy(struct scoreboard *sb)\n+{\n+\tint cnt = eye_candy_spots(sb);\n+\tint i, j;\n+\n+\tfprintf(stderr, \"\\033[2JAssigning blame for %s\\n\", sb->path);\n+\tfor (i = j = 0; i < cnt; i++) {\n+\t\tfputc('.', stderr);\n+\t\tj++;\n+\t\tif (NUM_EC_SPOT_PER_ROW <= j) {\n+\t\t\tj = 0;\n+\t\t\tfputc('\\n', stderr);\n+\t\t}\n+\t\telse if ((j % NUM_EC_SPOT_PER_GROUP) == 0)\n+\t\t\tfputc(' ', stderr);\n+\t}\n+\tif (j)\n+\t\tfputc('\\n', stderr);\n+}\n+\n+static int eye_candy_spot(struct scoreboard *sb, int lno)\n+{\n+\tint cnt = eye_candy_spots(sb);\n+\treturn lno * cnt / sb->num_lines;\n+}\n+\n+static void update_eye_candy(struct scoreboard *sb, struct blame_entry *ent)\n+{\n+\tint cnt = eye_candy_spots(sb);\n+\tint spot_lo, spot_hi, spot;\n+\tstruct blame_entry *lo, *hi;\n+\n+\tfor (lo = ent; lo->prev && lo->prev->guilty; lo = lo->prev)\n+\t\t;\n+\tspot_lo = eye_candy_spot(sb, lo->lno);\n+\tfor (hi = ent; hi->next && hi->next->guilty; hi = hi->next)\n+\t\t;\n+\tspot_hi = eye_candy_spot(sb, hi->lno + hi->num_lines - 1);\n+\n+\tfor (spot = spot_lo; spot <= spot_hi; spot++) {\n+\t\tint spot_x, spot_y;\n+\n+\t\tspot_x = spot % NUM_EC_SPOT_PER_ROW;\n+\t\tspot_x = spot_x + spot_x / NUM_EC_SPOT_PER_GROUP;\n+\n+\t\tspot_y = spot / NUM_EC_SPOT_PER_ROW;\n+\t\tspot_y = (cnt / NUM_EC_SPOT_PER_ROW) - spot_y;\n+\t\tif (cnt < NUM_EC_SPOT && (cnt % NUM_EC_SPOT_PER_ROW))\n+\t\t\tspot_y++;\n+\n+\t\tif (spot_y)\n+\t\t\tfprintf(stderr, \"\\033[%dA\", spot_y);\n+\t\tif (spot_x)\n+\t\t\tfprintf(stderr, \"\\033[%dC\", spot_x);\n+\t\tfputc('*', stderr);\n+\t\tfprintf(stderr, \"\\033[%dD\", spot_x + 1);\n+\t\tif (spot_y)\n+\t\t\tfprintf(stderr, \"\\033[%dB\", spot_y);\n+\t}\n+}\n+\n+static void found_guilty_entry(struct scoreboard *sb, struct blame_entry *ent)\n {\n \tif (ent->guilty)\n \t\treturn;\n@@ -1218,6 +1293,8 @@ static void found_guilty_entry(struct blame_entry *ent)\n \t\t}\n \t\twrite_filename_info(suspect->path);\n \t}\n+\telse if (eye_candy)\n+\t\tupdate_eye_candy(sb, ent);\n }\n \n static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)\n@@ -1253,7 +1330,7 @@ static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)\n \t\t/* Take responsibility for the remaining entries */\n \t\tfor (ent = sb->ent; ent; ent = ent->next)\n \t\t\tif (!cmp_suspect(ent->suspect, suspect))\n-\t\t\t\tfound_guilty_entry(ent);\n+\t\t\t\tfound_guilty_entry(sb, ent);\n \t\torigin_decref(suspect);\n \n \t\tif (DEBUG) /* sanity */\n@@ -1768,6 +1845,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\telse if (!strcmp(\"-n\", arg) ||\n \t\t\t !strcmp(\"--show-number\", arg))\n \t\t\toutput_option |= OUTPUT_SHOW_NUMBER;\n+\t\telse if (!strcmp(\"--progress\", arg))\n+\t\t\teye_candy = 1;\n \t\telse if (!strcmp(\"-p\", arg) ||\n \t\t\t !strcmp(\"--porcelain\", arg))\n \t\t\toutput_option |= OUTPUT_PORCELAIN;\n@@ -1951,6 +2030,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\tdie(\"reading graft file %s failed: %s\",\n \t\t    revs_file, strerror(errno));\n \n+\tif (eye_candy)\n+\t\tinitialize_eye_candy(&sb);\n \tassign_blame(&sb, &revs, opt);\n \n \tif (incremental)\n"},{"id":"32944","messageId":"20070129061807.GA4634@spearce.org","threadId":"6545","inReplyTo":"7vps8zfqlx.fsf@assigned-by-dhcp.cox.net","subject":"Re: More precise tag following","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-01-29T06:18:07Z","receivedAt":"2007-01-29T06:18:07Z","isPatch":false,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Junio C Hamano <junkio@cox.net> wrote:\n> Yes and no -- it might be interesting to start from a blank\n> canvas, and insert the lines as they are received at appropriate\n> places (recorded as ent->lno), although in general I agree the\n> GUI would have the way and the need to grab the blob contents\n> without us giving it in the --incremental output.\n\nI just implemented the blame --incremental thing in git-gui.\nI'm grabbing the file data ahead of time with git-cat-file, throwing\nup the UI, then streaming in the incremental blame data as it comes.\nIts *VERY* fast.  Nice job to both of you (Linus and Junio).\n\nIf you are curious its been pushed to repo.or.cz:\n\n  git://repo.or.cz/git-gui.git\n\n  Repository->Browse Current Branch\n  Double click on the file you want to see.\n\nI'm going to work up screenshots a bit later.\n\n-- \nShawn.\n"},{"id":"32947","messageId":"45BD9B8C.70003@fs.ei.tum.de","threadId":"6545","inReplyTo":"7vfy9ublvj.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git blame --progress","fromName":"Simon 'corecode' Schubert","fromEmail":"corecode@fs.ei.tum.de","sentAt":"2007-01-29T07:00:28Z","receivedAt":"2007-01-29T07:00:28Z","isPatch":true,"sender":{"key":"corecode@fs.ei.tum.de","avatar":"https://gravatar.com/avatar/eff9dbf0cdac0d1e6a6cd7ed0e50763edcb376b493b5253a35ff167918ad79e1?d=mp&s=160"},"body":"Junio C Hamano wrote:\n> +\tfprintf(stderr, \"\\033[2JAssigning blame for %s\\n\", sb->path);\n\nare you sure that you want to hard code the escape sequence?  I guess the correct way would be to query terminfo.\n\ncheers\n  simon\n\n-- \nServe - BSD     +++  RENT this banner advert  +++    ASCII Ribbon   /\"\\\nWork - Mac      +++  space for low €€€ NOW!1  +++      Campaign     \\ /\nParty Enjoy Relax   |   http://dragonflybsd.org      Against  HTML   \\\nDude 2c 2 the max   !   http://golden-apple.biz       Mail + News   / \\\n\n"},{"id":"32953","messageId":"7v8xfm87cz.fsf@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":"20070129061807.GA4634@spearce.org","subject":"Re: More precise tag following","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-29T10:17:32Z","receivedAt":"2007-01-29T10:17:32Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Shawn O. Pearce\" <spearce@spearce.org> writes:\n\n> If you are curious its been pushed to repo.or.cz:\n>\n>   git://repo.or.cz/git-gui.git\n>\n>   Repository->Browse Current Branch\n>   Double click on the file you want to see.\n\nCool.\n\nI think it is not a big deal for git-gui which is for active\ndevelopers and not primarily for archaeologists, but it does not\nseem to like to be invoked inside a bare repository.\n\nAlso it becomes very tempting to somehow have this \"file\nbrowser\" selection UI as \"tree browser\" that can wander around\nto view an arbitrary tree in the commit history.  The boundary\nbetween git-gui and gitk would start to blurrrrrr.....\n"},{"id":"32954","messageId":"20070129103137.GA1500@spearce.org","threadId":"6545","inReplyTo":"7v8xfm87cz.fsf@assigned-by-dhcp.cox.net","subject":"Re: More precise tag following","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-01-29T10:31:37Z","receivedAt":"2007-01-29T10:31:37Z","isPatch":false,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Junio C Hamano <junkio@cox.net> wrote:\n> \"Shawn O. Pearce\" <spearce@spearce.org> writes:\n> > If you are curious its been pushed to repo.or.cz:\n> >\n> >   git://repo.or.cz/git-gui.git\n> \n> I think it is not a big deal for git-gui which is for active\n> developers and not primarily for archaeologists, but it does not\n> seem to like to be invoked inside a bare repository.\n\nYes.  git-gui does some bad things.  On Mac OS X and Windows\nit lets you setup a \"shortcut\".  This bastard thing is a batch\nfile/script which basically sets the GIT_DIR environment variable,\nthen starts git-gui.  So it assumes that the GIT_DIR its getting\nis somehow connected to a working directory of sorts.\n\nI actually have plans to cleanup some of git-gui's internals so that\nits easier to specify what it can do, and cannot do, during startup,\nthen configure the UI from that.  For example, one should not be\nallowed to commit in a bare repository, or merge, but fetch and\npush are still OK.  So is browsing, creating and deleting branches.\nOr editing options (.git/config).\n\nI think the cleanup is easier than it sounds; a lot of the UI is\nalready parameterized based on [appname], which is 'git-gui' or\n'git-citool', depending on the name it was invoked as.  This just\nneeds to carry through a little bit more.\n \n> Also it becomes very tempting to somehow have this \"file\n> browser\" selection UI as \"tree browser\" that can wander around\n> to view an arbitrary tree in the commit history.  The boundary\n> between git-gui and gitk would start to blurrrrrr.....\n\nIndeed.  The main entrypoint is \"new_browser $committish\".  I don't\ncare what $committish is, just so long as git-blame would understand\nit.  It could actually be a treeish, but blame would obviously\nchoke when you open a file and we won't get annotation data.\n\nI just need to hook up some smarter UI to let you select the\ncommittish in question.  Then comes things like wanting to extract\nany given file to the local filesystem (e.g. \"git show b:file >c\"),\netc.\n\nAs for the line blurring between git-gui and gitk, yea, its heading\nthere.  Originally I set out to say \"git-gui is for making changes\nand transport, gitk is for history browsing\".  With this addition of\na tree browser and incremental blame viewer, I'm finding it hard to\nnot add some sort of commit viewer when you double click a commit\nin the blame output.\n\nI *really* do not want to redo what gitk does.  Paul, et.al. have\ndone an excellent job with gitk[*1*].  Its currently 6,324 lines.\ngit-gui is another 5,654 lines.  I don't think we want them redoing\neach other's work.  It would be better if Paul and I could find a\nway to meld the two into a single process.\n\n-- \nShawn.\n"},{"id":"32963","messageId":"Pine.LNX.4.64.0701290759570.3611@woody.linux-foundation.org","threadId":"6545","inReplyTo":"20070129061807.GA4634@spearce.org","subject":"Re: More precise tag following","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-01-29T16:24:52Z","receivedAt":"2007-01-29T16:24:52Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 29 Jan 2007, Shawn O. Pearce wrote:\n> \n> I just implemented the blame --incremental thing in git-gui.\n\nThat's a real technicolor interface ;)\n\nIt's prettier, but it highlights an issue I had with the perl-gtk blame \nviewer too (but there it was overshadowed by all the other aesthetic \nissues)..\n\nOne thing I never really enjoyed about the normal \"git blame\" either, and \nthat the git-gui interface makes even worse, is that it uses a *lot* of \nreal-estate for the blaming. I've got a big screen, and usually run with \n100+ character wide terminals, but for git blame, I think the canvas is \n120+ characters, and despite that over half of it is just the blame \noutput.\n\nThat's actually distracting for several reasons:\n\n - it may be interesting when the primary interest is the shiny new output \n   from \"git blame --incremental\", but at least the way I have ever used \n   annotations, I'm not actually *interested* in the annotations until I \n   find the code I'm looking for.\n\n   In other words, the actual file data is really the *primary* thing. \n   It's the stuff you need to look at first, and it's the thing that ends \n   up making all the rest relevant. The current \"git blame\" and \"git-gui\" \n   interfaces just seem to give too much importance to the annotation data \n   itself.\n\n   Now, in a plain-text pager thign (aka the traditional \"git blame\"), you \n   don't have much choice. The blame data needs to be there, and you can't \n   hide it, because if you do, there's no way to get at it. But things are \n   different with an interactive graphical environment (or a textual one, \n   for that matter: using some curses interface wouldn't change this \n   argument).\n\n   You _could_ just make the primary thing be the actual file data, and \n   the blame be \"incidental\". Which it really is.\n\n - As Ted already pointed out, you actually want to search for the point \n   you're interested in, but when you start out and see the top of the \n   file that generally gets annotated last, a natural reaction with the \n   current interface is to wait for the annotations to happen rather than \n   actually start looking at the code.\n\n   Which is silly. You end up waiting for somethign that you don't even \n   really care about..\n\n   Again, I think the basic issue is the same: by making the annotation \n   data *so* prominent, the lack of it just forces you mentally to think \n   that something important is missing.\n\n - Finally, the purely practical issue of \"on a small screen, this would \n   be almost impossible to use\".\n\n   Optimally, you should be able to see the whole (or at least the bulk) \n   of the actual file content even if you only had 80-character lines in \n   the blame viewer. And I just tested: if I make the blame viewer 80 \n   characters wide when I look at a random kernel file annotation, I don't \n   even see the \"current line number\", much less the actual file data. And \n   remember: the file data was supposed to be the *primary* thing.\n\n   If I make it 110 characters wide, I can see ~20 characters of the file \n   data, which means that I can't actually make sense out of anything that \n   is indented by more than two indents, and I usually can't even see the \n   full function names - much less arguments - in declarations..\n\nAnyway, all of these issues makes me suspect that the proper blame \ninterface is to basically *hide* the blame almost entirely, in order to \nmake the important parts much more visible, and in order to encourage \npeople to start looking for the piece of code that they are actually \ninterested in.\n\nThen, some *small* part of the annotation window (preferably on the \nright-hand side) should have some very basic blame info - possibly even \njust a \"grouping hint\" to see where the blame boundaries are. And only \nwhen you mouse over it or something, do you get the full data.\n\nI dunno. I'm horrible at actually doing GUI's, so you should take anything \nI say with a grain of salt. At the same time, I do know what *I* consider \nto be important (which tends to be unusual in a user), and I'd like to \nthink that I have a clue about how people work. And I've always hated \n\"annotate\" in CVS, but git made it even worse by making the annotation \ndata much bigger.\n\n(Yes, from a technical standpoint making the annotation data bigger is a \ngood thign: git simply has more useful information than CVS does. But the \nlack of information in CVS actually makes the \"stupid interface\" better, \nif only because you don't waste as much space on it).\n\nBut I'm not going to be able to actually do what I describe above. I can \nonly hope to inspire somebody else..\n\n\t\t\tLinus\n"},{"id":"32964","messageId":"81b0412b0701290854l52d54879l1e2edbfeb1872951@mail.gmail.com","threadId":"6545","inReplyTo":"7vfy9ublvj.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git blame --progress","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2007-01-29T16:54:41Z","receivedAt":"2007-01-29T16:54:41Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"On 1/29/07, Junio C Hamano <junkio@cox.net> wrote:\n> [PATCH] git blame --progress\n>\n> With  --progress option, the command shows a fairly useless but\n> amusing eye-candy while making the user wait.\n>\n\nIt is not only amusing - it also gives the user a visual\ninformation (not precise, but interesting) about something happening,\nhow fast is it happening and how long to wait.\nI like it, even though I seldom use git-blame myself, it's the kind of\nnice thing you have a fond memories afterwards.\nLike the ascii graphics :)\n"},{"id":"32965","messageId":"Pine.LNX.4.64.0701291224100.3021@xanadu.home","threadId":"6545","inReplyTo":"Pine.LNX.4.63.0701281425270.26863@qynat.qvtvafvgr.pbz","subject":"Re: More precise tag following","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2007-01-29T17:34:20Z","receivedAt":"2007-01-29T17:34:20Z","isPatch":false,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"On Sun, 28 Jan 2007, David Lang wrote:\n\n> On Sat, 27 Jan 2007, Chris Lee wrote:\n> \n> > From: Chris Lee <clee@kde.org>\n> > On 1/27/07, Linus Torvalds <torvalds@linux-foundation.org> wrote:\n> > > Do you have the converted repo somewhere to be cloned for? It's going to\n> > > be a lot more interesting for scalability testing than anything else.\n> >\n> > I don't have access to any servers that I could drop a 3GB packfile\n> > onto and expect them to serve it. And I don't have a connection at\n> > home that I could use to upload the 3GB pack from quickly - it would\n> > take days, at least. If anybody wants to hook me up with a hosting\n> > provider or a machine that just the git devs can access, I'd be\n> > willing to tie up my upstream bandwidth for a few days so you all can\n> > have access to it.\n\n> if nobody else steps forward I can arrange something like this on my home\n> server (only 768K updtream bandwidth, but it's better then nothing)\n\nHey guys,\n\nWe might be a couple people interested in this pack (I do as pack \nperformance is one of my main interest in git).  Linus is interested, \nand I'd guess Junio too, maybe a few others.\n\nChris: why don't you just set up a Bittorrent feed for it?  When we'll \nall start fetching it then the bandwidth will increasingly be shared \namongst all interested people.\n\n\nNicolas\n"},{"id":"32967","messageId":"Pine.LNX.4.64.0701290940520.3611@woody.linux-foundation.org","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701291224100.3021@xanadu.home","subject":"Re: More precise tag following","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-01-29T17:42:20Z","receivedAt":"2007-01-29T17:42:20Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 29 Jan 2007, Nicolas Pitre wrote:\n> \n> Chris: why don't you just set up a Bittorrent feed for it?  When we'll \n> all start fetching it then the bandwidth will increasingly be shared \n> amongst all interested people.\n\nWell, it doesn't really help Chris. All the data will end up starting from \nhim anyway. \n\nThe problem isn't the bandwidth for lots of people to download it, but the \nbandwidth for a *single* upload ;)\n\nOnce it's uploaded anywhere, we've got people willing to mirror it \ninfinitely ..\n\n\t\tLinus\n"},{"id":"32968","messageId":"Pine.LNX.4.64.0701291246090.3021@xanadu.home","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701290940520.3611@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2007-01-29T17:58:39Z","receivedAt":"2007-01-29T17:58:39Z","isPatch":false,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"On Mon, 29 Jan 2007, Linus Torvalds wrote:\n\n> \n> \n> On Mon, 29 Jan 2007, Nicolas Pitre wrote:\n> > \n> > Chris: why don't you just set up a Bittorrent feed for it?  When we'll \n> > all start fetching it then the bandwidth will increasingly be shared \n> > amongst all interested people.\n> \n> Well, it doesn't really help Chris. All the data will end up starting from \n> him anyway. \n\nSure, but I was under the impression this wasn't the problem.  Given \nwhat Chris said:\n\n|I don't have access to any servers that I could drop a 3GB packfile \n|onto and expect them to serve it. [...] If anybody wants to hook me up\n|with a hosting provider or a machine that just the git devs can access, \n|I'd be willing to tie up my upstream bandwidth for a few days so you \n|all can have access to it.\n\nAnd then David said:\n\n|if nobody else steps forward I can arrange something like this on my \n|home server (only 768K updtream bandwidth, but it's better then \n|nothing)\n\nSo it looks like the server was the main issue here.\n\n> Once it's uploaded anywhere, we've got people willing to mirror it \n> infinitely ..\n\nHas this been set up with Chris already?\n\n\nNicolas\n"},{"id":"32969","messageId":"45BE37F1.9090409@fs.ei.tum.de","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701290759570.3611@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Simon 'corecode' Schubert","fromEmail":"corecode@fs.ei.tum.de","sentAt":"2007-01-29T18:07:45Z","receivedAt":"2007-01-29T18:07:45Z","isPatch":false,"sender":{"key":"corecode@fs.ei.tum.de","avatar":"https://gravatar.com/avatar/eff9dbf0cdac0d1e6a6cd7ed0e50763edcb376b493b5253a35ff167918ad79e1?d=mp&s=160"},"body":"Linus Torvalds wrote:\n> (Yes, from a technical standpoint making the annotation data bigger is a \n> good thign: git simply has more useful information than CVS does. But the \n> lack of information in CVS actually makes the \"stupid interface\" better, \n> if only because you don't waste as much space on it).\n\nI absolutely agree.  My primary workflow around cvs annotate is about this:  I read code and would like to know why this one snippet was introduced in the first place (or changed).  So I go cvs annotate in my browser, and in parallel I display the cvs log, to actually see the commit message.  Then I retrieve the diff output to see what happened, maybe starting the cycle again with an older version.\n\nWhat I want to illustrate is:  No matter how much information you show in one line, you won't be able to fit all possible information.  So my dream interface is a display which shows which runs of lines were changed together, and in which order the runs were changed.  A temporary numbering of commits might help here (in CVS it is clear).  Now when I identify a run, i'd like to have an easy way to retrieve the git log -p output.  An additional blame should work on the parent of the commit associated with the current line (so that I can see how the line looked before this commit, and when this was changed).\n\ncheers\n  simon\n\n-- \nServe - BSD     +++  RENT this banner advert  +++    ASCII Ribbon   /\"\\\nWork - Mac      +++  space for low €€€ NOW!1  +++      Campaign     \\ /\nParty Enjoy Relax   |   http://dragonflybsd.org      Against  HTML   \\\nDude 2c 2 the max   !   http://golden-apple.biz       Mail + News   / \\\n\n"},{"id":"32970","messageId":"20070129181207.GA29451@moooo.ath.cx","threadId":"6545","inReplyTo":"7vfy9ublvj.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git blame --progress","fromName":"Matthias Lederhofer","fromEmail":"matled@gmx.net","sentAt":"2007-01-29T18:12:07Z","receivedAt":"2007-01-29T18:12:07Z","isPatch":true,"sender":{"key":"matled@gmx.net","avatar":null},"body":"Junio C Hamano <junkio@cox.net> wrote:\n> With  --progress option, the command shows a fairly useless but\n> amusing eye-candy while making the user wait.\nLooks nice :)  It makes it much more comfortable to wait for the real\noutput.  Perhaps there should be a config option to enable the\neye-candy when git-blame is run on a terminal (e.g. (configoption &&\nstdout is a tty && stderr is a tty) || --progress)?  Typing --progress\nis annoying.\n\nI also noticed that git-blame with --progress is slowed down a bit by\nthe terminal here; the output on stderr was ~400kb.\n"},{"id":"32971","messageId":"7vr6td7iv2.fsf@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":"20070129181207.GA29451@moooo.ath.cx","subject":"Re: [PATCH] git blame --progress","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-29T19:06:41Z","receivedAt":"2007-01-29T19:06:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthias Lederhofer <matled@gmx.net> writes:\n\n> Junio C Hamano <junkio@cox.net> wrote:\n>> With  --progress option, the command shows a fairly useless but\n>> amusing eye-candy while making the user wait.\n>\n> Looks nice :)  It makes it much more comfortable to wait for the real\n> output.  Perhaps there should be a config option to enable the\n> eye-candy when git-blame is run on a terminal (e.g. (configoption &&\n> stdout is a tty && stderr is a tty) || --progress)?  Typing --progress\n> is annoying.\n>\n> I also noticed that git-blame with --progress is slowed down a bit by\n> the terminal here; the output on stderr was ~400kb.\n\nEasy, that was not a serious patch meant for inclusion.\n\nIf somebody wants to polish it up and re-submit that is fine by\nme.  It might be interesting to add colors, make it less\ndependent on ANSI terminal control, and handle half-dots more\nintelligently.\n"},{"id":"32972","messageId":"204011cb0701291116r4169a0d9gf8837957c3f028d4@mail.gmail.com","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701291246090.3021@xanadu.home","subject":"Re: More precise tag following","fromName":"Chris Lee","fromEmail":"clee@kde.org","sentAt":"2007-01-29T19:16:43Z","receivedAt":"2007-01-29T19:16:43Z","isPatch":false,"sender":{"key":"clee@kde.org","avatar":"https://gravatar.com/avatar/c930bdc8cc6465094a5722188409ecb8955e0da2b188d7340137074b08f857e3?d=mp&s=160"},"body":"On 1/29/07, Nicolas Pitre <nico@cam.org> wrote:\n> On Mon, 29 Jan 2007, Linus Torvalds wrote:\n> > Well, it doesn't really help Chris. All the data will end up starting from\n> > him anyway.\n>\n> Sure, but I was under the impression this wasn't the problem.  Given\n> what Chris said:\n>\n> |I don't have access to any servers that I could drop a 3GB packfile\n> |onto and expect them to serve it. [...] If anybody wants to hook me up\n> |with a hosting provider or a machine that just the git devs can access,\n> |I'd be willing to tie up my upstream bandwidth for a few days so you\n> |all can have access to it.\n>\n> And then David said:\n>\n> |if nobody else steps forward I can arrange something like this on my\n> |home server (only 768K updtream bandwidth, but it's better then\n> |nothing)\n>\n> So it looks like the server was the main issue here.\n>\n> > Once it's uploaded anywhere, we've got people willing to mirror it\n> > infinitely ..\n>\n> Has this been set up with Chris already?\n\nI'm going to burn a DVD with the repo on it later today and mail it to\nhpa since he's only like two towns over.\n\nSo, hopefully, it'll be up somewhere useful by the end of the week :)\n"},{"id":"32973","messageId":"20070129192911.GA12903@thunk.org","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701290759570.3611@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Theodore Tso","fromEmail":"tytso@mit.edu","sentAt":"2007-01-29T19:29:12Z","receivedAt":"2007-01-29T19:29:12Z","isPatch":false,"sender":{"key":"tytso@mit.edu","avatar":"https://avatars.githubusercontent.com/u/51416?v=4"},"body":"On Mon, Jan 29, 2007 at 08:24:52AM -0800, Linus Torvalds wrote:\n> Anyway, all of these issues makes me suspect that the proper blame \n> interface is to basically *hide* the blame almost entirely, in order to \n> make the important parts much more visible, and in order to encourage \n> people to start looking for the piece of code that they are actually \n> interested in.\n\nOne approach which might work is where you hover your mouse over a\nline, and it pops up a tiny window with the blame information if the\nmouse remains stationary for more than a second or two.\n\nAnother thing which would be really useful is where the lines that\nhave been changed in the last n commits (where n is probably between\n3-5) are highlighted using different colors.  That way you can see\nwhat was changed recently, which is often what you are most interested\nin.  (As in, what changed recently that might have caused this file to\nget all screwed up?)\n\n\t\t\t\t\t\t- Ted\n"},{"id":"32974","messageId":"Pine.LNX.4.64.0701291140250.3611@woody.linux-foundation.org","threadId":"6545","inReplyTo":"20070129192911.GA12903@thunk.org","subject":"Re: More precise tag following","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-01-29T19:45:12Z","receivedAt":"2007-01-29T19:45:12Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 29 Jan 2007, Theodore Tso wrote:\n> \n> One approach which might work is where you hover your mouse over a\n> line, and it pops up a tiny window with the blame information if the\n> mouse remains stationary for more than a second or two.\n\nYes. I think that's the kind of interface that most people really want.\n\nOf course, almost always, you'd actually want it in your editor of choice, \nand that's not going to happen. But if it looks basically just like a \nnormal editor (perhaps with line numbers - that is fairly traditional in \nannotations), that's the basic most spartan interface I can think of. With \n\"hover\" just causing the extended information for the few lines around the \nmouse to show up (and a \"select <n> lines with mouse\" for more than just a \nfew lines).\n\n> Another thing which would be really useful is where the lines that\n> have been changed in the last n commits (where n is probably between\n> 3-5) are highlighted using different colors.\n\nThat would be very natural for the way \"git blame --incremental\" works, so \nyes, I agree. Not only does it highlight the likely interesting places, \nbut it's very much the natural flow for the whole tool.\n\nOk. Now we just need some sucker^H^H^H^H^H^Henterprising person to \nactually do this ;)\n\n\t\t\tLinus\n"},{"id":"32975","messageId":"45BE520A.6050906@lsrfire.ath.cx","threadId":"6545","inReplyTo":"7vfy9ublvj.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git blame --progress","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2007-01-29T19:59:06Z","receivedAt":"2007-01-29T19:59:06Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Junio C Hamano schrieb:\n> [PATCH] git blame --progress\n> \n> With  --progress option, the command shows a fairly useless but \n> amusing eye-candy while making the user wait.\n\nNicely done, I like it.  Well, then again, I used to watch the progress\nof filesystem defragmentors as a kid.  Ahem. :-P\n\nThe problem here is, of course, that we don't know how beforehand much\nwork needs to be done.  The indicator could be full of stars long before\nthe start of history is reached.\n\nThis could be helped somewhat by having three states instead of two:\nunblamed (.), blamed (o) and just-now-blamed (*).  Each time new stars\nare written you'd demote the other stars in the field to o's.  This way\nyou'll at least see something moving until the end, no matter how often\nblame is pushed further down for already blamed lines.\n\nThis increases terminal bandwidth usage and on-screen activity, but not\nnecessarily the usefulness of this thing. :)\n\nRené\n\n\ndiff --git a/builtin-blame.c b/builtin-blame.c\nindex cd54acf..9bed52f 100644\n--- a/builtin-blame.c\n+++ b/builtin-blame.c\n@@ -1229,18 +1229,9 @@ static int eye_candy_spot(struct scoreboard *sb, int lno)\n \treturn lno * cnt / sb->num_lines;\n }\n \n-static void update_eye_candy(struct scoreboard *sb, struct blame_entry *ent)\n+static void update_eye_candy_spots(int cnt, int spot_lo, int spot_hi, char c)\n {\n-\tint cnt = eye_candy_spots(sb);\n-\tint spot_lo, spot_hi, spot;\n-\tstruct blame_entry *lo, *hi;\n-\n-\tfor (lo = ent; lo->prev && lo->prev->guilty; lo = lo->prev)\n-\t\t;\n-\tspot_lo = eye_candy_spot(sb, lo->lno);\n-\tfor (hi = ent; hi->next && hi->next->guilty; hi = hi->next)\n-\t\t;\n-\tspot_hi = eye_candy_spot(sb, hi->lno + hi->num_lines - 1);\n+\tint spot;\n \n \tfor (spot = spot_lo; spot <= spot_hi; spot++) {\n \t\tint spot_x, spot_y;\n@@ -1257,13 +1248,35 @@ static void update_eye_candy(struct scoreboard *sb, struct blame_entry *ent)\n \t\t\tfprintf(stderr, \"\\033[%dA\", spot_y);\n \t\tif (spot_x)\n \t\t\tfprintf(stderr, \"\\033[%dC\", spot_x);\n-\t\tfputc('*', stderr);\n+\t\tfputc(c, stderr);\n \t\tfprintf(stderr, \"\\033[%dD\", spot_x + 1);\n \t\tif (spot_y)\n \t\t\tfprintf(stderr, \"\\033[%dB\", spot_y);\n \t}\n }\n \n+static void update_eye_candy(struct scoreboard *sb, struct blame_entry *ent)\n+{\n+\tint cnt = eye_candy_spots(sb);\n+\tint spot_lo, spot_hi;\n+\tstruct blame_entry *lo, *hi;\n+\tstatic int prev_cnt, prev_spot_lo, prev_spot_hi;\n+\n+\tfor (lo = ent; lo->prev && lo->prev->guilty; lo = lo->prev)\n+\t\t;\n+\tspot_lo = eye_candy_spot(sb, lo->lno);\n+\tfor (hi = ent; hi->next && hi->next->guilty; hi = hi->next)\n+\t\t;\n+\tspot_hi = eye_candy_spot(sb, hi->lno + hi->num_lines - 1);\n+\n+\tupdate_eye_candy_spots(prev_cnt, prev_spot_lo, prev_spot_hi, 'o');\n+\tupdate_eye_candy_spots(cnt, spot_lo, spot_hi, '*');\n+\n+\tprev_cnt = cnt;\n+\tprev_spot_lo = spot_lo;\n+\tprev_spot_hi = spot_hi;\n+}\n+\n static void found_guilty_entry(struct scoreboard *sb, struct blame_entry *ent)\n {\n \tif (ent->guilty)\n"},{"id":"32977","messageId":"Pine.LNX.4.64.0701291222540.3611@woody.linux-foundation.org","threadId":"6545","inReplyTo":"45BE520A.6050906@lsrfire.ath.cx","subject":"Re: [PATCH] git blame --progress","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-01-29T20:24:06Z","receivedAt":"2007-01-29T20:24:06Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 29 Jan 2007, René Scharfe wrote:\n> \n> The problem here is, of course, that we don't know how beforehand much\n> work needs to be done.  The indicator could be full of stars long before\n> the start of history is reached.\n\nWell, we do have an approximation for it: we know how many lines the file \nhas, and we do know (although we don't actually track) how many lines \nwe've blamed so far.\n\nSo it would be fairly easy to give at least a *rough* indication of \n\"percent blamed\" - although it doesn't necessarily say anything about how \nexpensive that last 1% is going to be..\n\n\t\tLinus"},{"id":"32976","messageId":"epll4j$iil$1@sea.gmane.org","threadId":"6545","inReplyTo":"20070129192911.GA12903@thunk.org","subject":"Re: More precise tag following","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-01-29T20:25:04Z","receivedAt":"2007-01-29T20:25:04Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Theodore Tso wrote:\n\n> On Mon, Jan 29, 2007 at 08:24:52AM -0800, Linus Torvalds wrote:\n>> Anyway, all of these issues makes me suspect that the proper blame \n>> interface is to basically *hide* the blame almost entirely, in order to \n>> make the important parts much more visible, and in order to encourage \n>> people to start looking for the piece of code that they are actually \n>> interested in.\n> \n> One approach which might work is where you hover your mouse over a\n> line, and it pops up a tiny window with the blame information if the\n> mouse remains stationary for more than a second or two.\n> \n> Another thing which would be really useful is where the lines that\n> have been changed in the last n commits (where n is probably between\n> 3-5) are highlighted using different colors.  That way you can see\n> what was changed recently, which is often what you are most interested\n> in.  (As in, what changed recently that might have caused this file to\n> get all screwed up?)\n\nIt would be also nice to have window split into two, and for example\nhave at bottom details of the commit which changed current line, like\nauthor, description, date, how many commits ago, branch name (e.g. taken\nfrom commit message if it was merged), perhaps also patch...\n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"32978","messageId":"20070129204740.GA30963@spearce.org","threadId":"6545","inReplyTo":"epll4j$iil$1@sea.gmane.org","subject":"Re: More precise tag following","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-01-29T20:47:40Z","receivedAt":"2007-01-29T20:47:40Z","isPatch":false,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> wrote:\n> Theodore Tso wrote:\n> > On Mon, Jan 29, 2007 at 08:24:52AM -0800, Linus Torvalds wrote:\n> >> Anyway, all of these issues makes me suspect that the proper blame \n> >> interface is to basically *hide* the blame almost entirely, in order to \n> >> make the important parts much more visible, and in order to encourage \n> >> people to start looking for the piece of code that they are actually \n> >> interested in.\n> > \n> > One approach which might work is where you hover your mouse over a\n> > line, and it pops up a tiny window with the blame information if the\n> > mouse remains stationary for more than a second or two.\n> > \n> > Another thing which would be really useful is where the lines that\n> > have been changed in the last n commits (where n is probably between\n> > 3-5) are highlighted using different colors.  That way you can see\n> > what was changed recently, which is often what you are most interested\n> > in.  (As in, what changed recently that might have caused this file to\n> > get all screwed up?)\n> \n> It would be also nice to have window split into two, and for example\n> have at bottom details of the commit which changed current line, like\n> author, description, date, how many commits ago, branch name (e.g. taken\n> from commit message if it was merged), perhaps also patch...\n\nThese are some really good ideas.  I knew that if I made pretty\ntechnicolor crap available, people would tell me what they really\nneeded.  :-)\n\nI'll like steal them (er, uhm, implement them) in git-gui in the next\nday or so.  The current blame UI was sort of a prototype.  Once I\ntossed the original filename and original line number into that\nthing it started to become pretty obvious its just too cluttered.\nBut at that point I wanted pretty colors, and uh, it was late... :-)\n\n-- \nShawn.\n"},{"id":"32979","messageId":"200701292202.14293.jnareb@gmail.com","threadId":"6545","inReplyTo":"20070129204740.GA30963@spearce.org","subject":"Re: More precise tag following","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-01-29T21:02:13Z","receivedAt":"2007-01-29T21:02:13Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Shawn O. Pearce wrote:\n\n> I'll like steal them (er, uhm, implement them) in git-gui in the next\n> day or so.  The current blame UI was sort of a prototype.  Once I\n> tossed the original filename and original line number into that\n> thing it started to become pretty obvious its just too cluttered.\n> But at that point I wanted pretty colors, and uh, it was late... :-)\n\nYou can borrow some of the ideas from gitweb new blame output \n(git_blame2) by Luben and Junio, too...\n\n-- \nJakub Narebski\nPoland\n"},{"id":"32984","messageId":"20070129230050.GA15492@localdomain","threadId":"6545","inReplyTo":"204011cb0701271136m655815f6o1501de2bf699b362@mail.gmail.com","subject":"Re: More precise tag following","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2007-01-29T23:00:50Z","receivedAt":"2007-01-29T23:00:50Z","isPatch":false,"sender":{"key":"e@80x24.org","avatar":null},"body":"Chris Lee <clee@kde.org> wrote:\n> Also, please note, the 3GB packed repo is only about 2/3 of the full\n> KDE repo - I cut off the import at revision 409202, because that was\n> when the KDE svn admins decided to move a bunch of modules from\n> /trunk/ to /trunk/KDE/ and it screws up everything. So a *full* KDE\n> history import would definitely be more than 4GB, packed.\n\nHmm.. This movement from /trunk to /trunk/KDE could be a good case\nfor the (still-improving) --follow-parent feature in git-svn.\n\nI've resigned to the fact that git-svn (and git) is not ideal for\ntracking the entire KDE repository.   I don't think most (sane)\ndevelopers check out the entire repository root when working on KDE,\nand cloning a 3GB pack just isn't realistic these days.\n\nBut if splitting the monster repository into separate repositories is an\noption for KDE, and I can make --follow-parent do what it's supposed to\ndo very well: then it could be a nice way to make things more manageable\nfor developers that will eventually switch to.\n\nFor tracking the trunk of kde-common, I'm running with my\nwork-in-progress version of git-svn:\n\ngit-svn init -i kde-common svn://anonsvn.kde.org/home/kde/trunk/KDE/kde-common\ngit-svn fetch -i kde-common --follow-parent\n\nIt seems to be following kde-common into pre-409202 revisions (down to\nr11472) pretty well.  I'll upload the result to git.bogomips.org when\nI'm done.\n\nYou can get my latest git-svn here: git://git.bogomips.org/git-svn\nNote: I've not used this to dcommit for serious work, so I can't tell\nif it's really useful.  But fetching seems fine.\n\nThe current --follow-parent implementation does not handle some cases\nvery well.  I would like to get it to correctly track parents analogous\nto the way gitk was merged into git, so that subprojects merged into\na bigger project are currently ignored.\n\nJunio: there are some huge changes; so please don't merge it into master\nyet, I don't feel it's ready yet, but it should be sometime in the next\nweek.  I would like to see this in 1.5.0, but only if it's not horribly\nbroken (especially w.r.t committing).\n\n-- \nEric Wong\n"},{"id":"32997","messageId":"20070130004247.GA18213@localdomain","threadId":"6545","inReplyTo":"20070129230050.GA15492@localdomain","subject":"Re: More precise tag following","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2007-01-30T00:42:47Z","receivedAt":"2007-01-30T00:42:47Z","isPatch":false,"sender":{"key":"e@80x24.org","avatar":null},"body":"Eric Wong <normalperson@yhbt.net> wrote:\n> It seems to be following kde-common into pre-409202 revisions (down to\n> r11472) pretty well.  I'll upload the result to git.bogomips.org when\n> I'm done.\n\nThis will have to wait: I'm getting \"Malformed network data\" errors with\nboth do_update and do_switch (not yet working on any release version of\nSVN incl. 1.4.3).  I'll have to examine this a bit more when I get the\ntime.\n\n-- \nEric Wong\n"},{"id":"32998","messageId":"20070130004810.GB18213@localdomain","threadId":"6545","inReplyTo":"20070130004247.GA18213@localdomain","subject":"Re: More precise tag following","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2007-01-30T00:48:10Z","receivedAt":"2007-01-30T00:48:10Z","isPatch":false,"sender":{"key":"e@80x24.org","avatar":null},"body":"Eric Wong <normalperson@yhbt.net> wrote:\n> Eric Wong <normalperson@yhbt.net> wrote:\n> > It seems to be following kde-common into pre-409202 revisions (down to\n> > r11472) pretty well.  I'll upload the result to git.bogomips.org when\n> > I'm done.\n> \n> This will have to wait: I'm getting \"Malformed network data\" errors with\n> both do_update and do_switch (not yet working on any release version of\n> SVN incl. 1.4.3).  I'll have to examine this a bit more when I get the\n> time.\n\nAlso, you should *not* be able to reproduce this error if you try\ntracking kde-common on a local file:// repository.\n\n-- \nEric Wong\n"},{"id":"33000","messageId":"7vlkjl700g.fsf@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":"45BE520A.6050906@lsrfire.ath.cx","subject":"Re: [PATCH] git blame --progress","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-30T01:53:51Z","receivedAt":"2007-01-30T01:53:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"René Scharfe <rene.scharfe@lsrfire.ath.cx> writes:\n\n> Junio C Hamano schrieb:\n>> [PATCH] git blame --progress\n>> \n>> With  --progress option, the command shows a fairly useless but \n>> amusing eye-candy while making the user wait.\n>\n> Nicely done, I like it.  Well, then again, I used to watch the progress\n> of filesystem defragmentors as a kid.  Ahem. :-P\n>\n> The problem here is, of course, that we don't know how beforehand much\n> work needs to be done.  The indicator could be full of stars long before\n> the start of history is reached.\n>\n> This could be helped somewhat by having three states instead of two:\n> unblamed (.), blamed (o) and just-now-blamed (*).  Each time new stars\n> are written you'd demote the other stars in the field to o's.  This way\n> you'll at least see something moving until the end, no matter how often\n> blame is pushed further down for already blamed lines.\n>\n> This increases terminal bandwidth usage and on-screen activity, but not\n> necessarily the usefulness of this thing. :)\n\nI do not know if this is working as you intended.\n\nIf somebody wants to really do this, the first clean-up to be\ndone is to remove the two loops that goes back and forward to\nfind the continguous guilty range.  That was done only because I\nwas lazy and did not want to count the boundary to deal with a\nhalf-dot problem (a displayed column on the screen can represent\nN lines -- what happens when the blame entry whose origin is now\nknown covers only partially?  You need to either draw it as a\nhalf-done, or you make sure to paint it only when all of the N\nlines are blamed).  The output from my original will repaint the\nwhole thing at the end of the blame because at that point\nspot_lo and spot_hi would cover the entire range -- which shows\nhow lazy I am ;-).\n"},{"id":"33022","messageId":"20070130085116.GC18213@localdomain","threadId":"6545","inReplyTo":"20070130004247.GA18213@localdomain","subject":"Re: More precise tag following","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2007-01-30T08:51:16Z","receivedAt":"2007-01-30T08:51:16Z","isPatch":false,"sender":{"key":"e@80x24.org","avatar":null},"body":"Eric Wong <normalperson@yhbt.net> wrote:\n> Eric Wong <normalperson@yhbt.net> wrote:\n> > It seems to be following kde-common into pre-409202 revisions (down to\n> > r11472) pretty well.  I'll upload the result to git.bogomips.org when\n> > I'm done.\n\nOk, kde-common is available here: http://git.bogomips.org/kde-common.git\n\n> This will have to wait: I'm getting \"Malformed network data\" errors with\n> both do_update and do_switch (not yet working on any release version of\n> SVN incl. 1.4.3).  I'll have to examine this a bit more when I get the\n> time.\n\nActually, I think I'm going crazy, do_update works fine, do_switch\n(which no released version of SVN supports, yet) did not because of\nreparenting.  Nevertheless, everything appears to work with my latest\ngit-svn (git://git.bogomips.org/git-svn.git)\n\n-- \nEric Wong\n"},{"id":"33024","messageId":"7vodog3m3f.fsf@assigned-by-dhcp.cox.net","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701281143190.25027@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-30T09:22:44Z","receivedAt":"2007-01-30T09:22:44Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> One thing I looked at, which *should* be easy to do inside \"git-blame\", is \n> to make the case where you do *not* give a head to start with, default to \n> \"current working tree\" instead of HEAD.\n\nThis is still very rough; the existing diff frontends are mess\nand making diff-cache and diff-tree behave more or less\ninterchangeably is quite a pain.  I am not proud of the new\ndo_diff_cache() interface I had to add, which is probably\ntotally useless for anybody other than the three calling sites\nthis patch has.\n\nI tested only the most trivial case that exercises the\ndo_diff_cache() cal in find_origin() before I got too tired, and\nI am retiring to bed now.\n\n-- >8 --\n[PATCH] git-blame: no rev means start from the working tree file.\n\nWarning: this changes the semantics.\n\nThis is a WIP to make \"git blame\" without any positive rev to\nstart digging from the working tree copy, which is made into a\nfake commit whose sole parent is the HEAD.\n\nIt might make sense to give \"git-blame --cached\" to start\ndigging from the index as well, which should be trivial.\n\nThe calls to do_diff_cache() in find_copy_in_parent() and\nfind_rename() need to be vetted, as I haven't checked them yet.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n builtin-blame.c |  119 ++++++++++++++++++++++++++++++++++++++++++++----------\n cache.h         |    1 +\n diff-lib.c      |   22 ++++++++++-\n diff.h          |    1 +\n ident.c         |    8 ++--\n 5 files changed, 124 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin-blame.c b/builtin-blame.c\nindex 3033e9b..a8668c0 100644\n--- a/builtin-blame.c\n+++ b/builtin-blame.c\n@@ -333,9 +333,13 @@ static struct origin *find_origin(struct scoreboard *sb,\n \tdiff_tree_setup_paths(paths, &diff_opts);\n \tif (diff_setup_done(&diff_opts) < 0)\n \t\tdie(\"diff-setup\");\n-\tdiff_tree_sha1(parent->tree->object.sha1,\n-\t\t       origin->commit->tree->object.sha1,\n-\t\t       \"\", &diff_opts);\n+\n+\tif (is_null_sha1(origin->commit->object.sha1))\n+\t\tdo_diff_cache(parent->tree->object.sha1, &diff_opts, 0);\n+\telse\n+\t\tdiff_tree_sha1(parent->tree->object.sha1,\n+\t\t\t       origin->commit->tree->object.sha1,\n+\t\t\t       \"\", &diff_opts);\n \tdiffcore_std(&diff_opts);\n \n \t/* It is either one entry that says \"modified\", or \"created\",\n@@ -402,9 +406,13 @@ static struct origin *find_rename(struct scoreboard *sb,\n \tdiff_tree_setup_paths(paths, &diff_opts);\n \tif (diff_setup_done(&diff_opts) < 0)\n \t\tdie(\"diff-setup\");\n-\tdiff_tree_sha1(parent->tree->object.sha1,\n-\t\t       origin->commit->tree->object.sha1,\n-\t\t       \"\", &diff_opts);\n+\n+\tif (is_null_sha1(origin->commit->object.sha1))\n+\t\tdo_diff_cache(parent->tree->object.sha1, &diff_opts, 0);\n+\telse\n+\t\tdiff_tree_sha1(parent->tree->object.sha1,\n+\t\t\t       origin->commit->tree->object.sha1,\n+\t\t\t       \"\", &diff_opts);\n \tdiffcore_std(&diff_opts);\n \n \tfor (i = 0; i < diff_queued_diff.nr; i++) {\n@@ -1047,9 +1055,12 @@ static int find_copy_in_parent(struct scoreboard *sb,\n \t    (!porigin || strcmp(target->path, porigin->path)))\n \t\tdiff_opts.find_copies_harder = 1;\n \n-\tdiff_tree_sha1(parent->tree->object.sha1,\n-\t\t       target->commit->tree->object.sha1,\n-\t\t       \"\", &diff_opts);\n+\tif (is_null_sha1(target->commit->object.sha1))\n+\t\tdo_diff_cache(parent->tree->object.sha1, &diff_opts, 0);\n+\telse\n+\t\tdiff_tree_sha1(parent->tree->object.sha1,\n+\t\t\t       target->commit->tree->object.sha1,\n+\t\t\t       \"\", &diff_opts);\n \n \tif (!diff_opts.find_copies_harder)\n \t\tdiffcore_std(&diff_opts);\n@@ -1910,6 +1921,64 @@ static int git_blame_config(const char *var, const char *value)\n \treturn git_default_config(var, value);\n }\n \n+static struct commit *fake_working_tree_commit(const char *path)\n+{\n+\tstruct stat st;\n+\tstruct commit *commit;\n+\tstruct origin *origin;\n+\tunsigned char head_sha1[20];\n+\tchar *buf;\n+\tconst char *ident;\n+\tint fd;\n+\n+\tif (lstat(path, &st) < 0)\n+\t\tdie(\"Cannot lstat %s\", path);\n+\tif (get_sha1(\"HEAD\", head_sha1))\n+\t\tdie(\"No such ref: HEAD\");\n+\n+\tcommit = xcalloc(1, sizeof(*commit));\n+\tcommit->parents = xcalloc(1, sizeof(*commit->parents));\n+\tcommit->parents->item = lookup_commit_reference(head_sha1);\n+\tcommit->object.parsed = 1;\n+\tcommit->date = st.st_mtime;\n+\tcommit->object.type = OBJ_COMMIT;\n+\n+\torigin = make_origin(commit, path);\n+\torigin->file.ptr = buf = xmalloc(st.st_size+1);\n+\torigin->file.size = st.st_size;\n+\tbuf[st.st_size] = 0;\n+\n+\tswitch (st.st_mode & S_IFMT) {\n+\tcase S_IFREG:\n+\t\tfd = open(path, O_RDONLY);\n+\t\tif (fd < 0)\n+\t\t\tdie(\"cannot open %s\", path);\n+\t\tif (read_in_full(fd, buf, st.st_size) != st.st_size)\n+\t\t\tdie(\"cannot read %s\", path);\n+\t\tbreak;\n+\tcase S_IFLNK:\n+\t\tif (readlink(path, buf, st.st_size+1) != st.st_size)\n+\t\t\tdie(\"cannot readlink %s\", path);\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(\"unsupported file type %s\", path);\n+\t}\n+\thash_sha1_file(buf, st.st_size, blob_type, origin->blob_sha1);\n+\tcommit->util = origin;\n+\n+\tcommit->buffer = xmalloc(400);\n+\tident = fmt_ident(\"Not Committed Yet\", \"not.committed.yet\", NULL, 0);\n+\tsprintf(commit->buffer,\n+\t\t\"tree 0000000000000000000000000000000000000000\\n\"\n+\t\t\"parent %s\\n\"\n+\t\t\"author %s\\n\"\n+\t\t\"committer %s\\n\\n\"\n+\t\t\"Version of %s from the working tree\",\n+\t\tsha1_to_hex(head_sha1),\n+\t\tident, ident, path);\n+\treturn commit;\n+}\n+\n int cmd_blame(int argc, const char **argv, const char *prefix)\n {\n \tstruct rev_info revs;\n@@ -2087,7 +2156,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \targv[unk] = NULL;\n \n \tinit_revisions(&revs, NULL);\n-\tsetup_revisions(unk, argv, &revs, \"HEAD\");\n+\tsetup_revisions(unk, argv, &revs, NULL);\n \tmemset(&sb, 0, sizeof(sb));\n \n \t/*\n@@ -2114,15 +2183,10 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tif (!sb.final) {\n \t\t/*\n \t\t * \"--not A B -- path\" without anything positive;\n-\t\t * default to HEAD.\n+\t\t * do not default to HEAD, but use the cache.\n \t\t */\n-\t\tunsigned char head_sha1[20];\n-\n-\t\tfinal_commit_name = \"HEAD\";\n-\t\tif (get_sha1(final_commit_name, head_sha1))\n-\t\t\tdie(\"No such ref: HEAD\");\n-\t\tsb.final = lookup_commit_reference(head_sha1);\n-\t\tadd_pending_object(&revs, &(sb.final->object), \"HEAD\");\n+\t\tsb.final = fake_working_tree_commit(path);\n+\t\tadd_pending_object(&revs, &(sb.final->object), \":\");\n \t}\n \n \t/*\n@@ -2132,11 +2196,22 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t */\n \tprepare_revision_walk(&revs);\n \n-\to = get_origin(&sb, sb.final, path);\n-\tif (fill_blob_sha1(o))\n-\t\tdie(\"no such path %s in %s\", path, final_commit_name);\n+\tif (is_null_sha1(sb.final->object.sha1)) {\n+\t\tchar *buf;\n+\t\to = sb.final->util;\n+\t\tbuf = xmalloc(o->file.size + 1);\n+\t\tmemcpy(buf, o->file.ptr, o->file.size + 1);\n+\t\tsb.final_buf = buf;\n+\t\tsb.final_buf_size = o->file.size;\n+\t}\n+\telse {\n+\t\to = get_origin(&sb, sb.final, path);\n+\t\tif (fill_blob_sha1(o))\n+\t\t\tdie(\"no such path %s in %s\", path, final_commit_name);\n \n-\tsb.final_buf = read_sha1_file(o->blob_sha1, type, &sb.final_buf_size);\n+\t\tsb.final_buf = read_sha1_file(o->blob_sha1, type,\n+\t\t\t\t\t      &sb.final_buf_size);\n+\t}\n \tnum_read_blob++;\n \tlno = prepare_lines(&sb);\n \ndiff --git a/cache.h b/cache.h\nindex 9873ee9..dcceea4 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -321,6 +321,7 @@ unsigned long approxidate(const char *);\n \n extern const char *git_author_info(int);\n extern const char *git_committer_info(int);\n+extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);\n \n struct checkout {\n \tconst char *base_dir;\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 2c9be60..b93f7a3 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -271,7 +271,7 @@ static int diff_cache(struct rev_info *revs,\n \t\t\t\tbreak;\n \t\t\t}\n \t\t\t/* Show difference between old and new */\n-\t\t\tshow_modified(revs,ac[1], ce, 1,\n+\t\t\tshow_modified(revs, ac[1], ce, 1,\n \t\t\t\t      cached, match_missing);\n \t\t\tbreak;\n \t\tcase 1:\n@@ -372,3 +372,23 @@ int run_diff_index(struct rev_info *revs, int cached)\n \tdiff_flush(&revs->diffopt);\n \treturn ret;\n }\n+\n+int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt, int cached)\n+{\n+\tstruct tree *tree;\n+\tstruct rev_info revs;\n+\n+\tinit_revisions(&revs, NULL);\n+\trevs.prune_data = opt->paths;\n+\tdiscard_cache();\n+\tif (read_cache() < 0)\n+\t\tdie(\"cannot read index\");\n+\tmark_merge_entries();\n+\ttree = parse_tree_indirect(tree_sha1);\n+\tif (!tree)\n+\t\tdie(\"bad tree object %s\", sha1_to_hex(tree_sha1));\n+\tif (read_tree(tree, 1, opt->paths))\n+\t\treturn error(\"unable to read tree %s\", sha1_to_hex(tree_sha1));\n+\treturn diff_cache(&revs, active_cache, active_nr, revs.prune_data,\n+\t\t\t  cached, 0);\n+}\ndiff --git a/diff.h b/diff.h\nindex 7a347cf..dd180b8 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -222,6 +222,7 @@ extern int run_diff_files(struct rev_info *revs, int silent_on_removed);\n \n extern int run_diff_index(struct rev_info *revs, int cached);\n \n+extern int do_diff_cache(const unsigned char *, struct diff_options *, int);\n extern int diff_flush_patch_id(struct diff_options *, unsigned char *);\n \n #endif /* DIFF_H */\ndiff --git a/ident.c b/ident.c\nindex a6fc7b5..bb03bdd 100644\n--- a/ident.c\n+++ b/ident.c\n@@ -185,8 +185,8 @@ static const char *env_hint =\n \"Add --global to set your account\\'s default\\n\"\n \"\\n\";\n \n-static const char *get_ident(const char *name, const char *email,\n-\t\t\t     const char *date_str, int error_on_no_name)\n+const char *fmt_ident(const char *name, const char *email,\n+\t\t      const char *date_str, int error_on_no_name)\n {\n \tstatic char buffer[1000];\n \tchar date[50];\n@@ -233,7 +233,7 @@ static const char *get_ident(const char *name, const char *email,\n \n const char *git_author_info(int error_on_no_name)\n {\n-\treturn get_ident(getenv(\"GIT_AUTHOR_NAME\"),\n+\treturn fmt_ident(getenv(\"GIT_AUTHOR_NAME\"),\n \t\t\t getenv(\"GIT_AUTHOR_EMAIL\"),\n \t\t\t getenv(\"GIT_AUTHOR_DATE\"),\n \t\t\t error_on_no_name);\n@@ -241,7 +241,7 @@ const char *git_author_info(int error_on_no_name)\n \n const char *git_committer_info(int error_on_no_name)\n {\n-\treturn get_ident(getenv(\"GIT_COMMITTER_NAME\"),\n+\treturn fmt_ident(getenv(\"GIT_COMMITTER_NAME\"),\n \t\t\t getenv(\"GIT_COMMITTER_EMAIL\"),\n \t\t\t getenv(\"GIT_COMMITTER_DATE\"),\n \t\t\t error_on_no_name);\n-- \n1.5.0.rc2.77.g1732a\n"},{"id":"33040","messageId":"20070130153126.GC25779@spearce.org","threadId":"6545","inReplyTo":"7vodog3m3f.fsf@assigned-by-dhcp.cox.net","subject":"Re: More precise tag following","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-01-30T15:31:26Z","receivedAt":"2007-01-30T15:31:26Z","isPatch":false,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Junio C Hamano <junkio@cox.net> wrote:\n> This is a WIP to make \"git blame\" without any positive rev to\n> start digging from the working tree copy, which is made into a\n> fake commit whose sole parent is the HEAD.\n\nI hate to be a stick in the mud, but including MERGE_HEAD as parents\nof the virtual commit would also be nice.  Then you can get a blame\non conflicted files while in the middle of a merge and are working\non sorting the mess out.  :-)\n\nYea, MERGE_HEAD is really strictly for git-merge and git-commit,\nbut here its got some use too.\n\n-- \nShawn.\n"},{"id":"33052","messageId":"Pine.LNX.4.64.0701300901220.3611@woody.linux-foundation.org","threadId":"6545","inReplyTo":"7vodog3m3f.fsf@assigned-by-dhcp.cox.net","subject":"Re: More precise tag following","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-01-30T17:02:00Z","receivedAt":"2007-01-30T17:02:00Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 30 Jan 2007, Junio C Hamano wrote:\n> \n> This is still very rough; the existing diff frontends are mess\n> and making diff-cache and diff-tree behave more or less\n> interchangeably is quite a pain.\n\nAhh. If it's that painful, then perhaps it's not worth it. It was just a \n\"wouldn't it be nice\" - I don't think anybody will really *depend* on this \nkind of politeness..\n\n\t\tLinus\n"},{"id":"33116","messageId":"87bqkf1tey.fsf@morpheus.local","threadId":"6545","inReplyTo":"Pine.LNX.4.64.0701290759570.3611@woody.linux-foundation.org","subject":"Re: More precise tag following","fromName":"David Kågedal","fromEmail":"davidk@lysator.liu.se","sentAt":"2007-01-31T08:39:49Z","receivedAt":"2007-01-31T08:39:49Z","isPatch":false,"sender":{"key":"davidk@lysator.liu.se","avatar":"https://avatars.githubusercontent.com/u/60530?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n>    Now, in a plain-text pager thign (aka the traditional \"git blame\"), you \n>    don't have much choice. The blame data needs to be there, and you can't \n>    hide it, because if you do, there's no way to get at it. But things are \n>    different with an interactive graphical environment (or a textual one, \n>    for that matter: using some curses interface wouldn't change this \n>    argument).\n>\n>    You _could_ just make the primary thing be the actual file data, and \n>    the blame be \"incidental\". Which it really is.\n\nHere is an emacs implementation of incremental git-blame.  When you\nturn it on while viewing a file, the editor buffer will be updated by\nsetting the background of individual lines to a color that reflects\nwhich commit it comes from.  And when you move around the buffer, a\none-line summary will be shown in the echo area.\n\nTo fix:  The colors are horrible, and work best if you normally\nuse a dark background.  You can see the list of colors near the top if\nyou want to change them.\n\nIf the file is changed from what is in HEAD, things will obviously be\nscrewed up.\n\nUsage instructions:  Open a file and type M-x git-blame-mode\n\n;;; git-blame.el\n\n(defvar git-blame-colors\n  '(\"black\" \"midnight blue\" \"medium blue\" \"steel blue\"\n    \"gray2\" \"gray4\" \"gray6\" \"gray8\" \"gray10\" \"gray12\" \"gray14\"\n    \"gray16\" \"gray18\" \"gray20\" \"gray22\" \"gray24\" \"gray26\" \"gray28\" \"gray30\"\n    \"gray32\" \"gray34\" \"gray36\" \"gray38\" \"gray40\" \"gray42\" \"gray44\" \"gray46\"\n    \"gray48\" \"gray56\" \"gray64\" \"gray72\" \"gray80\" \"gray88\" \"gray96\"))\n(defvar git-blame-ancient-color \"dark green\")\n\n(defvar git-blame-overlays nil)\n(defvar git-blame-cache nil)\n\n(defvar git-blame-mode nil)\n(make-variable-buffer-local 'git-blame-mode)\n(push (list 'git-blame-mode \" blame\") minor-mode-alist)\n\n(defun git-blame-mode (&optional arg)\n  (interactive \"P\")\n  (if arg\n      (setq git-blame-mode (eq arg 1))\n    (setq git-blame-mode (not git-blame-mode)))\n  (make-local-variable 'git-blame-overlays)\n  (make-local-variable 'git-blame-colors)\n  (make-local-variable 'git-blame-cache)\n  (setq git-blame-colors (default-value 'git-blame-colors))\n  (if git-blame-mode\n      (git-blame-run)\n    (git-blame-cleanup)))\n\n(defun git-blame-run ()\n  (interactive)\n  (let* ((display-buf (current-buffer))\n         (blame-buf (get-buffer-create\n                     (concat \" git blame for \" (buffer-name))))\n         (proc (start-process \"git-blame\" blame-buf\n                             \"git\" \"blame\" \"--incremental\"\n                             (file-name-nondirectory buffer-file-name))))\n    (mapcar 'delete-overlay git-blame-overlays)\n    (setq git-blame-overlays nil)\n    (setq git-blame-cache (make-hash-table))\n    (with-current-buffer blame-buf\n      (erase-buffer)\n      (make-local-variable 'git-blame-file)\n      (make-local-variable 'git-blame-current)\n      (setq git-blame-file display-buf)\n      (setq git-blame-current nil))\n    (set-process-filter proc 'git-blame-filter)\n    (set-process-sentinel proc 'git-blame-sentinel)))\n\n(defun git-blame-cleanup ()\n  \"Remove all blame properties\"\n    (mapcar 'delete-overlay git-blame-overlays)\n    (setq git-blame-overlays nil)\n    (let ((modified (buffer-modified-p)))\n      (remove-text-properties (point-min) (point-max) '(point-entered nil))\n      (set-buffer-modified-p modified)))\n    \n\n(defun git-blame-sentinel (proc status)\n  ;;(kill-buffer (process-buffer proc))\n  )\n\n(defvar in-blame-filter nil)\n\n(defun git-blame-filter (proc str)\n  (save-excursion\n    (set-buffer (process-buffer proc))\n    (goto-char (process-mark proc))\n    (insert-before-markers str)\n    (goto-char 0)\n    (unless in-blame-filter\n      (let ((more t)\n            (in-blame-filter t))\n        (while more\n          (setq more (git-blame-parse)))))))\n\n(defun git-blame-parse ()\n  (cond ((looking-at \"\\\\([0-9a-f]\\\\{40\\\\}\\\\) \\\\([0-9]+\\\\) \\\\([0-9]+\\\\) \\\\([0-9]+\\\\)\\n\")\n         (let ((hash (match-string 1))\n               (src-line (string-to-number (match-string 2)))\n               (res-line (string-to-number (match-string 3)))\n               (num-lines (string-to-number (match-string 4))))\n           (setq git-blame-current\n                 (git-blame-new-commit\n                  hash src-line res-line num-lines)))\n         (delete-region (point) (match-end 0))\n         t)\n        ((looking-at \"filename \\\\(.+\\\\)\\n\")\n         (let ((filename (match-string 1)))\n           (git-blame-add-info \"filename\" filename))\n         (delete-region (point) (match-end 0))\n         t)\n        ((looking-at \"\\\\([a-z-]+\\\\) \\\\(.+\\\\)\\n\")\n         (let ((key (match-string 1))\n               (value (match-string 2)))\n           (git-blame-add-info key value))\n         (delete-region (point) (match-end 0))\n         t)\n        (t\n         nil)))\n\n\n(defun git-blame-new-commit (hash src-line res-line num-lines)\n  (save-excursion\n    (set-buffer git-blame-file)\n    (let ((info (gethash hash git-blame-cache)))\n      (when (not info)\n        (let ((color (pop git-blame-colors)))\n          (unless color\n            (setq color git-blame-ancient-color))\n          (setq info (list hash src-line res-line num-lines\n                           (cons 'color color))))\n        (puthash hash info git-blame-cache))\n      (goto-line res-line)\n      (while (> num-lines 0)\n        (if (get-text-property (point) 'git-blame)\n            (forward-line)\n          (let* ((start (point))\n                 (end (progn (forward-line 1) (point)))\n                 (ovl (make-overlay start end)))\n            (push ovl git-blame-overlays)\n            (overlay-put ovl 'git-blame info)\n            (overlay-put ovl 'help-echo hash)\n            (overlay-put ovl 'face (list :background\n                                         (cdr (assq 'color (cddddr info)))))\n            ;;(overlay-put ovl 'point-entered\n            ;;             `(lambda (x y) (git-blame-identify ,hash)))\n            (let ((modified (buffer-modified-p)))\n              (put-text-property (if (= start 1) start (1- start)) (1- end)\n                                 'point-entered\n                                 `(lambda (x y) (git-blame-identify ,hash)))\n              (set-buffer-modified-p modified))))\n        (setq num-lines (1- num-lines))))))\n\n(defun git-blame-add-info (key value)\n  (nconc git-blame-current (list (cons (intern key) value))))\n\n(defun git-blame-current-commit ()\n  (let ((info (get-char-property (point) 'git-blame)))\n    (if info\n        (car info)\n      (error \"No commit info\"))))\n\n(defun git-blame-identify (&optional hash)\n  (interactive)\n  (shell-command\n   (format \"git log -1 --pretty=oneline %s\" (or hash\n                                                (git-blame-current-commit)))))\n\n\n-- \nDavid Kågedal\n"},{"id":"33122","messageId":"87fy9rxxzr.fsf@morpheus.local","threadId":"6545","inReplyTo":"87bqkf1tey.fsf@morpheus.local","subject":"Re: More precise tag following","fromName":"David Kågedal","fromEmail":"davidk@lysator.liu.se","sentAt":"2007-01-31T10:59:52Z","receivedAt":"2007-01-31T10:59:52Z","isPatch":false,"sender":{"key":"davidk@lysator.liu.se","avatar":"https://avatars.githubusercontent.com/u/60530?v=4"},"body":"David Kågedal <davidk@lysator.liu.se> writes:\n\n> Here is an emacs implementation of incremental git-blame.  When you\n> turn it on while viewing a file, the editor buffer will be updated by\n> setting the background of individual lines to a color that reflects\n> which commit it comes from.  And when you move around the buffer, a\n> one-line summary will be shown in the echo area.\n\nI noticed that the output of \"git blame --incremental\" sometimes\nhas a line only containing the word \"boundary\".  This is not described\nin the documentation.  The usage string for \"git blame\" mentions a -b\noption, but that doesn't seem to change the output in this case.\n\nAnyway, the version I posted was buggy.  This one seems to work\nbetter:\n\n;;; git-blame.el\n\n(defvar git-blame-colors\n  '(\"midnight blue\" \"medium blue\" \"steel blue\"\n    \"gray2\" \"gray4\" \"gray6\" \"gray8\" \"gray10\" \"gray12\" \"gray14\"\n    \"gray16\" \"gray18\" \"gray20\" \"gray22\" \"gray24\" \"gray26\" \"gray28\" \"gray30\"\n    \"gray32\" \"gray34\" \"gray36\" \"gray38\" \"gray40\" \"gray42\" \"gray44\" \"gray46\"\n    \"gray48\" \"gray56\" \"gray64\" \"gray72\" \"gray80\" \"gray88\" \"gray96\"))\n(defvar git-blame-ancient-color \"dark green\")\n\n(defvar git-blame-overlays nil)\n(defvar git-blame-cache nil)\n\n(defvar git-blame-mode nil)\n(make-variable-buffer-local 'git-blame-mode)\n(push (list 'git-blame-mode \" blame\") minor-mode-alist)\n\n(defun git-blame-mode (&optional arg)\n  (interactive \"P\")\n  (if arg\n      (setq git-blame-mode (eq arg 1))\n    (setq git-blame-mode (not git-blame-mode)))\n  (make-local-variable 'git-blame-overlays)\n  (make-local-variable 'git-blame-colors)\n  (make-local-variable 'git-blame-cache)\n  (setq git-blame-colors (default-value 'git-blame-colors))\n  (if git-blame-mode\n      (git-blame-run)\n    (git-blame-cleanup)))\n\n(defun git-blame-run ()\n  (interactive)\n  (let* ((display-buf (current-buffer))\n         (blame-buf (get-buffer-create\n                     (concat \" git blame for \" (buffer-name))))\n         (proc (start-process \"git-blame\" blame-buf\n                             \"git\" \"blame\" \"-b\" \"--incremental\"\n                             (file-name-nondirectory buffer-file-name))))\n    (mapcar 'delete-overlay git-blame-overlays)\n    (setq git-blame-overlays nil)\n    (setq git-blame-cache (make-hash-table :test 'equal))\n    (with-current-buffer blame-buf\n      (erase-buffer)\n      (make-local-variable 'git-blame-file)\n      (make-local-variable 'git-blame-current)\n      (setq git-blame-file display-buf)\n      (setq git-blame-current nil))\n    (set-process-filter proc 'git-blame-filter)\n    (set-process-sentinel proc 'git-blame-sentinel)))\n\n(defun git-blame-cleanup ()\n  \"Remove all blame properties\"\n    (mapcar 'delete-overlay git-blame-overlays)\n    (setq git-blame-overlays nil)\n    (let ((modified (buffer-modified-p)))\n      (remove-text-properties (point-min) (point-max) '(point-entered nil))\n      (set-buffer-modified-p modified)))\n    \n\n(defun git-blame-sentinel (proc status)\n  ;;(kill-buffer (process-buffer proc))\n  (message \"git blame finished\"))\n\n(defvar in-blame-filter nil)\n\n(defun git-blame-filter (proc str)\n  (save-excursion\n    (set-buffer (process-buffer proc))\n    (goto-char (process-mark proc))\n    (insert-before-markers str)\n    (goto-char 0)\n    (unless in-blame-filter\n      (let ((more t)\n            (in-blame-filter t))\n        (while more\n          (setq more (git-blame-parse)))))))\n\n(defun git-blame-parse ()\n  (cond ((looking-at \"\\\\([0-9a-f]\\\\{40\\\\}\\\\) \\\\([0-9]+\\\\) \\\\([0-9]+\\\\) \\\\([0-9]+\\\\)\\n\")\n         (let ((hash (match-string 1))\n               (src-line (string-to-number (match-string 2)))\n               (res-line (string-to-number (match-string 3)))\n               (num-lines (string-to-number (match-string 4))))\n           (setq git-blame-current\n                 (git-blame-new-commit\n                  hash src-line res-line num-lines)))\n         (delete-region (point) (match-end 0))\n         t)\n        ((looking-at \"filename \\\\(.+\\\\)\\n\")\n         (let ((filename (match-string 1)))\n           (git-blame-add-info \"filename\" filename))\n         (delete-region (point) (match-end 0))\n         t)\n        ((looking-at \"\\\\([a-z-]+\\\\) \\\\(.+\\\\)\\n\")\n         (let ((key (match-string 1))\n               (value (match-string 2)))\n           (git-blame-add-info key value))\n         (delete-region (point) (match-end 0))\n         t)\n        ((looking-at \"boundary\\n\")\n         (setq git-blame-current nil)\n         (delete-region (point) (match-end 0))\n         t)\n        (t\n         nil)))\n\n\n(defun git-blame-new-commit (hash src-line res-line num-lines)\n  (save-excursion\n    (set-buffer git-blame-file)\n    (let ((info (gethash hash git-blame-cache)))\n      (when (not info)\n        (let ((color (pop git-blame-colors)))\n          (unless color\n            (setq color git-blame-ancient-color))\n          (setq info (list hash src-line res-line num-lines\n                           (cons 'color color))))\n        (puthash hash info git-blame-cache))\n      (goto-line res-line)\n      (while (> num-lines 0)\n        (if (get-text-property (point) 'git-blame)\n            (forward-line)\n          (let* ((start (point))\n                 (end (progn (forward-line 1) (point)))\n                 (ovl (make-overlay start end)))\n            (push ovl git-blame-overlays)\n            (overlay-put ovl 'git-blame info)\n            (overlay-put ovl 'help-echo hash)\n            (overlay-put ovl 'face (list :background\n                                         (cdr (assq 'color (cddddr info)))))\n            ;;(overlay-put ovl 'point-entered\n            ;;             `(lambda (x y) (git-blame-identify ,hash)))\n            (let ((modified (buffer-modified-p)))\n              (put-text-property (if (= start 1) start (1- start)) (1- end)\n                                 'point-entered\n                                 `(lambda (x y) (git-blame-identify ,hash)))\n              (set-buffer-modified-p modified))))\n        (setq num-lines (1- num-lines))))))\n\n(defun git-blame-add-info (key value)\n  (if git-blame-current\n      (nconc git-blame-current (list (cons (intern key) value)))))\n\n(defun git-blame-current-commit ()\n  (let ((info (get-char-property (point) 'git-blame)))\n    (if info\n        (car info)\n      (error \"No commit info\"))))\n\n(defun git-blame-identify (&optional hash)\n  (interactive)\n  (shell-command\n   (format \"git log -1 --pretty=oneline %s\" (or hash\n                                                (git-blame-current-commit)))))\n\n-- \nDavid Kågedal\n"},{"id":"33147","messageId":"m3abzz6upz.fsf@localhost.localdomain","threadId":"6545","inReplyTo":"87bqkf1tey.fsf@morpheus.local","subject":"Re: More precise tag following","fromName":"Peter Eriksen","fromEmail":"s022018@student.dtu.dk","sentAt":"2007-01-31T16:12:40Z","receivedAt":"2007-01-31T16:12:40Z","isPatch":false,"sender":{"key":"s022018@student.dtu.dk","avatar":null},"body":"David Kågedal <davidk@lysator.liu.se> writes:\n\n> Usage instructions:  Open a file and type M-x git-blame-mode\n> \n> ;;; git-blame.el\n\nI saved the elisp code in a file .emacs.d/git-blame.el, and loaded it\nwith M-x load-file.  Then I visited git/cache.h, and typed M-x\ngit-blame-mode, but the background colours did not change.  What did I\nforget to do?\n\nRegards,\n\nPeter\n"},{"id":"33159","messageId":"87y7nj161j.fsf@morpheus.local","threadId":"6545","inReplyTo":"m3abzz6upz.fsf@localhost.localdomain","subject":"Re: More precise tag following","fromName":"David Kågedal","fromEmail":"davidk@lysator.liu.se","sentAt":"2007-01-31T17:04:40Z","receivedAt":"2007-01-31T17:04:40Z","isPatch":false,"sender":{"key":"davidk@lysator.liu.se","avatar":"https://avatars.githubusercontent.com/u/60530?v=4"},"body":"Peter Eriksen <s022018@student.dtu.dk> writes:\n\n> David Kågedal <davidk@lysator.liu.se> writes:\n>\n>> Usage instructions:  Open a file and type M-x git-blame-mode\n>> \n>> ;;; git-blame.el\n>\n> I saved the elisp code in a file .emacs.d/git-blame.el, and loaded it\n> with M-x load-file.  Then I visited git/cache.h, and typed M-x\n> git-blame-mode, but the background colours did not change.  What did I\n> forget to do?\n\nProbably you forgot to use the latest version :-)\n\nSee my mail with the subject line \"git-blame.el\".\n\n-- \nDavid Kågedal\n"},{"id":"33161","messageId":"m364an6rxp.fsf@localhost.localdomain","threadId":"6545","inReplyTo":"87y7nj161j.fsf@morpheus.local","subject":"Re: More precise tag following","fromName":"Peter Eriksen","fromEmail":"s022018@student.dtu.dk","sentAt":"2007-01-31T17:12:50Z","receivedAt":"2007-01-31T17:12:50Z","isPatch":false,"sender":{"key":"s022018@student.dtu.dk","avatar":null},"body":"David Kågedal <davidk@lysator.liu.se> writes:\n\n> Peter Eriksen <s022018@student.dtu.dk> writes:\n> \n> > David Kågedal <davidk@lysator.liu.se> writes:\n> >\n> >> Usage instructions:  Open a file and type M-x git-blame-mode\n> >> \n> >> ;;; git-blame.el\n> >\n> > I saved the elisp code in a file .emacs.d/git-blame.el, and loaded it\n> > with M-x load-file.  Then I visited git/cache.h, and typed M-x\n> > git-blame-mode, but the background colours did not change.  What did I\n> > forget to do?\n> \n> Probably you forgot to use the latest version :-)\n> \n> See my mail with the subject line \"git-blame.el\".\n\nI saw that mail just after I responded.  The newest version does not\nwork either, that is, it does not work in the same way, as the old\nversion.  Closing Emacs I can see, that Emacs did fork of \"git blame\"\nprocesses.  So it is just the colours, I cannot see.\n\nPeter\n"},{"id":"33162","messageId":"epqjv2$19h$1@sea.gmane.org","threadId":"6545","inReplyTo":"m364an6rxp.fsf@localhost.localdomain","subject":"Re: More precise tag following","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-01-31T17:35:47Z","receivedAt":"2007-01-31T17:35:47Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Peter Eriksen wrote:\n> David K?gedal <davidk@lysator.liu.se> writes:\n>> Peter Eriksen <s022018@student.dtu.dk> writes:\n>> \n>>> David K?gedal <davidk@lysator.liu.se> writes:\n>>>\n>>>> Usage instructions:  Open a file and type M-x git-blame-mode\n>>>> \n>>>> ;;; git-blame.el\n>>>\n>>> I saved the elisp code in a file .emacs.d/git-blame.el, and loaded it\n>>> with M-x load-file.  Then I visited git/cache.h, and typed M-x\n>>> git-blame-mode, but the background colours did not change.  What did I\n>>> forget to do?\n\nI always used M-x load-library, not M-x load-file...\n \n>> Probably you forgot to use the latest version :-)\n>> \n>> See my mail with the subject line \"git-blame.el\".\n> \n> I saw that mail just after I responded.  The newest version does not\n> work either, that is, it does not work in the same way, as the old\n> version.  Closing Emacs I can see, that Emacs did fork of \"git blame\"\n> processes.  So it is just the colours, I cannot see.\n\nDo you use new enough version of git, one which has git-blame --incremental?\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"33178","messageId":"87abzyzzdx.fsf@morpheus.local","threadId":"6545","inReplyTo":"m364an6rxp.fsf@localhost.localdomain","subject":"Re: More precise tag following","fromName":"David Kågedal","fromEmail":"davidk@lysator.liu.se","sentAt":"2007-01-31T20:59:06Z","receivedAt":"2007-01-31T20:59:06Z","isPatch":false,"sender":{"key":"davidk@lysator.liu.se","avatar":"https://avatars.githubusercontent.com/u/60530?v=4"},"body":"Peter Eriksen <s022018@student.dtu.dk> writes:\n\n> David Kågedal <davidk@lysator.liu.se> writes:\n>\n>> Peter Eriksen <s022018@student.dtu.dk> writes:\n>> \n>> > David Kågedal <davidk@lysator.liu.se> writes:\n>> >\n>> >> Usage instructions:  Open a file and type M-x git-blame-mode\n>> >> \n>> >> ;;; git-blame.el\n>> >\n>> > I saved the elisp code in a file .emacs.d/git-blame.el, and loaded it\n>> > with M-x load-file.  Then I visited git/cache.h, and typed M-x\n>> > git-blame-mode, but the background colours did not change.  What did I\n>> > forget to do?\n>> \n>> Probably you forgot to use the latest version :-)\n>> \n>> See my mail with the subject line \"git-blame.el\".\n>\n> I saw that mail just after I responded.  The newest version does not\n> work either, that is, it does not work in the same way, as the old\n> version.  Closing Emacs I can see, that Emacs did fork of \"git blame\"\n> processes.  So it is just the colours, I cannot see.\n\nI think it requires GNU Emacs 21.  If you'are using Emacs 20, try\nchanging this:\n\n            (overlay-put ovl 'face (list :background\n                                         (cdr (assq 'color (cddddr info)))))\n\nto\n\n            (overlay-put ovl 'face (cons 'background-color\n                                         (cdr (assq 'color (cddddr info)))))\n\n\n-- \nDavid Kågedal\n"},{"id":"34038","messageId":"20070209074113.GA2344@spearce.org","threadId":"6545","inReplyTo":"20070129192911.GA12903@thunk.org","subject":"Re: More precise tag following","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-02-09T07:41:14Z","receivedAt":"2007-02-09T07:41:14Z","isPatch":false,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Theodore Tso <tytso@mit.edu> wrote:\n> On Mon, Jan 29, 2007 at 08:24:52AM -0800, Linus Torvalds wrote:\n> > Anyway, all of these issues makes me suspect that the proper blame \n> > interface is to basically *hide* the blame almost entirely, in order to \n> > make the important parts much more visible, and in order to encourage \n> > people to start looking for the piece of code that they are actually \n> > interested in.\n> \n> One approach which might work is where you hover your mouse over a\n> line, and it pops up a tiny window with the blame information if the\n> mouse remains stationary for more than a second or two.\n> \n> Another thing which would be really useful is where the lines that\n> have been changed in the last n commits (where n is probably between\n> 3-5) are highlighted using different colors.  That way you can see\n> what was changed recently, which is often what you are most interested\n> in.  (As in, what changed recently that might have caused this file to\n> get all screwed up?)\n\nIn case you are interested, I've tweaked the display of annotation\ndata in git-gui.  The latest version lets you run blame right from\nthe command line:\n\n\tgit-gui blame master revision.c\n\nClicking on a line colors that line and all lines which are blamed\non the same commit as yellow; the commit before it (ancestor)\nis colored blue and the commit after it (descendant) is colored red.\n\nThe bottom part of the window is used to show the commit SHA-1,\nauthor, committer and complete log message.  I have not yet added\nnavigating to the prior commit, or support for the new --contents\nflag.\n\nBecause the data is now in the bottom pane (and not in columns),\nthe display is about 90 characters wide, instead of 180 or whatever\ninsane value it was.\n\n-- \nShawn.\n"}]}