D231: httppeer: add support for httppostargs when we're sending a file

2017-08-04 Thread martinvonz (Martin von Zweigbergk)
martinvonz added inline comments.

INLINE COMMENTS

> httppeer.py:95
> +raise ValueError(
> +'fileprepender only supports file objects that '
> +'have a length but this one does not:', type(f), f)

I'm guessing 'fileprepender' here and a few placed below is the old name for 
'_multifile' (so please update)

> httppeer.py:96
> +'fileprepender only supports file objects that '
> +'have a length but this one does not:', type(f), f)
> +self._fileobjs = fileobjs

It doesn't matter much since it's just a programming error if it happens, but 
how will these arguments to ValueError be rendered?

> httppeer.py:105
> +def read(self, amt=None):
> +if not amt:
> +return ''.join(f.read() for f in self._fileobjs)

i read that negative amount means the same thing (read all). do we care to 
follow that contract here?

> httppeer.py:111
> +got = len(parts[-1])
> +if got < amt:
> +self._index += 1

nit: i think this can be "if got <= amt" to avoid an unnecessary 0-length read 
the next time _multifile.read() (and it does look like that will be the normal 
case, that the reader will read exactly the size of the first "file" first)

> httppeer.py:125
> +'could be fixed if you need it')
> +for f in self._fileobjs:
> +f.seek(0)

also need to set self._index=0, no?

> httppeer.py:193
>  strargs = urlreq.urlencode(sorted(args.items()))
>  if strargs:
>  if not data:

Nit: to reduce indentation, it looks like you can change the condition above to 
"if postargsok and args" instead (because bool(strargs) == bool(args) AFAICT)

REPOSITORY
  rHG Mercurial

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

To: durin42, #hg-reviewers
Cc: martinvonz, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH] contrib: add check for use of admonitions and its validity

2017-08-04 Thread Rishabh Madan
# HG changeset patch
# User Rishabh Madan 
# Date 1501903168 -19800
#  Sat Aug 05 08:49:28 2017 +0530
# Node ID cc695f1458baf2c39ffdec4a1256a93fd659d62d
# Parent  609606d217659e0a6c1cf6f907b6512be5340e57
contrib: add check for use of admonitions and its validity

While using releasenotes extension, we will be using admonitions in commit 
messages.
This check will look for an admonition within the message. If it exists, it will
verify if it is stated under default or custom admonition. The check fails if 
the
admonition is not present in any of them.

diff -r 609606d21765 -r cc695f1458ba contrib/check-commit
--- a/contrib/check-commit  Thu Jul 20 01:30:41 2017 -0700
+++ b/contrib/check-commit  Sat Aug 05 08:49:28 2017 +0530
@@ -21,9 +21,16 @@
 import re
 import sys
 
+from mercurial import (
+config,
+hg,
+ui as uimod,
+)
+
 commitheader = r"^(?:# [^\n]*\n)*"
 afterheader = commitheader + r"(?!#)"
 beforepatch = afterheader + r"(?!\n(?!@@))"
+admonitioncheck = r"\.\. ([a-zA-Z0-9_]+)::"
 
 errors = [
 (beforepatch + r".*[(]bc[)]", "(BC) needs to be uppercase"),
@@ -59,6 +66,7 @@
 exitcode = 0
 printed = node is None
 hits = []
+exitcode = checkadmonition(commit, node)
 signtag = (afterheader +
   r'Added (tag [^ ]+|signature) for changeset [a-f0-9]{12}')
 if re.search(signtag, commit):
@@ -95,13 +103,38 @@
 def readcommit(node):
 return os.popen("hg export %s" % node).read()
 
+def getcustomadmonitions(node):
+repo = hg.repository(uimod.ui(), '.')
+ctx = repo[node]
+p = config.config()
+
+def read(f, sections=None, remap=None):
+if f in ctx:
+data = ctx[f].data()
+p.parse(f, data, sections, remap, read)
+
+if '.hgreleasenotes' in ctx:
+read('.hgreleasenotes')
+return p['sections'].keys()
+
+def checkadmonition(commit, node=None):
+admonitions = ["fix", "feature", "bc", "api", "perf"]
+x = re.search(admonitioncheck, commit)
+if x:
+if node:
+admonitions += getcustomadmonitions(node)
+if x.group(1) in admonitions:
+return 0
+else:
+print("admonition: %s is invalid" % x.group(1))
+return 1
+
 if __name__ == "__main__":
 exitcode = 0
 node = os.environ.get("HG_NODE")
-
 if node:
 commit = readcommit(node)
-exitcode = checkcommit(commit)
+exitcode = checkcommit(commit, node)
 elif sys.argv[1:]:
 for node in sys.argv[1:]:
 exitcode |= checkcommit(readcommit(node), node)
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 1 of 2 v8] releasenotes: add import check for fuzzywuzzy

2017-08-04 Thread Rishabh Madan
# HG changeset patch
# User Rishabh Madan 
# Date 1501317386 -19800
#  Sat Jul 29 14:06:26 2017 +0530
# Node ID 1d79b04c402f3f431ca052b677b1021ddd93a10e
# Parent  9a944e908ecf9ac3aabf30a24406513370eeebe7
releasenotes: add import check for fuzzywuzzy

This patch adds the has_fuzzywuzzy for import check of external dependency
fuzzywuzzy.

diff -r 9a944e908ecf -r 1d79b04c402f tests/hghave.py
--- a/tests/hghave.py   Tue Jul 18 23:04:08 2017 +0530
+++ b/tests/hghave.py   Sat Jul 29 14:06:26 2017 +0530
@@ -652,3 +652,12 @@
 @check("fsmonitor", "running tests with fsmonitor")
 def has_fsmonitor():
 return 'HGFSMONITOR_TESTS' in os.environ
+
+@check("fuzzywuzzy", "Fuzzy string matching library")
+def has_fuzzywuzzy():
+try:
+import fuzzywuzzy
+fuzzywuzzy.__version__
+return True
+except ImportError:
+return False
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 2 of 2 v8] releasenotes: add similarity check function to compare incoming notes

2017-08-04 Thread Rishabh Madan
# HG changeset patch
# User Rishabh Madan 
# Date 1501890936 -19800
#  Sat Aug 05 05:25:36 2017 +0530
# Node ID 4121eab826799f3a257eb1fe26015583b36bbb66
# Parent  1d79b04c402f3f431ca052b677b1021ddd93a10e
releasenotes: add similarity check function to compare incoming notes

It is possible that the incoming note fragments have some similar content as the
existing release notes. In case of a bug fix, we match for issue in the
existing notes. For other general cases, it makes use of fuzzywuzzy library to 
get
a similarity score. If the score is above a certain threshold, we ignore the
fragment, otherwise add it. But the score might be misleading for small commit
messages. So, it uses similarity function only if the length of string (in 
words)
is above a certain value. The patch adds tests related to its usage. But it 
needs
improvement in the sense of combining incoming notes. We can use interactive 
mode
for adding notes. Maybe we can do this if similarity is under a certain range.

diff -r 1d79b04c402f -r 4121eab82679 hgext/releasenotes.py
--- a/hgext/releasenotes.py Sat Jul 29 14:06:26 2017 +0530
+++ b/hgext/releasenotes.py Sat Aug 05 05:25:36 2017 +0530
@@ -14,6 +14,7 @@
 from __future__ import absolute_import
 
 import errno
+import fuzzywuzzy.fuzz as fuzz
 import re
 import sys
 import textwrap
@@ -46,6 +47,7 @@
 ]
 
 RE_DIRECTIVE = re.compile('^\.\. ([a-zA-Z0-9_]+)::\s*([^$]+)?$')
+RE_ISSUE = r'\bissue ?[0-9]{4,6}(?![0-9])\b'
 
 BULLET_SECTION = _('Other Changes')
 
@@ -92,6 +94,8 @@
 This is used to combine multiple sources of release notes together.
 """
 for section in other:
+existingnotes = converttitled(self.titledforsection(section)) + \
+convertnontitled(self.nontitledforsection(section))
 for title, paragraphs in other.titledforsection(section):
 if self.hastitledinsection(section, title):
 # TODO prompt for resolution if different and running in
@@ -100,16 +104,32 @@
  (title, section))
 continue
 
-# TODO perform similarity comparison and try to match against
-# existing.
+incoming_str = converttitled([(title, paragraphs)])[0]
+if section == 'fix':
+issue = getissuenum(incoming_str)
+if issue:
+if findissue(ui, existingnotes, issue):
+continue
+
+if similar(ui, existingnotes, incoming_str):
+continue
+
 self.addtitleditem(section, title, paragraphs)
 
 for paragraphs in other.nontitledforsection(section):
 if paragraphs in self.nontitledforsection(section):
 continue
 
-# TODO perform similarily comparison and try to match against
-# existing.
+incoming_str = convertnontitled([paragraphs])[0]
+if section == 'fix':
+issue = getissuenum(incoming_str)
+if issue:
+if findissue(ui, existingnotes, issue):
+continue
+
+if similar(ui, existingnotes, incoming_str):
+continue
+
 self.addnontitleditem(section, paragraphs)
 
 class releasenotessections(object):
@@ -136,6 +156,78 @@
 
 return None
 
+def converttitled(titledparagraphs):
+"""
+Convert titled paragraphs to strings
+"""
+string_list = []
+for title, paragraphs in titledparagraphs:
+lines = []
+for para in paragraphs:
+lines.extend(para)
+string_list.append(' '.join(lines))
+return string_list
+
+def convertnontitled(nontitledparagraphs):
+"""
+Convert non-titled bullets to strings
+"""
+string_list = []
+for paragraphs in nontitledparagraphs:
+lines = []
+for para in paragraphs:
+lines.extend(para)
+string_list.append(' '.join(lines))
+return string_list
+
+def getissuenum(incoming_str):
+"""
+Returns issue number from the incoming string if it exists
+"""
+issue = re.search(RE_ISSUE, incoming_str, re.IGNORECASE)
+if issue:
+issue = issue.group()
+return issue
+
+
+def findissue(ui, existing, issue):
+"""
+Returns true if issue number already exists in notes.
+"""
+if any(issue in s for s in existing):
+ui.write(_("\"%s\" already exists in notes; "
+ "ignoring\n") % issue)
+return True
+else:
+return False
+
+def similar(ui, existing, incoming_str):
+"""
+Returns true if similar note found in existing notes.
+"""
+if len(incoming_str.split()) > 10:
+merge = similaritycheck(incoming_str, existing)
+if not merge:
+ui.write(_("\"%s\" already 

Re: [PATCH 2 of 2 v7] releasenotes: add similarity check function to compare incoming notes

2017-08-04 Thread Rishabh Madan
On Thu, Aug 3, 2017 at 6:49 PM, Yuya Nishihara  wrote:

> On Tue, 01 Aug 2017 16:37:42 +0530, Rishabh Madan wrote:
> > # HG changeset patch
> > # User Rishabh Madan 
> > # Date 1501585562 -19800
> > #  Tue Aug 01 16:36:02 2017 +0530
> > # Node ID be3b6b414d443412d5df53ddbf3e80192b6311cc
> > # Parent  1d79b04c402f3f431ca052b677b1021ddd93a10e
> > releasenotes: add similarity check function to compare incoming notes
>
> The series seems getting better, thanks.
>
> > -# TODO perform similarity comparison and try to match
> against
> > -# existing.
> > +incoming_str = converttitled([(title, paragraphs)])[0]
>
> > +if section == 'fix':
> > +issue = getissuenum(incoming_str)
> > +if issue:
> > +if findissue(ui, existingnotes, issue):
> > +continue
> > +
> > +if similar(ui, existingnotes, incoming_str):
> > +continue
>
> > +
> >  self.addtitleditem(section, title, paragraphs)
> >
> >  for paragraphs in other.nontitledforsection(section):
> >  if paragraphs in self.nontitledforsection(section):
> >  continue
> >
> > -# TODO perform similarily comparison and try to match
> against
> > -# existing.
> > +incoming_str = convertnontitled([paragraphs])[0]
>
> > +if section == 'fix':
> > +issue = getissuenum(incoming_str)
> > +if issue:
> > +if findissue(ui, existingnotes, issue):
> > +continue
> > +
> > +if similar(ui, existingnotes, incoming_str):
> > +continue
>
> Optionally these similarity comparison logic could be factored out to a
> helper
> function or method.
>
I think that rendering them to a helper function would lead us to the same
problem of managing titled/non-titled notes.

>
> > +def converttitled(titledparagraphs):
> > +"""
> > +Convert titled paragraphs to strings
> > +"""
> > +string_list = []
> > +str = ""
> > +for title, paragraphs in titledparagraphs:
> > +for para in paragraphs:
> > +str = str.join(para)
> > +string_list.append(str)
> > +return string_list
>
> str was initially a separator, but is overwritten by the result. Perhaps
> this function needs to join a list of all lines by ' ' or '\n' ?
>
>   lines = []
>   for para in paragraphs:
>   lines.extend(para)
>   string_list.append(' '.join(lines))
>
> Thanks for pointing out. I'll correct this thing.

> > +def convertnontitled(nontitledparagraphs):
> > +"""
> > +Convert non-titled bullets to strings
> > +"""
> > +string_list = []
> > +str = ""
> > +for paragraphs in nontitledparagraphs:
> > +for para in paragraphs:
> > +str = str.join(para)
> > +string_list.append(str)
> > +return string_list
>
> Same here.

ᐧ
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D127: phabricator: add phabupdate command to update status in batch

2017-08-04 Thread quark (Jun Wu)
quark updated this revision to Diff 572.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D127?vs=557=572

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

AFFECTED FILES
  contrib/phabricator.py

CHANGE DETAILS

diff --git a/contrib/phabricator.py b/contrib/phabricator.py
--- a/contrib/phabricator.py
+++ b/contrib/phabricator.py
@@ -9,7 +9,7 @@
 This extension provides a ``phabsend`` command which sends a stack of
 changesets to Phabricator without amending commit messages, and a ``phabread``
 command which prints a stack of revisions in a format suitable
-for :hg:`import`.
+for :hg:`import`, and a ``phabupdate`` command to update statuses in batch.
 
 By default, Phabricator requires ``Test Plan`` which might prevent some
 changeset from being sent. The requirement could be disabled by changing
@@ -799,3 +799,32 @@
 spec = ':(%s)' % spec
 drevs = querydrev(repo, spec)
 readpatch(repo, drevs, ui.write)
+
+@command('phabupdate',
+ [('', 'accept', False, _('accept revisions')),
+  ('', 'reject', False, _('reject revisions')),
+  ('', 'abandon', False, _('abandon revisions')),
+  ('', 'reclaim', False, _('reclaim revisions')),
+  ('m', 'comment', '', _('comment on the last revision')),
+ ], _('DREVSPEC [OPTIONS]'))
+def phabupdate(ui, repo, spec, **opts):
+"""update Differential Revision in batch
+
+DREVSPEC selects revisions. See :hg:`help phabread` for its usage.
+"""
+flags = [n for n in 'accept reject abandon reclaim'.split() if opts.get(n)]
+if len(flags) > 1:
+raise error.Abort(_('%s cannot be used together') % ', '.join(flags))
+
+actions = []
+for f in flags:
+actions.append({'type': f, 'value': 'true'})
+
+drevs = querydrev(repo, spec)
+for i, drev in enumerate(drevs):
+if i + 1 == len(drevs) and opts.get('comment'):
+actions.append({'type': 'comment', 'value': opts['comment']})
+if actions:
+params = {'objectIdentifier': drev[r'phid'],
+  'transactions': actions}
+callconduit(repo, 'differential.revision.edit', params)



To: quark, #hg-reviewers
Cc: krbullock, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D124: phabricator: change "readpatch" to be more flexible

2017-08-04 Thread quark (Jun Wu)
quark updated this revision to Diff 569.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D124?vs=554=569

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

AFFECTED FILES
  contrib/phabricator.py

CHANGE DETAILS

diff --git a/contrib/phabricator.py b/contrib/phabricator.py
--- a/contrib/phabricator.py
+++ b/contrib/phabricator.py
@@ -636,15 +636,12 @@
 meta[r'parent'] = commit[r'parents'][0]
 return meta or {}
 
-def readpatch(repo, params, write, stack=False):
+def readpatch(repo, drevs, write):
 """generate plain-text patch readable by 'hg import'
 
-write is usually ui.write. params is passed to "differential.query". If
-stack is True, also write dependent patches.
+write is usually ui.write. drevs is what "querydrev" returns, results of
+"differential.query".
 """
-# Differential Revisions
-drevs = querydrev(repo, params, stack)
-
 # Prefetch hg:meta property for all diffs
 diffids = sorted(set(max(int(v) for v in drev[r'diffs']) for drev in 
drevs))
 diffs = callconduit(repo, 'differential.querydiffs', {'ids': diffids})
@@ -684,4 +681,5 @@
 revid = int(revid.split('/')[-1].replace('D', ''))
 except ValueError:
 raise error.Abort(_('invalid Revision ID: %s') % revid)
-readpatch(repo, {'ids': [revid]}, ui.write, opts.get('stack'))
+drevs = querydrev(repo, {'ids': [revid]}, opts.get('stack'))
+readpatch(repo, drevs, ui.write)



To: quark, #hg-reviewers, mitrandir
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D126: phabricator: add status to revision query language

2017-08-04 Thread quark (Jun Wu)
quark updated this revision to Diff 571.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D126?vs=556=571

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

AFFECTED FILES
  contrib/phabricator.py

CHANGE DETAILS

diff --git a/contrib/phabricator.py b/contrib/phabricator.py
--- a/contrib/phabricator.py
+++ b/contrib/phabricator.py
@@ -489,6 +489,13 @@
 
 return True
 
+_knownstatusnames = {'accepted', 'needsreview', 'needsrevision', 'closed',
+ 'abandoned'}
+
+def _getstatusname(drev):
+"""get normalized status name from a Differential Revision"""
+return drev[r'statusName'].replace(' ', '').lower()
+
 # Small language to specify differential revisions. Support symbols: (), :X,
 # +, and -.
 
@@ -505,19 +512,19 @@
 }
 
 def _tokenize(text):
-text = text.replace(' ', '') # remove space
 view = memoryview(text) # zero-copy slice
-special = '():+-&'
+special = '():+-& '
 pos = 0
 length = len(text)
 while pos < length:
 symbol = ''.join(itertools.takewhile(lambda ch: ch not in special,
  view[pos:]))
 if symbol:
 yield ('symbol', symbol, pos)
 pos += len(symbol)
-else: # special char
-yield (text[pos], None, pos)
+else: # special char, ignore space
+if text[pos] != ' ':
+yield (text[pos], None, pos)
 pos += 1
 yield ('end', None, pos)
 
@@ -645,15 +652,19 @@
 tofetch.update(range(max(1, r - batchsize), r + 1))
 if drevs:
 fetch({r'ids': list(tofetch)})
-getstack(list(ancestordrevs))
+validids = sorted(set(getstack(list(ancestordrevs))) | set(drevs))
 
 # Walk through the tree, return smartsets
 def walk(tree):
 op = tree[0]
 if op == 'symbol':
 drev = _parsedrev(tree[1])
 if drev:
 return smartset.baseset([drev])
+elif tree[1] in _knownstatusnames:
+drevs = [r for r in validids
+ if _getstatusname(prefetched[r]) == tree[1]]
+return smartset.baseset(drevs)
 else:
 raise error.Abort(_('unknown symbol: %s') % tree[1])
 elif op in {'and_', 'add', 'sub'}:
@@ -773,8 +784,13 @@
 ``&``, ``(``, ``)`` for complex queries. Prefix ``:`` could be used to
 select a stack.
 
+``abandoned``, ``accepted``, ``closed``, ``needsreview``, ``needsrevision``
+could be used to filter patches by status. For performance reason, they
+only represent a subset of non-status selections and cannot be used alone.
+
 For example, ``:D6+8-(2+D4)`` selects a stack up to D6, plus D8 and exclude
-D2 and D4.
+D2 and D4. ``:D9 & needsreview`` selects "Needs Review" revisions in a
+stack up to D9.
 
 If --stack is given, follow dependencies information and read all patches.
 It is equivalent to the ``:`` operator.



To: quark, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D122: phabricator: add --amend option to phabsend

2017-08-04 Thread quark (Jun Wu)
quark updated this revision to Diff 568.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D122?vs=553=568

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

AFFECTED FILES
  contrib/phabricator.py

CHANGE DETAILS

diff --git a/contrib/phabricator.py b/contrib/phabricator.py
--- a/contrib/phabricator.py
+++ b/contrib/phabricator.py
@@ -38,6 +38,8 @@
 from mercurial.node import bin, nullid
 from mercurial.i18n import _
 from mercurial import (
+cmdutil,
+context,
 encoding,
 error,
 mdiff,
@@ -316,7 +318,7 @@
 if not revision:
 raise error.Abort(_('cannot create revision for %s') % ctx)
 
-return revision
+return revision, diff
 
 def userphids(repo, names):
 """convert user names to PHIDs"""
@@ -334,6 +336,7 @@
 
 @command('phabsend',
  [('r', 'rev', [], _('revisions to send'), _('REV')),
+  ('', 'amend', False, _('update commit messages')),
   ('', 'reviewer', [], _('specify reviewers')),
   ('', 'confirm', None, _('ask for confirmation before sending'))],
  _('REV [OPTIONS]'))
@@ -349,18 +352,28 @@
 obsstore and tags information so it can figure out whether to update an
 existing Differential Revision, or create a new one.
 
+If --amend is set, update commit messages so they have the
+``Differential Revision`` URL, remove related tags. This is similar to what
+arcanist will do, and is more desired in author-push workflows. Otherwise,
+use local tags to record the ``Differential Revision`` association.
+
 The --confirm option lets you confirm changesets before sending them. You
 can also add following to your configuration file to make it default
 behaviour.
 
 [phabsend]
 confirm = true
+
+phabsend will check obsstore and the above association to decide whether to
+update an existing Differential Revision, or create a new one.
 """
 revs = list(revs) + opts.get('rev', [])
 revs = scmutil.revrange(repo, revs)
 
 if not revs:
 raise error.Abort(_('phabsend requires at least one changeset'))
+if opts.get('amend'):
+cmdutil.checkunfinished(repo)
 
 confirm = ui.configbool('phabsend', 'confirm')
 confirm |= bool(opts.get('confirm'))
@@ -378,6 +391,9 @@
 # {newnode: (oldnode, olddiff, olddrev}
 oldmap = getoldnodedrevmap(repo, [repo[r].node() for r in revs])
 
+drevids = [] # [int]
+diffmap = {} # {newnode: diff}
+
 # Send patches one by one so we know their Differential Revision IDs and
 # can provide dependency relationship
 lastrevid = None
@@ -389,28 +405,67 @@
 oldnode, olddiff, revid = oldmap.get(ctx.node(), (None, None, None))
 if oldnode != ctx.node():
 # Create or update Differential Revision
-revision = createdifferentialrevision(ctx, revid, lastrevid,
-  oldnode, olddiff, actions)
+revision, diff = createdifferentialrevision(
+ctx, revid, lastrevid, oldnode, olddiff, actions)
+diffmap[ctx.node()] = diff
 newrevid = int(revision[r'object'][r'id'])
 if revid:
 action = _('updated')
 else:
 action = _('created')
 
-# Create a local tag to note the association
-tagname = 'D%d' % newrevid
-tags.tag(repo, tagname, ctx.node(), message=None, user=None,
- date=None, local=True)
+# Create a local tag to note the association, if commit message
+# does not have it already
+m = _differentialrevisiondescre.search(ctx.description())
+if not m or int(m.group(1)) != newrevid:
+tagname = 'D%d' % newrevid
+tags.tag(repo, tagname, ctx.node(), message=None, user=None,
+ date=None, local=True)
 else:
 # Nothing changed. But still set "newrevid" so the next revision
 # could depend on this one.
 newrevid = revid
 action = _('skipped')
 
 ui.write(_('D%s: %s - %s: %s\n') % (newrevid, action, ctx,
 ctx.description().split('\n')[0]))
+drevids.append(newrevid)
 lastrevid = newrevid
 
+# Update commit messages and remove tags
+if opts.get('amend'):
+unfi = repo.unfiltered()
+drevs = callconduit(repo, 'differential.query', {'ids': drevids})
+with repo.wlock(), repo.lock(), repo.transaction('phabsend'):
+wnode = unfi['.'].node()
+mapping = {} # {oldnode: [newnode]}
+for i, rev in enumerate(revs):
+old = unfi[rev]
+drevid = drevids[i]
+drev = [d for d in drevs if int(d[r'id']) == drevid][0]
+newdesc = getdescfromdrev(drev)
+# Make sure commit message contain 

D125: phabricator: add a small language to query Differential Revisions

2017-08-04 Thread quark (Jun Wu)
quark updated this revision to Diff 570.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D125?vs=555=570

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

AFFECTED FILES
  contrib/phabricator.py

CHANGE DETAILS

diff --git a/contrib/phabricator.py b/contrib/phabricator.py
--- a/contrib/phabricator.py
+++ b/contrib/phabricator.py
@@ -32,7 +32,9 @@
 
 from __future__ import absolute_import
 
+import itertools
 import json
+import operator
 import re
 
 from mercurial.node import bin, nullid
@@ -44,9 +46,11 @@
 error,
 mdiff,
 obsolete,
+parser,
 patch,
 registrar,
 scmutil,
+smartset,
 tags,
 url as urlmod,
 util,
@@ -485,11 +489,77 @@
 
 return True
 
-def querydrev(repo, params, stack=False):
+# Small language to specify differential revisions. Support symbols: (), :X,
+# +, and -.
+
+_elements = {
+# token-type: binding-strength, primary, prefix, infix, suffix
+'(':  (12, None, ('group', 1, ')'), None, None),
+':':  (8, None, ('ancestors', 8), None, None),
+'&':  (5,  None, None, ('and_', 5), None),
+'+':  (4,  None, None, ('add', 4), None),
+'-':  (4,  None, None, ('sub', 4), None),
+')':  (0,  None, None, None, None),
+'symbol': (0, 'symbol', None, None, None),
+'end':(0, None, None, None, None),
+}
+
+def _tokenize(text):
+text = text.replace(' ', '') # remove space
+view = memoryview(text) # zero-copy slice
+special = '():+-&'
+pos = 0
+length = len(text)
+while pos < length:
+symbol = ''.join(itertools.takewhile(lambda ch: ch not in special,
+ view[pos:]))
+if symbol:
+yield ('symbol', symbol, pos)
+pos += len(symbol)
+else: # special char
+yield (text[pos], None, pos)
+pos += 1
+yield ('end', None, pos)
+
+def _parse(text):
+tree, pos = parser.parser(_elements).parse(_tokenize(text))
+if pos != len(text):
+raise error.ParseError('invalid token', pos)
+return tree
+
+def _parsedrev(symbol):
+"""str -> int or None, ex. 'D45' -> 45; '12' -> 12; 'x' -> None"""
+if symbol.startswith('D') and symbol[1:].isdigit():
+return int(symbol[1:])
+if symbol.isdigit():
+return int(symbol)
+
+def _prefetchdrevs(tree):
+"""return ({single-drev-id}, {ancestor-drev-id}) to prefetch"""
+drevs = set()
+ancestordrevs = set()
+op = tree[0]
+if op == 'symbol':
+r = _parsedrev(tree[1])
+if r:
+drevs.add(r)
+elif op == 'ancestors':
+r, a = _prefetchdrevs(tree[1])
+drevs.update(r)
+ancestordrevs.update(r)
+ancestordrevs.update(a)
+else:
+for t in tree[1:]:
+r, a = _prefetchdrevs(t)
+drevs.update(r)
+ancestordrevs.update(a)
+return drevs, ancestordrevs
+
+def querydrev(repo, spec):
 """return a list of "Differential Revision" dicts
 
-params is the input of "differential.query" API, and is expected to match
-just a single Differential Revision.
+spec is a string using a simple query language, see docstring in phabread
+for details.
 
 A "Differential Revision dict" looks like:
 
@@ -526,51 +596,77 @@
 "repositoryPHID": "PHID-REPO-hub2hx62ieuqeheznasv",
 "sourcePath": null
 }
-
-If stack is True, return a list of "Differential Revision dict"s in an
-order that the latter ones depend on the former ones. Otherwise, return a
-list of a unique "Differential Revision dict".
 """
-prefetched = {} # {id or phid: drev}
 def fetch(params):
 """params -> single drev or None"""
 key = (params.get(r'ids') or params.get(r'phids') or [None])[0]
 if key in prefetched:
 return prefetched[key]
-# Otherwise, send the request. If we're fetching a stack, be smarter
-# and fetch more ids in one batch, even if it could be unnecessary.
-batchparams = params
-if stack and len(params.get(r'ids', [])) == 1:
-i = int(params[r'ids'][0])
-# developer config: phabricator.batchsize
-batchsize = repo.ui.configint('phabricator', 'batchsize', 12)
-batchparams = {'ids': range(max(1, i - batchsize), i + 1)}
-drevs = callconduit(repo, 'differential.query', batchparams)
+drevs = callconduit(repo, 'differential.query', params)
 # Fill prefetched with the result
 for drev in drevs:
 prefetched[drev[r'phid']] = drev
 prefetched[int(drev[r'id'])] = drev
 if key not in prefetched:
 raise error.Abort(_('cannot get Differential Revision %r') % 
params)
 return prefetched[key]
 
-visited = set()
-result = []
-queue = [params]
-while queue:
-params = queue.pop()
-drev = fetch(params)
-   

D210: pushvars: move fb extension pushvars to core

2017-08-04 Thread pulkit (Pulkit Goyal)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGdb3dc11356ed: pushvars: move fb extension pushvars to core 
(authored by pulkit).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D210?vs=514=567

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

AFFECTED FILES
  mercurial/bundle2.py
  mercurial/commands.py
  mercurial/exchange.py
  tests/test-completion.t
  tests/test-pushvars.t

CHANGE DETAILS

diff --git a/tests/test-pushvars.t b/tests/test-pushvars.t
new file mode 100644
--- /dev/null
+++ b/tests/test-pushvars.t
@@ -0,0 +1,71 @@
+Setup
+
+  $ PYTHONPATH=$TESTDIR/..:$PYTHONPATH
+  $ export PYTHONPATH
+
+  $ cat > $TESTTMP/pretxnchangegroup.sh << EOF
+  > #!/bin/sh
+  > env | egrep "^HG_USERVAR_(DEBUG|BYPASS_REVIEW)" | sort
+  > exit 0
+  > EOF
+  $ chmod +x $TESTTMP/pretxnchangegroup.sh
+  $ cat >> $HGRCPATH << EOF
+  > [hooks]
+  > pretxnchangegroup = $TESTTMP/pretxnchangegroup.sh
+  > [experimental]
+  > bundle2-exp = true
+  > EOF
+
+  $ hg init repo
+  $ hg clone -q repo child
+  $ cd child
+
+Test pushing vars to repo with pushvars.server not set
+
+  $ echo b > a
+  $ hg commit -Aqm a
+  $ hg push --pushvars "DEBUG=1" --pushvars "BYPASS_REVIEW=true"
+  pushing to $TESTTMP/repo (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+
+Setting pushvars.sever = true and then pushing.
+
+  $ echo [push] >> $HGRCPATH
+  $ echo "pushvars.server = true" >> $HGRCPATH
+  $ echo b >> a
+  $ hg commit -Aqm a
+  $ hg push --pushvars "DEBUG=1" --pushvars "BYPASS_REVIEW=true"
+  pushing to $TESTTMP/repo (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  HG_USERVAR_BYPASS_REVIEW=true
+  HG_USERVAR_DEBUG=1
+
+Test pushing var with empty right-hand side
+
+  $ echo b >> a
+  $ hg commit -Aqm a
+  $ hg push --pushvars "DEBUG="
+  pushing to $TESTTMP/repo (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  HG_USERVAR_DEBUG=
+
+Test pushing bad vars
+
+  $ echo b >> a
+  $ hg commit -Aqm b
+  $ hg push --pushvars "DEBUG"
+  pushing to $TESTTMP/repo (glob)
+  abort: unable to parse variable 'DEBUG', should follow 'KEY=VALUE' or 'KEY=' 
format
+  [255]
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -228,7 +228,7 @@
   log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, 
user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, 
style, template, include, exclude
   merge: force, rev, preview, tool
   pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
-  push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
+  push: force, rev, bookmark, branch, new-branch, pushvars, ssh, remotecmd, 
insecure
   remove: after, force, subrepos, include, exclude
   serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, 
name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, 
ipv6, certificate, subrepos
   status: all, modified, added, removed, deleted, clean, unknown, ignored, 
no-status, terse, copies, print0, rev, change, include, exclude, subrepos, 
template
diff --git a/mercurial/exchange.py b/mercurial/exchange.py
--- a/mercurial/exchange.py
+++ b/mercurial/exchange.py
@@ -893,6 +893,14 @@
 pushop.bkresult = 1
 return handlereply
 
+@b2partsgenerator('pushvars', idx=0)
+def _getbundlesendvars(pushop, bundler):
+'''send shellvars via bundle2'''
+if getattr(pushop.repo, '_shellvars', ()):
+part = bundler.newpart('pushvars')
+
+for key, value in pushop.repo._shellvars.iteritems():
+part.addparam(key, value, mandatory=False)
 
 def _pushbundle2(pushop):
 """push data to the remote using bundle2
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -3970,6 +3970,7 @@
 ('b', 'branch', [],
  _('a specific branch you would like to push'), _('BRANCH')),
 ('', 'new-branch', False, _('allow pushing a new branch')),
+('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
 ] + remoteopts,
 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
 def push(ui, repo, dest=None, **opts):
@@ -4007,6 +4008,25 @@
 Please see :hg:`help urls` for important details about ``ssh://``
 URLs. If DESTINATION is omitted, a default path will be used.
 
+.. container:: verbose
+
+The --pushvars option sends strings to the server that become
+environment variables prepended with ``HG_USERVAR_``. For example,
+``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
+

D229: phabricator: update diff property even if we choose not to create a new diff

2017-08-04 Thread quark (Jun Wu)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGf100354cce52: phabricator: update diff property even if we 
choose not to create a new diff (authored by quark).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D229?vs=552=566

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

AFFECTED FILES
  contrib/phabricator.py

CHANGE DETAILS

diff --git a/contrib/phabricator.py b/contrib/phabricator.py
--- a/contrib/phabricator.py
+++ b/contrib/phabricator.py
@@ -255,7 +255,7 @@
 callconduit(ctx.repo(), 'differential.setdiffproperty', params)
 
 def createdifferentialrevision(ctx, revid=None, parentrevid=None, oldnode=None,
-   actions=None):
+   olddiff=None, actions=None):
 """create or update a Differential Revision
 
 If revid is None, create a new Differential Revision, otherwise update
@@ -279,6 +279,13 @@
 diff = creatediff(ctx)
 writediffproperties(ctx, diff)
 transactions.append({'type': 'update', 'value': diff[r'phid']})
+else:
+# Even if we don't need to upload a new diff because the patch content
+# does not change. We might still need to update its metadata so
+# pushers could know the correct node metadata.
+assert olddiff
+diff = olddiff
+writediffproperties(ctx, diff)
 
 # Use a temporary summary to set dependency. There might be better ways but
 # I cannot find them for now. But do not do that if we are updating an
@@ -383,7 +390,7 @@
 if oldnode != ctx.node():
 # Create or update Differential Revision
 revision = createdifferentialrevision(ctx, revid, lastrevid,
-  oldnode, actions)
+  oldnode, olddiff, actions)
 newrevid = int(revision[r'object'][r'id'])
 if revid:
 action = _('updated')



To: quark, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D121: phabricator: use Phabricator's last node information

2017-08-04 Thread quark (Jun Wu)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG1664406a44d9: phabricator: use Phabricator's last node 
information (authored by quark).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D121?vs=550=565

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

AFFECTED FILES
  contrib/phabricator.py

CHANGE DETAILS

diff --git a/contrib/phabricator.py b/contrib/phabricator.py
--- a/contrib/phabricator.py
+++ b/contrib/phabricator.py
@@ -138,28 +138,32 @@
 
 _differentialrevisiontagre = re.compile('\AD([1-9][0-9]*)\Z')
 _differentialrevisiondescre = re.compile(
-'^Differential Revision:\s*(.*)D([1-9][0-9]*)$', re.M)
+'^Differential Revision:\s*(?:.*)D([1-9][0-9]*)$', re.M)
 
 def getoldnodedrevmap(repo, nodelist):
 """find previous nodes that has been sent to Phabricator
 
-return {node: (oldnode or None, Differential Revision ID)}
+return {node: (oldnode, Differential diff, Differential Revision ID)}
 for node in nodelist with known previous sent versions, or associated
-Differential Revision IDs.
+Differential Revision IDs. ``oldnode`` and ``Differential diff`` could
+be ``None``.
 
-Examines all precursors and their tags. Tags with format like "D1234" are
-considered a match and the node with that tag, and the number after "D"
-(ex. 1234) will be returned.
+Examines commit messages like "Differential Revision:" to get the
+association information.
 
-If tags are not found, examine commit message. The "Differential Revision:"
-line could associate this changeset to a Differential Revision.
+If such commit message line is not found, examines all precursors and their
+tags. Tags with format like "D1234" are considered a match and the node
+with that tag, and the number after "D" (ex. 1234) will be returned.
+
+The ``old node``, if not None, is guaranteed to be the last diff of
+corresponding Differential Revision, and exist in the repo.
 """
 url, token = readurltoken(repo)
 unfi = repo.unfiltered()
 nodemap = unfi.changelog.nodemap
 
-result = {} # {node: (oldnode or None, drev)}
-toconfirm = {} # {node: (oldnode, {precnode}, drev)}
+result = {} # {node: (oldnode?, lastdiff?, drev)}
+toconfirm = {} # {node: (force, {precnode}, drev)}
 for node in nodelist:
 ctx = unfi[node]
 # For tags like "D123", put them into "toconfirm" to verify later
@@ -169,39 +173,49 @@
 for tag in unfi.nodetags(n):
 m = _differentialrevisiontagre.match(tag)
 if m:
-toconfirm[node] = (n, set(precnodes), int(m.group(1)))
+toconfirm[node] = (0, set(precnodes), int(m.group(1)))
 continue
 
-# Check commit message (make sure URL matches)
+# Check commit message
 m = _differentialrevisiondescre.search(ctx.description())
 if m:
-if m.group(1).rstrip('/') == url.rstrip('/'):
-result[node] = (None, int(m.group(2)))
-else:
-unfi.ui.warn(_('%s: Differential Revision URL ignored - host '
-   'does not match config\n') % ctx)
+toconfirm[node] = (1, set(precnodes), int(m.group(1)))
 
 # Double check if tags are genuine by collecting all old nodes from
 # Phabricator, and expect precursors overlap with it.
 if toconfirm:
-confirmed = {} # {drev: {oldnode}}
-drevs = [drev for n, precs, drev in toconfirm.values()]
-diffs = callconduit(unfi, 'differential.querydiffs',
-{'revisionIDs': drevs})
-for diff in diffs.values():
-drev = int(diff[r'revisionID'])
-oldnode = bin(encoding.unitolocal(getdiffmeta(diff).get(r'node')))
-if node:
-confirmed.setdefault(drev, set()).add(oldnode)
-for newnode, (oldnode, precset, drev) in toconfirm.items():
-if bool(precset & confirmed.get(drev, set())):
-result[newnode] = (oldnode, drev)
-else:
+drevs = [drev for force, precs, drev in toconfirm.values()]
+alldiffs = callconduit(unfi, 'differential.querydiffs',
+   {'revisionIDs': drevs})
+getnode = lambda d: bin(encoding.unitolocal(
+getdiffmeta(d).get(r'node', ''))) or None
+for newnode, (force, precset, drev) in toconfirm.items():
+diffs = [d for d in alldiffs.values()
+ if int(d[r'revisionID']) == drev]
+
+# "precursors" as known by Phabricator
+phprecset = set(getnode(d) for d in diffs)
+
+# Ignore if precursors (Phabricator and local repo) do not overlap,
+# and force is not set (when commit message says nothing)
+if not force and not bool(phprecset & precset):
   

D219: morestatus: move fb extension to core as '--repo-state' option to status

2017-08-04 Thread durin42 (Augie Fackler)
durin42 requested changes to this revision.
durin42 added a comment.
This revision now requires changes to proceed.


  In https://phab.mercurial-scm.org/D219#3755, @martinvonz wrote:
  
  > In https://phab.mercurial-scm.org/D219#3661, @quark wrote:
  >
  > > I'm thinking about the difference between the deprecated `[defaults]` and 
`[commands]` (and also `[alias]`) but don't quite get it. It seems people 
wanting the behavior might just set `alias.status = status -v`, or use 
`[defaults]`. What's the advantage of using a `[commands]` option?
  >
  >
  > Good point, there's probably no good reason to use [commands] for this. 
[defaults] has been discouraged, but I don't know if there's a good reason to 
discourage it (I think it's already ignored when HGPLAIN is in effect).
  
  
  The discouragement is twofold: we didn't used to have HGPLAIN, and it used to 
be there was no way to un-do the setting of a boolean flag in [defaults]. Both 
of those limitations are now fixed, so I'm less worried about it overall. That 
said, I would like a config knob for this, so it can be part of 
ui.tweakdefaults.
  
  > [commands] make sense when there's no command line flag for the behavior, 
but we do have that here. So if we agree on using --verbose for this, I don't 
think I see a reason to a special config for it and we can use [defaults] 
instead.
  > 
  > Hmm, I just realized there is a somewhat good reason to avoid [defaults]: 
it applies to *all* options at once. Let me clarify with an example. Let's say 
your sysadmin has set "defaults.status = --relative" (hypothetical option -- 
it's currently a config in [commands]) and you want to keep that *and* get 
verbose status output, you now have to copy the default config from the system 
hgrc. I don't think I've heard that argument against [defaults] before, but 
that seems more relevant than the scripting argument (which, again, goes away 
with HGPLAIN). Or am I mistaken?
  
  That's another argument in favor of a config knob IMO.
  
  I'm not sure if --verbose should be (ab)used for this, given that we've got 
--terse. --verbose almost sounds like the opposite of --terse, so it might be 
confusing. Meditate on that, I guess.
  
  (Marking as "Request changes" for now, since we seem largely agreed that 
--verbose is a decent way to go, and if nobody else is worried about the 
conceptual overlap with --terse I'm fine with it, especially since I more or 
less expect this to be on in tweakdefaults soon.)

REPOSITORY
  rHG Mercurial

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

To: pulkit, #hg-reviewers, durin42
Cc: durin42, quark, akushner, martinvonz, durham, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D210: pushvars: move fb extension pushvars to core

2017-08-04 Thread durin42 (Augie Fackler)
durin42 accepted this revision.
durin42 added a comment.
This revision is now accepted and ready to land.


  I like this, it opens the doors to some things I've wanted to do for a while.

REPOSITORY
  rHG Mercurial

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

To: pulkit, #hg-reviewers, durin42
Cc: quark, durin42, akushner, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D216: evolution: rename bumped to phase-divergent

2017-08-04 Thread lothiraldan (Boris Feld)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG9c27a2891b75: evolution: rename bumped to phase-divergent 
(authored by lothiraldan).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D216?vs=499=561

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

AFFECTED FILES
  mercurial/commands.py
  mercurial/context.py
  mercurial/exchange.py
  tests/test-obsolete.t

CHANGE DETAILS

diff --git a/tests/test-obsolete.t b/tests/test-obsolete.t
--- a/tests/test-obsolete.t
+++ b/tests/test-obsolete.t
@@ -207,7 +207,7 @@
 
   $ hg --hidden phase --public 2
   $ hg log -G
-  @  5:5601fb93a350 (draft bumped) [tip ] add new_3_c
+  @  5:5601fb93a350 (draft phase-divergent) [tip ] add new_3_c
   |
   | o  2:245bde4270cd (public) [ ] add original_c
   |/
@@ -224,7 +224,7 @@
 the public changeset
 
   $ hg log --hidden -r 'bumped()'
-  5:5601fb93a350 (draft bumped) [tip ] add new_3_c
+  5:5601fb93a350 (draft phase-divergent) [tip ] add new_3_c
 
 And that we can't push bumped changeset
 
@@ -239,20 +239,20 @@
   $ hg push ../tmpa
   pushing to ../tmpa
   searching for changes
-  abort: push includes bumped changeset: 5601fb93a350!
+  abort: push includes phase-divergent changeset: 5601fb93a350!
   [255]
 
 Fixing "bumped" situation
 We need to create a clone of 5 and add a special marker with a flag
 
   $ hg summary
-  parent: 5:5601fb93a350 tip (bumped)
+  parent: 5:5601fb93a350 tip (phase-divergent)
add new_3_c
   branch: default
   commit: (clean)
   update: 1 new changesets, 2 branch heads (merge)
   phases: 1 draft
-  bumped: 1 changesets
+  phase-divergent: 1 changesets
   $ hg up '5^'
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg revert -ar 5
@@ -914,7 +914,7 @@
   changeset:   7:50c51b361e60
   user:test
   date:Thu Jan 01 00:00:00 1970 +
-  instability: orphan, bumped
+  instability: orphan, phase-divergent
   summary: add babar
   
 
@@ -926,15 +926,15 @@
 test the "troubles" templatekw
 
   $ hg log -r 'bumped() and unstable()'
-  7:50c51b361e60 (draft orphan bumped) [ ] add babar
+  7:50c51b361e60 (draft orphan phase-divergent) [ ] add babar
 
 test the default cmdline template
 
   $ hg log -T default -r 'bumped()'
   changeset:   7:50c51b361e60
   user:test
   date:Thu Jan 01 00:00:00 1970 +
-  trouble: orphan, bumped
+  trouble: orphan, phase-divergent
   summary: add babar
   
   $ hg log -T default -r 'obsolete()'
@@ -950,14 +950,14 @@
   $ hg up -r 'bumped() and unstable()'
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg summary
-  parent: 7:50c51b361e60  (orphan, bumped)
+  parent: 7:50c51b361e60  (orphan, phase-divergent)
add babar
   branch: default
   commit: (clean)
   update: 2 new changesets (update)
   phases: 4 draft
   orphan: 2 changesets
-  bumped: 1 changesets
+  phase-divergent: 1 changesets
   $ hg up -r 'obsolete()'
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg summary
@@ -968,7 +968,7 @@
   update: 3 new changesets (update)
   phases: 4 draft
   orphan: 2 changesets
-  bumped: 1 changesets
+  phase-divergent: 1 changesets
 
 Test incoming/outcoming with changesets obsoleted remotely, known locally
 ===
diff --git a/mercurial/exchange.py b/mercurial/exchange.py
--- a/mercurial/exchange.py
+++ b/mercurial/exchange.py
@@ -677,9 +677,10 @@
 if unfi.obsstore:
 # this message are here for 80 char limit reason
 mso = _("push includes obsolete changeset: %s!")
+mspd = _("push includes phase-divergent changeset: %s!")
 mscd = _("push includes content-divergent changeset: %s!")
 mst = {"orphan": _("push includes orphan changeset: %s!"),
-   "bumped": _("push includes bumped changeset: %s!"),
+   "phase-divergent": mspd,
"content-divergent": mscd}
 # If we are to push if there is at least one
 # obsolete or unstable changeset in missing, at
diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -230,14 +230,14 @@
 
 Troubles are returned as strings. possible values are:
 - orphan,
-- bumped,
+- phase-divergent,
 - content-divergent.
 """
 troubles = []
 if self.unstable():
 troubles.append('orphan')
 if self.bumped():
-troubles.append('bumped')
+troubles.append('phase-divergent')
 if self.divergent():
 troubles.append('content-divergent')
 return troubles
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -4943,7 +4943,7 @@
 troublemsg = {
"unstable": 

D215: evolution: rename divergent to content-divergent

2017-08-04 Thread lothiraldan (Boris Feld)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG2194a8723138: evolution: rename divergent to 
content-divergent (authored by lothiraldan).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D215?vs=498=560

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

AFFECTED FILES
  mercurial/commands.py
  mercurial/context.py
  mercurial/exchange.py
  tests/test-obsmarker-template.t
  tests/test-obsolete-divergent.t
  tests/test-rebase-obsolete.t

CHANGE DETAILS

diff --git a/tests/test-rebase-obsolete.t b/tests/test-rebase-obsolete.t
--- a/tests/test-rebase-obsolete.t
+++ b/tests/test-rebase-obsolete.t
@@ -855,7 +855,7 @@
   commit: (clean)
   update: 1 new changesets, 2 branch heads (merge)
   phases: 8 draft
-  divergent: 2 changesets
+  content-divergent: 2 changesets
 
 rebase --continue + skipped rev because their successors are in destination
 we make a change in trunk and work on conflicting changes to make rebase abort.
diff --git a/tests/test-obsolete-divergent.t b/tests/test-obsolete-divergent.t
--- a/tests/test-obsolete-divergent.t
+++ b/tests/test-obsolete-divergent.t
@@ -107,7 +107,7 @@
   $ hg push ../other
   pushing to ../other
   searching for changes
-  abort: push includes divergent changeset: 392fd25390da!
+  abort: push includes content-divergent changeset: 392fd25390da!
   [255]
 
   $ cd ..
@@ -671,9 +671,9 @@
   $ rm .hg/localtags
   $ hg cleanup --config extensions.t=$TESTTMP/scmutilcleanup.py
   $ hg log -G -T '{rev}:{node|short} {desc} {troubles}' -r 'sort(all(), topo)'
-  @  5:1a2a9b5b0030 B2 divergent
+  @  5:1a2a9b5b0030 B2 content-divergent
   |
-  | o  4:70d5a63ca112 B4 divergent
+  | o  4:70d5a63ca112 B4 content-divergent
   | |
   | o  1:48b9aae0607f Z
   |
diff --git a/tests/test-obsmarker-template.t b/tests/test-obsmarker-template.t
--- a/tests/test-obsmarker-template.t
+++ b/tests/test-obsmarker-template.t
@@ -442,14 +442,14 @@
   |  parent:  0:ea207398892e
   |  user:test
   |  date:Thu Jan 01 00:00:00 1970 +
-  |  instability: divergent
+  |  instability: content-divergent
   |  summary: A2
   |
   | o  changeset:   2:fdf9bde5129a
   |/   parent:  0:ea207398892e
   |user:test
   |date:Thu Jan 01 00:00:00 1970 +
-  |instability: divergent
+  |instability: content-divergent
   |summary: A1
   |
   | x  changeset:   1:471f378eab4c
@@ -469,7 +469,7 @@
   |  parent:  0:ea207398892e
   |  user:test
   |  date:Thu Jan 01 00:00:00 1970 +
-  |  instability: divergent
+  |  instability: content-divergent
   |  summary: A3
   |
   | x  changeset:   3:65b757b745b9
@@ -482,7 +482,7 @@
   |/   parent:  0:ea207398892e
   |user:test
   |date:Thu Jan 01 00:00:00 1970 +
-  |instability: divergent
+  |instability: content-divergent
   |summary: A1
   |
   | x  changeset:   1:471f378eab4c
@@ -1086,20 +1086,20 @@
   |  parent:  5:dd800401bd8c
   |  user:test
   |  date:Thu Jan 01 00:00:00 1970 +
-  |  instability: divergent
+  |  instability: content-divergent
   |  summary: Add B only
   |
   | o  changeset:   8:b18bc8331526
   |/   parent:  5:dd800401bd8c
   |user:test
   |date:Thu Jan 01 00:00:00 1970 +
-  |instability: divergent
+  |instability: content-divergent
   |summary: Add only B
   |
   | o  changeset:   7:ba2ed02b0c9a
   | |  user:test
   | |  date:Thu Jan 01 00:00:00 1970 +
-  | |  instability: orphan, divergent
+  | |  instability: orphan, content-divergent
   | |  summary: Add A,B,C
   | |
   | x  changeset:   6:4a004186e638
@@ -,7 +,7 @@
   |  parent:  3:f897c6137566
   |  user:test
   |  date:Thu Jan 01 00:00:00 1970 +
-  |  instability: divergent
+  |  instability: content-divergent
   |  summary: Add A,B,C
   |
   o  changeset:   3:f897c6137566
diff --git a/mercurial/exchange.py b/mercurial/exchange.py
--- a/mercurial/exchange.py
+++ b/mercurial/exchange.py
@@ -677,9 +677,10 @@
 if unfi.obsstore:
 # this message are here for 80 char limit reason
 mso = _("push includes obsolete changeset: %s!")
+mscd = _("push includes content-divergent changeset: %s!")
 mst = {"orphan": _("push includes orphan changeset: %s!"),
"bumped": _("push includes bumped changeset: %s!"),
-   "divergent": _("push includes divergent changeset: %s!")}
+   "content-divergent": mscd}
 # If we are to push if there is at least one
 # obsolete or unstable changeset in missing, at
 # least one of the missinghead will be obsolete or
diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -231,15 +231,15 @@
 Troubles are returned as 

D218: phabricator: add --confirm option to phabsend command

2017-08-04 Thread pulkit (Pulkit Goyal)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG40cfe3197bc1: phabricator: add --confirm option to phabsend 
command (authored by pulkit).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D218?vs=515=562

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

AFFECTED FILES
  contrib/phabricator.py

CHANGE DETAILS

diff --git a/contrib/phabricator.py b/contrib/phabricator.py
--- a/contrib/phabricator.py
+++ b/contrib/phabricator.py
@@ -313,7 +313,8 @@
 
 @command('phabsend',
  [('r', 'rev', [], _('revisions to send'), _('REV')),
-  ('', 'reviewer', [], _('specify reviewers'))],
+  ('', 'reviewer', [], _('specify reviewers')),
+  ('', 'confirm', None, _('ask for confirmation before sending'))],
  _('REV [OPTIONS]'))
 def phabsend(ui, repo, *revs, **opts):
 """upload changesets to Phabricator
@@ -326,13 +327,27 @@
 maintain the association. After the first time, phabsend will check
 obsstore and tags information so it can figure out whether to update an
 existing Differential Revision, or create a new one.
+
+The --confirm option lets you confirm changesets before sending them. You
+can also add following to your configuration file to make it default
+behaviour.
+
+[phabsend]
+confirm = true
 """
 revs = list(revs) + opts.get('rev', [])
 revs = scmutil.revrange(repo, revs)
 
 if not revs:
 raise error.Abort(_('phabsend requires at least one changeset'))
 
+confirm = ui.configbool('phabsend', 'confirm')
+confirm |= bool(opts.get('confirm'))
+if confirm:
+confirmed = _confirmbeforesend(repo, revs)
+if not confirmed:
+raise error.Abort(_('phabsend cancelled'))
+
 actions = []
 reviewers = opts.get('reviewer', [])
 if reviewers:
@@ -379,6 +394,20 @@
 _metanamemap = util.sortdict([(r'user', 'User'), (r'date', 'Date'),
   (r'node', 'Node ID'), (r'parent', 'Parent ')])
 
+def _confirmbeforesend(repo, revs):
+ui = repo.ui
+for rev in revs:
+ctx = repo[rev]
+desc = ctx.description().splitlines()[0]
+ui.write(('%d: ' % rev), label='phabsend.revnumber')
+ui.write(('%s\n' % desc), label='phabsend.desc')
+
+if ui.promptchoice(_('Phabsend the above changes (yn)?'
+'$$  $$ ')):
+return False
+
+return True
+
 def querydrev(repo, params, stack=False):
 """return a list of "Differential Revision" dicts
 



To: pulkit, #hg-reviewers, durin42
Cc: durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D218: phabricator: add --confirm option to phabsend command

2017-08-04 Thread durin42 (Augie Fackler)
durin42 accepted this revision.
durin42 added a comment.
This revision is now accepted and ready to land.


  Sure.

REPOSITORY
  rHG Mercurial

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

To: pulkit, #hg-reviewers, durin42
Cc: durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


Re: [PATCH] py3: use bytes IO to write sample hgrc

2017-08-04 Thread Augie Fackler
On Thu, Aug 03, 2017 at 01:13:20AM +0900, Yuya Nishihara wrote:
> # HG changeset patch
> # User Yuya Nishihara 
> # Date 1501688702 -32400
> #  Thu Aug 03 00:45:02 2017 +0900
> # Node ID 52bbc0a0dc7a3e946ed16bf547929252e95603d4
> # Parent  6a620fd39e4cac88e1a46f18aa7b852639e3b729
> py3: use bytes IO to write sample hgrc

queued, thanks
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


Re: [PATCH 3 of 3 v4 sparse-ext] sparse: treat paths as cwd-relative

2017-08-04 Thread Augie Fackler
On Fri, Aug 04, 2017 at 05:45:59AM -0700, Kostia Balytskyi wrote:
> # HG changeset patch
> # User Kostia Balytskyi 
> # Date 1501850302 25200
> #  Fri Aug 04 05:38:22 2017 -0700
> # Node ID fe26df0128eaefab8b79f7552da89c762014dab7
> # Parent  c0136292871d85675ccb9f386f4ae1913da4eb53
> sparse: treat paths as cwd-relative

queued, thanks

>
> This commit makes it so sparse treats passed paths as CWD-relative,
> not repo-root-realive. This is a more intuitive behavior in my (and some
> other FB people's) opinion.
>
> This is breaking change however. My hope here is that since sparse is
> experimental, it's ok to introduce BCs.

Yes, that's why we have experimental things.

>
> The reason (glob)s are needed in the test is this: in these two cases we
> do not supply path together with slashes, but `os.path.join` adds them, which
> means that under Windows they can be backslashes. To demonstrate this 
> behavior,
> one could remove the (glob)s and run `./run-tests.py test-sparse.t` from
> MinGW's terminal on Windows.
>
> diff --git a/hgext/sparse.py b/hgext/sparse.py

[...]

>  elif exclude:
> diff --git a/tests/test-sparse.t b/tests/test-sparse.t
> --- a/tests/test-sparse.t
> +++ b/tests/test-sparse.t
> @@ -48,6 +48,31 @@ TODO: See if this can be made to fail th
>[255]
>  #endif
>
> +Paths should be treated as cwd-relative, not repo-root-relative
> +  $ mkdir subdir && cd subdir
> +  $ hg debugsparse --include path
> +  $ hg debugsparse
> +  [include]
> +  $TESTTMP/myrepo/hide
> +  hide
> +  subdir/path (glob)
> +
> +  $ cd ..
> +  $ echo hello > subdir/file2.ext
> +  $ cd subdir
> +  $ hg debugsparse --include '**.ext'  # let us test globs
> +  $ hg debugsparse --include 'path:abspath'  # and a path: pattern

I'm a little weirded out that it becomes absolute magically, but I
guess it matches `hg files` (h/t to Martin), so I'm fine with it.

> +  $ cd ..
> +  $ hg debugsparse
> +  [include]
> +  $TESTTMP/myrepo/hide
> +  hide
> +  path:abspath
> +  subdir/**.ext
> +  subdir/path (glob)
> +
> +  $ rm -rf subdir
> +
>  Verify commiting while sparse includes other files
>
>$ echo z > hide
> ___
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D231: httppeer: add support for httppostargs when we're sending a file

2017-08-04 Thread durin42 (Augie Fackler)
durin42 created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  This is probably only used in the 'unbundle' command, but the code
  ended up being cleaner to make it generic and treat *all* httppostargs
  with a non-args request body as though they were file-like in
  nature. It also means we get test coverage more or less for free. A
  previous version of this change didn't use io.BytesIO, and it was a
  lot more complicated.
  
  This also fixes a server-side bug, so anyone using httppostargs should
  update all of their servers to this revision or later *before* this
  gets to their clients, otherwise servers will hang trying to over-read
  the POST body.

REPOSITORY
  rHG Mercurial

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

AFFECTED FILES
  mercurial/hgweb/protocol.py
  mercurial/httppeer.py

CHANGE DETAILS

diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py
--- a/mercurial/httppeer.py
+++ b/mercurial/httppeer.py
@@ -9,6 +9,7 @@
 from __future__ import absolute_import
 
 import errno
+import io
 import os
 import socket
 import struct
@@ -86,6 +87,44 @@
 
 resp.__class__ = readerproxy
 
+class _multifile(object):
+def __init__(self, *fileobjs):
+for f in fileobjs:
+if not util.safehasattr(f, 'length'):
+raise ValueError(
+'fileprepender only supports file objects that '
+'have a length but this one does not:', type(f), f)
+self._fileobjs = fileobjs
+self._index = 0
+
+@property
+def length(self):
+return sum(f.length for f in self._fileobjs)
+
+def read(self, amt=None):
+if not amt:
+return ''.join(f.read() for f in self._fileobjs)
+parts = []
+while amt and self._index < len(self._fileobjs):
+parts.append(self._fileobjs[self._index].read(amt))
+got = len(parts[-1])
+if got < amt:
+self._index += 1
+amt -= got
+return ''.join(parts)
+
+def seek(self, offset, whence=os.SEEK_SET):
+if whence != os.SEEK_SET:
+raise NotImplementedError(
+'fileprepender does not support anything other'
+' than os.SEEK_SET for whence on seek()')
+if offset != 0:
+raise NotImplementedError(
+'fileprepender only supports seeking to start, but that '
+'could be fixed if you need it')
+for f in self._fileobjs:
+f.seek(0)
+
 class httppeer(wireproto.wirepeer):
 def __init__(self, ui, path):
 self.path = path
@@ -149,16 +188,19 @@
 # with infinite recursion when trying to look up capabilities
 # for the first time.
 postargsok = self.caps is not None and 'httppostargs' in self.caps
-# TODO: support for httppostargs when data is a file-like
-# object rather than a basestring
-canmungedata = not data or isinstance(data, basestring)
-if postargsok and canmungedata:
+if postargsok:
 strargs = urlreq.urlencode(sorted(args.items()))
 if strargs:
 if not data:
 data = strargs
-elif isinstance(data, basestring):
-data = strargs + data
+else:
+if isinstance(data, basestring):
+i = io.BytesIO(data)
+i.length = len(data)
+data = i
+argsio = io.BytesIO(strargs)
+argsio.length = len(strargs)
+data = _multifile(argsio, data)
 headers['X-HgArgs-Post'] = len(strargs)
 else:
 if len(args) > 0:
diff --git a/mercurial/hgweb/protocol.py b/mercurial/hgweb/protocol.py
--- a/mercurial/hgweb/protocol.py
+++ b/mercurial/hgweb/protocol.py
@@ -75,6 +75,9 @@
 return args
 def getfile(self, fp):
 length = int(self.req.env['CONTENT_LENGTH'])
+# If httppostargs is used, we need to read Content-Length
+# minus the amount that was consumed by args.
+length -= int(self.req.env.get('HTTP_X_HGARGS_POST', 0))
 for s in util.filechunkiter(self.req, limit=length):
 fp.write(s)
 def redirect(self):



To: durin42, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D230: http: add a test of actually pushing with httppostargs

2017-08-04 Thread durin42 (Augie Fackler)
durin42 created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  This was previously untested. Sigh.

REPOSITORY
  rHG Mercurial

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

AFFECTED FILES
  tests/test-push-http.t

CHANGE DETAILS

diff --git a/tests/test-push-http.t b/tests/test-push-http.t
--- a/tests/test-push-http.t
+++ b/tests/test-push-http.t
@@ -172,4 +172,20 @@
   % serve errors
   [255]
 
+  $ cat > .hg/hgrc < [web]
+  > push_ssl = false
+  > allow_push = *
+  > [experimental]
+  > httppostargs=true
+  > EOF
+  $ req
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 1 changes to 1 files
+  % serve errors
+
   $ cd ..



To: durin42, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D216: evolution: rename bumped to phase-divergent

2017-08-04 Thread durin42 (Augie Fackler)
durin42 accepted this revision as: durin42.
durin42 added a comment.


  I'm fine with this as-is for now, with the caveat that we might want to fix 
the two -divergent names in the future if they lead to user confusion.
  
  (Someone should feel encouraged to land this, I'm in the middle of some 
unpleasant debugging and am reviewing while I wait for wireshark to install.)

REPOSITORY
  rHG Mercurial

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

To: lothiraldan, #hg-reviewers, ryanmce, durin42
Cc: ryanmce, martinvonz, quark, durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D127: phabricator: add phabupdate command to update status in batch

2017-08-04 Thread quark (Jun Wu)
quark updated this revision to Diff 557.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D127?vs=239=557

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

AFFECTED FILES
  contrib/phabricator.py

CHANGE DETAILS

diff --git a/contrib/phabricator.py b/contrib/phabricator.py
--- a/contrib/phabricator.py
+++ b/contrib/phabricator.py
@@ -9,7 +9,7 @@
 This extension provides a ``phabsend`` command which sends a stack of
 changesets to Phabricator without amending commit messages, and a ``phabread``
 command which prints a stack of revisions in a format suitable
-for :hg:`import`.
+for :hg:`import`, and a ``phabupdate`` command to update statuses in batch.
 
 By default, Phabricator requires ``Test Plan`` which might prevent some
 changeset from being sent. The requirement could be disabled by changing
@@ -765,3 +765,32 @@
 spec = ':(%s)' % spec
 drevs = querydrev(repo, spec)
 readpatch(repo, drevs, ui.write)
+
+@command('phabupdate',
+ [('', 'accept', False, _('accept revisions')),
+  ('', 'reject', False, _('reject revisions')),
+  ('', 'abandon', False, _('abandon revisions')),
+  ('', 'reclaim', False, _('reclaim revisions')),
+  ('m', 'comment', '', _('comment on the last revision')),
+ ], _('DREVSPEC [OPTIONS]'))
+def phabupdate(ui, repo, spec, **opts):
+"""update Differential Revision in batch
+
+DREVSPEC selects revisions. See :hg:`help phabread` for its usage.
+"""
+flags = [n for n in 'accept reject abandon reclaim'.split() if opts.get(n)]
+if len(flags) > 1:
+raise error.Abort(_('%s cannot be used together') % ', '.join(flags))
+
+actions = []
+for f in flags:
+actions.append({'type': f, 'value': 'true'})
+
+drevs = querydrev(repo, spec)
+for i, drev in enumerate(drevs):
+if i + 1 == len(drevs) and opts.get('comment'):
+actions.append({'type': 'comment', 'value': opts['comment']})
+if actions:
+params = {'objectIdentifier': drev[r'phid'],
+  'transactions': actions}
+callconduit(repo, 'differential.revision.edit', params)



To: quark, #hg-reviewers
Cc: krbullock, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D122: phabricator: add --amend option to phabsend

2017-08-04 Thread quark (Jun Wu)
quark updated this revision to Diff 553.
quark edited the test plan for this revision.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D122?vs=485=553

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

AFFECTED FILES
  contrib/phabricator.py

CHANGE DETAILS

diff --git a/contrib/phabricator.py b/contrib/phabricator.py
--- a/contrib/phabricator.py
+++ b/contrib/phabricator.py
@@ -38,6 +38,8 @@
 from mercurial.node import bin, nullid
 from mercurial.i18n import _
 from mercurial import (
+cmdutil,
+context,
 encoding,
 error,
 mdiff,
@@ -316,7 +318,7 @@
 if not revision:
 raise error.Abort(_('cannot create revision for %s') % ctx)
 
-return revision
+return revision, diff
 
 def userphids(repo, names):
 """convert user names to PHIDs"""
@@ -334,6 +336,7 @@
 
 @command('phabsend',
  [('r', 'rev', [], _('revisions to send'), _('REV')),
+  ('', 'amend', False, _('update commit messages')),
   ('', 'reviewer', [], _('specify reviewers'))],
  _('REV [OPTIONS]'))
 def phabsend(ui, repo, *revs, **opts):
@@ -343,16 +346,21 @@
 with a linear dependencies relationship using the order specified by the
 revset.
 
-For the first time uploading changesets, local tags will be created to
-maintain the association. After the first time, phabsend will check
-obsstore and tags information so it can figure out whether to update an
-existing Differential Revision, or create a new one.
+If --amend is set, update commit messages so they have the
+``Differential Revision`` URL, remove related tags. This is similar to what
+arcanist will do, and is more desired in author-push workflows. Otherwise,
+use local tags to record the ``Differential Revision`` association.
+
+phabsend will check obsstore and the above association to decide whether to
+update an existing Differential Revision, or create a new one.
 """
 revs = list(revs) + opts.get('rev', [])
 revs = scmutil.revrange(repo, revs)
 
 if not revs:
 raise error.Abort(_('phabsend requires at least one changeset'))
+if opts.get('amend'):
+cmdutil.checkunfinished(repo)
 
 actions = []
 reviewers = opts.get('reviewer', [])
@@ -363,6 +371,9 @@
 # {newnode: (oldnode, olddiff, olddrev}
 oldmap = getoldnodedrevmap(repo, [repo[r].node() for r in revs])
 
+drevids = [] # [int]
+diffmap = {} # {newnode: diff}
+
 # Send patches one by one so we know their Differential Revision IDs and
 # can provide dependency relationship
 lastrevid = None
@@ -374,28 +385,67 @@
 oldnode, olddiff, revid = oldmap.get(ctx.node(), (None, None, None))
 if oldnode != ctx.node():
 # Create or update Differential Revision
-revision = createdifferentialrevision(ctx, revid, lastrevid,
-  oldnode, olddiff, actions)
+revision, diff = createdifferentialrevision(
+ctx, revid, lastrevid, oldnode, olddiff, actions)
+diffmap[ctx.node()] = diff
 newrevid = int(revision[r'object'][r'id'])
 if revid:
 action = _('updated')
 else:
 action = _('created')
 
-# Create a local tag to note the association
-tagname = 'D%d' % newrevid
-tags.tag(repo, tagname, ctx.node(), message=None, user=None,
- date=None, local=True)
+# Create a local tag to note the association, if commit message
+# does not have it already
+m = _differentialrevisiondescre.search(ctx.description())
+if not m or int(m.group(1)) != newrevid:
+tagname = 'D%d' % newrevid
+tags.tag(repo, tagname, ctx.node(), message=None, user=None,
+ date=None, local=True)
 else:
 # Nothing changed. But still set "newrevid" so the next revision
 # could depend on this one.
 newrevid = revid
 action = _('skipped')
 
 ui.write(_('D%s: %s - %s: %s\n') % (newrevid, action, ctx,
 ctx.description().split('\n')[0]))
+drevids.append(newrevid)
 lastrevid = newrevid
 
+# Update commit messages and remove tags
+if opts.get('amend'):
+unfi = repo.unfiltered()
+drevs = callconduit(repo, 'differential.query', {'ids': drevids})
+with repo.wlock(), repo.lock(), repo.transaction('phabsend'):
+wnode = unfi['.'].node()
+mapping = {} # {oldnode: [newnode]}
+for i, rev in enumerate(revs):
+old = unfi[rev]
+drevid = drevids[i]
+drev = [d for d in drevs if int(d[r'id']) == drevid][0]
+newdesc = getdescfromdrev(drev)
+# Make sure commit message contain 

D126: phabricator: add status to revision query language

2017-08-04 Thread quark (Jun Wu)
quark updated this revision to Diff 556.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D126?vs=488=556

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

AFFECTED FILES
  contrib/phabricator.py

CHANGE DETAILS

diff --git a/contrib/phabricator.py b/contrib/phabricator.py
--- a/contrib/phabricator.py
+++ b/contrib/phabricator.py
@@ -455,6 +455,13 @@
 _metanamemap = util.sortdict([(r'user', 'User'), (r'date', 'Date'),
   (r'node', 'Node ID'), (r'parent', 'Parent ')])
 
+_knownstatusnames = {'accepted', 'needsreview', 'needsrevision', 'closed',
+ 'abandoned'}
+
+def _getstatusname(drev):
+"""get normalized status name from a Differential Revision"""
+return drev[r'statusName'].replace(' ', '').lower()
+
 # Small language to specify differential revisions. Support symbols: (), :X,
 # +, and -.
 
@@ -471,19 +478,19 @@
 }
 
 def _tokenize(text):
-text = text.replace(' ', '') # remove space
 view = memoryview(text) # zero-copy slice
-special = '():+-&'
+special = '():+-& '
 pos = 0
 length = len(text)
 while pos < length:
 symbol = ''.join(itertools.takewhile(lambda ch: ch not in special,
  view[pos:]))
 if symbol:
 yield ('symbol', symbol, pos)
 pos += len(symbol)
-else: # special char
-yield (text[pos], None, pos)
+else: # special char, ignore space
+if text[pos] != ' ':
+yield (text[pos], None, pos)
 pos += 1
 yield ('end', None, pos)
 
@@ -611,15 +618,19 @@
 tofetch.update(range(max(1, r - batchsize), r + 1))
 if drevs:
 fetch({r'ids': list(tofetch)})
-getstack(list(ancestordrevs))
+validids = sorted(set(getstack(list(ancestordrevs))) | set(drevs))
 
 # Walk through the tree, return smartsets
 def walk(tree):
 op = tree[0]
 if op == 'symbol':
 drev = _parsedrev(tree[1])
 if drev:
 return smartset.baseset([drev])
+elif tree[1] in _knownstatusnames:
+drevs = [r for r in validids
+ if _getstatusname(prefetched[r]) == tree[1]]
+return smartset.baseset(drevs)
 else:
 raise error.Abort(_('unknown symbol: %s') % tree[1])
 elif op in {'and_', 'add', 'sub'}:
@@ -739,8 +750,13 @@
 ``&``, ``(``, ``)`` for complex queries. Prefix ``:`` could be used to
 select a stack.
 
+``abandoned``, ``accepted``, ``closed``, ``needsreview``, ``needsrevision``
+could be used to filter patches by status. For performance reason, they
+only represent a subset of non-status selections and cannot be used alone.
+
 For example, ``:D6+8-(2+D4)`` selects a stack up to D6, plus D8 and exclude
-D2 and D4.
+D2 and D4. ``:D9 & needsreview`` selects "Needs Review" revisions in a
+stack up to D9.
 
 If --stack is given, follow dependencies information and read all patches.
 It is equivalent to the ``:`` operator.



To: quark, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D124: phabricator: change "readpatch" to be more flexible

2017-08-04 Thread quark (Jun Wu)
quark updated this revision to Diff 554.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D124?vs=486=554

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

AFFECTED FILES
  contrib/phabricator.py

CHANGE DETAILS

diff --git a/contrib/phabricator.py b/contrib/phabricator.py
--- a/contrib/phabricator.py
+++ b/contrib/phabricator.py
@@ -602,15 +602,12 @@
 meta[r'parent'] = commit[r'parents'][0]
 return meta or {}
 
-def readpatch(repo, params, write, stack=False):
+def readpatch(repo, drevs, write):
 """generate plain-text patch readable by 'hg import'
 
-write is usually ui.write. params is passed to "differential.query". If
-stack is True, also write dependent patches.
+write is usually ui.write. drevs is what "querydrev" returns, results of
+"differential.query".
 """
-# Differential Revisions
-drevs = querydrev(repo, params, stack)
-
 # Prefetch hg:meta property for all diffs
 diffids = sorted(set(max(int(v) for v in drev[r'diffs']) for drev in 
drevs))
 diffs = callconduit(repo, 'differential.querydiffs', {'ids': diffids})
@@ -650,4 +647,5 @@
 revid = int(revid.split('/')[-1].replace('D', ''))
 except ValueError:
 raise error.Abort(_('invalid Revision ID: %s') % revid)
-readpatch(repo, {'ids': [revid]}, ui.write, opts.get('stack'))
+drevs = querydrev(repo, {'ids': [revid]}, opts.get('stack'))
+readpatch(repo, drevs, ui.write)



To: quark, #hg-reviewers, mitrandir
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D229: phabricator: update diff property even if we choose not to create a new diff

2017-08-04 Thread quark (Jun Wu)
quark created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  The diff property contains metadata like "HG Node". Previously we skip
  uploading a new diff if we are sure that the old patch and new patch have a
  same content. That has issues when a pusher adds an obsmarker using the old
  "HG Node" stored in the old diff.
  
  This patch adds logic to update the diff property so "HG Node" gets updated
  to prevent that issue.

REPOSITORY
  rHG Mercurial

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

AFFECTED FILES
  contrib/phabricator.py

CHANGE DETAILS

diff --git a/contrib/phabricator.py b/contrib/phabricator.py
--- a/contrib/phabricator.py
+++ b/contrib/phabricator.py
@@ -255,7 +255,7 @@
 callconduit(ctx.repo(), 'differential.setdiffproperty', params)
 
 def createdifferentialrevision(ctx, revid=None, parentrevid=None, oldnode=None,
-   actions=None):
+   olddiff=None, actions=None):
 """create or update a Differential Revision
 
 If revid is None, create a new Differential Revision, otherwise update
@@ -279,6 +279,13 @@
 diff = creatediff(ctx)
 writediffproperties(ctx, diff)
 transactions.append({'type': 'update', 'value': diff[r'phid']})
+else:
+# Even if we don't need to upload a new diff because the patch content
+# does not change. We might still need to update its metadata so
+# pushers could know the correct node metadata.
+assert olddiff
+diff = olddiff
+writediffproperties(ctx, diff)
 
 # Use a temporary summary to set dependency. There might be better ways but
 # I cannot find them for now. But do not do that if we are updating an
@@ -368,7 +375,7 @@
 if oldnode != ctx.node():
 # Create or update Differential Revision
 revision = createdifferentialrevision(ctx, revid, lastrevid,
-  oldnode, actions)
+  oldnode, olddiff, actions)
 newrevid = int(revision[r'object'][r'id'])
 if revid:
 action = _('updated')



To: quark, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


Re: [PATCH STABLE] i18n: use actual filename, in which function is defined, for hg.pot

2017-08-04 Thread FUJIWARA Katsunori
At Fri, 4 Aug 2017 00:36:28 +0900,
Yuya Nishihara wrote:
> 
> On Wed, 02 Aug 2017 22:36:45 +0900, FUJIWARA Katsunori wrote:
> > # HG changeset patch
> > # User FUJIWARA Katsunori 
> > # Date 1501599731 -32400
> > #  Wed Aug 02 00:02:11 2017 +0900
> > # Branch stable
> > # Node ID b6d0bcd19b4dffb2360f909f459c27104ad16565
> > # Parent  76b171209151fe41dbf8dbfec473cc533f3b40ca
> > # Available At https://bitbucket.org/foozy/mercurial-wip
> > #  hg pull https://bitbucket.org/foozy/mercurial-wip -r 
> > b6d0bcd19b4d
> > # EXP-Topic i18n-fix-update-pot-issues
> > i18n: use actual filename, in which function is defined, for hg.pot
> 
> > +funcmod = inspect.getmodule(func)
> > +extra = ''
> > +if funcmod.__package__ == funcmod.__name__:
> > +extra = '/__init__'
> > +actualpath = '%s%s.py' % (funcmod.__name__.replace('.', '/'), 
> > extra)
> > +
> >  src = inspect.getsource(func)
> > -name = "%s.%s" % (path, func.__name__)
> > +name = "%s.%s" % (actualpath, func.__name__)
> >  lineno = inspect.getsourcelines(func)[1]
> 
> Perhaps inspect.getsourcefile() can be used.
> 

Ah, I overlooked it. I'll post follow up patch on default. Thanks!

-- 
--
[FUJIWARA Katsunori] fo...@lares.dti.ne.jp
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D216: evolution: rename bumped to phase-divergent

2017-08-04 Thread quark (Jun Wu)
quark added a comment.


  In https://phab.mercurial-scm.org/D216#3674, @ryanmce wrote:
  
  > I disagree. `hg amend` --> `abort: cannot amend public changesets`
  
  
  The word "public" does not get associated with "phase" automatically. I think 
users can understand what "public" and "draft" without knowing what "phase" is. 
"divergent" also seems understandable without Googling. I'm not sure about 
"phase".
  
  Anyway, since we can always customize these (ex. `dialect.py`). From an 
internal UX point of view, I'm fine with whatever chosen.

REPOSITORY
  rHG Mercurial

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

To: lothiraldan, #hg-reviewers, ryanmce
Cc: ryanmce, martinvonz, quark, durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D224: build: Delaying version computation on macOS builds.

2017-08-04 Thread rdamazio (Rodrigo Damazio Bovendorp)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGf22513c3717d: build: Delaying version computation on macOS 
builds. (authored by rdamazio).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D224?vs=530=547

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

AFFECTED FILES
  Makefile

CHANGE DETAILS

diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -186,7 +186,7 @@
  PREFIX=/usr/local \
  clean install
mkdir -p $${OUTPUTDIR:-dist}
-   HGVER=$(shell python contrib/genosxversion.py $(OSXVERSIONFLAGS) 
build/mercurial/Library/Python/2.7/site-packages/mercurial/__version__.py ) && \
+   HGVER=$$(shell python contrib/genosxversion.py $(OSXVERSIONFLAGS) 
build/mercurial/Library/Python/2.7/site-packages/mercurial/__version__.py ) && \
OSXVER=$$(sw_vers -productVersion | cut -d. -f1,2) && \
pkgbuild --filter \\.DS_Store --root build/mercurial/ \
  --identifier org.mercurial-scm.mercurial \



To: rdamazio, #hg-reviewers, martinvonz
Cc: martinvonz, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


Evolution mini-sprint

2017-08-04 Thread Boris Feld
Hi mercurial developers,

This was in the air for a while and here it is! We are organizing an
Evolve mini-sprint focused on documentation and usability bug-fixes.
You're welcome to join us and share your thoughts, feedback and pain
around Evolve and Mercurial, but also meets the people behind IRC
pseudos and email addresses for real.

The mini-sprint will start on August 28th and ends on August 30th 2017
and will be hosted in Paris. (Yes that's the end of the month!) The
exact venue is not known yet. It will be published as soon as known.

Please register in this pad here if you want to come: https://tinyurl.c
om/evosprint17

If you have any sensitive information (food, health, transportation,
... related), please fill this form to contact us: https://framaforms.o
rg/evolve-mini-sprint-organization-form-1501847916

If you are considering attending but travel cost is a blocker, get in
touch with me.

Cheers,
Boris Feld
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D223: color: remove warnings if term is not formatted (==dumb or !ui.formatted())

2017-08-04 Thread spectral (Kyle Lippincott)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGf9f28ee41cac: color: remove warnings if term is not 
formatted (==dumb or !ui.formatted()) (authored by spectral).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D223?vs=528=546

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

AFFECTED FILES
  mercurial/color.py

CHANGE DETAILS

diff --git a/mercurial/color.py b/mercurial/color.py
--- a/mercurial/color.py
+++ b/mercurial/color.py
@@ -130,7 +130,7 @@
 def loadcolortable(ui, extname, colortable):
 _defaultstyles.update(colortable)
 
-def _terminfosetup(ui, mode):
+def _terminfosetup(ui, mode, formatted):
 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
 
 # If we failed to load curses, we go ahead and return.
@@ -164,8 +164,8 @@
 del ui._terminfoparams[key]
 if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
 # Only warn about missing terminfo entries if we explicitly asked for
-# terminfo mode.
-if mode == "terminfo":
+# terminfo mode and we're in a formatted terminal.
+if mode == "terminfo" and formatted:
 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
   "ECMA-48 color\n"))
 ui._terminfoparams.clear()
@@ -242,7 +242,7 @@
 def modewarn():
 # only warn if color.mode was explicitly set and we're in
 # a formatted terminal
-if mode == realmode and ui.formatted():
+if mode == realmode and formatted:
 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
 
 if realmode == 'win32':
@@ -253,7 +253,7 @@
 elif realmode == 'ansi':
 ui._terminfoparams.clear()
 elif realmode == 'terminfo':
-_terminfosetup(ui, mode)
+_terminfosetup(ui, mode, formatted)
 if not ui._terminfoparams:
 ## FIXME Shouldn't we return None in this case too?
 modewarn()



To: spectral, #hg-reviewers, yuja
Cc: yuja, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D223: color: remove warnings if term is not formatted (==dumb or !ui.formatted())

2017-08-04 Thread yuja (Yuya Nishihara)
yuja accepted this revision as: yuja.
yuja added a comment.


  Makes sense.

REPOSITORY
  rHG Mercurial

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

To: spectral, #hg-reviewers, yuja
Cc: yuja, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 3 of 3 v4 sparse-ext] sparse: treat paths as cwd-relative

2017-08-04 Thread Kostia Balytskyi
# HG changeset patch
# User Kostia Balytskyi 
# Date 1501850302 25200
#  Fri Aug 04 05:38:22 2017 -0700
# Node ID fe26df0128eaefab8b79f7552da89c762014dab7
# Parent  c0136292871d85675ccb9f386f4ae1913da4eb53
sparse: treat paths as cwd-relative

This commit makes it so sparse treats passed paths as CWD-relative,
not repo-root-realive. This is a more intuitive behavior in my (and some
other FB people's) opinion.

This is breaking change however. My hope here is that since sparse is
experimental, it's ok to introduce BCs.

The reason (glob)s are needed in the test is this: in these two cases we
do not supply path together with slashes, but `os.path.join` adds them, which
means that under Windows they can be backslashes. To demonstrate this behavior,
one could remove the (glob)s and run `./run-tests.py test-sparse.t` from
MinGW's terminal on Windows.

diff --git a/hgext/sparse.py b/hgext/sparse.py
--- a/hgext/sparse.py
+++ b/hgext/sparse.py
@@ -155,7 +155,8 @@ def _clonesparsecmd(orig, ui, repo, *arg
 if include or exclude or enableprofile:
 def clonesparse(orig, self, node, overwrite, *args, **kwargs):
 sparse.updateconfig(self.unfiltered(), pat, {}, include=include,
-exclude=exclude, enableprofile=enableprofile)
+exclude=exclude, enableprofile=enableprofile,
+usereporootpaths=True)
 return orig(self, node, overwrite, *args, **kwargs)
 extensions.wrapfunction(hg, 'updaterepo', clonesparse)
 return orig(ui, repo, *args, **opts)
diff --git a/mercurial/sparse.py b/mercurial/sparse.py
--- a/mercurial/sparse.py
+++ b/mercurial/sparse.py
@@ -17,6 +17,7 @@ from . import (
 error,
 match as matchmod,
 merge as mergemod,
+pathutil,
 pycompat,
 scmutil,
 util,
@@ -616,7 +617,7 @@ def importfromfiles(repo, opts, paths, f
 
 def updateconfig(repo, pats, opts, include=False, exclude=False, reset=False,
  delete=False, enableprofile=False, disableprofile=False,
- force=False):
+ force=False, usereporootpaths=False):
 """Perform a sparse config update.
 
 Only one of the actions may be performed.
@@ -639,6 +640,20 @@ def updateconfig(repo, pats, opts, inclu
 if any(os.path.isabs(pat) for pat in pats):
 raise error.Abort(_('paths cannot be absolute'))
 
+if not usereporootpaths:
+# let's treat paths as relative to cwd
+root, cwd = repo.root, repo.getcwd()
+abspats = []
+for kindpat in pats:
+kind, pat = matchmod._patsplit(kindpat, None)
+if kind in matchmod.cwdrelativepatternkinds or kind is None:
+ap = (kind + ':' if kind else '') +\
+pathutil.canonpath(root, cwd, pat)
+abspats.append(ap)
+else:
+abspats.append(kindpat)
+pats = abspats
+
 if include:
 newinclude.update(pats)
 elif exclude:
diff --git a/tests/test-sparse.t b/tests/test-sparse.t
--- a/tests/test-sparse.t
+++ b/tests/test-sparse.t
@@ -48,6 +48,31 @@ TODO: See if this can be made to fail th
   [255]
 #endif
 
+Paths should be treated as cwd-relative, not repo-root-relative
+  $ mkdir subdir && cd subdir
+  $ hg debugsparse --include path
+  $ hg debugsparse
+  [include]
+  $TESTTMP/myrepo/hide
+  hide
+  subdir/path (glob)
+  
+  $ cd ..
+  $ echo hello > subdir/file2.ext
+  $ cd subdir
+  $ hg debugsparse --include '**.ext'  # let us test globs
+  $ hg debugsparse --include 'path:abspath'  # and a path: pattern
+  $ cd ..
+  $ hg debugsparse
+  [include]
+  $TESTTMP/myrepo/hide
+  hide
+  path:abspath
+  subdir/**.ext
+  subdir/path (glob)
+  
+  $ rm -rf subdir
+
 Verify commiting while sparse includes other files
 
   $ echo z > hide
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 1 of 3 v4 sparse-ext] sparse: properly error out when absolute paths are used

2017-08-04 Thread Kostia Balytskyi
# HG changeset patch
# User Kostia Balytskyi 
# Date 1501711521 25200
#  Wed Aug 02 15:05:21 2017 -0700
# Node ID d24d6711aa2a5397d52f46ccffa64c2a638c088b
# Parent  609606d217659e0a6c1cf6f907b6512be5340e57
sparse: properly error out when absolute paths are used

Current logic is misleading (it says it drops only absolute paths, but
it actually drops all of them), not cross-platform (does not support Windows)
and IMO just wrong (as it should just error out if absolute paths are given).

This commit fixes it.

diff --git a/mercurial/sparse.py b/mercurial/sparse.py
--- a/mercurial/sparse.py
+++ b/mercurial/sparse.py
@@ -636,10 +636,10 @@ def updateconfig(repo, pats, opts, inclu
 newexclude = set(oldexclude)
 newprofiles = set(oldprofiles)
 
-if any(pat.startswith('/') for pat in pats):
-repo.ui.warn(_('warning: paths cannot start with /, ignoring: 
%s\n')
- % ([pat for pat in pats if pat.startswith('/')]))
-elif include:
+if any(os.path.isabs(pat) for pat in pats):
+raise error.Abort(_('paths cannot be absolute'))
+
+if include:
 newinclude.update(pats)
 elif exclude:
 newexclude.update(pats)
diff --git a/tests/test-sparse.t b/tests/test-sparse.t
--- a/tests/test-sparse.t
+++ b/tests/test-sparse.t
@@ -29,20 +29,22 @@ Absolute paths outside the repo should j
 
 #if no-windows
   $ hg debugsparse --include /foo/bar
-  warning: paths cannot start with /, ignoring: ['/foo/bar']
+  abort: paths cannot be absolute
+  [255]
   $ hg debugsparse --include '$TESTTMP/myrepo/hide'
 
   $ hg debugsparse --include '/root'
-  warning: paths cannot start with /, ignoring: ['/root']
+  abort: paths cannot be absolute
+  [255]
 #else
 TODO: See if this can be made to fail the same way as on Unix
   $ hg debugsparse --include /c/foo/bar
-  abort: c:/foo/bar not under root '$TESTTMP/myrepo' (glob)
+  abort: paths cannot be absolute
   [255]
   $ hg debugsparse --include '$TESTTMP/myrepo/hide'
 
   $ hg debugsparse --include '/c/root'
-  abort: c:/root not under root '$TESTTMP/myrepo' (glob)
+  abort: paths cannot be absolute
   [255]
 #endif
 
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 2 of 3 v4 sparse-ext] match: expose some data and functionality to other modules

2017-08-04 Thread Kostia Balytskyi
# HG changeset patch
# User Kostia Balytskyi 
# Date 1501714137 25200
#  Wed Aug 02 15:48:57 2017 -0700
# Node ID c0136292871d85675ccb9f386f4ae1913da4eb53
# Parent  d24d6711aa2a5397d52f46ccffa64c2a638c088b
match: expose some data and functionality to other modules

This patch makes sure that other modules can check whether patterns
are CWD-relative.

diff --git a/mercurial/match.py b/mercurial/match.py
--- a/mercurial/match.py
+++ b/mercurial/match.py
@@ -18,6 +18,11 @@ from . import (
 util,
 )
 
+allpatternkinds = ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
+   'listfile', 'listfile0', 'set', 'include', 'subinclude',
+   'rootfilesin')
+cwdrelativepatternkinds = ('relpath', 'glob')
+
 propertycache = util.propertycache
 
 def _rematcher(regex):
@@ -190,7 +195,7 @@ def _donormalize(patterns, default, root
 normalized and rooted patterns and with listfiles expanded.'''
 kindpats = []
 for kind, pat in [_patsplit(p, default) for p in patterns]:
-if kind in ('glob', 'relpath'):
+if kind in cwdrelativepatternkinds:
 pat = pathutil.canonpath(root, cwd, pat, auditor)
 elif kind in ('relglob', 'path', 'rootfilesin'):
 pat = util.normpath(pat)
@@ -691,9 +696,7 @@ def _patsplit(pattern, default):
 pattern."""
 if ':' in pattern:
 kind, pat = pattern.split(':', 1)
-if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
-'listfile', 'listfile0', 'set', 'include', 'subinclude',
-'rootfilesin'):
+if kind in allpatternkinds:
 return kind, pat
 return default, pattern
 
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D216: evolution: rename bumped to phase-divergent

2017-08-04 Thread ryanmce (Ryan McElroy)
ryanmce added a comment.


  In fact, right at the top of that CED page is an introduction that I think is 
pretty clear for such a powerful topic:
  
  Introduction to Instability
  ---
  
  Rewriting changesets may introduce **instability**.
  
  There are two main kinds of **unstable** changesets: **orphaned** changesets 
and **divergent** changesets.
  
  **Orphans** are changesets left behind when their ancestors are rewritten.
  
  **Divergence** has two variants:
  
  - **Content-divergence** occurs when independent rewrites of the same 
changeset lead to different results.
  - **Phase-divergence** occurs when the old (obsolete) version of a changeset 
becomes public.
  
  ---
  
  I'm probably biased because I helped write this (and I just cleaned it up and 
added emphasis), but I think it's pretty clear.

REPOSITORY
  rHG Mercurial

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

To: lothiraldan, #hg-reviewers, ryanmce
Cc: ryanmce, martinvonz, quark, durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


Re: 4.3 freeze over, release delayed

2017-08-04 Thread Boris Feld
Will the security fix be backported in a 4.2.3 release for people that
are still on Python 2.6?

Cheers,
Boris

On Tue, 2017-08-01 at 10:32 -0400, Augie Fackler wrote:
> Howdy folks,
> 
> We've got an upcoming security fix that needs to be coordinated with
> other packages, so we're not releasing 4.3 today. We *are*, however,
> unfreezing and you're welcome to send patches for default (which will
> become 4.4) even as we're waiting for the all-clear to release 4.3.
> 
> Thanks,
> Augie
> ___
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D216: evolution: rename bumped to phase-divergent

2017-08-04 Thread ryanmce (Ryan McElroy)
ryanmce accepted this revision.
ryanmce added a comment.


  A Dead Horse in Need of Kicking
  ===
  
  I have no strong desire to drag this dead horse out of its grave to continue 
kicking it. However, since we are here, I will state for the record that I 
disagree that "overdue" is more clear.
  
  Imagine I'm a `git` user trying out `hg` due to hearing about some of its 
cool features. I'm hacking on some stuff with a friend and now I see that I 
have "1 overdue changeset". I have no idea what that means, to be honest. Is it 
out-of-date somehow? Will it expire soon? Will I be fined by the library?
  
  However, if I see that a changeset is "phase-divergent" I may also be 
confused, but I pretty quickly know two things: (1) I should understand what 
"phase" means, and that this changeset disagrees with another changeset 
somehow. So, I look up phases and (1) I've learned something important about hg 
and (2) now phase-divergence makes a lot more sense just by me knowing about 
phases and the meaning of "divergent": my changeset is draft, but a different 
version of it is public, so mine is phase-divergent. It's clear from then on 
because it builds on top of the terminology we already use elsewhere in hg.
  
  Moving on and responding to other comments here
  ===
  
  > Do we /know/ that something that's phase-divergent is only divergent by 
phase? or could the content also be different? Is it possible to have a case in 
the tests that's both content-divergent and phase-divergent?
  
  I think that it's possible to be both at the same time. I'll spend some time 
coming up with the situation to explain it, but most of the time it's not the 
case as I recall.
  
  > I guess most users use source control are even aware of the "phase" concept 
so it might be nice to hide that word from UI.
  
  I disagree. `hg amend` --> `abort: cannot amend public changesets`
  
  We already expose phases in the UI and I argue that it's a good thing. Making 
users more aware of it and reusing the terminology I think is a good thing.
  
  > Maybe we can merge those two into divergent() to simplify the concept a 
user may face.
  
  This is kind of the direction we went. Instead of two separate concepts, 
"bumped" and "divergent", we now have two related concepts "content-divergent" 
and "phase-divergent". Both are "divergent", which means they need some similar 
kind of attention. The way it would be introduced in the docs is to explain the 
concept of divergence and then to explain the two types. However, I disagree 
with lumping all such types without exposing differences. I believe that will 
be more confusing since the way to arrive in the situation and the remedies are 
different.
  
  > We can think about alternative ways to display instability in the default 
log template.
  
  I'd love to hear what suggestions you have here (I lean towards 
"phase-divergent with f3967dc12" in the log so it's easy to figure out what the 
"other" version of you is).
  
  Conclusion
  ==
  
  I feel that these decisions were made a long time ago. They were recorded on 
the wiki. We sent out emails about it for comments. We met up at the tail end 
of the last sprint both at the sprint venue and in a follow-up for anyone who 
cared enough at the time, up at the Atlassian office. No naming scheme will be 
perfect -- language is always a compromise -- but I think the names currently 
on the CEDVocabulary page are quite good, significant improvements over what we 
currently have, and have been discussed for hours by experts who care, and I 
think we should stick with them. Unless someone proposes a new name that pretty 
much everyone agrees is better -- and that hasn't happened here -- I think we 
should stick with the current naming on the Wiki.

REPOSITORY
  rHG Mercurial

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

To: lothiraldan, #hg-reviewers, ryanmce
Cc: ryanmce, martinvonz, quark, durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel