{"thread":{"id":"43358","subject":"Re: What's the meaning of `parenthood' in git commits?","startedAt":"2006-11-08T00:39:00Z","lastAt":"2006-11-08T03:04:51Z","messageCount":7,"participants":["Nix","Linus Torvalds","Junio C Hamano","Jakub Narebski"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"295315","messageId":"878ximbwm3.fsf@hades.wkstn.nix","threadId":"43358","inReplyTo":null,"subject":"What's the meaning of `parenthood' in git commits?","fromName":"Nix","fromEmail":"nix@esperi.org.uk","sentAt":"2006-11-08T00:39:00Z","receivedAt":"2006-11-08T00:39:00Z","isPatch":false,"sender":{"key":"nix@esperi.org.uk","avatar":"https://avatars.githubusercontent.com/u/6503005?v=4"},"body":"So I'm back on the weird porcelain I mentioned months and months ago,\nthe one which treats source trees as named collections of patches merged\ntogether in different ways, almost like stgit on steroids, only not.\n\nIt occurred to me recently that packed refs provide about 50% of what I\nneed (efficient handling of lots and lots of refs); most of the other\n50% consits of a new extremely weird git merge strategy,\n`git-merge-patched', which merges branches A and B by finding the most\nrecent merge-base between branch B and any branch listed in\n.git/refs/trunks (`trunks' being a directory holding heads which are\ntreated this way by this weird merge strategy; the porcelain will have\nto keep it up to date, which shouldn't be too terribly hard), and\npatch(1)ing the diff between that base and the tip of branch B into\nbranch A. (A patch rejection, of course, means merge-by-hand and commit,\nas usual with merge conflicts.)\n\nThe idea being that if you have a tree like this:\n\n     B\n------------- ref trunks/latest\n     \\\n      ------ ref heads/some-change-foo\n\n ... -------- ref trunks/old-and-grotty\n\n\nthen this merge strategy, when asked to merge heads/some-change-foo into\ntrunks/old-and-grotty would spot that point B was the most recent\nmerge point into anything in trunks/, generate a diff between point B\nand heads/some-change-foo, and patch it into trunks/old-and-grotty.\n\n(I *know* this is really weird, but I've got a choice of doing this or\ncontinuing to use SCCS with the world's most horrible shell script\nwrapper as the source code repository for ~5Gb of source, with tens of\nthousands of files in a flat directory structure, expanded to 50Gb\nbecause we're storing binary files in there by the astonishingly\ninefficient means of uuencoding them and sccsing the result: you may be\nsick now. I know which I'd prefer. I may be distorting git into\nsomething unrecognisable to its own father but it's that or I go insane\n*and* run out of disk space.)\n\n\nAfter all that setup, my question's simple. Does a `parent' in git\nterminology simply mean `this commit was derived in some way from the\ncommit listed here'? If so, I suppose I can list heads/some-change-foo\nas one parent on these merge commits, even though the `merging'\nmechanism is so odd that I expect to be pelted with rotten vegetables as\nsoon as I post this.\n\nBut it's that or SCCS.\n\n(Of course this will go into a public git repository for people to laugh\nat. I don't expect anyone to actually *use* it.)\n\n-- \nRich industrial heritage: lifeless wasteland. `The land\n"},{"id":"296267","messageId":"eir9nq$cm8$1@sea.gmane.org","threadId":"43358","inReplyTo":"878ximbwm3.fsf@hades.wkstn.nix","subject":"Re: What's the meaning of `parenthood' in git commits?","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-11-08T00:52:56Z","receivedAt":"2006-11-08T00:52:56Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Nix wrote:\n\n> After all that setup, my question's simple. Does a `parent' in git\n> terminology simply mean `this commit was derived in some way from the\n> commit listed here'? If so, I suppose I can list heads/some-change-foo\n> as one parent on these merge commits, even though the `merging'\n> mechanism is so odd that I expect to be pelted with rotten vegetables as\n> soon as I post this.\n\nYes, being parent means that this commit was derived in some way from the\ncommit listed here. It needs not to be this commit is the result of merge\nof commits listed here... there was a discussion some time ago to use one\nof parents (first for example) instead of special header for \"prev\" link to\nprevious value of the ref (which discussion was obsoleted by reflog).\n\nIt provies two things you have to think about if to use 'parenthood' for\nsomething a bit unexpected. First, parents are connectivity, so even if you\ndelete trunks/some-name and then prune, averything that was merged in some\nbranch or tag which lives still wouldn't get pruned. Second, the\ninformation about merges is used in merge strategies: consider if having\nthis information would help your strange merge strategy.\n\nAnd of course there is a question if the graph as visualized by for example\ngitk would have more sense or not with the \"strange merges\" marked as\nmerges. \n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n\n"},{"id":"295551","messageId":"Pine.LNX.4.64.0611071644430.3667@g5.osdl.org","threadId":"43358","inReplyTo":"878ximbwm3.fsf@hades.wkstn.nix","subject":"Re: What's the meaning of `parenthood' in git commits?","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-11-08T00:58:43Z","receivedAt":"2006-11-08T00:58:43Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 8 Nov 2006, Nix wrote:\n> \n> [ Nix explains what he's doing now with SCCS ]: you may be\n> sick now.\n\nWow. You've got some strange setup there, Nix.\n\n> After all that setup, my question's simple. Does a `parent' in git\n> terminology simply mean `this commit was derived in some way from the\n> commit listed here'?\n\nWell, strictly speaking, git doesn't itself assign much any real meaning \nto \"parent\" at all. It has the obvious meanings:\n\n - the parent pointers act as reachability graph edges (so fsck cares \n   about it a lot, of course)\n\n - listing the \"log\" of a commit will show everything reachable from that \n   commit and it's parents, of course (with the commit date-stamp being \n   used as a \"ordering\" when having multiple choices of commits to show)\n\n - it has the obvious meanings for the \"revision arithmetic\", ie revision \n   name parsing (ie \"commit~3^2\")\n\n - parenthood will be used to show the diff (\"git show\", \"git log -p\" and \n   friends)\n\n - the \"merge-base\" algorithms obviously use it to find the most recent \n   common ancestor, and that in turn impacts the normal merge strategies, \n   of course.\n\nso parenthood does obviously have a number of very specific technical \nmeanings for different programs, but at the same time, no, git doesn't \nreally \"care\". You can happily generate your own parenthood if you want \nto, and git will just continue to follow the above rules.\n\n> If so, I suppose I can list heads/some-change-foo as one parent on these \n> merge commits, even though the `merging' mechanism is so odd that I \n> expect to be pelted with rotten vegetables as soon as I post this.\n\nYeah, git won't care. If you screw up parenthood, you have a few problems:\n\n - the diffs may look really strange. In particular, if you list multiple \n   parents, the git \"diff\" functions will all just assume that it's a \n   merge, and a \"git show\" will start showing the combined diff (which is \n   usually empty).\n\n   So if you end up having multiple parents, not because it was \"really\" a \n   merge, but because you use the other parent pointer to point to some \n   \"source\" for the patch, things like \"git log -p\" won't give nice output \n   any more. You need to manually ask for the diff with something like\n\n\t# show diff from second parent\n\tgit diff commit^2..commit\n\n   instead.\n\n - listing too _few_ parents is potentially more serious, if you have \n   reachability issues (ie you wanted to keep the other source around, but \n   since you didn't list it as a parent, git won't know that it had \n   anything to do with your commit, so it may be pruned away unless you \n   have some other way to reach it)\n\nbut if you just have a really strange merge algorithm, and the _data_ \nassociated with the parents is \"surprising\" from the standpoint of the \ndefault merge, git really won't care at all.\n\nYour usage does sound a bit strange.\n\n"},{"id":"295569","messageId":"7vy7qmyc46.fsf@assigned-by-dhcp.cox.net","threadId":"43358","inReplyTo":"878ximbwm3.fsf@hades.wkstn.nix","subject":"Re: What's the meaning of `parenthood' in git commits?","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-11-08T01:13:13Z","receivedAt":"2006-11-08T01:13:13Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nix <nix@esperi.org.uk> writes:\n\n> The idea being that if you have a tree like this:\n>\n>      B\n> ------------- ref trunks/latest\n>      \\\n>       ------ ref heads/some-change-foo\n>\n>  ... -------- ref trunks/old-and-grotty\n>\n>\n> then this merge strategy, when asked to merge heads/some-change-foo into\n> trunks/old-and-grotty would spot that point B was the most recent\n> merge point into anything in trunks/, generate a diff between point B\n> and heads/some-change-foo, and patch it into trunks/old-and-grotty.\n\nThis is a standard \"cherry-picking\" practice.\n\n> After all that setup, my question's simple. Does a `parent' in git\n> terminology simply mean `this commit was derived in some way from the\n> commit listed here'?\n\nWhen you think about commit ancestry, think of it this way:\n\n   These commits I list as its parents of this new commit, and\n   everything that leads to them, are what I considered when\n   derived this commit.  This new child commit of them suits the\n   purpose of _my_ branch better than any of these parent\n   commits I took into consideration because of such and such\n   reasons that I stated in its commit log message.\n\nIf you mark the resulting commit on old-and-grotty to have\nsome-change-foo as one of its parents, because some-change-foo\nhas almost everything 'latest' has (up to point B), you are also\nsaying \"I have considered everything that happened between\nold-and-grotty and B when making this commit\".\n\nWhat's implied by that statement is this, even though you do not\nexplicitly say:\n\n   I reject everything that happened on the development line\n   that led to 'latest' up to point B since old-and-grotty was\n   forked.\n\nThis is not necessarily a bad thing, by the way.  For somebody\nwho is trying to maintain extremely-stable branch by cherry\npicking only changes in a few narrow areas from the mainline\nwould _want_ to leave most of the \"new good stuff\" out from his\nbranch.  That's why I emphasized _my_ a few paragraphs above.\n\nBut it is _so_ different from the mindset of usual \"every branch\nmakes progress _forward_ perhaps with different pace\".  In this\nexample, this branch is actively choosing to stay behind and\nrefusing to take changes from the 'latest'.  So your users need\nto really understand what they are doing.  For example, if there\nis another topic forked off of B (or at a later commit from\nthere that leads to 'latest'), after your \"funny merge\" took\nplace, even the usual merge strategies would work as expected by\nyou --- it would still ignore the changes up to B because you\ntold git to do so.\n\nAlso, if you make a good change on top of the resulting merge\nthat _should_ be applicable to some-change-foo which is based on\nthe 'latest', you cannot merge that back in the usual way.\nUsual git merge will find your first \"funny merge\" as the merge\nbase, and because it chooses to reject everything leading to B,\nthe merge result would look very similar to the set of changes\nbased on old-and-grotty.  Actually, that would even fast forward\nto the version you made into a phony \"merge\" out of the\ncherry-picked result.\n\nBut that is at least consistent with the statement you made when\nyou created that commit.  Staying behind at old-and-grotty\nsuited _your_ branch'es purpose better than being based on\n'latest'.  And a person who is merging _your_ branch into\nsome-change-foo, by choosing to merge that branch into the\nlatter, is choosing to share your branch'es purpose, so it is\nnatural a lot of the \"good things\" that happened up to B is\nrewound by that merge.\n\nSo I think as long as you and your users understand what is\ngoing on, I do not see a problem at either the mechanical level\nor the philosophical level.  But I am sure it would confuse a\nlot of people, so please do not come back complaining that you\nended up getting your users heads explode ;-).\n\n\n"},{"id":"294991","messageId":"874ptabubr.fsf@hades.wkstn.nix","threadId":"43358","inReplyTo":"Pine.LNX.4.64.0611071644430.3667@g5.osdl.org","subject":"Re: What's the meaning of `parenthood' in git commits?","fromName":"Nix","fromEmail":"nix@esperi.org.uk","sentAt":"2006-11-08T01:28:24Z","receivedAt":"2006-11-08T01:28:24Z","isPatch":false,"sender":{"key":"nix@esperi.org.uk","avatar":"https://avatars.githubusercontent.com/u/6503005?v=4"},"body":"On 8 Nov 2006, Linus Torvalds uttered the following:\n> On Wed, 8 Nov 2006, Nix wrote:\n>> \n>> [ Nix explains what he's doing now with SCCS ]: you may be\n>> sick now.\n>\n> Wow. You've got some strange setup there, Nix.\n\nIt's what happens when a version-control system gets implemented as\nan emergency hack when moving from VMS, by people who don't really\ngrok Unix shell scripting... and then you let fifteen years pass,\nand nobody dares touch the hack because it's so damned delicate.\nIt took months of agony to implement crude half-functional branching\nin this. Writing a git porcelain should be vastly simpler, even with\nthe overhead of a conversion tool as well.\n\nWriting that conversion tool will be fun :( e.g. I'm going to have to\nidentify branches by diffing/xdeltaing each version of a file with every\nsingle previous version of that file, and if the diff is smallest\nagainst a version other than the immediate ancestor, it's assumed to be\na branch against that version. (I'm going to have to fake up packed refs\nfor these tiny branches so that they're at least accessible in\nemergencies, gah.)\n\nIt's all, well, nasty. But all will be so much happier in the shining\nworld of git.\n\n>> After all that setup, my question's simple. Does a `parent' in git\n>> terminology simply mean `this commit was derived in some way from the\n>> commit listed here'?\n>\n> Well, strictly speaking, git doesn't itself assign much any real meaning \n> to \"parent\" at all. It has the obvious meanings:\n\nOh *good*, that's what I thought.\n\n[snip more things which match my understanding]\n\n>  - parenthood will be used to show the diff (\"git show\", \"git log -p\" and \n>    friends)\n\nI'll list the patch-merged parent as the second parent, so that you'll only\nget the mostly-useless huge diff from that if you actually ask for it, and\nwill get a more useful result with ^.\n\n>  - the \"merge-base\" algorithms obviously use it to find the most recent \n>    common ancestor, and that in turn impacts the normal merge strategies, \n>    of course.\n\nHm, yeah, if merging iterates down patch-merged branches it might have\ninteresting consequences, because the trees on one side of patch- merges\nare likely to be very different to trees on the other side (years of\ndevelopment separate them). I'd like a way to specify that those parents\nare *not* to be traversed by the merge-base algorithms, really.\n\nA series of\n\nnot-merge-base: <sha1 id>\n\nheaders, perhaps? (I think that's likely to involve much less code churn\nthan introducing a new `not-merge-base-parent' tag).\n\n> Yeah, git won't care. If you screw up parenthood, you have a few problems:\n>\n>  - the diffs may look really strange. In particular, if you list multiple \n>    parents, the git \"diff\" functions will all just assume that it's a \n>    merge, and a \"git show\" will start showing the combined diff (which is \n>    usually empty).\n\nIt is a merge, so that's right. It's just a rather odd merge.\n\n(I don't envisage actual *changes* being made in these commits except to\nresolve conflicts.)\n\n>    So if you end up having multiple parents, not because it was \"really\" a \n>    merge, but because you use the other parent pointer to point to some \n>    \"source\" for the patch, things like \"git log -p\" won't give nice output \n>    any more. You need to manually ask for the diff with something like\n\nWell, I was envisaging that the other parent pointer would point to the\ntip of the changes tree. Going back to that graph again:\n\n     B\n------------- ref trunks/latest\n     \\\n      ------ ref heads/some-change-foo\n\n ... -------- ref trunks/old-and-grotty\n\nThe idea is that the patch-merge of trunks/old-and-grotty and\nheads/some-change-foo would consist textually of the diff between B and\nheads/some-change-foo, applied to trunks/old-and-grotty, and would list\nas its parents trunks/old-and-grotty, *and heads/some-change-foo*.\n\n(Perhaps this isn't really a merge after all? Should merge parents be\ntreated as differently as this? It'll all be covered over by the\nporcelain in any case: it won't be possible to confuse a trunk/ with a\nnormal head and accidentally patch-merge in the wrong direction.)\n\n>  - listing too _few_ parents is potentially more serious, if you have \n>    reachability issues (ie you wanted to keep the other source around, but \n>    since you didn't list it as a parent, git won't know that it had \n>    anything to do with your commit, so it may be pruned away unless you \n>    have some other way to reach it)\n\nYeah, that would be bad.\n\n> but if you just have a really strange merge algorithm, and the _data_ \n> associated with the parents is \"surprising\" from the standpoint of the \n> default merge, git really won't care at all.\n\nGood.\n\n> Your usage does sound a bit strange.\n\nAgreed. But there are hundreds of people banging on my door asking for a\nproper version control system, quilt isn't a proper version control\nsystem in that sense, and stgit has... issues when you try to distribute\nit and when you have a lot of people working on one tree at once: plus\nit doesn't fit our weird workflow with multiple parallel release\nbranches, at least one active development trunk, and all changes done\nunder a carefully-controlled bug tracking system (it's as if *every*\nchange has a bugzilla ticket associated, *always*, and we expect to be\nable to get from ticket to change efficiently).\n\n(We do both distribution and working-copy-sharing: the trees are too\nlarge to have one tree per person, not least because each tree requires\nan entire Oracle instance of its own to play with and massive amounts of\nmemory; and we have geographically distributed sites with trees of their\nown.)\n\n-- \nRich industrial heritage: lifeless wasteland. `The land\n"},{"id":"294482","messageId":"87zmb2afe8.fsf@hades.wkstn.nix","threadId":"43358","inReplyTo":"7vy7qmyc46.fsf@assigned-by-dhcp.cox.net","subject":"Re: What's the meaning of `parenthood' in git commits?","fromName":"Nix","fromEmail":"nix@esperi.org.uk","sentAt":"2006-11-08T01:36:15Z","receivedAt":"2006-11-08T01:36:15Z","isPatch":false,"sender":{"key":"nix@esperi.org.uk","avatar":"https://avatars.githubusercontent.com/u/6503005?v=4"},"body":"On 8 Nov 2006, Junio C. Hamano spake thusly:\n> Nix <nix@esperi.org.uk> writes:\n>\n>> The idea being that if you have a tree like this:\n>>\n>>      B\n>> ------------- ref trunks/latest\n>>      \\\n>>       ------ ref heads/some-change-foo\n>>\n>>  ... -------- ref trunks/old-and-grotty\n>>\n>>\n>> then this merge strategy, when asked to merge heads/some-change-foo into\n>> trunks/old-and-grotty would spot that point B was the most recent\n>> merge point into anything in trunks/, generate a diff between point B\n>> and heads/some-change-foo, and patch it into trunks/old-and-grotty.\n>\n> This is a standard \"cherry-picking\" practice.\n\nYes, pretty much, except that we do *everything* by cherry-picking, and\nwe want to track the cherry-picks in the same way that all other changes\nare tracked (i.e., a small branch for each (numbered) change, patching\nmadly in all directions into a variety of trunks and release branches,\nwith all those patches tracked.)\n\n>    These commits I list as its parents of this new commit, and\n>    everything that leads to them, are what I considered when\n>    derived this commit.  This new child commit of them suits the\n>    purpose of _my_ branch better than any of these parent\n>    commits I took into consideration because of such and such\n>    reasons that I stated in its commit log message.\n>\n> If you mark the resulting commit on old-and-grotty to have\n> some-change-foo as one of its parents, because some-change-foo\n> has almost everything 'latest' has (up to point B), you are also\n> saying \"I have considered everything that happened between\n> old-and-grotty and B when making this commit\".\n\nYeah. This is the merge-base tracking that Linus mentioned, and it's not\nquite what I'm looking for :/ it's a sort of step-parent, really...\n\n> What's implied by that statement is this, even though you do not\n> explicitly say:\n>\n>    I reject everything that happened on the development line\n>    that led to 'latest' up to point B since old-and-grotty was\n>    forked.\n\n(which is not necessarily true: we might want to backport an earlier\nchange, also on another `small change branch', later on. Stuff on the\ntrunks themselves will never want to get backported, but if the\nmerge-base algorithm traverses patch-merge parent links, it might\nconsider that a `small change branch' has been merged when it actually\nhasn't.)\n\n> This is not necessarily a bad thing, by the way.  For somebody\n> who is trying to maintain extremely-stable branch by cherry\n> picking only changes in a few narrow areas from the mainline\n> would _want_ to leave most of the \"new good stuff\" out from his\n> branch.  That's why I emphasized _my_ a few paragraphs above.\n\nThat's exactly what we're doing, across-the-board.\n\n> But it is _so_ different from the mindset of usual \"every branch\n> makes progress _forward_ perhaps with different pace\".  In this\n> example, this branch is actively choosing to stay behind and\n> refusing to take changes from the 'latest'.  So your users need\n> to really understand what they are doing.\n\n*hahahaaaaa*... hang on, that *was* a joke, right? ;)\n\n> So I think as long as you and your users understand what is\n> going on, I do not see a problem at either the mechanical level\n> or the philosophical level.  But I am sure it would confuse a\n> lot of people, so please do not come back complaining that you\n> ended up getting your users heads explode ;-).\n\nOK, I think I need to find a way to notate in the patch-merged commit\nthat one or more parents should be disregarded when searching for merge\nbases (and *only* when searching for merge bases). I think that will\ndo what's wanted in all areas: i.e., it'll act like a cherry-pick\nthat shows up in the logs/revlist and so on, but doesn't affect the\nsemantics of later merges of stuff from anywhere except for the\nsame limited branch.\n\n(obviously trying to patch-merge B to A twice is always going to\nfail, whether or not merge-base traversal jumps into B: I don't\nthink there's any real need to protect against that.)\n\n-- \nRich industrial heritage: lifeless wasteland. `The land\n"},{"id":"296057","messageId":"87velqabak.fsf@hades.wkstn.nix","threadId":"43358","inReplyTo":"874ptabubr.fsf@hades.wkstn.nix","subject":"Re: What's the meaning of `parenthood' in git commits?","fromName":"Nix","fromEmail":"nix@esperi.org.uk","sentAt":"2006-11-08T03:04:51Z","receivedAt":"2006-11-08T03:04:51Z","isPatch":false,"sender":{"key":"nix@esperi.org.uk","avatar":"https://avatars.githubusercontent.com/u/6503005?v=4"},"body":"On 8 Nov 2006, nix@esperi.org.uk spake thusly:\n> On 8 Nov 2006, Linus Torvalds uttered the following:\n>>  - the \"merge-base\" algorithms obviously use it to find the most recent \n>>    common ancestor, and that in turn impacts the normal merge strategies, \n>>    of course.\n>\n> Hm, yeah, if merging iterates down patch-merged branches it might have\n> interesting consequences, because the trees on one side of patch- merges\n> are likely to be very different to trees on the other side (years of\n> development separate them). I'd like a way to specify that those parents\n> are *not* to be traversed by the merge-base algorithms, really.\n>\n> A series of\n>\n> not-merge-base: <sha1 id>\n>\n> headers, perhaps? (I think that's likely to involve much less code churn\n> than introducing a new `not-merge-base-parent' tag).\n\nWrong. Sort of.\n\nWhen doing normal merges you don't want to consider patch-merged parents\nas real merges: but there is one situation when you *do* want merge-base\nchecking to traverse such links.\n\nSay you have the tree just described:\n\n     B\n------------- ref trunks/latest\n     \\\n      ------ ref heads/some-change-foo\n\n ... -------- ref trunks/old-and-grotty\n\nand you want to patch-merge heads/some-change-foo with\ntrunks/old-and-grotty.\n\nIt doesn't quite apply, so you end up with a conflict-resolution. This\nwill normally be in the merge commit, but there's no guarantee of that:\nperhaps you knew the source tree would conflict in advance and fixed it\nup so that it wouldn't, leaving the old heads/some-change-foo pointing\nbefore that fixup:\n\n     B\n------------- ref trunks/latest\n     \\\n      ------- ref heads/some-change-foo\n          D \\\n            c\n            |\n ... -------------- ref trunks/old-and-grotty\n\nLater on, you find a bug in that change. It's still the same conceptual\nchange, so you fix it, and you want to patch-merge the fix across:\n\n     B\n------------- ref trunks/latest\n     \\\n      -----------\\ ref heads/some-change-foo\n          C \\    .\n            c    . (link under construction)\n            |    .\n ... -------------- ref trunks/old-and-grotty\n            E    F\n\nWhat patch-merge must do in order to produce a diff-merge at point F is\ntherefore rather more involved than I'd hoped:\n\n - determine B as above (most recent merge-base of heads/some-change-foo\n   with anything in trunks/).\n\n - determine the merge-base of trunks/old-and-grotty with\n   heads/some-change-foo, *traversing patch-merge parents*. Call this\n   base C. (This is the only circumstance in which merge-base\n   determination should traverse patch-merged parents.)\n\n - Iff that base C is topologically a child of B, then we have already\n   merged part of this change in the past. In that case, instead of the\n   merge consisting of the diff between B and F, it consists of the diff\n   between C and the head, minus the set of changes c. So it remains to\n   determine c.\n\n - scan backwards along F with git-rev-list, searching specifically for\n   the most recent patch-merge naming any commit which has C as a\n   transitive parent: that is point E. (Such a point must exist as long\n   as only patch-merges have been used to merge heads/some-change-foo\n   with trunks/old-and-grotty: if other sorts of merge have been used,\n   all bets are off and I think we can legitimately fail the merge.)\n   (This requires the ability to distinguish patch-merges from normal\n   merges, but that's easy if we have any tag at all to distinguish\n   them, which we must for merge- base traversal to avoid such parents\n   normally.)\n\n - Reverse out the diff between C and E (if the two are not the same\n   commit) and remember it temporarily as c.\n\n - Apply the forwards diff between point C and heads/some-change-foo,\n   and then apply c in the forwards direction (if c is already present,\n   this is not an error: it just means that whatever conflict-\n   resolution was necessary as a one-off was later needed on the change\n   trunk).\n\nI think that should cope with just about everything. I've tried to mock\nup all sorts of contrived trees and I can't find anything that doesn't\nreduce to that case or a simplification of it. (And no, this case is not\ncontrived: we test on trunks, so we deal with it whenever anything fails\ntesting and has to be fixed...)\n\n(Now all I have to do is write it... enough words, time for action.\nActually time for sleep, it's three in the morning here. Action\ntomorrow.)\n\n-- \nRich industrial heritage: lifeless wasteland. `The land\n"}]}