Patchwork [2,of,2,evolve-ext] evolve: replace each obsolete sha1 in the description with its latest successor

login
register
mail settings
Submitter Matt Harbison
Date Aug. 10, 2014, 4:13 a.m.
Message ID <6e20d95da8ace63e621f.1407643983@Envy>
Download mbox | patch
Permalink /patch/5335/
State Not Applicable
Headers show

Comments

Matt Harbison - Aug. 10, 2014, 4:13 a.m.
# HG changeset patch
# User Matt Harbison <matt_harbison@yahoo.com>
# Date 1407625936 14400
#      Sat Aug 09 19:12:16 2014 -0400
# Node ID 6e20d95da8ace63e621f63a59d8b1426e227eaee
# Parent  66f4c5c52d970f145cd218ceed065c3fe096631d
evolve: replace each obsolete sha1 in the description with its latest successor

Obsolete csets are hidden by default and don't get pushed to the parent repo.
In order to avoid broken references in commit messages, it makes sense to evolve
those references to the latest and greatest successor, as each cset containing
them is evolved.

This alleviates the user that is evolving a series of commits from having to

  1) recognize that there is a hash that needs updating in any one of the series
  2) look up the latest successor manually
  3) hg amend -e

The regular expression for matching and the logic for replacing are borrowed
from 45562379ce4e in the convert extension.
Pierre-Yves David - Aug. 10, 2014, 7:28 a.m.
On 08/09/2014 09:13 PM, Matt Harbison wrote:
> # HG changeset patch
> # User Matt Harbison <matt_harbison@yahoo.com>
> # Date 1407625936 14400
> #      Sat Aug 09 19:12:16 2014 -0400
> # Node ID 6e20d95da8ace63e621f63a59d8b1426e227eaee
> # Parent  66f4c5c52d970f145cd218ceed065c3fe096631d
> evolve: replace each obsolete sha1 in the description with its latest successor

The feature looks awesome. Thanks. I've some feedback on the implementation.

> Obsolete csets are hidden by default and don't get pushed to the parent repo.
> In order to avoid broken references in commit messages, it makes sense to evolve
> those references to the latest and greatest successor, as each cset containing
> them is evolved.
>
> This alleviates the user that is evolving a series of commits from having to
>
>    1) recognize that there is a hash that needs updating in any one of the series
>    2) look up the latest successor manually
>    3) hg amend -e
>
> The regular expression for matching and the logic for replacing are borrowed
> from 45562379ce4e in the convert extension.
>
> diff --git a/hgext/evolve.py b/hgext/evolve.py
> --- a/hgext/evolve.py
> +++ b/hgext/evolve.py
> @@ -27,6 +27,8 @@
>   from StringIO import StringIO
>   import struct
>   import urllib
> +import re
> +sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
>
>   import mercurial
>   from mercurial import util
> @@ -856,6 +858,21 @@
>       destbookmarks = repo.nodebookmarks(dest.node())
>       nodesrc = orig.node()
>       destphase = repo[nodesrc].phase()
> +    commitmsg = orig.description()
> +
> +    sha1s = re.findall(sha1re, commitmsg)
> +    for sha1 in sha1s:
> +        try:
> +            if not repo[sha1].obsolete():

You are looking the node into a filtered repo. We could have the node 
obsoleted but hidden. But see the other comment below.


> +                continue
> +        except error.RepoLookupError:
> +            continue
> +
> +        # There will be at most 1, but may be none if the successor was stripped
> +        for successor in repo.set('last(allsuccessors(%s))', sha1):
> +            newsha1 = successor.hex()
> +            commitmsg = commitmsg.replace(sha1, newsha1[:len(sha1)])
> +

There is a dedicated function to find the list of sets of successors. 
This function have the good idea to take a raw node (So it works even 
with hidden and missing changesets). Have a look at its documentation.

     mercurial.obsolete.successorssets

(Note that this function does not take in account phases so it may be 
needs some improvement.)


>       tr = repo.transaction('relocate')
>       try:
>           try:
> @@ -866,7 +883,7 @@
>                           'unresolved merge conflicts (see hg help resolve)')
>               cmdutil.duplicatecopies(repo, orig.node(), dest.node())
>               nodenew = rebase.concludenode(repo, orig.node(), dest.node(),
> -                                          node.nullid)
> +                                          node.nullid, commitmsg)
>           except util.Abort, exc:
>               class LocalMergeFailure(MergeFailure, exc.__class__):
>                   pass
> diff --git a/tests/test-evolve.t b/tests/test-evolve.t
> --- a/tests/test-evolve.t
> +++ b/tests/test-evolve.t
> @@ -271,7 +271,7 @@
>     atop:[6] a nifty feature
>     merging main-file-1
>     $ hg log
> -  7	feature-B: another feature (child of 568a468b60fc) - test
> +  7	feature-B: another feature (child of ba0ec09b1bab) - test
>     6	feature-A: a nifty feature - test
>     0	: base - test
>
> @@ -300,7 +300,7 @@
>     $ hg glog
>     @  8	feature-B: another feature that rox - test
>     |
> -  | o  7	: another feature (child of 568a468b60fc) - test
> +  | o  7	: another feature (child of ba0ec09b1bab) - test
>     |/
>     o  6	feature-A: a nifty feature - test
>     |
> @@ -308,13 +308,13 @@
>
>     $ hg evolve --any --traceback
>     recreate:[8] another feature that rox
> -  atop:[7] another feature (child of 568a468b60fc)
> +  atop:[7] another feature (child of ba0ec09b1bab)
>     computing new diff
> -  committed as 8234cfce9af9
> +  committed as 476d0454d60e
>     $ hg glog
> -  @  9	feature-B: bumped update to 5b410c3ae5af: - test
> +  @  9	feature-B: bumped update to 5c9c8d9c2e4e: - test
>     |
> -  o  7	: another feature (child of 568a468b60fc) - test
> +  o  7	: another feature (child of ba0ec09b1bab) - test
>     |
>     o  6	feature-A: a nifty feature - test
>     |
> @@ -376,7 +376,7 @@
>     |
>     o  13	feature-B: dansk! - test
>     |
> -  o  7	: another feature (child of 568a468b60fc) - test
> +  o  7	: another feature (child of ba0ec09b1bab) - test
>     |
>     o  6	feature-A: a nifty feature - test
>     |
> @@ -743,11 +743,11 @@
>     2 changesets folded
>     1 files updated, 0 files merged, 0 files removed, 0 files unresolved
>     $ glog
> -  @  16:d0aee199f74c@default(draft) Folding with custom commit message
> +  @  16:d6239ff09c9f@default(draft) Folding with custom commit message
>     |
> -  o  13:b0258f43f9d0@default(draft) dansk!
> +  o  13:56ade053f46d@default(draft) dansk!
>     |
> -  o  7:5b410c3ae5af@default(public) another feature (child of 568a468b60fc)
> +  o  7:5c9c8d9c2e4e@default(public) another feature (child of ba0ec09b1bab)
>     |
>     o  6:ba0ec09b1bab@default(public) a nifty feature
>     |
> @@ -762,8 +762,8 @@
>     2 changesets folded
>     1 files updated, 0 files merged, 0 files removed, 0 files unresolved
>     $ hg qlog
> -  17 - bf2fd466ccd4 A longer
> +  17 - dba606655966 A longer
>                       commit message (draft)
> -  7 - 5b410c3ae5af another feature (child of 568a468b60fc) (public)
> +  7 - 5c9c8d9c2e4e another feature (child of ba0ec09b1bab) (public)
>     6 - ba0ec09b1bab a nifty feature (public)
>     0 - e55e0562ee93 base (public)
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
>
Angel Ezquerra - Aug. 10, 2014, 8:14 a.m.
El 10/08/2014 09:29, "Pierre-Yves David" <pierre-yves.david@ens-lyon.org>
escribió:
>
>
>
> On 08/09/2014 09:13 PM, Matt Harbison wrote:
>>
>> # HG changeset patch
>> # User Matt Harbison <matt_harbison@yahoo.com>
>> # Date 1407625936 14400
>> #      Sat Aug 09 19:12:16 2014 -0400
>> # Node ID 6e20d95da8ace63e621f63a59d8b1426e227eaee
>> # Parent  66f4c5c52d970f145cd218ceed065c3fe096631d
>> evolve: replace each obsolete sha1 in the description with its latest
successor
>
>
> The feature looks awesome. Thanks. I've some feedback on the
implementation.
>

I agree, this is just awesome.

It would be really really cool if we did the same for the tags that refer
to obsolete changesets. Amending tagged revisions is annoying.

Cheers,

Angel
Matt Harbison - Aug. 10, 2014, 3:27 p.m.
Pierre-Yves David wrote:
>
>
> On 08/09/2014 09:13 PM, Matt Harbison wrote:
>> # HG changeset patch
>> # User Matt Harbison <matt_harbison@yahoo.com>
>> # Date 1407625936 14400
>> # Sat Aug 09 19:12:16 2014 -0400
>> # Node ID 6e20d95da8ace63e621f63a59d8b1426e227eaee
>> # Parent 66f4c5c52d970f145cd218ceed065c3fe096631d
>> evolve: replace each obsolete sha1 in the description with its latest
>> successor
>
> The feature looks awesome. Thanks. I've some feedback on the
> implementation.
>
>> Obsolete csets are hidden by default and don't get pushed to the
>> parent repo.
>> In order to avoid broken references in commit messages, it makes sense
>> to evolve
>> those references to the latest and greatest successor, as each cset
>> containing
>> them is evolved.
>>
>> This alleviates the user that is evolving a series of commits from
>> having to
>>
>> 1) recognize that there is a hash that needs updating in any one of
>> the series
>> 2) look up the latest successor manually
>> 3) hg amend -e
>>
>> The regular expression for matching and the logic for replacing are
>> borrowed
>> from 45562379ce4e in the convert extension.
>>
>> diff --git a/hgext/evolve.py b/hgext/evolve.py
>> --- a/hgext/evolve.py
>> +++ b/hgext/evolve.py
>> @@ -27,6 +27,8 @@
>> from StringIO import StringIO
>> import struct
>> import urllib
>> +import re
>> +sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
>>
>> import mercurial
>> from mercurial import util
>> @@ -856,6 +858,21 @@
>> destbookmarks = repo.nodebookmarks(dest.node())
>> nodesrc = orig.node()
>> destphase = repo[nodesrc].phase()
>> + commitmsg = orig.description()
>> +
>> + sha1s = re.findall(sha1re, commitmsg)
>> + for sha1 in sha1s:
>> + try:
>> + if not repo[sha1].obsolete():
>
> You are looking the node into a filtered repo. We could have the node
> obsoleted but hidden. But see the other comment below.
>
>
>> + continue
>> + except error.RepoLookupError:
>> + continue
>> +
>> + # There will be at most 1, but may be none if the successor was
>> stripped
>> + for successor in repo.set('last(allsuccessors(%s))', sha1):
>> + newsha1 = successor.hex()
>> + commitmsg = commitmsg.replace(sha1, newsha1[:len(sha1)])
>> +
>
> There is a dedicated function to find the list of sets of successors.
> This function have the good idea to take a raw node (So it works even
> with hidden and missing changesets). Have a look at its documentation.
>
> mercurial.obsolete.successorssets

OK, I'll have a look at this tonight.  It looks like it might take 
awhile to get my mind around it.  I basically copied a couple other 
areas in evolve.py that created a revset for allprecursors and then 
blindly called next().  (Unrelated to this- is assuming that there is a 
next in those cases safe?)  I agree though, it was my intention to check 
hidden csets too.

> (Note that this function does not take in account phases so it may be
> needs some improvement.)

How would phases affect this?  I suppose somebody could set the latest 
successor to secret, but that cset's precursors are still obsolete, and 
therefore not pushed to other repos.  So updating references to point to 
obsolete draft csets probably isn't correct.

--Matt
Matt Harbison - Aug. 10, 2014, 4:01 p.m.
Angel Ezquerra wrote:
>
> El 10/08/2014 09:29, "Pierre-Yves David" <pierre-yves.david@ens-lyon.org
> <mailto:pierre-yves.david@ens-lyon.org>> escribió:
>  >
>  >
>  >
>  > On 08/09/2014 09:13 PM, Matt Harbison wrote:
>  >>
>  >> # HG changeset patch
>  >> # User Matt Harbison <matt_harbison@yahoo.com
> <mailto:matt_harbison@yahoo.com>>
>  >> # Date 1407625936 14400
>  >> #      Sat Aug 09 19:12:16 2014 -0400
>  >> # Node ID 6e20d95da8ace63e621f63a59d8b1426e227eaee
>  >> # Parent  66f4c5c52d970f145cd218ceed065c3fe096631d
>  >> evolve: replace each obsolete sha1 in the description with its
> latest successor
>  >
>  >
>  > The feature looks awesome. Thanks. I've some feedback on the
> implementation.
>  >
>
> I agree, this is just awesome.
>
> It would be really really cool if we did the same for the tags that
> refer to obsolete changesets. Amending tagged revisions is annoying.
>
> Cheers,
>
> Angel
>

I agree.  And then I went to tag what I submitted here, and realized 
that the tag would move as I evolved things, which would defeat the 
point of tagging it.  So maybe we should evolve global tags only. 
(Though I'm in the habit of only creating global tags without thinking, 
and probably others are too.)  Would it be too surprising if local tags 
didn't evolve and global tags did?  I can see the use for evolving local 
tags in some cases too though.  I don't want a command line switch for this.

It seems that the cset copy (as opposed to the finalization where I 
handled the message update) is in rebase.rebasenode(), and it looks like 
the majority of the work is actually in merge.update().  I don't know 
how to change that without affecting non-evolve related updates.  (I 
suppose the alternative is to do it in evolve.relocate() after 
cmdutil.duplicatecopies() and patch up dirstate if necessary).  I have 
long thought it would be nice if rebase itself handled updating tags and 
commit messages as it processed the series, but never quite figured out 
how to do that.  Evolution probably makes that moot now.

What if you have a cset with a tag file that adds 'Y' to .hgtags that 
already contains 'X'.  When you evolve that single tag commit adding 
'Y', if 'X' and 'Y' are obsolete, do you evolve both entries in the 
file?  That can probably only happen if you tag 'X' on a different 
branch, and I'm not sure how common that is.

I'm not sure that there will be enough content to justify breaking the 
translation of obsolete -> successor hash into a utility function to 
share between the message and tag update cases.  So do you want to try 
your hand at the tag updates?  You're probably more familiar with the 
tag file than me with the merge work you did recently.

--Matt
Pierre-Yves David - Sept. 17, 2014, 5:20 a.m.
On 08/10/2014 05:01 PM, Matt Harbison wrote:

This message apparently feel into a crack of my inbox, sorry about that.

> Angel Ezquerra wrote:
>>
>> El 10/08/2014 09:29, "Pierre-Yves David" <pierre-yves.david@ens-lyon.org
>> <mailto:pierre-yves.david@ens-lyon.org>> escribió:
>>  >
>>  >
>>  >
>>  > On 08/09/2014 09:13 PM, Matt Harbison wrote:
>>  >>
>>  >> # HG changeset patch
>>  >> # User Matt Harbison <matt_harbison@yahoo.com
>> <mailto:matt_harbison@yahoo.com>>
>>  >> # Date 1407625936 14400
>>  >> #      Sat Aug 09 19:12:16 2014 -0400
>>  >> # Node ID 6e20d95da8ace63e621f63a59d8b1426e227eaee
>>  >> # Parent  66f4c5c52d970f145cd218ceed065c3fe096631d
>>  >> evolve: replace each obsolete sha1 in the description with its
>> latest successor
>>  >
>>  >
>>  > The feature looks awesome. Thanks. I've some feedback on the
>> implementation.
>>  >
>>
>> I agree, this is just awesome.
>>
>> It would be really really cool if we did the same for the tags that
>> refer to obsolete changesets. Amending tagged revisions is annoying.
>>
>> Cheers,
>>
>> Angel
>>
>
> I agree.  And then I went to tag what I submitted here, and realized
> that the tag would move as I evolved things, which would defeat the
> point of tagging it.  So maybe we should evolve global tags only.
> (Though I'm in the habit of only creating global tags without thinking,
> and probably others are too.)  Would it be too surprising if local tags
> didn't evolve and global tags did?  I can see the use for evolving local
> tags in some cases too though.  I don't want a command line switch for
> this.

We should probably have different strategy here.

Updating global tags in all case seems a pure win. Even if I'm sometimes 
considering that adding a global tags should maybe turn stuff 
public//immutable.

Local tag should behave like bookmark. Whenever we obsolete something we 
move all relevants information to the successors (working copy parent, 
bookmark) local tag should go in that list (in my opinions)

> It seems that the cset copy (as opposed to the finalization where I
> handled the message update) is in rebase.rebasenode(), and it looks like
> the majority of the work is actually in merge.update().  I don't know
> how to change that without affecting non-evolve related updates.  (I
> suppose the alternative is to do it in evolve.relocate() after
> cmdutil.duplicatecopies() and patch up dirstate if necessary).  I have
> long thought it would be nice if rebase itself handled updating tags and
> commit messages as it processed the series, but never quite figured out
> how to do that.  Evolution probably makes that moot now.

The evolve implementation is hacky and most of these part was done 3 
year ago by a newbie me. Feel free to refactor//improve anything there.

> What if you have a cset with a tag file that adds 'Y' to .hgtags that
> already contains 'X'.  When you evolve that single tag commit adding
> 'Y', if 'X' and 'Y' are obsolete, do you evolve both entries in the
> file?  That can probably only happen if you tag 'X' on a different
> branch, and I'm not sure how common that is.

Touching only the entry one commit adds sounds more sensible.

> I'm not sure that there will be enough content to justify breaking the
> translation of obsolete -> successor hash into a utility function to
> share between the message and tag update cases.  So do you want to try
> your hand at the tag updates?  You're probably more familiar with the
> tag file than me with the merge work you did recently.

Yes it make sense.
Matt Harbison - Sept. 30, 2014, 1:59 a.m.
Pierre-Yves David wrote:
>
>
> On 08/10/2014 05:01 PM, Matt Harbison wrote:
>
> This message apparently feel into a crack of my inbox, sorry about that.
>
>> Angel Ezquerra wrote:
>>>
>>> El 10/08/2014 09:29, "Pierre-Yves David" <pierre-yves.david@ens-lyon.org
>>> <mailto:pierre-yves.david@ens-lyon.org>> escribió:
>>> >
>>> >
>>> >
>>> > On 08/09/2014 09:13 PM, Matt Harbison wrote:
>>> >>
>>> >> # HG changeset patch
>>> >> # User Matt Harbison <matt_harbison@yahoo.com
>>> <mailto:matt_harbison@yahoo.com>>
>>> >> # Date 1407625936 14400
>>> >> # Sat Aug 09 19:12:16 2014 -0400
>>> >> # Node ID 6e20d95da8ace63e621f63a59d8b1426e227eaee
>>> >> # Parent 66f4c5c52d970f145cd218ceed065c3fe096631d
>>> >> evolve: replace each obsolete sha1 in the description with its
>>> latest successor
>>> >
>>> >
>>> > The feature looks awesome. Thanks. I've some feedback on the
>>> implementation.
>>> >
>>>
>>> I agree, this is just awesome.
>>>
>>> It would be really really cool if we did the same for the tags that
>>> refer to obsolete changesets. Amending tagged revisions is annoying.
>>>
>>> Cheers,
>>>
>>> Angel
>>>
>>
>> I agree. And then I went to tag what I submitted here, and realized
>> that the tag would move as I evolved things, which would defeat the
>> point of tagging it. So maybe we should evolve global tags only.
>> (Though I'm in the habit of only creating global tags without thinking,
>> and probably others are too.) Would it be too surprising if local tags
>> didn't evolve and global tags did? I can see the use for evolving local
>> tags in some cases too though. I don't want a command line switch for
>> this.
>
> We should probably have different strategy here.
>
> Updating global tags in all case seems a pure win. Even if I'm sometimes
> considering that adding a global tags should maybe turn stuff
> public//immutable.
>
> Local tag should behave like bookmark. Whenever we obsolete something we
> move all relevants information to the successors (working copy parent,
> bookmark) local tag should go in that list (in my opinions)

I agree with the global tag part.  I'd like global tags to remain 
mutable, because there have been a couple of times where I've tagged 
something and then found I've needed to tweak something before the build 
itself goes public.  So it's convenient, though I understand the other 
side too.

I don't necessarily like what you suggest for local though, because I 
can't prevent it from moving when I don't want it to in the scenario 
described above.  I'll probably skip it for the first implementation anyway.

>> It seems that the cset copy (as opposed to the finalization where I
>> handled the message update) is in rebase.rebasenode(), and it looks like
>> the majority of the work is actually in merge.update(). I don't know
>> how to change that without affecting non-evolve related updates. (I
>> suppose the alternative is to do it in evolve.relocate() after
>> cmdutil.duplicatecopies() and patch up dirstate if necessary). I have
>> long thought it would be nice if rebase itself handled updating tags and
>> commit messages as it processed the series, but never quite figured out
>> how to do that. Evolution probably makes that moot now.
>
> The evolve implementation is hacky and most of these part was done 3
> year ago by a newbie me. Feel free to refactor//improve anything there.
>
>> What if you have a cset with a tag file that adds 'Y' to .hgtags that
>> already contains 'X'. When you evolve that single tag commit adding
>> 'Y', if 'X' and 'Y' are obsolete, do you evolve both entries in the
>> file? That can probably only happen if you tag 'X' on a different
>> branch, and I'm not sure how common that is.
>
> Touching only the entry one commit adds sounds more sensible.

OK, agreed.  Should I *replace* the obsolete entry in .hgtags, or just 
append the new one on to the end?  I would think you would want to 
replace it so that the dangling reference isn't there in the public repo 
for all time.  But I ran into a case the other day where I obsoleted a 
commit that created a tag, and then tagged something else with the same 
label.  That 'tag something else' commit added entries for both tags in 
a single commit, even though the original was off in its own extinct 
branch.  I know moving tags causes several entries to be added, but this 
didn't require a --force.  I couldn't tell if that was by design or a 
bug, but I realized I don't understand some of the tradeoffs made to 
track tags in a file.

(I can try to reproduce this if you think it is a bug.)

>> I'm not sure that there will be enough content to justify breaking the
>> translation of obsolete -> successor hash into a utility function to
>> share between the message and tag update cases. So do you want to try
>> your hand at the tag updates? You're probably more familiar with the
>> tag file than me with the merge work you did recently.
>
> Yes it make sense.
>
>
Pierre-Yves David - Sept. 30, 2014, 3:40 p.m.
On 09/29/2014 08:59 PM, Matt Harbison wrote:
> Pierre-Yves David wrote:
>>
>>
>> On 08/10/2014 05:01 PM, Matt Harbison wrote:
>>
>> This message apparently feel into a crack of my inbox, sorry about that.
>>
>>> Angel Ezquerra wrote:
>>>>
>>>> El 10/08/2014 09:29, "Pierre-Yves David"
>>>> <pierre-yves.david@ens-lyon.org
>>>> <mailto:pierre-yves.david@ens-lyon.org>> escribió:
>>>> >
>>>> >
>>>> >
>>>> > On 08/09/2014 09:13 PM, Matt Harbison wrote:
>>>> >>
>>>> >> # HG changeset patch
>>>> >> # User Matt Harbison <matt_harbison@yahoo.com
>>>> <mailto:matt_harbison@yahoo.com>>
>>>> >> # Date 1407625936 14400
>>>> >> # Sat Aug 09 19:12:16 2014 -0400
>>>> >> # Node ID 6e20d95da8ace63e621f63a59d8b1426e227eaee
>>>> >> # Parent 66f4c5c52d970f145cd218ceed065c3fe096631d
>>>> >> evolve: replace each obsolete sha1 in the description with its
>>>> latest successor
>>>> >
>>>> >
>>>> > The feature looks awesome. Thanks. I've some feedback on the
>>>> implementation.
>>>> >
>>>>
>>>> I agree, this is just awesome.
>>>>
>>>> It would be really really cool if we did the same for the tags that
>>>> refer to obsolete changesets. Amending tagged revisions is annoying.
>>>>
>>>> Cheers,
>>>>
>>>> Angel
>>>>
>>>
>>> I agree. And then I went to tag what I submitted here, and realized
>>> that the tag would move as I evolved things, which would defeat the
>>> point of tagging it. So maybe we should evolve global tags only.
>>> (Though I'm in the habit of only creating global tags without thinking,
>>> and probably others are too.) Would it be too surprising if local tags
>>> didn't evolve and global tags did? I can see the use for evolving local
>>> tags in some cases too though. I don't want a command line switch for
>>> this.
>>
>> We should probably have different strategy here.
>>
>> Updating global tags in all case seems a pure win. Even if I'm sometimes
>> considering that adding a global tags should maybe turn stuff
>> public//immutable.
>>
>> Local tag should behave like bookmark. Whenever we obsolete something we
>> move all relevants information to the successors (working copy parent,
>> bookmark) local tag should go in that list (in my opinions)
>
> I agree with the global tag part.  I'd like global tags to remain
> mutable, because there have been a couple of times where I've tagged
> something and then found I've needed to tweak something before the build
> itself goes public.  So it's convenient, though I understand the other
> side too.
>
> I don't necessarily like what you suggest for local though, because I
> can't prevent it from moving when I don't want it to in the scenario
> described above.  I'll probably skip it for the first implementation
> anyway.

My point here is:

   bookmarks move the successors when their location get obsoleted. (but 
bookmark added changesets already obsolete does not move (unless at 
re-obsoleted time?))

   localtag should behave the same as localtag.

>>> It seems that the cset copy (as opposed to the finalization where I
>>> handled the message update) is in rebase.rebasenode(), and it looks like
>>> the majority of the work is actually in merge.update(). I don't know
>>> how to change that without affecting non-evolve related updates. (I
>>> suppose the alternative is to do it in evolve.relocate() after
>>> cmdutil.duplicatecopies() and patch up dirstate if necessary). I have
>>> long thought it would be nice if rebase itself handled updating tags and
>>> commit messages as it processed the series, but never quite figured out
>>> how to do that. Evolution probably makes that moot now.
>>
>> The evolve implementation is hacky and most of these part was done 3
>> year ago by a newbie me. Feel free to refactor//improve anything there.
>>
>>> What if you have a cset with a tag file that adds 'Y' to .hgtags that
>>> already contains 'X'. When you evolve that single tag commit adding
>>> 'Y', if 'X' and 'Y' are obsolete, do you evolve both entries in the
>>> file? That can probably only happen if you tag 'X' on a different
>>> branch, and I'm not sure how common that is.
>>
>> Touching only the entry one commit adds sounds more sensible.
>
> OK, agreed.  Should I *replace* the obsolete entry in .hgtags, or just
> append the new one on to the end?  I would think you would want to
> replace it so that the dangling reference isn't there in the public repo
> for all time.  But I ran into a case the other day where I obsoleted a
> commit that created a tag, and then tagged something else with the same
> label.  That 'tag something else' commit added entries for both tags in
> a single commit, even though the original was off in its own extinct
> branch.  I know moving tags causes several entries to be added, but this
> didn't require a --force.  I couldn't tell if that was by design or a
> bug, but I realized I don't understand some of the tradeoffs made to
> track tags in a file.

I think obsolete changeset should not appear in the tag file.

> (I can try to reproduce this if you think it is a bug.)

Could be a filtering related bug. interested in a repro and report.

Patch

diff --git a/hgext/evolve.py b/hgext/evolve.py
--- a/hgext/evolve.py
+++ b/hgext/evolve.py
@@ -27,6 +27,8 @@ 
 from StringIO import StringIO
 import struct
 import urllib
+import re
+sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
 
 import mercurial
 from mercurial import util
@@ -856,6 +858,21 @@ 
     destbookmarks = repo.nodebookmarks(dest.node())
     nodesrc = orig.node()
     destphase = repo[nodesrc].phase()
+    commitmsg = orig.description()
+
+    sha1s = re.findall(sha1re, commitmsg)
+    for sha1 in sha1s:
+        try:
+            if not repo[sha1].obsolete():
+                continue
+        except error.RepoLookupError:
+            continue
+
+        # There will be at most 1, but may be none if the successor was stripped
+        for successor in repo.set('last(allsuccessors(%s))', sha1):
+            newsha1 = successor.hex()
+            commitmsg = commitmsg.replace(sha1, newsha1[:len(sha1)])
+
     tr = repo.transaction('relocate')
     try:
         try:
@@ -866,7 +883,7 @@ 
                         'unresolved merge conflicts (see hg help resolve)')
             cmdutil.duplicatecopies(repo, orig.node(), dest.node())
             nodenew = rebase.concludenode(repo, orig.node(), dest.node(),
-                                          node.nullid)
+                                          node.nullid, commitmsg)
         except util.Abort, exc:
             class LocalMergeFailure(MergeFailure, exc.__class__):
                 pass
diff --git a/tests/test-evolve.t b/tests/test-evolve.t
--- a/tests/test-evolve.t
+++ b/tests/test-evolve.t
@@ -271,7 +271,7 @@ 
   atop:[6] a nifty feature
   merging main-file-1
   $ hg log
-  7	feature-B: another feature (child of 568a468b60fc) - test
+  7	feature-B: another feature (child of ba0ec09b1bab) - test
   6	feature-A: a nifty feature - test
   0	: base - test
 
@@ -300,7 +300,7 @@ 
   $ hg glog
   @  8	feature-B: another feature that rox - test
   |
-  | o  7	: another feature (child of 568a468b60fc) - test
+  | o  7	: another feature (child of ba0ec09b1bab) - test
   |/
   o  6	feature-A: a nifty feature - test
   |
@@ -308,13 +308,13 @@ 
   
   $ hg evolve --any --traceback
   recreate:[8] another feature that rox
-  atop:[7] another feature (child of 568a468b60fc)
+  atop:[7] another feature (child of ba0ec09b1bab)
   computing new diff
-  committed as 8234cfce9af9
+  committed as 476d0454d60e
   $ hg glog
-  @  9	feature-B: bumped update to 5b410c3ae5af: - test
+  @  9	feature-B: bumped update to 5c9c8d9c2e4e: - test
   |
-  o  7	: another feature (child of 568a468b60fc) - test
+  o  7	: another feature (child of ba0ec09b1bab) - test
   |
   o  6	feature-A: a nifty feature - test
   |
@@ -376,7 +376,7 @@ 
   |
   o  13	feature-B: dansk! - test
   |
-  o  7	: another feature (child of 568a468b60fc) - test
+  o  7	: another feature (child of ba0ec09b1bab) - test
   |
   o  6	feature-A: a nifty feature - test
   |
@@ -743,11 +743,11 @@ 
   2 changesets folded
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ glog
-  @  16:d0aee199f74c@default(draft) Folding with custom commit message
+  @  16:d6239ff09c9f@default(draft) Folding with custom commit message
   |
-  o  13:b0258f43f9d0@default(draft) dansk!
+  o  13:56ade053f46d@default(draft) dansk!
   |
-  o  7:5b410c3ae5af@default(public) another feature (child of 568a468b60fc)
+  o  7:5c9c8d9c2e4e@default(public) another feature (child of ba0ec09b1bab)
   |
   o  6:ba0ec09b1bab@default(public) a nifty feature
   |
@@ -762,8 +762,8 @@ 
   2 changesets folded
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg qlog
-  17 - bf2fd466ccd4 A longer
+  17 - dba606655966 A longer
                     commit message (draft)
-  7 - 5b410c3ae5af another feature (child of 568a468b60fc) (public)
+  7 - 5c9c8d9c2e4e another feature (child of ba0ec09b1bab) (public)
   6 - ba0ec09b1bab a nifty feature (public)
   0 - e55e0562ee93 base (public)