{"thread":{"id":"2187","subject":"git-rev-list: add \"--dense\" flag","startedAt":"2005-10-21T23:40:54Z","lastAt":"2005-10-25T18:52:16Z","messageCount":20,"participants":["Linus Torvalds","Petr Baudis","Junio C Hamano","Jonas Fonseca"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"10445","messageId":"Pine.LNX.4.64.0510211631400.10477@g5.osdl.org","threadId":"2187","inReplyTo":null,"subject":"git-rev-list: add \"--dense\" flag","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-10-21T23:40:54Z","receivedAt":"2005-10-21T23:40:54Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThis is what the recent git-rev-list changes have all been gearing up for.\n\nWhen we use a path filter to git-rev-list, the new \"--dense\" flag asks\ngit-rev-list to compress the history so that it _only_ contains commits\nthat change files in the path filter.  It also rewrites the parent\ninformation so that tools like \"gitk\" will see the result as a dense\nhistory tree.\n\nFor example, on the current kernel archive:\n\n\t[torvalds@g5 linux]$ git-rev-list HEAD | wc -l\n\t9904\n\t[torvalds@g5 linux]$ git-rev-list HEAD -- kernel | wc -l\n\t5442\n\t[torvalds@g5 linux]$ git-rev-list --dense HEAD -- kernel | wc -l\n\t356\n\nwhich shows that while we have almost ten thousand commits, we can prune\ndown the work to slightly more than half by only following the merges\nthat are interesting. But further, we can then compress the history to\njust 356 entries that actually make changes to the kernel subdirectory.\n\nTo see this in action, try something like\n\n\tgitk --dense -- gitk\n\nto see just the history that affects gitk.  Or, to show that true\nparallell development still remains parallell, do\n\n\tgitk --dense -- daemon.c\n\nwhich shows some parallell commits in the current git tree.\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\n---\n\nI'm really happy with how this turned out. It's a bit expensive to run on \nbig archives, but I _really_ think it's quite spectacular. And likely very \nuseful indeed.\n\nFor example, say you love gitk, but only care about networking changes. \nEasy enough, just do\n\n\tgitk --dense -- net/ include/net/\n\nand off you go. It's not free (we do a _lot_ of tree comparisons), but \ndammit, it's fast enough that it's very very useful.  The tree comparisons \nare done very efficiently.\n\nThis is _way_ more powerful than annotate. Interested in just a single \nfile? Just do\n\n\tgitk --dense -- kernel/exit.c\n\nand it will show the 17 or so commits that change kernel/exit.c with the \nright history (it turns out that there is no parallell development at all \nin that file, so in this case it will linearize history entirely).\n\nDamn, I'm good. \n\ndiff --git a/rev-list.c b/rev-list.c\nindex b5dbb9f..5f125fd 100644\n--- a/rev-list.c\n+++ b/rev-list.c\n@@ -11,6 +11,7 @@\n #define INTERESTING\t(1u << 1)\n #define COUNTED\t\t(1u << 2)\n #define SHOWN\t\t(1u << 3)\n+#define TREECHANGE\t(1u << 4)\n \n static const char rev_list_usage[] =\n \t\"git-rev-list [OPTION] commit-id <commit-id>\\n\"\n@@ -27,6 +28,7 @@ static const char rev_list_usage[] =\n \t\t      \"  --merge-order [ --show-breaks ]\\n\"\n \t\t      \"  --topo-order\";\n \n+static int dense = 0;\n static int unpacked = 0;\n static int bisect_list = 0;\n static int tag_objects = 0;\n@@ -79,6 +81,26 @@ static void show_commit(struct commit *c\n \tfflush(stdout);\n }\n \n+static void rewrite_one(struct commit **pp)\n+{\n+\tfor (;;) {\n+\t\tstruct commit *p = *pp;\n+\t\tif (p->object.flags & (TREECHANGE | UNINTERESTING))\n+\t\t\treturn;\n+\t\t/* Only single-parent commits don't have TREECHANGE */\n+\t\t*pp = p->parents->item;\n+\t}\n+}\n+\n+static void rewrite_parents(struct commit *commit)\n+{\n+\tstruct commit_list *parent = commit->parents;\n+\twhile (parent) {\n+\t\trewrite_one(&parent->item);\n+\t\tparent = parent->next;\n+\t}\n+}\n+\n static int filter_commit(struct commit * commit)\n {\n \tif (stop_traversal && (commit->object.flags & BOUNDARY))\n@@ -95,6 +117,11 @@ static int filter_commit(struct commit *\n \t\treturn STOP;\n \tif (no_merges && (commit->parents && commit->parents->next))\n \t\treturn CONTINUE;\n+\tif (paths && dense) {\n+\t\tif (!(commit->object.flags & TREECHANGE))\n+\t\t\treturn CONTINUE;\n+\t\trewrite_parents(commit);\n+\t}\n \treturn DO;\n }\n \n@@ -404,6 +431,14 @@ static struct diff_options diff_opt = {\n \t.change = file_change,\n };\n \n+static int same_tree(struct tree *t1, struct tree *t2)\n+{\n+\tis_different = 0;\n+\tif (diff_tree_sha1(t1->object.sha1, t2->object.sha1, \"\", &diff_opt) < 0)\n+\t\treturn 0;\n+\treturn !is_different;\n+}\n+\n static struct commit *try_to_simplify_merge(struct commit *commit, struct commit_list *parent)\n {\n \tif (!commit->tree)\n@@ -415,11 +450,7 @@ static struct commit *try_to_simplify_me\n \t\tparse_commit(p);\n \t\tif (!p->tree)\n \t\t\tcontinue;\n-\t\tis_different = 0;\n-\t\tif (diff_tree_sha1(commit->tree->object.sha1,\n-\t\t\t\t   p->tree->object.sha1, \"\", &diff_opt) < 0)\n-\t\t\tcontinue;\n-\t\tif (!is_different)\n+\t\tif (same_tree(commit->tree, p->tree))\n \t\t\treturn p;\n \t}\n \treturn NULL;\n@@ -485,6 +516,27 @@ static void add_parents_to_list(struct c\n \t}\n }\n \n+static void compress_list(struct commit_list *list)\n+{\n+\twhile (list) {\n+\t\tstruct commit *commit = list->item;\n+\t\tstruct commit_list *parent = commit->parents;\n+\t\tlist = list->next;\n+\n+\t\t/*\n+\t\t * Exactly one parent? Check if it leaves the tree\n+\t\t * unchanged\n+\t\t */\n+\t\tif (parent && !parent->next) {\n+\t\t\tstruct tree *t1 = commit->tree;\n+\t\t\tstruct tree *t2 = parent->item->tree;\n+\t\t\tif (!t1 || !t2 || same_tree(t1, t2))\n+\t\t\t\tcontinue;\n+\t\t}\n+\t\tcommit->object.flags |= TREECHANGE;\n+\t}\n+}\n+\n static struct commit_list *limit_list(struct commit_list *list)\n {\n \tstruct commit_list *newlist = NULL;\n@@ -514,6 +566,8 @@ static struct commit_list *limit_list(st\n \t}\n \tif (tree_objects)\n \t\tmark_edges_uninteresting(newlist);\n+\tif (paths && dense)\n+\t\tcompress_list(newlist);\n \tif (bisect_list)\n \t\tnewlist = find_bisection(newlist);\n \treturn newlist;\n@@ -700,6 +754,10 @@ int main(int argc, const char **argv)\n \t\t        limited = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(arg, \"--dense\")) {\n+\t\t\tdense = 1;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(arg, \"--\")) {\n \t\t\tpaths = get_pathspec(prefix, argv + i + 1);\n \t\t\tif (paths) {\n"},{"id":"10446","messageId":"Pine.LNX.4.64.0510211643090.10477@g5.osdl.org","threadId":"2187","inReplyTo":"Pine.LNX.4.64.0510211631400.10477@g5.osdl.org","subject":"Re: git-rev-list: add \"--dense\" flag","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-10-21T23:47:12Z","receivedAt":"2005-10-21T23:47:12Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Fri, 21 Oct 2005, Linus Torvalds wrote:\n>\n> parallell\n\nOh, please also fix my spelling. That's one 'l' too many at the end.\n\nDamn, I think I'm a reasonably good speller most of the time, but this \ntime it shows that Swedish is my first language (where it really _is_ \nwritten that way, with four 'l's total).\n\n\t\tLinus\n"},{"id":"10447","messageId":"Pine.LNX.4.64.0510211648340.10477@g5.osdl.org","threadId":"2187","inReplyTo":"Pine.LNX.4.64.0510211631400.10477@g5.osdl.org","subject":"Re: git-rev-list: add \"--dense\" flag","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-10-21T23:55:21Z","receivedAt":"2005-10-21T23:55:21Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Fri, 21 Oct 2005, Linus Torvalds wrote:\n>\n> \t[torvalds@g5 linux]$ git-rev-list --dense HEAD -- kernel | wc -l\n> \t356\n\nBtw, one additional point.\n\nThis took 0.91 seconds to complete on the current kernel history on my \nmachine with a pretty much fully packed archive, and the memory footprint \nwas a total of about 12MB.\n\nAnd it scales pretty well too. On the historical linux archive, which is \nthree years of history, the same thing takes me just over 12 seconds and \n52MB, and that's for the _whole_ history. And it's not just following one \nfile: it's following that subdirectory.\n\nSo it really is pretty damn cool. \n\nOf course, I might have a bug somewhere, but it all _seems_ to work very \nwell indeed.\n\n\t\t\tLinus\n"},{"id":"10449","messageId":"20051022003733.GA8351@pasky.or.cz","threadId":"2187","inReplyTo":"Pine.LNX.4.64.0510211631400.10477@g5.osdl.org","subject":"Re: git-rev-list: add \"--dense\" flag","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2005-10-22T00:37:33Z","receivedAt":"2005-10-22T00:37:33Z","isPatch":false,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Dear diary, on Sat, Oct 22, 2005 at 01:40:54AM CEST, I got a letter\nwhere Linus Torvalds <torvalds@osdl.org> told me that...\n> When we use a path filter to git-rev-list, the new \"--dense\" flag asks\n> git-rev-list to compress the history so that it _only_ contains commits\n> that change files in the path filter.  It also rewrites the parent\n> information so that tools like \"gitk\" will see the result as a dense\n> history tree.\n\nThere is no documentation for the --dense flag and it is even missing\nfrom the usage string.\n\nBut my main concern is - will it be possible to do the rename detection\nhere as well? Using --dense instead of explicit diff-tree calls in\ncg-log would be nice optimization, but I was about to add support for\noptional following of renames for cg-log <filename>. That's really\npretty useful to have, every time I hit the Junio's big scripts rename\nI keep repeating that to myself. ;-) Now when core GIT got the comfort\nof per-file history, I only hope that it will start to annoy you as well.\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\nVI has two modes: the one in which it beeps and the one in which\nit doesn't.\n"},{"id":"10450","messageId":"20051022004743.GN30889@pasky.or.cz","threadId":"2187","inReplyTo":"20051022003733.GA8351@pasky.or.cz","subject":"Handling renames.","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2005-10-22T00:47:43Z","receivedAt":"2005-10-22T00:47:43Z","isPatch":false,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"  (Having Apr 14 flashbacks? Good memory!)\n\nDear diary, on Sat, Oct 22, 2005 at 02:37:33AM CEST, I got a letter\nwhere Petr Baudis <pasky@suse.cz> told me that...\n> But my main concern is - will it be possible to do the rename detection\n> here as well? Using --dense instead of explicit diff-tree calls in\n> cg-log would be nice optimization, but I was about to add support for\n> optional following of renames for cg-log <filename>. That's really\n> pretty useful to have, every time I hit the Junio's big scripts rename\n> I keep repeating that to myself. ;-) Now when core GIT got the comfort\n> of per-file history, I only hope that it will start to annoy you as well.\n\n  After all, this might be as good time to bring this up as any. Heavy\npost-1.0 material follows:\n\n  How to track renames? I believe the situation has changed in the last\nhalf a year.  GIT really is a full-fledged SCM by now (at least its\nmajor part code-wise), and I think it's hopefully becoming obvious that\nwe need to track renames. I actually decided to skip the whole\ndiscussion why so, because we already _do_ concern ourselves with\nrenames - that's what the cool diff -M gadget does. And people start to\nwant to use it for all kind of history-digging stuff (not just for nice\ndiffs between trees).\n\n  So the problem is whether we should make this explicit. diff -M is\nonly a heuristic and it can go wrong, while it was empirically found\nout in other SCMs that people actually don't mind telling their SCM\nabout renames explicitly - no more than telling it about adds and\nremovals explicitly. So the user is willing to tell us what precisely\nhappened and it would be foolish to throw that away and insist on\nguessing.  Besides, guessing (and even doing that everytime we go\nthrough the history) is fundamentally slow, orders of magnitude more\nthan just a tree diff.\n\n\n  If I convince you that it is worth tracking the renames explicitly,\n\"how\" is already a minor question. One idea of mine was to add an \"edge\"\nobject describing the edge between two trees (that's optimized for\nflexible use - some people use GIT for really weird things and perhaps\ndo not use commits at all; edge between two commits would be optimized\nfor flexibility in case we will later think of some other cool stuff to\ntrack at the edge):\n\n\ttrees c53f757133bb84a2d87e901c49207e9b7c48e1a6 6bc7aa4f652d0ef49108d9e30a7ea7fbf8e44639\n\trename git-pull-script\\0git-pull.sh\\0\n\tcopy somefile1\\0somefile2\\0\n\trewrite anotherfile1\\0anotherfile2\\0\n\n  (The \"rewrite\" line might be controversial. And you might want to\nmerge this with the delta objects during packing, or do something\nsimilarly clever.)\n\n  Then you could e.g. pass the edge object ID as the second ID on the\nparent line:\n\n\tparent 99977bd5fdeabbd0608a70e9411c243007ec4ea2 edgeobjectid\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\nVI has two modes: the one in which it beeps and the one in which\nit doesn't.\n"},{"id":"10451","messageId":"Pine.LNX.4.64.0510211814050.10477@g5.osdl.org","threadId":"2187","inReplyTo":"20051022003733.GA8351@pasky.or.cz","subject":"Re: git-rev-list: add \"--dense\" flag","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-10-22T01:26:27Z","receivedAt":"2005-10-22T01:26:27Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 22 Oct 2005, Petr Baudis wrote:\n> \n> There is no documentation for the --dense flag and it is even missing\n> from the usage string.\n\nI'm not much for docs ;)\n\nThe whole path argument also isn't even there (and without paths, --dense \ndoesn't matter)\n\n> But my main concern is - will it be possible to do the rename detection\n> here as well?\n\nYes. Note that git-rev-list doesn't actually _do_ the diff, it only checks \nwhether the tree is changed. You still just get a list of commits out of \nit, and it is up to you to decide what to do with it.\n\nIf you're just going to feed them to diff-tree _anyway_, then you might as \nwell not even do the dense thing, because quite frankly, you're just doing \nextra work.\n\nBut let's say that you want to follow a certain filename, what you can do \nis basically (fake shell syntax with \"goto restart\")\n\n\trev=HEAD\n   restart:\n\n\tgit-rev-list $rev --dense --parents -- \"$filename\" |\n\t\twhile read commit parent1 restofparents\n\t\tdo\n\t\t\tif [ \"$restofparents\" ]; then\n\t\t\t\t.. it's a merge, do whatever it is you do\n\t\t\t\t   with merges ..\n\t\t\telse\n\t\t\t\tshaold=$(git-ls-tree $parent -- \"$filename\")\n\t\t\t\tshanew=$(git-ls-tree $commit -- \"$filename\")\n\n\t\t\t\t# Did it disappear?\n\t\t\t\t# Maybe it got renamed from something\n\t\t\t\tif [ -z \"$shaold\" ]; then\n\t\t\t\t\told=$(git-diff-tree -M -r $commit |\n\t\t\t\t\t\tgrep \" R .* $filename\")\n\t\t\t\t\techo \"rename? $old\"\n\t\t\t\t\tfilename=figure-it-out-from-$old\n\t\t\t\t\trev=$parent\n\t\t\t\t\tgoto restart\n\t\t\t\tfi\n\t\t\t\tgit-diff-tree -p $commit -- \"$filename\"\n\t\t\tfi\n\t\twhile\n\nor something similar. \n\nIn other words: you'll basically have to figure out the renames on your \nown, and follow the renaming, but something like the above should do it.\n\n\t\t\tLinus\n"},{"id":"10452","messageId":"Pine.LNX.4.64.0510211826350.10477@g5.osdl.org","threadId":"2187","inReplyTo":"20051022004743.GN30889@pasky.or.cz","subject":"Re: Handling renames.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-10-22T01:28:54Z","receivedAt":"2005-10-22T01:28:54Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 22 Oct 2005, Petr Baudis wrote:\n> \n>   How to track renames? I believe the situation has changed in the last\n> half a year.\n\nI disagree.\n\nEvery single thing that said that renames were a bad idea to track when \ngit started is still equally true.\n\n>   If I convince you that it is worth tracking the renames explicitly,\n> \"how\" is already a minor question.\n\nNever. I'm 100% convinced that tracking renames is WRONG WRONG WRONG.\n\nYou can follow renames _afterwards_. \n\nGit tracks contents. And I think we've proven that figuring out renames \nafter-the-fact from those contents is not only doable, but very well \nsupported already.\n\nI'm convinced that git handles renames better than any other SCM ever. \nExactly because we figure it out when it matters.\n\n\t\tLinus\n"},{"id":"10453","messageId":"20051022015109.GO30889@pasky.or.cz","threadId":"2187","inReplyTo":"Pine.LNX.4.64.0510211826350.10477@g5.osdl.org","subject":"Re: Handling renames.","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2005-10-22T01:51:09Z","receivedAt":"2005-10-22T01:51:09Z","isPatch":false,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"> Every single thing that said that renames were a bad idea to track\n> when git started is still equally true.\n\nIt'd be good to clarify whether we discuss whether the idea of tracking\nrenames is good or bad, or whether having the user explicitly specify\nrenames is better than figuring that out automagically. Your first\ncomment would indicate the former, but the rest of your reply the\nlatter.\n\n> You can follow renames _afterwards_. \n\nI can - crudely, but what's the point, if the user is dying to give me\nthe information.\n\n> Git tracks contents. And I think we've proven that figuring out renames \n> after-the-fact from those contents is not only doable, but very well \n> supported already.\n\nIt's unreliable and it's slow (well, perhaps I should get some numbers\nto back that out, but given how it is done I take it for granted). Does\nnot sound too \"very well\" to me.\n\n> I'm convinced that git handles renames better than any other SCM ever. \n> Exactly because we figure it out when it matters.\n\nIt matters at least every time you show per-file history and every time\nyou merge cross the rename. I think that can be both pretty common if\nyou ever do the rename. That means you can do an expensive guess\nevery time you hit that, and the guess can get it wrong, in which case\nthere is no way around that and you lose.\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\nVI has two modes: the one in which it beeps and the one in which\nit doesn't.\n"},{"id":"10454","messageId":"7vbr1is3m4.fsf@assigned-by-dhcp.cox.net","threadId":"2187","inReplyTo":"20051022015109.GO30889@pasky.or.cz","subject":"Re: Handling renames.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-10-22T02:10:59Z","receivedAt":"2005-10-22T02:10:59Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Petr Baudis <pasky@suse.cz> writes:\n\n>> I'm convinced that git handles renames better than any other SCM ever. \n>> Exactly because we figure it out when it matters.\n>\n> It matters at least every time you show per-file history and every time\n> you merge cross the rename. I think that can be both pretty common if\n> you ever do the rename. That means you can do an expensive guess\n> every time you hit that, and the guess can get it wrong, in which case\n> there is no way around that and you lose.\n\nI think it is OK for the higher level layer (like your\nsingle-file-history follower) to use what you outlined with\n\"edges\", as either a hint/request from the user and/or a cache\nof what the expensive and unreliable thing figured out.\n\nI however do not necessarily think adding the \"edges\"\ninformation as an optional second item on \"parent \" line in a\ncommit is a good idea.  Rather, treating the set of \"edges\" just\nlike we treat the grafts feel more appropriate to me.  IOW,\ncreate .git/info/edges and keep your edge information there.\n"},{"id":"10455","messageId":"20051022024946.GP30889@pasky.or.cz","threadId":"2187","inReplyTo":"7vbr1is3m4.fsf@assigned-by-dhcp.cox.net","subject":"Re: Handling renames.","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2005-10-22T02:49:46Z","receivedAt":"2005-10-22T02:49:46Z","isPatch":false,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Dear diary, on Sat, Oct 22, 2005 at 04:10:59AM CEST, I got a letter\nwhere Junio C Hamano <junkio@cox.net> told me that...\n> I think it is OK for the higher level layer (like your\n> single-file-history follower) to use what you outlined with\n> \"edges\", as either a hint/request from the user and/or a cache\n> of what the expensive and unreliable thing figured out.\n> \n> I however do not necessarily think adding the \"edges\"\n> information as an optional second item on \"parent \" line in a\n> commit is a good idea.\n\nWell, it's obviously unflying idea when it doesn't become core part of\nGIT. ;-) That won't stop me with Cogito, though...\n\n> Rather, treating the set of \"edges\" just\n> like we treat the grafts feel more appropriate to me.  IOW,\n> create .git/info/edges and keep your edge information there.\n\nThe thing is, you aren't supposed to have a lot of grafts, they are\nexpected to be rare. OTOH, renames aren't really all that rare, and as\nthe history goes, you'll accumulate a lot of them. Besides, it's not\njust a cache - in case the user manually recorded the rename, it's an\nimportant historical information, as important as e.g. the commit\nmessage. It should be also distributed with the rest of the history\ndata, not be a per-repository thing. (Actually, this does not mix with\nthe cache at all - that should be a separate thing, quite possibly in\ninfo/.)\n\n\nBut if GIT won't do this, I will have to do this in some Cogito-specific\nway. My preliminary plan is appending something like this to the commit\nmessage:\n\n\t---\n\t!%$rename foo\\x20bar baz\\nquux\n\n!%$ being some arbitrary magic literal, and filter this out from cg-log\nand such. Also, all Cogito commits adding/removing files but not\nrenaming any would have a '!%$norenames' line in the commit message, so\nthat I know that I don't have to do the expensive heuristics because no\nrename/copy happened.\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\nVI has two modes: the one in which it beeps and the one in which\nit doesn't.\n"},{"id":"10456","messageId":"20051022025638.GQ30889@pasky.or.cz","threadId":"2187","inReplyTo":"Pine.LNX.4.64.0510211814050.10477@g5.osdl.org","subject":"Re: git-rev-list: add \"--dense\" flag","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2005-10-22T02:56:38Z","receivedAt":"2005-10-22T02:56:38Z","isPatch":false,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Dear diary, on Sat, Oct 22, 2005 at 03:26:27AM CEST, I got a letter\nwhere Linus Torvalds <torvalds@osdl.org> told me that...\n> On Sat, 22 Oct 2005, Petr Baudis wrote:\n> > There is no documentation for the --dense flag and it is even missing\n> > from the usage string.\n> \n> I'm not much for docs ;)\n> \n> The whole path argument also isn't even there (and without paths, --dense \n> doesn't matter)\n\nThen it should be added to the docs as well. ;-)\n\nOh well, I guess I can always send a patch when yours will get applied. :-)\n\n> > But my main concern is - will it be possible to do the rename detection\n> > here as well?\n> \n> Yes. Note that git-rev-list doesn't actually _do_ the diff, it only checks \n> whether the tree is changed. You still just get a list of commits out of \n> it, and it is up to you to decide what to do with it.\n\nOk, fair enough.\n\n> If you're just going to feed them to diff-tree _anyway_, then you might as \n> well not even do the dense thing, because quite frankly, you're just doing \n> extra work.\n\nfork() is awfully expensive for me. fork()ing something (supposedly)\ntrivial (it was stat) slowed my (non-trivial) loop about 4 times. So I\nthink it will still pay off hugely if I will be able do the diff-tree\nonly on the interesting commits.\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\nVI has two modes: the one in which it beeps and the one in which\nit doesn't.\n"},{"id":"10457","messageId":"Pine.LNX.4.64.0510212022100.10477@g5.osdl.org","threadId":"2187","inReplyTo":"20051022015109.GO30889@pasky.or.cz","subject":"Re: Handling renames.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-10-22T03:23:36Z","receivedAt":"2005-10-22T03:23:36Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 22 Oct 2005, Petr Baudis wrote:\n> \n> > You can follow renames _afterwards_. \n> \n> I can - crudely, but what's the point, if the user is dying to give me\n> the information.\n\nNo the user is NOT.\n\nThe fact is, users have not a frigging clue when a rename happens. \n\nI told you before, I'll tell you again: if you depend on users telling you \nabout renames, you'll get it wrong. You'll get it wrong quite often, in \nfact.\n\nThis is not something I'm going to discuss again. Go back to all the same \narguments from 6 months ago. I was right then, I'm right now.\n\n\t\tLinus\n"},{"id":"10458","messageId":"Pine.LNX.4.64.0510212025260.10477@g5.osdl.org","threadId":"2187","inReplyTo":"20051022025638.GQ30889@pasky.or.cz","subject":"Re: git-rev-list: add \"--dense\" flag","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-10-22T03:30:38Z","receivedAt":"2005-10-22T03:30:38Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 22 Oct 2005, Petr Baudis wrote:\n> \n> > If you're just going to feed them to diff-tree _anyway_, then you might as \n> > well not even do the dense thing, because quite frankly, you're just doing \n> > extra work.\n> \n> fork() is awfully expensive for me. fork()ing something (supposedly)\n> trivial (it was stat) slowed my (non-trivial) loop about 4 times.\n\nNot fork. More like\n\n\tgit-rev-list .. | git-diff-tree --stdin\n\nIf you do it that way (which is quite common - git-whatchanged, git-log \netc), there's no reason to ask for a dense output, because git-diff-tree \ncan handle the non-dense one as fast as git-rev-list can.\n\nBut the --dense option is wonderful for \"gitk\", and for \"slow stuff\". \nWhere \"slow\" can indeed be a fork, or just an interpreter loop like a \nshell/perl script, which would fork/exec git-diff-tree for each commit.\n\n>From a commit-list standpoint\n\n\tgit-rev-list --dense ... -- paths\n\nis the same as\n\n\tgit-rev-list -- paths | git-diff-tree -s -r --stdin -- paths\n\nbut it's somewhat more efficient, and more importantly, it rewrites the \nparents. So quite often, you'd want to use \"--dense\" together with \n\"--parent\" so that you get the \"rewritten\" history. That's how come gitk \ndidn't need any changes, and it \"just worked\". It's also how my example \nscript did the rename detection without having to traverse unnecessary \ncommits.\n\n\t\tLinus\n"},{"id":"10459","messageId":"7voe5iqk82.fsf@assigned-by-dhcp.cox.net","threadId":"2187","inReplyTo":"Pine.LNX.4.64.0510211814050.10477@g5.osdl.org","subject":"Re: git-rev-list: add \"--dense\" flag","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-10-22T03:55:09Z","receivedAt":"2005-10-22T03:55:09Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> Yes. Note that git-rev-list doesn't actually _do_ the diff, it only checks \n> whether the tree is changed. You still just get a list of commits out of \n> it, and it is up to you to decide what to do with it.\n>\n> If you're just going to feed them to diff-tree _anyway_, then you might as \n> well not even do the dense thing, because quite frankly, you're just doing \n> extra work.\n>\n> But let's say that you want to follow a certain filename, what you can do \n> is basically (fake shell syntax with \"goto restart\")\n>...\n> or something similar. \n\nIsn't that only true because you are not doing more than \"have\nthese paths change\" in the new rev-list that already has part of\ndiff-tree?\n\nIf rev-list can optionally be told to detect renames internally\n(it has necessary bits after all), it could adjust the set of\npaths to follow when it sees something got renamed, either by\nreplacing the original path given from the command line with its\nprevious name, or adding its previous name to the set of path\nlimitters (to cover the copy case as well).\n"},{"id":"10466","messageId":"Pine.LNX.4.64.0510221251330.10477@g5.osdl.org","threadId":"2187","inReplyTo":"7voe5iqk82.fsf@assigned-by-dhcp.cox.net","subject":"Re: git-rev-list: add \"--dense\" flag","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-10-22T20:37:42Z","receivedAt":"2005-10-22T20:37:42Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Fri, 21 Oct 2005, Junio C Hamano wrote:\n> \n> If rev-list can optionally be told to detect renames internally\n> (it has necessary bits after all), it could adjust the set of\n> paths to follow when it sees something got renamed, either by\n> replacing the original path given from the command line with its\n> previous name, or adding its previous name to the set of path\n> limitters (to cover the copy case as well).\n\nThe problem with renames isn't the straight-line case. \n\nThe problem with renames is the merge case. And quite frankly, I don't \nknow how to handle that sanely.\n\nIf everything was straight-line (CVS), renames would be trivial. But \ngit-rev-list very fundamentally works on something that isn't. So let's \nlook at the _fundamental_ problem:\n\n - git-rev-list traverses one commit at a time, and it doesn't even _know_ \n   whether it has seem all the parents of that commit during the first \n   phase.\n\n   The first phase is the \"limit_list()\" thing, which is when we decide \n   which commits are reachable, uninteresting, and which merges to follow.\n\n   Now, think about this: since we don't even know that we've seen all \n   parents, that pretty much by definition means that we can't know what \n   has been renamed if we were to track it.\n\n - in the second phase (which is where I do the densification), we do \n   actually have the full tree and that makes a lot of things a lot easier \n   to do. When it comes to dense, for example, it means that I know all \n   the UNINTERESTING logic has already been done, and that all merges have \n   been simplified.\n\n   But in the second phase, we couldn't do rename detection either, since \n   by then, we've already fixed the list of names as far as merges are \n   concerned.\n\nAnd note that this fundamental issue is true _whether_ we have some \nexplicit rename information in a commit or not. \n\nGit-rev-list has a few additional issues that make it even more \ninteresting:\n\n - git-rev-list fundamentally is designed for multiple heads. There is no \n   one special \"origin\" head. We might not have _one_ end-point, we might \n   have twenty-five different heads we're looking at at the same time. Try\n\n\tgitk --all --dense -d -- git-fetch.c\n\n   on the current git archive, and be impressed by how well it works (and \n   yes, you'll see a funny artifact: \"--dense\" never removes a line of \n   development entirely, so you'll see a single dot for the two \n   \"unrelated\" lines: \"gitk\" and \"to-do\" do not share a root with the rest \n   of them. You'll get a single dot for that root, even if it \n   obviously doesn't actually change \"git-fetch.c\").\n\n   You'll also very clearly see the \"Big tool rename\" which created that \n   name: it will be the first commit after the root that is visible that \n   way.\n\n - path limiters are fundamentally designed to take a list of paths and \n   directories. Personally, I find that to be a lot more important than a \n   single file. That's very efficient the way we do it, but if you were to \n   _change_ the list of paths, you'd basically have to track those changes \n   over history.\n\n   (This actually happens even with a single path - imagine a merge, and a \n   rename down one line of the merge. You'll have to keep track of \n   different files for the different lines of development, and then when \n   they join together, and the rename only happened in one of them, you'll \n   have to make an executive decision, or just continue to track _both_)\n\nSo what do I propose? I propose that you realize that \"git-rev-list\" is \njust the _building_ block. It does one thing, and one thing only: it \ncreates revision list.\n\nA user basically _never_ uses git-rev-list directly: it just doesn't make \nsense. It's not what git-rev-list is there for. git-rev-list is meant to \nbe used as a base for doing the real work. And that's how you can use it \nfor renaming too.\n\nIf you think of \"git-rev-list --dense -- name\" as a fast way to get a set \nof commits that affect \"name\", suddenly it all makes sense. You suddenly \nrealize that that's a nice building block for figuring out renames. It's \nnot _all_ of it, but it's a big portion.\n\nTo go back to \"gitk\", let's see what the path limitation shows us. Right \nnow, doing a\n\n\tgitk --all --dense -d -- git-fetch.sh\n\nonly shows that particular name, and that's by design. Maybe that's what \nthe user wants? You have to realize that especially if you remember an \n_old_ name that may not even exist any more, that's REALLY what you want. \nSomething that works more like \"annotate\" is useless, because something \nthat works like annotate would just say \"I don't have that file, I can't \nfollow renames\" and exit.\n\nSo the first lesson to learn is that following just pure path-names is \nactually meaningful ON ITS OWN! Sometimes you do NOT want to follow \nrenames.\n\nFor example, let's say that you used to work on git 4 months ago, but gave \nup, since it was too rough for you. But you played around with it, and now \nyou're back, and you have an old patch that you want to re-apply, but it \ndoesn't talk about \"git-fetch.sh\", it talks about \"git-fetch-script\". So \nyou do\n\n\tgitk --dense -- git-fetch-script\n\nand voila, it does the right thing, and top of the thing is \"Big tool \nrename\", which tells you _exactly_ what happened to that PATHNAME. \n\nSee? Static pathnames are important! \n\nNow, this does show that when you _do_ care about renames, \"gitk\" right \nnow doesn't help you very much (no offense to gitk - how could it know \nthat git-rev-list would give it pathname detection one day?). Let's \ngo back to the original example, and see what we could do to make gitk \nmore useful..\n\n\tgitk --all --dense -d -- git-fetch.sh\n\nGo and select that \"Big tool rename\" thing, and start looking for the \nrename..\n\nYou won't find it. Why? You'll see all-new files, no renames. It turns out \nthat \"gitk\" follows the \"parent\" thing a bit _too_ slavishly, which is \ncorrect on one level, but in this case with \"--dense\" it turns out that \nwhat you want to do is see only what _that_ commit does, not what that \ncommit did relative to its parent (which was the initial revision).\n\nSo while \"--dense\" made gitk work even without any changes, it's clear \nthat the new capability means that gitk might want to have a new button: \n\"show diff against 'fake parent'\" and \"show diff against 'real parent'\".\n\nIf you want the global view, the default gitk behaviour is correct (it \nwill show a \"valid\" diff - you'll see everything that changed between the \npoints it shows). But in a rename-centric world, you want the _local_ \nchange to that commit, and right now gitk can't show you that.\n\nSo for trackign renames, we probably want that as a helper to gitk.\n\nAlso, gitk has a \"re-read references\" button, but if you track renames, \nyou probably want to do more than re-read them: you want to re-DO them \nwith the \"Big rename\" as the new head (forget the old references \nentirely), and with the name list changed. New functionality (possibly \nyou'd like to havea \"New wiew\" button, which actually starts a new gitk \nso that you can see both of them). Right now you'd have to do it by hand:\n\n\tgitk --dense 215a7ad1ef790467a4cd3f0dcffbd6e5f04c38f7 -- git-fetch-script \n\n(where 215a.. is the thing you get when you select the \"Big tool rename\")\n\nYou'd also probably like to have some way to limit the names shown for the \ngit-diff-tree in gitk. \n\nIn short, the new \"gitk --dense -- filename\" doesn't help you nearly as \nmuch as it _could_. But when you squint a bit, I think you'll see how it's \nquite possible to do...\n\n\t\tLinus\n"},{"id":"10597","messageId":"20051025180707.GA7463@diku.dk","threadId":"2187","inReplyTo":"Pine.LNX.4.64.0510211631400.10477@g5.osdl.org","subject":"Re: git-rev-list: add \"--dense\" flag","fromName":"Jonas Fonseca","fromEmail":"fonseca@diku.dk","sentAt":"2005-10-25T18:07:07Z","receivedAt":"2005-10-25T18:07:07Z","isPatch":false,"sender":{"key":"fonseca@diku.dk","avatar":"https://gravatar.com/avatar/f82f3ad698717c51873b020c750a92438c820a24056dc39fe4d07baa10a92264?d=mp&s=160"},"body":"Linus Torvalds <torvalds@osdl.org> wrote Fri, Oct 21, 2005:\n> To see this in action, try something like\n> \n> \tgitk --dense -- gitk\n> \n> to see just the history that affects gitk.\n\nIs the initial commit supposed to be listed when the file has been added\nlater? I would expect it to only list until (and including) the commit\nwhere the file was introduced.\n\n\tprompt > git-rev-list --dense HEAD -- compat/mmap.c\n\tf48000fcbe1009c18f1cc46e56cde2cb632071fa\n\t730d48a2ef88a7fb7aa4409d40b1e6964a93267f\n\te83c5163316f89bfbde7d9ab23ca2e25604af290\n\tprompt > git-cat-file commit e83c\n\ttree 2b5bfdf7798569e0b59b16eb9602d5fa572d6038\n\tauthor Linus Torvalds <torvalds@ppc970.osdl.org> 1112911993 -0700\n\tcommitter Linus Torvalds <torvalds@ppc970.osdl.org> 1112911993 -0700\n\n\tInitial revision of \"git\", the information manager from hell\n\nInterestingly, for the special gitk, the correct commit is the last rev\nlisted.\n\n-- \nJonas Fonseca\n"},{"id":"10598","messageId":"Pine.LNX.4.64.0510251110050.10477@g5.osdl.org","threadId":"2187","inReplyTo":"20051025180707.GA7463@diku.dk","subject":"Re: git-rev-list: add \"--dense\" flag","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-10-25T18:23:49Z","receivedAt":"2005-10-25T18:23:49Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 25 Oct 2005, Jonas Fonseca wrote:\n> \n> Is the initial commit supposed to be listed when the file has been added\n> later?\n\nRight now --dense will _always_ show the root commit. I didn't do the \nlogic that does the diff against an empty tree. I was lazy.\n\nThis patch does that, and may or may not work.\n\nDoes this match what you expected?\n\n\t\tLinus\n\n----\ndiff --git a/rev-list.c b/rev-list.c\nindex 5f125fd..038dae2 100644\n--- a/rev-list.c\n+++ b/rev-list.c\n@@ -439,6 +439,30 @@ static int same_tree(struct tree *t1, st\n \treturn !is_different;\n }\n \n+static int same_tree_as_empty(struct tree *t1)\n+{\n+\tint retval;\n+\tvoid *tree;\n+\tstruct tree_desc empty, real;\n+\n+\tif (!t1)\n+\t\treturn 0;\n+\n+\ttree = read_object_with_reference(t1->object.sha1, \"tree\", &real.size, NULL);\n+\tif (!tree)\n+\t\treturn 0;\n+\treal.buf = tree;\n+\n+\tempty.buf = \"\";\n+\tempty.size = 0;\n+\n+\tis_different = 0;\n+\tretval = diff_tree(&empty, &real, \"\", &diff_opt);\n+\tfree(tree);\n+\n+\treturn !retval && !is_different;\n+}\n+\n static struct commit *try_to_simplify_merge(struct commit *commit, struct commit_list *parent)\n {\n \tif (!commit->tree)\n@@ -523,11 +547,17 @@ static void compress_list(struct commit_\n \t\tstruct commit_list *parent = commit->parents;\n \t\tlist = list->next;\n \n+\t\tif (!parent) {\n+\t\t\tif (!same_tree_as_empty(commit->tree))\n+\t\t\t\tcommit->object.flags |= TREECHANGE;\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\t/*\n \t\t * Exactly one parent? Check if it leaves the tree\n \t\t * unchanged\n \t\t */\n-\t\tif (parent && !parent->next) {\n+\t\tif (!parent->next) {\n \t\t\tstruct tree *t1 = commit->tree;\n \t\t\tstruct tree *t2 = parent->item->tree;\n \t\t\tif (!t1 || !t2 || same_tree(t1, t2))\n"},{"id":"10599","messageId":"20051025184030.GB7463@diku.dk","threadId":"2187","inReplyTo":"Pine.LNX.4.64.0510251110050.10477@g5.osdl.org","subject":"Re: git-rev-list: add \"--dense\" flag","fromName":"Jonas Fonseca","fromEmail":"fonseca@diku.dk","sentAt":"2005-10-25T18:40:30Z","receivedAt":"2005-10-25T18:40:30Z","isPatch":false,"sender":{"key":"fonseca@diku.dk","avatar":"https://gravatar.com/avatar/f82f3ad698717c51873b020c750a92438c820a24056dc39fe4d07baa10a92264?d=mp&s=160"},"body":"Linus Torvalds <torvalds@osdl.org> wrote Tue, Oct 25, 2005:\n> On Tue, 25 Oct 2005, Jonas Fonseca wrote:\n> > \n> > Is the initial commit supposed to be listed when the file has been added\n> > later?\n> \n> Right now --dense will _always_ show the root commit. I didn't do the \n> logic that does the diff against an empty tree. I was lazy.\n> \n> This patch does that, and may or may not work.\n\nWithout the workaround below it segfaults.\n\n> Does this match what you expected?\n\nYes, thanks.\n\ndiff --git a/rev-list.c b/rev-list.c\nindex 5f125fd..82ec656 100644\n--- a/rev-list.c\n+++ b/rev-list.c\n@@ -87,6 +87,7 @@ static void rewrite_one(struct commit **\n \t\tstruct commit *p = *pp;\n \t\tif (p->object.flags & (TREECHANGE | UNINTERESTING))\n \t\t\treturn;\n+\t\tif (!p->parents) return;\n \t\t/* Only single-parent commits don't have TREECHANGE */\n \t\t*pp = p->parents->item;\n \t}\n\n\n-- \nJonas Fonseca\n"},{"id":"10600","messageId":"Pine.LNX.4.64.0510251137110.10477@g5.osdl.org","threadId":"2187","inReplyTo":"Pine.LNX.4.64.0510251110050.10477@g5.osdl.org","subject":"Re: git-rev-list: add \"--dense\" flag","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-10-25T18:50:46Z","receivedAt":"2005-10-25T18:50:46Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 25 Oct 2005, Linus Torvalds wrote:\n> \n> Right now --dense will _always_ show the root commit. I didn't do the \n> logic that does the diff against an empty tree. I was lazy.\n> \n> This patch does that, and may or may not work.\n\nNever mind. It's incorrect. \n\nIt's -close- to being correct, but it will SIGSEGV because now the root \nwon't necessarily have the TREECHANGED flag, so now it can follow the root \ndown to its parents (which is a NULL pointer - that's the definition of a \nroot commit, of course).\n\nIt needs to remove the parent pointers that become NULL.\n\nThis patch is even slightly tested, and might do a better job.\n\n[ Sorry for sending untested crap out, but I have a fairly good track \n  record in general. Testing is for wimps ]\n\n\t\tLinus\n\n---\ndiff --git a/rev-list.c b/rev-list.c\nindex 5f125fd..edf3b37 100644\n--- a/rev-list.c\n+++ b/rev-list.c\n@@ -81,23 +81,28 @@ static void show_commit(struct commit *c\n \tfflush(stdout);\n }\n \n-static void rewrite_one(struct commit **pp)\n+static int rewrite_one(struct commit **pp)\n {\n \tfor (;;) {\n \t\tstruct commit *p = *pp;\n \t\tif (p->object.flags & (TREECHANGE | UNINTERESTING))\n-\t\t\treturn;\n-\t\t/* Only single-parent commits don't have TREECHANGE */\n+\t\t\treturn 0;\n+\t\tif (!p->parents)\n+\t\t\treturn -1;\n \t\t*pp = p->parents->item;\n \t}\n }\n \n static void rewrite_parents(struct commit *commit)\n {\n-\tstruct commit_list *parent = commit->parents;\n-\twhile (parent) {\n-\t\trewrite_one(&parent->item);\n-\t\tparent = parent->next;\n+\tstruct commit_list **pp = &commit->parents;\n+\twhile (*pp) {\n+\t\tstruct commit_list *parent = *pp;\n+\t\tif (rewrite_one(&parent->item) < 0) {\n+\t\t\t*pp = parent->next;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tpp = &parent->next;\n \t}\n }\n \n@@ -439,6 +444,30 @@ static int same_tree(struct tree *t1, st\n \treturn !is_different;\n }\n \n+static int same_tree_as_empty(struct tree *t1)\n+{\n+\tint retval;\n+\tvoid *tree;\n+\tstruct tree_desc empty, real;\n+\n+\tif (!t1)\n+\t\treturn 0;\n+\n+\ttree = read_object_with_reference(t1->object.sha1, \"tree\", &real.size, NULL);\n+\tif (!tree)\n+\t\treturn 0;\n+\treal.buf = tree;\n+\n+\tempty.buf = \"\";\n+\tempty.size = 0;\n+\n+\tis_different = 0;\n+\tretval = diff_tree(&empty, &real, \"\", &diff_opt);\n+\tfree(tree);\n+\n+\treturn retval >= 0 && !is_different;\n+}\n+\n static struct commit *try_to_simplify_merge(struct commit *commit, struct commit_list *parent)\n {\n \tif (!commit->tree)\n@@ -523,11 +552,17 @@ static void compress_list(struct commit_\n \t\tstruct commit_list *parent = commit->parents;\n \t\tlist = list->next;\n \n+\t\tif (!parent) {\n+\t\t\tif (!same_tree_as_empty(commit->tree))\n+\t\t\t\tcommit->object.flags |= TREECHANGE;\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\t/*\n \t\t * Exactly one parent? Check if it leaves the tree\n \t\t * unchanged\n \t\t */\n-\t\tif (parent && !parent->next) {\n+\t\tif (!parent->next) {\n \t\t\tstruct tree *t1 = commit->tree;\n \t\t\tstruct tree *t2 = parent->item->tree;\n \t\t\tif (!t1 || !t2 || same_tree(t1, t2))\n"},{"id":"10601","messageId":"Pine.LNX.4.64.0510251150580.10477@g5.osdl.org","threadId":"2187","inReplyTo":"20051025184030.GB7463@diku.dk","subject":"Re: git-rev-list: add \"--dense\" flag","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-10-25T18:52:16Z","receivedAt":"2005-10-25T18:52:16Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 25 Oct 2005, Jonas Fonseca wrote:\n> \n> Without the workaround below it segfaults.\n\nYes, but your patch only partly helps. It fixes the SIGSEGV, but it still \nneeds to remove the \"parents\" pointer when the parent ends up NULL.\n\nThe patch I just sent out should be better. I think.\n\n\t\t\tLinus\n"}]}