{"thread":{"id":"21799","subject":"[PATCH/RFC] Add a --bouquet option to git rev-list","startedAt":"2009-11-30T20:55:14Z","lastAt":"2009-12-01T18:21:32Z","messageCount":4,"participants":["Nathan W. Panike","Michael J Gruber","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"128811","messageId":"4b143a9c.c401be0a.364f.ffffba5b@mx.google.com","threadId":"21799","inReplyTo":null,"subject":"[PATCH/RFC] Add a --bouquet option to git rev-list","fromName":"Nathan W. Panike","fromEmail":"nathan.panike@gmail.com","sentAt":"2009-11-30T20:55:14Z","receivedAt":"2009-11-30T20:55:14Z","isPatch":true,"sender":{"key":"nathan.panike@gmail.com","avatar":"https://avatars.githubusercontent.com/u/389447?v=4"},"body":"Add a command line option to rev-list so the command 'git rev-list --bouquet'\nshows all revisions that are ancestors of refs which share history with HEAD.\n\nSigned-off-by: Nathan W. Panike <nathan.panike@gmail.com>\n---\nI have a repository with the following structure:\n\n      B\n     /\nA'--A--C\n     \\\n      D\n\nE'--E\n\nThus the command 'git merge base E A' returns nothing, as there is no common\nhistory.  The E history contains stuff that is derived from the other history\n(A, B, C, or D).  Often I find myself doing the following:\n\ngit checkout C\ngitk $(include_forks) &\n<View history, make changes, merges, et cetera>\ngit checkout E\n<go back to gitk, only see history for B, C, etc>\n\nNow the 'include_forks' command is a bash function in my .bashrc:\n\ninclude_forks () \n{ \n    local head=\"$(git show -s --pretty=format:'%H' HEAD)\";\n    echo \"HEAD $(git for-each-ref --format='%(refname)' \\\n\trefs/heads refs/remotes | while read ref; do \\\n\tif test \"$(git merge-base HEAD ${ref}^{commit})\" != \"\"; \\\n\t\tthen echo ${ref}; fi; done)\"\n}\n\nThe shell thus intercepts my command and I must restart gitk to see the history\nof E. \n\nWith this patch, I can issue the command 'gitk --bouquet' and when I checkout\nE, I can 'reload' in gitk and see the history of E automatically.\n\nIf there is an easier way to do this in git, please let me know.  Otherwise,\nplease let me know how to improve this patch.\n\n revision.c |   38 ++++++++++++++++++++++++++++++++++++++\n 1 files changed, 38 insertions(+), 0 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex a8a3c3a..ba367cc 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -699,6 +699,31 @@ static int handle_one_ref(const char *path, const unsigned char *sha1, int flag,\n \treturn 0;\n }\n \n+static int handle_one_connected_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)\n+{\n+\tstruct all_refs_cb *cb = cb_data;\n+\tstruct object *object = get_reference(cb->all_revs, path, sha1,\n+\t\t\t\t\t      cb->all_flags);\n+\tstruct commit *r;\n+\tstatic int got_head = 1;\n+\tstatic struct commit *head_commit;\n+\tstatic int head_nr = 0;\n+\tif(got_head) {\n+\t\tgot_head=head_ref(handle_one_ref,cb);\n+\t\tif(got_head)\n+\t\t\treturn 1;\n+\t\tif(cb && cb->all_revs && cb->all_revs->pending.nr > 0) {\n+\t\t\thead_nr = cb->all_revs->pending.nr - 1;\n+\t\t\thead_commit = (struct commit*)&cb->all_revs->pending.objects->item[head_nr];\n+\t\t}\n+\t}\n+\tr = lookup_commit_reference_gently(sha1,1);\n+\tif(r != NULL && head_commit)\n+\t\tif(get_merge_bases_many(head_commit,1,&r,1)) \n+\t\t\tadd_pending_object(cb->all_revs, object, path);\n+\treturn 0;\n+}\n+\n static void handle_refs(struct rev_info *revs, unsigned flags,\n \t\tint (*for_each)(each_ref_fn, void *))\n {\n@@ -708,6 +733,15 @@ static void handle_refs(struct rev_info *revs, unsigned flags,\n \tfor_each(handle_one_ref, &cb);\n }\n \n+static void handle_connected_refs(struct rev_info *revs, unsigned flags,\n+\t\tint (*for_each)(each_ref_fn, void *))\n+{\n+\tstruct all_refs_cb cb;\n+\tcb.all_revs = revs;\n+\tcb.all_flags = flags;\n+\tfor_each(handle_one_connected_ref, &cb);\n+}\n+\n static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)\n {\n \tstruct all_refs_cb *cb = cb_data;\n@@ -1352,6 +1386,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch\n \t\t\t\thandle_refs(revs, flags, for_each_remote_ref);\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (!strcmp(arg, \"--bouquet\")) {\n+\t\t\t\thandle_connected_refs(revs, flags, for_each_ref);\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (!strcmp(arg, \"--reflog\")) {\n \t\t\t\thandle_reflog(revs, flags);\n \t\t\t\tcontinue;\n-- \n1.6.5.3\n"},{"id":"128844","messageId":"4B14CF47.5020808@drmicha.warpmail.net","threadId":"21799","inReplyTo":"4b143a9c.c401be0a.364f.ffffba5b@mx.google.com","subject":"Re: [PATCH/RFC] Add a --bouquet option to git rev-list","fromName":"Michael J Gruber","fromEmail":"git@drmicha.warpmail.net","sentAt":"2009-12-01T08:09:43Z","receivedAt":"2009-12-01T08:09:43Z","isPatch":true,"sender":{"key":"git@grubix.eu","avatar":"https://avatars.githubusercontent.com/u/233215?v=4"},"body":"Nathan W. Panike venit, vidit, dixit 30.11.2009 21:55:\n> Add a command line option to rev-list so the command 'git rev-list --bouquet'\n> shows all revisions that are ancestors of refs which share history with HEAD.\n> \n> Signed-off-by: Nathan W. Panike <nathan.panike@gmail.com>\n> ---\n> I have a repository with the following structure:\n> \n>       B\n>      /\n> A'--A--C\n>      \\\n>       D\n> \n> E'--E\n> \n> Thus the command 'git merge base E A' returns nothing, as there is no common\n> history.  The E history contains stuff that is derived from the other history\n> (A, B, C, or D).  Often I find myself doing the following:\n\nEither I don't understand the diagram or your term \"derived\". If\n\"derived\" means \"on some branch of a merge\" and E is derived from A, B,\nC, or D, then (since B, C, D is derived from A, and from A') E is\nderived from A', and they will have a merge base.\n\nAre these diagrams really disconnected from each other?\n\n> git checkout C\n> gitk $(include_forks) &\n> <View history, make changes, merges, et cetera>\n> git checkout E\n> <go back to gitk, only see history for B, C, etc>\n> \n> Now the 'include_forks' command is a bash function in my .bashrc:\n> \n> include_forks () \n> { \n>     local head=\"$(git show -s --pretty=format:'%H' HEAD)\";\n>     echo \"HEAD $(git for-each-ref --format='%(refname)' \\\n> \trefs/heads refs/remotes | while read ref; do \\\n> \tif test \"$(git merge-base HEAD ${ref}^{commit})\" != \"\"; \\\n> \t\tthen echo ${ref}; fi; done)\"\n> }\n> \n> The shell thus intercepts my command and I must restart gitk to see the history\n> of E. \n> \n> With this patch, I can issue the command 'gitk --bouquet' and when I checkout\n> E, I can 'reload' in gitk and see the history of E automatically.\n\nWhat would your patch do in the example you gave above? Which refs would\nit cause gitk (rev-list) to show?\n\nMichael\n"},{"id":"128904","messageId":"d77df1110912010931l40472723v80ad675a92d23fa3@mail.gmail.com","threadId":"21799","inReplyTo":"4B14CF47.5020808@drmicha.warpmail.net","subject":"Re: [PATCH/RFC] Add a --bouquet option to git rev-list","fromName":"Nathan W. Panike","fromEmail":"nathan.panike@gmail.com","sentAt":"2009-12-01T17:31:56Z","receivedAt":"2009-12-01T17:31:56Z","isPatch":true,"sender":{"key":"nathan.panike@gmail.com","avatar":"https://avatars.githubusercontent.com/u/389447?v=4"},"body":"Hello,\n\nOn Tue, Dec 1, 2009 at 2:09 AM, Michael J Gruber\n<git@drmicha.warpmail.net> wrote:\n> Nathan W. Panike venit, vidit, dixit 30.11.2009 21:55:\n>> Add a command line option to rev-list so the command 'git rev-list --bouquet'\n>> shows all revisions that are ancestors of refs which share history with HEAD.\n>>\n>> Signed-off-by: Nathan W. Panike <nathan.panike@gmail.com>\n>> ---\n>> I have a repository with the following structure:\n>>\n>>       B\n>>      /\n>> A'--A--C\n>>      \\\n>>       D\n>>\n>> E'--E\n>>\n>> Thus the command 'git merge base E A' returns nothing, as there is no common\n>> history.  The E history contains stuff that is derived from the other history\n>> (A, B, C, or D).  Often I find myself doing the following:\n>\n> Either I don't understand the diagram or your term \"derived\". If\n> \"derived\" means \"on some branch of a merge\" and E is derived from A, B,\n> C, or D, then (since B, C, D is derived from A, and from A') E is\n> derived from A', and they will have a merge base.\n>\n\n\"Derived\" in my case means that E is processed from a snapshot of the\ntree at, say, A.\n\n> Are these diagrams really disconnected from each other?\n\nYes.  I started the history of E with plumbing using git commit-tree,\nwithout a -p flag specifying a parent\n\n>\n>> git checkout C\n>> gitk $(include_forks) &\n>> <View history, make changes, merges, et cetera>\n>> git checkout E\n>> <go back to gitk, only see history for B, C, etc>\n>>\n>> Now the 'include_forks' command is a bash function in my .bashrc:\n>>\n>> include_forks ()\n>> {\n>>     local head=\"$(git show -s --pretty=format:'%H' HEAD)\";\n>>     echo \"HEAD $(git for-each-ref --format='%(refname)' \\\n>>       refs/heads refs/remotes | while read ref; do \\\n>>       if test \"$(git merge-base HEAD ${ref}^{commit})\" != \"\"; \\\n>>               then echo ${ref}; fi; done)\"\n>> }\n>>\n>> The shell thus intercepts my command and I must restart gitk to see the history\n>> of E.\n>>\n>> With this patch, I can issue the command 'gitk --bouquet' and when I checkout\n>> E, I can 'reload' in gitk and see the history of E automatically.\n>\n> What would your patch do in the example you gave above? Which refs would\n> it cause gitk (rev-list) to show?\n>\n\nI wish to be concrete, so let us suppose you use a default clone of\ngit.git.  Further, suppose you are on origin/master.\nThen, with my patch,\n\ngit rev-list --bouquet\n\nshould be an---admittedly less efficient---equivalent to\n\ngit rev-list --all --not refs/remotes/origin/html\nrefs/remotes/origin/man refs/remotes/origin/todo\n\n> Michael\n>\n\nThanks,\n\nNathan Panike\n"},{"id":"128909","messageId":"7viqcqzhar.fsf@alter.siamese.dyndns.org","threadId":"21799","inReplyTo":"d77df1110912010931l40472723v80ad675a92d23fa3@mail.gmail.com","subject":"Re: [PATCH/RFC] Add a --bouquet option to git rev-list","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-12-01T18:21:32Z","receivedAt":"2009-12-01T18:21:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Nathan W. Panike\" <nathan.panike@gmail.com> writes:\n\n>>> include_forks ()\n>>> {\n>>>     local head=\"$(git show -s --pretty=format:'%H' HEAD)\";\n>>>     echo \"HEAD $(git for-each-ref --format='%(refname)' \\\n>>>       refs/heads refs/remotes | while read ref; do \\\n>>>       if test \"$(git merge-base HEAD ${ref}^{commit})\" != \"\"; \\\n>>>               then echo ${ref}; fi; done)\"\n>>> }\n\nBecause you have to traverse the entire history from tips of refs to know\nif the histories to reach them are disjoint, this is fundamentally a very\nexpensive operation and will not scale to projects with deep histories.\n\nIf a low-level support for this kind of thing is necessary, then I do not\nthink it should just be \"give me set of refs that is related to HEAD\".  I\nsuspect that is too inflexible to be useful in other situations.\n\nA command to list refs (i.e. not as rev-list argument that shows list of\ncommits, but as a new feature of for-each-ref) with new criteria might\nhave wider use (I am just thinking aloud).  Something like\n\n - among these refs (you would specify this with --all, --heads, or prefix\n   'refs/heads refs/remotes'), list only the ones related to this and that\n   ref (here you would give HEAD or whatever you want to check with as\n   argument)\"; and \n\n - its counterpart \"list the ones that are _not_ related\" with the same\n   input.\n\nAs to the implementation, instead of running get_merge_bases() number of\ntimes (a naive implementation would be O(n*m), I guess), I think it may\nmake sense to run the traversal in parallel, similar to the way done in\nshow-branches (but the termination condition would be different).\n"}]}