mercurial@36981: 3 new changesets (3 on stable)

2018-03-19 Thread Mercurial Commits
3 new changesets (3 on stable) in mercurial:

https://www.mercurial-scm.org/repo/hg/rev/b9a6ee2066f9
changeset:   36979:b9a6ee2066f9
branch:  stable
parent:  36845:ff2370a70fe8
user:Martin von Zweigbergk 
date:Thu Mar 15 21:51:33 2018 -0700
summary: tests: demonstrate aborted rebase strips commits that didn't need 
rebasing

https://www.mercurial-scm.org/repo/hg/rev/a046d6890761
changeset:   36980:a046d6890761
branch:  stable
user:Martin von Zweigbergk 
date:Thu Mar 15 21:40:51 2018 -0700
summary: rebase: avoid defining two lists with the same contents

https://www.mercurial-scm.org/repo/hg/rev/177f3b90335f
changeset:   36981:177f3b90335f
branch:  stable
tag: tip
user:Martin von Zweigbergk 
date:Thu Mar 15 22:35:07 2018 -0700
summary: rebase: on abort, don't strip commits that didn't need rebased 
(issue5822)

-- 
Repository URL: https://www.mercurial-scm.org/repo/hg
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2840: hgweb: allow defining Server response header for HTTP server

2018-03-19 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG5890e5872f36: hgweb: allow defining Server response header 
for HTTP server (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2840?vs=6993=7155

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

AFFECTED FILES
  mercurial/configitems.py
  mercurial/help/config.txt
  mercurial/hgweb/server.py
  tests/run-tests.py
  tests/test-archive.t
  tests/test-basic.t
  tests/test-commandserver.t
  tests/test-hgweb-commands.t
  tests/test-http-protocol.t

CHANGE DETAILS

diff --git a/tests/test-http-protocol.t b/tests/test-http-protocol.t
--- a/tests/test-http-protocol.t
+++ b/tests/test-http-protocol.t
@@ -50,41 +50,41 @@
   200 Script output follows
   content-type: application/mercurial-0.1
   date: $HTTP_DATE$
-  server: * (glob)
+  server: testing stub value
   transfer-encoding: chunked
 
 Server should send application/mercurial-0.1 when client says it wants it
 
   $ get-with-headers.py --hgproto '0.1' --headeronly $LOCALIP:$HGPORT 
'?cmd=getbundle=e93700bd72895c5addab234c56d4024b487a362f='
 -
   200 Script output follows
   content-type: application/mercurial-0.1
   date: $HTTP_DATE$
-  server: * (glob)
+  server: testing stub value
   transfer-encoding: chunked
 
 Server should send application/mercurial-0.2 when client says it wants it
 
   $ get-with-headers.py --hgproto '0.2' --headeronly $LOCALIP:$HGPORT 
'?cmd=getbundle=e93700bd72895c5addab234c56d4024b487a362f='
 -
   200 Script output follows
   content-type: application/mercurial-0.2
   date: $HTTP_DATE$
-  server: * (glob)
+  server: testing stub value
   transfer-encoding: chunked
 
   $ get-with-headers.py --hgproto '0.1 0.2' --headeronly $LOCALIP:$HGPORT 
'?cmd=getbundle=e93700bd72895c5addab234c56d4024b487a362f='
 -
   200 Script output follows
   content-type: application/mercurial-0.2
   date: $HTTP_DATE$
-  server: * (glob)
+  server: testing stub value
   transfer-encoding: chunked
 
 Requesting a compression format that server doesn't support results will fall 
back to 0.1
 
   $ get-with-headers.py --hgproto '0.2 comp=aa' --headeronly $LOCALIP:$HGPORT 
'?cmd=getbundle=e93700bd72895c5addab234c56d4024b487a362f='
 -
   200 Script output follows
   content-type: application/mercurial-0.1
   date: $HTTP_DATE$
-  server: * (glob)
+  server: testing stub value
   transfer-encoding: chunked
 
 #if zstd
@@ -106,7 +106,7 @@
   content-length: 41
   content-type: application/mercurial-0.1
   date: $HTTP_DATE$
-  server: * (glob)
+  server: testing stub value
   
   e93700bd72895c5addab234c56d4024b487a362f
 
diff --git a/tests/test-hgweb-commands.t b/tests/test-hgweb-commands.t
--- a/tests/test-hgweb-commands.t
+++ b/tests/test-hgweb-commands.t
@@ -1924,7 +1924,7 @@
   content-length: 12
   content-type: application/mercurial-0.1
   date: $HTTP_DATE$
-  server: * (glob)
+  server: testing stub value
   
   0
   Not Found
diff --git a/tests/test-commandserver.t b/tests/test-commandserver.t
--- a/tests/test-commandserver.t
+++ b/tests/test-commandserver.t
@@ -215,6 +215,7 @@
   ui.nontty=true
   web.address=localhost
   web\.ipv6=(?:True|False) (re)
+  web.server-header=testing stub value
   *** runcommand init foo
   *** runcommand -R foo showconfig ui defaults
   ui.slash=True
diff --git a/tests/test-basic.t b/tests/test-basic.t
--- a/tests/test-basic.t
+++ b/tests/test-basic.t
@@ -12,6 +12,7 @@
   ui.promptecho=True
   web.address=localhost
   web\.ipv6=(?:True|False) (re)
+  web.server-header=testing stub value
   $ hg init t
   $ cd t
 
diff --git a/tests/test-archive.t b/tests/test-archive.t
--- a/tests/test-archive.t
+++ b/tests/test-archive.t
@@ -128,24 +128,24 @@
   content-type: application/x-gzip
   date: $HTTP_DATE$
   etag: W/"*" (glob)
-  server: * (glob)
+  server: testing stub value
   transfer-encoding: chunked
   
   body: size=408, sha1=8fa06531bddecc365a9f5edb0f88b65974bfe505
   % tar.bz2 and zip disallowed should both give 403
   403 Archive type not allowed: bz2
   content-type: text/html; charset=ascii
   date: $HTTP_DATE$
   etag: W/"*" (glob)
-  server: * (glob)
+  server: testing stub value
   transfer-encoding: chunked
   
   body: size=1451, sha1=4c5cf0f574446c44feb7f88f4e0e2a56bd92c352
   403 Archive type not allowed: zip
   content-type: text/html; charset=ascii
   date: $HTTP_DATE$
   etag: W/"*" (glob)
-  server: * (glob)
+  server: testing stub value
   transfer-encoding: chunked
   
   body: size=1451, sha1=cbfa5574b337348bfd0564cc534474d002e7d6c7
@@ -156,24 +156,24 @@
   content-type: application/x-bzip2
   date: $HTTP_DATE$
   etag: W/"*" (glob)
-  server: * (glob)
+  server: testing stub value
   transfer-encoding: chunked
   
   body: size=426, 

D2841: debugcommands: support sending HTTP requests with debugwireproto

2018-03-19 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGb6a7070e7663: debugcommands: support sending HTTP requests 
with debugwireproto (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2841?vs=6995=7159

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

AFFECTED FILES
  mercurial/debugcommands.py
  tests/test-http-protocol.t

CHANGE DETAILS

diff --git a/tests/test-http-protocol.t b/tests/test-http-protocol.t
--- a/tests/test-http-protocol.t
+++ b/tests/test-http-protocol.t
@@ -226,4 +226,39 @@
   s> phases
   response: bookmarks  \nnamespaces\nphases
 
+Same thing, but with "httprequest" command
+
+  $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
+  > httprequest GET ?cmd=listkeys
+  > accept: application/mercurial-0.1
+  > user-agent: mercurial/proto-1.0 (Mercurial 42)
+  > x-hgarg-1: namespace=namespaces
+  > EOF
+  using raw connection to peer
+  s> sendall(*, 0): (glob)
+  s> GET /?cmd=listkeys HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> accept: application/mercurial-0.1\r\n
+  s> user-agent: mercurial/proto-1.0 (Mercurial 42)\r\n (glob)
+  s> x-hgarg-1: namespace=namespaces\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> readline() -> 36:
+  s> HTTP/1.1 200 Script output follows\r\n
+  s> readline() -> 28:
+  s> Server: testing stub value\r\n
+  s> readline() -> *: (glob)
+  s> Date: $HTTP_DATE$\r\n
+  s> readline() -> 41:
+  s> Content-Type: application/mercurial-0.1\r\n
+  s> readline() -> 20:
+  s> Content-Length: 30\r\n
+  s> readline() -> 2:
+  s> \r\n
+  s> read(30) -> 30:
+  s> bookmarks \n
+  s> namespaces\n
+  s> phases
+
   $ killdaemons.py
diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py
--- a/mercurial/debugcommands.py
+++ b/mercurial/debugcommands.py
@@ -14,6 +14,7 @@
 import operator
 import os
 import random
+import re
 import socket
 import ssl
 import stat
@@ -2692,6 +2693,24 @@
 
 This action MUST be paired with a ``batchbegin`` action.
 
+httprequest  
+---
+
+(HTTP peer only)
+
+Send an HTTP request to the peer.
+
+The HTTP request line follows the ``httprequest`` action. e.g. ``GET 
/foo``.
+
+Arguments of the form ``: `` are interpreted as HTTP request
+headers to add to the request. e.g. ``Accept: foo``.
+
+The following arguments are special:
+
+``BODYFILE``
+The content of the file defined as the value to this argument will be
+transferred verbatim as the HTTP request body.
+
 close
 -
 
@@ -2754,6 +2773,7 @@
 stdin = None
 stdout = None
 stderr = None
+opener = None
 
 if opts['localssh']:
 # We start the SSH server in its own process so there is process
@@ -2909,6 +2929,42 @@
 ui.status(_('response #%d: %s\n') % (i, 
util.escapedata(chunk)))
 
 batchedcommands = None
+
+elif action.startswith('httprequest '):
+if not opener:
+raise error.Abort(_('cannot use httprequest without an HTTP '
+'peer'))
+
+request = action.split(' ', 2)
+if len(request) != 3:
+raise error.Abort(_('invalid httprequest: expected format is '
+'"httprequest  '))
+
+method, httppath = request[1:]
+headers = {}
+body = None
+for line in lines:
+line = line.lstrip()
+m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
+if m:
+headers[m.group(1)] = m.group(2)
+continue
+
+if line.startswith(b'BODYFILE '):
+with open(line.split(b' ', 1), 'rb') as fh:
+body = fh.read()
+else:
+raise error.Abort(_('unknown argument to httprequest: %s') 
%
+  line)
+
+url = path + httppath
+req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
+
+try:
+opener.open(req).read()
+except util.urlerr.urlerror as e:
+e.read()
+
 elif action == 'close':
 peer.close()
 elif action == 'readavailable':



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


D2726: debugcommands: support connecting to HTTP peers

2018-03-19 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGfc8939825632: debugcommands: support connecting to HTTP 
peers (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2726?vs=6994=7158

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

AFFECTED FILES
  mercurial/debugcommands.py
  tests/test-http-protocol.t

CHANGE DETAILS

diff --git a/tests/test-http-protocol.t b/tests/test-http-protocol.t
--- a/tests/test-http-protocol.t
+++ b/tests/test-http-protocol.t
@@ -161,3 +161,69 @@
   : 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
   0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 7a 6c 69 62 |t follows...zlib|
   0020: 78  |x|
+
+  $ killdaemons.py
+  $ cd ..
+
+Test listkeys for listing namespaces
+
+  $ hg init empty
+  $ hg -R empty serve -p $HGPORT -d --pid-file hg.pid
+  $ cat hg.pid > $DAEMON_PIDS
+
+  $ hg --verbose debugwireproto http://$LOCALIP:$HGPORT << EOF
+  > command listkeys
+  > namespace namespaces
+  > EOF
+  s> sendall(*, 0): (glob)
+  s> GET /?cmd=capabilities HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> accept: application/mercurial-0.1\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> user-agent: mercurial/proto-1.0 (Mercurial *)\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> readline() -> 36:
+  s> HTTP/1.1 200 Script output follows\r\n
+  s> readline() -> 28:
+  s> Server: testing stub value\r\n
+  s> readline() -> *: (glob)
+  s> Date: $HTTP_DATE$\r\n
+  s> readline() -> 41:
+  s> Content-Type: application/mercurial-0.1\r\n
+  s> readline() -> 21:
+  s> Content-Length: *\r\n (glob)
+  s> readline() -> 2:
+  s> \r\n
+  s> read(*) -> *: lookup branchmap pushkey known getbundle unbundlehash batch 
changegroupsubset streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ 
unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx 
compression=$BUNDLE2_COMPRESSIONS$ (glob)
+  sending listkeys command
+  s> sendall(*, 0): (glob)
+  s> GET /?cmd=listkeys HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> vary: X-HgArg-1,X-HgProto-1\r\n
+  s> x-hgarg-1: namespace=namespaces\r\n
+  s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
+  s> accept: application/mercurial-0.1\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> user-agent: mercurial/proto-1.0 (Mercurial *)\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> readline() -> 36:
+  s> HTTP/1.1 200 Script output follows\r\n
+  s> readline() -> 28:
+  s> Server: testing stub value\r\n
+  s> readline() -> *: (glob)
+  s> Date: $HTTP_DATE$\r\n
+  s> readline() -> 41:
+  s> Content-Type: application/mercurial-0.1\r\n
+  s> readline() -> 20:
+  s> Content-Length: 30\r\n
+  s> readline() -> 2:
+  s> \r\n
+  s> read(30) -> 30:
+  s> bookmarks \n
+  s> namespaces\n
+  s> phases
+  response: bookmarks  \nnamespaces\nphases
+
+  $ killdaemons.py
diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py
--- a/mercurial/debugcommands.py
+++ b/mercurial/debugcommands.py
@@ -48,6 +48,7 @@
 fileset,
 formatter,
 hg,
+httppeer,
 localrepo,
 lock as lockmod,
 logcmdutil,
@@ -2602,9 +2603,9 @@
 ('', 'peer', '', _('construct a specific version of the peer')),
 ('', 'noreadstderr', False, _('do not read from stderr of the 
remote')),
 ] + cmdutil.remoteopts,
-_('[REPO]'),
+_('[PATH]'),
 optionalrepo=True)
-def debugwireproto(ui, repo, **opts):
+def debugwireproto(ui, repo, path=None, **opts):
 """send wire protocol commands to a server
 
 This command can be used to issue wire protocol commands to remote
@@ -2740,12 +2741,19 @@
 raise error.Abort(_('invalid value for --peer'),
   hint=_('valid values are "raw", "ssh1", and "ssh2"'))
 
+if path and opts['localssh']:
+raise error.Abort(_('cannot specify --localssh with an explicit '
+'path'))
+
 if ui.interactive():
 ui.write(_('(waiting for commands on stdin)\n'))
 
 blocks = list(_parsewirelangblocks(ui.fin))
 
 proc = None
+stdin = None
+stdout = None
+stderr = None
 
 if opts['localssh']:
 # We start the SSH server in its own process so there is process
@@ -2793,22 +2801,61 @@
 peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr,
 autoreadstderr=autoreadstderr)
 
+elif path:
+# We bypass hg.peer() so we can proxy the sockets.
+# TODO consider not doing this because we skip
+# ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
+u = util.url(path)
+if u.scheme != 'http':
+raise error.Abort(_('only 

D2839: tests: use $HTTP_DATE$ for Date header

2018-03-19 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG16203c6079e7: tests: use $HTTP_DATE$ for Date header 
(authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2839?vs=6992=7154

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

AFFECTED FILES
  tests/common-pattern.py
  tests/test-archive.t
  tests/test-hgweb-commands.t
  tests/test-http-protocol.t

CHANGE DETAILS

diff --git a/tests/test-http-protocol.t b/tests/test-http-protocol.t
--- a/tests/test-http-protocol.t
+++ b/tests/test-http-protocol.t
@@ -49,41 +49,41 @@
   $ get-with-headers.py --headeronly $LOCALIP:$HGPORT 
'?cmd=getbundle=e93700bd72895c5addab234c56d4024b487a362f='
 -
   200 Script output follows
   content-type: application/mercurial-0.1
-  date: * (glob)
+  date: $HTTP_DATE$
   server: * (glob)
   transfer-encoding: chunked
 
 Server should send application/mercurial-0.1 when client says it wants it
 
   $ get-with-headers.py --hgproto '0.1' --headeronly $LOCALIP:$HGPORT 
'?cmd=getbundle=e93700bd72895c5addab234c56d4024b487a362f='
 -
   200 Script output follows
   content-type: application/mercurial-0.1
-  date: * (glob)
+  date: $HTTP_DATE$
   server: * (glob)
   transfer-encoding: chunked
 
 Server should send application/mercurial-0.2 when client says it wants it
 
   $ get-with-headers.py --hgproto '0.2' --headeronly $LOCALIP:$HGPORT 
'?cmd=getbundle=e93700bd72895c5addab234c56d4024b487a362f='
 -
   200 Script output follows
   content-type: application/mercurial-0.2
-  date: * (glob)
+  date: $HTTP_DATE$
   server: * (glob)
   transfer-encoding: chunked
 
   $ get-with-headers.py --hgproto '0.1 0.2' --headeronly $LOCALIP:$HGPORT 
'?cmd=getbundle=e93700bd72895c5addab234c56d4024b487a362f='
 -
   200 Script output follows
   content-type: application/mercurial-0.2
-  date: * (glob)
+  date: $HTTP_DATE$
   server: * (glob)
   transfer-encoding: chunked
 
 Requesting a compression format that server doesn't support results will fall 
back to 0.1
 
   $ get-with-headers.py --hgproto '0.2 comp=aa' --headeronly $LOCALIP:$HGPORT 
'?cmd=getbundle=e93700bd72895c5addab234c56d4024b487a362f='
 -
   200 Script output follows
   content-type: application/mercurial-0.1
-  date: * (glob)
+  date: $HTTP_DATE$
   server: * (glob)
   transfer-encoding: chunked
 
@@ -105,7 +105,7 @@
   200 Script output follows
   content-length: 41
   content-type: application/mercurial-0.1
-  date: * (glob)
+  date: $HTTP_DATE$
   server: * (glob)
   
   e93700bd72895c5addab234c56d4024b487a362f
diff --git a/tests/test-hgweb-commands.t b/tests/test-hgweb-commands.t
--- a/tests/test-hgweb-commands.t
+++ b/tests/test-hgweb-commands.t
@@ -1923,7 +1923,7 @@
   404 Not Found
   content-length: 12
   content-type: application/mercurial-0.1
-  date: * (glob)
+  date: $HTTP_DATE$
   server: * (glob)
   
   0
diff --git a/tests/test-archive.t b/tests/test-archive.t
--- a/tests/test-archive.t
+++ b/tests/test-archive.t
@@ -126,24 +126,24 @@
   200 Script output follows
   content-disposition: attachment; filename=test-archive-1701ef1f1510.tar.gz
   content-type: application/x-gzip
-  date: * (glob)
+  date: $HTTP_DATE$
   etag: W/"*" (glob)
   server: * (glob)
   transfer-encoding: chunked
   
   body: size=408, sha1=8fa06531bddecc365a9f5edb0f88b65974bfe505
   % tar.bz2 and zip disallowed should both give 403
   403 Archive type not allowed: bz2
   content-type: text/html; charset=ascii
-  date: * (glob)
+  date: $HTTP_DATE$
   etag: W/"*" (glob)
   server: * (glob)
   transfer-encoding: chunked
   
   body: size=1451, sha1=4c5cf0f574446c44feb7f88f4e0e2a56bd92c352
   403 Archive type not allowed: zip
   content-type: text/html; charset=ascii
-  date: * (glob)
+  date: $HTTP_DATE$
   etag: W/"*" (glob)
   server: * (glob)
   transfer-encoding: chunked
@@ -154,24 +154,24 @@
   200 Script output follows
   content-disposition: attachment; filename=test-archive-1701ef1f1510.tar.bz2
   content-type: application/x-bzip2
-  date: * (glob)
+  date: $HTTP_DATE$
   etag: W/"*" (glob)
   server: * (glob)
   transfer-encoding: chunked
   
   body: size=426, sha1=8d87f5aba6e14f1bfea6c232985982c278b2fb0b
   % zip and tar.gz disallowed should both give 403
   403 Archive type not allowed: zip
   content-type: text/html; charset=ascii
-  date: * (glob)
+  date: $HTTP_DATE$
   etag: W/"*" (glob)
   server: * (glob)
   transfer-encoding: chunked
   
   body: size=1451, sha1=cbfa5574b337348bfd0564cc534474d002e7d6c7
   403 Archive type not allowed: gz
   content-type: text/html; charset=ascii
-  date: * (glob)
+  date: $HTTP_DATE$
   etag: W/"*" (glob)
   server: * (glob)
   transfer-encoding: chunked
@@ -182,24 +182,24 @@
   200 Script output follows
   content-disposition: 

D2722: url: add HTTP handler that uses a proxied socket

2018-03-19 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG02221d6fb041: url: add HTTP handler that uses a proxied 
socket (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2722?vs=6717=7157

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

AFFECTED FILES
  mercurial/url.py

CHANGE DETAILS

diff --git a/mercurial/url.py b/mercurial/url.py
--- a/mercurial/url.py
+++ b/mercurial/url.py
@@ -296,6 +296,34 @@
 _generic_start_transaction(self, h, req)
 return keepalive.HTTPHandler._start_transaction(self, h, req)
 
+class logginghttpconnection(keepalive.HTTPConnection):
+def __init__(self, createconn, *args, **kwargs):
+keepalive.HTTPConnection.__init__(self, *args, **kwargs)
+self._create_connection = createconn
+
+class logginghttphandler(httphandler):
+"""HTTP handler that logs socket I/O."""
+def __init__(self, logfh, name, observeropts):
+super(logginghttphandler, self).__init__()
+
+self._logfh = logfh
+self._logname = name
+self._observeropts = observeropts
+
+# do_open() calls the passed class to instantiate an HTTPConnection. We
+# pass in a callable method that creates a custom HTTPConnection instance
+# whose callback to create the socket knows how to proxy the socket.
+def http_open(self, req):
+return self.do_open(self._makeconnection, req)
+
+def _makeconnection(self, *args, **kwargs):
+def createconnection(*args, **kwargs):
+sock = socket.create_connection(*args, **kwargs)
+return util.makeloggingsocket(self._logfh, sock, self._logname,
+  **self._observeropts)
+
+return logginghttpconnection(createconnection, *args, **kwargs)
+
 if has_https:
 class httpsconnection(httplib.HTTPConnection):
 response_class = keepalive.HTTPResponse
@@ -465,14 +493,32 @@
 
 handlerfuncs = []
 
-def opener(ui, authinfo=None, useragent=None):
+def opener(ui, authinfo=None, useragent=None, loggingfh=None,
+   loggingname=b's', loggingopts=None):
 '''
 construct an opener suitable for urllib2
 authinfo will be added to the password manager
+
+The opener can be configured to log socket events if the various
+``logging*`` arguments are specified.
+
+``loggingfh`` denotes a file object to log events to.
+``loggingname`` denotes the name of the to print when logging.
+``loggingopts`` is a dict of keyword arguments to pass to the constructed
+``util.socketobserver`` instance.
 '''
-handlers = [httphandler()]
-if has_https:
-handlers.append(httpshandler(ui))
+handlers = []
+
+if loggingfh:
+handlers.append(logginghttphandler(loggingfh, loggingname,
+   loggingopts or {}))
+# We don't yet support HTTPS when logging I/O. If we attempt to open
+# an HTTPS URL, we'll likely fail due to unknown protocol.
+
+else:
+handlers.append(httphandler())
+if has_https:
+handlers.append(httpshandler(ui))
 
 handlers.append(proxyhandler(ui))
 



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


D2720: debugcommands: introduce actions to perform deterministic reads

2018-03-19 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG143219fc2620: debugcommands: introduce actions to perform 
deterministic reads (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2720?vs=6976=7153

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

AFFECTED FILES
  mercurial/debugcommands.py

CHANGE DETAILS

diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py
--- a/mercurial/debugcommands.py
+++ b/mercurial/debugcommands.py
@@ -2715,6 +2715,21 @@
 
 Read a line of output from the server. If there are multiple output
 pipes, reads only the main pipe.
+
+ereadline
+-
+
+Like ``readline``, but read from the stderr pipe, if available.
+
+read 
+
+
+``read()`` N bytes from the server's main output pipe.
+
+eread 
+-
+
+``read()`` N bytes from the server's stderr pipe, if available.
 """
 opts = pycompat.byteskwargs(opts)
 
@@ -2855,6 +2870,14 @@
 stderr.read()
 elif action == 'readline':
 stdout.readline()
+elif action == 'ereadline':
+stderr.readline()
+elif action.startswith('read '):
+count = int(action.split(' ', 1)[1])
+stdout.read(count)
+elif action.startswith('eread '):
+count = int(action.split(' ', 1)[1])
+stderr.read(count)
 else:
 raise error.Abort(_('unknown action: %s') % action)
 



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


D2721: util: observable proxy objects for sockets

2018-03-19 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG8453699a1f21: util: observable proxy objects for sockets 
(authored by indygreg, committed by ).

CHANGED PRIOR TO COMMIT
  https://phab.mercurial-scm.org/D2721?vs=7007=7156#toc

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2721?vs=7007=7156

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

AFFECTED FILES
  mercurial/util.py

CHANGE DETAILS

diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -689,6 +689,125 @@
 
 return res
 
+PROXIED_SOCKET_METHODS = {
+r'makefile',
+r'recv',
+r'recvfrom',
+r'recvfrom_into',
+r'recv_into',
+r'send',
+r'sendall',
+r'sendto',
+r'setblocking',
+r'settimeout',
+r'gettimeout',
+r'setsockopt',
+}
+
+class socketproxy(object):
+"""A proxy around a socket that tells a watcher when events occur.
+
+This is like ``fileobjectproxy`` except for sockets.
+
+This type is intended to only be used for testing purposes. Think hard
+before using it in important code.
+"""
+__slots__ = (
+r'_orig',
+r'_observer',
+)
+
+def __init__(self, sock, observer):
+object.__setattr__(self, r'_orig', sock)
+object.__setattr__(self, r'_observer', observer)
+
+def __getattribute__(self, name):
+if name in PROXIED_SOCKET_METHODS:
+return object.__getattribute__(self, name)
+
+return getattr(object.__getattribute__(self, r'_orig'), name)
+
+def __delattr__(self, name):
+return delattr(object.__getattribute__(self, r'_orig'), name)
+
+def __setattr__(self, name, value):
+return setattr(object.__getattribute__(self, r'_orig'), name, value)
+
+def __nonzero__(self):
+return bool(object.__getattribute__(self, r'_orig'))
+
+__bool__ = __nonzero__
+
+def _observedcall(self, name, *args, **kwargs):
+# Call the original object.
+orig = object.__getattribute__(self, r'_orig')
+res = getattr(orig, name)(*args, **kwargs)
+
+# Call a method on the observer of the same name with arguments
+# so it can react, log, etc.
+observer = object.__getattribute__(self, r'_observer')
+fn = getattr(observer, name, None)
+if fn:
+fn(res, *args, **kwargs)
+
+return res
+
+def makefile(self, *args, **kwargs):
+res = object.__getattribute__(self, r'_observedcall')(
+r'makefile', *args, **kwargs)
+
+# The file object may be used for I/O. So we turn it into a
+# proxy using our observer.
+observer = object.__getattribute__(self, r'_observer')
+return makeloggingfileobject(observer.fh, res, observer.name,
+ reads=observer.reads,
+ writes=observer.writes,
+ logdata=observer.logdata)
+
+def recv(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'recv', *args, **kwargs)
+
+def recvfrom(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'recvfrom', *args, **kwargs)
+
+def recvfrom_into(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'recvfrom_into', *args, **kwargs)
+
+def recv_into(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'recv_info', *args, **kwargs)
+
+def send(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'send', *args, **kwargs)
+
+def sendall(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'sendall', *args, **kwargs)
+
+def sendto(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'sendto', *args, **kwargs)
+
+def setblocking(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'setblocking', *args, **kwargs)
+
+def settimeout(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'settimeout', *args, **kwargs)
+
+def gettimeout(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'gettimeout', *args, **kwargs)
+
+def setsockopt(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'setsockopt', *args, **kwargs)
+
 DATA_ESCAPE_MAP = {pycompat.bytechr(i): br'\x%02x' % i for i in range(256)}
 DATA_ESCAPE_MAP.update({
 b'\\': b'',
@@ -703,15 +822,7 @@
 
 return DATA_ESCAPE_RE.sub(lambda m: DATA_ESCAPE_MAP[m.group(0)], s)
 
-class fileobjectobserver(object):
-"""Logs file object 

D2725: httppeer: refactor how httppeer is created (API)

2018-03-19 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG8e89c2bec1f7: httppeer: refactor how httppeer is created 
(API) (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2725?vs=7071=7152

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

AFFECTED FILES
  mercurial/httppeer.py
  tests/test-check-interfaces.py

CHANGE DETAILS

diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py
--- a/tests/test-check-interfaces.py
+++ b/tests/test-check-interfaces.py
@@ -50,10 +50,13 @@
 def _restrictcapabilities(self, caps):
 pass
 
+class dummyopener(object):
+handlers = []
+
 # Facilitates testing sshpeer without requiring an SSH server.
 class badpeer(httppeer.httppeer):
 def __init__(self):
-super(badpeer, self).__init__(uimod.ui(), 'http://localhost')
+super(badpeer, self).__init__(None, None, None, dummyopener())
 self.badattribute = True
 
 def badmethod(self):
@@ -67,7 +70,7 @@
 ui = uimod.ui()
 
 checkobject(badpeer())
-checkobject(httppeer.httppeer(ui, 'http://localhost'))
+checkobject(httppeer.httppeer(None, None, None, dummyopener()))
 checkobject(localrepo.localpeer(dummyrepo()))
 checkobject(sshpeer.sshv1peer(ui, 'ssh://localhost/foo', None, dummypipe(),
   dummypipe(), None, None))
diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py
--- a/mercurial/httppeer.py
+++ b/mercurial/httppeer.py
@@ -134,32 +134,20 @@
 self._index = 0
 
 class httppeer(wireproto.wirepeer):
-def __init__(self, ui, path):
+def __init__(self, ui, path, url, opener):
+self._ui = ui
 self._path = path
+self._url = url
 self._caps = None
-self._urlopener = None
+self._urlopener = opener
 # This is an its own attribute to facilitate extensions overriding
 # the default type.
 self._requestbuilder = urlreq.request
-u = util.url(path)
-if u.query or u.fragment:
-raise error.Abort(_('unsupported URL component: "%s"') %
- (u.query or u.fragment))
-
-# urllib cannot handle URLs with embedded user or passwd
-self._url, authinfo = u.authinfo()
-
-self._ui = ui
-ui.debug('using %s\n' % self._url)
-
-self._urlopener = urlmod.opener(ui, authinfo)
 
 def __del__(self):
-urlopener = getattr(self, '_urlopener', None)
-if urlopener:
-for h in urlopener.handlers:
-h.close()
-getattr(h, "close_all", lambda: None)()
+for h in self._urlopener.handlers:
+h.close()
+getattr(h, "close_all", lambda: None)()
 
 def _openurl(self, req):
 if (self._ui.debugflag
@@ -483,15 +471,29 @@
 def _abort(self, exception):
 raise exception
 
+def makepeer(ui, path):
+u = util.url(path)
+if u.query or u.fragment:
+raise error.Abort(_('unsupported URL component: "%s"') %
+  (u.query or u.fragment))
+
+# urllib cannot handle URLs with embedded user or passwd.
+url, authinfo = u.authinfo()
+ui.debug('using %s\n' % url)
+
+opener = urlmod.opener(ui, authinfo)
+
+return httppeer(ui, path, url, opener)
+
 def instance(ui, path, create):
 if create:
 raise error.Abort(_('cannot create new http repository'))
 try:
 if path.startswith('https:') and not urlmod.has_https:
 raise error.Abort(_('Python support for SSL and HTTPS '
 'is not installed'))
 
-inst = httppeer(ui, path)
+inst = makepeer(ui, path)
 inst._fetchcaps()
 
 return inst



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


D2842: util: don't log low-level I/O calls for HTTP peer

2018-03-19 Thread durin42 (Augie Fackler)
durin42 accepted this revision.
durin42 added a comment.
This revision is now accepted and ready to land.


  This one needs rebased, but looks good.

REPOSITORY
  rHG Mercurial

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

To: indygreg, #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


D2869: wireproto: add request IDs to frames

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 7145.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2869?vs=7053=7145

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

AFFECTED FILES
  mercurial/debugcommands.py
  mercurial/help/internals/wireprotocol.txt
  mercurial/wireprotoframing.py
  mercurial/wireprotoserver.py
  tests/test-http-api-httpv2.t
  tests/test-wireproto-serverreactor.py

CHANGE DETAILS

diff --git a/tests/test-wireproto-serverreactor.py 
b/tests/test-wireproto-serverreactor.py
--- a/tests/test-wireproto-serverreactor.py
+++ b/tests/test-wireproto-serverreactor.py
@@ -18,52 +18,53 @@
 Emits a generator of results from ``onframerecv()`` calls.
 """
 for frame in gen:
-frametype, frameflags, framelength = framing.parseheader(frame)
+rid, frametype, frameflags, framelength = framing.parseheader(frame)
 payload = frame[framing.FRAME_HEADER_SIZE:]
 assert len(payload) == framelength
 
-yield reactor.onframerecv(frametype, frameflags, payload)
+yield reactor.onframerecv(rid, frametype, frameflags, payload)
 
-def sendcommandframes(reactor, cmd, args, datafh=None):
+def sendcommandframes(reactor, rid, cmd, args, datafh=None):
 """Generate frames to run a command and send them to a reactor."""
-return sendframes(reactor, framing.createcommandframes(cmd, args, datafh))
+return sendframes(reactor,
+  framing.createcommandframes(rid, cmd, args, datafh))
 
 class FrameTests(unittest.TestCase):
 def testdataexactframesize(self):
 data = util.bytesio(b'x' * framing.DEFAULT_MAX_FRAME_SIZE)
 
-frames = list(framing.createcommandframes(b'command', {}, data))
+frames = list(framing.createcommandframes(1, b'command', {}, data))
 self.assertEqual(frames, [
-ffs(b'command-name have-data command'),
-ffs(b'command-data continuation %s' % data.getvalue()),
-ffs(b'command-data eos ')
+ffs(b'1 command-name have-data command'),
+ffs(b'1 command-data continuation %s' % data.getvalue()),
+ffs(b'1 command-data eos ')
 ])
 
 def testdatamultipleframes(self):
 data = util.bytesio(b'x' * (framing.DEFAULT_MAX_FRAME_SIZE + 1))
-frames = list(framing.createcommandframes(b'command', {}, data))
+frames = list(framing.createcommandframes(1, b'command', {}, data))
 self.assertEqual(frames, [
-ffs(b'command-name have-data command'),
-ffs(b'command-data continuation %s' % (
+ffs(b'1 command-name have-data command'),
+ffs(b'1 command-data continuation %s' % (
 b'x' * framing.DEFAULT_MAX_FRAME_SIZE)),
-ffs(b'command-data eos x'),
+ffs(b'1 command-data eos x'),
 ])
 
 def testargsanddata(self):
 data = util.bytesio(b'x' * 100)
 
-frames = list(framing.createcommandframes(b'command', {
+frames = list(framing.createcommandframes(1, b'command', {
 b'key1': b'key1value',
 b'key2': b'key2value',
 b'key3': b'key3value',
 }, data))
 
 self.assertEqual(frames, [
-ffs(b'command-name have-args|have-data command'),
-ffs(br'command-argument 0 \x04\x00\x09\x00key1key1value'),
-ffs(br'command-argument 0 \x04\x00\x09\x00key2key2value'),
-ffs(br'command-argument eoa \x04\x00\x09\x00key3key3value'),
-ffs(b'command-data eos %s' % data.getvalue()),
+ffs(b'1 command-name have-args|have-data command'),
+ffs(br'1 command-argument 0 \x04\x00\x09\x00key1key1value'),
+ffs(br'1 command-argument 0 \x04\x00\x09\x00key2key2value'),
+ffs(br'1 command-argument eoa \x04\x00\x09\x00key3key3value'),
+ffs(b'1 command-data eos %s' % data.getvalue()),
 ])
 
 class ServerReactorTests(unittest.TestCase):
@@ -86,10 +87,11 @@
 def test1framecommand(self):
 """Receiving a command in a single frame yields request to run it."""
 reactor = makereactor()
-results = list(sendcommandframes(reactor, b'mycommand', {}))
+results = list(sendcommandframes(reactor, 1, b'mycommand', {}))
 self.assertEqual(len(results), 1)
 self.assertaction(results[0], 'runcommand')
 self.assertEqual(results[0][1], {
+'requestid': 1,
 'command': b'mycommand',
 'args': {},
 'data': None,
@@ -100,50 +102,53 @@
 
 def test1argument(self):
 reactor = makereactor()
-results = list(sendcommandframes(reactor, b'mycommand',
+results = list(sendcommandframes(reactor, 41, b'mycommand',
  {b'foo': b'bar'}))
 self.assertEqual(len(results), 2)
 self.assertaction(results[0], 'wantframe')
 self.assertaction(results[1], 'runcommand')
 

D2884: wireproto: experimental command to emit file data

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 7150.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2884?vs=7081=7150

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

AFFECTED FILES
  mercurial/configitems.py
  mercurial/help/internals/wireprotocol.txt
  mercurial/wireproto.py
  tests/test-wireproto-revsfiledata.t

CHANGE DETAILS

diff --git a/tests/test-wireproto-revsfiledata.t 
b/tests/test-wireproto-revsfiledata.t
new file mode 100644
--- /dev/null
+++ b/tests/test-wireproto-revsfiledata.t
@@ -0,0 +1,244 @@
+  $ CMDNAME=exp-revfilesdata-001
+
+  $ cat >> $HGRCPATH << EOF
+  > [server]
+  > compressionengines = none
+  > EOF
+
+  $ hg init server
+  $ cd server
+  $ echo 'foo revision 0' > foo
+  $ hg -q commit -A -m initial
+  $ echo 'foo revision 1' > foo
+  $ echo 'bar 0' > bar
+  $ hg -q commit -A -m second
+  $ chmod +x foo
+  $ hg commit -m third
+
+revfilesdata requires a config options
+
+  $ hg serve -p $HGPORT -d --pid-file hg.pid
+  $ cat hg.pid > $DAEMON_PIDS
+
+  $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
+  > httprequest GET ?cmd=$CMDNAME
+  > user-agent: test
+  > x-hgarg-1: node=irrelevant
+  > x-hgproto-1: 0.2
+  > EOF
+  using raw connection to peer
+  s> GET /?cmd=exp-revfilesdata-001 HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> user-agent: test\r\n
+  s> x-hgarg-1: node=irrelevant\r\n
+  s> x-hgproto-1: 0.2\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> HTTP/1.1 200 Script output follows\r\n
+  s> Server: testing stub value\r\n
+  s> Date: $HTTP_DATE$\r\n
+  s> Content-Type: application/hg-error\r\n
+  s> Content-Length: 49\r\n
+  s> \r\n
+  s> revfilesdata wire protocol command is not enabled
+
+  $ cat >> $HGRCPATH << EOF
+  > [experimental]
+  > server.revfilesdata = true
+  > EOF
+
+  $ killdaemons.py
+  $ hg serve -p $HGPORT -d --pid-file hg.pid
+  $ cat hg.pid > $DAEMON_PIDS
+
+Node must be full hash
+
+  $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
+  > httprequest GET ?cmd=$CMDNAME
+  > user-agent: test
+  > x-hgarg-1: node=tip
+  > x-hgproto-1: 0.2
+  > EOF
+  using raw connection to peer
+  s> GET /?cmd=exp-revfilesdata-001 HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> user-agent: test\r\n
+  s> x-hgarg-1: node=tip\r\n
+  s> x-hgproto-1: 0.2\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> HTTP/1.1 200 Script output follows\r\n
+  s> Server: testing stub value\r\n
+  s> Date: $HTTP_DATE$\r\n
+  s> Content-Type: application/hg-error\r\n
+  s> Content-Length: 31\r\n
+  s> \r\n
+  s> nodes argument must be 40 bytes
+
+And it must be a known hash
+
+  $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
+  > httprequest GET ?cmd=$CMDNAME
+  > user-agent: test
+  > x-hgarg-1: node=
+  > x-hgproto-1: 0.2
+  > EOF
+  using raw connection to peer
+  s> GET /?cmd=exp-revfilesdata-001 HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> user-agent: test\r\n
+  s> x-hgarg-1: node=\r\n
+  s> x-hgproto-1: 0.2\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> HTTP/1.1 200 Script output follows\r\n
+  s> Server: testing stub value\r\n
+  s> Date: $HTTP_DATE$\r\n
+  s> Content-Type: application/hg-error\r\n
+  s> Content-Length: 54\r\n
+  s> \r\n
+  s> unknown node: 
+
+Request for revision with single file
+
+  $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
+  > httprequest GET ?cmd=$CMDNAME
+  > user-agent: test
+  > x-hgarg-1: node=a64d23ad96a87844da3723df73c209a1c5507999
+  > x-hgproto-1: 0.2
+  > EOF
+  using raw connection to peer
+  s> GET /?cmd=exp-revfilesdata-001 HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> user-agent: test\r\n
+  s> x-hgarg-1: node=a64d23ad96a87844da3723df73c209a1c5507999\r\n
+  s> x-hgproto-1: 0.2\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> HTTP/1.1 200 Script output follows\r\n
+  s> Server: testing stub value\r\n
+  s> Date: $HTTP_DATE$\r\n
+  s> Content-Type: application/mercurial-0.2\r\n
+  s> Transfer-Encoding: chunked\r\n
+  s> \r\n
+  s> 1\r\n
+  s> \x04
+  s> \r\n
+  s> 4\r\n
+  s> none
+  s> \r\n
+  s> 1f\r\n
+  s> 
F\x92\xc6\xd5/y\x90\xcce\x0c\xea\x80\xd0\xca\xe1\xde6\xb5wX\x03\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00
+  s> \r\n
+  s> 3\r\n
+  s> foo
+  s> \r\n
+  s> f\r\n
+  s> foo revision 0\n
+  s> \r\n
+  s> 0\r\n
+  s> \r\n
+
+Revision with multiple files
+
+  $ hg 

D2852: wireproto: implement basic frame reading and processing

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 7140.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2852?vs=7048=7140

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

AFFECTED FILES
  mercurial/configitems.py
  mercurial/util.py
  mercurial/wireprotoframing.py
  mercurial/wireprotoserver.py
  tests/test-http-api-httpv2.t
  tests/test-wireproto-serverreactor.py

CHANGE DETAILS

diff --git a/tests/test-wireproto-serverreactor.py 
b/tests/test-wireproto-serverreactor.py
new file mode 100644
--- /dev/null
+++ b/tests/test-wireproto-serverreactor.py
@@ -0,0 +1,275 @@
+from __future__ import absolute_import, print_function
+
+import unittest
+
+from mercurial import (
+util,
+wireprotoframing as framing,
+)
+
+ffs = framing.makeframefromhumanstring
+
+def makereactor():
+return framing.serverreactor()
+
+def sendframes(reactor, gen):
+"""Send a generator of frame bytearray to a reactor.
+
+Emits a generator of results from ``onframerecv()`` calls.
+"""
+for frame in gen:
+frametype, frameflags, framelength = framing.parseheader(frame)
+payload = frame[framing.FRAME_HEADER_SIZE:]
+assert len(payload) == framelength
+
+yield reactor.onframerecv(frametype, frameflags, payload)
+
+def sendcommandframes(reactor, cmd, args, datafh=None):
+"""Generate frames to run a command and send them to a reactor."""
+return sendframes(reactor, framing.createcommandframes(cmd, args, datafh))
+
+class FrameTests(unittest.TestCase):
+def testdataexactframesize(self):
+data = util.bytesio(b'x' * framing.DEFAULT_MAX_FRAME_SIZE)
+
+frames = list(framing.createcommandframes(b'command', {}, data))
+self.assertEqual(frames, [
+ffs(b'command-name have-data command'),
+ffs(b'command-data continuation %s' % data.getvalue()),
+ffs(b'command-data eos ')
+])
+
+def testdatamultipleframes(self):
+data = util.bytesio(b'x' * (framing.DEFAULT_MAX_FRAME_SIZE + 1))
+frames = list(framing.createcommandframes(b'command', {}, data))
+self.assertEqual(frames, [
+ffs(b'command-name have-data command'),
+ffs(b'command-data continuation %s' % (
+b'x' * framing.DEFAULT_MAX_FRAME_SIZE)),
+ffs(b'command-data eos x'),
+])
+
+def testargsanddata(self):
+data = util.bytesio(b'x' * 100)
+
+frames = list(framing.createcommandframes(b'command', {
+b'key1': b'key1value',
+b'key2': b'key2value',
+b'key3': b'key3value',
+}, data))
+
+self.assertEqual(frames, [
+ffs(b'command-name have-args|have-data command'),
+ffs(br'command-argument 0 \x04\x00\x09\x00key1key1value'),
+ffs(br'command-argument 0 \x04\x00\x09\x00key2key2value'),
+ffs(br'command-argument eoa \x04\x00\x09\x00key3key3value'),
+ffs(b'command-data eos %s' % data.getvalue()),
+])
+
+class ServerReactorTests(unittest.TestCase):
+def _sendsingleframe(self, reactor, s):
+results = list(sendframes(reactor, [ffs(s)]))
+self.assertEqual(len(results), 1)
+
+return results[0]
+
+def assertaction(self, res, expected):
+self.assertIsInstance(res, tuple)
+self.assertEqual(len(res), 2)
+self.assertIsInstance(res[1], dict)
+self.assertEqual(res[0], expected)
+
+def test1framecommand(self):
+"""Receiving a command in a single frame yields request to run it."""
+reactor = makereactor()
+results = list(sendcommandframes(reactor, b'mycommand', {}))
+self.assertEqual(len(results), 1)
+self.assertaction(results[0], 'runcommand')
+self.assertEqual(results[0][1], {
+'command': b'mycommand',
+'args': {},
+'data': None,
+})
+
+def test1argument(self):
+reactor = makereactor()
+results = list(sendcommandframes(reactor, b'mycommand',
+ {b'foo': b'bar'}))
+self.assertEqual(len(results), 2)
+self.assertaction(results[0], 'wantframe')
+self.assertaction(results[1], 'runcommand')
+self.assertEqual(results[1][1], {
+'command': b'mycommand',
+'args': {b'foo': b'bar'},
+'data': None,
+})
+
+def testmultiarguments(self):
+reactor = makereactor()
+results = list(sendcommandframes(reactor, b'mycommand',
+ {b'foo': b'bar', b'biz': b'baz'}))
+self.assertEqual(len(results), 3)
+self.assertaction(results[0], 'wantframe')
+self.assertaction(results[1], 'wantframe')
+self.assertaction(results[2], 'runcommand')
+self.assertEqual(results[2][1], {
+'command': b'mycommand',
+'args': {b'foo': b'bar', b'biz': b'baz'},
+'data': 

D2860: wireproto: buffer output frames when in half duplex mode

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 7144.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2860?vs=7052=7144

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

AFFECTED FILES
  mercurial/wireprotoframing.py
  mercurial/wireprotoserver.py
  tests/test-http-api-httpv2.t
  tests/test-wireproto-serverreactor.py

CHANGE DETAILS

diff --git a/tests/test-wireproto-serverreactor.py 
b/tests/test-wireproto-serverreactor.py
--- a/tests/test-wireproto-serverreactor.py
+++ b/tests/test-wireproto-serverreactor.py
@@ -9,8 +9,8 @@
 
 ffs = framing.makeframefromhumanstring
 
-def makereactor():
-return framing.serverreactor()
+def makereactor(deferoutput=False):
+return framing.serverreactor(deferoutput=deferoutput)
 
 def sendframes(reactor, gen):
 """Send a generator of frame bytearray to a reactor.
@@ -95,6 +95,9 @@
 'data': None,
 })
 
+result = reactor.oninputeof()
+self.assertaction(result, 'noop')
+
 def test1argument(self):
 reactor = makereactor()
 results = list(sendcommandframes(reactor, b'mycommand',
@@ -310,6 +313,37 @@
 b'error-response application some message',
 ])
 
+def test1commanddeferresponse(self):
+"""Responses when in deferred output mode are delayed until EOF."""
+reactor = makereactor(deferoutput=True)
+results = list(sendcommandframes(reactor, b'mycommand', {}))
+self.assertEqual(len(results), 1)
+self.assertaction(results[0], 'runcommand')
+
+result = reactor.onbytesresponseready(b'response')
+self.assertaction(result, 'noop')
+result = reactor.oninputeof()
+self.assertaction(result, 'sendframes')
+self.assertframesequal(result[1]['framegen'], [
+b'bytes-response eos response',
+])
+
+def testmultiplecommanddeferresponse(self):
+reactor = makereactor(deferoutput=True)
+list(sendcommandframes(reactor, b'command1', {}))
+list(sendcommandframes(reactor, b'command2', {}))
+
+result = reactor.onbytesresponseready(b'response1')
+self.assertaction(result, 'noop')
+result = reactor.onbytesresponseready(b'response2')
+self.assertaction(result, 'noop')
+result = reactor.oninputeof()
+self.assertaction(result, 'sendframes')
+self.assertframesequal(result[1]['framegen'], [
+b'bytes-response eos response1',
+b'bytes-response eos response2'
+])
+
 if __name__ == '__main__':
 import silenttestrunner
 silenttestrunner.main(__name__)
diff --git a/tests/test-http-api-httpv2.t b/tests/test-http-api-httpv2.t
--- a/tests/test-http-api-httpv2.t
+++ b/tests/test-http-api-httpv2.t
@@ -401,14 +401,15 @@
   s> Server: testing stub value\r\n
   s> Date: $HTTP_DATE$\r\n
   s> Content-Type: text/plain\r\n
-  s> Content-Length: 291\r\n
+  s> Content-Length: 310\r\n
   s> \r\n
   s> received: 1 2 command1\n
   s> ["wantframe", {"state": "command-receiving-args"}]\n
   s> received: 2 0 \x03\x00\x04\x00fooval1\n
   s> ["wantframe", {"state": "command-receiving-args"}]\n
   s> received: 2 2 \x04\x00\x03\x00bar1val\n
   s> ["runcommand", {"args": {"bar1": "val", "foo": "val1"}, "command": 
"command1", "data": null}]\n
-  s> received: 
+  s> received: \n
+  s> {"action": "noop"}
 
   $ cat error.log
diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py
+++ b/mercurial/wireprotoserver.py
@@ -401,6 +401,10 @@
 states.append(json.dumps((action, meta), sort_keys=True,
  separators=(', ', ': ')))
 
+action, meta = reactor.oninputeof()
+meta['action'] = action
+states.append(json.dumps(meta, sort_keys=True, separators=(', ',': ')))
+
 res.status = b'200 OK'
 res.headers[b'Content-Type'] = b'text/plain'
 res.setbodybytes(b'\n'.join(states))
@@ -411,7 +415,10 @@
 Called when the HTTP request contains unified frame-based protocol
 frames for evaluation.
 """
-reactor = wireprotoframing.serverreactor()
+# TODO Some HTTP clients are full duplex and can receive data before
+# the entire request is transmitted. Figure out a way to indicate support
+# for that so we can opt into full duplex mode.
+reactor = wireprotoframing.serverreactor(deferoutput=True)
 seencommand = False
 
 while True:
@@ -448,6 +455,19 @@
 raise error.ProgrammingError(
 'unhandled action from frame processor: %s' % action)
 
+action, meta = reactor.oninputeof()
+if action == 'sendframes':
+# We assume we haven't started sending the response yet. If we're
+# wrong, the response type will raise an exception.
+res.status = b'200 OK'
+res.headers[b'Content-Type'] = FRAMINGTYPE
+res.setbodygen(meta['framegen'])
+elif action 

D2856: wireproto: nominally don't expose "batch" to version 2 wire transports

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 7141.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2856?vs=7083=7141

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

AFFECTED FILES
  mercurial/wireproto.py
  mercurial/wireprotoserver.py
  tests/test-debugcommands.t
  tests/test-hgweb-commands.t
  tests/test-http-bad-server.t
  tests/test-http-protocol.t
  tests/test-ssh-bundle1.t
  tests/test-ssh-proto-unbundle.t
  tests/test-ssh-proto.t
  tests/test-ssh.t

CHANGE DETAILS

diff --git a/tests/test-ssh.t b/tests/test-ssh.t
--- a/tests/test-ssh.t
+++ b/tests/test-ssh.t
@@ -498,7 +498,7 @@
   sending between command
   remote: 403 (sshv1 !)
   protocol upgraded to exp-ssh-v2-0001 (sshv2 !)
-  remote: capabilities: lookup branchmap pushkey known getbundle unbundlehash 
batch changegroupsubset streamreqs=generaldelta,revlogv1 
$USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
+  remote: capabilities: lookup branchmap pushkey known getbundle unbundlehash 
changegroupsubset streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ 
unbundle=HG10GZ,HG10BZ,HG10UN batch
   remote: 1 (sshv1 !)
   query 1; heads
   devel-peer-request: batched-content
diff --git a/tests/test-ssh-proto.t b/tests/test-ssh-proto.t
--- a/tests/test-ssh-proto.t
+++ b/tests/test-ssh-proto.t
@@ -64,7 +64,7 @@
   devel-peer-request:   pairs: 81 bytes
   sending between command
   remote: 403
-  remote: capabilities: lookup branchmap pushkey known getbundle unbundlehash 
batch changegroupsubset streamreqs=generaldelta,revlogv1 
$USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
+  remote: capabilities: lookup branchmap pushkey known getbundle unbundlehash 
changegroupsubset streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ 
unbundle=HG10GZ,HG10BZ,HG10UN batch
   remote: 1
   url: ssh://user@dummy/server
   local: no
@@ -84,16 +84,16 @@
   o> readline() -> 4:
   o> 403\n
   o> readline() -> 403:
-  o> capabilities: lookup branchmap pushkey known getbundle unbundlehash 
batch changegroupsubset streamreqs=generaldelta,revlogv1 
$USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN\n
+  o> capabilities: lookup branchmap pushkey known getbundle unbundlehash 
changegroupsubset streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ 
unbundle=HG10GZ,HG10BZ,HG10UN batch\n
 
 `hg debugserve --sshstdio` works
 
   $ cd server
   $ hg debugserve --sshstdio << EOF
   > hello
   > EOF
   403
-  capabilities: lookup branchmap pushkey known getbundle unbundlehash batch 
changegroupsubset streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ 
unbundle=HG10GZ,HG10BZ,HG10UN
+  capabilities: lookup branchmap pushkey known getbundle unbundlehash 
changegroupsubset streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ 
unbundle=HG10GZ,HG10BZ,HG10UN batch
 
 I/O logging works
 
@@ -103,22 +103,22 @@
   o> write(4) -> 4:
   o> 403\n
   o> write(403) -> 403:
-  o> capabilities: lookup branchmap pushkey known getbundle unbundlehash 
batch changegroupsubset streamreqs=generaldelta,revlogv1 
$USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN\n
+  o> capabilities: lookup branchmap pushkey known getbundle unbundlehash 
changegroupsubset streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ 
unbundle=HG10GZ,HG10BZ,HG10UN batch\n
   403
-  capabilities: lookup branchmap pushkey known getbundle unbundlehash batch 
changegroupsubset streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ 
unbundle=HG10GZ,HG10BZ,HG10UN
+  capabilities: lookup branchmap pushkey known getbundle unbundlehash 
changegroupsubset streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ 
unbundle=HG10GZ,HG10BZ,HG10UN batch
   o> flush() -> None
 
   $ hg debugserve --sshstdio --logiofile $TESTTMP/io << EOF
   > hello
   > EOF
   403
-  capabilities: lookup branchmap pushkey known getbundle unbundlehash batch 
changegroupsubset streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ 
unbundle=HG10GZ,HG10BZ,HG10UN
+  capabilities: lookup branchmap pushkey known getbundle unbundlehash 
changegroupsubset streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ 
unbundle=HG10GZ,HG10BZ,HG10UN batch
 
   $ cat $TESTTMP/io
   o> write(4) -> 4:
   o> 403\n
   o> write(403) -> 403:
-  o> capabilities: lookup branchmap pushkey known getbundle unbundlehash 
batch changegroupsubset streamreqs=generaldelta,revlogv1 
$USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN\n
+  o> capabilities: lookup branchmap pushkey known getbundle unbundlehash 
changegroupsubset streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ 
unbundle=HG10GZ,HG10BZ,HG10UN batch\n
   o> flush() -> None
 
   $ cd ..
@@ -145,7 +145,7 @@
   o> readline() -> 4:
   o> 403\n
   o> readline() -> 403:
-  o> capabilities: lookup branchmap pushkey known getbundle unbundlehash 
batch changegroupsubset streamreqs=generaldelta,revlogv1 
$USUAL_BUNDLE2_CAPS_SERVER$ 

D2883: revlogstore: create and implement an interface for repo files storage

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 7149.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2883?vs=7080=7149

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

AFFECTED FILES
  mercurial/localrepo.py
  mercurial/repository.py
  mercurial/revlogstore.py

CHANGE DETAILS

diff --git a/mercurial/revlogstore.py b/mercurial/revlogstore.py
new file mode 100644
--- /dev/null
+++ b/mercurial/revlogstore.py
@@ -0,0 +1,37 @@
+# revlogstore.py - storage interface for repositories using revlog storage
+#
+# Copyright 2018 Gregory Szorc 
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+from __future__ import absolute_import
+
+from . import (
+error,
+filelog,
+repository,
+)
+
+class revlogfilesstore(repository.basefilesstore):
+"""Files storage layer using revlogs for files storage."""
+
+def __init__(self, svfs):
+self._svfs = svfs
+
+def resolvefilesdata(self, entries):
+for path, node in entries:
+fl = filelog.filelog(self._svfs, path)
+
+try:
+rev = fl.rev(node)
+except error.LookupError:
+yield 'missing', path, node, None
+continue
+
+if fl.iscensored(rev):
+yield 'censored', path, node, None
+continue
+
+data = fl.read(node)
+yield 'ok', path, node, data
diff --git a/mercurial/repository.py b/mercurial/repository.py
--- a/mercurial/repository.py
+++ b/mercurial/repository.py
@@ -266,3 +266,33 @@
 
 class legacypeer(peer, _baselegacywirecommands):
 """peer but with support for legacy wire protocol commands."""
+
+class basefilesstore(object):
+"""Storage interface for repository files data.
+
+This interface defines mechanisms to access repository files data in a
+storage agnostic manner. The goal of this interface is to abstract storage
+implementations so implementation details of storage don't leak into
+higher-level repository consumers.
+"""
+
+__metaclass__ = abc.ABCMeta
+
+def resolvefilesdata(self, entries):
+"""Resolve the fulltext data for an iterable of files.
+
+Each entry is defined by a 2-tuple of (path, node).
+
+The method is a generator that emits results as they become available.
+Each emitted item is a 4-tuple of (result, path, node, data), where
+the first element can be one of the following to represent the 
operation
+result for this request:
+
+ok
+   Successfully resolved fulltext data. Data field is a bytes-like
+   object.
+missing
+   Data for this item not found. Data field is ``None``.
+censored
+   Data for this revision is censored. Data field is ``None``.
+"""
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -52,6 +52,7 @@
 pycompat,
 repository,
 repoview,
+revlogstore,
 revset,
 revsetlang,
 scmutil,
@@ -479,6 +480,8 @@
 else: # standard vfs
 self.svfs.audit = self._getsvfsward(self.svfs.audit)
 self._applyopenerreqs()
+self.filesstore = revlogstore.revlogfilesstore(self.svfs)
+
 if create:
 self._writerequirements()
 



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


D2872: wireproto: define human output side channel frame

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 7148.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2872?vs=7057=7148

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

AFFECTED FILES
  mercurial/help/internals/wireprotocol.txt
  mercurial/wireprotoframing.py
  tests/test-wireproto-serverreactor.py

CHANGE DETAILS

diff --git a/tests/test-wireproto-serverreactor.py 
b/tests/test-wireproto-serverreactor.py
--- a/tests/test-wireproto-serverreactor.py
+++ b/tests/test-wireproto-serverreactor.py
@@ -67,6 +67,109 @@
 ffs(b'1 command-data eos %s' % data.getvalue()),
 ])
 
+def testtextoutputexcessiveargs(self):
+"""At most 255 formatting arguments are allowed."""
+with self.assertRaisesRegexp(ValueError,
+ 'cannot use more than 255 formatting'):
+args = [b'x' for i in range(256)]
+list(framing.createtextoutputframe(1, [(b'bleh', args, [])]))
+
+def testtextoutputexcessivelabels(self):
+"""At most 255 labels are allowed."""
+with self.assertRaisesRegexp(ValueError,
+ 'cannot use more than 255 labels'):
+labels = [b'l' for i in range(256)]
+list(framing.createtextoutputframe(1, [(b'bleh', [], labels)]))
+
+def testtextoutputformattingstringtype(self):
+"""Formatting string must be bytes."""
+with self.assertRaisesRegexp(ValueError, 'must use bytes formatting '):
+list(framing.createtextoutputframe(1, [
+(b'foo'.decode('ascii'), [], [])]))
+
+def testtextoutputargumentbytes(self):
+with self.assertRaisesRegexp(ValueError, 'must use bytes for 
argument'):
+list(framing.createtextoutputframe(1, [
+(b'foo', [b'foo'.decode('ascii')], [])]))
+
+def testtextoutputlabelbytes(self):
+with self.assertRaisesRegexp(ValueError, 'must use bytes for labels'):
+list(framing.createtextoutputframe(1, [
+(b'foo', [], [b'foo'.decode('ascii')])]))
+
+def testtextoutputtoolongformatstring(self):
+with self.assertRaisesRegexp(ValueError,
+ 'formatting string cannot be longer 
than'):
+list(framing.createtextoutputframe(1, [
+(b'x' * 65536, [], [])]))
+
+def testtextoutputtoolongargumentstring(self):
+with self.assertRaisesRegexp(ValueError,
+ 'argument string cannot be longer than'):
+list(framing.createtextoutputframe(1, [
+(b'bleh', [b'x' * 65536], [])]))
+
+def testtextoutputtoolonglabelstring(self):
+with self.assertRaisesRegexp(ValueError,
+ 'label string cannot be longer than'):
+list(framing.createtextoutputframe(1, [
+(b'bleh', [], [b'x' * 65536])]))
+
+def testtextoutput1simpleatom(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo', [], [])]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x03\x00\x00\x00foo'),
+])
+
+def testtextoutput2simpleatoms(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo', [], []),
+(b'bar', [], []),
+]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x03\x00\x00\x00foo\x03\x00\x00\x00bar'),
+])
+
+def testtextoutput1arg(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo %s', [b'val1'], []),
+]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x06\x00\x00\x01\x04\x00foo %sval1'),
+])
+
+def testtextoutput2arg(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo %s %s', [b'val', b'value'], []),
+]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x09\x00\x00\x02\x03\x00\x05\x00'
+br'foo %s %svalvalue'),
+])
+
+def testtextoutput1label(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo', [], [b'label']),
+]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x03\x00\x01\x00\x05foolabel'),
+])
+
+def testargandlabel(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo %s', [b'arg'], [b'label']),
+]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x06\x00\x01\x01\x05\x03\x00foo 
%slabelarg'),
+])
+
 class ServerReactorTests(unittest.TestCase):
 def _sendsingleframe(self, reactor, s):
 results = list(sendframes(reactor, [ffs(s)]))
diff --git a/mercurial/wireprotoframing.py b/mercurial/wireprotoframing.py
--- a/mercurial/wireprotoframing.py
+++ b/mercurial/wireprotoframing.py
@@ -27,13 +27,15 @@
 FRAME_TYPE_COMMAND_DATA = 0x03
 FRAME_TYPE_BYTES_RESPONSE = 0x04
 

D2871: wireproto: service multiple command requests per HTTP request

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 7147.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2871?vs=7055=7147

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

AFFECTED FILES
  mercurial/help/internals/wireprotocol.txt
  mercurial/wireprotoserver.py
  tests/test-http-api-httpv2.t

CHANGE DETAILS

diff --git a/tests/test-http-api-httpv2.t b/tests/test-http-api-httpv2.t
--- a/tests/test-http-api-httpv2.t
+++ b/tests/test-http-api-httpv2.t
@@ -412,4 +412,153 @@
   s> received: \n
   s> {"action": "noop"}
 
+Multiple requests to regular command URL are not allowed
+
+  $ send << EOF
+  > httprequest POST api/$HTTPV2/ro/customreadonly
+  > accept: $MEDIATYPE
+  > content-type: $MEDIATYPE
+  > user-agent: test
+  > frame 1 command-name eos customreadonly
+  > frame 3 command-name eos customreadonly
+  > EOF
+  using raw connection to peer
+  s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> accept: application/mercurial-exp-framing-0002\r\n
+  s> content-type: application/mercurial-exp-framing-0002\r\n
+  s> user-agent: test\r\n
+  s> content-length: 40\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> \r\n
+  s> 
\x0e\x00\x00\x01\x00\x11customreadonly\x0e\x00\x00\x03\x00\x11customreadonly
+  s> makefile('rb', None)
+  s> HTTP/1.1 200 OK\r\n
+  s> Server: testing stub value\r\n
+  s> Date: $HTTP_DATE$\r\n
+  s> Content-Type: text/plain\r\n
+  s> Content-Length: 46\r\n
+  s> \r\n
+  s> multiple commands cannot be issued to this URL
+
+Multiple requests to "multirequest" URL are allowed
+
+  $ send << EOF
+  > httprequest POST api/$HTTPV2/ro/multirequest
+  > accept: $MEDIATYPE
+  > content-type: $MEDIATYPE
+  > user-agent: test
+  > frame 1 command-name eos customreadonly
+  > frame 3 command-name eos customreadonly
+  > EOF
+  using raw connection to peer
+  s> POST /api/exp-http-v2-0001/ro/multirequest HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> accept: application/mercurial-exp-framing-0002\r\n
+  s> content-type: application/mercurial-exp-framing-0002\r\n
+  s> user-agent: test\r\n
+  s> *\r\n (glob)
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> \r\n
+  s> 
\x0e\x00\x00\x01\x00\x11customreadonly\x0e\x00\x00\x03\x00\x11customreadonly
+  s> makefile('rb', None)
+  s> HTTP/1.1 200 OK\r\n
+  s> Server: testing stub value\r\n
+  s> Date: $HTTP_DATE$\r\n
+  s> Content-Type: application/mercurial-exp-framing-0002\r\n
+  s> Transfer-Encoding: chunked\r\n
+  s> \r\n
+  s> *\r\n (glob)
+  s> \x1d\x00\x00\x01\x00Bcustomreadonly bytes response
+  s> \r\n
+  s> 23\r\n
+  s> \x1d\x00\x00\x03\x00Bcustomreadonly bytes response
+  s> \r\n
+  s> 0\r\n
+  s> \r\n
+
+Interleaved requests to "multirequest" are processed
+
+  $ send << EOF
+  > httprequest POST api/$HTTPV2/ro/multirequest
+  > accept: $MEDIATYPE
+  > content-type: $MEDIATYPE
+  > user-agent: test
+  > frame 1 command-name have-args listkeys
+  > frame 3 command-name have-args listkeys
+  > frame 3 command-argument eoa \x09\x00\x09\x00namespacebookmarks
+  > frame 1 command-argument eoa \x09\x00\x0a\x00namespacenamespaces
+  > EOF
+  using raw connection to peer
+  s> POST /api/exp-http-v2-0001/ro/multirequest HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> accept: application/mercurial-exp-framing-0002\r\n
+  s> content-type: application/mercurial-exp-framing-0002\r\n
+  s> user-agent: test\r\n
+  s> content-length: 85\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> \r\n
+  s> 
\x08\x00\x00\x01\x00\x12listkeys\x08\x00\x00\x03\x00\x12listkeys\x16\x00\x00\x03\x00"
 \x00\x00namespacebookmarks\x17\x00\x00\x01\x00" \x00\n
+  s> \x00namespacenamespaces
+  s> makefile('rb', None)
+  s> HTTP/1.1 200 OK\r\n
+  s> Server: testing stub value\r\n
+  s> Date: $HTTP_DATE$\r\n
+  s> Content-Type: application/mercurial-exp-framing-0002\r\n
+  s> Transfer-Encoding: chunked\r\n
+  s> \r\n
+  s> 6\r\n
+  s> \x00\x00\x00\x03\x00B
+  s> \r\n
+  s> 24\r\n
+  s> \x1e\x00\x00\x01\x00Bbookmarks\n
+  s> namespaces\n
+  s> phases
+  s> \r\n
+  s> 0\r\n
+  s> \r\n
+
+Restart server to disable read-write access
+
+  $ killdaemons.py
+  $ cat > server/.hg/hgrc << EOF
+  > [experimental]
+  > web.apiserver = true
+  > web.api.debugreflect = true
+  > web.api.http-v2 = true
+  > [web]
+  > push_ssl = false
+  > EOF
+
+  $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
+  $ cat hg.pid > $DAEMON_PIDS
+
+Attempting to run a read-write command via multirequest on read-only URL is 
not allowed
+
+  $ send << EOF
+  > httprequest POST api/$HTTPV2/ro/multirequest
+  > accept: $MEDIATYPE
+  > 

D2902: wireproto: define frame to represent progress updates

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Today, a long-running operation on a server may run without any sign
  of progress on the client. This can lead to the conclusion that the
  server has hung or the connection has dropped. In fact, connections
  can and do time out due to inactivity. And a long-running server
  operation can result in the connection dropping prematurely because
  no data is being sent!
  
  While we're inventing the new wire protocol, let's provide a mechanism
  for communicating progress on potentially expensive server-side events.
  
  We introduce a new frame type that conveys "progress" updates. This
  frame type essentially holds the data required to formulate a
  ``ui.progress()`` call.
  
  We only define the frame right now. Implementing it will be a bit of
  work since there is no analog to progress frames in the existing
  wire protocol. We'll need to teach the ui object to write to the
  wire protocol, etc.

REPOSITORY
  rHG Mercurial

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

AFFECTED FILES
  mercurial/help/internals/wireprotocol.txt
  mercurial/wireprotoframing.py

CHANGE DETAILS

diff --git a/mercurial/wireprotoframing.py b/mercurial/wireprotoframing.py
--- a/mercurial/wireprotoframing.py
+++ b/mercurial/wireprotoframing.py
@@ -31,14 +31,16 @@
 FRAME_TYPE_BYTES_RESPONSE = 0x04
 FRAME_TYPE_ERROR_RESPONSE = 0x05
 FRAME_TYPE_TEXT_OUTPUT = 0x06
+FRAME_TYPE_PROGRESS = 0x07
 
 FRAME_TYPES = {
 b'command-name': FRAME_TYPE_COMMAND_NAME,
 b'command-argument': FRAME_TYPE_COMMAND_ARGUMENT,
 b'command-data': FRAME_TYPE_COMMAND_DATA,
 b'bytes-response': FRAME_TYPE_BYTES_RESPONSE,
 b'error-response': FRAME_TYPE_ERROR_RESPONSE,
 b'text-output': FRAME_TYPE_TEXT_OUTPUT,
+b'progress': FRAME_TYPE_PROGRESS,
 }
 
 FLAG_COMMAND_NAME_EOS = 0x01
@@ -91,6 +93,7 @@
 FRAME_TYPE_BYTES_RESPONSE: FLAGS_BYTES_RESPONSE,
 FRAME_TYPE_ERROR_RESPONSE: FLAGS_ERROR_RESPONSE,
 FRAME_TYPE_TEXT_OUTPUT: {},
+FRAME_TYPE_PROGRESS: {},
 }
 
 ARGUMENT_FRAME_HEADER = struct.Struct(r'

D2858: wireproto: define and implement responses in framing protocol

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 7143.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2858?vs=7051=7143

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

AFFECTED FILES
  mercurial/help/internals/wireprotocol.txt
  mercurial/wireprotoframing.py
  mercurial/wireprotoserver.py
  tests/test-http-api-httpv2.t
  tests/test-wireproto-serverreactor.py

CHANGE DETAILS

diff --git a/tests/test-wireproto-serverreactor.py 
b/tests/test-wireproto-serverreactor.py
--- a/tests/test-wireproto-serverreactor.py
+++ b/tests/test-wireproto-serverreactor.py
@@ -79,6 +79,10 @@
 self.assertIsInstance(res[1], dict)
 self.assertEqual(res[0], expected)
 
+def assertframesequal(self, frames, framestrings):
+expected = [ffs(s) for s in framestrings]
+self.assertEqual(list(frames), expected)
+
 def test1framecommand(self):
 """Receiving a command in a single frame yields request to run it."""
 reactor = makereactor()
@@ -270,6 +274,42 @@
 'message': b'command data frame without flags',
 })
 
+def testsimpleresponse(self):
+"""Bytes response to command sends result frames."""
+reactor = makereactor()
+list(sendcommandframes(reactor, b'mycommand', {}))
+
+result = reactor.onbytesresponseready(b'response')
+self.assertaction(result, 'sendframes')
+self.assertframesequal(result[1]['framegen'], [
+b'bytes-response eos response',
+])
+
+def testmultiframeresponse(self):
+"""Bytes response spanning multiple frames is handled."""
+first = b'x' * framing.DEFAULT_MAX_FRAME_SIZE
+second = b'y' * 100
+
+reactor = makereactor()
+list(sendcommandframes(reactor, b'mycommand', {}))
+
+result = reactor.onbytesresponseready(first + second)
+self.assertaction(result, 'sendframes')
+self.assertframesequal(result[1]['framegen'], [
+b'bytes-response continuation %s' % first,
+b'bytes-response eos %s' % second,
+])
+
+def testapplicationerror(self):
+reactor = makereactor()
+list(sendcommandframes(reactor, b'mycommand', {}))
+
+result = reactor.onapplicationerror(b'some message')
+self.assertaction(result, 'sendframes')
+self.assertframesequal(result[1]['framegen'], [
+b'error-response application some message',
+])
+
 if __name__ == '__main__':
 import silenttestrunner
 silenttestrunner.main(__name__)
diff --git a/tests/test-http-api-httpv2.t b/tests/test-http-api-httpv2.t
--- a/tests/test-http-api-httpv2.t
+++ b/tests/test-http-api-httpv2.t
@@ -195,10 +195,14 @@
   s> HTTP/1.1 200 OK\r\n
   s> Server: testing stub value\r\n
   s> Date: $HTTP_DATE$\r\n
-  s> Content-Type: text/plain\r\n
-  s> Content-Length: 29\r\n
+  s> Content-Type: application/mercurial-exp-framing-0001\r\n
+  s> Transfer-Encoding: chunked\r\n
   s> \r\n
-  s> customreadonly bytes response
+  s> 21\r\n
+  s> \x1d\x00\x00Bcustomreadonly bytes response
+  s> \r\n
+  s> 0\r\n
+  s> \r\n
 
 Request to read-write command fails because server is read-only by default
 
@@ -302,10 +306,14 @@
   s> HTTP/1.1 200 OK\r\n
   s> Server: testing stub value\r\n
   s> Date: $HTTP_DATE$\r\n
-  s> Content-Type: text/plain\r\n
-  s> Content-Length: 29\r\n
+  s> Content-Type: application/mercurial-exp-framing-0001\r\n
+  s> Transfer-Encoding: chunked\r\n
   s> \r\n
-  s> customreadonly bytes response
+  s> 21\r\n
+  s> \x1d\x00\x00Bcustomreadonly bytes response
+  s> \r\n
+  s> 0\r\n
+  s> \r\n
 
 Authorized request for unknown command is rejected
 
diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py
+++ b/mercurial/wireprotoserver.py
@@ -493,15 +493,20 @@
 
 rsp = wireproto.dispatch(repo, proto, command['command'])
 
-# TODO use proper response format.
 res.status = b'200 OK'
-res.headers[b'Content-Type'] = b'text/plain'
+res.headers[b'Content-Type'] = FRAMINGTYPE
 
 if isinstance(rsp, wireprototypes.bytesresponse):
-res.setbodybytes(rsp.data)
+action, meta = reactor.onbytesresponseready(rsp.data)
 else:
-res.setbodybytes(b'unhandled response type from wire proto '
- 'command')
+action, meta = reactor.onapplicationerror(
+_('unhandled response type from wire proto command'))
+
+if action == 'sendframes':
+res.setbodygen(meta['framegen'])
+else:
+raise error.ProgrammingError('unhandled event from reactor: %s' %
+ action)
 
 # Maps API name to metadata so custom API can be registered.
 API_HANDLERS = {
diff --git a/mercurial/wireprotoframing.py b/mercurial/wireprotoframing.py
--- a/mercurial/wireprotoframing.py
+++ 

D2870: wireproto: support for receiving multiple requests

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 7146.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2870?vs=7054=7146

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

AFFECTED FILES
  mercurial/wireprotoframing.py
  tests/test-http-api-httpv2.t
  tests/test-wireproto-serverreactor.py

CHANGE DETAILS

diff --git a/tests/test-wireproto-serverreactor.py 
b/tests/test-wireproto-serverreactor.py
--- a/tests/test-wireproto-serverreactor.py
+++ b/tests/test-wireproto-serverreactor.py
@@ -196,6 +196,19 @@
 'message': b'expected command frame; got 2',
 })
 
+def testunexpectedcommandargumentreceiving(self):
+"""Same as above but the command is receiving."""
+results = list(sendframes(makereactor(), [
+ffs(b'1 command-name have-data command'),
+ffs(b'1 command-argument eoa ignored'),
+]))
+
+self.assertaction(results[1], 'error')
+self.assertEqual(results[1][1], {
+'message': b'received command argument frame for request that is '
+   b'not expecting arguments: 1',
+})
+
 def testunexpectedcommanddata(self):
 """Command argument frame when not running a command is an error."""
 result = self._sendsingleframe(makereactor(),
@@ -205,6 +218,19 @@
 'message': b'expected command frame; got 3',
 })
 
+def testunexpectedcommanddatareceiving(self):
+"""Same as above except the command is receiving."""
+results = list(sendframes(makereactor(), [
+ffs(b'1 command-name have-args command'),
+ffs(b'1 command-data eos ignored'),
+]))
+
+self.assertaction(results[1], 'error')
+self.assertEqual(results[1][1], {
+'message': b'received command data frame for request that is not '
+   b'expecting data: 1',
+})
+
 def testmissingcommandframeflags(self):
 """Command name frame must have flags set."""
 result = self._sendsingleframe(makereactor(),
@@ -214,19 +240,77 @@
 'message': b'missing frame flags on command frame',
 })
 
+def testconflictingrequestid(self):
+"""Multiple fully serviced commands with same request ID is allowed."""
+results = list(sendframes(makereactor(), [
+ffs(b'1 command-name eos command'),
+ffs(b'1 command-name eos command'),
+ffs(b'1 command-name eos command'),
+]))
+for i in range(3):
+self.assertaction(results[i], 'runcommand')
+self.assertEqual(results[i][1], {
+'requestid': 1,
+'command': b'command',
+'args': {},
+'data': None,
+})
+
+def testconflictingrequestid(self):
+"""Request ID for new command matching in-flight command is illegal."""
+results = list(sendframes(makereactor(), [
+ffs(b'1 command-name have-args command'),
+ffs(b'1 command-name eos command'),
+]))
+
+self.assertaction(results[0], 'wantframe')
+self.assertaction(results[1], 'error')
+self.assertEqual(results[1][1], {
+'message': b'request with ID 1 already received',
+})
+
+def testinterleavedcommands(self):
+results = list(sendframes(makereactor(), [
+ffs(b'1 command-name have-args command1'),
+ffs(b'3 command-name have-args command3'),
+ffs(br'1 command-argument 0 \x03\x00\x03\x00foobar'),
+ffs(br'3 command-argument 0 \x03\x00\x03\x00bizbaz'),
+ffs(br'3 command-argument eoa \x03\x00\x03\x00keyval'),
+ffs(br'1 command-argument eoa \x04\x00\x03\x00key1val'),
+]))
+
+self.assertEqual([t[0] for t in results], [
+'wantframe',
+'wantframe',
+'wantframe',
+'wantframe',
+'runcommand',
+'runcommand',
+])
+
+self.assertEqual(results[4][1], {
+'requestid': 3,
+'command': 'command3',
+'args': {b'biz': b'baz', b'key': b'val'},
+'data': None,
+})
+self.assertEqual(results[5][1], {
+'requestid': 1,
+'command': 'command1',
+'args': {b'foo': b'bar', b'key1': b'val'},
+'data': None,
+})
+
 def testmissingargumentframe(self):
+# This test attempts to test behavior when reactor has an incomplete
+# command request waiting on argument data. But it doesn't handle that
+# scenario yet. So this test does nothing of value.
 frames = [
 ffs(b'1 command-name have-args command'),
-ffs(b'1 command-name 0 ignored'),
 ]
 
 results = list(sendframes(makereactor(), frames))
-self.assertEqual(len(results), 2)
 self.assertaction(results[0], 'wantframe')
-

D2857: wireproto: implement basic command dispatching for HTTPv2

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 7142.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2857?vs=7050=7142

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

AFFECTED FILES
  mercurial/wireprotoserver.py
  tests/test-http-api-httpv2.t

CHANGE DETAILS

diff --git a/tests/test-http-api-httpv2.t b/tests/test-http-api-httpv2.t
--- a/tests/test-http-api-httpv2.t
+++ b/tests/test-http-api-httpv2.t
@@ -196,9 +196,9 @@
   s> Server: testing stub value\r\n
   s> Date: $HTTP_DATE$\r\n
   s> Content-Type: text/plain\r\n
-  s> Content-Length: 18\r\n
+  s> Content-Length: 29\r\n
   s> \r\n
-  s> ro/customreadonly\n
+  s> customreadonly bytes response
 
 Request to read-write command fails because server is read-only by default
 
@@ -303,9 +303,9 @@
   s> Server: testing stub value\r\n
   s> Date: $HTTP_DATE$\r\n
   s> Content-Type: text/plain\r\n
-  s> Content-Length: 18\r\n
+  s> Content-Length: 29\r\n
   s> \r\n
-  s> rw/customreadonly\n
+  s> customreadonly bytes response
 
 Authorized request for unknown command is rejected
 
diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py
+++ b/mercurial/wireprotoserver.py
@@ -360,10 +360,7 @@
'value: %s\n') % FRAMINGTYPE)
 return
 
-# We don't do anything meaningful yet.
-res.status = b'200 OK'
-res.headers[b'Content-Type'] = b'text/plain'
-res.setbodybytes(b'/'.join(urlparts) + b'\n')
+_processhttpv2request(ui, repo, req, res, permission, command, proto)
 
 def _processhttpv2reflectrequest(ui, repo, req, res):
 """Reads unified frame protocol request and dumps out state to client.
@@ -408,6 +405,104 @@
 res.headers[b'Content-Type'] = b'text/plain'
 res.setbodybytes(b'\n'.join(states))
 
+def _processhttpv2request(ui, repo, req, res, authedperm, reqcommand, proto):
+"""Post-validation handler for HTTPv2 requests.
+
+Called when the HTTP request contains unified frame-based protocol
+frames for evaluation.
+"""
+reactor = wireprotoframing.serverreactor()
+seencommand = False
+
+while True:
+frame = wireprotoframing.readframe(req.bodyfh)
+if not frame:
+break
+
+action, meta = reactor.onframerecv(*frame)
+
+if action == 'wantframe':
+# Need more data before we can do anything.
+continue
+elif action == 'runcommand':
+# We currently only support running a single command per
+# HTTP request.
+if seencommand:
+# TODO define proper error mechanism.
+res.status = b'200 OK'
+res.headers[b'Content-Type'] = b'text/plain'
+res.setbodybytes(_('support for multiple commands per request '
+   'not yet implemented'))
+return
+
+_httpv2runcommand(ui, repo, req, res, authedperm, reqcommand,
+  reactor, meta)
+
+elif action == 'error':
+# TODO define proper error mechanism.
+res.status = b'200 OK'
+res.headers[b'Content-Type'] = b'text/plain'
+res.setbodybytes(meta['message'] + b'\n')
+return
+else:
+raise error.ProgrammingError(
+'unhandled action from frame processor: %s' % action)
+
+def _httpv2runcommand(ui, repo, req, res, authedperm, reqcommand, reactor,
+  command):
+"""Dispatch a wire protocol command made from HTTPv2 requests.
+
+The authenticated permission (``authedperm``) along with the original
+command from the URL (``reqcommand``) are passed in.
+"""
+# We already validated that the session has permissions to perform the
+# actions in ``authedperm``. In the unified frame protocol, the canonical
+# command to run is expressed in a frame. However, the URL also requested
+# to run a specific command. We need to be careful that the command we
+# run doesn't have permissions requirements greater than what was granted
+# by ``authedperm``.
+#
+# For now, this is no big deal, as we only allow a single command per
+# request and that command must match the command in the URL. But when
+# things change, we need to watch out...
+if reqcommand != command['command']:
+# TODO define proper error mechanism
+res.status = b'200 OK'
+res.headers[b'Content-Type'] = b'text/plain'
+res.setbodybytes(_('command in frame must match command in URL'))
+return
+
+# TODO once we get rid of the command==URL restriction, we'll need to
+# revalidate command validity and auth here. checkperm,
+# wireproto.commands.commandavailable(), etc.
+
+proto = httpv2protocolhandler(req, ui, args=command['args'])
+assert 

D2850: wireproto: define content negotiation for HTTPv2

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 7138.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2850?vs=7016=7138

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

AFFECTED FILES
  mercurial/help/internals/wireprotocol.txt
  mercurial/wireprotoserver.py
  tests/test-http-api-httpv2.t

CHANGE DETAILS

diff --git a/tests/test-http-api-httpv2.t b/tests/test-http-api-httpv2.t
--- a/tests/test-http-api-httpv2.t
+++ b/tests/test-http-api-httpv2.t
@@ -1,4 +1,5 @@
   $ HTTPV2=exp-http-v2-0001
+  $ MEDIATYPE=application/mercurial-tbd
 
   $ send() {
   >   hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT/
@@ -60,27 +61,6 @@
   $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
   $ cat hg.pid > $DAEMON_PIDS
 
-Request to read-only command works out of the box
-
-  $ send << EOF
-  > httprequest POST api/$HTTPV2/ro/customreadonly
-  > user-agent: test
-  > EOF
-  using raw connection to peer
-  s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
-  s> Accept-Encoding: identity\r\n
-  s> user-agent: test\r\n
-  s> host: $LOCALIP:$HGPORT\r\n (glob)
-  s> \r\n
-  s> makefile('rb', None)
-  s> HTTP/1.1 200 OK\r\n
-  s> Server: testing stub value\r\n
-  s> Date: $HTTP_DATE$\r\n
-  s> Content-Type: text/plain\r\n
-  s> Content-Length: 18\r\n
-  s> \r\n
-  s> ro/customreadonly\n
-
 Request to unknown command yields 404
 
   $ send << EOF
@@ -123,6 +103,100 @@
   s> \r\n
   s> commands require POST requests
 
+Missing Accept header results in 406
+
+  $ send << EOF
+  > httprequest POST api/$HTTPV2/ro/customreadonly
+  > user-agent: test
+  > EOF
+  using raw connection to peer
+  s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> user-agent: test\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> HTTP/1.1 406 Not Acceptable\r\n
+  s> Server: testing stub value\r\n
+  s> Date: $HTTP_DATE$\r\n
+  s> Content-Type: text/plain\r\n
+  s> Content-Length: 72\r\n
+  s> \r\n
+  s> client MUST specify Accept header with value: 
application/mercurial-tbd\n
+
+Bad Accept header results in 406
+
+  $ send << EOF
+  > httprequest POST api/$HTTPV2/ro/customreadonly
+  > accept: invalid
+  > user-agent: test
+  > EOF
+  using raw connection to peer
+  s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> accept: invalid\r\n
+  s> user-agent: test\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> HTTP/1.1 406 Not Acceptable\r\n
+  s> Server: testing stub value\r\n
+  s> Date: $HTTP_DATE$\r\n
+  s> Content-Type: text/plain\r\n
+  s> Content-Length: 72\r\n
+  s> \r\n
+  s> client MUST specify Accept header with value: 
application/mercurial-tbd\n
+
+Bad Content-Type header results in 415
+
+  $ send << EOF
+  > httprequest POST api/$HTTPV2/ro/customreadonly
+  > accept: $MEDIATYPE
+  > user-agent: test
+  > content-type: badmedia
+  > EOF
+  using raw connection to peer
+  s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> accept: application/mercurial-tbd\r\n
+  s> content-type: badmedia\r\n
+  s> user-agent: test\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> HTTP/1.1 415 Unsupported Media Type\r\n
+  s> Server: testing stub value\r\n
+  s> Date: $HTTP_DATE$\r\n
+  s> Content-Type: text/plain\r\n
+  s> Content-Length: 75\r\n
+  s> \r\n
+  s> client MUST send Content-Type header with value: 
application/mercurial-tbd\n
+
+Request to read-only command works out of the box
+
+  $ send << EOF
+  > httprequest POST api/$HTTPV2/ro/customreadonly
+  > accept: $MEDIATYPE
+  > content-type: $MEDIATYPE
+  > user-agent: test
+  > EOF
+  using raw connection to peer
+  s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> accept: application/mercurial-tbd\r\n
+  s> content-type: application/mercurial-tbd\r\n
+  s> user-agent: test\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> HTTP/1.1 200 OK\r\n
+  s> Server: testing stub value\r\n
+  s> Date: $HTTP_DATE$\r\n
+  s> Content-Type: text/plain\r\n
+  s> Content-Length: 18\r\n
+  s> \r\n
+  s> ro/customreadonly\n
+
 Request to read-write command fails because server is read-only by default
 
 GET to read-write request yields 405
@@ -207,10 +281,14 @@
   $ send << EOF
   > httprequest POST api/$HTTPV2/rw/customreadonly
   > user-agent: test
+  > accept: $MEDIATYPE
+  > content-type: $MEDIATYPE
   > EOF
   using raw connection to peer
   s> POST 

D2851: wireproto: define and implement protocol for issuing requests

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 7139.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2851?vs=7047=7139

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

AFFECTED FILES
  mercurial/debugcommands.py
  mercurial/help/internals/wireprotocol.txt
  mercurial/wireprotoframing.py
  mercurial/wireprotoserver.py
  tests/test-http-api-httpv2.t

CHANGE DETAILS

diff --git a/tests/test-http-api-httpv2.t b/tests/test-http-api-httpv2.t
--- a/tests/test-http-api-httpv2.t
+++ b/tests/test-http-api-httpv2.t
@@ -1,5 +1,5 @@
   $ HTTPV2=exp-http-v2-0001
-  $ MEDIATYPE=application/mercurial-tbd
+  $ MEDIATYPE=application/mercurial-exp-framing-0001
 
   $ send() {
   >   hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT/
@@ -120,9 +120,9 @@
   s> Server: testing stub value\r\n
   s> Date: $HTTP_DATE$\r\n
   s> Content-Type: text/plain\r\n
-  s> Content-Length: 72\r\n
+  s> Content-Length: 85\r\n
   s> \r\n
-  s> client MUST specify Accept header with value: 
application/mercurial-tbd\n
+  s> client MUST specify Accept header with value: 
application/mercurial-exp-framing-0001\n
 
 Bad Accept header results in 406
 
@@ -143,9 +143,9 @@
   s> Server: testing stub value\r\n
   s> Date: $HTTP_DATE$\r\n
   s> Content-Type: text/plain\r\n
-  s> Content-Length: 72\r\n
+  s> Content-Length: 85\r\n
   s> \r\n
-  s> client MUST specify Accept header with value: 
application/mercurial-tbd\n
+  s> client MUST specify Accept header with value: 
application/mercurial-exp-framing-0001\n
 
 Bad Content-Type header results in 415
 
@@ -158,7 +158,7 @@
   using raw connection to peer
   s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
   s> Accept-Encoding: identity\r\n
-  s> accept: application/mercurial-tbd\r\n
+  s> accept: application/mercurial-exp-framing-0001\r\n
   s> content-type: badmedia\r\n
   s> user-agent: test\r\n
   s> host: $LOCALIP:$HGPORT\r\n (glob)
@@ -168,26 +168,29 @@
   s> Server: testing stub value\r\n
   s> Date: $HTTP_DATE$\r\n
   s> Content-Type: text/plain\r\n
-  s> Content-Length: 75\r\n
+  s> Content-Length: 88\r\n
   s> \r\n
-  s> client MUST send Content-Type header with value: 
application/mercurial-tbd\n
+  s> client MUST send Content-Type header with value: 
application/mercurial-exp-framing-0001\n
 
 Request to read-only command works out of the box
 
   $ send << EOF
   > httprequest POST api/$HTTPV2/ro/customreadonly
   > accept: $MEDIATYPE
   > content-type: $MEDIATYPE
   > user-agent: test
+  > frame command-name eos customreadonly
   > EOF
   using raw connection to peer
   s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
   s> Accept-Encoding: identity\r\n
-  s> accept: application/mercurial-tbd\r\n
-  s> content-type: application/mercurial-tbd\r\n
+  s> accept: application/mercurial-exp-framing-0001\r\n
+  s> content-type: application/mercurial-exp-framing-0001\r\n
   s> user-agent: test\r\n
+  s> content-length: 18\r\n
   s> host: $LOCALIP:$HGPORT\r\n (glob)
   s> \r\n
+  s> \x0e\x00\x00\x11customreadonly
   s> makefile('rb', None)
   s> HTTP/1.1 200 OK\r\n
   s> Server: testing stub value\r\n
@@ -283,15 +286,18 @@
   > user-agent: test
   > accept: $MEDIATYPE
   > content-type: $MEDIATYPE
+  > frame command-name eos customreadonly
   > EOF
   using raw connection to peer
   s> POST /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
   s> Accept-Encoding: identity\r\n
-  s> accept: application/mercurial-tbd\r\n
-  s> content-type: application/mercurial-tbd\r\n
+  s> accept: application/mercurial-exp-framing-0001\r\n
+  s> content-type: application/mercurial-exp-framing-0001\r\n
   s> user-agent: test\r\n
+  s> content-length: 18\r\n
   s> host: $LOCALIP:$HGPORT\r\n (glob)
   s> \r\n
+  s> \x0e\x00\x00\x11customreadonly
   s> makefile('rb', None)
   s> HTTP/1.1 200 OK\r\n
   s> Server: testing stub value\r\n
@@ -311,7 +317,7 @@
   using raw connection to peer
   s> POST /api/exp-http-v2-0001/rw/badcommand HTTP/1.1\r\n
   s> Accept-Encoding: identity\r\n
-  s> accept: application/mercurial-tbd\r\n
+  s> accept: application/mercurial-exp-framing-0001\r\n
   s> user-agent: test\r\n
   s> host: $LOCALIP:$HGPORT\r\n (glob)
   s> \r\n
diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py
+++ b/mercurial/wireprotoserver.py
@@ -32,7 +32,7 @@
 HGTYPE = 'application/mercurial-0.1'
 HGTYPE2 = 'application/mercurial-0.2'
 HGERRTYPE = 'application/hg-error'
-HTTPV2TYPE = 'application/mercurial-tbd'
+FRAMINGTYPE = b'application/mercurial-exp-framing-0001'
 
 HTTPV2 = wireprototypes.HTTPV2
 SSHV1 = wireprototypes.SSHV1
@@ -336,21 +336,21 @@
 res.setbodybytes(_('invalid wire 

D2836: wireproto: define permissions-based routing of HTTPv2 wire protocol

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 7135.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2836?vs=7014=7135

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

AFFECTED FILES
  mercurial/debugcommands.py
  mercurial/help/internals/wireprotocol.txt
  mercurial/wireprotoserver.py
  tests/test-http-api-httpv2.t

CHANGE DETAILS

diff --git a/tests/test-http-api-httpv2.t b/tests/test-http-api-httpv2.t
--- a/tests/test-http-api-httpv2.t
+++ b/tests/test-http-api-httpv2.t
@@ -1,7 +1,24 @@
+  $ HTTPV2=exp-http-v2-0001
+
   $ send() {
   >   hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT/
   > }
 
+  $ cat > dummycommands.py << EOF
+  > from mercurial import wireprototypes, wireproto
+  > @wireproto.wireprotocommand('customreadonly', permission='pull')
+  > def customreadonly(repo, proto):
+  > return wireprototypes.bytesresponse(b'customreadonly bytes response')
+  > @wireproto.wireprotocommand('customreadwrite', permission='push')
+  > def customreadwrite(repo, proto):
+  > return wireprototypes.bytesresponse(b'customreadwrite bytes response')
+  > EOF
+
+  $ cat >> $HGRCPATH << EOF
+  > [extensions]
+  > dummycommands = $TESTTMP/dummycommands.py
+  > EOF
+
   $ hg init server
   $ cat > server/.hg/hgrc << EOF
   > [experimental]
@@ -13,7 +30,7 @@
 HTTP v2 protocol not enabled by default
 
   $ send << EOF
-  > httprequest GET api/exp-http-v2-0001
+  > httprequest GET api/$HTTPV2
   > user-agent: test
   > EOF
   using raw connection to peer
@@ -43,14 +60,14 @@
   $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
   $ cat hg.pid > $DAEMON_PIDS
 
-Requests simply echo their path (for now)
+Request to read-only command works out of the box
 
   $ send << EOF
-  > httprequest GET api/exp-http-v2-0001/path1/path2
+  > httprequest GET api/$HTTPV2/ro/customreadonly
   > user-agent: test
   > EOF
   using raw connection to peer
-  s> GET /api/exp-http-v2-0001/path1/path2 HTTP/1.1\r\n
+  s> GET /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
   s> Accept-Encoding: identity\r\n
   s> user-agent: test\r\n
   s> host: $LOCALIP:$HGPORT\r\n (glob)
@@ -60,6 +77,178 @@
   s> Server: testing stub value\r\n
   s> Date: $HTTP_DATE$\r\n
   s> Content-Type: text/plain\r\n
-  s> Content-Length: 12\r\n
+  s> Content-Length: 18\r\n
+  s> \r\n
+  s> ro/customreadonly\n
+
+Request to unknown command yields 404
+
+  $ send << EOF
+  > httprequest GET api/$HTTPV2/ro/badcommand
+  > user-agent: test
+  > EOF
+  using raw connection to peer
+  s> GET /api/exp-http-v2-0001/ro/badcommand HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> user-agent: test\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> HTTP/1.1 404 Not Found\r\n
+  s> Server: testing stub value\r\n
+  s> Date: $HTTP_DATE$\r\n
+  s> Content-Type: text/plain\r\n
+  s> Content-Length: 42\r\n
+  s> \r\n
+  s> unknown wire protocol command: badcommand\n
+
+Request to read-write command fails because server is read-only by default
+
+GET to read-write request not allowed
+
+  $ send << EOF
+  > httprequest GET api/$HTTPV2/rw/customreadonly
+  > user-agent: test
+  > EOF
+  using raw connection to peer
+  s> GET /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> user-agent: test\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> HTTP/1.1 405 push requires POST request\r\n
+  s> Server: testing stub value\r\n
+  s> Date: $HTTP_DATE$\r\n
+  s> Content-Length: 17\r\n
+  s> \r\n
+  s> permission denied
+
+Even for unknown commands
+
+  $ send << EOF
+  > httprequest GET api/$HTTPV2/rw/badcommand
+  > user-agent: test
+  > EOF
+  using raw connection to peer
+  s> GET /api/exp-http-v2-0001/rw/badcommand HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> user-agent: test\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> HTTP/1.1 405 push requires POST request\r\n
+  s> Server: testing stub value\r\n
+  s> Date: $HTTP_DATE$\r\n
+  s> Content-Length: 17\r\n
+  s> \r\n
+  s> permission denied
+
+SSL required by default
+
+  $ send << EOF
+  > httprequest POST api/$HTTPV2/rw/customreadonly
+  > user-agent: test
+  > EOF
+  using raw connection to peer
+  s> POST /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> user-agent: test\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> HTTP/1.1 403 ssl required\r\n
+  s> Server: testing stub value\r\n
+  s> Date: $HTTP_DATE$\r\n
+  s> Content-Length: 17\r\n
   s> \r\n
-  s> path1/path2\n
+  s> permission denied
+
+Restart server to allow non-ssl read-write operations
+
+  $ 

D2837: wireproto: require POST for all HTTPv2 requests

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 7136.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2837?vs=7015=7136

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

AFFECTED FILES
  mercurial/help/internals/wireprotocol.txt
  mercurial/wireprotoserver.py
  tests/test-http-api-httpv2.t

CHANGE DETAILS

diff --git a/tests/test-http-api-httpv2.t b/tests/test-http-api-httpv2.t
--- a/tests/test-http-api-httpv2.t
+++ b/tests/test-http-api-httpv2.t
@@ -63,11 +63,11 @@
 Request to read-only command works out of the box
 
   $ send << EOF
-  > httprequest GET api/$HTTPV2/ro/customreadonly
+  > httprequest POST api/$HTTPV2/ro/customreadonly
   > user-agent: test
   > EOF
   using raw connection to peer
-  s> GET /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
+  s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
   s> Accept-Encoding: identity\r\n
   s> user-agent: test\r\n
   s> host: $LOCALIP:$HGPORT\r\n (glob)
@@ -84,11 +84,11 @@
 Request to unknown command yields 404
 
   $ send << EOF
-  > httprequest GET api/$HTTPV2/ro/badcommand
+  > httprequest POST api/$HTTPV2/ro/badcommand
   > user-agent: test
   > EOF
   using raw connection to peer
-  s> GET /api/exp-http-v2-0001/ro/badcommand HTTP/1.1\r\n
+  s> POST /api/exp-http-v2-0001/ro/badcommand HTTP/1.1\r\n
   s> Accept-Encoding: identity\r\n
   s> user-agent: test\r\n
   s> host: $LOCALIP:$HGPORT\r\n (glob)
@@ -102,9 +102,30 @@
   s> \r\n
   s> unknown wire protocol command: badcommand\n
 
+GET to read-only command yields a 405
+
+  $ send << EOF
+  > httprequest GET api/$HTTPV2/ro/customreadonly
+  > user-agent: test
+  > EOF
+  using raw connection to peer
+  s> GET /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> user-agent: test\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> HTTP/1.1 405 Method Not Allowed\r\n
+  s> Server: testing stub value\r\n
+  s> Date: $HTTP_DATE$\r\n
+  s> Allow: POST\r\n
+  s> Content-Length: 30\r\n
+  s> \r\n
+  s> commands require POST requests
+
 Request to read-write command fails because server is read-only by default
 
-GET to read-write request not allowed
+GET to read-write request yields 405
 
   $ send << EOF
   > httprequest GET api/$HTTPV2/rw/customreadonly
@@ -117,12 +138,13 @@
   s> host: $LOCALIP:$HGPORT\r\n (glob)
   s> \r\n
   s> makefile('rb', None)
-  s> HTTP/1.1 405 push requires POST request\r\n
+  s> HTTP/1.1 405 Method Not Allowed\r\n
   s> Server: testing stub value\r\n
   s> Date: $HTTP_DATE$\r\n
-  s> Content-Length: 17\r\n
+  s> Allow: POST\r\n
+  s> Content-Length: 30\r\n
   s> \r\n
-  s> permission denied
+  s> commands require POST requests
 
 Even for unknown commands
 
@@ -137,12 +159,13 @@
   s> host: $LOCALIP:$HGPORT\r\n (glob)
   s> \r\n
   s> makefile('rb', None)
-  s> HTTP/1.1 405 push requires POST request\r\n
+  s> HTTP/1.1 405 Method Not Allowed\r\n
   s> Server: testing stub value\r\n
   s> Date: $HTTP_DATE$\r\n
-  s> Content-Length: 17\r\n
+  s> Allow: POST\r\n
+  s> Content-Length: 30\r\n
   s> \r\n
-  s> permission denied
+  s> commands require POST requests
 
 SSL required by default
 
@@ -173,38 +196,6 @@
   > web.api.http-v2 = true
   > [web]
   > push_ssl = false
-  > EOF
-
-  $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
-  $ cat hg.pid > $DAEMON_PIDS
-
-Server insists on POST for read-write commands
-
-  $ send << EOF
-  > httprequest GET api/$HTTPV2/rw/customreadonly
-  > user-agent: test
-  > EOF
-  using raw connection to peer
-  s> GET /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
-  s> Accept-Encoding: identity\r\n
-  s> user-agent: test\r\n
-  s> host: $LOCALIP:$HGPORT\r\n (glob)
-  s> \r\n
-  s> makefile('rb', None)
-  s> HTTP/1.1 405 push requires POST request\r\n
-  s> Server: testing stub value\r\n
-  s> Date: $HTTP_DATE$\r\n
-  s> Content-Length: 17\r\n
-  s> \r\n
-  s> permission denied
-
-  $ killdaemons.py
-  $ cat > server/.hg/hgrc << EOF
-  > [experimental]
-  > web.apiserver = true
-  > web.api.http-v2 = true
-  > [web]
-  > push_ssl = false
   > allow-push = *
   > EOF
 
diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py
+++ b/mercurial/wireprotoserver.py
@@ -299,6 +299,12 @@
 res.setbodybytes(_('unknown permission: %s') % permission)
 return
 
+if req.method != 'POST':
+res.status = b'405 Method Not Allowed'
+res.headers[b'Allow'] = b'POST'
+res.setbodybytes(_('commands require POST requests'))
+return
+
 # At some point we'll want to use our own API instead of recycling the
 # behavior of version 1 of the wire protocol...
 # TODO return 

D2849: hgweb: also set Content-Type header

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 7137.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2849?vs=7012=7137

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

AFFECTED FILES
  mercurial/hgweb/request.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -325,6 +325,9 @@
 if 'CONTENT_LENGTH' in env and 'HTTP_CONTENT_LENGTH' not in env:
 headers['Content-Length'] = env['CONTENT_LENGTH']
 
+if 'CONTENT_TYPE' in env and 'HTTP_CONTENT_TYPE' not in env:
+headers['Content-Type'] = env['CONTENT_TYPE']
+
 bodyfh = env['wsgi.input']
 if 'Content-Length' in headers:
 bodyfh = util.cappedreader(bodyfh, int(headers['Content-Length']))



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


D2836: wireproto: define permissions-based routing of HTTPv2 wire protocol

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg planned changes to this revision.
indygreg added a comment.


  I'm going to refactor tests for this to deal with an issue I had testing 
things on my MBP.

REPOSITORY
  rHG Mercurial

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

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


D2834: wireproto: support /api/* URL space for exposing APIs

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg added a comment.


  So, assuming we can't introduce new top-level URLs without risking conflicts 
with subrepos or hgwebdir virtual repos, for at least the wire protocol, 
assuming we keep the `?cmd=capabilities` mechanism for the initial server 
handshake, we can add something that defines where API requests should be 
routed. We can then allow server operators to reconfigure the base URL for 
these requests.

REPOSITORY
  rHG Mercurial

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

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


D2901: wireproto: explicitly track which requests are active

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  We previously only tracked which requests are receiving. A
  misbehaving client could accidentally have multiple requests with
  the same ID in flight.
  
  We now explicitly track which request IDs are currently active.
  We make it illegal to receive a frame associated with a request
  ID that has already been dispatched.

REPOSITORY
  rHG Mercurial

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

AFFECTED FILES
  mercurial/wireprotoframing.py
  tests/test-wireproto-serverreactor.py

CHANGE DETAILS

diff --git a/tests/test-wireproto-serverreactor.py 
b/tests/test-wireproto-serverreactor.py
--- a/tests/test-wireproto-serverreactor.py
+++ b/tests/test-wireproto-serverreactor.py
@@ -478,11 +478,11 @@
 results = list(sendframes(makereactor(), [
 ffs(b'1 command-name eos command1'),
 ffs(b'3 command-name have-data command3'),
-ffs(b'1 command-argument eoa ignored'),
+ffs(b'5 command-argument eoa ignored'),
 ]))
 self.assertaction(results[2], 'error')
 self.assertEqual(results[2][1], {
-'message': b'received frame for request that is not receiving: 1',
+'message': b'received frame for request that is not receiving: 5',
 })
 
 def testsimpleresponse(self):
@@ -571,6 +571,56 @@
 b'5 bytes-response eos response5',
 ])
 
+def testduplicaterequestonactivecommand(self):
+"""Receiving a request ID that matches a request that isn't 
finished."""
+reactor = makereactor()
+list(sendcommandframes(reactor, 1, b'command1', {}))
+results = list(sendcommandframes(reactor, 1, b'command1', {}))
+
+self.assertaction(results[0], 'error')
+self.assertEqual(results[0][1], {
+'message': b'request with ID 1 is already active',
+})
+
+def testduplicaterequestonactivecommandnosend(self):
+"""Same as above but we've registered a response but haven't sent 
it."""
+reactor = makereactor()
+list(sendcommandframes(reactor, 1, b'command1', {}))
+reactor.onbytesresponseready(1, b'response')
+
+# We've registered the response but haven't sent it. From the
+# perspective of the reactor, the command is still active.
+
+results = list(sendcommandframes(reactor, 1, b'command1', {}))
+self.assertaction(results[0], 'error')
+self.assertEqual(results[0][1], {
+'message': b'request with ID 1 is already active',
+})
+
+def testduplicaterequestargumentframe(self):
+"""Variant on above except we sent an argument frame instead of 
name."""
+reactor = makereactor()
+list(sendcommandframes(reactor, 1, b'command', {}))
+results = list(sendframes(reactor, [
+ffs(b'3 command-name have-args command'),
+ffs(b'1 command-argument 0 ignored'),
+]))
+self.assertaction(results[0], 'wantframe')
+self.assertaction(results[1], 'error')
+self.assertEqual(results[1][1], {
+'message': 'received frame for request that is still active: 1',
+})
+
+def testduplicaterequestaftersend(self):
+"""We can use a duplicate request ID after we've sent the response."""
+reactor = makereactor()
+list(sendcommandframes(reactor, 1, b'command1', {}))
+res = reactor.onbytesresponseready(1, b'response')
+list(res[1]['framegen'])
+
+results = list(sendcommandframes(reactor, 1, b'command1', {}))
+self.assertaction(results[0], 'runcommand')
+
 if __name__ == '__main__':
 import silenttestrunner
 silenttestrunner.main(__name__)
diff --git a/mercurial/wireprotoframing.py b/mercurial/wireprotoframing.py
--- a/mercurial/wireprotoframing.py
+++ b/mercurial/wireprotoframing.py
@@ -472,6 +472,10 @@
 self._bufferedframegens = []
 # request id -> dict of commands that are actively being received.
 self._receivingcommands = {}
+# Request IDs that have been received and are actively being processed.
+# Once all output for a request has been sent, it is removed from this
+# set.
+self._activecommands = set()
 
 def onframerecv(self, frame):
 """Process a frame that has been received off the wire.
@@ -496,14 +500,20 @@
 
 The raw bytes response is passed as an argument.
 """
-framegen = createbytesresponseframesfrombytes(requestid, data)
+def sendframes():
+for frame in createbytesresponseframesfrombytes(requestid, data):
+yield frame
+
+self._activecommands.remove(requestid)
+
+result = sendframes()
 
 if self._deferoutput:
-self._bufferedframegens.append(framegen)
+self._bufferedframegens.append(result)
  

D2900: wireproto: use named arguments when passing around frame data

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Named arguments is easier to reason about compared to positional
  arguments. Especially when you have many positional arguments.

REPOSITORY
  rHG Mercurial

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

AFFECTED FILES
  mercurial/wireprotoframing.py

CHANGE DETAILS

diff --git a/mercurial/wireprotoframing.py b/mercurial/wireprotoframing.py
--- a/mercurial/wireprotoframing.py
+++ b/mercurial/wireprotoframing.py
@@ -113,7 +113,7 @@
 flags = attr.ib()
 payload = attr.ib()
 
-def makeframe(requestid, frametype, frameflags, payload):
+def makeframe(requestid, typeid, flags, payload):
 """Assemble a frame into a byte array."""
 # TODO assert size of payload.
 frame = bytearray(FRAME_HEADER_SIZE + len(payload))
@@ -126,7 +126,7 @@
 l = struct.pack(r'

D2899: wireproto: define attr-based classes for representing frames

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  When frames only had 3 attributes, it was reasonable to
  represent them as a tuple. With them growing more attributes,
  it will be easier to pass them around as a more formal type.
  So let's define attr-based classes to represent frame headers and
  full frames.

REPOSITORY
  rHG Mercurial

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

AFFECTED FILES
  mercurial/wireprotoframing.py
  mercurial/wireprotoserver.py
  tests/test-wireproto-serverreactor.py

CHANGE DETAILS

diff --git a/tests/test-wireproto-serverreactor.py 
b/tests/test-wireproto-serverreactor.py
--- a/tests/test-wireproto-serverreactor.py
+++ b/tests/test-wireproto-serverreactor.py
@@ -18,11 +18,14 @@
 Emits a generator of results from ``onframerecv()`` calls.
 """
 for frame in gen:
-rid, frametype, frameflags, framelength = framing.parseheader(frame)
+header = framing.parseheader(frame)
 payload = frame[framing.FRAME_HEADER_SIZE:]
-assert len(payload) == framelength
+assert len(payload) == header.length
 
-yield reactor.onframerecv(rid, frametype, frameflags, payload)
+yield reactor.onframerecv(framing.frame(header.requestid,
+header.typeid,
+header.flags,
+payload))
 
 def sendcommandframes(reactor, rid, cmd, args, datafh=None):
 """Generate frames to run a command and send them to a reactor."""
diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py
+++ b/mercurial/wireprotoserver.py
@@ -400,12 +400,11 @@
 states.append(b'received: ')
 break
 
-requestid, frametype, frameflags, payload = frame
-states.append(b'received: %d %d %d %s' % (frametype, frameflags,
-  requestid, payload))
+states.append(b'received: %d %d %d %s' % (frame.typeid, frame.flags,
+  frame.requestid,
+  frame.payload))
 
-action, meta = reactor.onframerecv(requestid, frametype, frameflags,
-   payload)
+action, meta = reactor.onframerecv(frame)
 states.append(json.dumps((action, meta), sort_keys=True,
  separators=(', ', ': ')))
 
@@ -434,7 +433,7 @@
 if not frame:
 break
 
-action, meta = reactor.onframerecv(*frame)
+action, meta = reactor.onframerecv(frame)
 
 if action == 'wantframe':
 # Need more data before we can do anything.
diff --git a/mercurial/wireprotoframing.py b/mercurial/wireprotoframing.py
--- a/mercurial/wireprotoframing.py
+++ b/mercurial/wireprotoframing.py
@@ -14,6 +14,9 @@
 import struct
 
 from .i18n import _
+from .thirdparty import (
+attr,
+)
 from . import (
 error,
 util,
@@ -92,6 +95,24 @@
 
 ARGUMENT_FRAME_HEADER = struct.Struct(r'> 4
 frameflags = typeflags & 0x0f
 
-return requestid, frametype, frameflags, framelength
+return frameheader(framelength, requestid, frametype, frameflags)
 
 def readframe(fh):
 """Read a unified framing protocol frame from a file object.
@@ -184,14 +205,14 @@
 raise error.Abort(_('received incomplete frame: got %d bytes: %s') %
   (readcount, header))
 
-requestid, frametype, frameflags, framelength = parseheader(header)
+h = parseheader(header)
 
-payload = fh.read(framelength)
-if len(payload) != framelength:
+payload = fh.read(h.length)
+if len(payload) != h.length:
 raise error.Abort(_('frame length error: expected %d; got %d') %
-  (framelength, len(payload)))
+  (h.length, len(payload)))
 
-return requestid, frametype, frameflags, payload
+return frame(h.requestid, h.typeid, h.flags, payload)
 
 def createcommandframes(requestid, cmd, args, datafh=None):
 """Create frames necessary to transmit a request to run a command.
@@ -433,7 +454,7 @@
 # request id -> dict of commands that are actively being received.
 

D1955: bundlespec: add support for some variants

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg accepted this revision.
indygreg added a comment.
This revision is now accepted and ready to land.


  This looks reasonable to me.
  
  I'll hold off landing due to requirements on an earlier, not yet accepted 
patch.

REPOSITORY
  rHG Mercurial

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

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


D1954: bundle: add the possibility to bundle a stream v2 part

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg requested changes to this revision.
indygreg added a comment.
This revision now requires changes to proceed.


  In https://phab.mercurial-scm.org/D1954#33576, @lothiraldan wrote:
  
  > Now that we have more time to review this series, this is the part I am the 
least comfortable with. I think we could/should extract the stream part 
generator somewhere else than exchange.py. Maybe bundle2.py directly but I'm 
not sure. Any ideas?
  
  
  Yup. You've stumbled upon something: we have 2 variations of code that 
produce bundle2 data. We have `bundle2._addpartsfromopts()` and 
`exchange.getbundlechunks()`. There is redundancy between the two. For example, 
creation of the `changegroup` part.
  
  I would *very* much like to see this code unified. Probably in `bundle2.py`. 
What makes it difficult to unify is that each implementation is determining 
*what data should I generate* via different mechanisms. `bundle2.py` is using a 
dict of [content] options. `exchange.py` is using bundle2 capabilities, which 
come from the client.
  
  If you squint hard enough, these are both almost the same thing. I would like 
to think that we could normalize b2caps and content options down to a single 
data structure that described what to generate and we could pass that into a 
common *generate bundle2 content* function. I /think/ we want to unify on 
content options. But I'm not sure.
  
  Things are a bit more difficult than they should be because bundle2 is both 
used for data at rest and in flight. It represents both static data and the 
request/results of an RPC call over the wire protocol. e.g. the `check:*`, 
`error:*`, and `reply:*` parts don't make a lot of sense for local-only bundle2 
bundles. Those are there mainly to facilitate wire protocol features. So any 
unified *create a bundle* or *apply a bundle* functionality need to account for 
the fact that there are special bundle2 parts and functionality that are unique 
to the wire protocol.
  
  Anyway, I'd fix the immediate concern of having to import `exchange` by 
moving the code for creating this bundle2 part into `bundle2.py` and having the 
part generator in `exchange` call into it.

REPOSITORY
  rHG Mercurial

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

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


D1952: bundlespec: move computing the bundle contentops in parsebundlespec

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg added a comment.


  I took a bit of a deeper look and found a few more things.
  
  Again, I like the direction of this patch. I think our goal for bundle2 
generation should be to derive a set of options for its parts up front as quick 
as possible and pass that data structure into the lower-level bundle2 
generation mechanism.

INLINE COMMENTS

> exchange.py:49-51
> +# Maps bundle version with content opts to choose which part to bundle
> +_bundlespeccontentops = {
> +'v1': {

I *really* like this and this is the direction we need to go. i.e. a bundlespec 
base version should imply a set of options for generation of the bundle.

> exchange.py:89
>  
>  Returns a 3-tuple of (compression, version, parameters). Compression will
>  be ``None`` if not in strict mode and a compression isn't defined.

This comment needs updated.

> exchange.py:193
> +
> +return compression, version, params, contentops
>  

At the point we're returning yet another element, it might be worth converting 
the return into an `attr`-derived class. We're starting to use those a bit to 
define data structures that are too large for tuples but don't yet merit a 
normal class.

REPOSITORY
  rHG Mercurial

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

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


D1951: bundle: condition the changegroup part when creating a new bundle

2018-03-19 Thread lothiraldan (Boris Feld)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG66c0ff381cfc: bundle: condition the changegroup part when 
creating a new bundle (authored by lothiraldan, committed by ).

CHANGED PRIOR TO COMMIT
  https://phab.mercurial-scm.org/D1951?vs=5150=7129#toc

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D1951?vs=5150=7129

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

AFFECTED FILES
  mercurial/bundle2.py
  mercurial/commands.py

CHANGE DETAILS

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -1263,7 +1263,7 @@
 compopts['level'] = complevel
 
 
-contentopts = {'cg.version': cgversion}
+contentopts = {'cg.version': cgversion, 'changegroup': True}
 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
 contentopts['obsolescence'] = True
 if repo.ui.configbool('experimental', 'bundle-phases'):
diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
--- a/mercurial/bundle2.py
+++ b/mercurial/bundle2.py
@@ -1577,19 +1577,21 @@
 # different right now. So we keep them separated for now for the sake of
 # simplicity.
 
-# we always want a changegroup in such bundle
-cgversion = opts.get('cg.version')
-if cgversion is None:
-cgversion = changegroup.safeversion(repo)
-cg = changegroup.makechangegroup(repo, outgoing, cgversion, source)
-part = bundler.newpart('changegroup', data=cg.getchunks())
-part.addparam('version', cg.version)
-if 'clcount' in cg.extras:
-part.addparam('nbchanges', '%d' % cg.extras['clcount'],
-  mandatory=False)
-if opts.get('phases') and repo.revs('%ln and secret()',
-outgoing.missingheads):
-part.addparam('targetphase', '%d' % phases.secret, mandatory=False)
+# we might not always want a changegroup in such bundle, for example in
+# stream bundles
+if opts.get('changegroup', True):
+cgversion = opts.get('cg.version')
+if cgversion is None:
+cgversion = changegroup.safeversion(repo)
+cg = changegroup.makechangegroup(repo, outgoing, cgversion, source)
+part = bundler.newpart('changegroup', data=cg.getchunks())
+part.addparam('version', cg.version)
+if 'clcount' in cg.extras:
+part.addparam('nbchanges', '%d' % cg.extras['clcount'],
+  mandatory=False)
+if opts.get('phases') and repo.revs('%ln and secret()',
+outgoing.missingheads):
+part.addparam('targetphase', '%d' % phases.secret, mandatory=False)
 
 addparttagsfnodescache(repo, bundler, outgoing)
 addpartrevbranchcache(repo, bundler, outgoing)



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


D2400: stack: begin to make the stack revset configurable

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg requested changes to this revision.
indygreg added a comment.
This revision now requires changes to proceed.


  Reading through the remainder of the series, I like the customization of 
stack evaluation. But the use of config options seems weird to me. It isn't 
clear why we need config options. It feels like these options should be passed 
in as function arguments. Now, it will be useful to test these variations. 
Maybe that does require config options. But until there is a user-facing need 
for these config options, It feels better to put them in `[devel]` or something 
like that.
  
  Is there a future consumer of these config options that justifies them being 
normal config options?

REPOSITORY
  rHG Mercurial

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

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


D2400: stack: begin to make the stack revset configurable

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg accepted this revision.
indygreg added a comment.
This revision is now accepted and ready to land.


  I'm not super keen on introducing a new `[stack]` config section. But I don't 
have any better ideas for where this should go. `[ui]` is my best idea, but I 
thought we decided we wanted to stop stuffing random stuff into `[ui]`. So I 
dunno.

INLINE COMMENTS

> stack.py:41-42
> +
> +finalrevspec = " and ".join(revspec)
> +revset = revsetlang.formatspec(finalrevspec, *revspecargs)
>  revisions = scmutil.revrange(repo, [revset])

As I review just this patch, I question if we shouldn't just concatenate `and 
not ::merge()` to the revset string. But I know other changes are coming. I 
assume the final state justifies the extra complexity.

REPOSITORY
  rHG Mercurial

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

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


D2396: stack: import Evolve stack test file

2018-03-19 Thread lothiraldan (Boris Feld)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG407934a97bc7: stack: import Evolve stack test file 
(authored by lothiraldan, committed by ).

CHANGED PRIOR TO COMMIT
  https://phab.mercurial-scm.org/D2396?vs=6213=7125#toc

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2396?vs=6213=7125

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

AFFECTED FILES
  mercurial/revset.py
  tests/test-stack.t

CHANGE DETAILS

diff --git a/tests/test-stack.t b/tests/test-stack.t
new file mode 100644
--- /dev/null
+++ b/tests/test-stack.t
@@ -0,0 +1,253 @@
+
+This test test the low-level definition of stack, agnostic from all formatting
+
+Initial setup
+
+  $ cat << EOF >> $HGRCPATH
+  > [ui]
+  > logtemplate = {rev} {branch} {phase} {desc|firstline}\n
+  > [extensions]
+  > rebase=
+  > [experimental]
+  > evolution=createmarkers,exchange,allowunstable
+  > EOF
+
+  $ hg init main
+  $ cd main
+  $ hg branch other
+  marked working directory as branch other
+  (branches are permanent and global, did you want a bookmark?)
+  $ echo aaa > aaa
+  $ hg add aaa
+  $ hg commit -m c_a
+  $ echo aaa > bbb
+  $ hg add bbb
+  $ hg commit -m c_b
+  $ hg branch foo
+  marked working directory as branch foo
+  $ echo aaa > ccc
+  $ hg add ccc
+  $ hg commit -m c_c
+  $ echo aaa > ddd
+  $ hg add ddd
+  $ hg commit -m c_d
+  $ echo aaa > eee
+  $ hg add eee
+  $ hg commit -m c_e
+  $ echo aaa > fff
+  $ hg add fff
+  $ hg commit -m c_f
+  $ hg log -G
+  @  5 foo draft c_f
+  |
+  o  4 foo draft c_e
+  |
+  o  3 foo draft c_d
+  |
+  o  2 foo draft c_c
+  |
+  o  1 other draft c_b
+  |
+  o  0 other draft c_a
+  
+
+Check that stack doesn't include public changesets
+--
+
+  $ hg up other
+  0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+  $ hg log -G -r "stack()"
+  @  1 other draft c_b
+  |
+  o  0 other draft c_a
+  
+  $ hg phase --public 'branch("other")'
+  $ hg log -G -r "stack()"
+  $ hg up foo
+  4 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+Simple test
+---
+
+'stack()' list all changeset in the branch
+
+  $ hg branch
+  foo
+  $ hg log -G -r "stack()"
+  @  5 foo draft c_f
+  |
+  o  4 foo draft c_e
+  |
+  o  3 foo draft c_d
+  |
+  o  2 foo draft c_c
+  |
+  ~
+
+Case with some of the branch unstable
+
+
+  $ hg up 3
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ echo bbb > ddd
+  $ hg commit --amend
+  2 new orphan changesets
+  $ hg log -G
+  @  6 foo draft c_d
+  |
+  | *  5 foo draft c_f
+  | |
+  | *  4 foo draft c_e
+  | |
+  | x  3 foo draft c_d
+  |/
+  o  2 foo draft c_c
+  |
+  o  1 other public c_b
+  |
+  o  0 other public c_a
+  
+  $ hg log -G -r "stack()"
+  @  6 foo draft c_d
+  |
+  ~
+  $ hg up -r "desc(c_e)"
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log -G -r "stack()"
+  @  4 foo draft c_e
+  |
+  x  3 foo draft c_d
+  |
+  ~
+  $ hg up -r "desc(c_d)"
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+  $ hg log -G -r "stack()"
+  @  6 foo draft c_d
+  |
+  ~
+
+Case with multiple topological heads
+
+
+Make things linear again
+
+  $ hg rebase -s 'desc(c_e)' -d 'desc(c_d) - obsolete()'
+  rebasing 4:4f2a69f6d380 "c_e"
+  rebasing 5:913c298d8b0a "c_f"
+  $ hg log -G
+  o  8 foo draft c_f
+  |
+  o  7 foo draft c_e
+  |
+  @  6 foo draft c_d
+  |
+  o  2 foo draft c_c
+  |
+  o  1 other public c_b
+  |
+  o  0 other public c_a
+  
+
+Create the second branch
+
+  $ hg up 'desc(c_d)'
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo aaa > ggg
+  $ hg add ggg
+  $ hg commit -m c_g
+  created new head
+  $ echo aaa > hhh
+  $ hg add hhh
+  $ hg commit -m c_h
+  $ hg log -G
+  @  10 foo draft c_h
+  |
+  o  9 foo draft c_g
+  |
+  | o  8 foo draft c_f
+  | |
+  | o  7 foo draft c_e
+  |/
+  o  6 foo draft c_d
+  |
+  o  2 foo draft c_c
+  |
+  o  1 other public c_b
+  |
+  o  0 other public c_a
+  
+
+Test output
+
+  $ hg log -G -r "stack(10)"
+  @  10 foo draft c_h
+  |
+  o  9 foo draft c_g
+  |
+  ~
+  $ hg log -G -r "stack(8)"
+  o  8 foo draft c_f
+  |
+  o  7 foo draft c_e
+  |
+  ~
+  $ hg log -G -r "stack(head())"
+  @  10 foo draft c_h
+  |
+  o  9 foo draft c_g
+  |
+  ~
+  o  8 foo draft c_f
+  |
+  o  7 foo draft c_e
+  |
+  ~
+Check the stack order
+  $ hg log -r "first(stack())"
+  10 foo draft c_h
+  $ hg log -r "first(stack(10))"
+  10 foo draft c_h
+  $ hg log -r "first(stack(8))"
+  8 foo draft c_f
+  $ hg log -r "first(stack(head()))"
+  8 foo draft c_f
+
+Case with multiple heads with unstability involved
+--
+
+We amend the message to make sure the display base pick the right changeset
+
+  $ hg up 'desc(c_d)'
+  0 files updated, 0 files merged, 2 

D2398: histedit: use the new stack definition for histedit

2018-03-19 Thread lothiraldan (Boris Feld)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG2987726085c6: histedit: use the new stack definition for 
histedit (authored by lothiraldan, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2398?vs=6214=7127

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

AFFECTED FILES
  hgext/histedit.py
  mercurial/destutil.py

CHANGE DETAILS

diff --git a/mercurial/destutil.py b/mercurial/destutil.py
--- a/mercurial/destutil.py
+++ b/mercurial/destutil.py
@@ -340,18 +340,20 @@
 onheadcheck=onheadcheck, destspace=destspace)
 return repo[node].rev()
 
-histeditdefaultrevset = 'reverse(only(.) and not public() and not ::merge())'
-
 def desthistedit(ui, repo):
 """Default base revision to edit for `hg histedit`."""
-default = ui.config('histedit', 'defaultrev', histeditdefaultrevset)
-if default:
+default = ui.config('histedit', 'defaultrev')
+
+if default is None:
+revs = stack.getstack(repo)
+elif default:
 revs = scmutil.revrange(repo, [default])
-if revs:
-# The revset supplied by the user may not be in ascending order nor
-# take the first revision. So do this manually.
-revs.sort()
-return revs.first()
+
+if revs:
+# The revset supplied by the user may not be in ascending order nor
+# take the first revision. So do this manually.
+revs.sort()
+return revs.first()
 
 return None
 
diff --git a/hgext/histedit.py b/hgext/histedit.py
--- a/hgext/histedit.py
+++ b/hgext/histedit.py
@@ -221,7 +221,7 @@
 default=False,
 )
 configitem('histedit', 'defaultrev',
-default=configitem.dynamicdefault,
+default=None,
 )
 configitem('histedit', 'dropmissing',
 default=False,



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


D2399: stack: return a sorted smartrev by default

2018-03-19 Thread lothiraldan (Boris Feld)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG68fcc5503ec5: stack: return a sorted smartrev by default 
(authored by lothiraldan, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2399?vs=6215=7128

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

AFFECTED FILES
  mercurial/destutil.py
  mercurial/stack.py
  tests/test-stack.t

CHANGE DETAILS

diff --git a/tests/test-stack.t b/tests/test-stack.t
--- a/tests/test-stack.t
+++ b/tests/test-stack.t
@@ -205,13 +205,13 @@
   ~
 Check the stack order
   $ hg log -r "first(stack())"
-  10 foo draft c_h
+  9 foo draft c_g
   $ hg log -r "first(stack(10))"
-  10 foo draft c_h
+  9 foo draft c_g
   $ hg log -r "first(stack(8))"
-  8 foo draft c_f
+  7 foo draft c_e
   $ hg log -r "first(stack(head()))"
-  8 foo draft c_f
+  7 foo draft c_e
 
 Case with multiple heads with unstability involved
 --
diff --git a/mercurial/stack.py b/mercurial/stack.py
--- a/mercurial/stack.py
+++ b/mercurial/stack.py
@@ -13,8 +13,8 @@
 )
 
 def getstack(repo, rev=None):
-"""return a smartrev of the stack containing either rev if it is not None
-or the current working directory parent.
+"""return a sorted smartrev of the stack containing either rev if it is
+not None or the current working directory parent.
 
 The stack will always contain all drafts changesets which are ancestors to
 the revision and are not merges.
@@ -24,4 +24,6 @@
 
 revspec = 'reverse(only(%s) and not public() and not ::merge())'
 revset = revsetlang.formatspec(revspec, rev)
-return scmutil.revrange(repo, [revset])
+revisions = scmutil.revrange(repo, [revset])
+revisions.sort()
+return revisions
diff --git a/mercurial/destutil.py b/mercurial/destutil.py
--- a/mercurial/destutil.py
+++ b/mercurial/destutil.py
@@ -359,7 +359,7 @@
 
 def stackbase(ui, repo):
 revs = stack.getstack(repo)
-return revs.last() if revs else None
+return revs.first() if revs else None
 
 def _statusotherbook(ui, repo):
 bmheads = bookmarks.headsforactive(repo)



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


D2397: show: use the new stack definition for show stack

2018-03-19 Thread lothiraldan (Boris Feld)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGa72198790e15: show: use the new stack definition for show 
stack (authored by lothiraldan, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2397?vs=6011=7126

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

AFFECTED FILES
  mercurial/destutil.py

CHANGE DETAILS

diff --git a/mercurial/destutil.py b/mercurial/destutil.py
--- a/mercurial/destutil.py
+++ b/mercurial/destutil.py
@@ -13,6 +13,7 @@
 error,
 obsutil,
 scmutil,
+stack
 )
 
 def _destupdateobs(repo, clean):
@@ -355,9 +356,7 @@
 return None
 
 def stackbase(ui, repo):
-# The histedit default base stops at public changesets, branchpoints,
-# and merges, which is exactly what we want for a stack.
-revs = scmutil.revrange(repo, [histeditdefaultrevset])
+revs = stack.getstack(repo)
 return revs.last() if revs else None
 
 def _statusotherbook(ui, repo):



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


D2396: stack: import Evolve stack test file

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg accepted this revision.
indygreg added a subscriber: yuja.
indygreg added inline comments.
This revision is now accepted and ready to land.

INLINE COMMENTS

> revset.py:1534
>  
> +@predicate('stack([revs])', safe=True)
> +def _stack(repo, subset, x):

I'm not sure if the predicate should be prefixed with an underscore to mark it 
as internal. My reading of the help code is that the lack of a docstring will 
hide it from the docs.

I /think/ this is OK. But I wouldn't be surprised if @yuja or someone told me 
it needs changed. I'll queue this for now. We can always fix it up later if it 
is a problem.

REPOSITORY
  rHG Mercurial

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

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


D2897: fix: new extension for automatically modifying file contents

2018-03-19 Thread hooper (Danny Hooper)
hooper created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  This change implements most of the corresponding proposal as discussed at the
  4.4 and 4.6 sprints: 
https://www.mercurial-scm.org/wiki/AutomaticFormattingPlan
  
  This change notably does not include parallel execution of the formatter/fixer
  tools. It does allow for implementing that without affecting other areas of 
the
  code.
  
  I believe the test coverage to be good, but this is a hotbed of corner cases.

REPOSITORY
  rHG Mercurial

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

AFFECTED FILES
  hgext/fix.py
  tests/test-doctest.py
  tests/test-fix-clang-format.t
  tests/test-fix-topology.t
  tests/test-fix.t

CHANGE DETAILS

diff --git a/tests/test-fix.t b/tests/test-fix.t
new file mode 100644
--- /dev/null
+++ b/tests/test-fix.t
@@ -0,0 +1,927 @@
+Set up the config with two simple fixers: one that fixes specific line ranges,
+and one that always fixes the whole file. They both "fix" files by converting
+letters to uppercase. They use different file extensions, so each test case can
+choose which behavior to use by naming files.
+
+  $ cat >> $HGRCPATH < [extensions]
+  > fix =
+  > [experimental]
+  > evolution.createmarkers=True
+  > evolution.allowunstable=True
+  > [fix]
+  > uppercase-whole-file:command=sed -e 's/.*/\U&/'
+  > uppercase-whole-file:fileset=set:**.whole
+  > uppercase-changed-lines:command=sed
+  > uppercase-changed-lines:linerange=-e '{first},{last} s/.*/\U&/'
+  > uppercase-changed-lines:fileset=set:**.changed
+  > EOF
+
+Help text for fix.
+
+  $ hg help fix
+  hg fix [OPTION]... [FILE]...
+  
+  rewrite file content in changesets or working directory
+  
+  Runs any configured tools to fix the content of files. Only affects files
+  with changes, unless file arguments are provided. Only affects changed
+  lines of files, unless the --whole flag is used. Some tools may always
+  affect the whole file regardless of --whole.
+  
+  If revisions are specified with --rev, those revisions will be checked,
+  and they may be replaced with new revisions that have fixed file content.
+  It is desirable to specify all descendants of each specified revision, so
+  that the fixes propagate to the descendants. If all descendants are fixed
+  at the same time, no merging, rebasing, or evolution will be required.
+  
+  If --working-dir is used, files with uncommitted changes in the working
+  copy will be fixed. If the checked-out revision is also fixed, the 
working
+  directory will update to the replacement revision.
+  
+  When determining what lines of each file to fix at each revision, the
+  whole set of revisions being fixed is considered, so that fixes to 
earlier
+  revisions are not forgotten in later ones. The --base flag can be used to
+  override this default behavior, though it is not usually desirable to do
+  so.
+  
+  (use 'hg help -e fix' to show help for the fix extension)
+  
+  options ([+] can be repeated):
+  
+  --base REV [+] revisions to diff against (overrides automatic selection,
+ and applies to every revision being fixed)
+   -r --rev REV [+]  revisions to fix
+   -w --working-dir  fix the working directory
+  --wholealways fix every line of a file
+  
+  (some details hidden, use --verbose to show complete help)
+
+  $ hg help -e fix
+  fix extension - rewrite file content in changesets or working copy
+  (EXPERIMENTAL)
+  
+  Provides a command that runs configured tools on the contents of modified
+  files, writing back any fixes to the working copy or replacing changesets.
+  
+  Here is an example configuration that causes 'hg fix' to apply automatic
+  formatting fixes to modified lines in C++ code:
+  
+[fix]
+clang-format:command=clang-format --assume-filename={rootpath}
+clang-format:linerange=--lines={first}:{last}
+clang-format:fileset=set:**.cpp or **.hpp
+  
+  The :command suboption forms the first part of the shell command that will be
+  used to fix a file. The content of the file is passed on standard input, and
+  the fixed file content is expected on standard output. If there is any output
+  on standard error, the file will not be affected. Some values may be
+  substituted into the command:
+  
+{rootpath}  The path of the file being fixed, relative to the repo root
+{basename}  The name of the file being fixed, without the directory path
+  
+  If the :linerange suboption is set, the tool will only be run if there are
+  changed lines in a file. The value of this suboption is appended to the shell
+  command once for every range of changed lines in the file. Some values may be
+  substituted into the command:
+  
+{first}   The 1-based line number of the first line in the modified range
+{last}The 1-based line number of the last line in the modified 

D2889: filemerge: make the 'local' path match the format that 'base' and 'other' use

2018-03-19 Thread spectral (Kyle Lippincott)
spectral requested review of this revision.
spectral added a comment.


  Ah, there's a "Request Review" that I'd never noticed before, choosing that 
to hopefully get this out of "Requires Revision"

REPOSITORY
  rHG Mercurial

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

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


D2887: filemerge: move temp file unlinks to _maketempfiles

2018-03-19 Thread spectral (Kyle Lippincott)
spectral updated this revision to Diff 7121.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2887?vs=7118=7121

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

AFFECTED FILES
  mercurial/filemerge.py

CHANGE DETAILS

diff --git a/mercurial/filemerge.py b/mercurial/filemerge.py
--- a/mercurial/filemerge.py
+++ b/mercurial/filemerge.py
@@ -7,6 +7,7 @@
 
 from __future__ import absolute_import
 
+import contextlib
 import os
 import re
 import tempfile
@@ -510,8 +511,8 @@
 return False, 1, None
 unused, unused, unused, back = files
 localpath = _workingpath(repo, fcd)
-basepath, otherpath = _maketempfiles(repo, fco, fca)
-try:
+with _maketempfiles(repo, fco, fca) as temppaths:
+basepath, otherpath = temppaths
 outpath = ""
 mylabel, otherlabel = labels[:2]
 if len(labels) >= 3:
@@ -549,9 +550,6 @@
 r = ui.system(cmd, cwd=repo.root, environ=env, blockedtag='mergetool')
 repo.ui.debug('merge tool returned: %d\n' % r)
 return True, r, False
-finally:
-util.unlink(basepath)
-util.unlink(otherpath)
 
 def _formatconflictmarker(ctx, template, label, pad):
 """Applies the given template to the ctx, prefixed by the label.
@@ -665,6 +663,7 @@
 # the backup context regardless of where it lives.
 return context.arbitraryfilectx(back, repo=repo)
 
+@contextlib.contextmanager
 def _maketempfiles(repo, fco, fca):
 """Writes out `fco` and `fca` as temporary files, so an external merge
 tool may use them.
@@ -681,8 +680,11 @@
 
 b = temp("base", fca)
 c = temp("other", fco)
-
-return b, c
+try:
+yield b, c
+finally:
+util.unlink(b)
+util.unlink(c)
 
 def _filemerge(premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=None):
 """perform a 3-way merge in the working directory



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


D2888: filemerge: use a single temp dir instead of temp files

2018-03-19 Thread spectral (Kyle Lippincott)
spectral updated this revision to Diff 7122.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2888?vs=7119=7122

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

AFFECTED FILES
  mercurial/configitems.py
  mercurial/filemerge.py
  tests/test-merge-tools.t

CHANGE DETAILS

diff --git a/tests/test-merge-tools.t b/tests/test-merge-tools.t
--- a/tests/test-merge-tools.t
+++ b/tests/test-merge-tools.t
@@ -1363,6 +1363,33 @@
   (branch merge, don't forget to commit)
   $ rm -f 'printargs_merge_tool'
 
+Same test with experimental.mergetempdirprefix set:
+
+  $ beforemerge
+  [merge-tools]
+  false.whatever=
+  true.priority=1
+  true.executable=cat
+  # hg update -C 1
+  $ cat < printargs_merge_tool
+  > while test \$# -gt 0; do echo arg: \"\$1\"; shift; done
+  > EOF
+  $ hg --config experimental.mergetempdirprefix=$TESTTMP/hgmerge. \
+  >--config merge-tools.true.executable='sh' \
+  >--config merge-tools.true.args='./printargs_merge_tool ll:$labellocal 
lo: $labelother lb:$labelbase": "$base' \
+  >--config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
+  >--config ui.mergemarkertemplate='uitmpl {rev}' \
+  >--config ui.mergemarkers=detailed \
+  >merge -r 2
+  merging f
+  arg: "ll:working copy"
+  arg: "lo:"
+  arg: "merge rev"
+  arg: "lb:base: $TESTTMP/hgmerge.??/f~base" (glob)
+  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ rm -f 'printargs_merge_tool'
+
 Merge using a tool that supports labellocal, labelother, and labelbase, 
checking
 that they're quoted properly as well. This is using 'detailed' mergemarkers,
 even though ui.mergemarkers is 'basic', and using the tool's
@@ -1562,6 +1589,20 @@
   0 files updated, 1 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
 
+Verify naming of temporary files and that extension is preserved
+(experimental.mergetempdirprefix version):
+
+  $ hg update -q -C 1
+  $ hg mv f f.txt
+  $ hg ci -qm "f.txt"
+  $ hg update -q -C 2
+  $ hg merge -y -r tip --tool echo \
+  >--config merge-tools.echo.args='$base $local $other $output' \
+  >--config experimental.mergetempdirprefix=$TESTTMP/hgmerge.
+  merging f and f.txt to f.txt
+  $TESTTMP/hgmerge.??/f~base $TESTTMP/f.txt.orig 
$TESTTMP/hgmerge.??/f~other.txt $TESTTMP/f.txt (glob)
+  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
 Check that debugpicktool examines which merge tool is chosen for
 specified file as expected
 
diff --git a/mercurial/filemerge.py b/mercurial/filemerge.py
--- a/mercurial/filemerge.py
+++ b/mercurial/filemerge.py
@@ -10,6 +10,7 @@
 import contextlib
 import os
 import re
+import shutil
 import tempfile
 
 from .i18n import _
@@ -668,12 +669,23 @@
 """Writes out `fco` and `fca` as temporary files, so an external merge
 tool may use them.
 """
+tmproot = None
+tmprootprefix = repo.ui.config('experimental', 'mergetempdirprefix')
+if tmprootprefix:
+tmproot = tempfile.mkdtemp(prefix=tmprootprefix)
+
 def temp(prefix, ctx):
 fullbase, ext = os.path.splitext(ctx.path())
-pre = "%s~%s." % (os.path.basename(fullbase), prefix)
-(fd, name) = tempfile.mkstemp(prefix=pre, suffix=ext)
+pre = "%s~%s" % (os.path.basename(fullbase), prefix)
+if tmproot:
+name = os.path.join(tmproot, pre)
+if ext:
+name += ext
+f = open(name, r"wb")
+else:
+(fd, name) = tempfile.mkstemp(prefix=pre + '.', suffix=ext)
+f = os.fdopen(fd, r"wb")
 data = repo.wwritedata(ctx.path(), ctx.data())
-f = os.fdopen(fd, r"wb")
 f.write(data)
 f.close()
 return name
@@ -683,8 +695,11 @@
 try:
 yield b, c
 finally:
-util.unlink(b)
-util.unlink(c)
+if tmproot:
+shutil.rmtree(tmproot)
+else:
+util.unlink(b)
+util.unlink(c)
 
 def _filemerge(premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=None):
 """perform a 3-way merge in the working directory
diff --git a/mercurial/configitems.py b/mercurial/configitems.py
--- a/mercurial/configitems.py
+++ b/mercurial/configitems.py
@@ -502,6 +502,9 @@
 coreconfigitem('experimental', 'maxdeltachainspan',
 default=-1,
 )
+coreconfigitem('experimental', 'mergetempdirprefix',
+default=None,
+)
 coreconfigitem('experimental', 'mmapindexthreshold',
 default=None,
 )



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


D2889: filemerge: make the 'local' path match the format that 'base' and 'other' use

2018-03-19 Thread spectral (Kyle Lippincott)
spectral added a comment.


  In https://phab.mercurial-scm.org/D2889#46482, @yuja wrote:
  
  > Not reviewed yet, but this can't be applied cleanly. Can you rebase?
  
  
  This series should rebase cleanly onto 
https://phab.mercurial-scm.org/rHGa4a95bd7158de9e932ccf5e8d60095609fbe9994 
(which was accepted by pulkit separately from the rest of the series).  I've 
rebase these three remaining ones and pushed.

REPOSITORY
  rHG Mercurial

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

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


Re: [PATCH 2 of 3 V2] templater: use named function to expand template against mapping dict (API)

2018-03-19 Thread Martin von Zweigbergk via Mercurial-devel
On Mon, Mar 19, 2018 at 6:45 AM Yuya Nishihara  wrote:

> # HG changeset patch
> # User Yuya Nishihara 
> # Date 1521203972 -32400
> #  Fri Mar 16 21:39:32 2018 +0900
> # Node ID faaed935789fd2b36121a5a5ec407d1cb694d48b
> # Parent  75c311c2a72be114b791e0a6d4dd0d1cef20fa7b
> templater: use named function to expand template against mapping dict (API)
>

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


D2896: commands: use keyword arguments in update function

2018-03-19 Thread pulkit (Pulkit Goyal)
pulkit created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  This will help us in having a dictionary with the values of all the arguments
  and we can add more flags without adding an argument to the function.

REPOSITORY
  rHG Mercurial

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

AFFECTED FILES
  mercurial/commands.py

CHANGE DETAILS

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -5442,8 +5442,7 @@
 ('r', 'rev', '', _('revision'), _('REV'))
  ] + mergetoolopts,
 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
-def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
-   merge=None, tool=None):
+def update(ui, repo, node=None, **opts):
 """update working directory (or switch revisions)
 
 Update the repository's working directory to the specified
@@ -5498,6 +5497,11 @@
 
 Returns 0 on success, 1 if there are unresolved files.
 """
+rev = opts.get('rev')
+date = opts.get('date')
+clean = opts.get('clean')
+check = opts.get('check')
+merge = opts.get('merge')
 if rev and node:
 raise error.Abort(_("please specify just one revision"))
 
@@ -5542,7 +5546,7 @@
 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
 ui.warn("(%s)\n" % obsfatemsg)
 
-repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
+repo.ui.setconfig('ui', 'forcemerge', opts.get('tool'), 'update')
 
 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
 updatecheck=updatecheck)



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


D2888: filemerge: use a single temp dir instead of temp files

2018-03-19 Thread spectral (Kyle Lippincott)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGe349ad5cbb71: filemerge: use a single temp dir instead of 
temp files (authored by spectral, committed by ).

CHANGED PRIOR TO COMMIT
  https://phab.mercurial-scm.org/D2888?vs=7086=7119#toc

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2888?vs=7086=7119

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

AFFECTED FILES
  mercurial/configitems.py
  mercurial/filemerge.py
  tests/test-merge-tools.t

CHANGE DETAILS

diff --git a/tests/test-merge-tools.t b/tests/test-merge-tools.t
--- a/tests/test-merge-tools.t
+++ b/tests/test-merge-tools.t
@@ -1363,6 +1363,33 @@
   (branch merge, don't forget to commit)
   $ rm -f 'printargs_merge_tool'
 
+Same test with experimental.mergetempdirprefix set:
+
+  $ beforemerge
+  [merge-tools]
+  false.whatever=
+  true.priority=1
+  true.executable=cat
+  # hg update -C 1
+  $ cat < printargs_merge_tool
+  > while test \$# -gt 0; do echo arg: \"\$1\"; shift; done
+  > EOF
+  $ hg --config experimental.mergetempdirprefix=$TESTTMP/hgmerge. \
+  >--config merge-tools.true.executable='sh' \
+  >--config merge-tools.true.args='./printargs_merge_tool ll:$labellocal 
lo: $labelother lb:$labelbase": "$base' \
+  >--config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
+  >--config ui.mergemarkertemplate='uitmpl {rev}' \
+  >--config ui.mergemarkers=detailed \
+  >merge -r 2
+  merging f
+  arg: "ll:working copy"
+  arg: "lo:"
+  arg: "merge rev"
+  arg: "lb:base: $TESTTMP/hgmerge.*/f~base" (glob)
+  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ rm -f 'printargs_merge_tool'
+
 Merge using a tool that supports labellocal, labelother, and labelbase, 
checking
 that they're quoted properly as well. This is using 'detailed' mergemarkers,
 even though ui.mergemarkers is 'basic', and using the tool's
@@ -1562,6 +1589,21 @@
   0 files updated, 1 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
 
+Verify naming of temporary files and that extension is preserved
+(experimental.mergetempdirprefix version):
+
+  $ hg update -q -C 1
+  $ hg mv f f.txt
+  $ hg ci -qm "f.txt"
+  $ hg update -q -C 2
+  $ hg merge -y -r tip --tool echo \
+  >--config merge-tools.echo.args='$base $local $other $output' \
+  >--config experimental.mergetempdirprefix=$TESTTMP/hgmerge.
+  merging f and f.txt to f.txt
+  $TESTTMP/hgmerge.*/f~base $TESTTMP/f.txt.orig $TESTTMP/hgmerge.*/f~other.txt 
$TESTTMP/f.txt (glob)
+  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+
 Check that debugpicktool examines which merge tool is chosen for
 specified file as expected
 
diff --git a/mercurial/filemerge.py b/mercurial/filemerge.py
--- a/mercurial/filemerge.py
+++ b/mercurial/filemerge.py
@@ -10,6 +10,7 @@
 import contextlib
 import os
 import re
+import shutil
 import tempfile
 
 from .i18n import _
@@ -668,12 +669,23 @@
 """Writes out `fco` and `fca` as temporary files, so an external merge
 tool may use them.
 """
+tmproot = None
+tmprootprefix = repo.ui.config('experimental', 'mergetempdirprefix')
+if tmprootprefix:
+tmproot = tempfile.mkdtemp(prefix=tmprootprefix)
+
 def temp(prefix, ctx):
 fullbase, ext = os.path.splitext(ctx.path())
-pre = "%s~%s." % (os.path.basename(fullbase), prefix)
-(fd, name) = tempfile.mkstemp(prefix=pre, suffix=ext)
+pre = "%s~%s" % (os.path.basename(fullbase), prefix)
+if tmproot:
+name = os.path.join(tmproot, pre)
+if ext:
+name += ext
+f = open(name, r"wb")
+else:
+(fd, name) = tempfile.mkstemp(prefix=pre + '.', suffix=ext)
+f = os.fdopen(fd, r"wb")
 data = repo.wwritedata(ctx.path(), ctx.data())
-f = os.fdopen(fd, r"wb")
 f.write(data)
 f.close()
 return name
@@ -683,8 +695,11 @@
 try:
 yield b, c
 finally:
-util.unlink(b)
-util.unlink(c)
+if tmproot:
+shutil.rmtree(tmproot)
+else:
+util.unlink(b)
+util.unlink(c)
 
 def _filemerge(premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=None):
 """perform a 3-way merge in the working directory
diff --git a/mercurial/configitems.py b/mercurial/configitems.py
--- a/mercurial/configitems.py
+++ b/mercurial/configitems.py
@@ -502,6 +502,9 @@
 coreconfigitem('experimental', 'maxdeltachainspan',
 default=-1,
 )
+coreconfigitem('experimental', 'mergetempdirprefix',
+default=None,
+)
 coreconfigitem('experimental', 'mmapindexthreshold',
 default=None,
 )



To: spectral, #hg-reviewers, yuja
Cc: mercurial-devel
___
Mercurial-devel mailing list

D2887: filemerge: move temp file unlinks to _maketempfiles

2018-03-19 Thread spectral (Kyle Lippincott)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG3723b42ff953: filemerge: move temp file unlinks to 
_maketempfiles (authored by spectral, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2887?vs=7085=7118

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

AFFECTED FILES
  mercurial/filemerge.py

CHANGE DETAILS

diff --git a/mercurial/filemerge.py b/mercurial/filemerge.py
--- a/mercurial/filemerge.py
+++ b/mercurial/filemerge.py
@@ -7,6 +7,7 @@
 
 from __future__ import absolute_import
 
+import contextlib
 import os
 import re
 import tempfile
@@ -510,8 +511,8 @@
 return False, 1, None
 unused, unused, unused, back = files
 localpath = _workingpath(repo, fcd)
-basepath, otherpath = _maketempfiles(repo, fco, fca)
-try:
+with _maketempfiles(repo, fco, fca) as temppaths:
+basepath, otherpath = temppaths
 outpath = ""
 mylabel, otherlabel = labels[:2]
 if len(labels) >= 3:
@@ -549,9 +550,6 @@
 r = ui.system(cmd, cwd=repo.root, environ=env, blockedtag='mergetool')
 repo.ui.debug('merge tool returned: %d\n' % r)
 return True, r, False
-finally:
-util.unlink(basepath)
-util.unlink(otherpath)
 
 def _formatconflictmarker(ctx, template, label, pad):
 """Applies the given template to the ctx, prefixed by the label.
@@ -665,6 +663,7 @@
 # the backup context regardless of where it lives.
 return context.arbitraryfilectx(back, repo=repo)
 
+@contextlib.contextmanager
 def _maketempfiles(repo, fco, fca):
 """Writes out `fco` and `fca` as temporary files, so an external merge
 tool may use them.
@@ -681,8 +680,11 @@
 
 b = temp("base", fca)
 c = temp("other", fco)
-
-return b, c
+try:
+yield b, c
+finally:
+util.unlink(b)
+util.unlink(c)
 
 def _filemerge(premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=None):
 """perform a 3-way merge in the working directory



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


D2889: filemerge: make the 'local' path match the format that 'base' and 'other' use

2018-03-19 Thread yuja (Yuya Nishihara)
yuja requested changes to this revision.
yuja added a comment.
This revision now requires changes to proceed.


  Not reviewed yet, but this can't be applied cleanly. Can you rebase?

REPOSITORY
  rHG Mercurial

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

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


D2879: rebase: on abort, don't strip commits that didn't need rebased (issue5822)

2018-03-19 Thread martinvonz (Martin von Zweigbergk)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG177f3b90335f: rebase: on abort, dont strip commits 
that didnt need rebased (issue5822) (authored by martinvonz, committed by 
).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2879?vs=7076=7117

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

AFFECTED FILES
  hgext/rebase.py
  tests/test-rebase-partial.t

CHANGE DETAILS

diff --git a/tests/test-rebase-partial.t b/tests/test-rebase-partial.t
--- a/tests/test-rebase-partial.t
+++ b/tests/test-rebase-partial.t
@@ -87,13 +87,13 @@
   unresolved conflicts (see hg resolve, then hg rebase --continue)
   [1]
   $ hg rebase --abort
-  saved backup bundle to 
$TESTTMP/abort/.hg/strip-backup/79f6d6ab7b14-cce2340e-backup.hg
   rebase aborted
-BROKEN: C got stripped
   $ hg tglog
-  o  2: ef8c0fe0897b D
+  o  3: 79f6d6ab7b14 C
   |
-  | o  1: 594087dbaf71 B
+  | o  2: ef8c0fe0897b D
+  | |
+  o |  1: 594087dbaf71 B
   |/
   o  0: 426bada5c675 A
   
diff --git a/hgext/rebase.py b/hgext/rebase.py
--- a/hgext/rebase.py
+++ b/hgext/rebase.py
@@ -1544,7 +1544,8 @@
 # If the first commits in the rebased set get skipped during the 
rebase,
 # their values within the state mapping will be the dest rev id. The
 # rebased list must must not contain the dest rev (issue4896)
-rebased = [s for r, s in state.items() if s >= 0 and s != destmap[r]]
+rebased = [s for r, s in state.items()
+   if s >= 0 and s != r and s != destmap[r]]
 immutable = [d for d in rebased if not repo[d].mutable()]
 cleanup = True
 if immutable:



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


D2878: rebase: avoid defining two lists with the same contents

2018-03-19 Thread martinvonz (Martin von Zweigbergk)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGa046d6890761: rebase: avoid defining two lists with the 
same contents (authored by martinvonz, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2878?vs=7075=7116

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

AFFECTED FILES
  hgext/rebase.py

CHANGE DETAILS

diff --git a/hgext/rebase.py b/hgext/rebase.py
--- a/hgext/rebase.py
+++ b/hgext/rebase.py
@@ -1543,28 +1543,26 @@
 try:
 # If the first commits in the rebased set get skipped during the 
rebase,
 # their values within the state mapping will be the dest rev id. The
-# dstates list must must not contain the dest rev (issue4896)
-dstates = [s for r, s in state.items() if s >= 0 and s != destmap[r]]
-immutable = [d for d in dstates if not repo[d].mutable()]
+# rebased list must must not contain the dest rev (issue4896)
+rebased = [s for r, s in state.items() if s >= 0 and s != destmap[r]]
+immutable = [d for d in rebased if not repo[d].mutable()]
 cleanup = True
 if immutable:
 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
 % ', '.join(str(repo[r]) for r in immutable),
 hint=_("see 'hg help phases' for details"))
 cleanup = False
 
 descendants = set()
-if dstates:
-descendants = set(repo.changelog.descendants(dstates))
-if descendants - set(dstates):
+if rebased:
+descendants = set(repo.changelog.descendants(rebased))
+if descendants - set(rebased):
 repo.ui.warn(_("warning: new changesets detected on destination "
"branch, can't strip\n"))
 cleanup = False
 
 if cleanup:
 shouldupdate = False
-rebased = [s for r, s in state.items()
-   if s >= 0 and s != destmap[r]]
 if rebased:
 strippoints = [
 c.node() for c in repo.set('roots(%ld)', rebased)]



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


D2877: tests: demonstrate aborted rebase strips commits that didn't need rebasing

2018-03-19 Thread martinvonz (Martin von Zweigbergk)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGb9a6ee2066f9: tests: demonstrate aborted rebase strips 
commits that didnt need rebasing (authored by martinvonz, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2877?vs=7074=7115

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

AFFECTED FILES
  tests/test-rebase-partial.t

CHANGE DETAILS

diff --git a/tests/test-rebase-partial.t b/tests/test-rebase-partial.t
--- a/tests/test-rebase-partial.t
+++ b/tests/test-rebase-partial.t
@@ -69,6 +69,36 @@
   |/
   o  0: 426bada5c675 A
   
+Abort doesn't lose the commits that were already in the right place
+
+  $ hg init abort
+  $ cd abort
+  $ hg debugdrawdag < C
+  > |
+  > B D  # B/file = B
+  > |/   # D/file = D
+  > A
+  > EOF
+  $ hg rebase -r C+D -d B
+  rebasing 2:ef8c0fe0897b "D" (D)
+  merging file
+  warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
+  unresolved conflicts (see hg resolve, then hg rebase --continue)
+  [1]
+  $ hg rebase --abort
+  saved backup bundle to 
$TESTTMP/abort/.hg/strip-backup/79f6d6ab7b14-cce2340e-backup.hg
+  rebase aborted
+BROKEN: C got stripped
+  $ hg tglog
+  o  2: ef8c0fe0897b D
+  |
+  | o  1: 594087dbaf71 B
+  |/
+  o  0: 426bada5c675 A
+  
+  $ cd ..
+
 Rebase with "holes". The commits after the hole should end up on the parent of
 the hole (B below), not on top of the destination (A).
 



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


D2877: tests: demonstrate aborted rebase strips commits that didn't need rebasing

2018-03-19 Thread yuja (Yuya Nishihara)
yuja added a comment.


  Queued for stable, thanks.

REPOSITORY
  rHG Mercurial

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

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


D2815: hgweb: extract entries() to standalone function

2018-03-19 Thread yuja (Yuya Nishihara)
yuja added a comment.


  > `entries` can be a list or a function returning a generator.
  
  I'm going to add a wrapper class for a generator of mappings, so this can be
  addressed later.

REPOSITORY
  rHG Mercurial

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

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


D2895: py3: use "%d" % int instead of str(int)

2018-03-19 Thread pulkit (Pulkit Goyal)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG1bf555cb680e: py3: use %d % int instead of 
str(int) (authored by pulkit, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2895?vs=7100=7113

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

AFFECTED FILES
  mercurial/commands.py

CHANGE DETAILS

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -1133,7 +1133,7 @@
 fm.startitem()
 fm.write('branch', '%s', tag, label=label)
 rev = ctx.rev()
-padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
+padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
 fmt = ' ' * padsize + ' %d:%s'
 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
  label='log.changeset changeset.%s' % ctx.phasestr())



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


D2893: py3: make tests/test-log-linerange.t work on Python 3

2018-03-19 Thread pulkit (Pulkit Goyal)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG0baf0e3ee569: py3: make tests/test-log-linerange.t work on 
Python 3 (authored by pulkit, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2893?vs=7098=7111

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

AFFECTED FILES
  tests/test-log-linerange.t

CHANGE DETAILS

diff --git a/tests/test-log-linerange.t b/tests/test-log-linerange.t
--- a/tests/test-log-linerange.t
+++ b/tests/test-log-linerange.t
@@ -871,7 +871,7 @@
 Binary files work but without diff hunks filtering.
 (Checking w/ and w/o diff.git option.)
 
-  >>> open('binary', 'wb').write('this\nis\na\nbinary\0')
+  >>> open('binary', 'wb').write(b'this\nis\na\nbinary\0') and None
   $ hg add binary
   $ hg ci -m 'add a binary file' --quiet
   $ hg log -f -L binary,1:2 -p



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


D2894: py3: use pycompat.bytestr() in dirstate.py

2018-03-19 Thread pulkit (Pulkit Goyal)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGd5d42c170f4d: py3: use pycompat.bytestr() in dirstate.py 
(authored by pulkit, committed by ).

CHANGED PRIOR TO COMMIT
  https://phab.mercurial-scm.org/D2894?vs=7099=7112#toc

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2894?vs=7099=7112

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

AFFECTED FILES
  mercurial/dirstate.py

CHANGE DETAILS

diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -371,15 +371,17 @@
 if state == 'a' or oldstate == 'r':
 scmutil.checkfilename(f)
 if self._map.hastrackeddir(f):
-raise error.Abort(_('directory %r already in dirstate') % f)
+raise error.Abort(_('directory %r already in dirstate') %
+  pycompat.bytestr(f))
 # shadows
 for d in util.finddirs(f):
 if self._map.hastrackeddir(d):
 break
 entry = self._map.get(d)
 if entry is not None and entry[0] != 'r':
 raise error.Abort(
-_('file %r in dirstate clashes with %r') % (d, f))
+_('file %r in dirstate clashes with %r') %
+(pycompat.bytestr(d), pycompat.bytestr(f)))
 self._dirty = True
 self._updatedfiles.add(f)
 self._map.addfile(f, oldstate, state, mode, size, mtime)



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


D2676: tests: stop over-specifying tempfile name

2018-03-19 Thread durin42 (Augie Fackler)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGccc2d5f10421: tests: stop over-specifying tempfile name 
(authored by durin42, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2676?vs=7096=7109

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

AFFECTED FILES
  contrib/python3-whitelist
  tests/test-merge-tools.t

CHANGE DETAILS

diff --git a/tests/test-merge-tools.t b/tests/test-merge-tools.t
--- a/tests/test-merge-tools.t
+++ b/tests/test-merge-tools.t
@@ -1558,7 +1558,7 @@
   $ hg update -q -C 2
   $ hg merge -y -r tip --tool echo --config merge-tools.echo.args='$base 
$local $other $output'
   merging f and f.txt to f.txt
-  */f~base.?? $TESTTMP/f.txt.orig */f~other.??.txt $TESTTMP/f.txt 
(glob)
+  */f~base.* $TESTTMP/f.txt.orig */f~other.*.txt $TESTTMP/f.txt (glob)
   0 files updated, 1 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
 
diff --git a/contrib/python3-whitelist b/contrib/python3-whitelist
--- a/contrib/python3-whitelist
+++ b/contrib/python3-whitelist
@@ -217,6 +217,7 @@
 test-merge-revert2.t
 test-merge-subrepos.t
 test-merge-symlinks.t
+test-merge-tools.t
 test-merge-types.t
 test-merge1.t
 test-merge10.t



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


D2892: py3: use print as a function in tests/test-narrow-clone-non-narrow-server.t

2018-03-19 Thread pulkit (Pulkit Goyal)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGf4c7dc24e889: py3: use print as a function in 
tests/test-narrow-clone-non-narrow-server.t (authored by pulkit, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2892?vs=7097=7110

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

AFFECTED FILES
  tests/test-narrow-clone-non-narrow-server.t

CHANGE DETAILS

diff --git a/tests/test-narrow-clone-non-narrow-server.t 
b/tests/test-narrow-clone-non-narrow-server.t
--- a/tests/test-narrow-clone-non-narrow-server.t
+++ b/tests/test-narrow-clone-non-narrow-server.t
@@ -19,7 +19,7 @@
 
 Verify that narrow is advertised in the bundle2 capabilities:
   $ echo hello | hg -R . serve --stdio | \
-  >   $PYTHON -c "import sys, urllib; print 
urllib.unquote_plus(list(sys.stdin)[1])" | grep narrow
+  >   $PYTHON -c "from __future__ import print_function; import sys, urllib; 
print(urllib.unquote_plus(list(sys.stdin)[1]))" | grep narrow
   narrow=v0
 
   $ cd ..



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


D2881: hgweb: refactor multirequest to be a dict of lists

2018-03-19 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG44467a4d472f: hgweb: refactor multirequest to be a dict of 
lists (authored by indygreg, committed by ).

CHANGED PRIOR TO COMMIT
  https://phab.mercurial-scm.org/D2881?vs=7078=7114#toc

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2881?vs=7078=7114

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

AFFECTED FILES
  mercurial/hgweb/request.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -28,39 +28,22 @@
 This is inspired by WebOb's class of the same name.
 """
 def __init__(self):
-# Stores (key, value) 2-tuples. This isn't the most efficient. But we
-# don't rely on parameters that much, so it shouldn't be a perf issue.
-# we can always add dict for fast lookups.
-self._items = []
+self._items = {}
 
 def __getitem__(self, key):
 """Returns the last set value for a key."""
-for k, v in reversed(self._items):
-if k == key:
-return v
-
-raise KeyError(key)
+return self._items[key][-1]
 
 def __setitem__(self, key, value):
 """Replace a values for a key with a new value."""
-try:
-del self[key]
-except KeyError:
-pass
-
-self._items.append((key, value))
+self._items[key] = [value]
 
 def __delitem__(self, key):
 """Delete all values for a key."""
-oldlen = len(self._items)
-
-self._items[:] = [(k, v) for k, v in self._items if k != key]
-
-if oldlen == len(self._items):
-raise KeyError(key)
+del self._items[key]
 
 def __contains__(self, key):
-return any(k == key for k, v in self._items)
+return key in self._items
 
 def __len__(self):
 return len(self._items)
@@ -73,36 +56,26 @@
 
 def add(self, key, value):
 """Add a new value for a key. Does not replace existing values."""
-self._items.append((key, value))
+self._items.setdefault(key, []).append(value)
 
 def getall(self, key):
 """Obtains all values for a key."""
-return [v for k, v in self._items if k == key]
+return self._items.get(key, [])
 
 def getone(self, key):
 """Obtain a single value for a key.
 
 Raises KeyError if key not defined or it has multiple values set.
 """
-vals = self.getall(key)
-
-if not vals:
-raise KeyError(key)
+vals = self._items[key]
 
 if len(vals) > 1:
 raise KeyError('multiple values for %r' % key)
 
 return vals[0]
 
 def asdictoflists(self):
-d = {}
-for k, v in self._items:
-if k in d:
-d[k].append(v)
-else:
-d[k] = [v]
-
-return d
+return {k: list(v) for k, v in self._items.iteritems()}
 
 @attr.s(frozen=True)
 class parsedrequest(object):



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


[PATCH 2 of 2] templatefuncs: do not stringify result of if*() expression

2018-03-19 Thread Yuya Nishihara
# HG changeset patch
# User Yuya Nishihara 
# Date 1521465040 -32400
#  Mon Mar 19 22:10:40 2018 +0900
# Node ID 3e0eb7c1020aa020f5781bee23cad4f78a8f03ef
# Parent  cb8ab2dddf79e4ff203e22b4130ceca5b88bb14b
templatefuncs: do not stringify result of if*() expression

Returning a generator means that the result is a byte string. I can't find
any reason to make the "if" condition lazy since it is evaluated anyway
when {if()} has to be evaluated. So let's simply make if*() return an input
expression unmodified.

diff --git a/mercurial/templatefuncs.py b/mercurial/templatefuncs.py
--- a/mercurial/templatefuncs.py
+++ b/mercurial/templatefuncs.py
@@ -248,9 +248,9 @@ def if_(context, mapping, args):
 
 test = evalboolean(context, mapping, args[0])
 if test:
-yield evalrawexp(context, mapping, args[1])
+return evalrawexp(context, mapping, args[1])
 elif len(args) == 3:
-yield evalrawexp(context, mapping, args[2])
+return evalrawexp(context, mapping, args[2])
 
 @templatefunc('ifcontains(needle, haystack, then[, else])')
 def ifcontains(context, mapping, args):
@@ -269,9 +269,9 @@ def ifcontains(context, mapping, args):
 found = False
 
 if found:
-yield evalrawexp(context, mapping, args[2])
+return evalrawexp(context, mapping, args[2])
 elif len(args) == 4:
-yield evalrawexp(context, mapping, args[3])
+return evalrawexp(context, mapping, args[3])
 
 @templatefunc('ifeq(expr1, expr2, then[, else])')
 def ifeq(context, mapping, args):
@@ -284,9 +284,9 @@ def ifeq(context, mapping, args):
 test = evalstring(context, mapping, args[0])
 match = evalstring(context, mapping, args[1])
 if test == match:
-yield evalrawexp(context, mapping, args[2])
+return evalrawexp(context, mapping, args[2])
 elif len(args) == 4:
-yield evalrawexp(context, mapping, args[3])
+return evalrawexp(context, mapping, args[3])
 
 @templatefunc('join(list, sep)')
 def join(context, mapping, args):
diff --git a/tests/test-command-template.t b/tests/test-command-template.t
--- a/tests/test-command-template.t
+++ b/tests/test-command-template.t
@@ -3242,6 +3242,35 @@ Test min/max of integers
   $ hg log -R latesttag -l1 -T '{max(revset("9:10"))}\n'
   10
 
+Test min/max of if() result
+
+  $ cd latesttag
+  $ hg log -l1 -T '{min(if(true, revset("9:10"), ""))}\n'
+  9
+  $ hg log -l1 -T '{max(if(false, "", revset("9:10")))}\n'
+  10
+  $ hg log -l1 -T '{min(ifcontains("a", "aa", revset("9:10"), ""))}\n'
+  9
+  $ hg log -l1 -T '{max(ifcontains("a", "bb", "", revset("9:10")))}\n'
+  10
+  $ hg log -l1 -T '{min(ifeq(0, 0, revset("9:10"), ""))}\n'
+  9
+  $ hg log -l1 -T '{max(ifeq(0, 1, "", revset("9:10")))}\n'
+  10
+  $ cd ..
+
+Test laziness of if() then/else clause
+
+  $ hg debugtemplate '{count(0)}'
+  abort: incompatible use of template filter 'count'
+  [255]
+  $ hg debugtemplate '{if(true, "", count(0))}'
+  $ hg debugtemplate '{if(false, count(0), "")}'
+  $ hg debugtemplate '{ifcontains("a", "aa", "", count(0))}'
+  $ hg debugtemplate '{ifcontains("a", "bb", count(0), "")}'
+  $ hg debugtemplate '{ifeq(0, 0, "", count(0))}'
+  $ hg debugtemplate '{ifeq(0, 1, count(0), "")}'
+
 Test dot operator precedence:
 
   $ hg debugtemplate -R latesttag -r0 -v '{manifest.node|short}\n'
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 1 of 2] templater: add brief doc about internal data types

2018-03-19 Thread Yuya Nishihara
# HG changeset patch
# User Yuya Nishihara 
# Date 1521464102 -32400
#  Mon Mar 19 21:55:02 2018 +0900
# Node ID cb8ab2dddf79e4ff203e22b4130ceca5b88bb14b
# Parent  cc058f8ed3d6ea8ee9d9a49263ea133c5adff587
templater: add brief doc about internal data types

Hopefully this will help reviewing upcoming my series. Start small.

diff --git a/mercurial/templater.py b/mercurial/templater.py
--- a/mercurial/templater.py
+++ b/mercurial/templater.py
@@ -5,6 +5,47 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+"""Slightly complicated template engine for commands and hgweb
+
+This module provides low-level interface to the template engine. See the
+formatter and cmdutil modules if you are looking for high-level functions
+such as ``cmdutil.rendertemplate(ctx, tmpl)``.
+
+Internal Data Types
+---
+
+Template keywords and functions take a dictionary of current symbols and
+resources (a "mapping") and return result. Inputs and outputs must be one
+of the following data types:
+
+bytes
+a byte string, which is generally a human-readable text in local encoding.
+
+generator
+a lazily-evaluated byte string, which is a possibly nested generator of
+values of any printable types, and  will be folded by ``stringify()``
+or ``flatten()``.
+
+BUG: hgweb overloads this type for mappings (i.e. some hgweb keywords
+returns a generator of dicts.)
+
+None
+sometimes represents an empty value, which can be stringified to ''.
+
+True, False, int, float
+can be stringified as such.
+
+date tuple
+a (unixtime, offset) tuple, which produces no meaningful output by itself.
+
+hybrid
+represents a list/dict of printable values, which can also be converted
+to mappings by % operator.
+
+mappable
+represents a scalar printable value, also supports % operator.
+"""
+
 from __future__ import absolute_import, print_function
 
 import os
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 3 of 3 V2] hgweb: evaluate the "default" value as template

2018-03-19 Thread Yuya Nishihara
# HG changeset patch
# User Yuya Nishihara 
# Date 1459660607 -32400
#  Sun Apr 03 14:16:47 2016 +0900
# Node ID cc058f8ed3d6ea8ee9d9a49263ea133c5adff587
# Parent  faaed935789fd2b36121a5a5ec407d1cb694d48b
hgweb: evaluate the "default" value as template

Strictly speaking, everything in the map file is a template. So let's not
take out an unparsed template string.

diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -386,7 +386,7 @@ class hgweb(object):
 self.check_perm(rctx, req, None)
 
 if cmd == '':
-req.qsparams['cmd'] = rctx.tmpl.cache['default']
+req.qsparams['cmd'] = rctx.tmpl.render('default', {})
 cmd = req.qsparams['cmd']
 
 # Don't enable caching if using a CSP nonce because then it 
wouldn't
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 1 of 3 V2] highlight: peek Content-Type header set by hgweb

2018-03-19 Thread Yuya Nishihara
# HG changeset patch
# User Yuya Nishihara 
# Date 1521461363 -32400
#  Mon Mar 19 21:09:23 2018 +0900
# Node ID 75c311c2a72be114b791e0a6d4dd0d1cef20fa7b
# Parent  b6a4881cec1937a8d9cd2e9bbbdf5ca31cfa73dd
highlight: peek Content-Type header set by hgweb

There should be no need to re-render the mimetype template since it's set
before dispatching webcommands.

diff --git a/hgext/highlight/__init__.py b/hgext/highlight/__init__.py
--- a/hgext/highlight/__init__.py
+++ b/hgext/highlight/__init__.py
@@ -35,7 +35,6 @@ from mercurial.hgweb import (
 )
 
 from mercurial import (
-encoding,
 extensions,
 fileset,
 )
@@ -59,7 +58,7 @@ def pygmentize(web, field, fctx, tmpl):
 guessfilenameonly=filenameonly)
 
 def filerevision_highlight(orig, web, fctx):
-mt = ''.join(web.tmpl('mimetype', encoding=encoding.encoding))
+mt = web.res.headers['Content-Type']
 # only pygmentize for mimetype containing 'html' so we both match
 # 'text/html' and possibly 'application/xhtml+xml' in the future
 # so that we don't have to touch the extension when the mimetype
@@ -73,7 +72,7 @@ def filerevision_highlight(orig, web, fc
 return orig(web, fctx)
 
 def annotate_highlight(orig, web):
-mt = ''.join(web.tmpl('mimetype', encoding=encoding.encoding))
+mt = web.res.headers['Content-Type']
 if 'html' in mt:
 fctx = webutil.filectx(web.repo, web.req)
 pygmentize(web, 'annotateline', fctx, web.tmpl)
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 2 of 3 V2] templater: use named function to expand template against mapping dict (API)

2018-03-19 Thread Yuya Nishihara
# HG changeset patch
# User Yuya Nishihara 
# Date 1521203972 -32400
#  Fri Mar 16 21:39:32 2018 +0900
# Node ID faaed935789fd2b36121a5a5ec407d1cb694d48b
# Parent  75c311c2a72be114b791e0a6d4dd0d1cef20fa7b
templater: use named function to expand template against mapping dict (API)

And replace __call__(t, **mapping) in favor of generate(t, mapping). I prefer
a named function here since the templater isn't a simple function-like object.

.. api::

   The templater is no longer callable. Use ``templater.generate(t, mapping)``
   instead of ``templater(t, **pycompat.strkwargs(mapping))``.

diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -198,7 +198,8 @@ class requestcontext(object):
 
 def sendtemplate(self, name, **kwargs):
 """Helper function to send a response generated from a template."""
-self.res.setbodygen(self.tmpl(name, **kwargs))
+kwargs = pycompat.byteskwargs(kwargs)
+self.res.setbodygen(self.tmpl.generate(name, kwargs))
 return self.res.sendresponse()
 
 class hgweb(object):
diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -452,12 +452,12 @@ class hgwebdir(object):
 
 # prefixes not found
 res.status = '404 Not Found'
-res.setbodygen(tmpl('notfound', repo=virtual))
+res.setbodygen(tmpl.generate('notfound', {'repo': virtual}))
 return res.sendresponse()
 
 except ErrorResponse as e:
 res.status = statusmessage(e.code, pycompat.bytestr(e))
-res.setbodygen(tmpl('error', error=e.message or ''))
+res.setbodygen(tmpl.generate('error', {'error': e.message or ''}))
 return res.sendresponse()
 finally:
 tmpl = None
@@ -485,15 +485,15 @@ class hgwebdir(object):
self.stripecount, sortcolumn=sortcolumn,
descending=descending, subdir=subdir)
 
-res.setbodygen(tmpl(
-'index',
-entries=entries,
-subdir=subdir,
-pathdef=hgweb_mod.makebreadcrumb('/' + subdir, self.prefix),
-sortcolumn=sortcolumn,
-descending=descending,
-**dict(sort)))
-
+mapping = {
+'entries': entries,
+'subdir': subdir,
+'pathdef': hgweb_mod.makebreadcrumb('/' + subdir, self.prefix),
+'sortcolumn': sortcolumn,
+'descending': descending,
+}
+mapping.update(sort)
+res.setbodygen(tmpl.generate('index', mapping))
 return res.sendresponse()
 
 def templater(self, req, nonce):
diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -294,12 +294,13 @@ def _search(web):
 files = webutil.listfilediffs(web.tmpl, ctx.files(), n,
   web.maxfiles)
 
-yield web.tmpl(
-'searchentry',
-parity=next(parity),
-changelogtag=showtags,
-files=files,
-**pycompat.strkwargs(webutil.commonentry(web.repo, ctx)))
+lm = webutil.commonentry(web.repo, ctx)
+lm.update({
+'parity': next(parity),
+'changelogtag': showtags,
+'files': files,
+})
+yield web.tmpl.generate('searchentry', lm)
 
 if count >= revcount:
 break
@@ -719,12 +720,12 @@ def summary(web):
 if count > 10: # limit to 10 tags
 break
 
-yield web.tmpl(
-'tagentry',
-parity=next(parity),
-tag=k,
-node=hex(n),
-date=web.repo[n].date())
+yield web.tmpl.generate('tagentry', {
+'parity': next(parity),
+'tag': k,
+'node': hex(n),
+'date': web.repo[n].date(),
+})
 
 def bookmarks(**map):
 parity = paritygen(web.stripecount)
@@ -745,11 +746,9 @@ def summary(web):
 revs = web.repo.changelog.revs(start, end - 1)
 for i in revs:
 ctx = web.repo[i]
-
-l.append(web.tmpl(
-'shortlogentry',
-parity=next(parity),
-**pycompat.strkwargs(webutil.commonentry(web.repo, ctx
+lm = webutil.commonentry(web.repo, ctx)
+lm['parity'] = next(parity)
+l.append(web.tmpl.generate('shortlogentry', lm))
 
 for entry in reversed(l):
 yield entry
diff --git a/mercurial/hgweb/webutil.py b/mercurial/hgweb/webutil.py
--- a/mercurial/hgweb/webutil.py
+++ 

D2881: hgweb: refactor multirequest to be a dict of lists

2018-03-19 Thread yuja (Yuya Nishihara)
yuja added a comment.


  Queued, thanks.

INLINE COMMENTS

> request.py:31
>  def __init__(self):
>  # Stores (key, value) 2-tuples. This isn't the most efficient. But we
>  # don't rely on parameters that much, so it shouldn't be a perf 
> issue.

Deleted this comment in flight.

> request.py:52
>  def __len__(self):
>  return len(self._items)
>  

The behavior of `len()` changed, but I don't think that matters. The new
behavior should be fine since multidict isn't iterable.

REPOSITORY
  rHG Mercurial

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

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


Re: [PATCH 4 of 5] templater: use named function to named template against mapping dict (API)

2018-03-19 Thread Yuya Nishihara
On Mon, 19 Mar 2018 05:13:05 +, Martin von Zweigbergk wrote:
> On Sat, Mar 17, 2018 at 9:39 PM Yuya Nishihara  wrote:
> 
> > # HG changeset patch
> > # User Yuya Nishihara 
> > # Date 1521203972 -32400
> > #  Fri Mar 16 21:39:32 2018 +0900
> > # Node ID 73de63e4c72042110b744e0838556421c7acaba8
> > # Parent  38286983261c9f01bb71cd7994033cd3f96e8d38
> > templater: use named function to named template against mapping dict (API)
> >
> > And replace __call__(t, **mapping) in favor of generate(t, mapping). I
> > prefer
> > a named function here since the templater isn't a simple function-like
> > object.
> >
> > .. api::
> >
> >The templater is no longer callable. Use ``templater.generate(t,
> > mapping)``
> >instead of ``templater(t, **pycompat.strkwargs(mapping))``.
> >
> 
> This patch seems to break test-highlight.t. There seems to be a templater
> that's used as a callable on hgext/highlight/__init__.py:62

Oops, new version coming. Thanks for spotting it.
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


Re: [PATCH V2] wireproto: explicitly flush stdio to prevent stalls on Windows

2018-03-19 Thread Yuya Nishihara
On Sun, 18 Mar 2018 15:48:12 -0400, Matt Harbison wrote:
> On Sun, 11 Mar 2018 15:51:24 -0400, Matt Harbison   
> wrote:
> 
> > # HG changeset patch
> > # User Matt Harbison 
> > # Date 1520744281 18000
> > #  Sat Mar 10 23:58:01 2018 -0500
> > # Node ID 6c6b10b5d5bd617a01942974928b8b9c59eddb4a
> > # Parent  963b4223d14fa419df2a82fbe47cd55075707b6a
> > wireproto: explicitly flush stdio to prevent stalls on Windows
> 
> Gentle ping on this.  While the hang in test-ssh-proto-unbundle.t with  
> D2720 won't matter unless that is accepted, this fixes the unstable output  
> in test-ssh-proto.t as well.

Queued, thanks. I have no idea how this can solve the hang, but flushing
debug logs should be good.
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2855: graft: add a version number to the state file formats

2018-03-19 Thread pulkit (Pulkit Goyal)
pulkit updated this revision to Diff 7108.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2855?vs=7024=7108

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

AFFECTED FILES
  mercurial/commands.py
  mercurial/state.py

CHANGE DETAILS

diff --git a/mercurial/state.py b/mercurial/state.py
--- a/mercurial/state.py
+++ b/mercurial/state.py
@@ -117,4 +117,4 @@
 @readoldstatefile('graftstate')
 def oldgraftstate(fp):
 nodes = fp.read().splitlines()
-return {'nodes': nodes}
+return {'version': 0, 'nodes': nodes}
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -2183,8 +2183,13 @@
 # read in unfinished revisions
 if cmdstate:
 cmdstate.load()
-nodes = cmdstate['nodes']
-revs = [repo[node].rev() for node in nodes]
+if cmdstate['version'] < 2:
+nodes = cmdstate['nodes']
+revs = [repo[node].rev() for node in nodes]
+else:
+# state-file is written by a newer mercurial than what we are
+# using
+raise error.Abort(_("unable to read to read the state file"))
 else:
 cmdutil.wrongtooltocontinue(repo, _('graft'))
 else:
@@ -2311,7 +2316,7 @@
 if stats[3] > 0:
 # write out state for --continue
 nodelines = [repo[rev].hex() for rev in revs[pos:]]
-cmdstate.addopts({'nodes': nodelines})
+cmdstate.addopts({'version': 1, 'nodes': nodelines})
 cmdstate.save()
 extra = ''
 if opts.get('user'):



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


D2595: graft: start using the new state file

2018-03-19 Thread pulkit (Pulkit Goyal)
pulkit updated this revision to Diff 7107.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2595?vs=7023=7107

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

AFFECTED FILES
  mercurial/commands.py

CHANGE DETAILS

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -2175,19 +2175,17 @@
  **pycompat.strkwargs(opts))
 
 cont = False
+cmdstate = statemod.cmdstate(repo, 'graftstate')
 if opts.get('continue'):
 cont = True
 if revs:
 raise error.Abort(_("can't specify --continue and revisions"))
 # read in unfinished revisions
-try:
-with repo.vfs('graftstate', 'rb') as fp:
-stateopts = statemod.oldgraftstate(fp)
-nodes = stateopts['nodes']
+if cmdstate:
+cmdstate.load()
+nodes = cmdstate['nodes']
 revs = [repo[node].rev() for node in nodes]
-except IOError as inst:
-if inst.errno != errno.ENOENT:
-raise
+else:
 cmdutil.wrongtooltocontinue(repo, _('graft'))
 else:
 if not revs:
@@ -2312,8 +2310,9 @@
 # report any conflicts
 if stats[3] > 0:
 # write out state for --continue
-nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
-repo.vfs.write('graftstate', ''.join(nodelines))
+nodelines = [repo[rev].hex() for rev in revs[pos:]]
+cmdstate.addopts({'nodes': nodelines})
+cmdstate.save()
 extra = ''
 if opts.get('user'):
 extra += ' --user %s' % util.shellquote(opts['user'])



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


D2594: graft: move logic to read current graft state file in state.py

2018-03-19 Thread pulkit (Pulkit Goyal)
pulkit updated this revision to Diff 7106.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2594?vs=7022=7106

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

AFFECTED FILES
  mercurial/commands.py
  mercurial/state.py

CHANGE DETAILS

diff --git a/mercurial/state.py b/mercurial/state.py
--- a/mercurial/state.py
+++ b/mercurial/state.py
@@ -113,3 +113,8 @@
 oldstatefilefns[path] = func
 return func
 return dec
+
+@readoldstatefile('graftstate')
+def oldgraftstate(fp):
+nodes = fp.read().splitlines()
+return {'nodes': nodes}
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -54,6 +54,7 @@
 rewriteutil,
 scmutil,
 server,
+state as statemod,
 streamclone,
 tags as tagsmod,
 templatekw,
@@ -2180,7 +2181,9 @@
 raise error.Abort(_("can't specify --continue and revisions"))
 # read in unfinished revisions
 try:
-nodes = repo.vfs.read('graftstate').splitlines()
+with repo.vfs('graftstate', 'rb') as fp:
+stateopts = statemod.oldgraftstate(fp)
+nodes = stateopts['nodes']
 revs = [repo[node].rev() for node in nodes]
 except IOError as inst:
 if inst.errno != errno.ENOENT:



To: pulkit, #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] test-lfs: glob over some output changes

2018-03-19 Thread Pulkit Goyal
On Mon, Mar 19, 2018 at 1:19 AM, Matt Harbison  wrote:
> # HG changeset patch
> # User Matt Harbison 
> # Date 1521401569 14400
> #  Sun Mar 18 15:32:49 2018 -0400
> # Node ID 2fc97972bc88809f86bbd91fbfe8582ab59c3ef7
> # Parent  317382151ac38e5a265d303c278f45bc022fc95b
> test-lfs: glob over some output changes
>
> These changes started with 2090044a288d.  They aren't important to LFS 
> testing.

Queued this. Many thanks!
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2874: remotenames: synchronise remotenames after push also

2018-03-19 Thread pulkit (Pulkit Goyal)
pulkit updated this revision to Diff 7104.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2874?vs=7062=7104

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

AFFECTED FILES
  mercurial/exchange.py
  tests/test-logexchange.t

CHANGE DETAILS

diff --git a/tests/test-logexchange.t b/tests/test-logexchange.t
--- a/tests/test-logexchange.t
+++ b/tests/test-logexchange.t
@@ -457,20 +457,21 @@
  bar   6:87d6d6676308
  foo   9:aa6a885086c0
 
-XXX: remotebookmarks should have been updated after this push
   $ hg log -G
   @  changeset:   9:aa6a885086c0
   |  branch:  wat
   |  tag: tip
+  |  remote bookmark:  default/foo
+  |  hoistedname:  foo
+  |  remote branch:  default/wat
   |  user:test
   |  date:Thu Jan 01 00:00:00 1970 +
   |  summary: added foobar
   |
   o  changeset:   8:3e1487808078
   |  branch:  wat
   |  bookmark:foo
   |  remote branch:  $TESTTMP/server2/wat
-  |  remote branch:  default/wat
   |  parent:  4:aa98ab95a928
   |  user:test
   |  date:Thu Jan 01 00:00:00 1970 +
@@ -503,8 +504,6 @@
   |
   o  changeset:   3:62615734edd5
   |  remote bookmark:  $TESTTMP/server2/foo
-  |  remote bookmark:  default/foo
-  |  hoistedname:  foo
   |  user:test
   |  date:Thu Jan 01 00:00:00 1970 +
   |  summary: Added d
diff --git a/mercurial/exchange.py b/mercurial/exchange.py
--- a/mercurial/exchange.py
+++ b/mercurial/exchange.py
@@ -484,6 +484,9 @@
 _pushobsolete(pushop)
 _pushbookmark(pushop)
 
+if repo.ui.configbool('experimental', 'remotenames'):
+logexchange.pullremotenames(repo, remote)
+
 return pushop
 
 # list of steps to perform discovery before push



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


D2875: remotenames: introduce a config option to allow creation of remote bookmarks

2018-03-19 Thread pulkit (Pulkit Goyal)
pulkit updated this revision to Diff 7105.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2875?vs=7063=7105

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

AFFECTED FILES
  hgext/remotenames.py
  tests/test-logexchange.t

CHANGE DETAILS

diff --git a/tests/test-logexchange.t b/tests/test-logexchange.t
--- a/tests/test-logexchange.t
+++ b/tests/test-logexchange.t
@@ -523,3 +523,57 @@
  date:Thu Jan 01 00:00:00 1970 +
  summary: Added a
   
+Testing the remotenames.createbookmark config option
+
+  $ hg bookmarks -R ../server2/
+ $TESTTMP/server/bar 6:87d6d6676308
+ $TESTTMP/server/foo 3:62615734edd5
+ bar   6:87d6d6676308
+ foo   3:62615734edd5
+
+  $ hg push ../server2/ -B nonexistant
+  pushing to ../server2/
+  searching for changes
+  abort: bookmark 'nonexistant' does not exists on remote
+  [255]
+
+  $ hg push ../server2/ -r . -B nonexistant --config 
remotenames.createremotebookmark=True
+  pushing to ../server2/
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  exporting bookmark nonexistant
+
+  $ hg bookmarks -R ../server2/
+ $TESTTMP/server/bar 6:87d6d6676308
+ $TESTTMP/server/foo 3:62615734edd5
+ bar   6:87d6d6676308
+ foo   3:62615734edd5
+ nonexistant   9:aa6a885086c0
+
+  $ hg log -R ../server2/ -r tip
+  changeset:   9:aa6a885086c0
+  branch:  wat
+  bookmark:nonexistant
+  tag: tip
+  user:test
+  date:Thu Jan 01 00:00:00 1970 +
+  summary: added foobar
+  
+Check synchornising of remotenames after push
+
+  $ hg log -G -r .
+  @  changeset:   9:aa6a885086c0
+  |  branch:  wat
+  ~  tag: tip
+ remote bookmark:  $TESTTMP/server2/nonexistant
+ remote bookmark:  default/foo
+ hoistedname:  foo
+ remote branch:  $TESTTMP/server2/wat
+ remote branch:  default/wat
+ user:test
+ date:Thu Jan 01 00:00:00 1970 +
+ summary: added foobar
+  
diff --git a/hgext/remotenames.py b/hgext/remotenames.py
--- a/hgext/remotenames.py
+++ b/hgext/remotenames.py
@@ -29,6 +29,10 @@
   the server. Errors if bookmark does not exists on the server. If multiple
   bookmarks are specified using `-B` flag, fallbacks to default behavior.
   (default: False)
+
+remotenames.createremotebookmark
+  Boolean value indicating whether a creating a new bookmark on the server 
using
+  remotenames.pushtobookmark config is allowed or not. (default: False)
 """
 
 from __future__ import absolute_import
@@ -83,6 +87,9 @@
 configitem('remotenames', 'pushtobookmark',
 default=False,
 )
+configitem('remotenames', 'createremotebookmark',
+default=False,
+)
 
 def expushdiscoverybookmarks(pushop):
 # config not set, fallback to normal push behavior
@@ -102,7 +109,7 @@
 old = ''
 if bookmark in remotemarks:
 old = remotemarks[bookmark]
-else:
+elif not pushop.repo.ui.configbool('remotenames', 'createremotebookmark'):
 msg = _("bookmark '%s' does not exists on remote")
 raise error.Abort(msg % bookmark)
 



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


D2873: remotenames: add functionality to override -B flag of push

2018-03-19 Thread pulkit (Pulkit Goyal)
pulkit updated this revision to Diff 7103.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2873?vs=7061=7103

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

AFFECTED FILES
  hgext/remotenames.py
  tests/test-logexchange.t

CHANGE DETAILS

diff --git a/tests/test-logexchange.t b/tests/test-logexchange.t
--- a/tests/test-logexchange.t
+++ b/tests/test-logexchange.t
@@ -333,3 +333,194 @@
  default/bar   6:87d6d6676308
  default/foo   3:62615734edd5
* foo   8:3e1487808078
+
+Testing the remotenames.pushtobookmark config option to push to bookmark
+
+  $ hg up .
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (leaving bookmark foo)
+  $ echo foo > foobar
+  $ hg add foobar
+  $ hg ci -m "added foobar"
+
+  $ hg log -G
+  @  changeset:   9:aa6a885086c0
+  |  branch:  wat
+  |  tag: tip
+  |  user:test
+  |  date:Thu Jan 01 00:00:00 1970 +
+  |  summary: added foobar
+  |
+  o  changeset:   8:3e1487808078
+  |  branch:  wat
+  |  bookmark:foo
+  |  remote branch:  $TESTTMP/server2/wat
+  |  remote branch:  default/wat
+  |  parent:  4:aa98ab95a928
+  |  user:test
+  |  date:Thu Jan 01 00:00:00 1970 +
+  |  summary: added bar
+  |
+  | o  changeset:   7:ec2426147f0e
+  | |  remote branch:  $TESTTMP/server2/default
+  | |  remote branch:  default/default
+  | |  user:test
+  | |  date:Thu Jan 01 00:00:00 1970 +
+  | |  summary: Added h
+  | |
+  | o  changeset:   6:87d6d6676308
+  | |  remote bookmark:  $TESTTMP/server2/bar
+  | |  remote bookmark:  default/bar
+  | |  hoistedname:  bar
+  | |  user:test
+  | |  date:Thu Jan 01 00:00:00 1970 +
+  | |  summary: Added g
+  | |
+  | o  changeset:   5:825660c69f0c
+  |/   user:test
+  |date:Thu Jan 01 00:00:00 1970 +
+  |summary: Added f
+  |
+  o  changeset:   4:aa98ab95a928
+  |  user:test
+  |  date:Thu Jan 01 00:00:00 1970 +
+  |  summary: Added e
+  |
+  o  changeset:   3:62615734edd5
+  |  remote bookmark:  $TESTTMP/server2/foo
+  |  remote bookmark:  default/foo
+  |  hoistedname:  foo
+  |  user:test
+  |  date:Thu Jan 01 00:00:00 1970 +
+  |  summary: Added d
+  |
+  o  changeset:   2:28ad74487de9
+  |  user:test
+  |  date:Thu Jan 01 00:00:00 1970 +
+  |  summary: Added c
+  |
+  o  changeset:   1:29becc82797a
+  |  user:test
+  |  date:Thu Jan 01 00:00:00 1970 +
+  |  summary: Added b
+  |
+  o  changeset:   0:18d04c59bb5d
+ user:test
+ date:Thu Jan 01 00:00:00 1970 +
+ summary: Added a
+  
+  $ echo '[remotenames]' >> .hg/hgrc
+  $ echo 'pushtobookmark = True' >> .hg/hgrc
+
+Trying to push a non-existing bookmark on the server
+
+  $ hg push ../server/ -B nonexistentbm
+  pushing to ../server/
+  searching for changes
+  abort: bookmark 'nonexistentbm' does not exists on remote
+  [255]
+
+  $ hg push ../server/ -B nonexistinentbm -f
+  pushing to ../server/
+  searching for changes
+  abort: bookmark 'nonexistinentbm' does not exists on remote
+  [255]
+
+Pushing changesets to a bookmark on the remote
+
+  $ hg bookmarks -R ../server/
+ bar   6:87d6d6676308
+ foo   3:62615734edd5
+
+  $ hg push ../server/ -r . -B foo
+  pushing to ../server/
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  updating bookmark foo
+
+After this push, `foo` bookmark on the server is updated whereas the local
+bookmark of that name stayed at its own place
+
+  $ hg log -G -R ../server/ -r tip
+  o  changeset:   9:aa6a885086c0
+  |  branch:  wat
+  ~  bookmark:foo
+ tag: tip
+ user:test
+ date:Thu Jan 01 00:00:00 1970 +
+ summary: added foobar
+  
+  $ hg bookmarks -R ../server/
+ bar   6:87d6d6676308
+ foo   9:aa6a885086c0
+
+XXX: remotebookmarks should have been updated after this push
+  $ hg log -G
+  @  changeset:   9:aa6a885086c0
+  |  branch:  wat
+  |  tag: tip
+  |  user:test
+  |  date:Thu Jan 01 00:00:00 1970 +
+  |  summary: added foobar
+  |
+  o  changeset:   8:3e1487808078
+  |  branch:  wat
+  |  bookmark:foo
+  |  remote branch:  $TESTTMP/server2/wat
+  |  remote branch:  default/wat
+  |  parent:  4:aa98ab95a928
+  |  user:test
+  |  date:Thu Jan 01 00:00:00 1970 +
+  |  summary: added bar
+  |
+  | o  changeset:   7:ec2426147f0e
+  | |  remote branch:  $TESTTMP/server2/default
+  | |  remote branch:  default/default
+  | |  user:test
+  | |  date:Thu Jan 01 00:00:00 1970 +
+  | |  summary: Added h
+  | |
+  | o  

D2808: remotenames: show remote bookmarks in `hg bookmarks`

2018-03-19 Thread pulkit (Pulkit Goyal)
pulkit updated this revision to Diff 7102.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2808?vs=6871=7102

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

AFFECTED FILES
  hgext/remotenames.py
  tests/test-logexchange.t

CHANGE DETAILS

diff --git a/tests/test-logexchange.t b/tests/test-logexchange.t
--- a/tests/test-logexchange.t
+++ b/tests/test-logexchange.t
@@ -327,3 +327,9 @@
   date:Thu Jan 01 00:00:00 1970 +
   summary: added bar
   
+  $ hg bookmarks
+ $TESTTMP/server2/bar 6:87d6d6676308
+ $TESTTMP/server2/foo 3:62615734edd5
+ default/bar   6:87d6d6676308
+ default/foo   3:62615734edd5
+   * foo   8:3e1487808078
diff --git a/hgext/remotenames.py b/hgext/remotenames.py
--- a/hgext/remotenames.py
+++ b/hgext/remotenames.py
@@ -32,6 +32,8 @@
 bin,
 )
 from mercurial import (
+bookmarks,
+extensions,
 logexchange,
 namespaces,
 pycompat,
@@ -228,6 +230,24 @@
 self._nodetohoists.setdefault(node[0], []).append(name)
 return self._nodetohoists
 
+def wrapprintbookmarks(orig, ui, repo, bmarks, **opts):
+if 'remotebookmarks' not in repo.names:
+return
+ns = repo.names['remotebookmarks']
+
+for name in ns.listnames(repo):
+nodes = ns.nodes(repo, name)
+if not nodes:
+continue
+node = nodes[0]
+
+bmarks[name] = (node, ' ', '')
+
+return orig(ui, repo, bmarks, **opts)
+
+def extsetup(ui):
+extensions.wrapfunction(bookmarks, '_printbookmarks', wrapprintbookmarks)
+
 def reposetup(ui, repo):
 if not repo.local():
 return



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


D2807: remotenames: add functionality to hoist remotebookmarks

2018-03-19 Thread pulkit (Pulkit Goyal)
pulkit updated this revision to Diff 7101.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2807?vs=6870=7101

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

AFFECTED FILES
  hgext/remotenames.py
  tests/test-logexchange.t

CHANGE DETAILS

diff --git a/tests/test-logexchange.t b/tests/test-logexchange.t
--- a/tests/test-logexchange.t
+++ b/tests/test-logexchange.t
@@ -145,6 +145,7 @@
   | |  bookmark:bar
   | |  remote bookmark:  $TESTTMP/server2/bar
   | |  remote bookmark:  default/bar
+  | |  hoistedname:  bar
   | |  user:test
   | |  date:Thu Jan 01 00:00:00 1970 +
   | |  summary: Added g
@@ -163,6 +164,7 @@
   |  bookmark:foo
   |  remote bookmark:  $TESTTMP/server2/foo
   |  remote bookmark:  default/foo
+  |  hoistedname:  foo
   |  user:test
   |  date:Thu Jan 01 00:00:00 1970 +
   |  summary: Added d
@@ -226,6 +228,28 @@
   |
   o  0:18d04c59bb5d [] ()
   
+The `hoistednames` template keyword
+
+  $ hg log -GT "{rev}:{node|short} ({hoistednames})"
+  @  8:3e1487808078 ()
+  |
+  | o  7:ec2426147f0e ()
+  | |
+  | o  6:87d6d6676308 (bar)
+  | |
+  | o  5:825660c69f0c ()
+  |/
+  o  4:aa98ab95a928 ()
+  |
+  o  3:62615734edd5 (foo)
+  |
+  o  2:28ad74487de9 ()
+  |
+  o  1:29becc82797a ()
+  |
+  o  0:18d04c59bb5d ()
+  
+
 Testing the revsets provided by remotenames extension
 
 `remotenames` revset
@@ -259,3 +283,47 @@
   o  3:62615734edd5 $TESTTMP/server2/foo default/foo
   |
   ~
+
+Updating to revision using hoisted name
+
+Deleting local bookmark to make sure we update to hoisted name only
+
+  $ hg bookmark -d bar
+
+  $ hg up bar
+  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+  $ hg log -r .
+  changeset:   6:87d6d6676308
+  remote bookmark:  $TESTTMP/server2/bar
+  remote bookmark:  default/bar
+  hoistedname:  bar
+  user:test
+  date:Thu Jan 01 00:00:00 1970 +
+  summary: Added g
+  
+When both local bookmark and hoisted name exists but on different revs
+
+  $ hg up 8
+  1 files updated, 0 files merged, 2 files removed, 0 files unresolved
+
+  $ hg bookmark foo
+  moving bookmark 'foo' forward from 62615734edd5
+
+Local bookmark should take precedence over hoisted name
+
+  $ hg up foo
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ hg log -r .
+  changeset:   8:3e1487808078
+  branch:  wat
+  bookmark:foo
+  tag: tip
+  remote branch:  $TESTTMP/server2/wat
+  remote branch:  default/wat
+  parent:  4:aa98ab95a928
+  user:test
+  date:Thu Jan 01 00:00:00 1970 +
+  summary: added bar
+  
diff --git a/hgext/remotenames.py b/hgext/remotenames.py
--- a/hgext/remotenames.py
+++ b/hgext/remotenames.py
@@ -14,10 +14,14 @@
 Config options to tweak the default behaviour:
 
 remotenames.bookmarks
-  Boolean value to enable or disable showing of remotebookmarks
+  Boolean value to enable or disable showing of remotebookmarks (default: True)
 
 remotenames.branches
-  Boolean value to enable or disable showing of remotebranches
+  Boolean value to enable or disable showing of remotebranches (default: True)
+
+remotenames.hoist
+  Name of the peer whose remotebookmarks should be hoisted into the top-level
+  namespace (default: 'default')
 """
 
 from __future__ import absolute_import
@@ -61,6 +65,9 @@
 configitem('remotenames', 'branches',
 default=True,
 )
+configitem('remotenames', 'hoist',
+default='default',
+)
 
 class lazyremotenamedict(mutablemapping):
 """
@@ -173,6 +180,8 @@
 def _invalidatecache(self):
 self._nodetobmarks = None
 self._nodetobranch = None
+self._hoisttonodes = None
+self._nodetohoists = None
 
 def bmarktonodes(self):
 return self.bookmarks
@@ -197,6 +206,28 @@
 self._nodetobranch.setdefault(node, []).append(name)
 return self._nodetobranch
 
+def hoisttonodes(self, hoist):
+if not self._hoisttonodes:
+marktonodes = self.bmarktonodes()
+self._hoisttonodes = {}
+hoist += '/'
+for name, node in marktonodes.iteritems():
+if name.startswith(hoist):
+name = name[len(hoist):]
+self._hoisttonodes[name] = node
+return self._hoisttonodes
+
+def nodetohoists(self, hoist):
+if not self._nodetohoists:
+marktonodes = self.bmarktonodes()
+self._nodetohoists = {}
+hoist += '/'
+for name, node in marktonodes.iteritems():
+if name.startswith(hoist):
+name = name[len(hoist):]
+self._nodetohoists.setdefault(node[0], []).append(name)
+return self._nodetohoists
+
 def reposetup(ui, repo):
 if not repo.local():
 return
@@ -217,6 +248,22 @@
 repo._remotenames.nodetobmarks().get(node, []))
 

D2895: py3: use "%d" % int instead of str(int)

2018-03-19 Thread pulkit (Pulkit Goyal)
pulkit created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  We need to use bytes internally.

REPOSITORY
  rHG Mercurial

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

AFFECTED FILES
  mercurial/commands.py

CHANGE DETAILS

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -1133,7 +1133,7 @@
 fm.startitem()
 fm.write('branch', '%s', tag, label=label)
 rev = ctx.rev()
-padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
+padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
 fmt = ' ' * padsize + ' %d:%s'
 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
  label='log.changeset changeset.%s' % ctx.phasestr())



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


D2893: py3: make tests/test-log-linerange.t work on Python 3

2018-03-19 Thread pulkit (Pulkit Goyal)
pulkit created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Made sure we write bytes to file and we suppress the return value of open().

REPOSITORY
  rHG Mercurial

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

AFFECTED FILES
  tests/test-log-linerange.t

CHANGE DETAILS

diff --git a/tests/test-log-linerange.t b/tests/test-log-linerange.t
--- a/tests/test-log-linerange.t
+++ b/tests/test-log-linerange.t
@@ -871,7 +871,7 @@
 Binary files work but without diff hunks filtering.
 (Checking w/ and w/o diff.git option.)
 
-  >>> open('binary', 'wb').write('this\nis\na\nbinary\0')
+  >>> open('binary', 'wb').write(b'this\nis\na\nbinary\0') and None
   $ hg add binary
   $ hg ci -m 'add a binary file' --quiet
   $ hg log -f -L binary,1:2 -p



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


D2894: py3: use pycompat.bytestr() in dirstate.py

2018-03-19 Thread pulkit (Pulkit Goyal)
pulkit created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  This prevents extra b'' prefixes in output.

REPOSITORY
  rHG Mercurial

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

AFFECTED FILES
  mercurial/dirstate.py

CHANGE DETAILS

diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -371,15 +371,17 @@
 if state == 'a' or oldstate == 'r':
 scmutil.checkfilename(f)
 if self._map.hastrackeddir(f):
-raise error.Abort(_('directory %r already in dirstate') % f)
+raise error.Abort(_('directory %r already in dirstate') %
+pycompat.bytestr(f))
 # shadows
 for d in util.finddirs(f):
 if self._map.hastrackeddir(d):
 break
 entry = self._map.get(d)
 if entry is not None and entry[0] != 'r':
 raise error.Abort(
-_('file %r in dirstate clashes with %r') % (d, f))
+_('file %r in dirstate clashes with %r') %
+(pycompat.bytestr(d), pycompat.bytestr(f)))
 self._dirty = True
 self._updatedfiles.add(f)
 self._map.addfile(f, oldstate, state, mode, size, mtime)



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


D2892: py3: use print as a function in tests/test-narrow-clone-non-narrow-server.t

2018-03-19 Thread pulkit (Pulkit Goyal)
pulkit created this revision.
Herald added a reviewer: durin42.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REPOSITORY
  rHG Mercurial

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

AFFECTED FILES
  tests/test-narrow-clone-non-narrow-server.t

CHANGE DETAILS

diff --git a/tests/test-narrow-clone-non-narrow-server.t 
b/tests/test-narrow-clone-non-narrow-server.t
--- a/tests/test-narrow-clone-non-narrow-server.t
+++ b/tests/test-narrow-clone-non-narrow-server.t
@@ -19,7 +19,7 @@
 
 Verify that narrow is advertised in the bundle2 capabilities:
   $ echo hello | hg -R . serve --stdio | \
-  >   $PYTHON -c "import sys, urllib; print 
urllib.unquote_plus(list(sys.stdin)[1])" | grep narrow
+  >   $PYTHON -c "from __future__ import print_function; import sys, urllib; 
print(urllib.unquote_plus(list(sys.stdin)[1]))" | grep narrow
   narrow=v0
 
   $ cd ..



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


Re: Introduction for GSOC'18

2018-03-19 Thread Pulkit Goyal
Hey Lalit,

Here are some links which will help you:

1) Ideas page: https://www.mercurial-scm.org/wiki/SummerOfCode/Ideas2018.
​2) ​Contributing Changes:
https://www.mercurial-scm.org/wiki/ContributingChanges
3) Easy bugs to solve:
https://bz.mercurial-scm.org/buglist.cgi?quicksearch=keyword%3Aeasy_id=10840

If you have any questions or face any problem, feel free to ping me. I am
pulkit on IRC.

Regards
Pulkit

On Fri, Mar 16, 2018 at 1:34 PM, Lalit Kumar 
wrote:

> Hello Devs,
>
> I am Lalit Kumar from India and I am pursuing my graduation in Information
> Technology from Indian Institute of Information Technology Vadodara.
>
> I want to participate in GSOC'18 that's why I am introducing myself to get
> a better start with your organization.  This is my first time when I am
> trying for GSOC.  So I request you to help me a little bit for proceeding
> further.
>
> I am very excited for this. I am damn sure that I will do my level best.
>
> Thanking You.
>
> --
> Regards
> Lalit Kumar
>
>
>
> ‌
>
> ___
> 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