Patchwork D5496: revset: add "samebranch" keyword argument to the merge revset

login
register
mail settings
Submitter phabricator
Date Jan. 13, 2019, 10:45 p.m.
Message ID <6297c55d3785470b4961c3604a212824@localhost.localdomain>
Download mbox | patch
Permalink /patch/37716/
State Not Applicable
Headers show

Comments

phabricator - Jan. 13, 2019, 10:45 p.m.
angel.ezquerra updated this revision to Diff 13205.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D5496?vs=13018&id=13205

REVISION DETAIL
  https://phab.mercurial-scm.org/D5496

AFFECTED FILES
  mercurial/revset.py

CHANGE DETAILS




To: angel.ezquerra, #hg-reviewers
Cc: mercurial-devel
Yuya Nishihara - Jan. 14, 2019, 3:08 a.m.
> -@predicate('merge(withbranch)', safe=True)
> +@predicate('merge(withbranch, samebranch=True)', safe=True)

`[, samebranch]` or [, samebranch=False]`.

>      withbranch = ''
>      if 'withbranch' in args:
>          withbranch = getstring(args['withbranch'],
>                                 _('withbranch argument must be a string'))
>          kind, branchname, branchmatcher = stringutil.stringmatcher(withbranch)
> +    samebranch = None
> +    if 'samebranch' in args:
> +        # i18n: "samebranch" is a keyword
> +        samebranch = getboolean(args['samebranch'],
> +            _('samebranch argument must be a True or False'))
>      cl = repo.changelog
>      # create the function that will be used to filter the subset
>      if withbranch:
>          # matchfn is a function that returns true when a revision
>          # is a merge and the second parent belongs to a branch that
>          # matches the withbranch pattern (which can be a literal or a regex)
>          if kind == 'literal':
> -            matchfn = lambda r: (cl.parentrevs(r)[1] != -1
> -                                 and repo[r].p2().branch() == withbranch)
> +            basematchfn = lambda r: (cl.parentrevs(r)[1] != -1
> +                                     and repo[r].p2().branch() == withbranch)
>          else:
> -            matchfn = lambda r: (cl.parentrevs(r)[1] != -1
> -                                 and branchmatcher(repo[r].p2().branch()))
> -    else:
> -        # matchfn is a function that returns true when a revision is a merge
> -        matchfn = lambda r: cl.parentrevs(r)[1] != -1
> +            basematchfn = lambda r: (cl.parentrevs(r)[1] != -1
> +                                     and branchmatcher(repo[r].p2().branch()))
> +    else:
> +        basematchfn = lambda r: cl.parentrevs(r)[1] != -1
> +    if samebranch is None:
> +        matchfn = basematchfn
> +    else:
> +        # if samebranch was specified, build a new match function
> +        # that on top of basematch checks if the parents belong (or not)
> +        # to the same branch (depending on the value of samebranch)
> +        def matchfn(r):
> +            c = repo[r]
> +            if not basematchfn(r):
> +                return False
> +            issamebranchmerge = c.p1().branch() == c.p2().branch()
> +            return issamebranchmerge if samebranch else not issamebranchmerge

These conditions can be formed as followed:

```
matchfns = [lambda r: cl.parentrevs(r)[1] != -1]
if withbranch:
    matchfns.append(lambda r: branchmatcher(repo[r].p2().branch()))
if samebranch:
    matchfns.append(samebranchmatchfn)

if len(matchfns) == 1:
    # fast path for common case
    return subset.filter(matchfn[0], ...)
else:
    return subset.filter(lambda r: all(p(r) for p in matchfn), ...)
```
phabricator - Jan. 14, 2019, 3:11 a.m.
yuja added a comment.


  > -@predicate('merge(withbranch)', safe=True)
  >  +@predicate('merge(withbranch, samebranch=True)', safe=True)
  
  `[, samebranch]` or [, samebranch=False]`.
  
  >   withbranch = ''
  >   if 'withbranch' in args:
  >       withbranch = getstring(args['withbranch'],
  >                              _('withbranch argument must be a string'))
  >       kind, branchname, branchmatcher = stringutil.stringmatcher(withbranch)
  > 
  > +    samebranch = None
  >  +    if 'samebranch' in args:
  >  +        # i18n: "samebranch" is a keyword
  >  +        samebranch = getboolean(args['samebranch'],
  >  +            _('samebranch argument must be a True or False'))
  > 
  >   cl = repo.changelog
  >   # create the function that will be used to filter the subset
  >   if withbranch:
  >       # matchfn is a function that returns true when a revision
  >       # is a merge and the second parent belongs to a branch that
  >       # matches the withbranch pattern (which can be a literal or a regex)
  >       if kind == 'literal':
  > 
  > - matchfn = lambda r: (cl.parentrevs(r)[1] != -1
  > - and repo[r].p2().branch() == withbranch) +            basematchfn = lambda r: (cl.parentrevs(r)[1] != -1 +                                     and repo[r].p2().branch() == withbranch) else:
  > - matchfn = lambda r: (cl.parentrevs(r)[1] != -1
  > - and branchmatcher(repo[r].p2().branch()))
  > - else:
  > - # matchfn is a function that returns true when a revision is a merge
  > - matchfn = lambda r: cl.parentrevs(r)[1] != -1 +            basematchfn = lambda r: (cl.parentrevs(r)[1] != -1 +                                     and branchmatcher(repo[r].p2().branch())) +    else: +        basematchfn = lambda r: cl.parentrevs(r)[1] != -1 +    if samebranch is None: +        matchfn = basematchfn +    else: +        # if samebranch was specified, build a new match function +        # that on top of basematch checks if the parents belong (or not) +        # to the same branch (depending on the value of samebranch) +        def matchfn(r): +            c = repo[r] +            if not basematchfn(r): +                return False +            issamebranchmerge = c.p1().branch() == c.p2().branch() +            return issamebranchmerge if samebranch else not issamebranchmerge
  
  These conditions can be formed as followed:
  
    matchfns = [lambda r: cl.parentrevs(r)[1] != -1]
    if withbranch:
        matchfns.append(lambda r: branchmatcher(repo[r].p2().branch()))
    if samebranch:
        matchfns.append(samebranchmatchfn)
    
    if len(matchfns) == 1:
        # fast path for common case
        return subset.filter(matchfn[0], ...)
    else:
        return subset.filter(lambda r: all(p(r) for p in matchfn), ...)

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D5496

To: angel.ezquerra, #hg-reviewers
Cc: yuja, mercurial-devel

Patch

diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -1249,7 +1249,7 @@ 
         pass
     return baseset(datarepr=('<max %r, %r>', subset, os))
 
-@predicate('merge(withbranch)', safe=True)
+@predicate('merge(withbranch, samebranch=True)', safe=True)
 def merge(repo, subset, x):
     """Changeset is a merge changeset
 
@@ -1260,29 +1260,50 @@ 
     possible to specify a regular expression by starting the pattern
     with "re:". This can be used to match more than one branch
     (e.g. "re:branch1|branch2").
+
+    It is also possible to only return merges where both parents belong to
+    the same branch by specifying samebranch=True. If samebranch=False is
+    set then only merges where both parents do not belong to the same branch
+    will be returned.
     """
     # i18n: "merge" is a keyword
-    args = getargsdict(x, 'merge', 'withbranch')
+    args = getargsdict(x, 'merge', 'withbranch samebranch')
     withbranch = ''
     if 'withbranch' in args:
         withbranch = getstring(args['withbranch'],
                                _('withbranch argument must be a string'))
         kind, branchname, branchmatcher = stringutil.stringmatcher(withbranch)
+    samebranch = None
+    if 'samebranch' in args:
+        # i18n: "samebranch" is a keyword
+        samebranch = getboolean(args['samebranch'],
+            _('samebranch argument must be a True or False'))
     cl = repo.changelog
     # create the function that will be used to filter the subset
     if withbranch:
         # matchfn is a function that returns true when a revision
         # is a merge and the second parent belongs to a branch that
         # matches the withbranch pattern (which can be a literal or a regex)
         if kind == 'literal':
-            matchfn = lambda r: (cl.parentrevs(r)[1] != -1
-                                 and repo[r].p2().branch() == withbranch)
+            basematchfn = lambda r: (cl.parentrevs(r)[1] != -1
+                                     and repo[r].p2().branch() == withbranch)
         else:
-            matchfn = lambda r: (cl.parentrevs(r)[1] != -1
-                                 and branchmatcher(repo[r].p2().branch()))
-    else:
-        # matchfn is a function that returns true when a revision is a merge
-        matchfn = lambda r: cl.parentrevs(r)[1] != -1
+            basematchfn = lambda r: (cl.parentrevs(r)[1] != -1
+                                     and branchmatcher(repo[r].p2().branch()))
+    else:
+        basematchfn = lambda r: cl.parentrevs(r)[1] != -1
+    if samebranch is None:
+        matchfn = basematchfn
+    else:
+        # if samebranch was specified, build a new match function
+        # that on top of basematch checks if the parents belong (or not)
+        # to the same branch (depending on the value of samebranch)
+        def matchfn(r):
+            c = repo[r]
+            if not basematchfn(r):
+                return False
+            issamebranchmerge = c.p1().branch() == c.p2().branch()
+            return issamebranchmerge if samebranch else not issamebranchmerge
     return subset.filter(matchfn, condrepr='<merge>')
 
 @predicate('branchpoint()', safe=True)