{"thread":{"id":"7509","subject":"How can I easily verify my diffs are in parent branch?","startedAt":"2007-04-04T11:36:23Z","lastAt":"2007-04-14T08:57:32Z","messageCount":19,"participants":["Alex Bennee","Alex Riesen","Andy Parkins","Linus Torvalds","Junio C Hamano","David Kågedal","Brian Gernhardt","Johannes Schindelin"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"38607","messageId":"1175686583.19898.68.camel@okra.transitives.com","threadId":"7509","inReplyTo":null,"subject":"How can I easily verify my diffs are in parent branch?","fromName":"Alex Bennee","fromEmail":"kernel-hacker@bennee.com","sentAt":"2007-04-04T11:36:23Z","receivedAt":"2007-04-04T11:36:23Z","isPatch":false,"sender":{"key":"kernel-hacker@bennee.com","avatar":null},"body":"Hi,\n\nThis is not the case of looking through the logs for my commit as I'm\nexporting my changes from my tree into the company system through CVS.\nThis means all the usual commit tracking benefits are lost.\n\nSo I have a master branch which tracks this master baseline from CVS and\neach release I import a big change set which includes all the fixes that\nwent into that baseline.\n\nWhat I need to do is check that my commits that I submitted for the\nbaseline have been correctly merged. Of course if I do \"git-diff\nmaster..mywork\" I'll see all the other code that has been added in (or\nmore usually is missing from my branch).\n\nIs there an invocation of git-diff or another tool that can tell me all\nmy diffs are present in the big uber-commit of my master branch baseline\nrelease?\n\n-- \nAlex, homepage: http://www.bennee.com/~alex/\nAny excuse will serve a tyrant. -- Aesop\n"},{"id":"38610","messageId":"81b0412b0704040528g3fd6b3d0id9cc1b98fe58d8e@mail.gmail.com","threadId":"7509","inReplyTo":"1175686583.19898.68.camel@okra.transitives.com","subject":"Re: How can I easily verify my diffs are in parent branch?","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2007-04-04T12:28:08Z","receivedAt":"2007-04-04T12:28:08Z","isPatch":false,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"On 4/4/07, Alex Bennee <kernel-hacker@bennee.com> wrote:\n> Is there an invocation of git-diff or another tool that can tell me all\n> my diffs are present in the big uber-commit of my master branch baseline\n> release?\n\nYou can limit git-diff to the pathnames you touched in your commits\n(assuming you now the first commit you sent upstream).\n\ngit diff-tree --name-only first HEAD | while read f; do\n  git diff-tree --shortstat upstream HEAD -- \"$f\"\ndone\n\nIt is very inefficient, though\n"},{"id":"38612","messageId":"200704041356.47123.andyparkins@gmail.com","threadId":"7509","inReplyTo":"1175686583.19898.68.camel@okra.transitives.com","subject":"Re: How can I easily verify my diffs are in parent branch?","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-04-04T12:56:46Z","receivedAt":"2007-04-04T12:56:46Z","isPatch":false,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"On Wednesday 2007 April 04 12:36, Alex Bennee wrote:\n\n> What I need to do is check that my commits that I submitted for the\n> baseline have been correctly merged. Of course if I do \"git-diff\n> master..mywork\" I'll see all the other code that has been added in (or\n> more usually is missing from my branch).\n>\n> Is there an invocation of git-diff or another tool that can tell me all\n> my diffs are present in the big uber-commit of my master branch baseline\n> release?\n\nKind of.  Try git-cherry (I like to use -v as well).  This will compare the \nhash of the diff of each of the revisions in your current branch with those \nof an upstream branch.\n\n * -- O -- * -- * -- C' -- * (upstream)\n       \\\n        A -- B -- C -- D (yourbranch)\n\nWith something like the above, were C has been accepted into upstream as \nrevision C', runing git-cherry on yourbranch would give:\n\n $ git-cherry upstream\n +A\n +B\n -C\n +D\n\nThe \"-\" in front of C means that you can remove C from yourbranch - it's been \naccepted.\n\nHowever, this relies on the applied patch matching exactly (not the log \nmessage - that can be anything), so if a typo got fixed by the maintainer, it \nwould show up as not having been accepted.\n\nAnother good way of telling is to rebase yourbranch onto the current \nupstream - a patch that doesn't need applying (because it's already there) \nget's dropped automatically by git.  This is really handy because git \neffectively filters those patches that you don't need to worry about any more \nin yourbranch.  This would cope with a typo fix as well, because git-rebase \nwould note a conflict which you would then see as being a minor typo and \nwould do \"git rebase --skip\" which would manually drop the patch from \nyourbranch.\n\n\n\nAndy\n-- \nDr Andy Parkins, M Eng (hons), MIET\nandyparkins@gmail.com\n"},{"id":"38622","messageId":"Pine.LNX.4.64.0704040744160.6730@woody.linux-foundation.org","threadId":"7509","inReplyTo":"1175686583.19898.68.camel@okra.transitives.com","subject":"Re: How can I easily verify my diffs are in parent branch?","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-04-04T15:12:20Z","receivedAt":"2007-04-04T15:12:20Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 4 Apr 2007, Alex Bennee wrote:\n> \n> This is not the case of looking through the logs for my commit as I'm\n> exporting my changes from my tree into the company system through CVS.\n> This means all the usual commit tracking benefits are lost.\n\nYeah, sad.\n\n> So I have a master branch which tracks this master baseline from CVS and\n> each release I import a big change set which includes all the fixes that\n> went into that baseline.\n\nSo all your small diffs get smushed in as part of one *big* change? Or do \nthey still exist in the baseline CVS tree as individual commits?\n\nIf they still exist in the CVS tree as individual commits, you're slightly \nbetter off: you can use \"git patch-id\" to generate a hash of all the \npatches, and compare just the hashes. That allows you to efficiently find \npatches that have been applied *identically* in both trees.\n\nNOTE! \"git-patch-id\" generates a hash of a patch by ignoring \n - line numbers\n - whitespace\n - commit comments\nso \"identical\" means just that the patch has to have the exact same \ncontext and +/- patterns, but it will still be considered identical if \nit's been moved around (perhaps because some other patch added/removed \ncode before it) or if the whitespace has been tweaked.\n\nYou can then compare the hashes upstream with all the commits *you* cared \nabout, and see if they are all there. But as noted, this only works if \nupstream is expected to actually honor patch-boundaries. If you just get a \nsingle big changeset that contains *all* the changes, doign this obviously \nwon't work.\n\nNOTE2! I don't think anybody actually *uses* git-patch-id, and what you \nshould do is to use \"git cherry\" that does this all internally, but it is \nworth understanding *what* git-cherry does.\n\nSo to compare all patch-ID's, you can do\n\n\tgit cherry cvs-upstream my-branch\n\nadn it should look at all the commits that are in *your* branch but not \nupstream, and report their ID's preceded by a \"-\" if they are upstream, \nand a \"+\" if they are not.\n\nYou can then look at the \"+\" commits more closely, to see whether maybe \nthey actually did get merged, but got changed/fixed in the process, or \nwhether they really are missing.\n\n> Is there an invocation of git-diff or another tool that can tell me all\n> my diffs are present in the big uber-commit of my master branch baseline\n> release?\n\nIf git cherry doesn't work for you, you're kind of screwed and have to do \nit manually. Of course, even \"manually\" can be done with a lot of help \nfrom git.\n\nFor example, one thing you can do, if the number of commits you have is \nfairly small, is to just be on your \"my-branch\" and then do\n\n\tgit rebase [--merge] cvs-upstream\n\nwhich will rebase your \"my-branch\" onto the CVS upstream thing. It will \nautomatically discard any patches that get merged away (which effectively \nmeans that they were already there). The end result will be that your \nbranch will now either be identical to \"cvs-upstream\" (if everything was \nalready there) or will contain the commits that weren't there on top of \nthe new cvs-upstream tip.\n\nNOTE NOTE NOTE! The \"git rebase\" thing sounds perfect, but the fact is, \nquite often you'll end up having to help it do its work. It really \ndefaults to just trying to apply the patches (ie without \"--merge\" it's \nliterally just a fancy\n\n\tgit-format-patch | git am\n\npipeline).\n\nSo \"git rebase\" may well be the right thing for you, but quite frankly, \nit's more likely to work well for simple cases (with no real conflicts \nwith anybody elses work in the same areas) than for anything complicated.\n\nFor complicated stuff, you'll be on your own. \"git diff pathname\" etc..\n\n\t\tLinus\n"},{"id":"38674","messageId":"7vircbwfym.fsf@assigned-by-dhcp.cox.net","threadId":"7509","inReplyTo":"Pine.LNX.4.64.0704040744160.6730@woody.linux-foundation.org","subject":"Re: How can I easily verify my diffs are in parent branch?","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-04-05T05:25:05Z","receivedAt":"2007-04-05T05:25:05Z","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> So to compare all patch-ID's, you can do\n>\n> \tgit cherry cvs-upstream my-branch\n>\n> adn it should look at all the commits that are in *your* branch but not \n> upstream, and report their ID's preceded by a \"-\" if they are upstream, \n> and a \"+\" if they are not.\n>\n> You can then look at the \"+\" commits more closely, to see whether maybe \n> they actually did get merged, but got changed/fixed in the process, or \n> whether they really are missing.\n\nFunny.\n\nLast night I was thinking about git-cherry, as it is one of the\nfew commands that have \"funny parameter semantics that do not\nmesh well with git-log family\" (others are format-patch and\nrebase).\n\nI think we should be able to use --left-right and ... operator\nto express what the above cherry does with something like:\n\n    $ git log --left-right --ignore-common-patch cvs-upstream...my-branch\n\nThe --ignore-common-patch option does not exist yet, but the\nbasic code to implement it should already be accessible from the\nlog family, as that is what format-patch needs to do.\n"},{"id":"38689","messageId":"87y7l7tc4j.fsf@morpheus.local","threadId":"7509","inReplyTo":"7vircbwfym.fsf@assigned-by-dhcp.cox.net","subject":"Re: How can I easily verify my diffs are in parent branch?","fromName":"David Kågedal","fromEmail":"davidk@lysator.liu.se","sentAt":"2007-04-05T09:16:12Z","receivedAt":"2007-04-05T09:16:12Z","isPatch":false,"sender":{"key":"davidk@lysator.liu.se","avatar":"https://avatars.githubusercontent.com/u/60530?v=4"},"body":"Junio C Hamano <junkio@cox.net> writes:\n\n> I think we should be able to use --left-right and ... operator\n> to express what the above cherry does with something like:\n>\n>     $ git log --left-right --ignore-common-patch cvs-upstream...my-branch\n\n--left-right is not documented, so I think it's hard for people (for\nme at least) to follow your reasoning.\n\nWould it be to much to ask for to want to have all options have some\nkind of documentation? As in not accepting patches for new options\nthat don't include documentation?\n\n\"git log -Sleft-right\" will actually give me the documentation I want,\nsince the docs are written but only put in the commit message.  A git\nuser that only uses a git installation has no way of finding that,\nthough.\n\n-- \nDavid Kågedal\n"},{"id":"38692","messageId":"7vwt0rqftv.fsf@assigned-by-dhcp.cox.net","threadId":"7509","inReplyTo":"87y7l7tc4j.fsf@morpheus.local","subject":"Re: How can I easily verify my diffs are in parent branch?","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-04-05T10:24:28Z","receivedAt":"2007-04-05T10:24:28Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"David Kågedal <davidk@lysator.liu.se> writes:\n\n> Junio C Hamano <junkio@cox.net> writes:\n>\n>> I think we should be able to use --left-right and ... operator\n>> to express what the above cherry does with something like:\n>>\n>>     $ git log --left-right --ignore-common-patch cvs-upstream...my-branch\n>\n> --left-right is not documented, so I think it's hard for people (for\n> me at least) to follow your reasoning.\n>\n> Would it be to much to ask for to want to have all options have some\n> kind of documentation?\n\nNot at all.  I often wish that ;-)\n\n> As in not accepting patches for new options\n> that don't include documentation?\n>\n> \"git log -Sleft-right\" will actually give me the documentation I want,\n> since the docs are written but only put in the commit message.\n\nFair enough.  Perhaps the list can help out ;-).\n"},{"id":"38708","messageId":"20070405145307.GA18458@174.242.249.10.in-addr.arpa","threadId":"7509","inReplyTo":"7vwt0rqftv.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] Document --left-right option to rev-list.","fromName":"Brian Gernhardt","fromEmail":"benji@silverinsanity.com","sentAt":"2007-04-05T14:53:07Z","receivedAt":"2007-04-05T14:53:07Z","isPatch":true,"sender":{"key":"benji@silverinsanity.com","avatar":"https://gravatar.com/avatar/e06c101dbc25c68114d859b4a9ec7cf8a2c52fd2b0270ef0eac0e2e63ff22311?d=mp&s=160"},"body":"Explanation is paraphrased from \"577ed5c... rev-list --left-right\"\n---\n\nJunio C Hamano <junkio@cox.net> writes:\n\n> David Kågedal <davidk@lysator.liu.se> writes:\n>\n>> \"git log -Sleft-right\" will actually give me the documentation I want,\n>> since the docs are written but only put in the commit message.\n>\n> Fair enough.  Perhaps the list can help out ;-).\n\nLike this?  :-)\n\n Documentation/git-rev-list.txt |   19 +++++++++++++++++++\n builtin-rev-list.c             |    1 +\n 2 files changed, 20 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt\nindex 3fa45b8..5b33865 100644\n--- a/Documentation/git-rev-list.txt\n+++ b/Documentation/git-rev-list.txt\n@@ -21,6 +21,7 @@ SYNOPSIS\n \t     [ \\--stdin ]\n \t     [ \\--topo-order ]\n \t     [ \\--parents ]\n+\t     [ \\--left-right ]\n \t     [ \\--encoding[=<encoding>] ]\n \t     [ \\--(author|committer|grep)=<pattern> ]\n \t     [ [\\--objects | \\--objects-edge] [ \\--unpacked ] ]\n@@ -101,6 +102,24 @@ include::pretty-formats.txt[]\n \n \tPrint the parents of the commit.\n \n+--left-right::\n+\n+\tMark which side of a symmetric diff a commit is reachable from.\n+\tCommits from the left side are prefixed with `<` and those from\n+\tthe right with `>`.  If combined with `--boundary`, those\n+\tcommits are prefixed with `-`.  For example:\n+\n+-----------------------------------------------------------------------\n+\t$ git rev-list --left-right --boundary --pretty=oneline A...B\n+\n+\t>bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 3rd on b\n+\t>bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 2nd on b\n+\t<aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 3rd on a\n+\t<aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 2nd on a\n+\t-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 1st on b\n+\t-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 1st on a\n+-----------------------------------------------------------------------\n+\n Diff Formatting\n ~~~~~~~~~~~~~~~\n \ndiff --git a/builtin-rev-list.c b/builtin-rev-list.c\nindex f91685a..09774f9 100644\n--- a/builtin-rev-list.c\n+++ b/builtin-rev-list.c\n@@ -35,6 +35,7 @@ static const char rev_list_usage[] =\n \"    --header | --pretty\\n\"\n \"    --abbrev=nr | --no-abbrev\\n\"\n \"    --abbrev-commit\\n\"\n+\"    --left-right\\n\"\n \"  special purpose:\\n\"\n \"    --bisect\\n\"\n \"    --bisect-vars\"\n-- \n1.5.1.32.gdd6cd\n"},{"id":"38739","messageId":"7v1wiyo63s.fsf@assigned-by-dhcp.cox.net","threadId":"7509","inReplyTo":"20070405145307.GA18458@174.242.249.10.in-addr.arpa","subject":"Re: [PATCH] Document --left-right option to rev-list.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-04-05T21:37:27Z","receivedAt":"2007-04-05T21:37:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thanks.\n"},{"id":"38841","messageId":"81b0412b0704070354n56d9901agfc4b351736f1d574@mail.gmail.com","threadId":"7509","inReplyTo":"20070405145307.GA18458@174.242.249.10.in-addr.arpa","subject":"Re: [PATCH] Document --left-right option to rev-list.","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2007-04-07T10:54:31Z","receivedAt":"2007-04-07T10:54:31Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"On 4/5/07, Brian Gernhardt <benji@silverinsanity.com> wrote:\n> +--left-right::\n> +\n> +       Mark which side of a symmetric diff a commit is reachable from.\n> +       Commits from the left side are prefixed with `<` and those from\n> +       the right with `>`.  If combined with `--boundary`, those\n> +       commits are prefixed with `-`.  For example:\n\n\"If combined with '--boundary', the boundary commits will be also printed\nand prefixed with '-'.\"\n\nOtherwise it is a bit unclear what commits are prefixed with '-': the left,\nthe right, or the boundary.\n"},{"id":"38926","messageId":"7virc524cd.fsf_-_@assigned-by-dhcp.cox.net","threadId":"7509","inReplyTo":"7vircbwfym.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 1/2] git-log --cherry-pick","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-04-09T11:07:46Z","receivedAt":"2007-04-09T11:07:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This is meant to be a saner replacement for \"git-cherry\".\n\nWhen used with \"A...B\", this filters out commits whose patch\ntext has the same patch-id as a commit on the other side.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\n  Junio C Hamano <junkio@cox.net> writes:\n\n  > Funny.\n  >\n  > Last night I was thinking about git-cherry, as it is one of the\n  > few commands that have \"funny parameter semantics that do not\n  > mesh well with git-log family\" (others are format-patch and\n  > rebase).\n  >\n  > I think we should be able to use --left-right and ... operator\n  > to express what the above cherry does with something like:\n  >\n  >     $ git log --left-right --ignore-common-patch cvs-upstream...my-branch\n  >\n  > The --ignore-common-patch option does not exist yet, but the\n  > basic code to implement it should already be accessible from the\n  > log family, as that is what format-patch needs to do.\n\n revision.c |  141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n revision.h |    1 +\n 2 files changed, 142 insertions(+), 0 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 486393c..0903f19 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -422,6 +422,139 @@ static void add_parents_to_list(struct rev_info *revs, struct commit *commit, st\n \t}\n }\n \n+/*\n+ * This needs to be moved from builtin-log -- its get_patch_ids() implementation\n+ * is horrible -- it pollutes the object array with non objects!\n+ */\n+static int get_patch_id(struct commit *commit, struct diff_options *options,\n+\t\tunsigned char *sha1)\n+{\n+\tif (commit->parents)\n+\t\tdiff_tree_sha1(commit->parents->item->object.sha1,\n+\t\t               commit->object.sha1, \"\", options);\n+\telse\n+\t\tdiff_root_tree_sha1(commit->object.sha1, \"\", options);\n+\tdiffcore_std(options);\n+\treturn diff_flush_patch_id(options, sha1);\n+}\n+\n+struct patch_id_ent {\n+\tunsigned char patch_id[20];\n+\tchar seen;\n+};\n+\n+static int compare_patch_id(const void *a_, const void *b_)\n+{\n+\tstruct patch_id_ent *a = *((struct patch_id_ent **)a_);\n+\tstruct patch_id_ent *b = *((struct patch_id_ent **)b_);\n+\treturn hashcmp(a->patch_id, b->patch_id);\n+}\n+\n+static void cherry_pick_list(struct commit_list *list)\n+{\n+\tstruct commit_list *p;\n+\tint left_count = 0, right_count = 0, nr;\n+\tstruct patch_id_ent *patches, **table;\n+\tint left_first, table_size;\n+\tstruct diff_options opts;\n+\n+\t/* First count the commits on the left and on the right */\n+\tfor (p = list; p; p = p->next) {\n+\t\tstruct commit *commit = p->item;\n+\t\tunsigned flags = commit->object.flags;\n+\t\tif (flags & BOUNDARY)\n+\t\t\t;\n+\t\telse if (flags & SYMMETRIC_LEFT)\n+\t\t\tleft_count++;\n+\t\telse\n+\t\t\tright_count++;\n+\t}\n+\n+\tleft_first = left_count < right_count;\n+\ttable_size = left_first ? left_count : right_count;\n+\n+\t/* Allocate a look-up table to help matching up */\n+\tpatches = xcalloc(table_size, sizeof(struct patch_id_ent));\n+\ttable = xcalloc(table_size, sizeof(struct patch_id_ent *));\n+\tnr = 0;\n+\n+\tdiff_setup(&opts);\n+\topts.recursive = 1;\n+\tif (diff_setup_done(&opts) < 0)\n+\t\tdie(\"diff_setup_done failed\");\n+\n+\t/* Compute patch-ids for one side */\n+\tfor (p = list; p; p = p->next) {\n+\t\tstruct commit *commit = p->item;\n+\t\tunsigned flags = commit->object.flags;\n+\n+\t\tif (flags & BOUNDARY)\n+\t\t\tcontinue;\n+\t\t/*\n+\t\t * If we have fewer left, left_first is set and we omit\n+\t\t * commits on the right branch in this loop.  If we have\n+\t\t * fewer right, we skip the left ones.\n+\t\t */\n+\t\tif (left_first != !!(flags & SYMMETRIC_LEFT))\n+\t\t\tcontinue;\n+\t\tif (get_patch_id(commit, &opts, patches[nr].patch_id))\n+\t\t\tcontinue;\n+\t\t/*\n+\t\t * FIXME: this does not really work if the side\n+\t\t * we are dealing with have two commits with the same\n+\t\t * patch id, as we end up having two entries in the\n+\t\t * patch table.\n+\t\t */\n+\t\ttable[nr] = &(patches[nr]);\n+\t\tcommit->util = table[nr];\n+\t\tnr++;\n+\t}\n+\tqsort(table, nr, sizeof(table[0]), compare_patch_id);\n+\n+\t/* Check the other side */\n+\tfor (p = list; p; p = p->next) {\n+\t\tstruct commit *commit = p->item;\n+\t\tunsigned flags = commit->object.flags;\n+\t\tstruct patch_id_ent ent, *entp = &ent, **found;\n+\n+\t\tif (flags & BOUNDARY)\n+\t\t\tcontinue;\n+\t\t/*\n+\t\t * If we have fewer left, left_first is set and we omit\n+\t\t * commits on the left branch in this loop.\n+\t\t */\n+\t\tif (left_first == !!(flags & SYMMETRIC_LEFT))\n+\t\t\tcontinue;\n+\t\tif (get_patch_id(commit, &opts, ent.patch_id))\n+\t\t\tcontinue;\n+\t\t/*\n+\t\t * Have we seen the same patch id?\n+\t\t */\n+\t\tfound = bsearch(&entp, table, nr, sizeof(table[0]),\n+\t\t\t\tcompare_patch_id);\n+\t\tif (!found)\n+\t\t\tcontinue;\n+\t\t(*found)->seen = 1;\n+\t\tcommit->object.flags |= SHOWN; /* exclude this from the output set */\n+\t}\n+\n+\t/* Now check the original side for seen ones */\n+\tfor (p = list; p; p = p->next) {\n+\t\tstruct commit *commit = p->item;\n+\t\tstruct patch_id_ent *ent;\n+\n+\t\tent = commit->util;\n+\t\tif (!ent)\n+\t\t\tcontinue;\n+\t\tif (ent->seen)\n+\t\t\tcommit->object.flags |= SHOWN;\n+\t\tcommit->util = NULL;\n+\t}\n+\n+\tfree(table);\n+\tfree(patches);\n+}\n+\n static void limit_list(struct rev_info *revs)\n {\n \tstruct commit_list *list = revs->commits;\n@@ -449,6 +582,9 @@ static void limit_list(struct rev_info *revs)\n \t\t\tcontinue;\n \t\tp = &commit_list_insert(commit, p)->next;\n \t}\n+\tif (revs->cherry_pick)\n+\t\tcherry_pick_list(newlist);\n+\n \trevs->commits = newlist;\n }\n \n@@ -913,6 +1049,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch\n \t\t\t\trevs->left_right = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (!strcmp(arg, \"--cherry-pick\")) {\n+\t\t\t\trevs->cherry_pick = 1;\n+\t\t\t\trevs->left_right = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (!strcmp(arg, \"--objects\")) {\n \t\t\t\trevs->tag_objects = 1;\n \t\t\t\trevs->tree_objects = 1;\ndiff --git a/revision.h b/revision.h\nindex 55e6b53..b69624a 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -47,6 +47,7 @@ struct rev_info {\n \t\t\tleft_right:1,\n \t\t\tparents:1,\n \t\t\treverse:1,\n+\t\t\tcherry_pick:1,\n \t\t\tfirst_parent_only:1;\n \n \t/* Diff flags */\n"},{"id":"38927","messageId":"7vbqhx246m.fsf_-_@assigned-by-dhcp.cox.net","threadId":"7509","inReplyTo":"7vircbwfym.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 2/2] Add %m to '--pretty=format:'","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-04-09T11:11:13Z","receivedAt":"2007-04-09T11:11:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When used with '--boundary A...B', this shows the -/</> marker you\nwould get with --left-right option to 'git-log' family.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\n * This can be used like this:\n\n\t$ git log --cherry-pick --pretty='format:%m%h %s' origin...master\n\t<e712d0c upstream side commit\n        <deadbee upstream side commit\n        >cafedee commit on my side\n\n   to identify commits yet to be sent, probably easier to read\n   than git-cherry.\n\n commit.c |   16 +++++++++++++---\n 1 files changed, 13 insertions(+), 3 deletions(-)\n\ndiff --git a/commit.c b/commit.c\nindex 754d1b8..952095f 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -4,6 +4,8 @@\n #include \"pkt-line.h\"\n #include \"utf8.h\"\n #include \"interpolate.h\"\n+#include \"diff.h\"\n+#include \"revision.h\"\n \n int save_commit_buffer = 1;\n \n@@ -808,7 +810,8 @@ static long format_commit_message(const struct commit *commit,\n \t\t{ \"%Cgreen\" },\t/* green */\n \t\t{ \"%Cblue\" },\t/* blue */\n \t\t{ \"%Creset\" },\t/* reset color */\n-\t\t{ \"%n\" }\t/* newline */\n+\t\t{ \"%n\" },\t/* newline */\n+\t\t{ \"%m\" },\t/* left/right/bottom */\n \t};\n \tenum interp_index {\n \t\tIHASH = 0, IHASH_ABBREV,\n@@ -824,14 +827,15 @@ static long format_commit_message(const struct commit *commit,\n \t\tISUBJECT,\n \t\tIBODY,\n \t\tIRED, IGREEN, IBLUE, IRESET_COLOR,\n-\t\tINEWLINE\n+\t\tINEWLINE,\n+\t\tILEFT_RIGHT,\n \t};\n \tstruct commit_list *p;\n \tchar parents[1024];\n \tint i;\n \tenum { HEADER, SUBJECT, BODY } state;\n \n-\tif (INEWLINE + 1 != ARRAY_SIZE(table))\n+\tif (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))\n \t\tdie(\"invalid interp table!\");\n \n \t/* these are independent of the commit */\n@@ -852,6 +856,12 @@ static long format_commit_message(const struct commit *commit,\n \tinterp_set_entry(table, ITREE_ABBREV,\n \t\t\tfind_unique_abbrev(commit->tree->object.sha1,\n \t\t\t\tDEFAULT_ABBREV));\n+\tinterp_set_entry(table, ILEFT_RIGHT,\n+\t\t\t (commit->object.flags & BOUNDARY)\n+\t\t\t ? \"-\"\n+\t\t\t : (commit->object.flags & SYMMETRIC_LEFT)\n+\t\t\t ? \"<\"\n+\t\t\t : \">\");\n \n \tparents[1] = 0;\n \tfor (i = 0, p = commit->parents;\n"},{"id":"39067","messageId":"7vslb7ua60.fsf_-_@assigned-by-dhcp.cox.net","threadId":"7509","inReplyTo":"7virc524cd.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 1/4] Add %m to '--pretty=format:'","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-04-10T22:39:03Z","receivedAt":"2007-04-10T22:39:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When used with '--boundary A...B', this shows the -/</> marker\nyou would get with --left-right option to 'git-log' family.\nWhen symmetric diff is not used, everybody is shown to be on the\n\"right\" branch.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n * This was one thing I found missing from the \"--pretty=format:\"\n   Dscho did.  This time it comes with a documentation updates ;-).\n\n Documentation/pretty-formats.txt |    1 +\n commit.c                         |   16 +++++++++++++---\n 2 files changed, 14 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex 2fe6c31..d7ffc21 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -117,6 +117,7 @@ The placeholders are:\n - '%Cgreen': switch color to green\n - '%Cblue': switch color to blue\n - '%Creset': reset color\n+- '%m': left, right or boundary mark\n - '%n': newline\n \n \ndiff --git a/commit.c b/commit.c\nindex 754d1b8..952095f 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -4,6 +4,8 @@\n #include \"pkt-line.h\"\n #include \"utf8.h\"\n #include \"interpolate.h\"\n+#include \"diff.h\"\n+#include \"revision.h\"\n \n int save_commit_buffer = 1;\n \n@@ -808,7 +810,8 @@ static long format_commit_message(const struct commit *commit,\n \t\t{ \"%Cgreen\" },\t/* green */\n \t\t{ \"%Cblue\" },\t/* blue */\n \t\t{ \"%Creset\" },\t/* reset color */\n-\t\t{ \"%n\" }\t/* newline */\n+\t\t{ \"%n\" },\t/* newline */\n+\t\t{ \"%m\" },\t/* left/right/bottom */\n \t};\n \tenum interp_index {\n \t\tIHASH = 0, IHASH_ABBREV,\n@@ -824,14 +827,15 @@ static long format_commit_message(const struct commit *commit,\n \t\tISUBJECT,\n \t\tIBODY,\n \t\tIRED, IGREEN, IBLUE, IRESET_COLOR,\n-\t\tINEWLINE\n+\t\tINEWLINE,\n+\t\tILEFT_RIGHT,\n \t};\n \tstruct commit_list *p;\n \tchar parents[1024];\n \tint i;\n \tenum { HEADER, SUBJECT, BODY } state;\n \n-\tif (INEWLINE + 1 != ARRAY_SIZE(table))\n+\tif (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))\n \t\tdie(\"invalid interp table!\");\n \n \t/* these are independent of the commit */\n@@ -852,6 +856,12 @@ static long format_commit_message(const struct commit *commit,\n \tinterp_set_entry(table, ITREE_ABBREV,\n \t\t\tfind_unique_abbrev(commit->tree->object.sha1,\n \t\t\t\tDEFAULT_ABBREV));\n+\tinterp_set_entry(table, ILEFT_RIGHT,\n+\t\t\t (commit->object.flags & BOUNDARY)\n+\t\t\t ? \"-\"\n+\t\t\t : (commit->object.flags & SYMMETRIC_LEFT)\n+\t\t\t ? \"<\"\n+\t\t\t : \">\");\n \n \tparents[1] = 0;\n \tfor (i = 0, p = commit->parents;\n-- \n1.5.1.777.gd14d3\n"},{"id":"39050","messageId":"7vmz1fua4r.fsf_-_@assigned-by-dhcp.cox.net","threadId":"7509","inReplyTo":"7virc524cd.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 2/4] Refactor patch-id filtering out of git-cherry and git-format-patch.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-04-10T22:39:48Z","receivedAt":"2007-04-10T22:39:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This implements the patch-id computation and recording library,\npatch-ids.c, and rewrites the get_patch_ids() function used in\ncherry and format-patch to use it, so that they do not pollute\nthe object namespace.  Earlier code threw non-objects into the\nin-core object database, and hoped for not getting bitten by\nSHA-1 collisions.  While it may be practically Ok, it still was\nan ugly hack.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n * Replacement of the previous one.  I wanted to clean-up the\n   mess first.\n\n Makefile      |    3 +-\n builtin-log.c |   44 ++++---------\n patch-ids.c   |  192 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n patch-ids.h   |   21 ++++++\n 4 files changed, 228 insertions(+), 32 deletions(-)\n create mode 100644 patch-ids.c\n create mode 100644 patch-ids.h\n\ndiff --git a/Makefile b/Makefile\nindex a77d31d..f956c3d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -283,7 +283,7 @@ LIB_H = \\\n \tdiff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \\\n \trun-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \\\n \ttree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \\\n-\tutf8.h reflog-walk.h\n+\tutf8.h reflog-walk.h patch-ids.h\n \n DIFF_OBJS = \\\n \tdiff.o diff-lib.o diffcore-break.o diffcore-order.o \\\n@@ -295,6 +295,7 @@ LIB_OBJS = \\\n \tdate.o diff-delta.o entry.o exec_cmd.o ident.o \\\n \tinterpolate.o \\\n \tlockfile.o \\\n+\tpatch-ids.o \\\n \tobject.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \\\n \treachable.o reflog-walk.o \\\n \tquote.o read-cache.o refs.o run-command.o dir.o object-refs.o \\\ndiff --git a/builtin-log.c b/builtin-log.c\nindex 71df957..df676df 100644\n--- a/builtin-log.c\n+++ b/builtin-log.c\n@@ -12,6 +12,7 @@\n #include \"builtin.h\"\n #include \"tag.h\"\n #include \"reflog-walk.h\"\n+#include \"patch-ids.h\"\n \n static int default_show_root = 1;\n \n@@ -333,25 +334,12 @@ static int reopen_stdout(struct commit *commit, int nr, int keep_subject)\n \n }\n \n-static int get_patch_id(struct commit *commit, struct diff_options *options,\n-\t\tunsigned char *sha1)\n-{\n-\tif (commit->parents)\n-\t\tdiff_tree_sha1(commit->parents->item->object.sha1,\n-\t\t               commit->object.sha1, \"\", options);\n-\telse\n-\t\tdiff_root_tree_sha1(commit->object.sha1, \"\", options);\n-\tdiffcore_std(options);\n-\treturn diff_flush_patch_id(options, sha1);\n-}\n-\n-static void get_patch_ids(struct rev_info *rev, struct diff_options *options, const char *prefix)\n+static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const char *prefix)\n {\n \tstruct rev_info check_rev;\n \tstruct commit *commit;\n \tstruct object *o1, *o2;\n \tunsigned flags1, flags2;\n-\tunsigned char sha1[20];\n \n \tif (rev->pending.nr != 2)\n \t\tdie(\"Need exactly one range.\");\n@@ -364,10 +352,7 @@ static void get_patch_ids(struct rev_info *rev, struct diff_options *options, co\n \tif ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))\n \t\tdie(\"Not a range.\");\n \n-\tdiff_setup(options);\n-\toptions->recursive = 1;\n-\tif (diff_setup_done(options) < 0)\n-\t\tdie(\"diff_setup_done failed\");\n+\tinit_patch_ids(ids);\n \n \t/* given a range a..b get all patch ids for b..a */\n \tinit_revisions(&check_rev, prefix);\n@@ -382,8 +367,7 @@ static void get_patch_ids(struct rev_info *rev, struct diff_options *options, co\n \t\tif (commit->parents && commit->parents->next)\n \t\t\tcontinue;\n \n-\t\tif (!get_patch_id(commit, options, sha1))\n-\t\t\tcreated_object(sha1, xcalloc(1, sizeof(struct object)));\n+\t\tadd_commit_patch_id(commit, ids);\n \t}\n \n \t/* reset for next revision walk */\n@@ -420,7 +404,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tint ignore_if_in_upstream = 0;\n \tint thread = 0;\n \tconst char *in_reply_to = NULL;\n-\tstruct diff_options patch_id_opts;\n+\tstruct patch_ids ids;\n \tchar *add_signoff = NULL;\n \tchar message_id[1024];\n \tchar ref_message_id[1024];\n@@ -554,22 +538,19 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (ignore_if_in_upstream)\n-\t\tget_patch_ids(&rev, &patch_id_opts, prefix);\n+\t\tget_patch_ids(&rev, &ids, prefix);\n \n \tif (!use_stdout)\n \t\trealstdout = fdopen(dup(1), \"w\");\n \n \tprepare_revision_walk(&rev);\n \twhile ((commit = get_revision(&rev)) != NULL) {\n-\t\tunsigned char sha1[20];\n-\n \t\t/* ignore merges */\n \t\tif (commit->parents && commit->parents->next)\n \t\t\tcontinue;\n \n \t\tif (ignore_if_in_upstream &&\n-\t\t\t\t!get_patch_id(commit, &patch_id_opts, sha1) &&\n-\t\t\t\tlookup_object(sha1))\n+\t\t\t\thas_commit_patch_id(commit, &ids))\n \t\t\tcontinue;\n \n \t\tnr++;\n@@ -624,6 +605,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\tfclose(stdout);\n \t}\n \tfree(list);\n+\tif (ignore_if_in_upstream)\n+\t\tfree_patch_ids(&ids);\n \treturn 0;\n }\n \n@@ -646,7 +629,7 @@ static const char cherry_usage[] =\n int cmd_cherry(int argc, const char **argv, const char *prefix)\n {\n \tstruct rev_info revs;\n-\tstruct diff_options patch_id_opts;\n+\tstruct patch_ids ids;\n \tstruct commit *commit;\n \tstruct commit_list *list = NULL;\n \tconst char *upstream;\n@@ -692,7 +675,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)\n \t\t\treturn 0;\n \t}\n \n-\tget_patch_ids(&revs, &patch_id_opts, prefix);\n+\tget_patch_ids(&revs, &ids, prefix);\n \n \tif (limit && add_pending_commit(limit, &revs, UNINTERESTING))\n \t\tdie(\"Unknown commit %s\", limit);\n@@ -708,12 +691,10 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)\n \t}\n \n \twhile (list) {\n-\t\tunsigned char sha1[20];\n \t\tchar sign = '+';\n \n \t\tcommit = list->item;\n-\t\tif (!get_patch_id(commit, &patch_id_opts, sha1) &&\n-\t\t    lookup_object(sha1))\n+\t\tif (has_commit_patch_id(commit, &ids))\n \t\t\tsign = '-';\n \n \t\tif (verbose) {\n@@ -731,5 +712,6 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)\n \t\tlist = list->next;\n \t}\n \n+\tfree_patch_ids(&ids);\n \treturn 0;\n }\ndiff --git a/patch-ids.c b/patch-ids.c\nnew file mode 100644\nindex 0000000..a288fac\n--- /dev/null\n+++ b/patch-ids.c\n@@ -0,0 +1,192 @@\n+#include \"cache.h\"\n+#include \"diff.h\"\n+#include \"commit.h\"\n+#include \"patch-ids.h\"\n+\n+static int commit_patch_id(struct commit *commit, struct diff_options *options,\n+\t\t    unsigned char *sha1)\n+{\n+\tif (commit->parents)\n+\t\tdiff_tree_sha1(commit->parents->item->object.sha1,\n+\t\t               commit->object.sha1, \"\", options);\n+\telse\n+\t\tdiff_root_tree_sha1(commit->object.sha1, \"\", options);\n+\tdiffcore_std(options);\n+\treturn diff_flush_patch_id(options, sha1);\n+}\n+\n+static uint32_t take2(const unsigned char *id)\n+{\n+\treturn ((id[0] << 8) | id[1]);\n+}\n+\n+/*\n+ * Conventional binary search loop looks like this:\n+ *\n+ *      do {\n+ *              int mi = (lo + hi) / 2;\n+ *              int cmp = \"entry pointed at by mi\" minus \"target\";\n+ *              if (!cmp)\n+ *                      return (mi is the wanted one)\n+ *              if (cmp > 0)\n+ *                      hi = mi; \"mi is larger than target\"\n+ *              else\n+ *                      lo = mi+1; \"mi is smaller than target\"\n+ *      } while (lo < hi);\n+ *\n+ * The invariants are:\n+ *\n+ * - When entering the loop, lo points at a slot that is never\n+ *   above the target (it could be at the target), hi points at a\n+ *   slot that is guaranteed to be above the target (it can never\n+ *   be at the target).\n+ *\n+ * - We find a point 'mi' between lo and hi (mi could be the same\n+ *   as lo, but never can be the same as hi), and check if it hits\n+ *   the target.  There are three cases:\n+ *\n+ *    - if it is a hit, we are happy.\n+ *\n+ *    - if it is strictly higher than the target, we update hi with\n+ *      it.\n+ *\n+ *    - if it is strictly lower than the target, we update lo to be\n+ *      one slot after it, because we allow lo to be at the target.\n+ *\n+ * When choosing 'mi', we do not have to take the \"middle\" but\n+ * anywhere in between lo and hi, as long as lo <= mi < hi is\n+ * satisfied.  When we somehow know that the distance between the\n+ * target and lo is much shorter than the target and hi, we could\n+ * pick mi that is much closer to lo than the midway.\n+ */\n+static int patch_pos(struct patch_id **table, int nr, const unsigned char *id)\n+{\n+\tint hi = nr;\n+\tint lo = 0;\n+\tint mi = 0;\n+\n+\tif (!nr)\n+\t\treturn -1;\n+\n+\tif (nr != 1) {\n+\t\tunsigned lov, hiv, miv, ofs;\n+\n+\t\tfor (ofs = 0; ofs < 18; ofs += 2) {\n+\t\t\tlov = take2(table[0]->patch_id + ofs);\n+\t\t\thiv = take2(table[nr-1]->patch_id + ofs);\n+\t\t\tmiv = take2(id + ofs);\n+\t\t\tif (miv < lov)\n+\t\t\t\treturn -1;\n+\t\t\tif (hiv < miv)\n+\t\t\t\treturn -1 - nr;\n+\t\t\tif (lov != hiv) {\n+\t\t\t\t/*\n+\t\t\t\t * At this point miv could be equal\n+\t\t\t\t * to hiv (but id could still be higher);\n+\t\t\t\t * the invariant of (mi < hi) should be\n+\t\t\t\t * kept.\n+\t\t\t\t */\n+\t\t\t\tmi = (nr-1) * (miv - lov) / (hiv - lov);\n+\t\t\t\tif (lo <= mi && mi < hi)\n+\t\t\t\t\tbreak;\n+\t\t\t\tdie(\"oops\");\n+\t\t\t}\n+\t\t}\n+\t\tif (18 <= ofs)\n+\t\t\tdie(\"cannot happen -- lo and hi are identical\");\n+\t}\n+\n+\tdo {\n+\t\tint cmp;\n+\t\tcmp = hashcmp(table[mi]->patch_id, id);\n+\t\tif (!cmp)\n+\t\t\treturn mi;\n+\t\tif (cmp > 0)\n+\t\t\thi = mi;\n+\t\telse\n+\t\t\tlo = mi + 1;\n+\t\tmi = (hi + lo) / 2;\n+\t} while (lo < hi);\n+\treturn -lo-1;\n+}\n+\n+#define BUCKET_SIZE 190 /* 190 * 21 = 3990, with slop close enough to 4K */\n+struct patch_id_bucket {\n+\tstruct patch_id_bucket *next;\n+\tint nr;\n+\tstruct patch_id bucket[BUCKET_SIZE];\n+};\n+\n+int init_patch_ids(struct patch_ids *ids)\n+{\n+\tmemset(ids, 0, sizeof(*ids));\n+\tdiff_setup(&ids->diffopts);\n+\tids->diffopts.recursive = 1;\n+\tif (diff_setup_done(&ids->diffopts) < 0)\n+\t\treturn error(\"diff_setup_done failed\");\n+\treturn 0;\n+}\n+\n+int free_patch_ids(struct patch_ids *ids)\n+{\n+\tstruct patch_id_bucket *next, *patches;\n+\n+\tfree(ids->table);\n+\tfor (patches = ids->patches; patches; patches = next) {\n+\t\tnext = patches->next;\n+\t\tfree(patches);\n+\t}\n+\treturn 0;\n+}\n+\n+static struct patch_id *add_commit(struct commit *commit,\n+\t\t\t\t   struct patch_ids *ids,\n+\t\t\t\t   int no_add)\n+{\n+\tstruct patch_id_bucket *bucket;\n+\tstruct patch_id *ent;\n+\tunsigned char sha1[20];\n+\tint pos;\n+\n+\tif (commit_patch_id(commit, &ids->diffopts, sha1))\n+\t\treturn NULL;\n+\tpos = patch_pos(ids->table, ids->nr, sha1);\n+\tif (0 <= pos)\n+\t\treturn ids->table[pos];\n+\tif (no_add)\n+\t\treturn NULL;\n+\n+\tpos = -1 - pos;\n+\n+\tbucket = ids->patches;\n+\tif (!bucket || (BUCKET_SIZE <= bucket->nr)) {\n+\t\tbucket = xcalloc(1, sizeof(*bucket));\n+\t\tbucket->next = ids->patches;\n+\t\tids->patches = bucket;\n+\t}\n+\tent = &bucket->bucket[bucket->nr++];\n+\thashcpy(ent->patch_id, sha1);\n+\n+\tif (ids->alloc <= ids->nr) {\n+\t\tids->alloc = alloc_nr(ids->nr);\n+\t\tids->table = xrealloc(ids->table, sizeof(ent) * ids->alloc);\n+\t}\n+\tif (pos < ids->nr)\n+\t\tmemmove(ids->table + pos + 1, ids->table + pos,\n+\t\t\tsizeof(ent) * (ids->nr - pos));\n+\tids->nr++;\n+\tids->table[pos] = ent;\n+\treturn ids->table[pos];\n+}\n+\n+struct patch_id *has_commit_patch_id(struct commit *commit,\n+\t\t\t\t     struct patch_ids *ids)\n+{\n+\treturn add_commit(commit, ids, 1);\n+}\n+\n+struct patch_id *add_commit_patch_id(struct commit *commit,\n+\t\t\t\t     struct patch_ids *ids)\n+{\n+\treturn add_commit(commit, ids, 0);\n+}\ndiff --git a/patch-ids.h b/patch-ids.h\nnew file mode 100644\nindex 0000000..c8c7ca1\n--- /dev/null\n+++ b/patch-ids.h\n@@ -0,0 +1,21 @@\n+#ifndef PATCH_IDS_H\n+#define PATCH_IDS_H\n+\n+struct patch_id {\n+\tunsigned char patch_id[20];\n+\tchar seen;\n+};\n+\n+struct patch_ids {\n+\tstruct diff_options diffopts;\n+\tint nr, alloc;\n+\tstruct patch_id **table;\n+\tstruct patch_id_bucket *patches;\n+};\n+\n+int init_patch_ids(struct patch_ids *);\n+int free_patch_ids(struct patch_ids *);\n+struct patch_id *add_commit_patch_id(struct commit *, struct patch_ids *);\n+struct patch_id *has_commit_patch_id(struct commit *, struct patch_ids *);\n+\n+#endif /* PATCH_IDS_H */\n-- \n1.5.1.777.gd14d3\n"},{"id":"39068","messageId":"7vfy77ua3o.fsf_-_@assigned-by-dhcp.cox.net","threadId":"7509","inReplyTo":"7virc524cd.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 3/4] git-log --cherry-pick A...B","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-04-10T22:40:27Z","receivedAt":"2007-04-10T22:40:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This is meant to be a saner replacement for \"git-cherry\".\n\nWhen used with \"A...B\", this filters out commits whose patch\ntext has the same patch-id as a commit on the other side.  It\nwould probably most useful to use with --left-right.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n revision.c |   88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n revision.h |    1 +\n 2 files changed, 89 insertions(+), 0 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 486393c..e9de865 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -8,6 +8,7 @@\n #include \"revision.h\"\n #include \"grep.h\"\n #include \"reflog-walk.h\"\n+#include \"patch-ids.h\"\n \n static char *path_name(struct name_path *path, const char *name)\n {\n@@ -422,6 +423,86 @@ static void add_parents_to_list(struct rev_info *revs, struct commit *commit, st\n \t}\n }\n \n+static void cherry_pick_list(struct commit_list *list)\n+{\n+\tstruct commit_list *p;\n+\tint left_count = 0, right_count = 0;\n+\tint left_first;\n+\tstruct patch_ids ids;\n+\n+\t/* First count the commits on the left and on the right */\n+\tfor (p = list; p; p = p->next) {\n+\t\tstruct commit *commit = p->item;\n+\t\tunsigned flags = commit->object.flags;\n+\t\tif (flags & BOUNDARY)\n+\t\t\t;\n+\t\telse if (flags & SYMMETRIC_LEFT)\n+\t\t\tleft_count++;\n+\t\telse\n+\t\t\tright_count++;\n+\t}\n+\n+\tleft_first = left_count < right_count;\n+\tinit_patch_ids(&ids);\n+\n+\t/* Compute patch-ids for one side */\n+\tfor (p = list; p; p = p->next) {\n+\t\tstruct commit *commit = p->item;\n+\t\tunsigned flags = commit->object.flags;\n+\n+\t\tif (flags & BOUNDARY)\n+\t\t\tcontinue;\n+\t\t/*\n+\t\t * If we have fewer left, left_first is set and we omit\n+\t\t * commits on the right branch in this loop.  If we have\n+\t\t * fewer right, we skip the left ones.\n+\t\t */\n+\t\tif (left_first != !!(flags & SYMMETRIC_LEFT))\n+\t\t\tcontinue;\n+\t\tcommit->util = add_commit_patch_id(commit, &ids);\n+\t}\n+\n+\t/* Check the other side */\n+\tfor (p = list; p; p = p->next) {\n+\t\tstruct commit *commit = p->item;\n+\t\tstruct patch_id *id;\n+\t\tunsigned flags = commit->object.flags;\n+\n+\t\tif (flags & BOUNDARY)\n+\t\t\tcontinue;\n+\t\t/*\n+\t\t * If we have fewer left, left_first is set and we omit\n+\t\t * commits on the left branch in this loop.\n+\t\t */\n+\t\tif (left_first == !!(flags & SYMMETRIC_LEFT))\n+\t\t\tcontinue;\n+\n+\t\t/*\n+\t\t * Have we seen the same patch id?\n+\t\t */\n+\t\tid = has_commit_patch_id(commit, &ids);\n+\t\tif (!id)\n+\t\t\tcontinue;\n+\t\tid->seen = 1;\n+\t\tcommit->object.flags |= SHOWN;\n+\t}\n+\n+\t/* Now check the original side for seen ones */\n+\tfor (p = list; p; p = p->next) {\n+\t\tstruct commit *commit = p->item;\n+\t\tstruct patch_id *ent;\n+\n+\t\tent = commit->util;\n+\t\tif (!ent)\n+\t\t\tcontinue;\n+\t\tif (ent->seen)\n+\t\t\tcommit->object.flags |= SHOWN;\n+\t\tcommit->util = NULL;\n+\t}\n+\n+\tfree_patch_ids(&ids);\n+}\n+\n static void limit_list(struct rev_info *revs)\n {\n \tstruct commit_list *list = revs->commits;\n@@ -449,6 +530,9 @@ static void limit_list(struct rev_info *revs)\n \t\t\tcontinue;\n \t\tp = &commit_list_insert(commit, p)->next;\n \t}\n+\tif (revs->cherry_pick)\n+\t\tcherry_pick_list(newlist);\n+\n \trevs->commits = newlist;\n }\n \n@@ -913,6 +997,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch\n \t\t\t\trevs->left_right = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (!strcmp(arg, \"--cherry-pick\")) {\n+\t\t\t\trevs->cherry_pick = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (!strcmp(arg, \"--objects\")) {\n \t\t\t\trevs->tag_objects = 1;\n \t\t\t\trevs->tree_objects = 1;\ndiff --git a/revision.h b/revision.h\nindex 55e6b53..b69624a 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -47,6 +47,7 @@ struct rev_info {\n \t\t\tleft_right:1,\n \t\t\tparents:1,\n \t\t\treverse:1,\n+\t\t\tcherry_pick:1,\n \t\t\tfirst_parent_only:1;\n \n \t/* Diff flags */\n-- \n1.5.1.777.gd14d3\n"},{"id":"39069","messageId":"7vabxfua2b.fsf_-_@assigned-by-dhcp.cox.net","threadId":"7509","inReplyTo":"7virc524cd.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 4/4] Documentation: --cherry-pick","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-04-10T22:41:16Z","receivedAt":"2007-04-10T22:41:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Document how to use --cherry-pick, using earlier documentation\nupdate that adds an example to --left-right.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n Documentation/git-rev-list.txt |   15 +++++++++++++++\n 1 files changed, 15 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt\nindex 12b71ed..77e068b 100644\n--- a/Documentation/git-rev-list.txt\n+++ b/Documentation/git-rev-list.txt\n@@ -22,6 +22,7 @@ SYNOPSIS\n \t     [ \\--topo-order ]\n \t     [ \\--parents ]\n \t     [ \\--left-right ]\n+\t     [ \\--cherry-pick ]\n \t     [ \\--encoding[=<encoding>] ]\n \t     [ \\--(author|committer|grep)=<pattern> ]\n \t     [ [\\--objects | \\--objects-edge] [ \\--unpacked ] ]\n@@ -224,6 +225,20 @@ limiting may be applied.\n \tIn addition to the '<commit>' listed on the command\n \tline, read them from the standard input.\n \n+--cherry-pick::\n+\n+\tOmit any commit that introduces the same change as\n+\tanother commit on the \"other side\" when the set of\n+\tcommits are limited with symmetric difference.\n++\n+For example, if you have two branches, `A` and `B`, a usual way\n+to list all commits on only one side of them is with\n+`--left-right`, like the example above in the description of\n+that option.  It however shows the commits that were cherry-picked\n+from the other branch (for example, \"3rd on b\" may be cherry-picked\n+from branch A).  With this option, such pairs of commits are\n+excluded from the output.\n+\n -g, --walk-reflogs::\n \n \tInstead of walking the commit ancestry chain, walk\n-- \n1.5.1.777.gd14d3\n"},{"id":"39112","messageId":"1176291474.11130.12.camel@okra.transitives.com","threadId":"7509","inReplyTo":"Pine.LNX.4.64.0704040744160.6730@woody.linux-foundation.org","subject":"Re: How can I easily verify my diffs are in parent branch?","fromName":"Alex Bennee","fromEmail":"kernel-hacker@bennee.com","sentAt":"2007-04-11T11:37:54Z","receivedAt":"2007-04-11T11:37:54Z","isPatch":false,"sender":{"key":"kernel-hacker@bennee.com","avatar":null},"body":"On Wed, 2007-04-04 at 08:12 -0700, Linus Torvalds wrote:\n> \n> On Wed, 4 Apr 2007, Alex Bennee wrote:\n> > \n> > This is not the case of looking through the logs for my commit as I'm\n> > exporting my changes from my tree into the company system through CVS.\n> > This means all the usual commit tracking benefits are lost.\n> \n> Yeah, sad.\n> <snip>\n> So all your small diffs get smushed in as part of one *big* change? Or do \n> they still exist in the baseline CVS tree as individual commits?\n\nUnfortunately they are all smushed together :-(\n\n<snip>\n\n> For example, one thing you can do, if the number of commits you have is \n> fairly small, is to just be on your \"my-branch\" and then do\n> \n> \tgit rebase [--merge] cvs-upstream\n\nYeah I've tried using the rebase approach (which I in fact use a lot\nwhen re-baseing my work anyway without losing my micro commit history).\nThe one fly in the ointment is the branch result at the end contains no\nchanges so I have no historical record of what I did while creating the\nchange.\n\nI assume the commit objects are still in git somewhere but I'm not sure\nhow to get at it. What I would like to ask git is \"what did my git-log\nlook like when 'mybranch' was based off master at A instead of B after O\nrebased?\"\n\nFor the time being I tend to verify my work has got in by generating a\nmaster..branch diff and loading it into emacs patch-mode and testing\neach hunk has applied ok. I'm still deciding if I should bite the bullet\nand write some more elisp to make it a one button operation or use a bit\nof perl with git. \n\n\n-- \nAlex, homepage: http://www.bennee.com/~alex/\n\"The most difficult thing in the world is to know how to do a thing and\nto watch someone else do it wrong without comment.\" -- Theodore H. White\n"},{"id":"39121","messageId":"Pine.LNX.4.64.0704110822530.6730@woody.linux-foundation.org","threadId":"7509","inReplyTo":"1176291474.11130.12.camel@okra.transitives.com","subject":"Re: How can I easily verify my diffs are in parent branch?","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-04-11T16:00:10Z","receivedAt":"2007-04-11T16:00:10Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 11 Apr 2007, Alex Bennee wrote:\n\n> On Wed, 2007-04-04 at 08:12 -0700, Linus Torvalds wrote:\n> > \n> > Yeah, sad.\n> > <snip>\n> > So all your small diffs get smushed in as part of one *big* change? Or do \n> > they still exist in the baseline CVS tree as individual commits?\n> \n> Unfortunately they are all smushed together :-(\n\nOk, that just sucks.\n\n> > For example, one thing you can do, if the number of commits you have is \n> > fairly small, is to just be on your \"my-branch\" and then do\n> > \n> > \tgit rebase [--merge] cvs-upstream\n> \n> Yeah I've tried using the rebase approach (which I in fact use a lot\n> when re-baseing my work anyway without losing my micro commit history).\n> The one fly in the ointment is the branch result at the end contains no\n> changes so I have no historical record of what I did while creating the\n> change.\n\nOk, that's what \"rebase\" is *meant* to do. If the upstream already \ncontains the patch (which especially with \"--merge\" means that it just \ncleanly did a 3-way merge - whether upstream was one big smushed- \ntogether thing or actually contained that patch explicitly doesn't \nmatter), then \"rebase\" just skips that patch, since it's not \"necessary\" \nany more.\n\nIf you actually want to keep your own cleaner history, you should really \ndo a \"git merge\", not a rebase. That's kind of the fundamental difference \nbetween rebasing and merging: rebasing throws away the old history (and \ncreates totally new commits to keep the stuff that wasn't there), while \n\"merge\" creates a *superset* of the two histories.\n\n> I assume the commit objects are still in git somewhere but I'm not sure\n> how to get at it. What I would like to ask git is \"what did my git-log\n> look like when 'mybranch' was based off master at A instead of B after O\n> rebased?\"\n\nWhat you *can* do, is to do \"rebase *and* merge\" kind of operation, if \nyou really want to.\n\nYour history will look a bit odd, and you'll have unmerged commits always \nshow up twice (or as many times as you do this operation, in fact - if you \nkeep on doing it, and they don't get merged up-stream, they'll always be \nre-done over and over again). But you'll have *both* the rebase result \n*and* the merge result.\n\nThe way to do that would be to basically be something like this by having \n*three* branches rather than two: your CVS import branch (call it \"cvs\"), \nyour \"merged work\" branch (call it \"cvs-merged\") and the branch you \nactually do development on (call it \"master\")\n\n - update the \"CVS tracking branch\"\n\n\t# something like this.. \n\tgit cvsimport -i -m -o cvs\n\n - switch to the \"cvs-merged\" branch, and merge your old changes *and* the \n   new CVS state into it, but merge it as the CVS state *only*:\n\n\t# reset the \"cvs-merge\" branch to the new \"cvs\" state\n\tgit branch -f cvs-merge cvs\n\n\t# switch to it\n\tgit checkout cvs\n\n\t# and merge your old \"master\" into it but only merge the history, \n\t# not the actual contents (ie using the \"ours\" strategy)\n\tgit merge -s ours master\n\n - now, go back to your development branch, and rebase the work you have \n   there into the cvs-merge branch that works as the \"history branch\"\n\n\t# switch back to the development branch (which doesn't have the merge)\n\tgit checkout master\n\n\t# Now, rebase the stuff that was *not* int he original \"cvs\" \n\t# branch, but is in your development branch, and put it on top of \n\t# the merge you just did.\n\tgit rebase --merge --onto cvs-merge cvs\n\nwhich *should* mean that in the end you have your \"master\" branch that \ncontains your old history *and* the CVS history merged, *and* on top of \nthat merge it also has the patches that you had in your old history that \nweren't in the CVS tree.\n\n(The above is just a rough idea - I'm not actually guaranteeing that it \nworks, I didn't test it, I just wrote it up as an example. That last \n\"rebase\" in particular might need some work to make sure that it only \nrebases your new stuff since the last cvs-merge: I think it will do that \nas-is, but I've always found the \"git rebase\" command to have very \nnon-intuitive semantics because it doesn't use the normal \"range\" \noperations to describe what to actually rebase).\n\nYou get the idea. Once you get that working, you can just script it. The \nwhole point is that you can use a merge *and* a rebase to both save away \nyour old history, *and* to then re-do the commits that weren't merged on \ntop of it.\n\nSo you *can* keep both the history *and* rewrite it, but it will require \nyou to do more work, and you'd have to experiment a bit (as mentioned, I \nreally don't think that \"git rebase\" example I gave above necessaly works \nas-is - you might have to tweak it a bit to make sure that it rebases \nexactly the commits that have been done since the last cvs merge: it might \ninvolve using \"git-merge-base\" to figure out what the last merge was \nbefore you do the \"git merge -s ours\" thing etc etc)\n\n\t\tLinus\n"},{"id":"39330","messageId":"Pine.LNX.4.64.0704141028220.18846@racer.site","threadId":"7509","inReplyTo":"7vmz1fua4r.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 2/4] Refactor patch-id filtering out of git-cherry and git-format-patch.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-04-14T08:57:32Z","receivedAt":"2007-04-14T08:57:32Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 10 Apr 2007, Junio C Hamano wrote:\n\n> This implements the patch-id computation and recording library,\n> patch-ids.c, and rewrites the get_patch_ids() function used in\n> cherry and format-patch to use it, so that they do not pollute\n> the object namespace.  Earlier code threw non-objects into the\n> in-core object database, and hoped for not getting bitten by\n> SHA-1 collisions.  While it may be practically Ok, it still was\n> an ugly hack.\n\nNo, it was not an ugly hack. Instead, it served as a reminder that we \n_rely_ on different content having different SHA-1 ids.\n\nHaving said that, I do not particularly mind patch-ids being stored \nelsewhere, if only as a clean up. However, I would have liked your patch \nso much more if you had done the only sane thing: resurrect object-hash.c.\n\nNow, we have yet another data structure which is unnecessarily bound to a \ncertain use, and the next person wanting a new hashmap needs to add _yet_ \nanother data structure.\n\nCiao,\nDscho\n"}]}