Patchwork [10,of,10] effectflag: detect when diff changed

login
register
mail settings
Submitter Boris Feld
Date July 7, 2017, 12:38 p.m.
Message ID <449fc1c748c6e058e892.1499431119@FB>
Download mbox | patch
Permalink /patch/22098/
State Changes Requested
Headers show

Comments

Boris Feld - July 7, 2017, 12:38 p.m.
# HG changeset patch
# User Boris Feld <boris.feld@octobus.net>
# Date 1499346007 -7200
#      Thu Jul 06 15:00:07 2017 +0200
# Node ID 449fc1c748c6e058e892a4c940e20137e52e7808
# Parent  6a40d87dfedcce4064eb4bcdb131ed4d427fd4de
# EXP-Topic effectflag
effectflag: detect when diff changed

Store in effect flag when the diff changed between the predecessor and
its successors.

Comparing the diff is not easy because we do not want to incorrectly detect a
Jun Wu - July 9, 2017, 8:17 p.m.
It's not very clear to me that what do we plan to use the effectflags.

I guess the most useful case is divergent resolution, like Alice changes
message, Bob changes content, so we know what to do. But a chain of changes
could cancel effects with each other, like:

  A0 - (amend by Alice) -> A1 - (amend by Alice) -> A2
   \
    -- (a chain of rewrites by Bob) -> A9

"A0 -> A1" changes diff content, "A1 -> A2" changes diff content. But
"A0 -> A2" does not necessarily change diff content.

This is a divergent case where A0 has two visible successors: A2 and A9. A2
does not have diff content change but with effectflags, A2 might be treated
as having diff content change.

I think we should store the hash of actual diff text as metadata to detect
diff changes across a chain of rewrites. That hash could be stored in a
cache.

For other information like branch, desc, meta, parents. Since reading them
is very fast using changelog, I'm not sure about the benefit of storing them
in obsmarkers. It might be helpful to provide extra information for nodes
unknown to local changelog (I'd also argue those markers are meaningless and
should be hidden from the user, but that's another topic), but I don't think
this feature is useful for an end-user.


Excerpts from Boris Feld's message of 2017-07-07 14:38:39 +0200:
> # HG changeset patch
> # User Boris Feld <boris.feld@octobus.net>
> # Date 1499346007 -7200
> #      Thu Jul 06 15:00:07 2017 +0200
> # Node ID 449fc1c748c6e058e892a4c940e20137e52e7808
> # Parent  6a40d87dfedcce4064eb4bcdb131ed4d427fd4de
> # EXP-Topic effectflag
> effectflag: detect when diff changed
> 
> Store in effect flag when the diff changed between the predecessor and
> its successors.
> 
> Comparing the diff is not easy because we do not want to incorrectly detect a
> diff modification when the changeset has only been rebased.
> 
> diff -r 6a40d87dfedc -r 449fc1c748c6 mercurial/obsutil.py
> --- a/mercurial/obsutil.py    Thu Jul 06 14:58:44 2017 +0200
> +++ b/mercurial/obsutil.py    Thu Jul 06 15:00:07 2017 +0200
> @@ -542,6 +542,7 @@
>  DESCCHANGED = 1 << 0 # action changed the description
>  METACHANGED = 1 << 1 # action change the meta
>  PARENTCHANGED = 1 << 2 # action change the parent
> +DIFFCHANGED = 1 << 3 # action change diff introduced by the changeset
>  USERCHANGED = 1 << 4 # the user changed
>  DATECHANGED = 1 << 5 # the date changed
>  BRANCHCHANGED = 1 << 6 # the branch changed
> @@ -565,6 +566,46 @@
>  
>      return True
>  
> +def _prepare_hunk(hunk):
> +    """Drop all information but the username and patch"""
> +    cleanunk = []
> +    for line in hunk.splitlines():
> +        if line.startswith(b'# User') or not line.startswith(b'#'):
> +            if line.startswith(b'@@'):
> +                line = b'@@\n'
> +            cleanunk.append(line)
> +    return cleanunk
> +
> +def _getdifflines(iterdiff):
> +    """return a cleaned up lines"""
> +    try:
> +        lines = next(iterdiff)
> +    except StopIteration:
> +        return None
> +    return _prepare_hunk(lines)
> +
> +def _cmpdiff(leftctx, rightctx):
> +    """return True if both ctx introduce the "same diff"
> +
> +    This is a first and basic implementation, with many shortcoming.
> +    """
> +
> +    # Leftctx or right ctx might be filtered, so we need to use the contexts
> +    # with an unfiltered repository to safely compute the diff
> +    leftunfi = leftctx._repo.unfiltered()[leftctx.rev()]
> +    leftdiff = leftunfi.diff(git=1)
> +    rightunfi = rightctx._repo.unfiltered()[rightctx.rev()]
> +    rightdiff = rightunfi.diff(git=1)
> +
> +    left, right = (0, 0)
> +    while None not in (left, right):
> +        left = _getdifflines(leftdiff)
> +        right = _getdifflines(rightdiff)
> +
> +        if left != right:
> +            return False
> +    return True
> +
>  def geteffectflag(relation):
>      """ From an obs-marker relation, compute what changed between the
>      predecessor and the successor.
> @@ -604,4 +645,12 @@
>          if ctxmeta != srcmeta:
>              effects |= METACHANGED
>  
> +        # Check if at least one of the parent has changed
> +        if changectx.parents() != source.parents():
> +            effects |= PARENTCHANGED
> +
> +        # Check if the diff has changed
> +        if not _cmpdiff(source, changectx):
> +            effects |= DIFFCHANGED
> +
>      return effects
> diff -r 6a40d87dfedc -r 449fc1c748c6 tests/test-obsmarkers-effectflag.t
> --- a/tests/test-obsmarkers-effectflag.t    Thu Jul 06 14:58:44 2017 +0200
> +++ b/tests/test-obsmarkers-effectflag.t    Thu Jul 06 15:00:07 2017 +0200
> @@ -95,7 +95,7 @@
>  
>    $ hg debugobsolete --rev .
>    d6f4d8b8d3c8cde990f13915bced7f92ce1cc54f 0 {ebfe0333e0d96f68a917afd97c0a0af87f1c3b5f} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'user': 'test'}
> -  ebfe0333e0d96f68a917afd97c0a0af87f1c3b5f 75781fdbdbf58a987516b00c980bccda1e9ae588 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'user': 'test'}
> +  ebfe0333e0d96f68a917afd97c0a0af87f1c3b5f 75781fdbdbf58a987516b00c980bccda1e9ae588 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'user': 'test'}
>  
>  amend with multiple effect (desc and meta)
Augie Fackler - July 14, 2017, 5:48 p.m.
On Fri, Jul 07, 2017 at 02:38:39PM +0200, Boris Feld wrote:
> # HG changeset patch
> # User Boris Feld <boris.feld@octobus.net>
> # Date 1499346007 -7200
> #      Thu Jul 06 15:00:07 2017 +0200
> # Node ID 449fc1c748c6e058e892a4c940e20137e52e7808
> # Parent  6a40d87dfedcce4064eb4bcdb131ed4d427fd4de
> # EXP-Topic effectflag
> effectflag: detect when diff changed

I've done some thinking about this, and I'd like to see a bit of a
unified story between these bits of metadata and
https://www.mercurial-scm.org/wiki/CommitCustodyConcept - I know
Mozilla is super-interested in that, and I think it's got a lot of
merit generally (instead of just recording how things mutate, we could
also do things like have a CI system sign off on a revision in an
in-history way, for example).

I'm a little curious about the decision to use a metadata field but
then do a bitfield inside the metadata. Any reason to not use
comma-separated verbs or something? Just a space constraint concern?

(A v2 of this series, should we go that route, probably also wants to
document the nature of the ef1 field someplace.)

>
> Store in effect flag when the diff changed between the predecessor and
> its successors.
>
> Comparing the diff is not easy because we do not want to incorrectly detect a
> diff modification when the changeset has only been rebased.
>
> diff -r 6a40d87dfedc -r 449fc1c748c6 mercurial/obsutil.py
> --- a/mercurial/obsutil.py	Thu Jul 06 14:58:44 2017 +0200
> +++ b/mercurial/obsutil.py	Thu Jul 06 15:00:07 2017 +0200
> @@ -542,6 +542,7 @@
>  DESCCHANGED = 1 << 0 # action changed the description
>  METACHANGED = 1 << 1 # action change the meta
>  PARENTCHANGED = 1 << 2 # action change the parent
> +DIFFCHANGED = 1 << 3 # action change diff introduced by the changeset
>  USERCHANGED = 1 << 4 # the user changed
>  DATECHANGED = 1 << 5 # the date changed
>  BRANCHCHANGED = 1 << 6 # the branch changed
> @@ -565,6 +566,46 @@
>
>      return True
>
> +def _prepare_hunk(hunk):
> +    """Drop all information but the username and patch"""
> +    cleanunk = []
> +    for line in hunk.splitlines():
> +        if line.startswith(b'# User') or not line.startswith(b'#'):
> +            if line.startswith(b'@@'):
> +                line = b'@@\n'
> +            cleanunk.append(line)
> +    return cleanunk
> +
> +def _getdifflines(iterdiff):
> +    """return a cleaned up lines"""
> +    try:
> +        lines = next(iterdiff)
> +    except StopIteration:
> +        return None
> +    return _prepare_hunk(lines)
> +
> +def _cmpdiff(leftctx, rightctx):
> +    """return True if both ctx introduce the "same diff"
> +
> +    This is a first and basic implementation, with many shortcoming.
> +    """
> +
> +    # Leftctx or right ctx might be filtered, so we need to use the contexts
> +    # with an unfiltered repository to safely compute the diff
> +    leftunfi = leftctx._repo.unfiltered()[leftctx.rev()]
> +    leftdiff = leftunfi.diff(git=1)
> +    rightunfi = rightctx._repo.unfiltered()[rightctx.rev()]
> +    rightdiff = rightunfi.diff(git=1)
> +
> +    left, right = (0, 0)
> +    while None not in (left, right):
> +        left = _getdifflines(leftdiff)
> +        right = _getdifflines(rightdiff)
> +
> +        if left != right:
> +            return False
> +    return True
> +
>  def geteffectflag(relation):
>      """ From an obs-marker relation, compute what changed between the
>      predecessor and the successor.
> @@ -604,4 +645,12 @@
>          if ctxmeta != srcmeta:
>              effects |= METACHANGED
>
> +        # Check if at least one of the parent has changed
> +        if changectx.parents() != source.parents():
> +            effects |= PARENTCHANGED
> +
> +        # Check if the diff has changed
> +        if not _cmpdiff(source, changectx):
> +            effects |= DIFFCHANGED
> +
>      return effects
> diff -r 6a40d87dfedc -r 449fc1c748c6 tests/test-obsmarkers-effectflag.t
> --- a/tests/test-obsmarkers-effectflag.t	Thu Jul 06 14:58:44 2017 +0200
> +++ b/tests/test-obsmarkers-effectflag.t	Thu Jul 06 15:00:07 2017 +0200
> @@ -95,7 +95,7 @@
>
>    $ hg debugobsolete --rev .
>    d6f4d8b8d3c8cde990f13915bced7f92ce1cc54f 0 {ebfe0333e0d96f68a917afd97c0a0af87f1c3b5f} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'user': 'test'}
> -  ebfe0333e0d96f68a917afd97c0a0af87f1c3b5f 75781fdbdbf58a987516b00c980bccda1e9ae588 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'user': 'test'}
> +  ebfe0333e0d96f68a917afd97c0a0af87f1c3b5f 75781fdbdbf58a987516b00c980bccda1e9ae588 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'user': 'test'}
>
>  amend with multiple effect (desc and meta)
>  -------------------------------------------
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Boris Feld - July 18, 2017, 10:58 a.m.
On Fri, 2017-07-14 at 13:48 -0400, Augie Fackler wrote:
> On Fri, Jul 07, 2017 at 02:38:39PM +0200, Boris Feld wrote:
> > # HG changeset patch
> > # User Boris Feld <boris.feld@octobus.net>
> > # Date 1499346007 -7200
> > #      Thu Jul 06 15:00:07 2017 +0200
> > # Node ID 449fc1c748c6e058e892a4c940e20137e52e7808
> > # Parent  6a40d87dfedcce4064eb4bcdb131ed4d427fd4de
> > # EXP-Topic effectflag
> > effectflag: detect when diff changed
> 
> I've done some thinking about this, and I'd like to see a bit of a
> unified story between these bits of metadata and
> https://www.mercurial-scm.org/wiki/CommitCustodyConcept - I know
> Mozilla is super-interested in that, and I think it's got a lot of
> merit generally (instead of just recording how things mutate, we
> could
> also do things like have a CI system sign off on a revision in an
> in-history way, for example).

CommitCustody seems somehow related to effect flag. While CommitCustody
stores high-level information in a secure way (cryptography signed),
effectflag store low-level information when a changeset evolve,
basically what changes between a changeset and its successors.

Also I think CommitCustody will trigger a changeset evolution as I
imagine it like this: I reviewed this patch, add my signature in meta
(or somewhere else) and amend the changeset. Effectflag take place when
a changeset evolution is triggered.

We should make them both collaborate or else each new custody will also
store an effect flag.

> 
> I'm a little curious about the decision to use a metadata field but
> then do a bitfield inside the metadata. Any reason to not use
> comma-separated verbs or something? Just a space constraint concern?

Our initial idea was to store it in the obsmarker flag field, but
reviewers were concerned about using some bits in the flag field right
now while we might want to improve effect flag in a near future. We
fallbacked storing it on the metadata field. We kept the bitfield for
putting it in the obsmarker flag when it will be ready.

It's also why the name field has a number in it, ef1 is basically
"Effect Flag v1". If we want to add a new version, we could just put it
in ef2 without worrying too much about BC.

> 
> (A v2 of this series, should we go that route, probably also wants to
> document the nature of the ef1 field someplace.)
> 

I would be happy to send a V2 documenting it in obsutil.py, just above
the fields themselves, what do you think?

> > 
> > Store in effect flag when the diff changed between the predecessor
> > and
> > its successors.
> > 
> > Comparing the diff is not easy because we do not want to
> > incorrectly detect a
> > diff modification when the changeset has only been rebased.
> > 
> > diff -r 6a40d87dfedc -r 449fc1c748c6 mercurial/obsutil.py
> > --- a/mercurial/obsutil.py	Thu Jul 06 14:58:44 2017 +0200
> > +++ b/mercurial/obsutil.py	Thu Jul 06 15:00:07 2017 +0200
> > @@ -542,6 +542,7 @@
> >  DESCCHANGED = 1 << 0 # action changed the description
> >  METACHANGED = 1 << 1 # action change the meta
> >  PARENTCHANGED = 1 << 2 # action change the parent
> > +DIFFCHANGED = 1 << 3 # action change diff introduced by the
> > changeset
> >  USERCHANGED = 1 << 4 # the user changed
> >  DATECHANGED = 1 << 5 # the date changed
> >  BRANCHCHANGED = 1 << 6 # the branch changed
> > @@ -565,6 +566,46 @@
> > 
> >      return True
> > 
> > +def _prepare_hunk(hunk):
> > +    """Drop all information but the username and patch"""
> > +    cleanunk = []
> > +    for line in hunk.splitlines():
> > +        if line.startswith(b'# User') or not
> > line.startswith(b'#'):
> > +            if line.startswith(b'@@'):
> > +                line = b'@@\n'
> > +            cleanunk.append(line)
> > +    return cleanunk
> > +
> > +def _getdifflines(iterdiff):
> > +    """return a cleaned up lines"""
> > +    try:
> > +        lines = next(iterdiff)
> > +    except StopIteration:
> > +        return None
> > +    return _prepare_hunk(lines)
> > +
> > +def _cmpdiff(leftctx, rightctx):
> > +    """return True if both ctx introduce the "same diff"
> > +
> > +    This is a first and basic implementation, with many
> > shortcoming.
> > +    """
> > +
> > +    # Leftctx or right ctx might be filtered, so we need to use
> > the contexts
> > +    # with an unfiltered repository to safely compute the diff
> > +    leftunfi = leftctx._repo.unfiltered()[leftctx.rev()]
> > +    leftdiff = leftunfi.diff(git=1)
> > +    rightunfi = rightctx._repo.unfiltered()[rightctx.rev()]
> > +    rightdiff = rightunfi.diff(git=1)
> > +
> > +    left, right = (0, 0)
> > +    while None not in (left, right):
> > +        left = _getdifflines(leftdiff)
> > +        right = _getdifflines(rightdiff)
> > +
> > +        if left != right:
> > +            return False
> > +    return True
> > +
> >  def geteffectflag(relation):
> >      """ From an obs-marker relation, compute what changed between
> > the
> >      predecessor and the successor.
> > @@ -604,4 +645,12 @@
> >          if ctxmeta != srcmeta:
> >              effects |= METACHANGED
> > 
> > +        # Check if at least one of the parent has changed
> > +        if changectx.parents() != source.parents():
> > +            effects |= PARENTCHANGED
> > +
> > +        # Check if the diff has changed
> > +        if not _cmpdiff(source, changectx):
> > +            effects |= DIFFCHANGED
> > +
> >      return effects
> > diff -r 6a40d87dfedc -r 449fc1c748c6 tests/test-obsmarkers-
> > effectflag.t
> > --- a/tests/test-obsmarkers-effectflag.t	Thu Jul 06 14:58:44
> > 2017 +0200
> > +++ b/tests/test-obsmarkers-effectflag.t	Thu Jul 06 15:00:07
> > 2017 +0200
> > @@ -95,7 +95,7 @@
> > 
> >    $ hg debugobsolete --rev .
> >    d6f4d8b8d3c8cde990f13915bced7f92ce1cc54f 0
> > {ebfe0333e0d96f68a917afd97c0a0af87f1c3b5f} (Thu Jan 01 00:00:00
> > 1970 +0000) {'ef1': '0', 'user': 'test'}
> > -  ebfe0333e0d96f68a917afd97c0a0af87f1c3b5f
> > 75781fdbdbf58a987516b00c980bccda1e9ae588 0 (Thu Jan 01 00:00:00
> > 1970 +0000) {'ef1': '0', 'user': 'test'}
> > +  ebfe0333e0d96f68a917afd97c0a0af87f1c3b5f
> > 75781fdbdbf58a987516b00c980bccda1e9ae588 0 (Thu Jan 01 00:00:00
> > 1970 +0000) {'ef1': '8', 'user': 'test'}
> > 
> >  amend with multiple effect (desc and meta)
> >  -------------------------------------------
> > _______________________________________________
> > Mercurial-devel mailing list
> > Mercurial-devel@mercurial-scm.org
> > https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Christophe de Vienne - July 18, 2017, 1:12 p.m.
Hello,

As a end-user I encountered some situations were having such information
about changeset not available in my local repo would have help.

The main situation I recall is a colleague doing multiple amend or
rebase of several changesets. On my side I had made other rebase &
amends (lack of communication I guess) and pushed. When he pulled we
ended up with several divergent changeset that were not trivial to sort.
To solve the issue I pulled everything locally and had to compare the
divergent changeset diffs one by one.

Having some more information about obsolete (and not available) cset in
my repository would have help a lot to decide which version of the
changeset to take. Like who did what (amend ? rebase ?).

Le 09/07/2017 à 22:17, quark at fb.com (Jun Wu) a écrit :
> It's not very clear to me that what do we plan to use the effectflags.
> 
> I guess the most useful case is divergent resolution, like Alice changes
> message, Bob changes content, so we know what to do. But a chain of changes
> could cancel effects with each other, like:
> 
>   A0 - (amend by Alice) -> A1 - (amend by Alice) -> A2
>    \
>     -- (a chain of rewrites by Bob) -> A9
> 
> "A0 -> A1" changes diff content, "A1 -> A2" changes diff content. But
> "A0 -> A2" does not necessarily change diff content.
> 
> This is a divergent case where A0 has two visible successors: A2 and A9. A2
> does not have diff content change but with effectflags, A2 might be treated
> as having diff content change.
> 
> I think we should store the hash of actual diff text as metadata to detect
> diff changes across a chain of rewrites. That hash could be stored in a
> cache.
> 
> For other information like branch, desc, meta, parents. Since reading them
> is very fast using changelog, I'm not sure about the benefit of storing them
> in obsmarkers. It might be helpful to provide extra information for nodes
> unknown to local changelog (I'd also argue those markers are meaningless and
> should be hidden from the user, but that's another topic), but I don't think
> this feature is useful for an end-user.
> 
> 
> Excerpts from Boris Feld's message of 2017-07-07 14:38:39 +0200:
>> # HG changeset patch
>> # User Boris Feld <boris.feld at octobus.net>
>> # Date 1499346007 -7200
>> #      Thu Jul 06 15:00:07 2017 +0200
>> # Node ID 449fc1c748c6e058e892a4c940e20137e52e7808
>> # Parent  6a40d87dfedcce4064eb4bcdb131ed4d427fd4de
>> # EXP-Topic effectflag
>> effectflag: detect when diff changed
>>
>> Store in effect flag when the diff changed between the predecessor and
>> its successors.
>>
>> Comparing the diff is not easy because we do not want to incorrectly detect a
>> diff modification when the changeset has only been rebased.
>>
>> diff -r 6a40d87dfedc -r 449fc1c748c6 mercurial/obsutil.py
>> --- a/mercurial/obsutil.py    Thu Jul 06 14:58:44 2017 +0200
>> +++ b/mercurial/obsutil.py    Thu Jul 06 15:00:07 2017 +0200
>> @@ -542,6 +542,7 @@
>>  DESCCHANGED = 1 << 0 # action changed the description
>>  METACHANGED = 1 << 1 # action change the meta
>>  PARENTCHANGED = 1 << 2 # action change the parent
>> +DIFFCHANGED = 1 << 3 # action change diff introduced by the changeset
>>  USERCHANGED = 1 << 4 # the user changed
>>  DATECHANGED = 1 << 5 # the date changed
>>  BRANCHCHANGED = 1 << 6 # the branch changed
>> @@ -565,6 +566,46 @@
>>  
>>      return True
>>  
>> +def _prepare_hunk(hunk):
>> +    """Drop all information but the username and patch"""
>> +    cleanunk = []
>> +    for line in hunk.splitlines():
>> +        if line.startswith(b'# User') or not line.startswith(b'#'):
>> +            if line.startswith(b'@@'):
>> +                line = b'@@\n'
>> +            cleanunk.append(line)
>> +    return cleanunk
>> +
>> +def _getdifflines(iterdiff):
>> +    """return a cleaned up lines"""
>> +    try:
>> +        lines = next(iterdiff)
>> +    except StopIteration:
>> +        return None
>> +    return _prepare_hunk(lines)
>> +
>> +def _cmpdiff(leftctx, rightctx):
>> +    """return True if both ctx introduce the "same diff"
>> +
>> +    This is a first and basic implementation, with many shortcoming.
>> +    """
>> +
>> +    # Leftctx or right ctx might be filtered, so we need to use the contexts
>> +    # with an unfiltered repository to safely compute the diff
>> +    leftunfi = leftctx._repo.unfiltered()[leftctx.rev()]
>> +    leftdiff = leftunfi.diff(git=1)
>> +    rightunfi = rightctx._repo.unfiltered()[rightctx.rev()]
>> +    rightdiff = rightunfi.diff(git=1)
>> +
>> +    left, right = (0, 0)
>> +    while None not in (left, right):
>> +        left = _getdifflines(leftdiff)
>> +        right = _getdifflines(rightdiff)
>> +
>> +        if left != right:
>> +            return False
>> +    return True
>> +
>>  def geteffectflag(relation):
>>      """ From an obs-marker relation, compute what changed between the
>>      predecessor and the successor.
>> @@ -604,4 +645,12 @@
>>          if ctxmeta != srcmeta:
>>              effects |= METACHANGED
>>  
>> +        # Check if at least one of the parent has changed
>> +        if changectx.parents() != source.parents():
>> +            effects |= PARENTCHANGED
>> +
>> +        # Check if the diff has changed
>> +        if not _cmpdiff(source, changectx):
>> +            effects |= DIFFCHANGED
>> +
>>      return effects
>> diff -r 6a40d87dfedc -r 449fc1c748c6 tests/test-obsmarkers-effectflag.t
>> --- a/tests/test-obsmarkers-effectflag.t    Thu Jul 06 14:58:44 2017 +0200
>> +++ b/tests/test-obsmarkers-effectflag.t    Thu Jul 06 15:00:07 2017 +0200
>> @@ -95,7 +95,7 @@
>>  
>>    $ hg debugobsolete --rev .
>>    d6f4d8b8d3c8cde990f13915bced7f92ce1cc54f 0 {ebfe0333e0d96f68a917afd97c0a0af87f1c3b5f} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'user': 'test'}
>> -  ebfe0333e0d96f68a917afd97c0a0af87f1c3b5f 75781fdbdbf58a987516b00c980bccda1e9ae588 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'user': 'test'}
>> +  ebfe0333e0d96f68a917afd97c0a0af87f1c3b5f 75781fdbdbf58a987516b00c980bccda1e9ae588 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'user': 'test'}
>>  
>>  amend with multiple effect (desc and meta)
>
Jun Wu - July 18, 2017, 4:57 p.m.
Excerpts from Christophe de Vienne's message of 2017-07-18 15:12:10 +0200:
> Hello,
> 
> As a end-user I encountered some situations were having such information
> about changeset not available in my local repo would have help.
> 
> The main situation I recall is a colleague doing multiple amend or
> rebase of several changesets. On my side I had made other rebase &
> amends (lack of communication I guess) and pushed. When he pulled we
> ended up with several divergent changeset that were not trivial to sort.
> To solve the issue I pulled everything locally and had to compare the
> divergent changeset diffs one by one.
> 
> Having some more information about obsolete (and not available) cset in
> my repository would have help a lot to decide which version of the
> changeset to take. Like who did what (amend ? rebase ?).

The obsmarker has "user", "date", "operation" (disabled by default)
information already. We need to define templates to render them. I think
they will be available at 4.4 release.

Patch

diff modification when the changeset has only been rebased.

diff -r 6a40d87dfedc -r 449fc1c748c6 mercurial/obsutil.py
--- a/mercurial/obsutil.py	Thu Jul 06 14:58:44 2017 +0200
+++ b/mercurial/obsutil.py	Thu Jul 06 15:00:07 2017 +0200
@@ -542,6 +542,7 @@ 
 DESCCHANGED = 1 << 0 # action changed the description
 METACHANGED = 1 << 1 # action change the meta
 PARENTCHANGED = 1 << 2 # action change the parent
+DIFFCHANGED = 1 << 3 # action change diff introduced by the changeset
 USERCHANGED = 1 << 4 # the user changed
 DATECHANGED = 1 << 5 # the date changed
 BRANCHCHANGED = 1 << 6 # the branch changed
@@ -565,6 +566,46 @@ 
 
     return True
 
+def _prepare_hunk(hunk):
+    """Drop all information but the username and patch"""
+    cleanunk = []
+    for line in hunk.splitlines():
+        if line.startswith(b'# User') or not line.startswith(b'#'):
+            if line.startswith(b'@@'):
+                line = b'@@\n'
+            cleanunk.append(line)
+    return cleanunk
+
+def _getdifflines(iterdiff):
+    """return a cleaned up lines"""
+    try:
+        lines = next(iterdiff)
+    except StopIteration:
+        return None
+    return _prepare_hunk(lines)
+
+def _cmpdiff(leftctx, rightctx):
+    """return True if both ctx introduce the "same diff"
+
+    This is a first and basic implementation, with many shortcoming.
+    """
+
+    # Leftctx or right ctx might be filtered, so we need to use the contexts
+    # with an unfiltered repository to safely compute the diff
+    leftunfi = leftctx._repo.unfiltered()[leftctx.rev()]
+    leftdiff = leftunfi.diff(git=1)
+    rightunfi = rightctx._repo.unfiltered()[rightctx.rev()]
+    rightdiff = rightunfi.diff(git=1)
+
+    left, right = (0, 0)
+    while None not in (left, right):
+        left = _getdifflines(leftdiff)
+        right = _getdifflines(rightdiff)
+
+        if left != right:
+            return False
+    return True
+
 def geteffectflag(relation):
     """ From an obs-marker relation, compute what changed between the
     predecessor and the successor.
@@ -604,4 +645,12 @@ 
         if ctxmeta != srcmeta:
             effects |= METACHANGED
 
+        # Check if at least one of the parent has changed
+        if changectx.parents() != source.parents():
+            effects |= PARENTCHANGED
+
+        # Check if the diff has changed
+        if not _cmpdiff(source, changectx):
+            effects |= DIFFCHANGED
+
     return effects
diff -r 6a40d87dfedc -r 449fc1c748c6 tests/test-obsmarkers-effectflag.t
--- a/tests/test-obsmarkers-effectflag.t	Thu Jul 06 14:58:44 2017 +0200
+++ b/tests/test-obsmarkers-effectflag.t	Thu Jul 06 15:00:07 2017 +0200
@@ -95,7 +95,7 @@ 
 
   $ hg debugobsolete --rev .
   d6f4d8b8d3c8cde990f13915bced7f92ce1cc54f 0 {ebfe0333e0d96f68a917afd97c0a0af87f1c3b5f} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'user': 'test'}
-  ebfe0333e0d96f68a917afd97c0a0af87f1c3b5f 75781fdbdbf58a987516b00c980bccda1e9ae588 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'user': 'test'}
+  ebfe0333e0d96f68a917afd97c0a0af87f1c3b5f 75781fdbdbf58a987516b00c980bccda1e9ae588 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'user': 'test'}
 
 amend with multiple effect (desc and meta)
 -------------------------------------------