[PATCH 7 of 8 V2] revset: add origins() predicate

Matt Mackall mpm at selenic.com
Wed Jun 27 16:08:10 CDT 2012


On Sat, 2012-06-23 at 01:21 -0400, Matt Harbison wrote:
> > In no case should we have a "foo" and "foos" with opposite semantics.
> > That's just way too confusing to live. And if we have origin and
> > destination that aren't opposites, we've also got a problem. At least
> > for simple grafts, I'd expect origin(destination(x)) = x and
> > destination(origin(y)) = y.
> 
> Makes sense, though I don't think we can do that for graft specifically, 
> unless "simple graft" means that graft is only done once on a node, and 
> never on the node that gets created because of that graft.

Exactly.

> Graft just has a dangling "REVISION...", but the help text references 
> "source changesets".  A very quick scan of the source tree revealed a 
> few "...copy source..." messages in verify.py that seem file related, 
> and the choose local/remote prompt for subrepos.

No strong opinion, but see below..

> I think 'source' is a more natural complement to destination, and can 
> dig further to find other uses if it seems like a useful rename.
> 
> 
> Since this probably hasn't been on anybody's radar recently, and there 
> were surprises as I implemented this, let me restate/modify the goals 
> here, both for me and to get more opinions.  Apologies in advance, 
> because this is a bit long.
> 
> I think it is useful to be able to answer:
> 
> A) Where did cset X come from?
> B) Where did X go?
> C) What duplicates of X exist? (I don't know if duplicates is an 
> acceptable synonym for copies[2])
> D) Is this the very original X? (I don't think that the opposite, "the 
> very latest X" is meaningful or even possible, since the original date 
> is preserved for each)
> 
> 
> For the sake of this, let there be a cset X, which when copied creates 
> cset Y, which when itself is copied creates Z.  Copy can be any of the 
> three ops.  And let's use "origin(x)" means "the origin of x".  The 
> possible results follow, with alternate 2 being the naive/simplest result:
> 
> A) Where did X come from?
> 
>     Op      Preferred       Alternate 1    Alternate 2
> origin(X)    {}               same           same
> origin(Y)    {X}              same           same
> origin(Z)    {X, Y} *         {X}             {Y}

I tend to prefer {X} here.

> * I prefer this because I think it is useful to see the entire list of 
> the preceding hops Z took, without manually taking the result of one and 
> requerying until the trail ends.  Thg uses a hyperlink to work backward 
> one hop at a time, which is much easier when trying to follow the copies 
> than reentering a command with a new rev each time.
>    The downside is if Y and Z were created by grafting, the only choice 
> is {X} because of how graft is implemented (it preserves the 'source' 
> field if it already exists).  A second pass could be made to include all 
> revs with a 'source' field that matches Z's 'source' (i.e. X for a 
> graft), but it would have to be a special case for graft alone so as to 
> not follow an unrelated path that was _transplanted_ from X.
> 
> 
> B) Where did X go?
> 
>     Op             Preferred    Alternative 1     Alternative 2
> destination(X)     {Y, Z} *        {Z}               {Y}
> destination(Y)     {Z} **          same              same
> destination(Z)     {}              same              same

But not sure about this. If I have branches dev, 1.0 and 2.0, and I
graft dev->2.0->1.0, then ask about destinations of dev cset, I want to
hear about both.

> * I prefer this because it is symmetric with the preferred definition of 
> origin (i.e. showing all the hops), and it is the only result possible 
> if the copy operations were grafts (since Y and Z both point directly to 
> X).  I don't like exposing the implementation differences between copy 
> ops to the user.  The downside is "origin(destination(X))" == {X, Y}, 
> which Matt noted isn't desirable, though maybe this case isn't "simple" 
> because of the "copy of a copy" scenario.
> 
> ** Notice that if both copies were grafts, the result is {}, and there's 
> nothing that can be done about it.
> 
> 
> C) What duplicates of X exist?
> 
>     Op             Alternative 1    Alternative 2
> duplicate(X)      {Y, Z}           {X, Y, Z}
> duplicate(Y)      {X, Z}           {X, Y, Z}
> duplicate(Z)      {X, Y}           {X, Y, Z}
> 
> I don't have a preference here.  Excluding the parameter from the result 
> means that duplicate(all()) can't find all duplicates anywhere, because 
> the result is always {}.

We have some precedents here:

 ancestors(X) = {X, ...}

I don't have a strong opinion, though. But I'm really not sure about
this name. It's only going to find duplicates made by
graft/transplant/etc., not anything produced 'manually'. And that will
disappoint people. Silly people.


Also, are you aware of Pierre-Yves' evolution work? It has a rather
closely related notion of predecessors and successor changesets and will
probably want a notion of duplicates as well. So.. do we want to have
two sets of predicates? You probably need to talk to him about that.

> D) Is this the very original X?
> 
> foo(X) == {}
> foo(Y) == {X}
> foo(Z) == {X}

I don't think the question matches the result here, nor is it in the
right canonical form for a revset query. Better would be:

 "the set of all changesets that are not grafts/transplants"
 origin(all()) # alternative 1

or

 "the set of all changesets that are first origins of <set>"
 origin(all()) # alternative 1

or

 "X if X is not a graft/etc."
 X and origin(all())

or

 "X if X -is- a graft/etc."
 X and not origin(all())

I forget whether all() is the proposed default here, but it seems like
it'd be useful.

-- 
Mathematics is the supreme nostalgia of our time.




More information about the Mercurial-devel mailing list