##// END OF EJS Templates
py3: wrap tempfile.NamedTemporaryFile() to return bytes fp.name...
Yuya Nishihara -
r38184:cc9aa887 default
parent child Browse files
Show More
@@ -1,154 +1,158 b''
1 # This software may be used and distributed according to the terms of the
1 # This software may be used and distributed according to the terms of the
2 # GNU General Public License version 2 or any later version.
2 # GNU General Public License version 2 or any later version.
3
3
4 # based on bundleheads extension by Gregory Szorc <gps@mozilla.com>
4 # based on bundleheads extension by Gregory Szorc <gps@mozilla.com>
5
5
6 from __future__ import absolute_import
6 from __future__ import absolute_import
7
7
8 import abc
8 import abc
9 import hashlib
9 import hashlib
10 import os
10 import os
11 import subprocess
11 import subprocess
12 import tempfile
12 import tempfile
13
13
14 NamedTemporaryFile = tempfile.NamedTemporaryFile
14 NamedTemporaryFile = tempfile.NamedTemporaryFile
15
15
16 class BundleWriteException(Exception):
16 class BundleWriteException(Exception):
17 pass
17 pass
18
18
19 class BundleReadException(Exception):
19 class BundleReadException(Exception):
20 pass
20 pass
21
21
22 class abstractbundlestore(object):
22 class abstractbundlestore(object):
23 """Defines the interface for bundle stores.
23 """Defines the interface for bundle stores.
24
24
25 A bundle store is an entity that stores raw bundle data. It is a simple
25 A bundle store is an entity that stores raw bundle data. It is a simple
26 key-value store. However, the keys are chosen by the store. The keys can
26 key-value store. However, the keys are chosen by the store. The keys can
27 be any Python object understood by the corresponding bundle index (see
27 be any Python object understood by the corresponding bundle index (see
28 ``abstractbundleindex`` below).
28 ``abstractbundleindex`` below).
29 """
29 """
30 __metaclass__ = abc.ABCMeta
30 __metaclass__ = abc.ABCMeta
31
31
32 @abc.abstractmethod
32 @abc.abstractmethod
33 def write(self, data):
33 def write(self, data):
34 """Write bundle data to the store.
34 """Write bundle data to the store.
35
35
36 This function receives the raw data to be written as a str.
36 This function receives the raw data to be written as a str.
37 Throws BundleWriteException
37 Throws BundleWriteException
38 The key of the written data MUST be returned.
38 The key of the written data MUST be returned.
39 """
39 """
40
40
41 @abc.abstractmethod
41 @abc.abstractmethod
42 def read(self, key):
42 def read(self, key):
43 """Obtain bundle data for a key.
43 """Obtain bundle data for a key.
44
44
45 Returns None if the bundle isn't known.
45 Returns None if the bundle isn't known.
46 Throws BundleReadException
46 Throws BundleReadException
47 The returned object should be a file object supporting read()
47 The returned object should be a file object supporting read()
48 and close().
48 and close().
49 """
49 """
50
50
51 class filebundlestore(object):
51 class filebundlestore(object):
52 """bundle store in filesystem
52 """bundle store in filesystem
53
53
54 meant for storing bundles somewhere on disk and on network filesystems
54 meant for storing bundles somewhere on disk and on network filesystems
55 """
55 """
56 def __init__(self, ui, repo):
56 def __init__(self, ui, repo):
57 self.ui = ui
57 self.ui = ui
58 self.repo = repo
58 self.repo = repo
59 self.storepath = ui.configpath('scratchbranch', 'storepath')
59 self.storepath = ui.configpath('scratchbranch', 'storepath')
60 if not self.storepath:
60 if not self.storepath:
61 self.storepath = self.repo.vfs.join("scratchbranches",
61 self.storepath = self.repo.vfs.join("scratchbranches",
62 "filebundlestore")
62 "filebundlestore")
63 if not os.path.exists(self.storepath):
63 if not os.path.exists(self.storepath):
64 os.makedirs(self.storepath)
64 os.makedirs(self.storepath)
65
65
66 def _dirpath(self, hashvalue):
66 def _dirpath(self, hashvalue):
67 """First two bytes of the hash are the name of the upper
67 """First two bytes of the hash are the name of the upper
68 level directory, next two bytes are the name of the
68 level directory, next two bytes are the name of the
69 next level directory"""
69 next level directory"""
70 return os.path.join(self.storepath, hashvalue[0:2], hashvalue[2:4])
70 return os.path.join(self.storepath, hashvalue[0:2], hashvalue[2:4])
71
71
72 def _filepath(self, filename):
72 def _filepath(self, filename):
73 return os.path.join(self._dirpath(filename), filename)
73 return os.path.join(self._dirpath(filename), filename)
74
74
75 def write(self, data):
75 def write(self, data):
76 filename = hashlib.sha1(data).hexdigest()
76 filename = hashlib.sha1(data).hexdigest()
77 dirpath = self._dirpath(filename)
77 dirpath = self._dirpath(filename)
78
78
79 if not os.path.exists(dirpath):
79 if not os.path.exists(dirpath):
80 os.makedirs(dirpath)
80 os.makedirs(dirpath)
81
81
82 with open(self._filepath(filename), 'wb') as f:
82 with open(self._filepath(filename), 'wb') as f:
83 f.write(data)
83 f.write(data)
84
84
85 return filename
85 return filename
86
86
87 def read(self, key):
87 def read(self, key):
88 try:
88 try:
89 with open(self._filepath(key), 'rb') as f:
89 with open(self._filepath(key), 'rb') as f:
90 return f.read()
90 return f.read()
91 except IOError:
91 except IOError:
92 return None
92 return None
93
93
94 class externalbundlestore(abstractbundlestore):
94 class externalbundlestore(abstractbundlestore):
95 def __init__(self, put_binary, put_args, get_binary, get_args):
95 def __init__(self, put_binary, put_args, get_binary, get_args):
96 """
96 """
97 `put_binary` - path to binary file which uploads bundle to external
97 `put_binary` - path to binary file which uploads bundle to external
98 storage and prints key to stdout
98 storage and prints key to stdout
99 `put_args` - format string with additional args to `put_binary`
99 `put_args` - format string with additional args to `put_binary`
100 {filename} replacement field can be used.
100 {filename} replacement field can be used.
101 `get_binary` - path to binary file which accepts filename and key
101 `get_binary` - path to binary file which accepts filename and key
102 (in that order), downloads bundle from store and saves it to file
102 (in that order), downloads bundle from store and saves it to file
103 `get_args` - format string with additional args to `get_binary`.
103 `get_args` - format string with additional args to `get_binary`.
104 {filename} and {handle} replacement field can be used.
104 {filename} and {handle} replacement field can be used.
105 """
105 """
106
106
107 self.put_args = put_args
107 self.put_args = put_args
108 self.get_args = get_args
108 self.get_args = get_args
109 self.put_binary = put_binary
109 self.put_binary = put_binary
110 self.get_binary = get_binary
110 self.get_binary = get_binary
111
111
112 def _call_binary(self, args):
112 def _call_binary(self, args):
113 p = subprocess.Popen(
113 p = subprocess.Popen(
114 args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
114 args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
115 close_fds=True)
115 close_fds=True)
116 stdout, stderr = p.communicate()
116 stdout, stderr = p.communicate()
117 returncode = p.returncode
117 returncode = p.returncode
118 return returncode, stdout, stderr
118 return returncode, stdout, stderr
119
119
120 def write(self, data):
120 def write(self, data):
121 # Won't work on windows because you can't open file second time without
121 # Won't work on windows because you can't open file second time without
122 # closing it
122 # closing it
123 # TODO: rewrite without str.format() and replace NamedTemporaryFile()
124 # with pycompat.namedtempfile()
123 with NamedTemporaryFile() as temp:
125 with NamedTemporaryFile() as temp:
124 temp.write(data)
126 temp.write(data)
125 temp.flush()
127 temp.flush()
126 temp.seek(0)
128 temp.seek(0)
127 formatted_args = [arg.format(filename=temp.name)
129 formatted_args = [arg.format(filename=temp.name)
128 for arg in self.put_args]
130 for arg in self.put_args]
129 returncode, stdout, stderr = self._call_binary(
131 returncode, stdout, stderr = self._call_binary(
130 [self.put_binary] + formatted_args)
132 [self.put_binary] + formatted_args)
131
133
132 if returncode != 0:
134 if returncode != 0:
133 raise BundleWriteException(
135 raise BundleWriteException(
134 'Failed to upload to external store: %s' % stderr)
136 'Failed to upload to external store: %s' % stderr)
135 stdout_lines = stdout.splitlines()
137 stdout_lines = stdout.splitlines()
136 if len(stdout_lines) == 1:
138 if len(stdout_lines) == 1:
137 return stdout_lines[0]
139 return stdout_lines[0]
138 else:
140 else:
139 raise BundleWriteException(
141 raise BundleWriteException(
140 'Bad output from %s: %s' % (self.put_binary, stdout))
142 'Bad output from %s: %s' % (self.put_binary, stdout))
141
143
142 def read(self, handle):
144 def read(self, handle):
143 # Won't work on windows because you can't open file second time without
145 # Won't work on windows because you can't open file second time without
144 # closing it
146 # closing it
147 # TODO: rewrite without str.format() and replace NamedTemporaryFile()
148 # with pycompat.namedtempfile()
145 with NamedTemporaryFile() as temp:
149 with NamedTemporaryFile() as temp:
146 formatted_args = [arg.format(filename=temp.name, handle=handle)
150 formatted_args = [arg.format(filename=temp.name, handle=handle)
147 for arg in self.get_args]
151 for arg in self.get_args]
148 returncode, stdout, stderr = self._call_binary(
152 returncode, stdout, stderr = self._call_binary(
149 [self.get_binary] + formatted_args)
153 [self.get_binary] + formatted_args)
150
154
151 if returncode != 0:
155 if returncode != 0:
152 raise BundleReadException(
156 raise BundleReadException(
153 'Failed to download from external store: %s' % stderr)
157 'Failed to download from external store: %s' % stderr)
154 return temp.read()
158 return temp.read()
@@ -1,3138 +1,3137 b''
1 # debugcommands.py - command processing for debug* commands
1 # debugcommands.py - command processing for debug* commands
2 #
2 #
3 # Copyright 2005-2016 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2016 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import codecs
10 import codecs
11 import collections
11 import collections
12 import difflib
12 import difflib
13 import errno
13 import errno
14 import operator
14 import operator
15 import os
15 import os
16 import random
16 import random
17 import re
17 import re
18 import socket
18 import socket
19 import ssl
19 import ssl
20 import stat
20 import stat
21 import string
21 import string
22 import subprocess
22 import subprocess
23 import sys
23 import sys
24 import tempfile
25 import time
24 import time
26
25
27 from .i18n import _
26 from .i18n import _
28 from .node import (
27 from .node import (
29 bin,
28 bin,
30 hex,
29 hex,
31 nullhex,
30 nullhex,
32 nullid,
31 nullid,
33 nullrev,
32 nullrev,
34 short,
33 short,
35 )
34 )
36 from .thirdparty import (
35 from .thirdparty import (
37 cbor,
36 cbor,
38 )
37 )
39 from . import (
38 from . import (
40 bundle2,
39 bundle2,
41 changegroup,
40 changegroup,
42 cmdutil,
41 cmdutil,
43 color,
42 color,
44 context,
43 context,
45 dagparser,
44 dagparser,
46 dagutil,
45 dagutil,
47 encoding,
46 encoding,
48 error,
47 error,
49 exchange,
48 exchange,
50 extensions,
49 extensions,
51 filemerge,
50 filemerge,
52 fileset,
51 fileset,
53 formatter,
52 formatter,
54 hg,
53 hg,
55 httppeer,
54 httppeer,
56 localrepo,
55 localrepo,
57 lock as lockmod,
56 lock as lockmod,
58 logcmdutil,
57 logcmdutil,
59 merge as mergemod,
58 merge as mergemod,
60 obsolete,
59 obsolete,
61 obsutil,
60 obsutil,
62 phases,
61 phases,
63 policy,
62 policy,
64 pvec,
63 pvec,
65 pycompat,
64 pycompat,
66 registrar,
65 registrar,
67 repair,
66 repair,
68 revlog,
67 revlog,
69 revset,
68 revset,
70 revsetlang,
69 revsetlang,
71 scmutil,
70 scmutil,
72 setdiscovery,
71 setdiscovery,
73 simplemerge,
72 simplemerge,
74 smartset,
73 smartset,
75 sshpeer,
74 sshpeer,
76 sslutil,
75 sslutil,
77 streamclone,
76 streamclone,
78 templater,
77 templater,
79 treediscovery,
78 treediscovery,
80 upgrade,
79 upgrade,
81 url as urlmod,
80 url as urlmod,
82 util,
81 util,
83 vfs as vfsmod,
82 vfs as vfsmod,
84 wireprotoframing,
83 wireprotoframing,
85 wireprotoserver,
84 wireprotoserver,
86 wireprotov2peer,
85 wireprotov2peer,
87 )
86 )
88 from .utils import (
87 from .utils import (
89 dateutil,
88 dateutil,
90 procutil,
89 procutil,
91 stringutil,
90 stringutil,
92 )
91 )
93
92
94 release = lockmod.release
93 release = lockmod.release
95
94
96 command = registrar.command()
95 command = registrar.command()
97
96
98 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
97 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
99 def debugancestor(ui, repo, *args):
98 def debugancestor(ui, repo, *args):
100 """find the ancestor revision of two revisions in a given index"""
99 """find the ancestor revision of two revisions in a given index"""
101 if len(args) == 3:
100 if len(args) == 3:
102 index, rev1, rev2 = args
101 index, rev1, rev2 = args
103 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False), index)
102 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False), index)
104 lookup = r.lookup
103 lookup = r.lookup
105 elif len(args) == 2:
104 elif len(args) == 2:
106 if not repo:
105 if not repo:
107 raise error.Abort(_('there is no Mercurial repository here '
106 raise error.Abort(_('there is no Mercurial repository here '
108 '(.hg not found)'))
107 '(.hg not found)'))
109 rev1, rev2 = args
108 rev1, rev2 = args
110 r = repo.changelog
109 r = repo.changelog
111 lookup = repo.lookup
110 lookup = repo.lookup
112 else:
111 else:
113 raise error.Abort(_('either two or three arguments required'))
112 raise error.Abort(_('either two or three arguments required'))
114 a = r.ancestor(lookup(rev1), lookup(rev2))
113 a = r.ancestor(lookup(rev1), lookup(rev2))
115 ui.write('%d:%s\n' % (r.rev(a), hex(a)))
114 ui.write('%d:%s\n' % (r.rev(a), hex(a)))
116
115
117 @command('debugapplystreamclonebundle', [], 'FILE')
116 @command('debugapplystreamclonebundle', [], 'FILE')
118 def debugapplystreamclonebundle(ui, repo, fname):
117 def debugapplystreamclonebundle(ui, repo, fname):
119 """apply a stream clone bundle file"""
118 """apply a stream clone bundle file"""
120 f = hg.openpath(ui, fname)
119 f = hg.openpath(ui, fname)
121 gen = exchange.readbundle(ui, f, fname)
120 gen = exchange.readbundle(ui, f, fname)
122 gen.apply(repo)
121 gen.apply(repo)
123
122
124 @command('debugbuilddag',
123 @command('debugbuilddag',
125 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
124 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
126 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
125 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
127 ('n', 'new-file', None, _('add new file at each rev'))],
126 ('n', 'new-file', None, _('add new file at each rev'))],
128 _('[OPTION]... [TEXT]'))
127 _('[OPTION]... [TEXT]'))
129 def debugbuilddag(ui, repo, text=None,
128 def debugbuilddag(ui, repo, text=None,
130 mergeable_file=False,
129 mergeable_file=False,
131 overwritten_file=False,
130 overwritten_file=False,
132 new_file=False):
131 new_file=False):
133 """builds a repo with a given DAG from scratch in the current empty repo
132 """builds a repo with a given DAG from scratch in the current empty repo
134
133
135 The description of the DAG is read from stdin if not given on the
134 The description of the DAG is read from stdin if not given on the
136 command line.
135 command line.
137
136
138 Elements:
137 Elements:
139
138
140 - "+n" is a linear run of n nodes based on the current default parent
139 - "+n" is a linear run of n nodes based on the current default parent
141 - "." is a single node based on the current default parent
140 - "." is a single node based on the current default parent
142 - "$" resets the default parent to null (implied at the start);
141 - "$" resets the default parent to null (implied at the start);
143 otherwise the default parent is always the last node created
142 otherwise the default parent is always the last node created
144 - "<p" sets the default parent to the backref p
143 - "<p" sets the default parent to the backref p
145 - "*p" is a fork at parent p, which is a backref
144 - "*p" is a fork at parent p, which is a backref
146 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
145 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
147 - "/p2" is a merge of the preceding node and p2
146 - "/p2" is a merge of the preceding node and p2
148 - ":tag" defines a local tag for the preceding node
147 - ":tag" defines a local tag for the preceding node
149 - "@branch" sets the named branch for subsequent nodes
148 - "@branch" sets the named branch for subsequent nodes
150 - "#...\\n" is a comment up to the end of the line
149 - "#...\\n" is a comment up to the end of the line
151
150
152 Whitespace between the above elements is ignored.
151 Whitespace between the above elements is ignored.
153
152
154 A backref is either
153 A backref is either
155
154
156 - a number n, which references the node curr-n, where curr is the current
155 - a number n, which references the node curr-n, where curr is the current
157 node, or
156 node, or
158 - the name of a local tag you placed earlier using ":tag", or
157 - the name of a local tag you placed earlier using ":tag", or
159 - empty to denote the default parent.
158 - empty to denote the default parent.
160
159
161 All string valued-elements are either strictly alphanumeric, or must
160 All string valued-elements are either strictly alphanumeric, or must
162 be enclosed in double quotes ("..."), with "\\" as escape character.
161 be enclosed in double quotes ("..."), with "\\" as escape character.
163 """
162 """
164
163
165 if text is None:
164 if text is None:
166 ui.status(_("reading DAG from stdin\n"))
165 ui.status(_("reading DAG from stdin\n"))
167 text = ui.fin.read()
166 text = ui.fin.read()
168
167
169 cl = repo.changelog
168 cl = repo.changelog
170 if len(cl) > 0:
169 if len(cl) > 0:
171 raise error.Abort(_('repository is not empty'))
170 raise error.Abort(_('repository is not empty'))
172
171
173 # determine number of revs in DAG
172 # determine number of revs in DAG
174 total = 0
173 total = 0
175 for type, data in dagparser.parsedag(text):
174 for type, data in dagparser.parsedag(text):
176 if type == 'n':
175 if type == 'n':
177 total += 1
176 total += 1
178
177
179 if mergeable_file:
178 if mergeable_file:
180 linesperrev = 2
179 linesperrev = 2
181 # make a file with k lines per rev
180 # make a file with k lines per rev
182 initialmergedlines = ['%d' % i for i in xrange(0, total * linesperrev)]
181 initialmergedlines = ['%d' % i for i in xrange(0, total * linesperrev)]
183 initialmergedlines.append("")
182 initialmergedlines.append("")
184
183
185 tags = []
184 tags = []
186
185
187 wlock = lock = tr = None
186 wlock = lock = tr = None
188 try:
187 try:
189 wlock = repo.wlock()
188 wlock = repo.wlock()
190 lock = repo.lock()
189 lock = repo.lock()
191 tr = repo.transaction("builddag")
190 tr = repo.transaction("builddag")
192
191
193 at = -1
192 at = -1
194 atbranch = 'default'
193 atbranch = 'default'
195 nodeids = []
194 nodeids = []
196 id = 0
195 id = 0
197 ui.progress(_('building'), id, unit=_('revisions'), total=total)
196 ui.progress(_('building'), id, unit=_('revisions'), total=total)
198 for type, data in dagparser.parsedag(text):
197 for type, data in dagparser.parsedag(text):
199 if type == 'n':
198 if type == 'n':
200 ui.note(('node %s\n' % pycompat.bytestr(data)))
199 ui.note(('node %s\n' % pycompat.bytestr(data)))
201 id, ps = data
200 id, ps = data
202
201
203 files = []
202 files = []
204 filecontent = {}
203 filecontent = {}
205
204
206 p2 = None
205 p2 = None
207 if mergeable_file:
206 if mergeable_file:
208 fn = "mf"
207 fn = "mf"
209 p1 = repo[ps[0]]
208 p1 = repo[ps[0]]
210 if len(ps) > 1:
209 if len(ps) > 1:
211 p2 = repo[ps[1]]
210 p2 = repo[ps[1]]
212 pa = p1.ancestor(p2)
211 pa = p1.ancestor(p2)
213 base, local, other = [x[fn].data() for x in (pa, p1,
212 base, local, other = [x[fn].data() for x in (pa, p1,
214 p2)]
213 p2)]
215 m3 = simplemerge.Merge3Text(base, local, other)
214 m3 = simplemerge.Merge3Text(base, local, other)
216 ml = [l.strip() for l in m3.merge_lines()]
215 ml = [l.strip() for l in m3.merge_lines()]
217 ml.append("")
216 ml.append("")
218 elif at > 0:
217 elif at > 0:
219 ml = p1[fn].data().split("\n")
218 ml = p1[fn].data().split("\n")
220 else:
219 else:
221 ml = initialmergedlines
220 ml = initialmergedlines
222 ml[id * linesperrev] += " r%i" % id
221 ml[id * linesperrev] += " r%i" % id
223 mergedtext = "\n".join(ml)
222 mergedtext = "\n".join(ml)
224 files.append(fn)
223 files.append(fn)
225 filecontent[fn] = mergedtext
224 filecontent[fn] = mergedtext
226
225
227 if overwritten_file:
226 if overwritten_file:
228 fn = "of"
227 fn = "of"
229 files.append(fn)
228 files.append(fn)
230 filecontent[fn] = "r%i\n" % id
229 filecontent[fn] = "r%i\n" % id
231
230
232 if new_file:
231 if new_file:
233 fn = "nf%i" % id
232 fn = "nf%i" % id
234 files.append(fn)
233 files.append(fn)
235 filecontent[fn] = "r%i\n" % id
234 filecontent[fn] = "r%i\n" % id
236 if len(ps) > 1:
235 if len(ps) > 1:
237 if not p2:
236 if not p2:
238 p2 = repo[ps[1]]
237 p2 = repo[ps[1]]
239 for fn in p2:
238 for fn in p2:
240 if fn.startswith("nf"):
239 if fn.startswith("nf"):
241 files.append(fn)
240 files.append(fn)
242 filecontent[fn] = p2[fn].data()
241 filecontent[fn] = p2[fn].data()
243
242
244 def fctxfn(repo, cx, path):
243 def fctxfn(repo, cx, path):
245 if path in filecontent:
244 if path in filecontent:
246 return context.memfilectx(repo, cx, path,
245 return context.memfilectx(repo, cx, path,
247 filecontent[path])
246 filecontent[path])
248 return None
247 return None
249
248
250 if len(ps) == 0 or ps[0] < 0:
249 if len(ps) == 0 or ps[0] < 0:
251 pars = [None, None]
250 pars = [None, None]
252 elif len(ps) == 1:
251 elif len(ps) == 1:
253 pars = [nodeids[ps[0]], None]
252 pars = [nodeids[ps[0]], None]
254 else:
253 else:
255 pars = [nodeids[p] for p in ps]
254 pars = [nodeids[p] for p in ps]
256 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
255 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
257 date=(id, 0),
256 date=(id, 0),
258 user="debugbuilddag",
257 user="debugbuilddag",
259 extra={'branch': atbranch})
258 extra={'branch': atbranch})
260 nodeid = repo.commitctx(cx)
259 nodeid = repo.commitctx(cx)
261 nodeids.append(nodeid)
260 nodeids.append(nodeid)
262 at = id
261 at = id
263 elif type == 'l':
262 elif type == 'l':
264 id, name = data
263 id, name = data
265 ui.note(('tag %s\n' % name))
264 ui.note(('tag %s\n' % name))
266 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
265 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
267 elif type == 'a':
266 elif type == 'a':
268 ui.note(('branch %s\n' % data))
267 ui.note(('branch %s\n' % data))
269 atbranch = data
268 atbranch = data
270 ui.progress(_('building'), id, unit=_('revisions'), total=total)
269 ui.progress(_('building'), id, unit=_('revisions'), total=total)
271 tr.close()
270 tr.close()
272
271
273 if tags:
272 if tags:
274 repo.vfs.write("localtags", "".join(tags))
273 repo.vfs.write("localtags", "".join(tags))
275 finally:
274 finally:
276 ui.progress(_('building'), None)
275 ui.progress(_('building'), None)
277 release(tr, lock, wlock)
276 release(tr, lock, wlock)
278
277
279 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
278 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
280 indent_string = ' ' * indent
279 indent_string = ' ' * indent
281 if all:
280 if all:
282 ui.write(("%sformat: id, p1, p2, cset, delta base, len(delta)\n")
281 ui.write(("%sformat: id, p1, p2, cset, delta base, len(delta)\n")
283 % indent_string)
282 % indent_string)
284
283
285 def showchunks(named):
284 def showchunks(named):
286 ui.write("\n%s%s\n" % (indent_string, named))
285 ui.write("\n%s%s\n" % (indent_string, named))
287 for deltadata in gen.deltaiter():
286 for deltadata in gen.deltaiter():
288 node, p1, p2, cs, deltabase, delta, flags = deltadata
287 node, p1, p2, cs, deltabase, delta, flags = deltadata
289 ui.write("%s%s %s %s %s %s %d\n" %
288 ui.write("%s%s %s %s %s %s %d\n" %
290 (indent_string, hex(node), hex(p1), hex(p2),
289 (indent_string, hex(node), hex(p1), hex(p2),
291 hex(cs), hex(deltabase), len(delta)))
290 hex(cs), hex(deltabase), len(delta)))
292
291
293 chunkdata = gen.changelogheader()
292 chunkdata = gen.changelogheader()
294 showchunks("changelog")
293 showchunks("changelog")
295 chunkdata = gen.manifestheader()
294 chunkdata = gen.manifestheader()
296 showchunks("manifest")
295 showchunks("manifest")
297 for chunkdata in iter(gen.filelogheader, {}):
296 for chunkdata in iter(gen.filelogheader, {}):
298 fname = chunkdata['filename']
297 fname = chunkdata['filename']
299 showchunks(fname)
298 showchunks(fname)
300 else:
299 else:
301 if isinstance(gen, bundle2.unbundle20):
300 if isinstance(gen, bundle2.unbundle20):
302 raise error.Abort(_('use debugbundle2 for this file'))
301 raise error.Abort(_('use debugbundle2 for this file'))
303 chunkdata = gen.changelogheader()
302 chunkdata = gen.changelogheader()
304 for deltadata in gen.deltaiter():
303 for deltadata in gen.deltaiter():
305 node, p1, p2, cs, deltabase, delta, flags = deltadata
304 node, p1, p2, cs, deltabase, delta, flags = deltadata
306 ui.write("%s%s\n" % (indent_string, hex(node)))
305 ui.write("%s%s\n" % (indent_string, hex(node)))
307
306
308 def _debugobsmarkers(ui, part, indent=0, **opts):
307 def _debugobsmarkers(ui, part, indent=0, **opts):
309 """display version and markers contained in 'data'"""
308 """display version and markers contained in 'data'"""
310 opts = pycompat.byteskwargs(opts)
309 opts = pycompat.byteskwargs(opts)
311 data = part.read()
310 data = part.read()
312 indent_string = ' ' * indent
311 indent_string = ' ' * indent
313 try:
312 try:
314 version, markers = obsolete._readmarkers(data)
313 version, markers = obsolete._readmarkers(data)
315 except error.UnknownVersion as exc:
314 except error.UnknownVersion as exc:
316 msg = "%sunsupported version: %s (%d bytes)\n"
315 msg = "%sunsupported version: %s (%d bytes)\n"
317 msg %= indent_string, exc.version, len(data)
316 msg %= indent_string, exc.version, len(data)
318 ui.write(msg)
317 ui.write(msg)
319 else:
318 else:
320 msg = "%sversion: %d (%d bytes)\n"
319 msg = "%sversion: %d (%d bytes)\n"
321 msg %= indent_string, version, len(data)
320 msg %= indent_string, version, len(data)
322 ui.write(msg)
321 ui.write(msg)
323 fm = ui.formatter('debugobsolete', opts)
322 fm = ui.formatter('debugobsolete', opts)
324 for rawmarker in sorted(markers):
323 for rawmarker in sorted(markers):
325 m = obsutil.marker(None, rawmarker)
324 m = obsutil.marker(None, rawmarker)
326 fm.startitem()
325 fm.startitem()
327 fm.plain(indent_string)
326 fm.plain(indent_string)
328 cmdutil.showmarker(fm, m)
327 cmdutil.showmarker(fm, m)
329 fm.end()
328 fm.end()
330
329
331 def _debugphaseheads(ui, data, indent=0):
330 def _debugphaseheads(ui, data, indent=0):
332 """display version and markers contained in 'data'"""
331 """display version and markers contained in 'data'"""
333 indent_string = ' ' * indent
332 indent_string = ' ' * indent
334 headsbyphase = phases.binarydecode(data)
333 headsbyphase = phases.binarydecode(data)
335 for phase in phases.allphases:
334 for phase in phases.allphases:
336 for head in headsbyphase[phase]:
335 for head in headsbyphase[phase]:
337 ui.write(indent_string)
336 ui.write(indent_string)
338 ui.write('%s %s\n' % (hex(head), phases.phasenames[phase]))
337 ui.write('%s %s\n' % (hex(head), phases.phasenames[phase]))
339
338
340 def _quasirepr(thing):
339 def _quasirepr(thing):
341 if isinstance(thing, (dict, util.sortdict, collections.OrderedDict)):
340 if isinstance(thing, (dict, util.sortdict, collections.OrderedDict)):
342 return '{%s}' % (
341 return '{%s}' % (
343 b', '.join(b'%s: %s' % (k, thing[k]) for k in sorted(thing)))
342 b', '.join(b'%s: %s' % (k, thing[k]) for k in sorted(thing)))
344 return pycompat.bytestr(repr(thing))
343 return pycompat.bytestr(repr(thing))
345
344
346 def _debugbundle2(ui, gen, all=None, **opts):
345 def _debugbundle2(ui, gen, all=None, **opts):
347 """lists the contents of a bundle2"""
346 """lists the contents of a bundle2"""
348 if not isinstance(gen, bundle2.unbundle20):
347 if not isinstance(gen, bundle2.unbundle20):
349 raise error.Abort(_('not a bundle2 file'))
348 raise error.Abort(_('not a bundle2 file'))
350 ui.write(('Stream params: %s\n' % _quasirepr(gen.params)))
349 ui.write(('Stream params: %s\n' % _quasirepr(gen.params)))
351 parttypes = opts.get(r'part_type', [])
350 parttypes = opts.get(r'part_type', [])
352 for part in gen.iterparts():
351 for part in gen.iterparts():
353 if parttypes and part.type not in parttypes:
352 if parttypes and part.type not in parttypes:
354 continue
353 continue
355 ui.write('%s -- %s\n' % (part.type, _quasirepr(part.params)))
354 ui.write('%s -- %s\n' % (part.type, _quasirepr(part.params)))
356 if part.type == 'changegroup':
355 if part.type == 'changegroup':
357 version = part.params.get('version', '01')
356 version = part.params.get('version', '01')
358 cg = changegroup.getunbundler(version, part, 'UN')
357 cg = changegroup.getunbundler(version, part, 'UN')
359 if not ui.quiet:
358 if not ui.quiet:
360 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
359 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
361 if part.type == 'obsmarkers':
360 if part.type == 'obsmarkers':
362 if not ui.quiet:
361 if not ui.quiet:
363 _debugobsmarkers(ui, part, indent=4, **opts)
362 _debugobsmarkers(ui, part, indent=4, **opts)
364 if part.type == 'phase-heads':
363 if part.type == 'phase-heads':
365 if not ui.quiet:
364 if not ui.quiet:
366 _debugphaseheads(ui, part, indent=4)
365 _debugphaseheads(ui, part, indent=4)
367
366
368 @command('debugbundle',
367 @command('debugbundle',
369 [('a', 'all', None, _('show all details')),
368 [('a', 'all', None, _('show all details')),
370 ('', 'part-type', [], _('show only the named part type')),
369 ('', 'part-type', [], _('show only the named part type')),
371 ('', 'spec', None, _('print the bundlespec of the bundle'))],
370 ('', 'spec', None, _('print the bundlespec of the bundle'))],
372 _('FILE'),
371 _('FILE'),
373 norepo=True)
372 norepo=True)
374 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
373 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
375 """lists the contents of a bundle"""
374 """lists the contents of a bundle"""
376 with hg.openpath(ui, bundlepath) as f:
375 with hg.openpath(ui, bundlepath) as f:
377 if spec:
376 if spec:
378 spec = exchange.getbundlespec(ui, f)
377 spec = exchange.getbundlespec(ui, f)
379 ui.write('%s\n' % spec)
378 ui.write('%s\n' % spec)
380 return
379 return
381
380
382 gen = exchange.readbundle(ui, f, bundlepath)
381 gen = exchange.readbundle(ui, f, bundlepath)
383 if isinstance(gen, bundle2.unbundle20):
382 if isinstance(gen, bundle2.unbundle20):
384 return _debugbundle2(ui, gen, all=all, **opts)
383 return _debugbundle2(ui, gen, all=all, **opts)
385 _debugchangegroup(ui, gen, all=all, **opts)
384 _debugchangegroup(ui, gen, all=all, **opts)
386
385
387 @command('debugcapabilities',
386 @command('debugcapabilities',
388 [], _('PATH'),
387 [], _('PATH'),
389 norepo=True)
388 norepo=True)
390 def debugcapabilities(ui, path, **opts):
389 def debugcapabilities(ui, path, **opts):
391 """lists the capabilities of a remote peer"""
390 """lists the capabilities of a remote peer"""
392 opts = pycompat.byteskwargs(opts)
391 opts = pycompat.byteskwargs(opts)
393 peer = hg.peer(ui, opts, path)
392 peer = hg.peer(ui, opts, path)
394 caps = peer.capabilities()
393 caps = peer.capabilities()
395 ui.write(('Main capabilities:\n'))
394 ui.write(('Main capabilities:\n'))
396 for c in sorted(caps):
395 for c in sorted(caps):
397 ui.write((' %s\n') % c)
396 ui.write((' %s\n') % c)
398 b2caps = bundle2.bundle2caps(peer)
397 b2caps = bundle2.bundle2caps(peer)
399 if b2caps:
398 if b2caps:
400 ui.write(('Bundle2 capabilities:\n'))
399 ui.write(('Bundle2 capabilities:\n'))
401 for key, values in sorted(b2caps.iteritems()):
400 for key, values in sorted(b2caps.iteritems()):
402 ui.write((' %s\n') % key)
401 ui.write((' %s\n') % key)
403 for v in values:
402 for v in values:
404 ui.write((' %s\n') % v)
403 ui.write((' %s\n') % v)
405
404
406 @command('debugcheckstate', [], '')
405 @command('debugcheckstate', [], '')
407 def debugcheckstate(ui, repo):
406 def debugcheckstate(ui, repo):
408 """validate the correctness of the current dirstate"""
407 """validate the correctness of the current dirstate"""
409 parent1, parent2 = repo.dirstate.parents()
408 parent1, parent2 = repo.dirstate.parents()
410 m1 = repo[parent1].manifest()
409 m1 = repo[parent1].manifest()
411 m2 = repo[parent2].manifest()
410 m2 = repo[parent2].manifest()
412 errors = 0
411 errors = 0
413 for f in repo.dirstate:
412 for f in repo.dirstate:
414 state = repo.dirstate[f]
413 state = repo.dirstate[f]
415 if state in "nr" and f not in m1:
414 if state in "nr" and f not in m1:
416 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
415 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
417 errors += 1
416 errors += 1
418 if state in "a" and f in m1:
417 if state in "a" and f in m1:
419 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
418 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
420 errors += 1
419 errors += 1
421 if state in "m" and f not in m1 and f not in m2:
420 if state in "m" and f not in m1 and f not in m2:
422 ui.warn(_("%s in state %s, but not in either manifest\n") %
421 ui.warn(_("%s in state %s, but not in either manifest\n") %
423 (f, state))
422 (f, state))
424 errors += 1
423 errors += 1
425 for f in m1:
424 for f in m1:
426 state = repo.dirstate[f]
425 state = repo.dirstate[f]
427 if state not in "nrm":
426 if state not in "nrm":
428 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
427 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
429 errors += 1
428 errors += 1
430 if errors:
429 if errors:
431 error = _(".hg/dirstate inconsistent with current parent's manifest")
430 error = _(".hg/dirstate inconsistent with current parent's manifest")
432 raise error.Abort(error)
431 raise error.Abort(error)
433
432
434 @command('debugcolor',
433 @command('debugcolor',
435 [('', 'style', None, _('show all configured styles'))],
434 [('', 'style', None, _('show all configured styles'))],
436 'hg debugcolor')
435 'hg debugcolor')
437 def debugcolor(ui, repo, **opts):
436 def debugcolor(ui, repo, **opts):
438 """show available color, effects or style"""
437 """show available color, effects or style"""
439 ui.write(('color mode: %s\n') % ui._colormode)
438 ui.write(('color mode: %s\n') % ui._colormode)
440 if opts.get(r'style'):
439 if opts.get(r'style'):
441 return _debugdisplaystyle(ui)
440 return _debugdisplaystyle(ui)
442 else:
441 else:
443 return _debugdisplaycolor(ui)
442 return _debugdisplaycolor(ui)
444
443
445 def _debugdisplaycolor(ui):
444 def _debugdisplaycolor(ui):
446 ui = ui.copy()
445 ui = ui.copy()
447 ui._styles.clear()
446 ui._styles.clear()
448 for effect in color._activeeffects(ui).keys():
447 for effect in color._activeeffects(ui).keys():
449 ui._styles[effect] = effect
448 ui._styles[effect] = effect
450 if ui._terminfoparams:
449 if ui._terminfoparams:
451 for k, v in ui.configitems('color'):
450 for k, v in ui.configitems('color'):
452 if k.startswith('color.'):
451 if k.startswith('color.'):
453 ui._styles[k] = k[6:]
452 ui._styles[k] = k[6:]
454 elif k.startswith('terminfo.'):
453 elif k.startswith('terminfo.'):
455 ui._styles[k] = k[9:]
454 ui._styles[k] = k[9:]
456 ui.write(_('available colors:\n'))
455 ui.write(_('available colors:\n'))
457 # sort label with a '_' after the other to group '_background' entry.
456 # sort label with a '_' after the other to group '_background' entry.
458 items = sorted(ui._styles.items(),
457 items = sorted(ui._styles.items(),
459 key=lambda i: ('_' in i[0], i[0], i[1]))
458 key=lambda i: ('_' in i[0], i[0], i[1]))
460 for colorname, label in items:
459 for colorname, label in items:
461 ui.write(('%s\n') % colorname, label=label)
460 ui.write(('%s\n') % colorname, label=label)
462
461
463 def _debugdisplaystyle(ui):
462 def _debugdisplaystyle(ui):
464 ui.write(_('available style:\n'))
463 ui.write(_('available style:\n'))
465 if not ui._styles:
464 if not ui._styles:
466 return
465 return
467 width = max(len(s) for s in ui._styles)
466 width = max(len(s) for s in ui._styles)
468 for label, effects in sorted(ui._styles.items()):
467 for label, effects in sorted(ui._styles.items()):
469 ui.write('%s' % label, label=label)
468 ui.write('%s' % label, label=label)
470 if effects:
469 if effects:
471 # 50
470 # 50
472 ui.write(': ')
471 ui.write(': ')
473 ui.write(' ' * (max(0, width - len(label))))
472 ui.write(' ' * (max(0, width - len(label))))
474 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
473 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
475 ui.write('\n')
474 ui.write('\n')
476
475
477 @command('debugcreatestreamclonebundle', [], 'FILE')
476 @command('debugcreatestreamclonebundle', [], 'FILE')
478 def debugcreatestreamclonebundle(ui, repo, fname):
477 def debugcreatestreamclonebundle(ui, repo, fname):
479 """create a stream clone bundle file
478 """create a stream clone bundle file
480
479
481 Stream bundles are special bundles that are essentially archives of
480 Stream bundles are special bundles that are essentially archives of
482 revlog files. They are commonly used for cloning very quickly.
481 revlog files. They are commonly used for cloning very quickly.
483 """
482 """
484 # TODO we may want to turn this into an abort when this functionality
483 # TODO we may want to turn this into an abort when this functionality
485 # is moved into `hg bundle`.
484 # is moved into `hg bundle`.
486 if phases.hassecret(repo):
485 if phases.hassecret(repo):
487 ui.warn(_('(warning: stream clone bundle will contain secret '
486 ui.warn(_('(warning: stream clone bundle will contain secret '
488 'revisions)\n'))
487 'revisions)\n'))
489
488
490 requirements, gen = streamclone.generatebundlev1(repo)
489 requirements, gen = streamclone.generatebundlev1(repo)
491 changegroup.writechunks(ui, gen, fname)
490 changegroup.writechunks(ui, gen, fname)
492
491
493 ui.write(_('bundle requirements: %s\n') % ', '.join(sorted(requirements)))
492 ui.write(_('bundle requirements: %s\n') % ', '.join(sorted(requirements)))
494
493
495 @command('debugdag',
494 @command('debugdag',
496 [('t', 'tags', None, _('use tags as labels')),
495 [('t', 'tags', None, _('use tags as labels')),
497 ('b', 'branches', None, _('annotate with branch names')),
496 ('b', 'branches', None, _('annotate with branch names')),
498 ('', 'dots', None, _('use dots for runs')),
497 ('', 'dots', None, _('use dots for runs')),
499 ('s', 'spaces', None, _('separate elements by spaces'))],
498 ('s', 'spaces', None, _('separate elements by spaces'))],
500 _('[OPTION]... [FILE [REV]...]'),
499 _('[OPTION]... [FILE [REV]...]'),
501 optionalrepo=True)
500 optionalrepo=True)
502 def debugdag(ui, repo, file_=None, *revs, **opts):
501 def debugdag(ui, repo, file_=None, *revs, **opts):
503 """format the changelog or an index DAG as a concise textual description
502 """format the changelog or an index DAG as a concise textual description
504
503
505 If you pass a revlog index, the revlog's DAG is emitted. If you list
504 If you pass a revlog index, the revlog's DAG is emitted. If you list
506 revision numbers, they get labeled in the output as rN.
505 revision numbers, they get labeled in the output as rN.
507
506
508 Otherwise, the changelog DAG of the current repo is emitted.
507 Otherwise, the changelog DAG of the current repo is emitted.
509 """
508 """
510 spaces = opts.get(r'spaces')
509 spaces = opts.get(r'spaces')
511 dots = opts.get(r'dots')
510 dots = opts.get(r'dots')
512 if file_:
511 if file_:
513 rlog = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
512 rlog = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
514 file_)
513 file_)
515 revs = set((int(r) for r in revs))
514 revs = set((int(r) for r in revs))
516 def events():
515 def events():
517 for r in rlog:
516 for r in rlog:
518 yield 'n', (r, list(p for p in rlog.parentrevs(r)
517 yield 'n', (r, list(p for p in rlog.parentrevs(r)
519 if p != -1))
518 if p != -1))
520 if r in revs:
519 if r in revs:
521 yield 'l', (r, "r%i" % r)
520 yield 'l', (r, "r%i" % r)
522 elif repo:
521 elif repo:
523 cl = repo.changelog
522 cl = repo.changelog
524 tags = opts.get(r'tags')
523 tags = opts.get(r'tags')
525 branches = opts.get(r'branches')
524 branches = opts.get(r'branches')
526 if tags:
525 if tags:
527 labels = {}
526 labels = {}
528 for l, n in repo.tags().items():
527 for l, n in repo.tags().items():
529 labels.setdefault(cl.rev(n), []).append(l)
528 labels.setdefault(cl.rev(n), []).append(l)
530 def events():
529 def events():
531 b = "default"
530 b = "default"
532 for r in cl:
531 for r in cl:
533 if branches:
532 if branches:
534 newb = cl.read(cl.node(r))[5]['branch']
533 newb = cl.read(cl.node(r))[5]['branch']
535 if newb != b:
534 if newb != b:
536 yield 'a', newb
535 yield 'a', newb
537 b = newb
536 b = newb
538 yield 'n', (r, list(p for p in cl.parentrevs(r)
537 yield 'n', (r, list(p for p in cl.parentrevs(r)
539 if p != -1))
538 if p != -1))
540 if tags:
539 if tags:
541 ls = labels.get(r)
540 ls = labels.get(r)
542 if ls:
541 if ls:
543 for l in ls:
542 for l in ls:
544 yield 'l', (r, l)
543 yield 'l', (r, l)
545 else:
544 else:
546 raise error.Abort(_('need repo for changelog dag'))
545 raise error.Abort(_('need repo for changelog dag'))
547
546
548 for line in dagparser.dagtextlines(events(),
547 for line in dagparser.dagtextlines(events(),
549 addspaces=spaces,
548 addspaces=spaces,
550 wraplabels=True,
549 wraplabels=True,
551 wrapannotations=True,
550 wrapannotations=True,
552 wrapnonlinear=dots,
551 wrapnonlinear=dots,
553 usedots=dots,
552 usedots=dots,
554 maxlinewidth=70):
553 maxlinewidth=70):
555 ui.write(line)
554 ui.write(line)
556 ui.write("\n")
555 ui.write("\n")
557
556
558 @command('debugdata', cmdutil.debugrevlogopts, _('-c|-m|FILE REV'))
557 @command('debugdata', cmdutil.debugrevlogopts, _('-c|-m|FILE REV'))
559 def debugdata(ui, repo, file_, rev=None, **opts):
558 def debugdata(ui, repo, file_, rev=None, **opts):
560 """dump the contents of a data file revision"""
559 """dump the contents of a data file revision"""
561 opts = pycompat.byteskwargs(opts)
560 opts = pycompat.byteskwargs(opts)
562 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
561 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
563 if rev is not None:
562 if rev is not None:
564 raise error.CommandError('debugdata', _('invalid arguments'))
563 raise error.CommandError('debugdata', _('invalid arguments'))
565 file_, rev = None, file_
564 file_, rev = None, file_
566 elif rev is None:
565 elif rev is None:
567 raise error.CommandError('debugdata', _('invalid arguments'))
566 raise error.CommandError('debugdata', _('invalid arguments'))
568 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
567 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
569 try:
568 try:
570 ui.write(r.revision(r.lookup(rev), raw=True))
569 ui.write(r.revision(r.lookup(rev), raw=True))
571 except KeyError:
570 except KeyError:
572 raise error.Abort(_('invalid revision identifier %s') % rev)
571 raise error.Abort(_('invalid revision identifier %s') % rev)
573
572
574 @command('debugdate',
573 @command('debugdate',
575 [('e', 'extended', None, _('try extended date formats'))],
574 [('e', 'extended', None, _('try extended date formats'))],
576 _('[-e] DATE [RANGE]'),
575 _('[-e] DATE [RANGE]'),
577 norepo=True, optionalrepo=True)
576 norepo=True, optionalrepo=True)
578 def debugdate(ui, date, range=None, **opts):
577 def debugdate(ui, date, range=None, **opts):
579 """parse and display a date"""
578 """parse and display a date"""
580 if opts[r"extended"]:
579 if opts[r"extended"]:
581 d = dateutil.parsedate(date, util.extendeddateformats)
580 d = dateutil.parsedate(date, util.extendeddateformats)
582 else:
581 else:
583 d = dateutil.parsedate(date)
582 d = dateutil.parsedate(date)
584 ui.write(("internal: %d %d\n") % d)
583 ui.write(("internal: %d %d\n") % d)
585 ui.write(("standard: %s\n") % dateutil.datestr(d))
584 ui.write(("standard: %s\n") % dateutil.datestr(d))
586 if range:
585 if range:
587 m = dateutil.matchdate(range)
586 m = dateutil.matchdate(range)
588 ui.write(("match: %s\n") % m(d[0]))
587 ui.write(("match: %s\n") % m(d[0]))
589
588
590 @command('debugdeltachain',
589 @command('debugdeltachain',
591 cmdutil.debugrevlogopts + cmdutil.formatteropts,
590 cmdutil.debugrevlogopts + cmdutil.formatteropts,
592 _('-c|-m|FILE'),
591 _('-c|-m|FILE'),
593 optionalrepo=True)
592 optionalrepo=True)
594 def debugdeltachain(ui, repo, file_=None, **opts):
593 def debugdeltachain(ui, repo, file_=None, **opts):
595 """dump information about delta chains in a revlog
594 """dump information about delta chains in a revlog
596
595
597 Output can be templatized. Available template keywords are:
596 Output can be templatized. Available template keywords are:
598
597
599 :``rev``: revision number
598 :``rev``: revision number
600 :``chainid``: delta chain identifier (numbered by unique base)
599 :``chainid``: delta chain identifier (numbered by unique base)
601 :``chainlen``: delta chain length to this revision
600 :``chainlen``: delta chain length to this revision
602 :``prevrev``: previous revision in delta chain
601 :``prevrev``: previous revision in delta chain
603 :``deltatype``: role of delta / how it was computed
602 :``deltatype``: role of delta / how it was computed
604 :``compsize``: compressed size of revision
603 :``compsize``: compressed size of revision
605 :``uncompsize``: uncompressed size of revision
604 :``uncompsize``: uncompressed size of revision
606 :``chainsize``: total size of compressed revisions in chain
605 :``chainsize``: total size of compressed revisions in chain
607 :``chainratio``: total chain size divided by uncompressed revision size
606 :``chainratio``: total chain size divided by uncompressed revision size
608 (new delta chains typically start at ratio 2.00)
607 (new delta chains typically start at ratio 2.00)
609 :``lindist``: linear distance from base revision in delta chain to end
608 :``lindist``: linear distance from base revision in delta chain to end
610 of this revision
609 of this revision
611 :``extradist``: total size of revisions not part of this delta chain from
610 :``extradist``: total size of revisions not part of this delta chain from
612 base of delta chain to end of this revision; a measurement
611 base of delta chain to end of this revision; a measurement
613 of how much extra data we need to read/seek across to read
612 of how much extra data we need to read/seek across to read
614 the delta chain for this revision
613 the delta chain for this revision
615 :``extraratio``: extradist divided by chainsize; another representation of
614 :``extraratio``: extradist divided by chainsize; another representation of
616 how much unrelated data is needed to load this delta chain
615 how much unrelated data is needed to load this delta chain
617
616
618 If the repository is configured to use the sparse read, additional keywords
617 If the repository is configured to use the sparse read, additional keywords
619 are available:
618 are available:
620
619
621 :``readsize``: total size of data read from the disk for a revision
620 :``readsize``: total size of data read from the disk for a revision
622 (sum of the sizes of all the blocks)
621 (sum of the sizes of all the blocks)
623 :``largestblock``: size of the largest block of data read from the disk
622 :``largestblock``: size of the largest block of data read from the disk
624 :``readdensity``: density of useful bytes in the data read from the disk
623 :``readdensity``: density of useful bytes in the data read from the disk
625 :``srchunks``: in how many data hunks the whole revision would be read
624 :``srchunks``: in how many data hunks the whole revision would be read
626
625
627 The sparse read can be enabled with experimental.sparse-read = True
626 The sparse read can be enabled with experimental.sparse-read = True
628 """
627 """
629 opts = pycompat.byteskwargs(opts)
628 opts = pycompat.byteskwargs(opts)
630 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
629 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
631 index = r.index
630 index = r.index
632 start = r.start
631 start = r.start
633 length = r.length
632 length = r.length
634 generaldelta = r.version & revlog.FLAG_GENERALDELTA
633 generaldelta = r.version & revlog.FLAG_GENERALDELTA
635 withsparseread = getattr(r, '_withsparseread', False)
634 withsparseread = getattr(r, '_withsparseread', False)
636
635
637 def revinfo(rev):
636 def revinfo(rev):
638 e = index[rev]
637 e = index[rev]
639 compsize = e[1]
638 compsize = e[1]
640 uncompsize = e[2]
639 uncompsize = e[2]
641 chainsize = 0
640 chainsize = 0
642
641
643 if generaldelta:
642 if generaldelta:
644 if e[3] == e[5]:
643 if e[3] == e[5]:
645 deltatype = 'p1'
644 deltatype = 'p1'
646 elif e[3] == e[6]:
645 elif e[3] == e[6]:
647 deltatype = 'p2'
646 deltatype = 'p2'
648 elif e[3] == rev - 1:
647 elif e[3] == rev - 1:
649 deltatype = 'prev'
648 deltatype = 'prev'
650 elif e[3] == rev:
649 elif e[3] == rev:
651 deltatype = 'base'
650 deltatype = 'base'
652 else:
651 else:
653 deltatype = 'other'
652 deltatype = 'other'
654 else:
653 else:
655 if e[3] == rev:
654 if e[3] == rev:
656 deltatype = 'base'
655 deltatype = 'base'
657 else:
656 else:
658 deltatype = 'prev'
657 deltatype = 'prev'
659
658
660 chain = r._deltachain(rev)[0]
659 chain = r._deltachain(rev)[0]
661 for iterrev in chain:
660 for iterrev in chain:
662 e = index[iterrev]
661 e = index[iterrev]
663 chainsize += e[1]
662 chainsize += e[1]
664
663
665 return compsize, uncompsize, deltatype, chain, chainsize
664 return compsize, uncompsize, deltatype, chain, chainsize
666
665
667 fm = ui.formatter('debugdeltachain', opts)
666 fm = ui.formatter('debugdeltachain', opts)
668
667
669 fm.plain(' rev chain# chainlen prev delta '
668 fm.plain(' rev chain# chainlen prev delta '
670 'size rawsize chainsize ratio lindist extradist '
669 'size rawsize chainsize ratio lindist extradist '
671 'extraratio')
670 'extraratio')
672 if withsparseread:
671 if withsparseread:
673 fm.plain(' readsize largestblk rddensity srchunks')
672 fm.plain(' readsize largestblk rddensity srchunks')
674 fm.plain('\n')
673 fm.plain('\n')
675
674
676 chainbases = {}
675 chainbases = {}
677 for rev in r:
676 for rev in r:
678 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
677 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
679 chainbase = chain[0]
678 chainbase = chain[0]
680 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
679 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
681 basestart = start(chainbase)
680 basestart = start(chainbase)
682 revstart = start(rev)
681 revstart = start(rev)
683 lineardist = revstart + comp - basestart
682 lineardist = revstart + comp - basestart
684 extradist = lineardist - chainsize
683 extradist = lineardist - chainsize
685 try:
684 try:
686 prevrev = chain[-2]
685 prevrev = chain[-2]
687 except IndexError:
686 except IndexError:
688 prevrev = -1
687 prevrev = -1
689
688
690 chainratio = float(chainsize) / float(uncomp)
689 chainratio = float(chainsize) / float(uncomp)
691 extraratio = float(extradist) / float(chainsize)
690 extraratio = float(extradist) / float(chainsize)
692
691
693 fm.startitem()
692 fm.startitem()
694 fm.write('rev chainid chainlen prevrev deltatype compsize '
693 fm.write('rev chainid chainlen prevrev deltatype compsize '
695 'uncompsize chainsize chainratio lindist extradist '
694 'uncompsize chainsize chainratio lindist extradist '
696 'extraratio',
695 'extraratio',
697 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f',
696 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f',
698 rev, chainid, len(chain), prevrev, deltatype, comp,
697 rev, chainid, len(chain), prevrev, deltatype, comp,
699 uncomp, chainsize, chainratio, lineardist, extradist,
698 uncomp, chainsize, chainratio, lineardist, extradist,
700 extraratio,
699 extraratio,
701 rev=rev, chainid=chainid, chainlen=len(chain),
700 rev=rev, chainid=chainid, chainlen=len(chain),
702 prevrev=prevrev, deltatype=deltatype, compsize=comp,
701 prevrev=prevrev, deltatype=deltatype, compsize=comp,
703 uncompsize=uncomp, chainsize=chainsize,
702 uncompsize=uncomp, chainsize=chainsize,
704 chainratio=chainratio, lindist=lineardist,
703 chainratio=chainratio, lindist=lineardist,
705 extradist=extradist, extraratio=extraratio)
704 extradist=extradist, extraratio=extraratio)
706 if withsparseread:
705 if withsparseread:
707 readsize = 0
706 readsize = 0
708 largestblock = 0
707 largestblock = 0
709 srchunks = 0
708 srchunks = 0
710
709
711 for revschunk in revlog._slicechunk(r, chain):
710 for revschunk in revlog._slicechunk(r, chain):
712 srchunks += 1
711 srchunks += 1
713 blkend = start(revschunk[-1]) + length(revschunk[-1])
712 blkend = start(revschunk[-1]) + length(revschunk[-1])
714 blksize = blkend - start(revschunk[0])
713 blksize = blkend - start(revschunk[0])
715
714
716 readsize += blksize
715 readsize += blksize
717 if largestblock < blksize:
716 if largestblock < blksize:
718 largestblock = blksize
717 largestblock = blksize
719
718
720 readdensity = float(chainsize) / float(readsize)
719 readdensity = float(chainsize) / float(readsize)
721
720
722 fm.write('readsize largestblock readdensity srchunks',
721 fm.write('readsize largestblock readdensity srchunks',
723 ' %10d %10d %9.5f %8d',
722 ' %10d %10d %9.5f %8d',
724 readsize, largestblock, readdensity, srchunks,
723 readsize, largestblock, readdensity, srchunks,
725 readsize=readsize, largestblock=largestblock,
724 readsize=readsize, largestblock=largestblock,
726 readdensity=readdensity, srchunks=srchunks)
725 readdensity=readdensity, srchunks=srchunks)
727
726
728 fm.plain('\n')
727 fm.plain('\n')
729
728
730 fm.end()
729 fm.end()
731
730
732 @command('debugdirstate|debugstate',
731 @command('debugdirstate|debugstate',
733 [('', 'nodates', None, _('do not display the saved mtime')),
732 [('', 'nodates', None, _('do not display the saved mtime')),
734 ('', 'datesort', None, _('sort by saved mtime'))],
733 ('', 'datesort', None, _('sort by saved mtime'))],
735 _('[OPTION]...'))
734 _('[OPTION]...'))
736 def debugstate(ui, repo, **opts):
735 def debugstate(ui, repo, **opts):
737 """show the contents of the current dirstate"""
736 """show the contents of the current dirstate"""
738
737
739 nodates = opts.get(r'nodates')
738 nodates = opts.get(r'nodates')
740 datesort = opts.get(r'datesort')
739 datesort = opts.get(r'datesort')
741
740
742 timestr = ""
741 timestr = ""
743 if datesort:
742 if datesort:
744 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
743 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
745 else:
744 else:
746 keyfunc = None # sort by filename
745 keyfunc = None # sort by filename
747 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
746 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
748 if ent[3] == -1:
747 if ent[3] == -1:
749 timestr = 'unset '
748 timestr = 'unset '
750 elif nodates:
749 elif nodates:
751 timestr = 'set '
750 timestr = 'set '
752 else:
751 else:
753 timestr = time.strftime(r"%Y-%m-%d %H:%M:%S ",
752 timestr = time.strftime(r"%Y-%m-%d %H:%M:%S ",
754 time.localtime(ent[3]))
753 time.localtime(ent[3]))
755 timestr = encoding.strtolocal(timestr)
754 timestr = encoding.strtolocal(timestr)
756 if ent[1] & 0o20000:
755 if ent[1] & 0o20000:
757 mode = 'lnk'
756 mode = 'lnk'
758 else:
757 else:
759 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
758 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
760 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
759 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
761 for f in repo.dirstate.copies():
760 for f in repo.dirstate.copies():
762 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
761 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
763
762
764 @command('debugdiscovery',
763 @command('debugdiscovery',
765 [('', 'old', None, _('use old-style discovery')),
764 [('', 'old', None, _('use old-style discovery')),
766 ('', 'nonheads', None,
765 ('', 'nonheads', None,
767 _('use old-style discovery with non-heads included')),
766 _('use old-style discovery with non-heads included')),
768 ('', 'rev', [], 'restrict discovery to this set of revs'),
767 ('', 'rev', [], 'restrict discovery to this set of revs'),
769 ] + cmdutil.remoteopts,
768 ] + cmdutil.remoteopts,
770 _('[--rev REV] [OTHER]'))
769 _('[--rev REV] [OTHER]'))
771 def debugdiscovery(ui, repo, remoteurl="default", **opts):
770 def debugdiscovery(ui, repo, remoteurl="default", **opts):
772 """runs the changeset discovery protocol in isolation"""
771 """runs the changeset discovery protocol in isolation"""
773 opts = pycompat.byteskwargs(opts)
772 opts = pycompat.byteskwargs(opts)
774 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl))
773 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl))
775 remote = hg.peer(repo, opts, remoteurl)
774 remote = hg.peer(repo, opts, remoteurl)
776 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
775 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
777
776
778 # make sure tests are repeatable
777 # make sure tests are repeatable
779 random.seed(12323)
778 random.seed(12323)
780
779
781 def doit(pushedrevs, remoteheads, remote=remote):
780 def doit(pushedrevs, remoteheads, remote=remote):
782 if opts.get('old'):
781 if opts.get('old'):
783 if not util.safehasattr(remote, 'branches'):
782 if not util.safehasattr(remote, 'branches'):
784 # enable in-client legacy support
783 # enable in-client legacy support
785 remote = localrepo.locallegacypeer(remote.local())
784 remote = localrepo.locallegacypeer(remote.local())
786 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
785 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
787 force=True)
786 force=True)
788 common = set(common)
787 common = set(common)
789 if not opts.get('nonheads'):
788 if not opts.get('nonheads'):
790 ui.write(("unpruned common: %s\n") %
789 ui.write(("unpruned common: %s\n") %
791 " ".join(sorted(short(n) for n in common)))
790 " ".join(sorted(short(n) for n in common)))
792 dag = dagutil.revlogdag(repo.changelog)
791 dag = dagutil.revlogdag(repo.changelog)
793 all = dag.ancestorset(dag.internalizeall(common))
792 all = dag.ancestorset(dag.internalizeall(common))
794 common = dag.externalizeall(dag.headsetofconnecteds(all))
793 common = dag.externalizeall(dag.headsetofconnecteds(all))
795 else:
794 else:
796 nodes = None
795 nodes = None
797 if pushedrevs:
796 if pushedrevs:
798 revs = scmutil.revrange(repo, pushedrevs)
797 revs = scmutil.revrange(repo, pushedrevs)
799 nodes = [repo[r].node() for r in revs]
798 nodes = [repo[r].node() for r in revs]
800 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote,
799 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote,
801 ancestorsof=nodes)
800 ancestorsof=nodes)
802 common = set(common)
801 common = set(common)
803 rheads = set(hds)
802 rheads = set(hds)
804 lheads = set(repo.heads())
803 lheads = set(repo.heads())
805 ui.write(("common heads: %s\n") %
804 ui.write(("common heads: %s\n") %
806 " ".join(sorted(short(n) for n in common)))
805 " ".join(sorted(short(n) for n in common)))
807 if lheads <= common:
806 if lheads <= common:
808 ui.write(("local is subset\n"))
807 ui.write(("local is subset\n"))
809 elif rheads <= common:
808 elif rheads <= common:
810 ui.write(("remote is subset\n"))
809 ui.write(("remote is subset\n"))
811
810
812 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches, revs=None)
811 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches, revs=None)
813 localrevs = opts['rev']
812 localrevs = opts['rev']
814 doit(localrevs, remoterevs)
813 doit(localrevs, remoterevs)
815
814
816 _chunksize = 4 << 10
815 _chunksize = 4 << 10
817
816
818 @command('debugdownload',
817 @command('debugdownload',
819 [
818 [
820 ('o', 'output', '', _('path')),
819 ('o', 'output', '', _('path')),
821 ],
820 ],
822 optionalrepo=True)
821 optionalrepo=True)
823 def debugdownload(ui, repo, url, output=None, **opts):
822 def debugdownload(ui, repo, url, output=None, **opts):
824 """download a resource using Mercurial logic and config
823 """download a resource using Mercurial logic and config
825 """
824 """
826 fh = urlmod.open(ui, url, output)
825 fh = urlmod.open(ui, url, output)
827
826
828 dest = ui
827 dest = ui
829 if output:
828 if output:
830 dest = open(output, "wb", _chunksize)
829 dest = open(output, "wb", _chunksize)
831 try:
830 try:
832 data = fh.read(_chunksize)
831 data = fh.read(_chunksize)
833 while data:
832 while data:
834 dest.write(data)
833 dest.write(data)
835 data = fh.read(_chunksize)
834 data = fh.read(_chunksize)
836 finally:
835 finally:
837 if output:
836 if output:
838 dest.close()
837 dest.close()
839
838
840 @command('debugextensions', cmdutil.formatteropts, [], optionalrepo=True)
839 @command('debugextensions', cmdutil.formatteropts, [], optionalrepo=True)
841 def debugextensions(ui, repo, **opts):
840 def debugextensions(ui, repo, **opts):
842 '''show information about active extensions'''
841 '''show information about active extensions'''
843 opts = pycompat.byteskwargs(opts)
842 opts = pycompat.byteskwargs(opts)
844 exts = extensions.extensions(ui)
843 exts = extensions.extensions(ui)
845 hgver = util.version()
844 hgver = util.version()
846 fm = ui.formatter('debugextensions', opts)
845 fm = ui.formatter('debugextensions', opts)
847 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
846 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
848 isinternal = extensions.ismoduleinternal(extmod)
847 isinternal = extensions.ismoduleinternal(extmod)
849 extsource = pycompat.fsencode(extmod.__file__)
848 extsource = pycompat.fsencode(extmod.__file__)
850 if isinternal:
849 if isinternal:
851 exttestedwith = [] # never expose magic string to users
850 exttestedwith = [] # never expose magic string to users
852 else:
851 else:
853 exttestedwith = getattr(extmod, 'testedwith', '').split()
852 exttestedwith = getattr(extmod, 'testedwith', '').split()
854 extbuglink = getattr(extmod, 'buglink', None)
853 extbuglink = getattr(extmod, 'buglink', None)
855
854
856 fm.startitem()
855 fm.startitem()
857
856
858 if ui.quiet or ui.verbose:
857 if ui.quiet or ui.verbose:
859 fm.write('name', '%s\n', extname)
858 fm.write('name', '%s\n', extname)
860 else:
859 else:
861 fm.write('name', '%s', extname)
860 fm.write('name', '%s', extname)
862 if isinternal or hgver in exttestedwith:
861 if isinternal or hgver in exttestedwith:
863 fm.plain('\n')
862 fm.plain('\n')
864 elif not exttestedwith:
863 elif not exttestedwith:
865 fm.plain(_(' (untested!)\n'))
864 fm.plain(_(' (untested!)\n'))
866 else:
865 else:
867 lasttestedversion = exttestedwith[-1]
866 lasttestedversion = exttestedwith[-1]
868 fm.plain(' (%s!)\n' % lasttestedversion)
867 fm.plain(' (%s!)\n' % lasttestedversion)
869
868
870 fm.condwrite(ui.verbose and extsource, 'source',
869 fm.condwrite(ui.verbose and extsource, 'source',
871 _(' location: %s\n'), extsource or "")
870 _(' location: %s\n'), extsource or "")
872
871
873 if ui.verbose:
872 if ui.verbose:
874 fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal])
873 fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal])
875 fm.data(bundled=isinternal)
874 fm.data(bundled=isinternal)
876
875
877 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
876 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
878 _(' tested with: %s\n'),
877 _(' tested with: %s\n'),
879 fm.formatlist(exttestedwith, name='ver'))
878 fm.formatlist(exttestedwith, name='ver'))
880
879
881 fm.condwrite(ui.verbose and extbuglink, 'buglink',
880 fm.condwrite(ui.verbose and extbuglink, 'buglink',
882 _(' bug reporting: %s\n'), extbuglink or "")
881 _(' bug reporting: %s\n'), extbuglink or "")
883
882
884 fm.end()
883 fm.end()
885
884
886 @command('debugfileset',
885 @command('debugfileset',
887 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
886 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
888 _('[-r REV] FILESPEC'))
887 _('[-r REV] FILESPEC'))
889 def debugfileset(ui, repo, expr, **opts):
888 def debugfileset(ui, repo, expr, **opts):
890 '''parse and apply a fileset specification'''
889 '''parse and apply a fileset specification'''
891 ctx = scmutil.revsingle(repo, opts.get(r'rev'), None)
890 ctx = scmutil.revsingle(repo, opts.get(r'rev'), None)
892 if ui.verbose:
891 if ui.verbose:
893 tree = fileset.parse(expr)
892 tree = fileset.parse(expr)
894 ui.note(fileset.prettyformat(tree), "\n")
893 ui.note(fileset.prettyformat(tree), "\n")
895
894
896 for f in ctx.getfileset(expr):
895 for f in ctx.getfileset(expr):
897 ui.write("%s\n" % f)
896 ui.write("%s\n" % f)
898
897
899 @command('debugformat',
898 @command('debugformat',
900 [] + cmdutil.formatteropts,
899 [] + cmdutil.formatteropts,
901 _(''))
900 _(''))
902 def debugformat(ui, repo, **opts):
901 def debugformat(ui, repo, **opts):
903 """display format information about the current repository
902 """display format information about the current repository
904
903
905 Use --verbose to get extra information about current config value and
904 Use --verbose to get extra information about current config value and
906 Mercurial default."""
905 Mercurial default."""
907 opts = pycompat.byteskwargs(opts)
906 opts = pycompat.byteskwargs(opts)
908 maxvariantlength = max(len(fv.name) for fv in upgrade.allformatvariant)
907 maxvariantlength = max(len(fv.name) for fv in upgrade.allformatvariant)
909 maxvariantlength = max(len('format-variant'), maxvariantlength)
908 maxvariantlength = max(len('format-variant'), maxvariantlength)
910
909
911 def makeformatname(name):
910 def makeformatname(name):
912 return '%s:' + (' ' * (maxvariantlength - len(name)))
911 return '%s:' + (' ' * (maxvariantlength - len(name)))
913
912
914 fm = ui.formatter('debugformat', opts)
913 fm = ui.formatter('debugformat', opts)
915 if fm.isplain():
914 if fm.isplain():
916 def formatvalue(value):
915 def formatvalue(value):
917 if util.safehasattr(value, 'startswith'):
916 if util.safehasattr(value, 'startswith'):
918 return value
917 return value
919 if value:
918 if value:
920 return 'yes'
919 return 'yes'
921 else:
920 else:
922 return 'no'
921 return 'no'
923 else:
922 else:
924 formatvalue = pycompat.identity
923 formatvalue = pycompat.identity
925
924
926 fm.plain('format-variant')
925 fm.plain('format-variant')
927 fm.plain(' ' * (maxvariantlength - len('format-variant')))
926 fm.plain(' ' * (maxvariantlength - len('format-variant')))
928 fm.plain(' repo')
927 fm.plain(' repo')
929 if ui.verbose:
928 if ui.verbose:
930 fm.plain(' config default')
929 fm.plain(' config default')
931 fm.plain('\n')
930 fm.plain('\n')
932 for fv in upgrade.allformatvariant:
931 for fv in upgrade.allformatvariant:
933 fm.startitem()
932 fm.startitem()
934 repovalue = fv.fromrepo(repo)
933 repovalue = fv.fromrepo(repo)
935 configvalue = fv.fromconfig(repo)
934 configvalue = fv.fromconfig(repo)
936
935
937 if repovalue != configvalue:
936 if repovalue != configvalue:
938 namelabel = 'formatvariant.name.mismatchconfig'
937 namelabel = 'formatvariant.name.mismatchconfig'
939 repolabel = 'formatvariant.repo.mismatchconfig'
938 repolabel = 'formatvariant.repo.mismatchconfig'
940 elif repovalue != fv.default:
939 elif repovalue != fv.default:
941 namelabel = 'formatvariant.name.mismatchdefault'
940 namelabel = 'formatvariant.name.mismatchdefault'
942 repolabel = 'formatvariant.repo.mismatchdefault'
941 repolabel = 'formatvariant.repo.mismatchdefault'
943 else:
942 else:
944 namelabel = 'formatvariant.name.uptodate'
943 namelabel = 'formatvariant.name.uptodate'
945 repolabel = 'formatvariant.repo.uptodate'
944 repolabel = 'formatvariant.repo.uptodate'
946
945
947 fm.write('name', makeformatname(fv.name), fv.name,
946 fm.write('name', makeformatname(fv.name), fv.name,
948 label=namelabel)
947 label=namelabel)
949 fm.write('repo', ' %3s', formatvalue(repovalue),
948 fm.write('repo', ' %3s', formatvalue(repovalue),
950 label=repolabel)
949 label=repolabel)
951 if fv.default != configvalue:
950 if fv.default != configvalue:
952 configlabel = 'formatvariant.config.special'
951 configlabel = 'formatvariant.config.special'
953 else:
952 else:
954 configlabel = 'formatvariant.config.default'
953 configlabel = 'formatvariant.config.default'
955 fm.condwrite(ui.verbose, 'config', ' %6s', formatvalue(configvalue),
954 fm.condwrite(ui.verbose, 'config', ' %6s', formatvalue(configvalue),
956 label=configlabel)
955 label=configlabel)
957 fm.condwrite(ui.verbose, 'default', ' %7s', formatvalue(fv.default),
956 fm.condwrite(ui.verbose, 'default', ' %7s', formatvalue(fv.default),
958 label='formatvariant.default')
957 label='formatvariant.default')
959 fm.plain('\n')
958 fm.plain('\n')
960 fm.end()
959 fm.end()
961
960
962 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
961 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
963 def debugfsinfo(ui, path="."):
962 def debugfsinfo(ui, path="."):
964 """show information detected about current filesystem"""
963 """show information detected about current filesystem"""
965 ui.write(('path: %s\n') % path)
964 ui.write(('path: %s\n') % path)
966 ui.write(('mounted on: %s\n') % (util.getfsmountpoint(path) or '(unknown)'))
965 ui.write(('mounted on: %s\n') % (util.getfsmountpoint(path) or '(unknown)'))
967 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
966 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
968 ui.write(('fstype: %s\n') % (util.getfstype(path) or '(unknown)'))
967 ui.write(('fstype: %s\n') % (util.getfstype(path) or '(unknown)'))
969 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
968 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
970 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
969 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
971 casesensitive = '(unknown)'
970 casesensitive = '(unknown)'
972 try:
971 try:
973 with tempfile.NamedTemporaryFile(prefix='.debugfsinfo', dir=path) as f:
972 with pycompat.namedtempfile(prefix='.debugfsinfo', dir=path) as f:
974 casesensitive = util.fscasesensitive(f.name) and 'yes' or 'no'
973 casesensitive = util.fscasesensitive(f.name) and 'yes' or 'no'
975 except OSError:
974 except OSError:
976 pass
975 pass
977 ui.write(('case-sensitive: %s\n') % casesensitive)
976 ui.write(('case-sensitive: %s\n') % casesensitive)
978
977
979 @command('debuggetbundle',
978 @command('debuggetbundle',
980 [('H', 'head', [], _('id of head node'), _('ID')),
979 [('H', 'head', [], _('id of head node'), _('ID')),
981 ('C', 'common', [], _('id of common node'), _('ID')),
980 ('C', 'common', [], _('id of common node'), _('ID')),
982 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
981 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
983 _('REPO FILE [-H|-C ID]...'),
982 _('REPO FILE [-H|-C ID]...'),
984 norepo=True)
983 norepo=True)
985 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
984 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
986 """retrieves a bundle from a repo
985 """retrieves a bundle from a repo
987
986
988 Every ID must be a full-length hex node id string. Saves the bundle to the
987 Every ID must be a full-length hex node id string. Saves the bundle to the
989 given file.
988 given file.
990 """
989 """
991 opts = pycompat.byteskwargs(opts)
990 opts = pycompat.byteskwargs(opts)
992 repo = hg.peer(ui, opts, repopath)
991 repo = hg.peer(ui, opts, repopath)
993 if not repo.capable('getbundle'):
992 if not repo.capable('getbundle'):
994 raise error.Abort("getbundle() not supported by target repository")
993 raise error.Abort("getbundle() not supported by target repository")
995 args = {}
994 args = {}
996 if common:
995 if common:
997 args[r'common'] = [bin(s) for s in common]
996 args[r'common'] = [bin(s) for s in common]
998 if head:
997 if head:
999 args[r'heads'] = [bin(s) for s in head]
998 args[r'heads'] = [bin(s) for s in head]
1000 # TODO: get desired bundlecaps from command line.
999 # TODO: get desired bundlecaps from command line.
1001 args[r'bundlecaps'] = None
1000 args[r'bundlecaps'] = None
1002 bundle = repo.getbundle('debug', **args)
1001 bundle = repo.getbundle('debug', **args)
1003
1002
1004 bundletype = opts.get('type', 'bzip2').lower()
1003 bundletype = opts.get('type', 'bzip2').lower()
1005 btypes = {'none': 'HG10UN',
1004 btypes = {'none': 'HG10UN',
1006 'bzip2': 'HG10BZ',
1005 'bzip2': 'HG10BZ',
1007 'gzip': 'HG10GZ',
1006 'gzip': 'HG10GZ',
1008 'bundle2': 'HG20'}
1007 'bundle2': 'HG20'}
1009 bundletype = btypes.get(bundletype)
1008 bundletype = btypes.get(bundletype)
1010 if bundletype not in bundle2.bundletypes:
1009 if bundletype not in bundle2.bundletypes:
1011 raise error.Abort(_('unknown bundle type specified with --type'))
1010 raise error.Abort(_('unknown bundle type specified with --type'))
1012 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
1011 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
1013
1012
1014 @command('debugignore', [], '[FILE]')
1013 @command('debugignore', [], '[FILE]')
1015 def debugignore(ui, repo, *files, **opts):
1014 def debugignore(ui, repo, *files, **opts):
1016 """display the combined ignore pattern and information about ignored files
1015 """display the combined ignore pattern and information about ignored files
1017
1016
1018 With no argument display the combined ignore pattern.
1017 With no argument display the combined ignore pattern.
1019
1018
1020 Given space separated file names, shows if the given file is ignored and
1019 Given space separated file names, shows if the given file is ignored and
1021 if so, show the ignore rule (file and line number) that matched it.
1020 if so, show the ignore rule (file and line number) that matched it.
1022 """
1021 """
1023 ignore = repo.dirstate._ignore
1022 ignore = repo.dirstate._ignore
1024 if not files:
1023 if not files:
1025 # Show all the patterns
1024 # Show all the patterns
1026 ui.write("%s\n" % pycompat.byterepr(ignore))
1025 ui.write("%s\n" % pycompat.byterepr(ignore))
1027 else:
1026 else:
1028 m = scmutil.match(repo[None], pats=files)
1027 m = scmutil.match(repo[None], pats=files)
1029 for f in m.files():
1028 for f in m.files():
1030 nf = util.normpath(f)
1029 nf = util.normpath(f)
1031 ignored = None
1030 ignored = None
1032 ignoredata = None
1031 ignoredata = None
1033 if nf != '.':
1032 if nf != '.':
1034 if ignore(nf):
1033 if ignore(nf):
1035 ignored = nf
1034 ignored = nf
1036 ignoredata = repo.dirstate._ignorefileandline(nf)
1035 ignoredata = repo.dirstate._ignorefileandline(nf)
1037 else:
1036 else:
1038 for p in util.finddirs(nf):
1037 for p in util.finddirs(nf):
1039 if ignore(p):
1038 if ignore(p):
1040 ignored = p
1039 ignored = p
1041 ignoredata = repo.dirstate._ignorefileandline(p)
1040 ignoredata = repo.dirstate._ignorefileandline(p)
1042 break
1041 break
1043 if ignored:
1042 if ignored:
1044 if ignored == nf:
1043 if ignored == nf:
1045 ui.write(_("%s is ignored\n") % m.uipath(f))
1044 ui.write(_("%s is ignored\n") % m.uipath(f))
1046 else:
1045 else:
1047 ui.write(_("%s is ignored because of "
1046 ui.write(_("%s is ignored because of "
1048 "containing folder %s\n")
1047 "containing folder %s\n")
1049 % (m.uipath(f), ignored))
1048 % (m.uipath(f), ignored))
1050 ignorefile, lineno, line = ignoredata
1049 ignorefile, lineno, line = ignoredata
1051 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
1050 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
1052 % (ignorefile, lineno, line))
1051 % (ignorefile, lineno, line))
1053 else:
1052 else:
1054 ui.write(_("%s is not ignored\n") % m.uipath(f))
1053 ui.write(_("%s is not ignored\n") % m.uipath(f))
1055
1054
1056 @command('debugindex', cmdutil.debugrevlogopts +
1055 @command('debugindex', cmdutil.debugrevlogopts +
1057 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1056 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1058 _('[-f FORMAT] -c|-m|FILE'),
1057 _('[-f FORMAT] -c|-m|FILE'),
1059 optionalrepo=True)
1058 optionalrepo=True)
1060 def debugindex(ui, repo, file_=None, **opts):
1059 def debugindex(ui, repo, file_=None, **opts):
1061 """dump the contents of an index file"""
1060 """dump the contents of an index file"""
1062 opts = pycompat.byteskwargs(opts)
1061 opts = pycompat.byteskwargs(opts)
1063 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1062 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1064 format = opts.get('format', 0)
1063 format = opts.get('format', 0)
1065 if format not in (0, 1):
1064 if format not in (0, 1):
1066 raise error.Abort(_("unknown format %d") % format)
1065 raise error.Abort(_("unknown format %d") % format)
1067
1066
1068 if ui.debugflag:
1067 if ui.debugflag:
1069 shortfn = hex
1068 shortfn = hex
1070 else:
1069 else:
1071 shortfn = short
1070 shortfn = short
1072
1071
1073 # There might not be anything in r, so have a sane default
1072 # There might not be anything in r, so have a sane default
1074 idlen = 12
1073 idlen = 12
1075 for i in r:
1074 for i in r:
1076 idlen = len(shortfn(r.node(i)))
1075 idlen = len(shortfn(r.node(i)))
1077 break
1076 break
1078
1077
1079 if format == 0:
1078 if format == 0:
1080 if ui.verbose:
1079 if ui.verbose:
1081 ui.write((" rev offset length linkrev"
1080 ui.write((" rev offset length linkrev"
1082 " %s %s p2\n") % ("nodeid".ljust(idlen),
1081 " %s %s p2\n") % ("nodeid".ljust(idlen),
1083 "p1".ljust(idlen)))
1082 "p1".ljust(idlen)))
1084 else:
1083 else:
1085 ui.write((" rev linkrev %s %s p2\n") % (
1084 ui.write((" rev linkrev %s %s p2\n") % (
1086 "nodeid".ljust(idlen), "p1".ljust(idlen)))
1085 "nodeid".ljust(idlen), "p1".ljust(idlen)))
1087 elif format == 1:
1086 elif format == 1:
1088 if ui.verbose:
1087 if ui.verbose:
1089 ui.write((" rev flag offset length size link p1"
1088 ui.write((" rev flag offset length size link p1"
1090 " p2 %s\n") % "nodeid".rjust(idlen))
1089 " p2 %s\n") % "nodeid".rjust(idlen))
1091 else:
1090 else:
1092 ui.write((" rev flag size link p1 p2 %s\n") %
1091 ui.write((" rev flag size link p1 p2 %s\n") %
1093 "nodeid".rjust(idlen))
1092 "nodeid".rjust(idlen))
1094
1093
1095 for i in r:
1094 for i in r:
1096 node = r.node(i)
1095 node = r.node(i)
1097 if format == 0:
1096 if format == 0:
1098 try:
1097 try:
1099 pp = r.parents(node)
1098 pp = r.parents(node)
1100 except Exception:
1099 except Exception:
1101 pp = [nullid, nullid]
1100 pp = [nullid, nullid]
1102 if ui.verbose:
1101 if ui.verbose:
1103 ui.write("% 6d % 9d % 7d % 7d %s %s %s\n" % (
1102 ui.write("% 6d % 9d % 7d % 7d %s %s %s\n" % (
1104 i, r.start(i), r.length(i), r.linkrev(i),
1103 i, r.start(i), r.length(i), r.linkrev(i),
1105 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
1104 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
1106 else:
1105 else:
1107 ui.write("% 6d % 7d %s %s %s\n" % (
1106 ui.write("% 6d % 7d %s %s %s\n" % (
1108 i, r.linkrev(i), shortfn(node), shortfn(pp[0]),
1107 i, r.linkrev(i), shortfn(node), shortfn(pp[0]),
1109 shortfn(pp[1])))
1108 shortfn(pp[1])))
1110 elif format == 1:
1109 elif format == 1:
1111 pr = r.parentrevs(i)
1110 pr = r.parentrevs(i)
1112 if ui.verbose:
1111 if ui.verbose:
1113 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d %s\n" % (
1112 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d %s\n" % (
1114 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1113 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1115 r.linkrev(i), pr[0], pr[1], shortfn(node)))
1114 r.linkrev(i), pr[0], pr[1], shortfn(node)))
1116 else:
1115 else:
1117 ui.write("% 6d %04x % 8d % 6d % 6d % 6d %s\n" % (
1116 ui.write("% 6d %04x % 8d % 6d % 6d % 6d %s\n" % (
1118 i, r.flags(i), r.rawsize(i), r.linkrev(i), pr[0], pr[1],
1117 i, r.flags(i), r.rawsize(i), r.linkrev(i), pr[0], pr[1],
1119 shortfn(node)))
1118 shortfn(node)))
1120
1119
1121 @command('debugindexdot', cmdutil.debugrevlogopts,
1120 @command('debugindexdot', cmdutil.debugrevlogopts,
1122 _('-c|-m|FILE'), optionalrepo=True)
1121 _('-c|-m|FILE'), optionalrepo=True)
1123 def debugindexdot(ui, repo, file_=None, **opts):
1122 def debugindexdot(ui, repo, file_=None, **opts):
1124 """dump an index DAG as a graphviz dot file"""
1123 """dump an index DAG as a graphviz dot file"""
1125 opts = pycompat.byteskwargs(opts)
1124 opts = pycompat.byteskwargs(opts)
1126 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
1125 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
1127 ui.write(("digraph G {\n"))
1126 ui.write(("digraph G {\n"))
1128 for i in r:
1127 for i in r:
1129 node = r.node(i)
1128 node = r.node(i)
1130 pp = r.parents(node)
1129 pp = r.parents(node)
1131 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1130 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1132 if pp[1] != nullid:
1131 if pp[1] != nullid:
1133 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1132 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1134 ui.write("}\n")
1133 ui.write("}\n")
1135
1134
1136 @command('debuginstall', [] + cmdutil.formatteropts, '', norepo=True)
1135 @command('debuginstall', [] + cmdutil.formatteropts, '', norepo=True)
1137 def debuginstall(ui, **opts):
1136 def debuginstall(ui, **opts):
1138 '''test Mercurial installation
1137 '''test Mercurial installation
1139
1138
1140 Returns 0 on success.
1139 Returns 0 on success.
1141 '''
1140 '''
1142 opts = pycompat.byteskwargs(opts)
1141 opts = pycompat.byteskwargs(opts)
1143
1142
1144 def writetemp(contents):
1143 def writetemp(contents):
1145 (fd, name) = pycompat.mkstemp(prefix="hg-debuginstall-")
1144 (fd, name) = pycompat.mkstemp(prefix="hg-debuginstall-")
1146 f = os.fdopen(fd, r"wb")
1145 f = os.fdopen(fd, r"wb")
1147 f.write(contents)
1146 f.write(contents)
1148 f.close()
1147 f.close()
1149 return name
1148 return name
1150
1149
1151 problems = 0
1150 problems = 0
1152
1151
1153 fm = ui.formatter('debuginstall', opts)
1152 fm = ui.formatter('debuginstall', opts)
1154 fm.startitem()
1153 fm.startitem()
1155
1154
1156 # encoding
1155 # encoding
1157 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
1156 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
1158 err = None
1157 err = None
1159 try:
1158 try:
1160 codecs.lookup(pycompat.sysstr(encoding.encoding))
1159 codecs.lookup(pycompat.sysstr(encoding.encoding))
1161 except LookupError as inst:
1160 except LookupError as inst:
1162 err = stringutil.forcebytestr(inst)
1161 err = stringutil.forcebytestr(inst)
1163 problems += 1
1162 problems += 1
1164 fm.condwrite(err, 'encodingerror', _(" %s\n"
1163 fm.condwrite(err, 'encodingerror', _(" %s\n"
1165 " (check that your locale is properly set)\n"), err)
1164 " (check that your locale is properly set)\n"), err)
1166
1165
1167 # Python
1166 # Python
1168 fm.write('pythonexe', _("checking Python executable (%s)\n"),
1167 fm.write('pythonexe', _("checking Python executable (%s)\n"),
1169 pycompat.sysexecutable)
1168 pycompat.sysexecutable)
1170 fm.write('pythonver', _("checking Python version (%s)\n"),
1169 fm.write('pythonver', _("checking Python version (%s)\n"),
1171 ("%d.%d.%d" % sys.version_info[:3]))
1170 ("%d.%d.%d" % sys.version_info[:3]))
1172 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
1171 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
1173 os.path.dirname(pycompat.fsencode(os.__file__)))
1172 os.path.dirname(pycompat.fsencode(os.__file__)))
1174
1173
1175 security = set(sslutil.supportedprotocols)
1174 security = set(sslutil.supportedprotocols)
1176 if sslutil.hassni:
1175 if sslutil.hassni:
1177 security.add('sni')
1176 security.add('sni')
1178
1177
1179 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
1178 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
1180 fm.formatlist(sorted(security), name='protocol',
1179 fm.formatlist(sorted(security), name='protocol',
1181 fmt='%s', sep=','))
1180 fmt='%s', sep=','))
1182
1181
1183 # These are warnings, not errors. So don't increment problem count. This
1182 # These are warnings, not errors. So don't increment problem count. This
1184 # may change in the future.
1183 # may change in the future.
1185 if 'tls1.2' not in security:
1184 if 'tls1.2' not in security:
1186 fm.plain(_(' TLS 1.2 not supported by Python install; '
1185 fm.plain(_(' TLS 1.2 not supported by Python install; '
1187 'network connections lack modern security\n'))
1186 'network connections lack modern security\n'))
1188 if 'sni' not in security:
1187 if 'sni' not in security:
1189 fm.plain(_(' SNI not supported by Python install; may have '
1188 fm.plain(_(' SNI not supported by Python install; may have '
1190 'connectivity issues with some servers\n'))
1189 'connectivity issues with some servers\n'))
1191
1190
1192 # TODO print CA cert info
1191 # TODO print CA cert info
1193
1192
1194 # hg version
1193 # hg version
1195 hgver = util.version()
1194 hgver = util.version()
1196 fm.write('hgver', _("checking Mercurial version (%s)\n"),
1195 fm.write('hgver', _("checking Mercurial version (%s)\n"),
1197 hgver.split('+')[0])
1196 hgver.split('+')[0])
1198 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
1197 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
1199 '+'.join(hgver.split('+')[1:]))
1198 '+'.join(hgver.split('+')[1:]))
1200
1199
1201 # compiled modules
1200 # compiled modules
1202 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
1201 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
1203 policy.policy)
1202 policy.policy)
1204 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
1203 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
1205 os.path.dirname(pycompat.fsencode(__file__)))
1204 os.path.dirname(pycompat.fsencode(__file__)))
1206
1205
1207 if policy.policy in ('c', 'allow'):
1206 if policy.policy in ('c', 'allow'):
1208 err = None
1207 err = None
1209 try:
1208 try:
1210 from .cext import (
1209 from .cext import (
1211 base85,
1210 base85,
1212 bdiff,
1211 bdiff,
1213 mpatch,
1212 mpatch,
1214 osutil,
1213 osutil,
1215 )
1214 )
1216 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1215 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1217 except Exception as inst:
1216 except Exception as inst:
1218 err = stringutil.forcebytestr(inst)
1217 err = stringutil.forcebytestr(inst)
1219 problems += 1
1218 problems += 1
1220 fm.condwrite(err, 'extensionserror', " %s\n", err)
1219 fm.condwrite(err, 'extensionserror', " %s\n", err)
1221
1220
1222 compengines = util.compengines._engines.values()
1221 compengines = util.compengines._engines.values()
1223 fm.write('compengines', _('checking registered compression engines (%s)\n'),
1222 fm.write('compengines', _('checking registered compression engines (%s)\n'),
1224 fm.formatlist(sorted(e.name() for e in compengines),
1223 fm.formatlist(sorted(e.name() for e in compengines),
1225 name='compengine', fmt='%s', sep=', '))
1224 name='compengine', fmt='%s', sep=', '))
1226 fm.write('compenginesavail', _('checking available compression engines '
1225 fm.write('compenginesavail', _('checking available compression engines '
1227 '(%s)\n'),
1226 '(%s)\n'),
1228 fm.formatlist(sorted(e.name() for e in compengines
1227 fm.formatlist(sorted(e.name() for e in compengines
1229 if e.available()),
1228 if e.available()),
1230 name='compengine', fmt='%s', sep=', '))
1229 name='compengine', fmt='%s', sep=', '))
1231 wirecompengines = util.compengines.supportedwireengines(util.SERVERROLE)
1230 wirecompengines = util.compengines.supportedwireengines(util.SERVERROLE)
1232 fm.write('compenginesserver', _('checking available compression engines '
1231 fm.write('compenginesserver', _('checking available compression engines '
1233 'for wire protocol (%s)\n'),
1232 'for wire protocol (%s)\n'),
1234 fm.formatlist([e.name() for e in wirecompengines
1233 fm.formatlist([e.name() for e in wirecompengines
1235 if e.wireprotosupport()],
1234 if e.wireprotosupport()],
1236 name='compengine', fmt='%s', sep=', '))
1235 name='compengine', fmt='%s', sep=', '))
1237 re2 = 'missing'
1236 re2 = 'missing'
1238 if util._re2:
1237 if util._re2:
1239 re2 = 'available'
1238 re2 = 'available'
1240 fm.plain(_('checking "re2" regexp engine (%s)\n') % re2)
1239 fm.plain(_('checking "re2" regexp engine (%s)\n') % re2)
1241 fm.data(re2=bool(util._re2))
1240 fm.data(re2=bool(util._re2))
1242
1241
1243 # templates
1242 # templates
1244 p = templater.templatepaths()
1243 p = templater.templatepaths()
1245 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
1244 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
1246 fm.condwrite(not p, '', _(" no template directories found\n"))
1245 fm.condwrite(not p, '', _(" no template directories found\n"))
1247 if p:
1246 if p:
1248 m = templater.templatepath("map-cmdline.default")
1247 m = templater.templatepath("map-cmdline.default")
1249 if m:
1248 if m:
1250 # template found, check if it is working
1249 # template found, check if it is working
1251 err = None
1250 err = None
1252 try:
1251 try:
1253 templater.templater.frommapfile(m)
1252 templater.templater.frommapfile(m)
1254 except Exception as inst:
1253 except Exception as inst:
1255 err = stringutil.forcebytestr(inst)
1254 err = stringutil.forcebytestr(inst)
1256 p = None
1255 p = None
1257 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
1256 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
1258 else:
1257 else:
1259 p = None
1258 p = None
1260 fm.condwrite(p, 'defaulttemplate',
1259 fm.condwrite(p, 'defaulttemplate',
1261 _("checking default template (%s)\n"), m)
1260 _("checking default template (%s)\n"), m)
1262 fm.condwrite(not m, 'defaulttemplatenotfound',
1261 fm.condwrite(not m, 'defaulttemplatenotfound',
1263 _(" template '%s' not found\n"), "default")
1262 _(" template '%s' not found\n"), "default")
1264 if not p:
1263 if not p:
1265 problems += 1
1264 problems += 1
1266 fm.condwrite(not p, '',
1265 fm.condwrite(not p, '',
1267 _(" (templates seem to have been installed incorrectly)\n"))
1266 _(" (templates seem to have been installed incorrectly)\n"))
1268
1267
1269 # editor
1268 # editor
1270 editor = ui.geteditor()
1269 editor = ui.geteditor()
1271 editor = util.expandpath(editor)
1270 editor = util.expandpath(editor)
1272 editorbin = procutil.shellsplit(editor)[0]
1271 editorbin = procutil.shellsplit(editor)[0]
1273 fm.write('editor', _("checking commit editor... (%s)\n"), editorbin)
1272 fm.write('editor', _("checking commit editor... (%s)\n"), editorbin)
1274 cmdpath = procutil.findexe(editorbin)
1273 cmdpath = procutil.findexe(editorbin)
1275 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
1274 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
1276 _(" No commit editor set and can't find %s in PATH\n"
1275 _(" No commit editor set and can't find %s in PATH\n"
1277 " (specify a commit editor in your configuration"
1276 " (specify a commit editor in your configuration"
1278 " file)\n"), not cmdpath and editor == 'vi' and editorbin)
1277 " file)\n"), not cmdpath and editor == 'vi' and editorbin)
1279 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
1278 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
1280 _(" Can't find editor '%s' in PATH\n"
1279 _(" Can't find editor '%s' in PATH\n"
1281 " (specify a commit editor in your configuration"
1280 " (specify a commit editor in your configuration"
1282 " file)\n"), not cmdpath and editorbin)
1281 " file)\n"), not cmdpath and editorbin)
1283 if not cmdpath and editor != 'vi':
1282 if not cmdpath and editor != 'vi':
1284 problems += 1
1283 problems += 1
1285
1284
1286 # check username
1285 # check username
1287 username = None
1286 username = None
1288 err = None
1287 err = None
1289 try:
1288 try:
1290 username = ui.username()
1289 username = ui.username()
1291 except error.Abort as e:
1290 except error.Abort as e:
1292 err = stringutil.forcebytestr(e)
1291 err = stringutil.forcebytestr(e)
1293 problems += 1
1292 problems += 1
1294
1293
1295 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
1294 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
1296 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
1295 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
1297 " (specify a username in your configuration file)\n"), err)
1296 " (specify a username in your configuration file)\n"), err)
1298
1297
1299 fm.condwrite(not problems, '',
1298 fm.condwrite(not problems, '',
1300 _("no problems detected\n"))
1299 _("no problems detected\n"))
1301 if not problems:
1300 if not problems:
1302 fm.data(problems=problems)
1301 fm.data(problems=problems)
1303 fm.condwrite(problems, 'problems',
1302 fm.condwrite(problems, 'problems',
1304 _("%d problems detected,"
1303 _("%d problems detected,"
1305 " please check your install!\n"), problems)
1304 " please check your install!\n"), problems)
1306 fm.end()
1305 fm.end()
1307
1306
1308 return problems
1307 return problems
1309
1308
1310 @command('debugknown', [], _('REPO ID...'), norepo=True)
1309 @command('debugknown', [], _('REPO ID...'), norepo=True)
1311 def debugknown(ui, repopath, *ids, **opts):
1310 def debugknown(ui, repopath, *ids, **opts):
1312 """test whether node ids are known to a repo
1311 """test whether node ids are known to a repo
1313
1312
1314 Every ID must be a full-length hex node id string. Returns a list of 0s
1313 Every ID must be a full-length hex node id string. Returns a list of 0s
1315 and 1s indicating unknown/known.
1314 and 1s indicating unknown/known.
1316 """
1315 """
1317 opts = pycompat.byteskwargs(opts)
1316 opts = pycompat.byteskwargs(opts)
1318 repo = hg.peer(ui, opts, repopath)
1317 repo = hg.peer(ui, opts, repopath)
1319 if not repo.capable('known'):
1318 if not repo.capable('known'):
1320 raise error.Abort("known() not supported by target repository")
1319 raise error.Abort("known() not supported by target repository")
1321 flags = repo.known([bin(s) for s in ids])
1320 flags = repo.known([bin(s) for s in ids])
1322 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1321 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1323
1322
1324 @command('debuglabelcomplete', [], _('LABEL...'))
1323 @command('debuglabelcomplete', [], _('LABEL...'))
1325 def debuglabelcomplete(ui, repo, *args):
1324 def debuglabelcomplete(ui, repo, *args):
1326 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
1325 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
1327 debugnamecomplete(ui, repo, *args)
1326 debugnamecomplete(ui, repo, *args)
1328
1327
1329 @command('debuglocks',
1328 @command('debuglocks',
1330 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
1329 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
1331 ('W', 'force-wlock', None,
1330 ('W', 'force-wlock', None,
1332 _('free the working state lock (DANGEROUS)')),
1331 _('free the working state lock (DANGEROUS)')),
1333 ('s', 'set-lock', None, _('set the store lock until stopped')),
1332 ('s', 'set-lock', None, _('set the store lock until stopped')),
1334 ('S', 'set-wlock', None,
1333 ('S', 'set-wlock', None,
1335 _('set the working state lock until stopped'))],
1334 _('set the working state lock until stopped'))],
1336 _('[OPTION]...'))
1335 _('[OPTION]...'))
1337 def debuglocks(ui, repo, **opts):
1336 def debuglocks(ui, repo, **opts):
1338 """show or modify state of locks
1337 """show or modify state of locks
1339
1338
1340 By default, this command will show which locks are held. This
1339 By default, this command will show which locks are held. This
1341 includes the user and process holding the lock, the amount of time
1340 includes the user and process holding the lock, the amount of time
1342 the lock has been held, and the machine name where the process is
1341 the lock has been held, and the machine name where the process is
1343 running if it's not local.
1342 running if it's not local.
1344
1343
1345 Locks protect the integrity of Mercurial's data, so should be
1344 Locks protect the integrity of Mercurial's data, so should be
1346 treated with care. System crashes or other interruptions may cause
1345 treated with care. System crashes or other interruptions may cause
1347 locks to not be properly released, though Mercurial will usually
1346 locks to not be properly released, though Mercurial will usually
1348 detect and remove such stale locks automatically.
1347 detect and remove such stale locks automatically.
1349
1348
1350 However, detecting stale locks may not always be possible (for
1349 However, detecting stale locks may not always be possible (for
1351 instance, on a shared filesystem). Removing locks may also be
1350 instance, on a shared filesystem). Removing locks may also be
1352 blocked by filesystem permissions.
1351 blocked by filesystem permissions.
1353
1352
1354 Setting a lock will prevent other commands from changing the data.
1353 Setting a lock will prevent other commands from changing the data.
1355 The command will wait until an interruption (SIGINT, SIGTERM, ...) occurs.
1354 The command will wait until an interruption (SIGINT, SIGTERM, ...) occurs.
1356 The set locks are removed when the command exits.
1355 The set locks are removed when the command exits.
1357
1356
1358 Returns 0 if no locks are held.
1357 Returns 0 if no locks are held.
1359
1358
1360 """
1359 """
1361
1360
1362 if opts.get(r'force_lock'):
1361 if opts.get(r'force_lock'):
1363 repo.svfs.unlink('lock')
1362 repo.svfs.unlink('lock')
1364 if opts.get(r'force_wlock'):
1363 if opts.get(r'force_wlock'):
1365 repo.vfs.unlink('wlock')
1364 repo.vfs.unlink('wlock')
1366 if opts.get(r'force_lock') or opts.get(r'force_wlock'):
1365 if opts.get(r'force_lock') or opts.get(r'force_wlock'):
1367 return 0
1366 return 0
1368
1367
1369 locks = []
1368 locks = []
1370 try:
1369 try:
1371 if opts.get(r'set_wlock'):
1370 if opts.get(r'set_wlock'):
1372 try:
1371 try:
1373 locks.append(repo.wlock(False))
1372 locks.append(repo.wlock(False))
1374 except error.LockHeld:
1373 except error.LockHeld:
1375 raise error.Abort(_('wlock is already held'))
1374 raise error.Abort(_('wlock is already held'))
1376 if opts.get(r'set_lock'):
1375 if opts.get(r'set_lock'):
1377 try:
1376 try:
1378 locks.append(repo.lock(False))
1377 locks.append(repo.lock(False))
1379 except error.LockHeld:
1378 except error.LockHeld:
1380 raise error.Abort(_('lock is already held'))
1379 raise error.Abort(_('lock is already held'))
1381 if len(locks):
1380 if len(locks):
1382 ui.promptchoice(_("ready to release the lock (y)? $$ &Yes"))
1381 ui.promptchoice(_("ready to release the lock (y)? $$ &Yes"))
1383 return 0
1382 return 0
1384 finally:
1383 finally:
1385 release(*locks)
1384 release(*locks)
1386
1385
1387 now = time.time()
1386 now = time.time()
1388 held = 0
1387 held = 0
1389
1388
1390 def report(vfs, name, method):
1389 def report(vfs, name, method):
1391 # this causes stale locks to get reaped for more accurate reporting
1390 # this causes stale locks to get reaped for more accurate reporting
1392 try:
1391 try:
1393 l = method(False)
1392 l = method(False)
1394 except error.LockHeld:
1393 except error.LockHeld:
1395 l = None
1394 l = None
1396
1395
1397 if l:
1396 if l:
1398 l.release()
1397 l.release()
1399 else:
1398 else:
1400 try:
1399 try:
1401 st = vfs.lstat(name)
1400 st = vfs.lstat(name)
1402 age = now - st[stat.ST_MTIME]
1401 age = now - st[stat.ST_MTIME]
1403 user = util.username(st.st_uid)
1402 user = util.username(st.st_uid)
1404 locker = vfs.readlock(name)
1403 locker = vfs.readlock(name)
1405 if ":" in locker:
1404 if ":" in locker:
1406 host, pid = locker.split(':')
1405 host, pid = locker.split(':')
1407 if host == socket.gethostname():
1406 if host == socket.gethostname():
1408 locker = 'user %s, process %s' % (user, pid)
1407 locker = 'user %s, process %s' % (user, pid)
1409 else:
1408 else:
1410 locker = 'user %s, process %s, host %s' \
1409 locker = 'user %s, process %s, host %s' \
1411 % (user, pid, host)
1410 % (user, pid, host)
1412 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
1411 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
1413 return 1
1412 return 1
1414 except OSError as e:
1413 except OSError as e:
1415 if e.errno != errno.ENOENT:
1414 if e.errno != errno.ENOENT:
1416 raise
1415 raise
1417
1416
1418 ui.write(("%-6s free\n") % (name + ":"))
1417 ui.write(("%-6s free\n") % (name + ":"))
1419 return 0
1418 return 0
1420
1419
1421 held += report(repo.svfs, "lock", repo.lock)
1420 held += report(repo.svfs, "lock", repo.lock)
1422 held += report(repo.vfs, "wlock", repo.wlock)
1421 held += report(repo.vfs, "wlock", repo.wlock)
1423
1422
1424 return held
1423 return held
1425
1424
1426 @command('debugmergestate', [], '')
1425 @command('debugmergestate', [], '')
1427 def debugmergestate(ui, repo, *args):
1426 def debugmergestate(ui, repo, *args):
1428 """print merge state
1427 """print merge state
1429
1428
1430 Use --verbose to print out information about whether v1 or v2 merge state
1429 Use --verbose to print out information about whether v1 or v2 merge state
1431 was chosen."""
1430 was chosen."""
1432 def _hashornull(h):
1431 def _hashornull(h):
1433 if h == nullhex:
1432 if h == nullhex:
1434 return 'null'
1433 return 'null'
1435 else:
1434 else:
1436 return h
1435 return h
1437
1436
1438 def printrecords(version):
1437 def printrecords(version):
1439 ui.write(('* version %d records\n') % version)
1438 ui.write(('* version %d records\n') % version)
1440 if version == 1:
1439 if version == 1:
1441 records = v1records
1440 records = v1records
1442 else:
1441 else:
1443 records = v2records
1442 records = v2records
1444
1443
1445 for rtype, record in records:
1444 for rtype, record in records:
1446 # pretty print some record types
1445 # pretty print some record types
1447 if rtype == 'L':
1446 if rtype == 'L':
1448 ui.write(('local: %s\n') % record)
1447 ui.write(('local: %s\n') % record)
1449 elif rtype == 'O':
1448 elif rtype == 'O':
1450 ui.write(('other: %s\n') % record)
1449 ui.write(('other: %s\n') % record)
1451 elif rtype == 'm':
1450 elif rtype == 'm':
1452 driver, mdstate = record.split('\0', 1)
1451 driver, mdstate = record.split('\0', 1)
1453 ui.write(('merge driver: %s (state "%s")\n')
1452 ui.write(('merge driver: %s (state "%s")\n')
1454 % (driver, mdstate))
1453 % (driver, mdstate))
1455 elif rtype in 'FDC':
1454 elif rtype in 'FDC':
1456 r = record.split('\0')
1455 r = record.split('\0')
1457 f, state, hash, lfile, afile, anode, ofile = r[0:7]
1456 f, state, hash, lfile, afile, anode, ofile = r[0:7]
1458 if version == 1:
1457 if version == 1:
1459 onode = 'not stored in v1 format'
1458 onode = 'not stored in v1 format'
1460 flags = r[7]
1459 flags = r[7]
1461 else:
1460 else:
1462 onode, flags = r[7:9]
1461 onode, flags = r[7:9]
1463 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
1462 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
1464 % (f, rtype, state, _hashornull(hash)))
1463 % (f, rtype, state, _hashornull(hash)))
1465 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
1464 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
1466 ui.write((' ancestor path: %s (node %s)\n')
1465 ui.write((' ancestor path: %s (node %s)\n')
1467 % (afile, _hashornull(anode)))
1466 % (afile, _hashornull(anode)))
1468 ui.write((' other path: %s (node %s)\n')
1467 ui.write((' other path: %s (node %s)\n')
1469 % (ofile, _hashornull(onode)))
1468 % (ofile, _hashornull(onode)))
1470 elif rtype == 'f':
1469 elif rtype == 'f':
1471 filename, rawextras = record.split('\0', 1)
1470 filename, rawextras = record.split('\0', 1)
1472 extras = rawextras.split('\0')
1471 extras = rawextras.split('\0')
1473 i = 0
1472 i = 0
1474 extrastrings = []
1473 extrastrings = []
1475 while i < len(extras):
1474 while i < len(extras):
1476 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
1475 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
1477 i += 2
1476 i += 2
1478
1477
1479 ui.write(('file extras: %s (%s)\n')
1478 ui.write(('file extras: %s (%s)\n')
1480 % (filename, ', '.join(extrastrings)))
1479 % (filename, ', '.join(extrastrings)))
1481 elif rtype == 'l':
1480 elif rtype == 'l':
1482 labels = record.split('\0', 2)
1481 labels = record.split('\0', 2)
1483 labels = [l for l in labels if len(l) > 0]
1482 labels = [l for l in labels if len(l) > 0]
1484 ui.write(('labels:\n'))
1483 ui.write(('labels:\n'))
1485 ui.write((' local: %s\n' % labels[0]))
1484 ui.write((' local: %s\n' % labels[0]))
1486 ui.write((' other: %s\n' % labels[1]))
1485 ui.write((' other: %s\n' % labels[1]))
1487 if len(labels) > 2:
1486 if len(labels) > 2:
1488 ui.write((' base: %s\n' % labels[2]))
1487 ui.write((' base: %s\n' % labels[2]))
1489 else:
1488 else:
1490 ui.write(('unrecognized entry: %s\t%s\n')
1489 ui.write(('unrecognized entry: %s\t%s\n')
1491 % (rtype, record.replace('\0', '\t')))
1490 % (rtype, record.replace('\0', '\t')))
1492
1491
1493 # Avoid mergestate.read() since it may raise an exception for unsupported
1492 # Avoid mergestate.read() since it may raise an exception for unsupported
1494 # merge state records. We shouldn't be doing this, but this is OK since this
1493 # merge state records. We shouldn't be doing this, but this is OK since this
1495 # command is pretty low-level.
1494 # command is pretty low-level.
1496 ms = mergemod.mergestate(repo)
1495 ms = mergemod.mergestate(repo)
1497
1496
1498 # sort so that reasonable information is on top
1497 # sort so that reasonable information is on top
1499 v1records = ms._readrecordsv1()
1498 v1records = ms._readrecordsv1()
1500 v2records = ms._readrecordsv2()
1499 v2records = ms._readrecordsv2()
1501 order = 'LOml'
1500 order = 'LOml'
1502 def key(r):
1501 def key(r):
1503 idx = order.find(r[0])
1502 idx = order.find(r[0])
1504 if idx == -1:
1503 if idx == -1:
1505 return (1, r[1])
1504 return (1, r[1])
1506 else:
1505 else:
1507 return (0, idx)
1506 return (0, idx)
1508 v1records.sort(key=key)
1507 v1records.sort(key=key)
1509 v2records.sort(key=key)
1508 v2records.sort(key=key)
1510
1509
1511 if not v1records and not v2records:
1510 if not v1records and not v2records:
1512 ui.write(('no merge state found\n'))
1511 ui.write(('no merge state found\n'))
1513 elif not v2records:
1512 elif not v2records:
1514 ui.note(('no version 2 merge state\n'))
1513 ui.note(('no version 2 merge state\n'))
1515 printrecords(1)
1514 printrecords(1)
1516 elif ms._v1v2match(v1records, v2records):
1515 elif ms._v1v2match(v1records, v2records):
1517 ui.note(('v1 and v2 states match: using v2\n'))
1516 ui.note(('v1 and v2 states match: using v2\n'))
1518 printrecords(2)
1517 printrecords(2)
1519 else:
1518 else:
1520 ui.note(('v1 and v2 states mismatch: using v1\n'))
1519 ui.note(('v1 and v2 states mismatch: using v1\n'))
1521 printrecords(1)
1520 printrecords(1)
1522 if ui.verbose:
1521 if ui.verbose:
1523 printrecords(2)
1522 printrecords(2)
1524
1523
1525 @command('debugnamecomplete', [], _('NAME...'))
1524 @command('debugnamecomplete', [], _('NAME...'))
1526 def debugnamecomplete(ui, repo, *args):
1525 def debugnamecomplete(ui, repo, *args):
1527 '''complete "names" - tags, open branch names, bookmark names'''
1526 '''complete "names" - tags, open branch names, bookmark names'''
1528
1527
1529 names = set()
1528 names = set()
1530 # since we previously only listed open branches, we will handle that
1529 # since we previously only listed open branches, we will handle that
1531 # specially (after this for loop)
1530 # specially (after this for loop)
1532 for name, ns in repo.names.iteritems():
1531 for name, ns in repo.names.iteritems():
1533 if name != 'branches':
1532 if name != 'branches':
1534 names.update(ns.listnames(repo))
1533 names.update(ns.listnames(repo))
1535 names.update(tag for (tag, heads, tip, closed)
1534 names.update(tag for (tag, heads, tip, closed)
1536 in repo.branchmap().iterbranches() if not closed)
1535 in repo.branchmap().iterbranches() if not closed)
1537 completions = set()
1536 completions = set()
1538 if not args:
1537 if not args:
1539 args = ['']
1538 args = ['']
1540 for a in args:
1539 for a in args:
1541 completions.update(n for n in names if n.startswith(a))
1540 completions.update(n for n in names if n.startswith(a))
1542 ui.write('\n'.join(sorted(completions)))
1541 ui.write('\n'.join(sorted(completions)))
1543 ui.write('\n')
1542 ui.write('\n')
1544
1543
1545 @command('debugobsolete',
1544 @command('debugobsolete',
1546 [('', 'flags', 0, _('markers flag')),
1545 [('', 'flags', 0, _('markers flag')),
1547 ('', 'record-parents', False,
1546 ('', 'record-parents', False,
1548 _('record parent information for the precursor')),
1547 _('record parent information for the precursor')),
1549 ('r', 'rev', [], _('display markers relevant to REV')),
1548 ('r', 'rev', [], _('display markers relevant to REV')),
1550 ('', 'exclusive', False, _('restrict display to markers only '
1549 ('', 'exclusive', False, _('restrict display to markers only '
1551 'relevant to REV')),
1550 'relevant to REV')),
1552 ('', 'index', False, _('display index of the marker')),
1551 ('', 'index', False, _('display index of the marker')),
1553 ('', 'delete', [], _('delete markers specified by indices')),
1552 ('', 'delete', [], _('delete markers specified by indices')),
1554 ] + cmdutil.commitopts2 + cmdutil.formatteropts,
1553 ] + cmdutil.commitopts2 + cmdutil.formatteropts,
1555 _('[OBSOLETED [REPLACEMENT ...]]'))
1554 _('[OBSOLETED [REPLACEMENT ...]]'))
1556 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
1555 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
1557 """create arbitrary obsolete marker
1556 """create arbitrary obsolete marker
1558
1557
1559 With no arguments, displays the list of obsolescence markers."""
1558 With no arguments, displays the list of obsolescence markers."""
1560
1559
1561 opts = pycompat.byteskwargs(opts)
1560 opts = pycompat.byteskwargs(opts)
1562
1561
1563 def parsenodeid(s):
1562 def parsenodeid(s):
1564 try:
1563 try:
1565 # We do not use revsingle/revrange functions here to accept
1564 # We do not use revsingle/revrange functions here to accept
1566 # arbitrary node identifiers, possibly not present in the
1565 # arbitrary node identifiers, possibly not present in the
1567 # local repository.
1566 # local repository.
1568 n = bin(s)
1567 n = bin(s)
1569 if len(n) != len(nullid):
1568 if len(n) != len(nullid):
1570 raise TypeError()
1569 raise TypeError()
1571 return n
1570 return n
1572 except TypeError:
1571 except TypeError:
1573 raise error.Abort('changeset references must be full hexadecimal '
1572 raise error.Abort('changeset references must be full hexadecimal '
1574 'node identifiers')
1573 'node identifiers')
1575
1574
1576 if opts.get('delete'):
1575 if opts.get('delete'):
1577 indices = []
1576 indices = []
1578 for v in opts.get('delete'):
1577 for v in opts.get('delete'):
1579 try:
1578 try:
1580 indices.append(int(v))
1579 indices.append(int(v))
1581 except ValueError:
1580 except ValueError:
1582 raise error.Abort(_('invalid index value: %r') % v,
1581 raise error.Abort(_('invalid index value: %r') % v,
1583 hint=_('use integers for indices'))
1582 hint=_('use integers for indices'))
1584
1583
1585 if repo.currenttransaction():
1584 if repo.currenttransaction():
1586 raise error.Abort(_('cannot delete obsmarkers in the middle '
1585 raise error.Abort(_('cannot delete obsmarkers in the middle '
1587 'of transaction.'))
1586 'of transaction.'))
1588
1587
1589 with repo.lock():
1588 with repo.lock():
1590 n = repair.deleteobsmarkers(repo.obsstore, indices)
1589 n = repair.deleteobsmarkers(repo.obsstore, indices)
1591 ui.write(_('deleted %i obsolescence markers\n') % n)
1590 ui.write(_('deleted %i obsolescence markers\n') % n)
1592
1591
1593 return
1592 return
1594
1593
1595 if precursor is not None:
1594 if precursor is not None:
1596 if opts['rev']:
1595 if opts['rev']:
1597 raise error.Abort('cannot select revision when creating marker')
1596 raise error.Abort('cannot select revision when creating marker')
1598 metadata = {}
1597 metadata = {}
1599 metadata['user'] = opts['user'] or ui.username()
1598 metadata['user'] = opts['user'] or ui.username()
1600 succs = tuple(parsenodeid(succ) for succ in successors)
1599 succs = tuple(parsenodeid(succ) for succ in successors)
1601 l = repo.lock()
1600 l = repo.lock()
1602 try:
1601 try:
1603 tr = repo.transaction('debugobsolete')
1602 tr = repo.transaction('debugobsolete')
1604 try:
1603 try:
1605 date = opts.get('date')
1604 date = opts.get('date')
1606 if date:
1605 if date:
1607 date = dateutil.parsedate(date)
1606 date = dateutil.parsedate(date)
1608 else:
1607 else:
1609 date = None
1608 date = None
1610 prec = parsenodeid(precursor)
1609 prec = parsenodeid(precursor)
1611 parents = None
1610 parents = None
1612 if opts['record_parents']:
1611 if opts['record_parents']:
1613 if prec not in repo.unfiltered():
1612 if prec not in repo.unfiltered():
1614 raise error.Abort('cannot used --record-parents on '
1613 raise error.Abort('cannot used --record-parents on '
1615 'unknown changesets')
1614 'unknown changesets')
1616 parents = repo.unfiltered()[prec].parents()
1615 parents = repo.unfiltered()[prec].parents()
1617 parents = tuple(p.node() for p in parents)
1616 parents = tuple(p.node() for p in parents)
1618 repo.obsstore.create(tr, prec, succs, opts['flags'],
1617 repo.obsstore.create(tr, prec, succs, opts['flags'],
1619 parents=parents, date=date,
1618 parents=parents, date=date,
1620 metadata=metadata, ui=ui)
1619 metadata=metadata, ui=ui)
1621 tr.close()
1620 tr.close()
1622 except ValueError as exc:
1621 except ValueError as exc:
1623 raise error.Abort(_('bad obsmarker input: %s') %
1622 raise error.Abort(_('bad obsmarker input: %s') %
1624 pycompat.bytestr(exc))
1623 pycompat.bytestr(exc))
1625 finally:
1624 finally:
1626 tr.release()
1625 tr.release()
1627 finally:
1626 finally:
1628 l.release()
1627 l.release()
1629 else:
1628 else:
1630 if opts['rev']:
1629 if opts['rev']:
1631 revs = scmutil.revrange(repo, opts['rev'])
1630 revs = scmutil.revrange(repo, opts['rev'])
1632 nodes = [repo[r].node() for r in revs]
1631 nodes = [repo[r].node() for r in revs]
1633 markers = list(obsutil.getmarkers(repo, nodes=nodes,
1632 markers = list(obsutil.getmarkers(repo, nodes=nodes,
1634 exclusive=opts['exclusive']))
1633 exclusive=opts['exclusive']))
1635 markers.sort(key=lambda x: x._data)
1634 markers.sort(key=lambda x: x._data)
1636 else:
1635 else:
1637 markers = obsutil.getmarkers(repo)
1636 markers = obsutil.getmarkers(repo)
1638
1637
1639 markerstoiter = markers
1638 markerstoiter = markers
1640 isrelevant = lambda m: True
1639 isrelevant = lambda m: True
1641 if opts.get('rev') and opts.get('index'):
1640 if opts.get('rev') and opts.get('index'):
1642 markerstoiter = obsutil.getmarkers(repo)
1641 markerstoiter = obsutil.getmarkers(repo)
1643 markerset = set(markers)
1642 markerset = set(markers)
1644 isrelevant = lambda m: m in markerset
1643 isrelevant = lambda m: m in markerset
1645
1644
1646 fm = ui.formatter('debugobsolete', opts)
1645 fm = ui.formatter('debugobsolete', opts)
1647 for i, m in enumerate(markerstoiter):
1646 for i, m in enumerate(markerstoiter):
1648 if not isrelevant(m):
1647 if not isrelevant(m):
1649 # marker can be irrelevant when we're iterating over a set
1648 # marker can be irrelevant when we're iterating over a set
1650 # of markers (markerstoiter) which is bigger than the set
1649 # of markers (markerstoiter) which is bigger than the set
1651 # of markers we want to display (markers)
1650 # of markers we want to display (markers)
1652 # this can happen if both --index and --rev options are
1651 # this can happen if both --index and --rev options are
1653 # provided and thus we need to iterate over all of the markers
1652 # provided and thus we need to iterate over all of the markers
1654 # to get the correct indices, but only display the ones that
1653 # to get the correct indices, but only display the ones that
1655 # are relevant to --rev value
1654 # are relevant to --rev value
1656 continue
1655 continue
1657 fm.startitem()
1656 fm.startitem()
1658 ind = i if opts.get('index') else None
1657 ind = i if opts.get('index') else None
1659 cmdutil.showmarker(fm, m, index=ind)
1658 cmdutil.showmarker(fm, m, index=ind)
1660 fm.end()
1659 fm.end()
1661
1660
1662 @command('debugpathcomplete',
1661 @command('debugpathcomplete',
1663 [('f', 'full', None, _('complete an entire path')),
1662 [('f', 'full', None, _('complete an entire path')),
1664 ('n', 'normal', None, _('show only normal files')),
1663 ('n', 'normal', None, _('show only normal files')),
1665 ('a', 'added', None, _('show only added files')),
1664 ('a', 'added', None, _('show only added files')),
1666 ('r', 'removed', None, _('show only removed files'))],
1665 ('r', 'removed', None, _('show only removed files'))],
1667 _('FILESPEC...'))
1666 _('FILESPEC...'))
1668 def debugpathcomplete(ui, repo, *specs, **opts):
1667 def debugpathcomplete(ui, repo, *specs, **opts):
1669 '''complete part or all of a tracked path
1668 '''complete part or all of a tracked path
1670
1669
1671 This command supports shells that offer path name completion. It
1670 This command supports shells that offer path name completion. It
1672 currently completes only files already known to the dirstate.
1671 currently completes only files already known to the dirstate.
1673
1672
1674 Completion extends only to the next path segment unless
1673 Completion extends only to the next path segment unless
1675 --full is specified, in which case entire paths are used.'''
1674 --full is specified, in which case entire paths are used.'''
1676
1675
1677 def complete(path, acceptable):
1676 def complete(path, acceptable):
1678 dirstate = repo.dirstate
1677 dirstate = repo.dirstate
1679 spec = os.path.normpath(os.path.join(pycompat.getcwd(), path))
1678 spec = os.path.normpath(os.path.join(pycompat.getcwd(), path))
1680 rootdir = repo.root + pycompat.ossep
1679 rootdir = repo.root + pycompat.ossep
1681 if spec != repo.root and not spec.startswith(rootdir):
1680 if spec != repo.root and not spec.startswith(rootdir):
1682 return [], []
1681 return [], []
1683 if os.path.isdir(spec):
1682 if os.path.isdir(spec):
1684 spec += '/'
1683 spec += '/'
1685 spec = spec[len(rootdir):]
1684 spec = spec[len(rootdir):]
1686 fixpaths = pycompat.ossep != '/'
1685 fixpaths = pycompat.ossep != '/'
1687 if fixpaths:
1686 if fixpaths:
1688 spec = spec.replace(pycompat.ossep, '/')
1687 spec = spec.replace(pycompat.ossep, '/')
1689 speclen = len(spec)
1688 speclen = len(spec)
1690 fullpaths = opts[r'full']
1689 fullpaths = opts[r'full']
1691 files, dirs = set(), set()
1690 files, dirs = set(), set()
1692 adddir, addfile = dirs.add, files.add
1691 adddir, addfile = dirs.add, files.add
1693 for f, st in dirstate.iteritems():
1692 for f, st in dirstate.iteritems():
1694 if f.startswith(spec) and st[0] in acceptable:
1693 if f.startswith(spec) and st[0] in acceptable:
1695 if fixpaths:
1694 if fixpaths:
1696 f = f.replace('/', pycompat.ossep)
1695 f = f.replace('/', pycompat.ossep)
1697 if fullpaths:
1696 if fullpaths:
1698 addfile(f)
1697 addfile(f)
1699 continue
1698 continue
1700 s = f.find(pycompat.ossep, speclen)
1699 s = f.find(pycompat.ossep, speclen)
1701 if s >= 0:
1700 if s >= 0:
1702 adddir(f[:s])
1701 adddir(f[:s])
1703 else:
1702 else:
1704 addfile(f)
1703 addfile(f)
1705 return files, dirs
1704 return files, dirs
1706
1705
1707 acceptable = ''
1706 acceptable = ''
1708 if opts[r'normal']:
1707 if opts[r'normal']:
1709 acceptable += 'nm'
1708 acceptable += 'nm'
1710 if opts[r'added']:
1709 if opts[r'added']:
1711 acceptable += 'a'
1710 acceptable += 'a'
1712 if opts[r'removed']:
1711 if opts[r'removed']:
1713 acceptable += 'r'
1712 acceptable += 'r'
1714 cwd = repo.getcwd()
1713 cwd = repo.getcwd()
1715 if not specs:
1714 if not specs:
1716 specs = ['.']
1715 specs = ['.']
1717
1716
1718 files, dirs = set(), set()
1717 files, dirs = set(), set()
1719 for spec in specs:
1718 for spec in specs:
1720 f, d = complete(spec, acceptable or 'nmar')
1719 f, d = complete(spec, acceptable or 'nmar')
1721 files.update(f)
1720 files.update(f)
1722 dirs.update(d)
1721 dirs.update(d)
1723 files.update(dirs)
1722 files.update(dirs)
1724 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
1723 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
1725 ui.write('\n')
1724 ui.write('\n')
1726
1725
1727 @command('debugpeer', [], _('PATH'), norepo=True)
1726 @command('debugpeer', [], _('PATH'), norepo=True)
1728 def debugpeer(ui, path):
1727 def debugpeer(ui, path):
1729 """establish a connection to a peer repository"""
1728 """establish a connection to a peer repository"""
1730 # Always enable peer request logging. Requires --debug to display
1729 # Always enable peer request logging. Requires --debug to display
1731 # though.
1730 # though.
1732 overrides = {
1731 overrides = {
1733 ('devel', 'debug.peer-request'): True,
1732 ('devel', 'debug.peer-request'): True,
1734 }
1733 }
1735
1734
1736 with ui.configoverride(overrides):
1735 with ui.configoverride(overrides):
1737 peer = hg.peer(ui, {}, path)
1736 peer = hg.peer(ui, {}, path)
1738
1737
1739 local = peer.local() is not None
1738 local = peer.local() is not None
1740 canpush = peer.canpush()
1739 canpush = peer.canpush()
1741
1740
1742 ui.write(_('url: %s\n') % peer.url())
1741 ui.write(_('url: %s\n') % peer.url())
1743 ui.write(_('local: %s\n') % (_('yes') if local else _('no')))
1742 ui.write(_('local: %s\n') % (_('yes') if local else _('no')))
1744 ui.write(_('pushable: %s\n') % (_('yes') if canpush else _('no')))
1743 ui.write(_('pushable: %s\n') % (_('yes') if canpush else _('no')))
1745
1744
1746 @command('debugpickmergetool',
1745 @command('debugpickmergetool',
1747 [('r', 'rev', '', _('check for files in this revision'), _('REV')),
1746 [('r', 'rev', '', _('check for files in this revision'), _('REV')),
1748 ('', 'changedelete', None, _('emulate merging change and delete')),
1747 ('', 'changedelete', None, _('emulate merging change and delete')),
1749 ] + cmdutil.walkopts + cmdutil.mergetoolopts,
1748 ] + cmdutil.walkopts + cmdutil.mergetoolopts,
1750 _('[PATTERN]...'),
1749 _('[PATTERN]...'),
1751 inferrepo=True)
1750 inferrepo=True)
1752 def debugpickmergetool(ui, repo, *pats, **opts):
1751 def debugpickmergetool(ui, repo, *pats, **opts):
1753 """examine which merge tool is chosen for specified file
1752 """examine which merge tool is chosen for specified file
1754
1753
1755 As described in :hg:`help merge-tools`, Mercurial examines
1754 As described in :hg:`help merge-tools`, Mercurial examines
1756 configurations below in this order to decide which merge tool is
1755 configurations below in this order to decide which merge tool is
1757 chosen for specified file.
1756 chosen for specified file.
1758
1757
1759 1. ``--tool`` option
1758 1. ``--tool`` option
1760 2. ``HGMERGE`` environment variable
1759 2. ``HGMERGE`` environment variable
1761 3. configurations in ``merge-patterns`` section
1760 3. configurations in ``merge-patterns`` section
1762 4. configuration of ``ui.merge``
1761 4. configuration of ``ui.merge``
1763 5. configurations in ``merge-tools`` section
1762 5. configurations in ``merge-tools`` section
1764 6. ``hgmerge`` tool (for historical reason only)
1763 6. ``hgmerge`` tool (for historical reason only)
1765 7. default tool for fallback (``:merge`` or ``:prompt``)
1764 7. default tool for fallback (``:merge`` or ``:prompt``)
1766
1765
1767 This command writes out examination result in the style below::
1766 This command writes out examination result in the style below::
1768
1767
1769 FILE = MERGETOOL
1768 FILE = MERGETOOL
1770
1769
1771 By default, all files known in the first parent context of the
1770 By default, all files known in the first parent context of the
1772 working directory are examined. Use file patterns and/or -I/-X
1771 working directory are examined. Use file patterns and/or -I/-X
1773 options to limit target files. -r/--rev is also useful to examine
1772 options to limit target files. -r/--rev is also useful to examine
1774 files in another context without actual updating to it.
1773 files in another context without actual updating to it.
1775
1774
1776 With --debug, this command shows warning messages while matching
1775 With --debug, this command shows warning messages while matching
1777 against ``merge-patterns`` and so on, too. It is recommended to
1776 against ``merge-patterns`` and so on, too. It is recommended to
1778 use this option with explicit file patterns and/or -I/-X options,
1777 use this option with explicit file patterns and/or -I/-X options,
1779 because this option increases amount of output per file according
1778 because this option increases amount of output per file according
1780 to configurations in hgrc.
1779 to configurations in hgrc.
1781
1780
1782 With -v/--verbose, this command shows configurations below at
1781 With -v/--verbose, this command shows configurations below at
1783 first (only if specified).
1782 first (only if specified).
1784
1783
1785 - ``--tool`` option
1784 - ``--tool`` option
1786 - ``HGMERGE`` environment variable
1785 - ``HGMERGE`` environment variable
1787 - configuration of ``ui.merge``
1786 - configuration of ``ui.merge``
1788
1787
1789 If merge tool is chosen before matching against
1788 If merge tool is chosen before matching against
1790 ``merge-patterns``, this command can't show any helpful
1789 ``merge-patterns``, this command can't show any helpful
1791 information, even with --debug. In such case, information above is
1790 information, even with --debug. In such case, information above is
1792 useful to know why a merge tool is chosen.
1791 useful to know why a merge tool is chosen.
1793 """
1792 """
1794 opts = pycompat.byteskwargs(opts)
1793 opts = pycompat.byteskwargs(opts)
1795 overrides = {}
1794 overrides = {}
1796 if opts['tool']:
1795 if opts['tool']:
1797 overrides[('ui', 'forcemerge')] = opts['tool']
1796 overrides[('ui', 'forcemerge')] = opts['tool']
1798 ui.note(('with --tool %r\n') % (pycompat.bytestr(opts['tool'])))
1797 ui.note(('with --tool %r\n') % (pycompat.bytestr(opts['tool'])))
1799
1798
1800 with ui.configoverride(overrides, 'debugmergepatterns'):
1799 with ui.configoverride(overrides, 'debugmergepatterns'):
1801 hgmerge = encoding.environ.get("HGMERGE")
1800 hgmerge = encoding.environ.get("HGMERGE")
1802 if hgmerge is not None:
1801 if hgmerge is not None:
1803 ui.note(('with HGMERGE=%r\n') % (pycompat.bytestr(hgmerge)))
1802 ui.note(('with HGMERGE=%r\n') % (pycompat.bytestr(hgmerge)))
1804 uimerge = ui.config("ui", "merge")
1803 uimerge = ui.config("ui", "merge")
1805 if uimerge:
1804 if uimerge:
1806 ui.note(('with ui.merge=%r\n') % (pycompat.bytestr(uimerge)))
1805 ui.note(('with ui.merge=%r\n') % (pycompat.bytestr(uimerge)))
1807
1806
1808 ctx = scmutil.revsingle(repo, opts.get('rev'))
1807 ctx = scmutil.revsingle(repo, opts.get('rev'))
1809 m = scmutil.match(ctx, pats, opts)
1808 m = scmutil.match(ctx, pats, opts)
1810 changedelete = opts['changedelete']
1809 changedelete = opts['changedelete']
1811 for path in ctx.walk(m):
1810 for path in ctx.walk(m):
1812 fctx = ctx[path]
1811 fctx = ctx[path]
1813 try:
1812 try:
1814 if not ui.debugflag:
1813 if not ui.debugflag:
1815 ui.pushbuffer(error=True)
1814 ui.pushbuffer(error=True)
1816 tool, toolpath = filemerge._picktool(repo, ui, path,
1815 tool, toolpath = filemerge._picktool(repo, ui, path,
1817 fctx.isbinary(),
1816 fctx.isbinary(),
1818 'l' in fctx.flags(),
1817 'l' in fctx.flags(),
1819 changedelete)
1818 changedelete)
1820 finally:
1819 finally:
1821 if not ui.debugflag:
1820 if not ui.debugflag:
1822 ui.popbuffer()
1821 ui.popbuffer()
1823 ui.write(('%s = %s\n') % (path, tool))
1822 ui.write(('%s = %s\n') % (path, tool))
1824
1823
1825 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
1824 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
1826 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1825 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1827 '''access the pushkey key/value protocol
1826 '''access the pushkey key/value protocol
1828
1827
1829 With two args, list the keys in the given namespace.
1828 With two args, list the keys in the given namespace.
1830
1829
1831 With five args, set a key to new if it currently is set to old.
1830 With five args, set a key to new if it currently is set to old.
1832 Reports success or failure.
1831 Reports success or failure.
1833 '''
1832 '''
1834
1833
1835 target = hg.peer(ui, {}, repopath)
1834 target = hg.peer(ui, {}, repopath)
1836 if keyinfo:
1835 if keyinfo:
1837 key, old, new = keyinfo
1836 key, old, new = keyinfo
1838 with target.commandexecutor() as e:
1837 with target.commandexecutor() as e:
1839 r = e.callcommand('pushkey', {
1838 r = e.callcommand('pushkey', {
1840 'namespace': namespace,
1839 'namespace': namespace,
1841 'key': key,
1840 'key': key,
1842 'old': old,
1841 'old': old,
1843 'new': new,
1842 'new': new,
1844 }).result()
1843 }).result()
1845
1844
1846 ui.status(pycompat.bytestr(r) + '\n')
1845 ui.status(pycompat.bytestr(r) + '\n')
1847 return not r
1846 return not r
1848 else:
1847 else:
1849 for k, v in sorted(target.listkeys(namespace).iteritems()):
1848 for k, v in sorted(target.listkeys(namespace).iteritems()):
1850 ui.write("%s\t%s\n" % (stringutil.escapestr(k),
1849 ui.write("%s\t%s\n" % (stringutil.escapestr(k),
1851 stringutil.escapestr(v)))
1850 stringutil.escapestr(v)))
1852
1851
1853 @command('debugpvec', [], _('A B'))
1852 @command('debugpvec', [], _('A B'))
1854 def debugpvec(ui, repo, a, b=None):
1853 def debugpvec(ui, repo, a, b=None):
1855 ca = scmutil.revsingle(repo, a)
1854 ca = scmutil.revsingle(repo, a)
1856 cb = scmutil.revsingle(repo, b)
1855 cb = scmutil.revsingle(repo, b)
1857 pa = pvec.ctxpvec(ca)
1856 pa = pvec.ctxpvec(ca)
1858 pb = pvec.ctxpvec(cb)
1857 pb = pvec.ctxpvec(cb)
1859 if pa == pb:
1858 if pa == pb:
1860 rel = "="
1859 rel = "="
1861 elif pa > pb:
1860 elif pa > pb:
1862 rel = ">"
1861 rel = ">"
1863 elif pa < pb:
1862 elif pa < pb:
1864 rel = "<"
1863 rel = "<"
1865 elif pa | pb:
1864 elif pa | pb:
1866 rel = "|"
1865 rel = "|"
1867 ui.write(_("a: %s\n") % pa)
1866 ui.write(_("a: %s\n") % pa)
1868 ui.write(_("b: %s\n") % pb)
1867 ui.write(_("b: %s\n") % pb)
1869 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
1868 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
1870 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
1869 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
1871 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
1870 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
1872 pa.distance(pb), rel))
1871 pa.distance(pb), rel))
1873
1872
1874 @command('debugrebuilddirstate|debugrebuildstate',
1873 @command('debugrebuilddirstate|debugrebuildstate',
1875 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
1874 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
1876 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
1875 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
1877 'the working copy parent')),
1876 'the working copy parent')),
1878 ],
1877 ],
1879 _('[-r REV]'))
1878 _('[-r REV]'))
1880 def debugrebuilddirstate(ui, repo, rev, **opts):
1879 def debugrebuilddirstate(ui, repo, rev, **opts):
1881 """rebuild the dirstate as it would look like for the given revision
1880 """rebuild the dirstate as it would look like for the given revision
1882
1881
1883 If no revision is specified the first current parent will be used.
1882 If no revision is specified the first current parent will be used.
1884
1883
1885 The dirstate will be set to the files of the given revision.
1884 The dirstate will be set to the files of the given revision.
1886 The actual working directory content or existing dirstate
1885 The actual working directory content or existing dirstate
1887 information such as adds or removes is not considered.
1886 information such as adds or removes is not considered.
1888
1887
1889 ``minimal`` will only rebuild the dirstate status for files that claim to be
1888 ``minimal`` will only rebuild the dirstate status for files that claim to be
1890 tracked but are not in the parent manifest, or that exist in the parent
1889 tracked but are not in the parent manifest, or that exist in the parent
1891 manifest but are not in the dirstate. It will not change adds, removes, or
1890 manifest but are not in the dirstate. It will not change adds, removes, or
1892 modified files that are in the working copy parent.
1891 modified files that are in the working copy parent.
1893
1892
1894 One use of this command is to make the next :hg:`status` invocation
1893 One use of this command is to make the next :hg:`status` invocation
1895 check the actual file content.
1894 check the actual file content.
1896 """
1895 """
1897 ctx = scmutil.revsingle(repo, rev)
1896 ctx = scmutil.revsingle(repo, rev)
1898 with repo.wlock():
1897 with repo.wlock():
1899 dirstate = repo.dirstate
1898 dirstate = repo.dirstate
1900 changedfiles = None
1899 changedfiles = None
1901 # See command doc for what minimal does.
1900 # See command doc for what minimal does.
1902 if opts.get(r'minimal'):
1901 if opts.get(r'minimal'):
1903 manifestfiles = set(ctx.manifest().keys())
1902 manifestfiles = set(ctx.manifest().keys())
1904 dirstatefiles = set(dirstate)
1903 dirstatefiles = set(dirstate)
1905 manifestonly = manifestfiles - dirstatefiles
1904 manifestonly = manifestfiles - dirstatefiles
1906 dsonly = dirstatefiles - manifestfiles
1905 dsonly = dirstatefiles - manifestfiles
1907 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
1906 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
1908 changedfiles = manifestonly | dsnotadded
1907 changedfiles = manifestonly | dsnotadded
1909
1908
1910 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
1909 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
1911
1910
1912 @command('debugrebuildfncache', [], '')
1911 @command('debugrebuildfncache', [], '')
1913 def debugrebuildfncache(ui, repo):
1912 def debugrebuildfncache(ui, repo):
1914 """rebuild the fncache file"""
1913 """rebuild the fncache file"""
1915 repair.rebuildfncache(ui, repo)
1914 repair.rebuildfncache(ui, repo)
1916
1915
1917 @command('debugrename',
1916 @command('debugrename',
1918 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1917 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1919 _('[-r REV] FILE'))
1918 _('[-r REV] FILE'))
1920 def debugrename(ui, repo, file1, *pats, **opts):
1919 def debugrename(ui, repo, file1, *pats, **opts):
1921 """dump rename information"""
1920 """dump rename information"""
1922
1921
1923 opts = pycompat.byteskwargs(opts)
1922 opts = pycompat.byteskwargs(opts)
1924 ctx = scmutil.revsingle(repo, opts.get('rev'))
1923 ctx = scmutil.revsingle(repo, opts.get('rev'))
1925 m = scmutil.match(ctx, (file1,) + pats, opts)
1924 m = scmutil.match(ctx, (file1,) + pats, opts)
1926 for abs in ctx.walk(m):
1925 for abs in ctx.walk(m):
1927 fctx = ctx[abs]
1926 fctx = ctx[abs]
1928 o = fctx.filelog().renamed(fctx.filenode())
1927 o = fctx.filelog().renamed(fctx.filenode())
1929 rel = m.rel(abs)
1928 rel = m.rel(abs)
1930 if o:
1929 if o:
1931 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1930 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1932 else:
1931 else:
1933 ui.write(_("%s not renamed\n") % rel)
1932 ui.write(_("%s not renamed\n") % rel)
1934
1933
1935 @command('debugrevlog', cmdutil.debugrevlogopts +
1934 @command('debugrevlog', cmdutil.debugrevlogopts +
1936 [('d', 'dump', False, _('dump index data'))],
1935 [('d', 'dump', False, _('dump index data'))],
1937 _('-c|-m|FILE'),
1936 _('-c|-m|FILE'),
1938 optionalrepo=True)
1937 optionalrepo=True)
1939 def debugrevlog(ui, repo, file_=None, **opts):
1938 def debugrevlog(ui, repo, file_=None, **opts):
1940 """show data and statistics about a revlog"""
1939 """show data and statistics about a revlog"""
1941 opts = pycompat.byteskwargs(opts)
1940 opts = pycompat.byteskwargs(opts)
1942 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1941 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1943
1942
1944 if opts.get("dump"):
1943 if opts.get("dump"):
1945 numrevs = len(r)
1944 numrevs = len(r)
1946 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
1945 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
1947 " rawsize totalsize compression heads chainlen\n"))
1946 " rawsize totalsize compression heads chainlen\n"))
1948 ts = 0
1947 ts = 0
1949 heads = set()
1948 heads = set()
1950
1949
1951 for rev in xrange(numrevs):
1950 for rev in xrange(numrevs):
1952 dbase = r.deltaparent(rev)
1951 dbase = r.deltaparent(rev)
1953 if dbase == -1:
1952 if dbase == -1:
1954 dbase = rev
1953 dbase = rev
1955 cbase = r.chainbase(rev)
1954 cbase = r.chainbase(rev)
1956 clen = r.chainlen(rev)
1955 clen = r.chainlen(rev)
1957 p1, p2 = r.parentrevs(rev)
1956 p1, p2 = r.parentrevs(rev)
1958 rs = r.rawsize(rev)
1957 rs = r.rawsize(rev)
1959 ts = ts + rs
1958 ts = ts + rs
1960 heads -= set(r.parentrevs(rev))
1959 heads -= set(r.parentrevs(rev))
1961 heads.add(rev)
1960 heads.add(rev)
1962 try:
1961 try:
1963 compression = ts / r.end(rev)
1962 compression = ts / r.end(rev)
1964 except ZeroDivisionError:
1963 except ZeroDivisionError:
1965 compression = 0
1964 compression = 0
1966 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
1965 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
1967 "%11d %5d %8d\n" %
1966 "%11d %5d %8d\n" %
1968 (rev, p1, p2, r.start(rev), r.end(rev),
1967 (rev, p1, p2, r.start(rev), r.end(rev),
1969 r.start(dbase), r.start(cbase),
1968 r.start(dbase), r.start(cbase),
1970 r.start(p1), r.start(p2),
1969 r.start(p1), r.start(p2),
1971 rs, ts, compression, len(heads), clen))
1970 rs, ts, compression, len(heads), clen))
1972 return 0
1971 return 0
1973
1972
1974 v = r.version
1973 v = r.version
1975 format = v & 0xFFFF
1974 format = v & 0xFFFF
1976 flags = []
1975 flags = []
1977 gdelta = False
1976 gdelta = False
1978 if v & revlog.FLAG_INLINE_DATA:
1977 if v & revlog.FLAG_INLINE_DATA:
1979 flags.append('inline')
1978 flags.append('inline')
1980 if v & revlog.FLAG_GENERALDELTA:
1979 if v & revlog.FLAG_GENERALDELTA:
1981 gdelta = True
1980 gdelta = True
1982 flags.append('generaldelta')
1981 flags.append('generaldelta')
1983 if not flags:
1982 if not flags:
1984 flags = ['(none)']
1983 flags = ['(none)']
1985
1984
1986 nummerges = 0
1985 nummerges = 0
1987 numfull = 0
1986 numfull = 0
1988 numprev = 0
1987 numprev = 0
1989 nump1 = 0
1988 nump1 = 0
1990 nump2 = 0
1989 nump2 = 0
1991 numother = 0
1990 numother = 0
1992 nump1prev = 0
1991 nump1prev = 0
1993 nump2prev = 0
1992 nump2prev = 0
1994 chainlengths = []
1993 chainlengths = []
1995 chainbases = []
1994 chainbases = []
1996 chainspans = []
1995 chainspans = []
1997
1996
1998 datasize = [None, 0, 0]
1997 datasize = [None, 0, 0]
1999 fullsize = [None, 0, 0]
1998 fullsize = [None, 0, 0]
2000 deltasize = [None, 0, 0]
1999 deltasize = [None, 0, 0]
2001 chunktypecounts = {}
2000 chunktypecounts = {}
2002 chunktypesizes = {}
2001 chunktypesizes = {}
2003
2002
2004 def addsize(size, l):
2003 def addsize(size, l):
2005 if l[0] is None or size < l[0]:
2004 if l[0] is None or size < l[0]:
2006 l[0] = size
2005 l[0] = size
2007 if size > l[1]:
2006 if size > l[1]:
2008 l[1] = size
2007 l[1] = size
2009 l[2] += size
2008 l[2] += size
2010
2009
2011 numrevs = len(r)
2010 numrevs = len(r)
2012 for rev in xrange(numrevs):
2011 for rev in xrange(numrevs):
2013 p1, p2 = r.parentrevs(rev)
2012 p1, p2 = r.parentrevs(rev)
2014 delta = r.deltaparent(rev)
2013 delta = r.deltaparent(rev)
2015 if format > 0:
2014 if format > 0:
2016 addsize(r.rawsize(rev), datasize)
2015 addsize(r.rawsize(rev), datasize)
2017 if p2 != nullrev:
2016 if p2 != nullrev:
2018 nummerges += 1
2017 nummerges += 1
2019 size = r.length(rev)
2018 size = r.length(rev)
2020 if delta == nullrev:
2019 if delta == nullrev:
2021 chainlengths.append(0)
2020 chainlengths.append(0)
2022 chainbases.append(r.start(rev))
2021 chainbases.append(r.start(rev))
2023 chainspans.append(size)
2022 chainspans.append(size)
2024 numfull += 1
2023 numfull += 1
2025 addsize(size, fullsize)
2024 addsize(size, fullsize)
2026 else:
2025 else:
2027 chainlengths.append(chainlengths[delta] + 1)
2026 chainlengths.append(chainlengths[delta] + 1)
2028 baseaddr = chainbases[delta]
2027 baseaddr = chainbases[delta]
2029 revaddr = r.start(rev)
2028 revaddr = r.start(rev)
2030 chainbases.append(baseaddr)
2029 chainbases.append(baseaddr)
2031 chainspans.append((revaddr - baseaddr) + size)
2030 chainspans.append((revaddr - baseaddr) + size)
2032 addsize(size, deltasize)
2031 addsize(size, deltasize)
2033 if delta == rev - 1:
2032 if delta == rev - 1:
2034 numprev += 1
2033 numprev += 1
2035 if delta == p1:
2034 if delta == p1:
2036 nump1prev += 1
2035 nump1prev += 1
2037 elif delta == p2:
2036 elif delta == p2:
2038 nump2prev += 1
2037 nump2prev += 1
2039 elif delta == p1:
2038 elif delta == p1:
2040 nump1 += 1
2039 nump1 += 1
2041 elif delta == p2:
2040 elif delta == p2:
2042 nump2 += 1
2041 nump2 += 1
2043 elif delta != nullrev:
2042 elif delta != nullrev:
2044 numother += 1
2043 numother += 1
2045
2044
2046 # Obtain data on the raw chunks in the revlog.
2045 # Obtain data on the raw chunks in the revlog.
2047 segment = r._getsegmentforrevs(rev, rev)[1]
2046 segment = r._getsegmentforrevs(rev, rev)[1]
2048 if segment:
2047 if segment:
2049 chunktype = bytes(segment[0:1])
2048 chunktype = bytes(segment[0:1])
2050 else:
2049 else:
2051 chunktype = 'empty'
2050 chunktype = 'empty'
2052
2051
2053 if chunktype not in chunktypecounts:
2052 if chunktype not in chunktypecounts:
2054 chunktypecounts[chunktype] = 0
2053 chunktypecounts[chunktype] = 0
2055 chunktypesizes[chunktype] = 0
2054 chunktypesizes[chunktype] = 0
2056
2055
2057 chunktypecounts[chunktype] += 1
2056 chunktypecounts[chunktype] += 1
2058 chunktypesizes[chunktype] += size
2057 chunktypesizes[chunktype] += size
2059
2058
2060 # Adjust size min value for empty cases
2059 # Adjust size min value for empty cases
2061 for size in (datasize, fullsize, deltasize):
2060 for size in (datasize, fullsize, deltasize):
2062 if size[0] is None:
2061 if size[0] is None:
2063 size[0] = 0
2062 size[0] = 0
2064
2063
2065 numdeltas = numrevs - numfull
2064 numdeltas = numrevs - numfull
2066 numoprev = numprev - nump1prev - nump2prev
2065 numoprev = numprev - nump1prev - nump2prev
2067 totalrawsize = datasize[2]
2066 totalrawsize = datasize[2]
2068 datasize[2] /= numrevs
2067 datasize[2] /= numrevs
2069 fulltotal = fullsize[2]
2068 fulltotal = fullsize[2]
2070 fullsize[2] /= numfull
2069 fullsize[2] /= numfull
2071 deltatotal = deltasize[2]
2070 deltatotal = deltasize[2]
2072 if numrevs - numfull > 0:
2071 if numrevs - numfull > 0:
2073 deltasize[2] /= numrevs - numfull
2072 deltasize[2] /= numrevs - numfull
2074 totalsize = fulltotal + deltatotal
2073 totalsize = fulltotal + deltatotal
2075 avgchainlen = sum(chainlengths) / numrevs
2074 avgchainlen = sum(chainlengths) / numrevs
2076 maxchainlen = max(chainlengths)
2075 maxchainlen = max(chainlengths)
2077 maxchainspan = max(chainspans)
2076 maxchainspan = max(chainspans)
2078 compratio = 1
2077 compratio = 1
2079 if totalsize:
2078 if totalsize:
2080 compratio = totalrawsize / totalsize
2079 compratio = totalrawsize / totalsize
2081
2080
2082 basedfmtstr = '%%%dd\n'
2081 basedfmtstr = '%%%dd\n'
2083 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2082 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2084
2083
2085 def dfmtstr(max):
2084 def dfmtstr(max):
2086 return basedfmtstr % len(str(max))
2085 return basedfmtstr % len(str(max))
2087 def pcfmtstr(max, padding=0):
2086 def pcfmtstr(max, padding=0):
2088 return basepcfmtstr % (len(str(max)), ' ' * padding)
2087 return basepcfmtstr % (len(str(max)), ' ' * padding)
2089
2088
2090 def pcfmt(value, total):
2089 def pcfmt(value, total):
2091 if total:
2090 if total:
2092 return (value, 100 * float(value) / total)
2091 return (value, 100 * float(value) / total)
2093 else:
2092 else:
2094 return value, 100.0
2093 return value, 100.0
2095
2094
2096 ui.write(('format : %d\n') % format)
2095 ui.write(('format : %d\n') % format)
2097 ui.write(('flags : %s\n') % ', '.join(flags))
2096 ui.write(('flags : %s\n') % ', '.join(flags))
2098
2097
2099 ui.write('\n')
2098 ui.write('\n')
2100 fmt = pcfmtstr(totalsize)
2099 fmt = pcfmtstr(totalsize)
2101 fmt2 = dfmtstr(totalsize)
2100 fmt2 = dfmtstr(totalsize)
2102 ui.write(('revisions : ') + fmt2 % numrevs)
2101 ui.write(('revisions : ') + fmt2 % numrevs)
2103 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2102 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2104 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2103 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2105 ui.write(('revisions : ') + fmt2 % numrevs)
2104 ui.write(('revisions : ') + fmt2 % numrevs)
2106 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2105 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2107 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2106 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2108 ui.write(('revision size : ') + fmt2 % totalsize)
2107 ui.write(('revision size : ') + fmt2 % totalsize)
2109 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2108 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2110 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2109 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2111
2110
2112 def fmtchunktype(chunktype):
2111 def fmtchunktype(chunktype):
2113 if chunktype == 'empty':
2112 if chunktype == 'empty':
2114 return ' %s : ' % chunktype
2113 return ' %s : ' % chunktype
2115 elif chunktype in pycompat.bytestr(string.ascii_letters):
2114 elif chunktype in pycompat.bytestr(string.ascii_letters):
2116 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
2115 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
2117 else:
2116 else:
2118 return ' 0x%s : ' % hex(chunktype)
2117 return ' 0x%s : ' % hex(chunktype)
2119
2118
2120 ui.write('\n')
2119 ui.write('\n')
2121 ui.write(('chunks : ') + fmt2 % numrevs)
2120 ui.write(('chunks : ') + fmt2 % numrevs)
2122 for chunktype in sorted(chunktypecounts):
2121 for chunktype in sorted(chunktypecounts):
2123 ui.write(fmtchunktype(chunktype))
2122 ui.write(fmtchunktype(chunktype))
2124 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
2123 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
2125 ui.write(('chunks size : ') + fmt2 % totalsize)
2124 ui.write(('chunks size : ') + fmt2 % totalsize)
2126 for chunktype in sorted(chunktypecounts):
2125 for chunktype in sorted(chunktypecounts):
2127 ui.write(fmtchunktype(chunktype))
2126 ui.write(fmtchunktype(chunktype))
2128 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
2127 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
2129
2128
2130 ui.write('\n')
2129 ui.write('\n')
2131 fmt = dfmtstr(max(avgchainlen, maxchainlen, maxchainspan, compratio))
2130 fmt = dfmtstr(max(avgchainlen, maxchainlen, maxchainspan, compratio))
2132 ui.write(('avg chain length : ') + fmt % avgchainlen)
2131 ui.write(('avg chain length : ') + fmt % avgchainlen)
2133 ui.write(('max chain length : ') + fmt % maxchainlen)
2132 ui.write(('max chain length : ') + fmt % maxchainlen)
2134 ui.write(('max chain reach : ') + fmt % maxchainspan)
2133 ui.write(('max chain reach : ') + fmt % maxchainspan)
2135 ui.write(('compression ratio : ') + fmt % compratio)
2134 ui.write(('compression ratio : ') + fmt % compratio)
2136
2135
2137 if format > 0:
2136 if format > 0:
2138 ui.write('\n')
2137 ui.write('\n')
2139 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2138 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2140 % tuple(datasize))
2139 % tuple(datasize))
2141 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2140 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2142 % tuple(fullsize))
2141 % tuple(fullsize))
2143 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2142 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2144 % tuple(deltasize))
2143 % tuple(deltasize))
2145
2144
2146 if numdeltas > 0:
2145 if numdeltas > 0:
2147 ui.write('\n')
2146 ui.write('\n')
2148 fmt = pcfmtstr(numdeltas)
2147 fmt = pcfmtstr(numdeltas)
2149 fmt2 = pcfmtstr(numdeltas, 4)
2148 fmt2 = pcfmtstr(numdeltas, 4)
2150 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2149 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2151 if numprev > 0:
2150 if numprev > 0:
2152 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2151 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2153 numprev))
2152 numprev))
2154 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2153 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2155 numprev))
2154 numprev))
2156 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2155 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2157 numprev))
2156 numprev))
2158 if gdelta:
2157 if gdelta:
2159 ui.write(('deltas against p1 : ')
2158 ui.write(('deltas against p1 : ')
2160 + fmt % pcfmt(nump1, numdeltas))
2159 + fmt % pcfmt(nump1, numdeltas))
2161 ui.write(('deltas against p2 : ')
2160 ui.write(('deltas against p2 : ')
2162 + fmt % pcfmt(nump2, numdeltas))
2161 + fmt % pcfmt(nump2, numdeltas))
2163 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2162 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2164 numdeltas))
2163 numdeltas))
2165
2164
2166 @command('debugrevspec',
2165 @command('debugrevspec',
2167 [('', 'optimize', None,
2166 [('', 'optimize', None,
2168 _('print parsed tree after optimizing (DEPRECATED)')),
2167 _('print parsed tree after optimizing (DEPRECATED)')),
2169 ('', 'show-revs', True, _('print list of result revisions (default)')),
2168 ('', 'show-revs', True, _('print list of result revisions (default)')),
2170 ('s', 'show-set', None, _('print internal representation of result set')),
2169 ('s', 'show-set', None, _('print internal representation of result set')),
2171 ('p', 'show-stage', [],
2170 ('p', 'show-stage', [],
2172 _('print parsed tree at the given stage'), _('NAME')),
2171 _('print parsed tree at the given stage'), _('NAME')),
2173 ('', 'no-optimized', False, _('evaluate tree without optimization')),
2172 ('', 'no-optimized', False, _('evaluate tree without optimization')),
2174 ('', 'verify-optimized', False, _('verify optimized result')),
2173 ('', 'verify-optimized', False, _('verify optimized result')),
2175 ],
2174 ],
2176 ('REVSPEC'))
2175 ('REVSPEC'))
2177 def debugrevspec(ui, repo, expr, **opts):
2176 def debugrevspec(ui, repo, expr, **opts):
2178 """parse and apply a revision specification
2177 """parse and apply a revision specification
2179
2178
2180 Use -p/--show-stage option to print the parsed tree at the given stages.
2179 Use -p/--show-stage option to print the parsed tree at the given stages.
2181 Use -p all to print tree at every stage.
2180 Use -p all to print tree at every stage.
2182
2181
2183 Use --no-show-revs option with -s or -p to print only the set
2182 Use --no-show-revs option with -s or -p to print only the set
2184 representation or the parsed tree respectively.
2183 representation or the parsed tree respectively.
2185
2184
2186 Use --verify-optimized to compare the optimized result with the unoptimized
2185 Use --verify-optimized to compare the optimized result with the unoptimized
2187 one. Returns 1 if the optimized result differs.
2186 one. Returns 1 if the optimized result differs.
2188 """
2187 """
2189 opts = pycompat.byteskwargs(opts)
2188 opts = pycompat.byteskwargs(opts)
2190 aliases = ui.configitems('revsetalias')
2189 aliases = ui.configitems('revsetalias')
2191 stages = [
2190 stages = [
2192 ('parsed', lambda tree: tree),
2191 ('parsed', lambda tree: tree),
2193 ('expanded', lambda tree: revsetlang.expandaliases(tree, aliases,
2192 ('expanded', lambda tree: revsetlang.expandaliases(tree, aliases,
2194 ui.warn)),
2193 ui.warn)),
2195 ('concatenated', revsetlang.foldconcat),
2194 ('concatenated', revsetlang.foldconcat),
2196 ('analyzed', revsetlang.analyze),
2195 ('analyzed', revsetlang.analyze),
2197 ('optimized', revsetlang.optimize),
2196 ('optimized', revsetlang.optimize),
2198 ]
2197 ]
2199 if opts['no_optimized']:
2198 if opts['no_optimized']:
2200 stages = stages[:-1]
2199 stages = stages[:-1]
2201 if opts['verify_optimized'] and opts['no_optimized']:
2200 if opts['verify_optimized'] and opts['no_optimized']:
2202 raise error.Abort(_('cannot use --verify-optimized with '
2201 raise error.Abort(_('cannot use --verify-optimized with '
2203 '--no-optimized'))
2202 '--no-optimized'))
2204 stagenames = set(n for n, f in stages)
2203 stagenames = set(n for n, f in stages)
2205
2204
2206 showalways = set()
2205 showalways = set()
2207 showchanged = set()
2206 showchanged = set()
2208 if ui.verbose and not opts['show_stage']:
2207 if ui.verbose and not opts['show_stage']:
2209 # show parsed tree by --verbose (deprecated)
2208 # show parsed tree by --verbose (deprecated)
2210 showalways.add('parsed')
2209 showalways.add('parsed')
2211 showchanged.update(['expanded', 'concatenated'])
2210 showchanged.update(['expanded', 'concatenated'])
2212 if opts['optimize']:
2211 if opts['optimize']:
2213 showalways.add('optimized')
2212 showalways.add('optimized')
2214 if opts['show_stage'] and opts['optimize']:
2213 if opts['show_stage'] and opts['optimize']:
2215 raise error.Abort(_('cannot use --optimize with --show-stage'))
2214 raise error.Abort(_('cannot use --optimize with --show-stage'))
2216 if opts['show_stage'] == ['all']:
2215 if opts['show_stage'] == ['all']:
2217 showalways.update(stagenames)
2216 showalways.update(stagenames)
2218 else:
2217 else:
2219 for n in opts['show_stage']:
2218 for n in opts['show_stage']:
2220 if n not in stagenames:
2219 if n not in stagenames:
2221 raise error.Abort(_('invalid stage name: %s') % n)
2220 raise error.Abort(_('invalid stage name: %s') % n)
2222 showalways.update(opts['show_stage'])
2221 showalways.update(opts['show_stage'])
2223
2222
2224 treebystage = {}
2223 treebystage = {}
2225 printedtree = None
2224 printedtree = None
2226 tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo))
2225 tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo))
2227 for n, f in stages:
2226 for n, f in stages:
2228 treebystage[n] = tree = f(tree)
2227 treebystage[n] = tree = f(tree)
2229 if n in showalways or (n in showchanged and tree != printedtree):
2228 if n in showalways or (n in showchanged and tree != printedtree):
2230 if opts['show_stage'] or n != 'parsed':
2229 if opts['show_stage'] or n != 'parsed':
2231 ui.write(("* %s:\n") % n)
2230 ui.write(("* %s:\n") % n)
2232 ui.write(revsetlang.prettyformat(tree), "\n")
2231 ui.write(revsetlang.prettyformat(tree), "\n")
2233 printedtree = tree
2232 printedtree = tree
2234
2233
2235 if opts['verify_optimized']:
2234 if opts['verify_optimized']:
2236 arevs = revset.makematcher(treebystage['analyzed'])(repo)
2235 arevs = revset.makematcher(treebystage['analyzed'])(repo)
2237 brevs = revset.makematcher(treebystage['optimized'])(repo)
2236 brevs = revset.makematcher(treebystage['optimized'])(repo)
2238 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2237 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2239 ui.write(("* analyzed set:\n"), smartset.prettyformat(arevs), "\n")
2238 ui.write(("* analyzed set:\n"), smartset.prettyformat(arevs), "\n")
2240 ui.write(("* optimized set:\n"), smartset.prettyformat(brevs), "\n")
2239 ui.write(("* optimized set:\n"), smartset.prettyformat(brevs), "\n")
2241 arevs = list(arevs)
2240 arevs = list(arevs)
2242 brevs = list(brevs)
2241 brevs = list(brevs)
2243 if arevs == brevs:
2242 if arevs == brevs:
2244 return 0
2243 return 0
2245 ui.write(('--- analyzed\n'), label='diff.file_a')
2244 ui.write(('--- analyzed\n'), label='diff.file_a')
2246 ui.write(('+++ optimized\n'), label='diff.file_b')
2245 ui.write(('+++ optimized\n'), label='diff.file_b')
2247 sm = difflib.SequenceMatcher(None, arevs, brevs)
2246 sm = difflib.SequenceMatcher(None, arevs, brevs)
2248 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2247 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2249 if tag in ('delete', 'replace'):
2248 if tag in ('delete', 'replace'):
2250 for c in arevs[alo:ahi]:
2249 for c in arevs[alo:ahi]:
2251 ui.write('-%s\n' % c, label='diff.deleted')
2250 ui.write('-%s\n' % c, label='diff.deleted')
2252 if tag in ('insert', 'replace'):
2251 if tag in ('insert', 'replace'):
2253 for c in brevs[blo:bhi]:
2252 for c in brevs[blo:bhi]:
2254 ui.write('+%s\n' % c, label='diff.inserted')
2253 ui.write('+%s\n' % c, label='diff.inserted')
2255 if tag == 'equal':
2254 if tag == 'equal':
2256 for c in arevs[alo:ahi]:
2255 for c in arevs[alo:ahi]:
2257 ui.write(' %s\n' % c)
2256 ui.write(' %s\n' % c)
2258 return 1
2257 return 1
2259
2258
2260 func = revset.makematcher(tree)
2259 func = revset.makematcher(tree)
2261 revs = func(repo)
2260 revs = func(repo)
2262 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2261 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2263 ui.write(("* set:\n"), smartset.prettyformat(revs), "\n")
2262 ui.write(("* set:\n"), smartset.prettyformat(revs), "\n")
2264 if not opts['show_revs']:
2263 if not opts['show_revs']:
2265 return
2264 return
2266 for c in revs:
2265 for c in revs:
2267 ui.write("%d\n" % c)
2266 ui.write("%d\n" % c)
2268
2267
2269 @command('debugserve', [
2268 @command('debugserve', [
2270 ('', 'sshstdio', False, _('run an SSH server bound to process handles')),
2269 ('', 'sshstdio', False, _('run an SSH server bound to process handles')),
2271 ('', 'logiofd', '', _('file descriptor to log server I/O to')),
2270 ('', 'logiofd', '', _('file descriptor to log server I/O to')),
2272 ('', 'logiofile', '', _('file to log server I/O to')),
2271 ('', 'logiofile', '', _('file to log server I/O to')),
2273 ], '')
2272 ], '')
2274 def debugserve(ui, repo, **opts):
2273 def debugserve(ui, repo, **opts):
2275 """run a server with advanced settings
2274 """run a server with advanced settings
2276
2275
2277 This command is similar to :hg:`serve`. It exists partially as a
2276 This command is similar to :hg:`serve`. It exists partially as a
2278 workaround to the fact that ``hg serve --stdio`` must have specific
2277 workaround to the fact that ``hg serve --stdio`` must have specific
2279 arguments for security reasons.
2278 arguments for security reasons.
2280 """
2279 """
2281 opts = pycompat.byteskwargs(opts)
2280 opts = pycompat.byteskwargs(opts)
2282
2281
2283 if not opts['sshstdio']:
2282 if not opts['sshstdio']:
2284 raise error.Abort(_('only --sshstdio is currently supported'))
2283 raise error.Abort(_('only --sshstdio is currently supported'))
2285
2284
2286 logfh = None
2285 logfh = None
2287
2286
2288 if opts['logiofd'] and opts['logiofile']:
2287 if opts['logiofd'] and opts['logiofile']:
2289 raise error.Abort(_('cannot use both --logiofd and --logiofile'))
2288 raise error.Abort(_('cannot use both --logiofd and --logiofile'))
2290
2289
2291 if opts['logiofd']:
2290 if opts['logiofd']:
2292 # Line buffered because output is line based.
2291 # Line buffered because output is line based.
2293 logfh = os.fdopen(int(opts['logiofd']), r'ab', 1)
2292 logfh = os.fdopen(int(opts['logiofd']), r'ab', 1)
2294 elif opts['logiofile']:
2293 elif opts['logiofile']:
2295 logfh = open(opts['logiofile'], 'ab', 1)
2294 logfh = open(opts['logiofile'], 'ab', 1)
2296
2295
2297 s = wireprotoserver.sshserver(ui, repo, logfh=logfh)
2296 s = wireprotoserver.sshserver(ui, repo, logfh=logfh)
2298 s.serve_forever()
2297 s.serve_forever()
2299
2298
2300 @command('debugsetparents', [], _('REV1 [REV2]'))
2299 @command('debugsetparents', [], _('REV1 [REV2]'))
2301 def debugsetparents(ui, repo, rev1, rev2=None):
2300 def debugsetparents(ui, repo, rev1, rev2=None):
2302 """manually set the parents of the current working directory
2301 """manually set the parents of the current working directory
2303
2302
2304 This is useful for writing repository conversion tools, but should
2303 This is useful for writing repository conversion tools, but should
2305 be used with care. For example, neither the working directory nor the
2304 be used with care. For example, neither the working directory nor the
2306 dirstate is updated, so file status may be incorrect after running this
2305 dirstate is updated, so file status may be incorrect after running this
2307 command.
2306 command.
2308
2307
2309 Returns 0 on success.
2308 Returns 0 on success.
2310 """
2309 """
2311
2310
2312 node1 = scmutil.revsingle(repo, rev1).node()
2311 node1 = scmutil.revsingle(repo, rev1).node()
2313 node2 = scmutil.revsingle(repo, rev2, 'null').node()
2312 node2 = scmutil.revsingle(repo, rev2, 'null').node()
2314
2313
2315 with repo.wlock():
2314 with repo.wlock():
2316 repo.setparents(node1, node2)
2315 repo.setparents(node1, node2)
2317
2316
2318 @command('debugssl', [], '[SOURCE]', optionalrepo=True)
2317 @command('debugssl', [], '[SOURCE]', optionalrepo=True)
2319 def debugssl(ui, repo, source=None, **opts):
2318 def debugssl(ui, repo, source=None, **opts):
2320 '''test a secure connection to a server
2319 '''test a secure connection to a server
2321
2320
2322 This builds the certificate chain for the server on Windows, installing the
2321 This builds the certificate chain for the server on Windows, installing the
2323 missing intermediates and trusted root via Windows Update if necessary. It
2322 missing intermediates and trusted root via Windows Update if necessary. It
2324 does nothing on other platforms.
2323 does nothing on other platforms.
2325
2324
2326 If SOURCE is omitted, the 'default' path will be used. If a URL is given,
2325 If SOURCE is omitted, the 'default' path will be used. If a URL is given,
2327 that server is used. See :hg:`help urls` for more information.
2326 that server is used. See :hg:`help urls` for more information.
2328
2327
2329 If the update succeeds, retry the original operation. Otherwise, the cause
2328 If the update succeeds, retry the original operation. Otherwise, the cause
2330 of the SSL error is likely another issue.
2329 of the SSL error is likely another issue.
2331 '''
2330 '''
2332 if not pycompat.iswindows:
2331 if not pycompat.iswindows:
2333 raise error.Abort(_('certificate chain building is only possible on '
2332 raise error.Abort(_('certificate chain building is only possible on '
2334 'Windows'))
2333 'Windows'))
2335
2334
2336 if not source:
2335 if not source:
2337 if not repo:
2336 if not repo:
2338 raise error.Abort(_("there is no Mercurial repository here, and no "
2337 raise error.Abort(_("there is no Mercurial repository here, and no "
2339 "server specified"))
2338 "server specified"))
2340 source = "default"
2339 source = "default"
2341
2340
2342 source, branches = hg.parseurl(ui.expandpath(source))
2341 source, branches = hg.parseurl(ui.expandpath(source))
2343 url = util.url(source)
2342 url = util.url(source)
2344 addr = None
2343 addr = None
2345
2344
2346 defaultport = {'https': 443, 'ssh': 22}
2345 defaultport = {'https': 443, 'ssh': 22}
2347 if url.scheme in defaultport:
2346 if url.scheme in defaultport:
2348 try:
2347 try:
2349 addr = (url.host, int(url.port or defaultport[url.scheme]))
2348 addr = (url.host, int(url.port or defaultport[url.scheme]))
2350 except ValueError:
2349 except ValueError:
2351 raise error.Abort(_("malformed port number in URL"))
2350 raise error.Abort(_("malformed port number in URL"))
2352 else:
2351 else:
2353 raise error.Abort(_("only https and ssh connections are supported"))
2352 raise error.Abort(_("only https and ssh connections are supported"))
2354
2353
2355 from . import win32
2354 from . import win32
2356
2355
2357 s = ssl.wrap_socket(socket.socket(), ssl_version=ssl.PROTOCOL_TLS,
2356 s = ssl.wrap_socket(socket.socket(), ssl_version=ssl.PROTOCOL_TLS,
2358 cert_reqs=ssl.CERT_NONE, ca_certs=None)
2357 cert_reqs=ssl.CERT_NONE, ca_certs=None)
2359
2358
2360 try:
2359 try:
2361 s.connect(addr)
2360 s.connect(addr)
2362 cert = s.getpeercert(True)
2361 cert = s.getpeercert(True)
2363
2362
2364 ui.status(_('checking the certificate chain for %s\n') % url.host)
2363 ui.status(_('checking the certificate chain for %s\n') % url.host)
2365
2364
2366 complete = win32.checkcertificatechain(cert, build=False)
2365 complete = win32.checkcertificatechain(cert, build=False)
2367
2366
2368 if not complete:
2367 if not complete:
2369 ui.status(_('certificate chain is incomplete, updating... '))
2368 ui.status(_('certificate chain is incomplete, updating... '))
2370
2369
2371 if not win32.checkcertificatechain(cert):
2370 if not win32.checkcertificatechain(cert):
2372 ui.status(_('failed.\n'))
2371 ui.status(_('failed.\n'))
2373 else:
2372 else:
2374 ui.status(_('done.\n'))
2373 ui.status(_('done.\n'))
2375 else:
2374 else:
2376 ui.status(_('full certificate chain is available\n'))
2375 ui.status(_('full certificate chain is available\n'))
2377 finally:
2376 finally:
2378 s.close()
2377 s.close()
2379
2378
2380 @command('debugsub',
2379 @command('debugsub',
2381 [('r', 'rev', '',
2380 [('r', 'rev', '',
2382 _('revision to check'), _('REV'))],
2381 _('revision to check'), _('REV'))],
2383 _('[-r REV] [REV]'))
2382 _('[-r REV] [REV]'))
2384 def debugsub(ui, repo, rev=None):
2383 def debugsub(ui, repo, rev=None):
2385 ctx = scmutil.revsingle(repo, rev, None)
2384 ctx = scmutil.revsingle(repo, rev, None)
2386 for k, v in sorted(ctx.substate.items()):
2385 for k, v in sorted(ctx.substate.items()):
2387 ui.write(('path %s\n') % k)
2386 ui.write(('path %s\n') % k)
2388 ui.write((' source %s\n') % v[0])
2387 ui.write((' source %s\n') % v[0])
2389 ui.write((' revision %s\n') % v[1])
2388 ui.write((' revision %s\n') % v[1])
2390
2389
2391 @command('debugsuccessorssets',
2390 @command('debugsuccessorssets',
2392 [('', 'closest', False, _('return closest successors sets only'))],
2391 [('', 'closest', False, _('return closest successors sets only'))],
2393 _('[REV]'))
2392 _('[REV]'))
2394 def debugsuccessorssets(ui, repo, *revs, **opts):
2393 def debugsuccessorssets(ui, repo, *revs, **opts):
2395 """show set of successors for revision
2394 """show set of successors for revision
2396
2395
2397 A successors set of changeset A is a consistent group of revisions that
2396 A successors set of changeset A is a consistent group of revisions that
2398 succeed A. It contains non-obsolete changesets only unless closests
2397 succeed A. It contains non-obsolete changesets only unless closests
2399 successors set is set.
2398 successors set is set.
2400
2399
2401 In most cases a changeset A has a single successors set containing a single
2400 In most cases a changeset A has a single successors set containing a single
2402 successor (changeset A replaced by A').
2401 successor (changeset A replaced by A').
2403
2402
2404 A changeset that is made obsolete with no successors are called "pruned".
2403 A changeset that is made obsolete with no successors are called "pruned".
2405 Such changesets have no successors sets at all.
2404 Such changesets have no successors sets at all.
2406
2405
2407 A changeset that has been "split" will have a successors set containing
2406 A changeset that has been "split" will have a successors set containing
2408 more than one successor.
2407 more than one successor.
2409
2408
2410 A changeset that has been rewritten in multiple different ways is called
2409 A changeset that has been rewritten in multiple different ways is called
2411 "divergent". Such changesets have multiple successor sets (each of which
2410 "divergent". Such changesets have multiple successor sets (each of which
2412 may also be split, i.e. have multiple successors).
2411 may also be split, i.e. have multiple successors).
2413
2412
2414 Results are displayed as follows::
2413 Results are displayed as follows::
2415
2414
2416 <rev1>
2415 <rev1>
2417 <successors-1A>
2416 <successors-1A>
2418 <rev2>
2417 <rev2>
2419 <successors-2A>
2418 <successors-2A>
2420 <successors-2B1> <successors-2B2> <successors-2B3>
2419 <successors-2B1> <successors-2B2> <successors-2B3>
2421
2420
2422 Here rev2 has two possible (i.e. divergent) successors sets. The first
2421 Here rev2 has two possible (i.e. divergent) successors sets. The first
2423 holds one element, whereas the second holds three (i.e. the changeset has
2422 holds one element, whereas the second holds three (i.e. the changeset has
2424 been split).
2423 been split).
2425 """
2424 """
2426 # passed to successorssets caching computation from one call to another
2425 # passed to successorssets caching computation from one call to another
2427 cache = {}
2426 cache = {}
2428 ctx2str = bytes
2427 ctx2str = bytes
2429 node2str = short
2428 node2str = short
2430 for rev in scmutil.revrange(repo, revs):
2429 for rev in scmutil.revrange(repo, revs):
2431 ctx = repo[rev]
2430 ctx = repo[rev]
2432 ui.write('%s\n'% ctx2str(ctx))
2431 ui.write('%s\n'% ctx2str(ctx))
2433 for succsset in obsutil.successorssets(repo, ctx.node(),
2432 for succsset in obsutil.successorssets(repo, ctx.node(),
2434 closest=opts[r'closest'],
2433 closest=opts[r'closest'],
2435 cache=cache):
2434 cache=cache):
2436 if succsset:
2435 if succsset:
2437 ui.write(' ')
2436 ui.write(' ')
2438 ui.write(node2str(succsset[0]))
2437 ui.write(node2str(succsset[0]))
2439 for node in succsset[1:]:
2438 for node in succsset[1:]:
2440 ui.write(' ')
2439 ui.write(' ')
2441 ui.write(node2str(node))
2440 ui.write(node2str(node))
2442 ui.write('\n')
2441 ui.write('\n')
2443
2442
2444 @command('debugtemplate',
2443 @command('debugtemplate',
2445 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
2444 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
2446 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
2445 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
2447 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
2446 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
2448 optionalrepo=True)
2447 optionalrepo=True)
2449 def debugtemplate(ui, repo, tmpl, **opts):
2448 def debugtemplate(ui, repo, tmpl, **opts):
2450 """parse and apply a template
2449 """parse and apply a template
2451
2450
2452 If -r/--rev is given, the template is processed as a log template and
2451 If -r/--rev is given, the template is processed as a log template and
2453 applied to the given changesets. Otherwise, it is processed as a generic
2452 applied to the given changesets. Otherwise, it is processed as a generic
2454 template.
2453 template.
2455
2454
2456 Use --verbose to print the parsed tree.
2455 Use --verbose to print the parsed tree.
2457 """
2456 """
2458 revs = None
2457 revs = None
2459 if opts[r'rev']:
2458 if opts[r'rev']:
2460 if repo is None:
2459 if repo is None:
2461 raise error.RepoError(_('there is no Mercurial repository here '
2460 raise error.RepoError(_('there is no Mercurial repository here '
2462 '(.hg not found)'))
2461 '(.hg not found)'))
2463 revs = scmutil.revrange(repo, opts[r'rev'])
2462 revs = scmutil.revrange(repo, opts[r'rev'])
2464
2463
2465 props = {}
2464 props = {}
2466 for d in opts[r'define']:
2465 for d in opts[r'define']:
2467 try:
2466 try:
2468 k, v = (e.strip() for e in d.split('=', 1))
2467 k, v = (e.strip() for e in d.split('=', 1))
2469 if not k or k == 'ui':
2468 if not k or k == 'ui':
2470 raise ValueError
2469 raise ValueError
2471 props[k] = v
2470 props[k] = v
2472 except ValueError:
2471 except ValueError:
2473 raise error.Abort(_('malformed keyword definition: %s') % d)
2472 raise error.Abort(_('malformed keyword definition: %s') % d)
2474
2473
2475 if ui.verbose:
2474 if ui.verbose:
2476 aliases = ui.configitems('templatealias')
2475 aliases = ui.configitems('templatealias')
2477 tree = templater.parse(tmpl)
2476 tree = templater.parse(tmpl)
2478 ui.note(templater.prettyformat(tree), '\n')
2477 ui.note(templater.prettyformat(tree), '\n')
2479 newtree = templater.expandaliases(tree, aliases)
2478 newtree = templater.expandaliases(tree, aliases)
2480 if newtree != tree:
2479 if newtree != tree:
2481 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
2480 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
2482
2481
2483 if revs is None:
2482 if revs is None:
2484 tres = formatter.templateresources(ui, repo)
2483 tres = formatter.templateresources(ui, repo)
2485 t = formatter.maketemplater(ui, tmpl, resources=tres)
2484 t = formatter.maketemplater(ui, tmpl, resources=tres)
2486 ui.write(t.renderdefault(props))
2485 ui.write(t.renderdefault(props))
2487 else:
2486 else:
2488 displayer = logcmdutil.maketemplater(ui, repo, tmpl)
2487 displayer = logcmdutil.maketemplater(ui, repo, tmpl)
2489 for r in revs:
2488 for r in revs:
2490 displayer.show(repo[r], **pycompat.strkwargs(props))
2489 displayer.show(repo[r], **pycompat.strkwargs(props))
2491 displayer.close()
2490 displayer.close()
2492
2491
2493 @command('debuguigetpass', [
2492 @command('debuguigetpass', [
2494 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2493 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2495 ], _('[-p TEXT]'), norepo=True)
2494 ], _('[-p TEXT]'), norepo=True)
2496 def debuguigetpass(ui, prompt=''):
2495 def debuguigetpass(ui, prompt=''):
2497 """show prompt to type password"""
2496 """show prompt to type password"""
2498 r = ui.getpass(prompt)
2497 r = ui.getpass(prompt)
2499 ui.write(('respose: %s\n') % r)
2498 ui.write(('respose: %s\n') % r)
2500
2499
2501 @command('debuguiprompt', [
2500 @command('debuguiprompt', [
2502 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2501 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2503 ], _('[-p TEXT]'), norepo=True)
2502 ], _('[-p TEXT]'), norepo=True)
2504 def debuguiprompt(ui, prompt=''):
2503 def debuguiprompt(ui, prompt=''):
2505 """show plain prompt"""
2504 """show plain prompt"""
2506 r = ui.prompt(prompt)
2505 r = ui.prompt(prompt)
2507 ui.write(('response: %s\n') % r)
2506 ui.write(('response: %s\n') % r)
2508
2507
2509 @command('debugupdatecaches', [])
2508 @command('debugupdatecaches', [])
2510 def debugupdatecaches(ui, repo, *pats, **opts):
2509 def debugupdatecaches(ui, repo, *pats, **opts):
2511 """warm all known caches in the repository"""
2510 """warm all known caches in the repository"""
2512 with repo.wlock(), repo.lock():
2511 with repo.wlock(), repo.lock():
2513 repo.updatecaches(full=True)
2512 repo.updatecaches(full=True)
2514
2513
2515 @command('debugupgraderepo', [
2514 @command('debugupgraderepo', [
2516 ('o', 'optimize', [], _('extra optimization to perform'), _('NAME')),
2515 ('o', 'optimize', [], _('extra optimization to perform'), _('NAME')),
2517 ('', 'run', False, _('performs an upgrade')),
2516 ('', 'run', False, _('performs an upgrade')),
2518 ])
2517 ])
2519 def debugupgraderepo(ui, repo, run=False, optimize=None):
2518 def debugupgraderepo(ui, repo, run=False, optimize=None):
2520 """upgrade a repository to use different features
2519 """upgrade a repository to use different features
2521
2520
2522 If no arguments are specified, the repository is evaluated for upgrade
2521 If no arguments are specified, the repository is evaluated for upgrade
2523 and a list of problems and potential optimizations is printed.
2522 and a list of problems and potential optimizations is printed.
2524
2523
2525 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
2524 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
2526 can be influenced via additional arguments. More details will be provided
2525 can be influenced via additional arguments. More details will be provided
2527 by the command output when run without ``--run``.
2526 by the command output when run without ``--run``.
2528
2527
2529 During the upgrade, the repository will be locked and no writes will be
2528 During the upgrade, the repository will be locked and no writes will be
2530 allowed.
2529 allowed.
2531
2530
2532 At the end of the upgrade, the repository may not be readable while new
2531 At the end of the upgrade, the repository may not be readable while new
2533 repository data is swapped in. This window will be as long as it takes to
2532 repository data is swapped in. This window will be as long as it takes to
2534 rename some directories inside the ``.hg`` directory. On most machines, this
2533 rename some directories inside the ``.hg`` directory. On most machines, this
2535 should complete almost instantaneously and the chances of a consumer being
2534 should complete almost instantaneously and the chances of a consumer being
2536 unable to access the repository should be low.
2535 unable to access the repository should be low.
2537 """
2536 """
2538 return upgrade.upgraderepo(ui, repo, run=run, optimize=optimize)
2537 return upgrade.upgraderepo(ui, repo, run=run, optimize=optimize)
2539
2538
2540 @command('debugwalk', cmdutil.walkopts, _('[OPTION]... [FILE]...'),
2539 @command('debugwalk', cmdutil.walkopts, _('[OPTION]... [FILE]...'),
2541 inferrepo=True)
2540 inferrepo=True)
2542 def debugwalk(ui, repo, *pats, **opts):
2541 def debugwalk(ui, repo, *pats, **opts):
2543 """show how files match on given patterns"""
2542 """show how files match on given patterns"""
2544 opts = pycompat.byteskwargs(opts)
2543 opts = pycompat.byteskwargs(opts)
2545 m = scmutil.match(repo[None], pats, opts)
2544 m = scmutil.match(repo[None], pats, opts)
2546 ui.write(('matcher: %r\n' % m))
2545 ui.write(('matcher: %r\n' % m))
2547 items = list(repo[None].walk(m))
2546 items = list(repo[None].walk(m))
2548 if not items:
2547 if not items:
2549 return
2548 return
2550 f = lambda fn: fn
2549 f = lambda fn: fn
2551 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
2550 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
2552 f = lambda fn: util.normpath(fn)
2551 f = lambda fn: util.normpath(fn)
2553 fmt = 'f %%-%ds %%-%ds %%s' % (
2552 fmt = 'f %%-%ds %%-%ds %%s' % (
2554 max([len(abs) for abs in items]),
2553 max([len(abs) for abs in items]),
2555 max([len(m.rel(abs)) for abs in items]))
2554 max([len(m.rel(abs)) for abs in items]))
2556 for abs in items:
2555 for abs in items:
2557 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2556 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2558 ui.write("%s\n" % line.rstrip())
2557 ui.write("%s\n" % line.rstrip())
2559
2558
2560 @command('debugwhyunstable', [], _('REV'))
2559 @command('debugwhyunstable', [], _('REV'))
2561 def debugwhyunstable(ui, repo, rev):
2560 def debugwhyunstable(ui, repo, rev):
2562 """explain instabilities of a changeset"""
2561 """explain instabilities of a changeset"""
2563 for entry in obsutil.whyunstable(repo, scmutil.revsingle(repo, rev)):
2562 for entry in obsutil.whyunstable(repo, scmutil.revsingle(repo, rev)):
2564 dnodes = ''
2563 dnodes = ''
2565 if entry.get('divergentnodes'):
2564 if entry.get('divergentnodes'):
2566 dnodes = ' '.join('%s (%s)' % (ctx.hex(), ctx.phasestr())
2565 dnodes = ' '.join('%s (%s)' % (ctx.hex(), ctx.phasestr())
2567 for ctx in entry['divergentnodes']) + ' '
2566 for ctx in entry['divergentnodes']) + ' '
2568 ui.write('%s: %s%s %s\n' % (entry['instability'], dnodes,
2567 ui.write('%s: %s%s %s\n' % (entry['instability'], dnodes,
2569 entry['reason'], entry['node']))
2568 entry['reason'], entry['node']))
2570
2569
2571 @command('debugwireargs',
2570 @command('debugwireargs',
2572 [('', 'three', '', 'three'),
2571 [('', 'three', '', 'three'),
2573 ('', 'four', '', 'four'),
2572 ('', 'four', '', 'four'),
2574 ('', 'five', '', 'five'),
2573 ('', 'five', '', 'five'),
2575 ] + cmdutil.remoteopts,
2574 ] + cmdutil.remoteopts,
2576 _('REPO [OPTIONS]... [ONE [TWO]]'),
2575 _('REPO [OPTIONS]... [ONE [TWO]]'),
2577 norepo=True)
2576 norepo=True)
2578 def debugwireargs(ui, repopath, *vals, **opts):
2577 def debugwireargs(ui, repopath, *vals, **opts):
2579 opts = pycompat.byteskwargs(opts)
2578 opts = pycompat.byteskwargs(opts)
2580 repo = hg.peer(ui, opts, repopath)
2579 repo = hg.peer(ui, opts, repopath)
2581 for opt in cmdutil.remoteopts:
2580 for opt in cmdutil.remoteopts:
2582 del opts[opt[1]]
2581 del opts[opt[1]]
2583 args = {}
2582 args = {}
2584 for k, v in opts.iteritems():
2583 for k, v in opts.iteritems():
2585 if v:
2584 if v:
2586 args[k] = v
2585 args[k] = v
2587 args = pycompat.strkwargs(args)
2586 args = pycompat.strkwargs(args)
2588 # run twice to check that we don't mess up the stream for the next command
2587 # run twice to check that we don't mess up the stream for the next command
2589 res1 = repo.debugwireargs(*vals, **args)
2588 res1 = repo.debugwireargs(*vals, **args)
2590 res2 = repo.debugwireargs(*vals, **args)
2589 res2 = repo.debugwireargs(*vals, **args)
2591 ui.write("%s\n" % res1)
2590 ui.write("%s\n" % res1)
2592 if res1 != res2:
2591 if res1 != res2:
2593 ui.warn("%s\n" % res2)
2592 ui.warn("%s\n" % res2)
2594
2593
2595 def _parsewirelangblocks(fh):
2594 def _parsewirelangblocks(fh):
2596 activeaction = None
2595 activeaction = None
2597 blocklines = []
2596 blocklines = []
2598
2597
2599 for line in fh:
2598 for line in fh:
2600 line = line.rstrip()
2599 line = line.rstrip()
2601 if not line:
2600 if not line:
2602 continue
2601 continue
2603
2602
2604 if line.startswith(b'#'):
2603 if line.startswith(b'#'):
2605 continue
2604 continue
2606
2605
2607 if not line.startswith(' '):
2606 if not line.startswith(' '):
2608 # New block. Flush previous one.
2607 # New block. Flush previous one.
2609 if activeaction:
2608 if activeaction:
2610 yield activeaction, blocklines
2609 yield activeaction, blocklines
2611
2610
2612 activeaction = line
2611 activeaction = line
2613 blocklines = []
2612 blocklines = []
2614 continue
2613 continue
2615
2614
2616 # Else we start with an indent.
2615 # Else we start with an indent.
2617
2616
2618 if not activeaction:
2617 if not activeaction:
2619 raise error.Abort(_('indented line outside of block'))
2618 raise error.Abort(_('indented line outside of block'))
2620
2619
2621 blocklines.append(line)
2620 blocklines.append(line)
2622
2621
2623 # Flush last block.
2622 # Flush last block.
2624 if activeaction:
2623 if activeaction:
2625 yield activeaction, blocklines
2624 yield activeaction, blocklines
2626
2625
2627 @command('debugwireproto',
2626 @command('debugwireproto',
2628 [
2627 [
2629 ('', 'localssh', False, _('start an SSH server for this repo')),
2628 ('', 'localssh', False, _('start an SSH server for this repo')),
2630 ('', 'peer', '', _('construct a specific version of the peer')),
2629 ('', 'peer', '', _('construct a specific version of the peer')),
2631 ('', 'noreadstderr', False, _('do not read from stderr of the remote')),
2630 ('', 'noreadstderr', False, _('do not read from stderr of the remote')),
2632 ('', 'nologhandshake', False,
2631 ('', 'nologhandshake', False,
2633 _('do not log I/O related to the peer handshake')),
2632 _('do not log I/O related to the peer handshake')),
2634 ] + cmdutil.remoteopts,
2633 ] + cmdutil.remoteopts,
2635 _('[PATH]'),
2634 _('[PATH]'),
2636 optionalrepo=True)
2635 optionalrepo=True)
2637 def debugwireproto(ui, repo, path=None, **opts):
2636 def debugwireproto(ui, repo, path=None, **opts):
2638 """send wire protocol commands to a server
2637 """send wire protocol commands to a server
2639
2638
2640 This command can be used to issue wire protocol commands to remote
2639 This command can be used to issue wire protocol commands to remote
2641 peers and to debug the raw data being exchanged.
2640 peers and to debug the raw data being exchanged.
2642
2641
2643 ``--localssh`` will start an SSH server against the current repository
2642 ``--localssh`` will start an SSH server against the current repository
2644 and connect to that. By default, the connection will perform a handshake
2643 and connect to that. By default, the connection will perform a handshake
2645 and establish an appropriate peer instance.
2644 and establish an appropriate peer instance.
2646
2645
2647 ``--peer`` can be used to bypass the handshake protocol and construct a
2646 ``--peer`` can be used to bypass the handshake protocol and construct a
2648 peer instance using the specified class type. Valid values are ``raw``,
2647 peer instance using the specified class type. Valid values are ``raw``,
2649 ``http2``, ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending
2648 ``http2``, ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending
2650 raw data payloads and don't support higher-level command actions.
2649 raw data payloads and don't support higher-level command actions.
2651
2650
2652 ``--noreadstderr`` can be used to disable automatic reading from stderr
2651 ``--noreadstderr`` can be used to disable automatic reading from stderr
2653 of the peer (for SSH connections only). Disabling automatic reading of
2652 of the peer (for SSH connections only). Disabling automatic reading of
2654 stderr is useful for making output more deterministic.
2653 stderr is useful for making output more deterministic.
2655
2654
2656 Commands are issued via a mini language which is specified via stdin.
2655 Commands are issued via a mini language which is specified via stdin.
2657 The language consists of individual actions to perform. An action is
2656 The language consists of individual actions to perform. An action is
2658 defined by a block. A block is defined as a line with no leading
2657 defined by a block. A block is defined as a line with no leading
2659 space followed by 0 or more lines with leading space. Blocks are
2658 space followed by 0 or more lines with leading space. Blocks are
2660 effectively a high-level command with additional metadata.
2659 effectively a high-level command with additional metadata.
2661
2660
2662 Lines beginning with ``#`` are ignored.
2661 Lines beginning with ``#`` are ignored.
2663
2662
2664 The following sections denote available actions.
2663 The following sections denote available actions.
2665
2664
2666 raw
2665 raw
2667 ---
2666 ---
2668
2667
2669 Send raw data to the server.
2668 Send raw data to the server.
2670
2669
2671 The block payload contains the raw data to send as one atomic send
2670 The block payload contains the raw data to send as one atomic send
2672 operation. The data may not actually be delivered in a single system
2671 operation. The data may not actually be delivered in a single system
2673 call: it depends on the abilities of the transport being used.
2672 call: it depends on the abilities of the transport being used.
2674
2673
2675 Each line in the block is de-indented and concatenated. Then, that
2674 Each line in the block is de-indented and concatenated. Then, that
2676 value is evaluated as a Python b'' literal. This allows the use of
2675 value is evaluated as a Python b'' literal. This allows the use of
2677 backslash escaping, etc.
2676 backslash escaping, etc.
2678
2677
2679 raw+
2678 raw+
2680 ----
2679 ----
2681
2680
2682 Behaves like ``raw`` except flushes output afterwards.
2681 Behaves like ``raw`` except flushes output afterwards.
2683
2682
2684 command <X>
2683 command <X>
2685 -----------
2684 -----------
2686
2685
2687 Send a request to run a named command, whose name follows the ``command``
2686 Send a request to run a named command, whose name follows the ``command``
2688 string.
2687 string.
2689
2688
2690 Arguments to the command are defined as lines in this block. The format of
2689 Arguments to the command are defined as lines in this block. The format of
2691 each line is ``<key> <value>``. e.g.::
2690 each line is ``<key> <value>``. e.g.::
2692
2691
2693 command listkeys
2692 command listkeys
2694 namespace bookmarks
2693 namespace bookmarks
2695
2694
2696 If the value begins with ``eval:``, it will be interpreted as a Python
2695 If the value begins with ``eval:``, it will be interpreted as a Python
2697 literal expression. Otherwise values are interpreted as Python b'' literals.
2696 literal expression. Otherwise values are interpreted as Python b'' literals.
2698 This allows sending complex types and encoding special byte sequences via
2697 This allows sending complex types and encoding special byte sequences via
2699 backslash escaping.
2698 backslash escaping.
2700
2699
2701 The following arguments have special meaning:
2700 The following arguments have special meaning:
2702
2701
2703 ``PUSHFILE``
2702 ``PUSHFILE``
2704 When defined, the *push* mechanism of the peer will be used instead
2703 When defined, the *push* mechanism of the peer will be used instead
2705 of the static request-response mechanism and the content of the
2704 of the static request-response mechanism and the content of the
2706 file specified in the value of this argument will be sent as the
2705 file specified in the value of this argument will be sent as the
2707 command payload.
2706 command payload.
2708
2707
2709 This can be used to submit a local bundle file to the remote.
2708 This can be used to submit a local bundle file to the remote.
2710
2709
2711 batchbegin
2710 batchbegin
2712 ----------
2711 ----------
2713
2712
2714 Instruct the peer to begin a batched send.
2713 Instruct the peer to begin a batched send.
2715
2714
2716 All ``command`` blocks are queued for execution until the next
2715 All ``command`` blocks are queued for execution until the next
2717 ``batchsubmit`` block.
2716 ``batchsubmit`` block.
2718
2717
2719 batchsubmit
2718 batchsubmit
2720 -----------
2719 -----------
2721
2720
2722 Submit previously queued ``command`` blocks as a batch request.
2721 Submit previously queued ``command`` blocks as a batch request.
2723
2722
2724 This action MUST be paired with a ``batchbegin`` action.
2723 This action MUST be paired with a ``batchbegin`` action.
2725
2724
2726 httprequest <method> <path>
2725 httprequest <method> <path>
2727 ---------------------------
2726 ---------------------------
2728
2727
2729 (HTTP peer only)
2728 (HTTP peer only)
2730
2729
2731 Send an HTTP request to the peer.
2730 Send an HTTP request to the peer.
2732
2731
2733 The HTTP request line follows the ``httprequest`` action. e.g. ``GET /foo``.
2732 The HTTP request line follows the ``httprequest`` action. e.g. ``GET /foo``.
2734
2733
2735 Arguments of the form ``<key>: <value>`` are interpreted as HTTP request
2734 Arguments of the form ``<key>: <value>`` are interpreted as HTTP request
2736 headers to add to the request. e.g. ``Accept: foo``.
2735 headers to add to the request. e.g. ``Accept: foo``.
2737
2736
2738 The following arguments are special:
2737 The following arguments are special:
2739
2738
2740 ``BODYFILE``
2739 ``BODYFILE``
2741 The content of the file defined as the value to this argument will be
2740 The content of the file defined as the value to this argument will be
2742 transferred verbatim as the HTTP request body.
2741 transferred verbatim as the HTTP request body.
2743
2742
2744 ``frame <type> <flags> <payload>``
2743 ``frame <type> <flags> <payload>``
2745 Send a unified protocol frame as part of the request body.
2744 Send a unified protocol frame as part of the request body.
2746
2745
2747 All frames will be collected and sent as the body to the HTTP
2746 All frames will be collected and sent as the body to the HTTP
2748 request.
2747 request.
2749
2748
2750 close
2749 close
2751 -----
2750 -----
2752
2751
2753 Close the connection to the server.
2752 Close the connection to the server.
2754
2753
2755 flush
2754 flush
2756 -----
2755 -----
2757
2756
2758 Flush data written to the server.
2757 Flush data written to the server.
2759
2758
2760 readavailable
2759 readavailable
2761 -------------
2760 -------------
2762
2761
2763 Close the write end of the connection and read all available data from
2762 Close the write end of the connection and read all available data from
2764 the server.
2763 the server.
2765
2764
2766 If the connection to the server encompasses multiple pipes, we poll both
2765 If the connection to the server encompasses multiple pipes, we poll both
2767 pipes and read available data.
2766 pipes and read available data.
2768
2767
2769 readline
2768 readline
2770 --------
2769 --------
2771
2770
2772 Read a line of output from the server. If there are multiple output
2771 Read a line of output from the server. If there are multiple output
2773 pipes, reads only the main pipe.
2772 pipes, reads only the main pipe.
2774
2773
2775 ereadline
2774 ereadline
2776 ---------
2775 ---------
2777
2776
2778 Like ``readline``, but read from the stderr pipe, if available.
2777 Like ``readline``, but read from the stderr pipe, if available.
2779
2778
2780 read <X>
2779 read <X>
2781 --------
2780 --------
2782
2781
2783 ``read()`` N bytes from the server's main output pipe.
2782 ``read()`` N bytes from the server's main output pipe.
2784
2783
2785 eread <X>
2784 eread <X>
2786 ---------
2785 ---------
2787
2786
2788 ``read()`` N bytes from the server's stderr pipe, if available.
2787 ``read()`` N bytes from the server's stderr pipe, if available.
2789
2788
2790 Specifying Unified Frame-Based Protocol Frames
2789 Specifying Unified Frame-Based Protocol Frames
2791 ----------------------------------------------
2790 ----------------------------------------------
2792
2791
2793 It is possible to emit a *Unified Frame-Based Protocol* by using special
2792 It is possible to emit a *Unified Frame-Based Protocol* by using special
2794 syntax.
2793 syntax.
2795
2794
2796 A frame is composed as a type, flags, and payload. These can be parsed
2795 A frame is composed as a type, flags, and payload. These can be parsed
2797 from a string of the form:
2796 from a string of the form:
2798
2797
2799 <request-id> <stream-id> <stream-flags> <type> <flags> <payload>
2798 <request-id> <stream-id> <stream-flags> <type> <flags> <payload>
2800
2799
2801 ``request-id`` and ``stream-id`` are integers defining the request and
2800 ``request-id`` and ``stream-id`` are integers defining the request and
2802 stream identifiers.
2801 stream identifiers.
2803
2802
2804 ``type`` can be an integer value for the frame type or the string name
2803 ``type`` can be an integer value for the frame type or the string name
2805 of the type. The strings are defined in ``wireprotoframing.py``. e.g.
2804 of the type. The strings are defined in ``wireprotoframing.py``. e.g.
2806 ``command-name``.
2805 ``command-name``.
2807
2806
2808 ``stream-flags`` and ``flags`` are a ``|`` delimited list of flag
2807 ``stream-flags`` and ``flags`` are a ``|`` delimited list of flag
2809 components. Each component (and there can be just one) can be an integer
2808 components. Each component (and there can be just one) can be an integer
2810 or a flag name for stream flags or frame flags, respectively. Values are
2809 or a flag name for stream flags or frame flags, respectively. Values are
2811 resolved to integers and then bitwise OR'd together.
2810 resolved to integers and then bitwise OR'd together.
2812
2811
2813 ``payload`` represents the raw frame payload. If it begins with
2812 ``payload`` represents the raw frame payload. If it begins with
2814 ``cbor:``, the following string is evaluated as Python code and the
2813 ``cbor:``, the following string is evaluated as Python code and the
2815 resulting object is fed into a CBOR encoder. Otherwise it is interpreted
2814 resulting object is fed into a CBOR encoder. Otherwise it is interpreted
2816 as a Python byte string literal.
2815 as a Python byte string literal.
2817 """
2816 """
2818 opts = pycompat.byteskwargs(opts)
2817 opts = pycompat.byteskwargs(opts)
2819
2818
2820 if opts['localssh'] and not repo:
2819 if opts['localssh'] and not repo:
2821 raise error.Abort(_('--localssh requires a repository'))
2820 raise error.Abort(_('--localssh requires a repository'))
2822
2821
2823 if opts['peer'] and opts['peer'] not in ('raw', 'http2', 'ssh1', 'ssh2'):
2822 if opts['peer'] and opts['peer'] not in ('raw', 'http2', 'ssh1', 'ssh2'):
2824 raise error.Abort(_('invalid value for --peer'),
2823 raise error.Abort(_('invalid value for --peer'),
2825 hint=_('valid values are "raw", "ssh1", and "ssh2"'))
2824 hint=_('valid values are "raw", "ssh1", and "ssh2"'))
2826
2825
2827 if path and opts['localssh']:
2826 if path and opts['localssh']:
2828 raise error.Abort(_('cannot specify --localssh with an explicit '
2827 raise error.Abort(_('cannot specify --localssh with an explicit '
2829 'path'))
2828 'path'))
2830
2829
2831 if ui.interactive():
2830 if ui.interactive():
2832 ui.write(_('(waiting for commands on stdin)\n'))
2831 ui.write(_('(waiting for commands on stdin)\n'))
2833
2832
2834 blocks = list(_parsewirelangblocks(ui.fin))
2833 blocks = list(_parsewirelangblocks(ui.fin))
2835
2834
2836 proc = None
2835 proc = None
2837 stdin = None
2836 stdin = None
2838 stdout = None
2837 stdout = None
2839 stderr = None
2838 stderr = None
2840 opener = None
2839 opener = None
2841
2840
2842 if opts['localssh']:
2841 if opts['localssh']:
2843 # We start the SSH server in its own process so there is process
2842 # We start the SSH server in its own process so there is process
2844 # separation. This prevents a whole class of potential bugs around
2843 # separation. This prevents a whole class of potential bugs around
2845 # shared state from interfering with server operation.
2844 # shared state from interfering with server operation.
2846 args = procutil.hgcmd() + [
2845 args = procutil.hgcmd() + [
2847 '-R', repo.root,
2846 '-R', repo.root,
2848 'debugserve', '--sshstdio',
2847 'debugserve', '--sshstdio',
2849 ]
2848 ]
2850 proc = subprocess.Popen(args, stdin=subprocess.PIPE,
2849 proc = subprocess.Popen(args, stdin=subprocess.PIPE,
2851 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
2850 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
2852 bufsize=0)
2851 bufsize=0)
2853
2852
2854 stdin = proc.stdin
2853 stdin = proc.stdin
2855 stdout = proc.stdout
2854 stdout = proc.stdout
2856 stderr = proc.stderr
2855 stderr = proc.stderr
2857
2856
2858 # We turn the pipes into observers so we can log I/O.
2857 # We turn the pipes into observers so we can log I/O.
2859 if ui.verbose or opts['peer'] == 'raw':
2858 if ui.verbose or opts['peer'] == 'raw':
2860 stdin = util.makeloggingfileobject(ui, proc.stdin, b'i',
2859 stdin = util.makeloggingfileobject(ui, proc.stdin, b'i',
2861 logdata=True)
2860 logdata=True)
2862 stdout = util.makeloggingfileobject(ui, proc.stdout, b'o',
2861 stdout = util.makeloggingfileobject(ui, proc.stdout, b'o',
2863 logdata=True)
2862 logdata=True)
2864 stderr = util.makeloggingfileobject(ui, proc.stderr, b'e',
2863 stderr = util.makeloggingfileobject(ui, proc.stderr, b'e',
2865 logdata=True)
2864 logdata=True)
2866
2865
2867 # --localssh also implies the peer connection settings.
2866 # --localssh also implies the peer connection settings.
2868
2867
2869 url = 'ssh://localserver'
2868 url = 'ssh://localserver'
2870 autoreadstderr = not opts['noreadstderr']
2869 autoreadstderr = not opts['noreadstderr']
2871
2870
2872 if opts['peer'] == 'ssh1':
2871 if opts['peer'] == 'ssh1':
2873 ui.write(_('creating ssh peer for wire protocol version 1\n'))
2872 ui.write(_('creating ssh peer for wire protocol version 1\n'))
2874 peer = sshpeer.sshv1peer(ui, url, proc, stdin, stdout, stderr,
2873 peer = sshpeer.sshv1peer(ui, url, proc, stdin, stdout, stderr,
2875 None, autoreadstderr=autoreadstderr)
2874 None, autoreadstderr=autoreadstderr)
2876 elif opts['peer'] == 'ssh2':
2875 elif opts['peer'] == 'ssh2':
2877 ui.write(_('creating ssh peer for wire protocol version 2\n'))
2876 ui.write(_('creating ssh peer for wire protocol version 2\n'))
2878 peer = sshpeer.sshv2peer(ui, url, proc, stdin, stdout, stderr,
2877 peer = sshpeer.sshv2peer(ui, url, proc, stdin, stdout, stderr,
2879 None, autoreadstderr=autoreadstderr)
2878 None, autoreadstderr=autoreadstderr)
2880 elif opts['peer'] == 'raw':
2879 elif opts['peer'] == 'raw':
2881 ui.write(_('using raw connection to peer\n'))
2880 ui.write(_('using raw connection to peer\n'))
2882 peer = None
2881 peer = None
2883 else:
2882 else:
2884 ui.write(_('creating ssh peer from handshake results\n'))
2883 ui.write(_('creating ssh peer from handshake results\n'))
2885 peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr,
2884 peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr,
2886 autoreadstderr=autoreadstderr)
2885 autoreadstderr=autoreadstderr)
2887
2886
2888 elif path:
2887 elif path:
2889 # We bypass hg.peer() so we can proxy the sockets.
2888 # We bypass hg.peer() so we can proxy the sockets.
2890 # TODO consider not doing this because we skip
2889 # TODO consider not doing this because we skip
2891 # ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
2890 # ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
2892 u = util.url(path)
2891 u = util.url(path)
2893 if u.scheme != 'http':
2892 if u.scheme != 'http':
2894 raise error.Abort(_('only http:// paths are currently supported'))
2893 raise error.Abort(_('only http:// paths are currently supported'))
2895
2894
2896 url, authinfo = u.authinfo()
2895 url, authinfo = u.authinfo()
2897 openerargs = {
2896 openerargs = {
2898 r'useragent': b'Mercurial debugwireproto',
2897 r'useragent': b'Mercurial debugwireproto',
2899 }
2898 }
2900
2899
2901 # Turn pipes/sockets into observers so we can log I/O.
2900 # Turn pipes/sockets into observers so we can log I/O.
2902 if ui.verbose:
2901 if ui.verbose:
2903 openerargs.update({
2902 openerargs.update({
2904 r'loggingfh': ui,
2903 r'loggingfh': ui,
2905 r'loggingname': b's',
2904 r'loggingname': b's',
2906 r'loggingopts': {
2905 r'loggingopts': {
2907 r'logdata': True,
2906 r'logdata': True,
2908 r'logdataapis': False,
2907 r'logdataapis': False,
2909 },
2908 },
2910 })
2909 })
2911
2910
2912 if ui.debugflag:
2911 if ui.debugflag:
2913 openerargs[r'loggingopts'][r'logdataapis'] = True
2912 openerargs[r'loggingopts'][r'logdataapis'] = True
2914
2913
2915 # Don't send default headers when in raw mode. This allows us to
2914 # Don't send default headers when in raw mode. This allows us to
2916 # bypass most of the behavior of our URL handling code so we can
2915 # bypass most of the behavior of our URL handling code so we can
2917 # have near complete control over what's sent on the wire.
2916 # have near complete control over what's sent on the wire.
2918 if opts['peer'] == 'raw':
2917 if opts['peer'] == 'raw':
2919 openerargs[r'sendaccept'] = False
2918 openerargs[r'sendaccept'] = False
2920
2919
2921 opener = urlmod.opener(ui, authinfo, **openerargs)
2920 opener = urlmod.opener(ui, authinfo, **openerargs)
2922
2921
2923 if opts['peer'] == 'http2':
2922 if opts['peer'] == 'http2':
2924 ui.write(_('creating http peer for wire protocol version 2\n'))
2923 ui.write(_('creating http peer for wire protocol version 2\n'))
2925 # We go through makepeer() because we need an API descriptor for
2924 # We go through makepeer() because we need an API descriptor for
2926 # the peer instance to be useful.
2925 # the peer instance to be useful.
2927 with ui.configoverride({
2926 with ui.configoverride({
2928 ('experimental', 'httppeer.advertise-v2'): True}):
2927 ('experimental', 'httppeer.advertise-v2'): True}):
2929 if opts['nologhandshake']:
2928 if opts['nologhandshake']:
2930 ui.pushbuffer()
2929 ui.pushbuffer()
2931
2930
2932 peer = httppeer.makepeer(ui, path, opener=opener)
2931 peer = httppeer.makepeer(ui, path, opener=opener)
2933
2932
2934 if opts['nologhandshake']:
2933 if opts['nologhandshake']:
2935 ui.popbuffer()
2934 ui.popbuffer()
2936
2935
2937 if not isinstance(peer, httppeer.httpv2peer):
2936 if not isinstance(peer, httppeer.httpv2peer):
2938 raise error.Abort(_('could not instantiate HTTP peer for '
2937 raise error.Abort(_('could not instantiate HTTP peer for '
2939 'wire protocol version 2'),
2938 'wire protocol version 2'),
2940 hint=_('the server may not have the feature '
2939 hint=_('the server may not have the feature '
2941 'enabled or is not allowing this '
2940 'enabled or is not allowing this '
2942 'client version'))
2941 'client version'))
2943
2942
2944 elif opts['peer'] == 'raw':
2943 elif opts['peer'] == 'raw':
2945 ui.write(_('using raw connection to peer\n'))
2944 ui.write(_('using raw connection to peer\n'))
2946 peer = None
2945 peer = None
2947 elif opts['peer']:
2946 elif opts['peer']:
2948 raise error.Abort(_('--peer %s not supported with HTTP peers') %
2947 raise error.Abort(_('--peer %s not supported with HTTP peers') %
2949 opts['peer'])
2948 opts['peer'])
2950 else:
2949 else:
2951 peer = httppeer.makepeer(ui, path, opener=opener)
2950 peer = httppeer.makepeer(ui, path, opener=opener)
2952
2951
2953 # We /could/ populate stdin/stdout with sock.makefile()...
2952 # We /could/ populate stdin/stdout with sock.makefile()...
2954 else:
2953 else:
2955 raise error.Abort(_('unsupported connection configuration'))
2954 raise error.Abort(_('unsupported connection configuration'))
2956
2955
2957 batchedcommands = None
2956 batchedcommands = None
2958
2957
2959 # Now perform actions based on the parsed wire language instructions.
2958 # Now perform actions based on the parsed wire language instructions.
2960 for action, lines in blocks:
2959 for action, lines in blocks:
2961 if action in ('raw', 'raw+'):
2960 if action in ('raw', 'raw+'):
2962 if not stdin:
2961 if not stdin:
2963 raise error.Abort(_('cannot call raw/raw+ on this peer'))
2962 raise error.Abort(_('cannot call raw/raw+ on this peer'))
2964
2963
2965 # Concatenate the data together.
2964 # Concatenate the data together.
2966 data = ''.join(l.lstrip() for l in lines)
2965 data = ''.join(l.lstrip() for l in lines)
2967 data = stringutil.unescapestr(data)
2966 data = stringutil.unescapestr(data)
2968 stdin.write(data)
2967 stdin.write(data)
2969
2968
2970 if action == 'raw+':
2969 if action == 'raw+':
2971 stdin.flush()
2970 stdin.flush()
2972 elif action == 'flush':
2971 elif action == 'flush':
2973 if not stdin:
2972 if not stdin:
2974 raise error.Abort(_('cannot call flush on this peer'))
2973 raise error.Abort(_('cannot call flush on this peer'))
2975 stdin.flush()
2974 stdin.flush()
2976 elif action.startswith('command'):
2975 elif action.startswith('command'):
2977 if not peer:
2976 if not peer:
2978 raise error.Abort(_('cannot send commands unless peer instance '
2977 raise error.Abort(_('cannot send commands unless peer instance '
2979 'is available'))
2978 'is available'))
2980
2979
2981 command = action.split(' ', 1)[1]
2980 command = action.split(' ', 1)[1]
2982
2981
2983 args = {}
2982 args = {}
2984 for line in lines:
2983 for line in lines:
2985 # We need to allow empty values.
2984 # We need to allow empty values.
2986 fields = line.lstrip().split(' ', 1)
2985 fields = line.lstrip().split(' ', 1)
2987 if len(fields) == 1:
2986 if len(fields) == 1:
2988 key = fields[0]
2987 key = fields[0]
2989 value = ''
2988 value = ''
2990 else:
2989 else:
2991 key, value = fields
2990 key, value = fields
2992
2991
2993 if value.startswith('eval:'):
2992 if value.startswith('eval:'):
2994 value = stringutil.evalpythonliteral(value[5:])
2993 value = stringutil.evalpythonliteral(value[5:])
2995 else:
2994 else:
2996 value = stringutil.unescapestr(value)
2995 value = stringutil.unescapestr(value)
2997
2996
2998 args[key] = value
2997 args[key] = value
2999
2998
3000 if batchedcommands is not None:
2999 if batchedcommands is not None:
3001 batchedcommands.append((command, args))
3000 batchedcommands.append((command, args))
3002 continue
3001 continue
3003
3002
3004 ui.status(_('sending %s command\n') % command)
3003 ui.status(_('sending %s command\n') % command)
3005
3004
3006 if 'PUSHFILE' in args:
3005 if 'PUSHFILE' in args:
3007 with open(args['PUSHFILE'], r'rb') as fh:
3006 with open(args['PUSHFILE'], r'rb') as fh:
3008 del args['PUSHFILE']
3007 del args['PUSHFILE']
3009 res, output = peer._callpush(command, fh,
3008 res, output = peer._callpush(command, fh,
3010 **pycompat.strkwargs(args))
3009 **pycompat.strkwargs(args))
3011 ui.status(_('result: %s\n') % stringutil.escapestr(res))
3010 ui.status(_('result: %s\n') % stringutil.escapestr(res))
3012 ui.status(_('remote output: %s\n') %
3011 ui.status(_('remote output: %s\n') %
3013 stringutil.escapestr(output))
3012 stringutil.escapestr(output))
3014 else:
3013 else:
3015 with peer.commandexecutor() as e:
3014 with peer.commandexecutor() as e:
3016 res = e.callcommand(command, args).result()
3015 res = e.callcommand(command, args).result()
3017
3016
3018 if isinstance(res, wireprotov2peer.commandresponse):
3017 if isinstance(res, wireprotov2peer.commandresponse):
3019 val = list(res.cborobjects())
3018 val = list(res.cborobjects())
3020 ui.status(_('response: %s\n') %
3019 ui.status(_('response: %s\n') %
3021 stringutil.pprint(val, bprefix=True))
3020 stringutil.pprint(val, bprefix=True))
3022
3021
3023 else:
3022 else:
3024 ui.status(_('response: %s\n') %
3023 ui.status(_('response: %s\n') %
3025 stringutil.pprint(res, bprefix=True))
3024 stringutil.pprint(res, bprefix=True))
3026
3025
3027 elif action == 'batchbegin':
3026 elif action == 'batchbegin':
3028 if batchedcommands is not None:
3027 if batchedcommands is not None:
3029 raise error.Abort(_('nested batchbegin not allowed'))
3028 raise error.Abort(_('nested batchbegin not allowed'))
3030
3029
3031 batchedcommands = []
3030 batchedcommands = []
3032 elif action == 'batchsubmit':
3031 elif action == 'batchsubmit':
3033 # There is a batching API we could go through. But it would be
3032 # There is a batching API we could go through. But it would be
3034 # difficult to normalize requests into function calls. It is easier
3033 # difficult to normalize requests into function calls. It is easier
3035 # to bypass this layer and normalize to commands + args.
3034 # to bypass this layer and normalize to commands + args.
3036 ui.status(_('sending batch with %d sub-commands\n') %
3035 ui.status(_('sending batch with %d sub-commands\n') %
3037 len(batchedcommands))
3036 len(batchedcommands))
3038 for i, chunk in enumerate(peer._submitbatch(batchedcommands)):
3037 for i, chunk in enumerate(peer._submitbatch(batchedcommands)):
3039 ui.status(_('response #%d: %s\n') %
3038 ui.status(_('response #%d: %s\n') %
3040 (i, stringutil.escapestr(chunk)))
3039 (i, stringutil.escapestr(chunk)))
3041
3040
3042 batchedcommands = None
3041 batchedcommands = None
3043
3042
3044 elif action.startswith('httprequest '):
3043 elif action.startswith('httprequest '):
3045 if not opener:
3044 if not opener:
3046 raise error.Abort(_('cannot use httprequest without an HTTP '
3045 raise error.Abort(_('cannot use httprequest without an HTTP '
3047 'peer'))
3046 'peer'))
3048
3047
3049 request = action.split(' ', 2)
3048 request = action.split(' ', 2)
3050 if len(request) != 3:
3049 if len(request) != 3:
3051 raise error.Abort(_('invalid httprequest: expected format is '
3050 raise error.Abort(_('invalid httprequest: expected format is '
3052 '"httprequest <method> <path>'))
3051 '"httprequest <method> <path>'))
3053
3052
3054 method, httppath = request[1:]
3053 method, httppath = request[1:]
3055 headers = {}
3054 headers = {}
3056 body = None
3055 body = None
3057 frames = []
3056 frames = []
3058 for line in lines:
3057 for line in lines:
3059 line = line.lstrip()
3058 line = line.lstrip()
3060 m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
3059 m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
3061 if m:
3060 if m:
3062 headers[m.group(1)] = m.group(2)
3061 headers[m.group(1)] = m.group(2)
3063 continue
3062 continue
3064
3063
3065 if line.startswith(b'BODYFILE '):
3064 if line.startswith(b'BODYFILE '):
3066 with open(line.split(b' ', 1), 'rb') as fh:
3065 with open(line.split(b' ', 1), 'rb') as fh:
3067 body = fh.read()
3066 body = fh.read()
3068 elif line.startswith(b'frame '):
3067 elif line.startswith(b'frame '):
3069 frame = wireprotoframing.makeframefromhumanstring(
3068 frame = wireprotoframing.makeframefromhumanstring(
3070 line[len(b'frame '):])
3069 line[len(b'frame '):])
3071
3070
3072 frames.append(frame)
3071 frames.append(frame)
3073 else:
3072 else:
3074 raise error.Abort(_('unknown argument to httprequest: %s') %
3073 raise error.Abort(_('unknown argument to httprequest: %s') %
3075 line)
3074 line)
3076
3075
3077 url = path + httppath
3076 url = path + httppath
3078
3077
3079 if frames:
3078 if frames:
3080 body = b''.join(bytes(f) for f in frames)
3079 body = b''.join(bytes(f) for f in frames)
3081
3080
3082 req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
3081 req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
3083
3082
3084 # urllib.Request insists on using has_data() as a proxy for
3083 # urllib.Request insists on using has_data() as a proxy for
3085 # determining the request method. Override that to use our
3084 # determining the request method. Override that to use our
3086 # explicitly requested method.
3085 # explicitly requested method.
3087 req.get_method = lambda: method
3086 req.get_method = lambda: method
3088
3087
3089 try:
3088 try:
3090 res = opener.open(req)
3089 res = opener.open(req)
3091 body = res.read()
3090 body = res.read()
3092 except util.urlerr.urlerror as e:
3091 except util.urlerr.urlerror as e:
3093 e.read()
3092 e.read()
3094 continue
3093 continue
3095
3094
3096 if res.headers.get('Content-Type') == 'application/mercurial-cbor':
3095 if res.headers.get('Content-Type') == 'application/mercurial-cbor':
3097 ui.write(_('cbor> %s\n') %
3096 ui.write(_('cbor> %s\n') %
3098 stringutil.pprint(cbor.loads(body), bprefix=True))
3097 stringutil.pprint(cbor.loads(body), bprefix=True))
3099
3098
3100 elif action == 'close':
3099 elif action == 'close':
3101 peer.close()
3100 peer.close()
3102 elif action == 'readavailable':
3101 elif action == 'readavailable':
3103 if not stdout or not stderr:
3102 if not stdout or not stderr:
3104 raise error.Abort(_('readavailable not available on this peer'))
3103 raise error.Abort(_('readavailable not available on this peer'))
3105
3104
3106 stdin.close()
3105 stdin.close()
3107 stdout.read()
3106 stdout.read()
3108 stderr.read()
3107 stderr.read()
3109
3108
3110 elif action == 'readline':
3109 elif action == 'readline':
3111 if not stdout:
3110 if not stdout:
3112 raise error.Abort(_('readline not available on this peer'))
3111 raise error.Abort(_('readline not available on this peer'))
3113 stdout.readline()
3112 stdout.readline()
3114 elif action == 'ereadline':
3113 elif action == 'ereadline':
3115 if not stderr:
3114 if not stderr:
3116 raise error.Abort(_('ereadline not available on this peer'))
3115 raise error.Abort(_('ereadline not available on this peer'))
3117 stderr.readline()
3116 stderr.readline()
3118 elif action.startswith('read '):
3117 elif action.startswith('read '):
3119 count = int(action.split(' ', 1)[1])
3118 count = int(action.split(' ', 1)[1])
3120 if not stdout:
3119 if not stdout:
3121 raise error.Abort(_('read not available on this peer'))
3120 raise error.Abort(_('read not available on this peer'))
3122 stdout.read(count)
3121 stdout.read(count)
3123 elif action.startswith('eread '):
3122 elif action.startswith('eread '):
3124 count = int(action.split(' ', 1)[1])
3123 count = int(action.split(' ', 1)[1])
3125 if not stderr:
3124 if not stderr:
3126 raise error.Abort(_('eread not available on this peer'))
3125 raise error.Abort(_('eread not available on this peer'))
3127 stderr.read(count)
3126 stderr.read(count)
3128 else:
3127 else:
3129 raise error.Abort(_('unknown action: %s') % action)
3128 raise error.Abort(_('unknown action: %s') % action)
3130
3129
3131 if batchedcommands is not None:
3130 if batchedcommands is not None:
3132 raise error.Abort(_('unclosed "batchbegin" request'))
3131 raise error.Abort(_('unclosed "batchbegin" request'))
3133
3132
3134 if peer:
3133 if peer:
3135 peer.close()
3134 peer.close()
3136
3135
3137 if proc:
3136 if proc:
3138 proc.kill()
3137 proc.kill()
@@ -1,684 +1,683 b''
1 # posix.py - Posix utility function implementations for Mercurial
1 # posix.py - Posix utility function implementations for Mercurial
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import fcntl
11 import fcntl
12 import getpass
12 import getpass
13 import grp
13 import grp
14 import os
14 import os
15 import pwd
15 import pwd
16 import re
16 import re
17 import select
17 import select
18 import stat
18 import stat
19 import sys
19 import sys
20 import tempfile
20 import tempfile
21 import unicodedata
21 import unicodedata
22
22
23 from .i18n import _
23 from .i18n import _
24 from . import (
24 from . import (
25 encoding,
25 encoding,
26 error,
26 error,
27 policy,
27 policy,
28 pycompat,
28 pycompat,
29 )
29 )
30
30
31 osutil = policy.importmod(r'osutil')
31 osutil = policy.importmod(r'osutil')
32
32
33 posixfile = open
33 posixfile = open
34 normpath = os.path.normpath
34 normpath = os.path.normpath
35 samestat = os.path.samestat
35 samestat = os.path.samestat
36 try:
36 try:
37 oslink = os.link
37 oslink = os.link
38 except AttributeError:
38 except AttributeError:
39 # Some platforms build Python without os.link on systems that are
39 # Some platforms build Python without os.link on systems that are
40 # vaguely unix-like but don't have hardlink support. For those
40 # vaguely unix-like but don't have hardlink support. For those
41 # poor souls, just say we tried and that it failed so we fall back
41 # poor souls, just say we tried and that it failed so we fall back
42 # to copies.
42 # to copies.
43 def oslink(src, dst):
43 def oslink(src, dst):
44 raise OSError(errno.EINVAL,
44 raise OSError(errno.EINVAL,
45 'hardlinks not supported: %s to %s' % (src, dst))
45 'hardlinks not supported: %s to %s' % (src, dst))
46 unlink = os.unlink
46 unlink = os.unlink
47 rename = os.rename
47 rename = os.rename
48 removedirs = os.removedirs
48 removedirs = os.removedirs
49 expandglobs = False
49 expandglobs = False
50
50
51 umask = os.umask(0)
51 umask = os.umask(0)
52 os.umask(umask)
52 os.umask(umask)
53
53
54 def split(p):
54 def split(p):
55 '''Same as posixpath.split, but faster
55 '''Same as posixpath.split, but faster
56
56
57 >>> import posixpath
57 >>> import posixpath
58 >>> for f in [b'/absolute/path/to/file',
58 >>> for f in [b'/absolute/path/to/file',
59 ... b'relative/path/to/file',
59 ... b'relative/path/to/file',
60 ... b'file_alone',
60 ... b'file_alone',
61 ... b'path/to/directory/',
61 ... b'path/to/directory/',
62 ... b'/multiple/path//separators',
62 ... b'/multiple/path//separators',
63 ... b'/file_at_root',
63 ... b'/file_at_root',
64 ... b'///multiple_leading_separators_at_root',
64 ... b'///multiple_leading_separators_at_root',
65 ... b'']:
65 ... b'']:
66 ... assert split(f) == posixpath.split(f), f
66 ... assert split(f) == posixpath.split(f), f
67 '''
67 '''
68 ht = p.rsplit('/', 1)
68 ht = p.rsplit('/', 1)
69 if len(ht) == 1:
69 if len(ht) == 1:
70 return '', p
70 return '', p
71 nh = ht[0].rstrip('/')
71 nh = ht[0].rstrip('/')
72 if nh:
72 if nh:
73 return nh, ht[1]
73 return nh, ht[1]
74 return ht[0] + '/', ht[1]
74 return ht[0] + '/', ht[1]
75
75
76 def openhardlinks():
76 def openhardlinks():
77 '''return true if it is safe to hold open file handles to hardlinks'''
77 '''return true if it is safe to hold open file handles to hardlinks'''
78 return True
78 return True
79
79
80 def nlinks(name):
80 def nlinks(name):
81 '''return number of hardlinks for the given file'''
81 '''return number of hardlinks for the given file'''
82 return os.lstat(name).st_nlink
82 return os.lstat(name).st_nlink
83
83
84 def parsepatchoutput(output_line):
84 def parsepatchoutput(output_line):
85 """parses the output produced by patch and returns the filename"""
85 """parses the output produced by patch and returns the filename"""
86 pf = output_line[14:]
86 pf = output_line[14:]
87 if pycompat.sysplatform == 'OpenVMS':
87 if pycompat.sysplatform == 'OpenVMS':
88 if pf[0] == '`':
88 if pf[0] == '`':
89 pf = pf[1:-1] # Remove the quotes
89 pf = pf[1:-1] # Remove the quotes
90 else:
90 else:
91 if pf.startswith("'") and pf.endswith("'") and " " in pf:
91 if pf.startswith("'") and pf.endswith("'") and " " in pf:
92 pf = pf[1:-1] # Remove the quotes
92 pf = pf[1:-1] # Remove the quotes
93 return pf
93 return pf
94
94
95 def sshargs(sshcmd, host, user, port):
95 def sshargs(sshcmd, host, user, port):
96 '''Build argument list for ssh'''
96 '''Build argument list for ssh'''
97 args = user and ("%s@%s" % (user, host)) or host
97 args = user and ("%s@%s" % (user, host)) or host
98 if '-' in args[:1]:
98 if '-' in args[:1]:
99 raise error.Abort(
99 raise error.Abort(
100 _('illegal ssh hostname or username starting with -: %s') % args)
100 _('illegal ssh hostname or username starting with -: %s') % args)
101 args = shellquote(args)
101 args = shellquote(args)
102 if port:
102 if port:
103 args = '-p %s %s' % (shellquote(port), args)
103 args = '-p %s %s' % (shellquote(port), args)
104 return args
104 return args
105
105
106 def isexec(f):
106 def isexec(f):
107 """check whether a file is executable"""
107 """check whether a file is executable"""
108 return (os.lstat(f).st_mode & 0o100 != 0)
108 return (os.lstat(f).st_mode & 0o100 != 0)
109
109
110 def setflags(f, l, x):
110 def setflags(f, l, x):
111 st = os.lstat(f)
111 st = os.lstat(f)
112 s = st.st_mode
112 s = st.st_mode
113 if l:
113 if l:
114 if not stat.S_ISLNK(s):
114 if not stat.S_ISLNK(s):
115 # switch file to link
115 # switch file to link
116 fp = open(f, 'rb')
116 fp = open(f, 'rb')
117 data = fp.read()
117 data = fp.read()
118 fp.close()
118 fp.close()
119 unlink(f)
119 unlink(f)
120 try:
120 try:
121 os.symlink(data, f)
121 os.symlink(data, f)
122 except OSError:
122 except OSError:
123 # failed to make a link, rewrite file
123 # failed to make a link, rewrite file
124 fp = open(f, "wb")
124 fp = open(f, "wb")
125 fp.write(data)
125 fp.write(data)
126 fp.close()
126 fp.close()
127 # no chmod needed at this point
127 # no chmod needed at this point
128 return
128 return
129 if stat.S_ISLNK(s):
129 if stat.S_ISLNK(s):
130 # switch link to file
130 # switch link to file
131 data = os.readlink(f)
131 data = os.readlink(f)
132 unlink(f)
132 unlink(f)
133 fp = open(f, "wb")
133 fp = open(f, "wb")
134 fp.write(data)
134 fp.write(data)
135 fp.close()
135 fp.close()
136 s = 0o666 & ~umask # avoid restatting for chmod
136 s = 0o666 & ~umask # avoid restatting for chmod
137
137
138 sx = s & 0o100
138 sx = s & 0o100
139 if st.st_nlink > 1 and bool(x) != bool(sx):
139 if st.st_nlink > 1 and bool(x) != bool(sx):
140 # the file is a hardlink, break it
140 # the file is a hardlink, break it
141 with open(f, "rb") as fp:
141 with open(f, "rb") as fp:
142 data = fp.read()
142 data = fp.read()
143 unlink(f)
143 unlink(f)
144 with open(f, "wb") as fp:
144 with open(f, "wb") as fp:
145 fp.write(data)
145 fp.write(data)
146
146
147 if x and not sx:
147 if x and not sx:
148 # Turn on +x for every +r bit when making a file executable
148 # Turn on +x for every +r bit when making a file executable
149 # and obey umask.
149 # and obey umask.
150 os.chmod(f, s | (s & 0o444) >> 2 & ~umask)
150 os.chmod(f, s | (s & 0o444) >> 2 & ~umask)
151 elif not x and sx:
151 elif not x and sx:
152 # Turn off all +x bits
152 # Turn off all +x bits
153 os.chmod(f, s & 0o666)
153 os.chmod(f, s & 0o666)
154
154
155 def copymode(src, dst, mode=None):
155 def copymode(src, dst, mode=None):
156 '''Copy the file mode from the file at path src to dst.
156 '''Copy the file mode from the file at path src to dst.
157 If src doesn't exist, we're using mode instead. If mode is None, we're
157 If src doesn't exist, we're using mode instead. If mode is None, we're
158 using umask.'''
158 using umask.'''
159 try:
159 try:
160 st_mode = os.lstat(src).st_mode & 0o777
160 st_mode = os.lstat(src).st_mode & 0o777
161 except OSError as inst:
161 except OSError as inst:
162 if inst.errno != errno.ENOENT:
162 if inst.errno != errno.ENOENT:
163 raise
163 raise
164 st_mode = mode
164 st_mode = mode
165 if st_mode is None:
165 if st_mode is None:
166 st_mode = ~umask
166 st_mode = ~umask
167 st_mode &= 0o666
167 st_mode &= 0o666
168 os.chmod(dst, st_mode)
168 os.chmod(dst, st_mode)
169
169
170 def checkexec(path):
170 def checkexec(path):
171 """
171 """
172 Check whether the given path is on a filesystem with UNIX-like exec flags
172 Check whether the given path is on a filesystem with UNIX-like exec flags
173
173
174 Requires a directory (like /foo/.hg)
174 Requires a directory (like /foo/.hg)
175 """
175 """
176
176
177 # VFAT on some Linux versions can flip mode but it doesn't persist
177 # VFAT on some Linux versions can flip mode but it doesn't persist
178 # a FS remount. Frequently we can detect it if files are created
178 # a FS remount. Frequently we can detect it if files are created
179 # with exec bit on.
179 # with exec bit on.
180
180
181 try:
181 try:
182 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
182 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
183 cachedir = os.path.join(path, '.hg', 'cache')
183 cachedir = os.path.join(path, '.hg', 'cache')
184 if os.path.isdir(cachedir):
184 if os.path.isdir(cachedir):
185 checkisexec = os.path.join(cachedir, 'checkisexec')
185 checkisexec = os.path.join(cachedir, 'checkisexec')
186 checknoexec = os.path.join(cachedir, 'checknoexec')
186 checknoexec = os.path.join(cachedir, 'checknoexec')
187
187
188 try:
188 try:
189 m = os.stat(checkisexec).st_mode
189 m = os.stat(checkisexec).st_mode
190 except OSError as e:
190 except OSError as e:
191 if e.errno != errno.ENOENT:
191 if e.errno != errno.ENOENT:
192 raise
192 raise
193 # checkisexec does not exist - fall through ...
193 # checkisexec does not exist - fall through ...
194 else:
194 else:
195 # checkisexec exists, check if it actually is exec
195 # checkisexec exists, check if it actually is exec
196 if m & EXECFLAGS != 0:
196 if m & EXECFLAGS != 0:
197 # ensure checkisexec exists, check it isn't exec
197 # ensure checkisexec exists, check it isn't exec
198 try:
198 try:
199 m = os.stat(checknoexec).st_mode
199 m = os.stat(checknoexec).st_mode
200 except OSError as e:
200 except OSError as e:
201 if e.errno != errno.ENOENT:
201 if e.errno != errno.ENOENT:
202 raise
202 raise
203 open(checknoexec, 'w').close() # might fail
203 open(checknoexec, 'w').close() # might fail
204 m = os.stat(checknoexec).st_mode
204 m = os.stat(checknoexec).st_mode
205 if m & EXECFLAGS == 0:
205 if m & EXECFLAGS == 0:
206 # check-exec is exec and check-no-exec is not exec
206 # check-exec is exec and check-no-exec is not exec
207 return True
207 return True
208 # checknoexec exists but is exec - delete it
208 # checknoexec exists but is exec - delete it
209 unlink(checknoexec)
209 unlink(checknoexec)
210 # checkisexec exists but is not exec - delete it
210 # checkisexec exists but is not exec - delete it
211 unlink(checkisexec)
211 unlink(checkisexec)
212
212
213 # check using one file, leave it as checkisexec
213 # check using one file, leave it as checkisexec
214 checkdir = cachedir
214 checkdir = cachedir
215 else:
215 else:
216 # check directly in path and don't leave checkisexec behind
216 # check directly in path and don't leave checkisexec behind
217 checkdir = path
217 checkdir = path
218 checkisexec = None
218 checkisexec = None
219 fh, fn = pycompat.mkstemp(dir=checkdir, prefix='hg-checkexec-')
219 fh, fn = pycompat.mkstemp(dir=checkdir, prefix='hg-checkexec-')
220 try:
220 try:
221 os.close(fh)
221 os.close(fh)
222 m = os.stat(fn).st_mode
222 m = os.stat(fn).st_mode
223 if m & EXECFLAGS == 0:
223 if m & EXECFLAGS == 0:
224 os.chmod(fn, m & 0o777 | EXECFLAGS)
224 os.chmod(fn, m & 0o777 | EXECFLAGS)
225 if os.stat(fn).st_mode & EXECFLAGS != 0:
225 if os.stat(fn).st_mode & EXECFLAGS != 0:
226 if checkisexec is not None:
226 if checkisexec is not None:
227 os.rename(fn, checkisexec)
227 os.rename(fn, checkisexec)
228 fn = None
228 fn = None
229 return True
229 return True
230 finally:
230 finally:
231 if fn is not None:
231 if fn is not None:
232 unlink(fn)
232 unlink(fn)
233 except (IOError, OSError):
233 except (IOError, OSError):
234 # we don't care, the user probably won't be able to commit anyway
234 # we don't care, the user probably won't be able to commit anyway
235 return False
235 return False
236
236
237 def checklink(path):
237 def checklink(path):
238 """check whether the given path is on a symlink-capable filesystem"""
238 """check whether the given path is on a symlink-capable filesystem"""
239 # mktemp is not racy because symlink creation will fail if the
239 # mktemp is not racy because symlink creation will fail if the
240 # file already exists
240 # file already exists
241 while True:
241 while True:
242 cachedir = os.path.join(path, '.hg', 'cache')
242 cachedir = os.path.join(path, '.hg', 'cache')
243 checklink = os.path.join(cachedir, 'checklink')
243 checklink = os.path.join(cachedir, 'checklink')
244 # try fast path, read only
244 # try fast path, read only
245 if os.path.islink(checklink):
245 if os.path.islink(checklink):
246 return True
246 return True
247 if os.path.isdir(cachedir):
247 if os.path.isdir(cachedir):
248 checkdir = cachedir
248 checkdir = cachedir
249 else:
249 else:
250 checkdir = path
250 checkdir = path
251 cachedir = None
251 cachedir = None
252 fscheckdir = pycompat.fsdecode(checkdir)
252 name = tempfile.mktemp(dir=pycompat.fsdecode(checkdir),
253 name = tempfile.mktemp(dir=fscheckdir,
254 prefix=r'checklink-')
253 prefix=r'checklink-')
255 name = pycompat.fsencode(name)
254 name = pycompat.fsencode(name)
256 try:
255 try:
257 fd = None
256 fd = None
258 if cachedir is None:
257 if cachedir is None:
259 fd = tempfile.NamedTemporaryFile(dir=fscheckdir,
258 fd = pycompat.namedtempfile(dir=checkdir,
260 prefix=r'hg-checklink-')
259 prefix='hg-checklink-')
261 target = pycompat.fsencode(os.path.basename(fd.name))
260 target = os.path.basename(fd.name)
262 else:
261 else:
263 # create a fixed file to link to; doesn't matter if it
262 # create a fixed file to link to; doesn't matter if it
264 # already exists.
263 # already exists.
265 target = 'checklink-target'
264 target = 'checklink-target'
266 try:
265 try:
267 fullpath = os.path.join(cachedir, target)
266 fullpath = os.path.join(cachedir, target)
268 open(fullpath, 'w').close()
267 open(fullpath, 'w').close()
269 except IOError as inst:
268 except IOError as inst:
270 if inst[0] == errno.EACCES:
269 if inst[0] == errno.EACCES:
271 # If we can't write to cachedir, just pretend
270 # If we can't write to cachedir, just pretend
272 # that the fs is readonly and by association
271 # that the fs is readonly and by association
273 # that the fs won't support symlinks. This
272 # that the fs won't support symlinks. This
274 # seems like the least dangerous way to avoid
273 # seems like the least dangerous way to avoid
275 # data loss.
274 # data loss.
276 return False
275 return False
277 raise
276 raise
278 try:
277 try:
279 os.symlink(target, name)
278 os.symlink(target, name)
280 if cachedir is None:
279 if cachedir is None:
281 unlink(name)
280 unlink(name)
282 else:
281 else:
283 try:
282 try:
284 os.rename(name, checklink)
283 os.rename(name, checklink)
285 except OSError:
284 except OSError:
286 unlink(name)
285 unlink(name)
287 return True
286 return True
288 except OSError as inst:
287 except OSError as inst:
289 # link creation might race, try again
288 # link creation might race, try again
290 if inst.errno == errno.EEXIST:
289 if inst.errno == errno.EEXIST:
291 continue
290 continue
292 raise
291 raise
293 finally:
292 finally:
294 if fd is not None:
293 if fd is not None:
295 fd.close()
294 fd.close()
296 except AttributeError:
295 except AttributeError:
297 return False
296 return False
298 except OSError as inst:
297 except OSError as inst:
299 # sshfs might report failure while successfully creating the link
298 # sshfs might report failure while successfully creating the link
300 if inst.errno == errno.EIO and os.path.exists(name):
299 if inst.errno == errno.EIO and os.path.exists(name):
301 unlink(name)
300 unlink(name)
302 return False
301 return False
303
302
304 def checkosfilename(path):
303 def checkosfilename(path):
305 '''Check that the base-relative path is a valid filename on this platform.
304 '''Check that the base-relative path is a valid filename on this platform.
306 Returns None if the path is ok, or a UI string describing the problem.'''
305 Returns None if the path is ok, or a UI string describing the problem.'''
307 return None # on posix platforms, every path is ok
306 return None # on posix platforms, every path is ok
308
307
309 def getfsmountpoint(dirpath):
308 def getfsmountpoint(dirpath):
310 '''Get the filesystem mount point from a directory (best-effort)
309 '''Get the filesystem mount point from a directory (best-effort)
311
310
312 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
311 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
313 '''
312 '''
314 return getattr(osutil, 'getfsmountpoint', lambda x: None)(dirpath)
313 return getattr(osutil, 'getfsmountpoint', lambda x: None)(dirpath)
315
314
316 def getfstype(dirpath):
315 def getfstype(dirpath):
317 '''Get the filesystem type name from a directory (best-effort)
316 '''Get the filesystem type name from a directory (best-effort)
318
317
319 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
318 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
320 '''
319 '''
321 return getattr(osutil, 'getfstype', lambda x: None)(dirpath)
320 return getattr(osutil, 'getfstype', lambda x: None)(dirpath)
322
321
323 def setbinary(fd):
322 def setbinary(fd):
324 pass
323 pass
325
324
326 def pconvert(path):
325 def pconvert(path):
327 return path
326 return path
328
327
329 def localpath(path):
328 def localpath(path):
330 return path
329 return path
331
330
332 def samefile(fpath1, fpath2):
331 def samefile(fpath1, fpath2):
333 """Returns whether path1 and path2 refer to the same file. This is only
332 """Returns whether path1 and path2 refer to the same file. This is only
334 guaranteed to work for files, not directories."""
333 guaranteed to work for files, not directories."""
335 return os.path.samefile(fpath1, fpath2)
334 return os.path.samefile(fpath1, fpath2)
336
335
337 def samedevice(fpath1, fpath2):
336 def samedevice(fpath1, fpath2):
338 """Returns whether fpath1 and fpath2 are on the same device. This is only
337 """Returns whether fpath1 and fpath2 are on the same device. This is only
339 guaranteed to work for files, not directories."""
338 guaranteed to work for files, not directories."""
340 st1 = os.lstat(fpath1)
339 st1 = os.lstat(fpath1)
341 st2 = os.lstat(fpath2)
340 st2 = os.lstat(fpath2)
342 return st1.st_dev == st2.st_dev
341 return st1.st_dev == st2.st_dev
343
342
344 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
343 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
345 def normcase(path):
344 def normcase(path):
346 return path.lower()
345 return path.lower()
347
346
348 # what normcase does to ASCII strings
347 # what normcase does to ASCII strings
349 normcasespec = encoding.normcasespecs.lower
348 normcasespec = encoding.normcasespecs.lower
350 # fallback normcase function for non-ASCII strings
349 # fallback normcase function for non-ASCII strings
351 normcasefallback = normcase
350 normcasefallback = normcase
352
351
353 if pycompat.isdarwin:
352 if pycompat.isdarwin:
354
353
355 def normcase(path):
354 def normcase(path):
356 '''
355 '''
357 Normalize a filename for OS X-compatible comparison:
356 Normalize a filename for OS X-compatible comparison:
358 - escape-encode invalid characters
357 - escape-encode invalid characters
359 - decompose to NFD
358 - decompose to NFD
360 - lowercase
359 - lowercase
361 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
360 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
362
361
363 >>> normcase(b'UPPER')
362 >>> normcase(b'UPPER')
364 'upper'
363 'upper'
365 >>> normcase(b'Caf\\xc3\\xa9')
364 >>> normcase(b'Caf\\xc3\\xa9')
366 'cafe\\xcc\\x81'
365 'cafe\\xcc\\x81'
367 >>> normcase(b'\\xc3\\x89')
366 >>> normcase(b'\\xc3\\x89')
368 'e\\xcc\\x81'
367 'e\\xcc\\x81'
369 >>> normcase(b'\\xb8\\xca\\xc3\\xca\\xbe\\xc8.JPG') # issue3918
368 >>> normcase(b'\\xb8\\xca\\xc3\\xca\\xbe\\xc8.JPG') # issue3918
370 '%b8%ca%c3\\xca\\xbe%c8.jpg'
369 '%b8%ca%c3\\xca\\xbe%c8.jpg'
371 '''
370 '''
372
371
373 try:
372 try:
374 return encoding.asciilower(path) # exception for non-ASCII
373 return encoding.asciilower(path) # exception for non-ASCII
375 except UnicodeDecodeError:
374 except UnicodeDecodeError:
376 return normcasefallback(path)
375 return normcasefallback(path)
377
376
378 normcasespec = encoding.normcasespecs.lower
377 normcasespec = encoding.normcasespecs.lower
379
378
380 def normcasefallback(path):
379 def normcasefallback(path):
381 try:
380 try:
382 u = path.decode('utf-8')
381 u = path.decode('utf-8')
383 except UnicodeDecodeError:
382 except UnicodeDecodeError:
384 # OS X percent-encodes any bytes that aren't valid utf-8
383 # OS X percent-encodes any bytes that aren't valid utf-8
385 s = ''
384 s = ''
386 pos = 0
385 pos = 0
387 l = len(path)
386 l = len(path)
388 while pos < l:
387 while pos < l:
389 try:
388 try:
390 c = encoding.getutf8char(path, pos)
389 c = encoding.getutf8char(path, pos)
391 pos += len(c)
390 pos += len(c)
392 except ValueError:
391 except ValueError:
393 c = '%%%02X' % ord(path[pos:pos + 1])
392 c = '%%%02X' % ord(path[pos:pos + 1])
394 pos += 1
393 pos += 1
395 s += c
394 s += c
396
395
397 u = s.decode('utf-8')
396 u = s.decode('utf-8')
398
397
399 # Decompose then lowercase (HFS+ technote specifies lower)
398 # Decompose then lowercase (HFS+ technote specifies lower)
400 enc = unicodedata.normalize(r'NFD', u).lower().encode('utf-8')
399 enc = unicodedata.normalize(r'NFD', u).lower().encode('utf-8')
401 # drop HFS+ ignored characters
400 # drop HFS+ ignored characters
402 return encoding.hfsignoreclean(enc)
401 return encoding.hfsignoreclean(enc)
403
402
404 if pycompat.sysplatform == 'cygwin':
403 if pycompat.sysplatform == 'cygwin':
405 # workaround for cygwin, in which mount point part of path is
404 # workaround for cygwin, in which mount point part of path is
406 # treated as case sensitive, even though underlying NTFS is case
405 # treated as case sensitive, even though underlying NTFS is case
407 # insensitive.
406 # insensitive.
408
407
409 # default mount points
408 # default mount points
410 cygwinmountpoints = sorted([
409 cygwinmountpoints = sorted([
411 "/usr/bin",
410 "/usr/bin",
412 "/usr/lib",
411 "/usr/lib",
413 "/cygdrive",
412 "/cygdrive",
414 ], reverse=True)
413 ], reverse=True)
415
414
416 # use upper-ing as normcase as same as NTFS workaround
415 # use upper-ing as normcase as same as NTFS workaround
417 def normcase(path):
416 def normcase(path):
418 pathlen = len(path)
417 pathlen = len(path)
419 if (pathlen == 0) or (path[0] != pycompat.ossep):
418 if (pathlen == 0) or (path[0] != pycompat.ossep):
420 # treat as relative
419 # treat as relative
421 return encoding.upper(path)
420 return encoding.upper(path)
422
421
423 # to preserve case of mountpoint part
422 # to preserve case of mountpoint part
424 for mp in cygwinmountpoints:
423 for mp in cygwinmountpoints:
425 if not path.startswith(mp):
424 if not path.startswith(mp):
426 continue
425 continue
427
426
428 mplen = len(mp)
427 mplen = len(mp)
429 if mplen == pathlen: # mount point itself
428 if mplen == pathlen: # mount point itself
430 return mp
429 return mp
431 if path[mplen] == pycompat.ossep:
430 if path[mplen] == pycompat.ossep:
432 return mp + encoding.upper(path[mplen:])
431 return mp + encoding.upper(path[mplen:])
433
432
434 return encoding.upper(path)
433 return encoding.upper(path)
435
434
436 normcasespec = encoding.normcasespecs.other
435 normcasespec = encoding.normcasespecs.other
437 normcasefallback = normcase
436 normcasefallback = normcase
438
437
439 # Cygwin translates native ACLs to POSIX permissions,
438 # Cygwin translates native ACLs to POSIX permissions,
440 # but these translations are not supported by native
439 # but these translations are not supported by native
441 # tools, so the exec bit tends to be set erroneously.
440 # tools, so the exec bit tends to be set erroneously.
442 # Therefore, disable executable bit access on Cygwin.
441 # Therefore, disable executable bit access on Cygwin.
443 def checkexec(path):
442 def checkexec(path):
444 return False
443 return False
445
444
446 # Similarly, Cygwin's symlink emulation is likely to create
445 # Similarly, Cygwin's symlink emulation is likely to create
447 # problems when Mercurial is used from both Cygwin and native
446 # problems when Mercurial is used from both Cygwin and native
448 # Windows, with other native tools, or on shared volumes
447 # Windows, with other native tools, or on shared volumes
449 def checklink(path):
448 def checklink(path):
450 return False
449 return False
451
450
452 _needsshellquote = None
451 _needsshellquote = None
453 def shellquote(s):
452 def shellquote(s):
454 if pycompat.sysplatform == 'OpenVMS':
453 if pycompat.sysplatform == 'OpenVMS':
455 return '"%s"' % s
454 return '"%s"' % s
456 global _needsshellquote
455 global _needsshellquote
457 if _needsshellquote is None:
456 if _needsshellquote is None:
458 _needsshellquote = re.compile(br'[^a-zA-Z0-9._/+-]').search
457 _needsshellquote = re.compile(br'[^a-zA-Z0-9._/+-]').search
459 if s and not _needsshellquote(s):
458 if s and not _needsshellquote(s):
460 # "s" shouldn't have to be quoted
459 # "s" shouldn't have to be quoted
461 return s
460 return s
462 else:
461 else:
463 return "'%s'" % s.replace("'", "'\\''")
462 return "'%s'" % s.replace("'", "'\\''")
464
463
465 def shellsplit(s):
464 def shellsplit(s):
466 """Parse a command string in POSIX shell way (best-effort)"""
465 """Parse a command string in POSIX shell way (best-effort)"""
467 return pycompat.shlexsplit(s, posix=True)
466 return pycompat.shlexsplit(s, posix=True)
468
467
469 def quotecommand(cmd):
468 def quotecommand(cmd):
470 return cmd
469 return cmd
471
470
472 def testpid(pid):
471 def testpid(pid):
473 '''return False if pid dead, True if running or not sure'''
472 '''return False if pid dead, True if running or not sure'''
474 if pycompat.sysplatform == 'OpenVMS':
473 if pycompat.sysplatform == 'OpenVMS':
475 return True
474 return True
476 try:
475 try:
477 os.kill(pid, 0)
476 os.kill(pid, 0)
478 return True
477 return True
479 except OSError as inst:
478 except OSError as inst:
480 return inst.errno != errno.ESRCH
479 return inst.errno != errno.ESRCH
481
480
482 def isowner(st):
481 def isowner(st):
483 """Return True if the stat object st is from the current user."""
482 """Return True if the stat object st is from the current user."""
484 return st.st_uid == os.getuid()
483 return st.st_uid == os.getuid()
485
484
486 def findexe(command):
485 def findexe(command):
487 '''Find executable for command searching like which does.
486 '''Find executable for command searching like which does.
488 If command is a basename then PATH is searched for command.
487 If command is a basename then PATH is searched for command.
489 PATH isn't searched if command is an absolute or relative path.
488 PATH isn't searched if command is an absolute or relative path.
490 If command isn't found None is returned.'''
489 If command isn't found None is returned.'''
491 if pycompat.sysplatform == 'OpenVMS':
490 if pycompat.sysplatform == 'OpenVMS':
492 return command
491 return command
493
492
494 def findexisting(executable):
493 def findexisting(executable):
495 'Will return executable if existing file'
494 'Will return executable if existing file'
496 if os.path.isfile(executable) and os.access(executable, os.X_OK):
495 if os.path.isfile(executable) and os.access(executable, os.X_OK):
497 return executable
496 return executable
498 return None
497 return None
499
498
500 if pycompat.ossep in command:
499 if pycompat.ossep in command:
501 return findexisting(command)
500 return findexisting(command)
502
501
503 if pycompat.sysplatform == 'plan9':
502 if pycompat.sysplatform == 'plan9':
504 return findexisting(os.path.join('/bin', command))
503 return findexisting(os.path.join('/bin', command))
505
504
506 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
505 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
507 executable = findexisting(os.path.join(path, command))
506 executable = findexisting(os.path.join(path, command))
508 if executable is not None:
507 if executable is not None:
509 return executable
508 return executable
510 return None
509 return None
511
510
512 def setsignalhandler():
511 def setsignalhandler():
513 pass
512 pass
514
513
515 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
514 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
516
515
517 def statfiles(files):
516 def statfiles(files):
518 '''Stat each file in files. Yield each stat, or None if a file does not
517 '''Stat each file in files. Yield each stat, or None if a file does not
519 exist or has a type we don't care about.'''
518 exist or has a type we don't care about.'''
520 lstat = os.lstat
519 lstat = os.lstat
521 getkind = stat.S_IFMT
520 getkind = stat.S_IFMT
522 for nf in files:
521 for nf in files:
523 try:
522 try:
524 st = lstat(nf)
523 st = lstat(nf)
525 if getkind(st.st_mode) not in _wantedkinds:
524 if getkind(st.st_mode) not in _wantedkinds:
526 st = None
525 st = None
527 except OSError as err:
526 except OSError as err:
528 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
527 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
529 raise
528 raise
530 st = None
529 st = None
531 yield st
530 yield st
532
531
533 def getuser():
532 def getuser():
534 '''return name of current user'''
533 '''return name of current user'''
535 return pycompat.fsencode(getpass.getuser())
534 return pycompat.fsencode(getpass.getuser())
536
535
537 def username(uid=None):
536 def username(uid=None):
538 """Return the name of the user with the given uid.
537 """Return the name of the user with the given uid.
539
538
540 If uid is None, return the name of the current user."""
539 If uid is None, return the name of the current user."""
541
540
542 if uid is None:
541 if uid is None:
543 uid = os.getuid()
542 uid = os.getuid()
544 try:
543 try:
545 return pwd.getpwuid(uid)[0]
544 return pwd.getpwuid(uid)[0]
546 except KeyError:
545 except KeyError:
547 return str(uid)
546 return str(uid)
548
547
549 def groupname(gid=None):
548 def groupname(gid=None):
550 """Return the name of the group with the given gid.
549 """Return the name of the group with the given gid.
551
550
552 If gid is None, return the name of the current group."""
551 If gid is None, return the name of the current group."""
553
552
554 if gid is None:
553 if gid is None:
555 gid = os.getgid()
554 gid = os.getgid()
556 try:
555 try:
557 return grp.getgrgid(gid)[0]
556 return grp.getgrgid(gid)[0]
558 except KeyError:
557 except KeyError:
559 return str(gid)
558 return str(gid)
560
559
561 def groupmembers(name):
560 def groupmembers(name):
562 """Return the list of members of the group with the given
561 """Return the list of members of the group with the given
563 name, KeyError if the group does not exist.
562 name, KeyError if the group does not exist.
564 """
563 """
565 return list(grp.getgrnam(name).gr_mem)
564 return list(grp.getgrnam(name).gr_mem)
566
565
567 def spawndetached(args):
566 def spawndetached(args):
568 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
567 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
569 args[0], args)
568 args[0], args)
570
569
571 def gethgcmd():
570 def gethgcmd():
572 return sys.argv[:1]
571 return sys.argv[:1]
573
572
574 def makedir(path, notindexed):
573 def makedir(path, notindexed):
575 os.mkdir(path)
574 os.mkdir(path)
576
575
577 def lookupreg(key, name=None, scope=None):
576 def lookupreg(key, name=None, scope=None):
578 return None
577 return None
579
578
580 def hidewindow():
579 def hidewindow():
581 """Hide current shell window.
580 """Hide current shell window.
582
581
583 Used to hide the window opened when starting asynchronous
582 Used to hide the window opened when starting asynchronous
584 child process under Windows, unneeded on other systems.
583 child process under Windows, unneeded on other systems.
585 """
584 """
586 pass
585 pass
587
586
588 class cachestat(object):
587 class cachestat(object):
589 def __init__(self, path):
588 def __init__(self, path):
590 self.stat = os.stat(path)
589 self.stat = os.stat(path)
591
590
592 def cacheable(self):
591 def cacheable(self):
593 return bool(self.stat.st_ino)
592 return bool(self.stat.st_ino)
594
593
595 __hash__ = object.__hash__
594 __hash__ = object.__hash__
596
595
597 def __eq__(self, other):
596 def __eq__(self, other):
598 try:
597 try:
599 # Only dev, ino, size, mtime and atime are likely to change. Out
598 # Only dev, ino, size, mtime and atime are likely to change. Out
600 # of these, we shouldn't compare atime but should compare the
599 # of these, we shouldn't compare atime but should compare the
601 # rest. However, one of the other fields changing indicates
600 # rest. However, one of the other fields changing indicates
602 # something fishy going on, so return False if anything but atime
601 # something fishy going on, so return False if anything but atime
603 # changes.
602 # changes.
604 return (self.stat.st_mode == other.stat.st_mode and
603 return (self.stat.st_mode == other.stat.st_mode and
605 self.stat.st_ino == other.stat.st_ino and
604 self.stat.st_ino == other.stat.st_ino and
606 self.stat.st_dev == other.stat.st_dev and
605 self.stat.st_dev == other.stat.st_dev and
607 self.stat.st_nlink == other.stat.st_nlink and
606 self.stat.st_nlink == other.stat.st_nlink and
608 self.stat.st_uid == other.stat.st_uid and
607 self.stat.st_uid == other.stat.st_uid and
609 self.stat.st_gid == other.stat.st_gid and
608 self.stat.st_gid == other.stat.st_gid and
610 self.stat.st_size == other.stat.st_size and
609 self.stat.st_size == other.stat.st_size and
611 self.stat[stat.ST_MTIME] == other.stat[stat.ST_MTIME] and
610 self.stat[stat.ST_MTIME] == other.stat[stat.ST_MTIME] and
612 self.stat[stat.ST_CTIME] == other.stat[stat.ST_CTIME])
611 self.stat[stat.ST_CTIME] == other.stat[stat.ST_CTIME])
613 except AttributeError:
612 except AttributeError:
614 return False
613 return False
615
614
616 def __ne__(self, other):
615 def __ne__(self, other):
617 return not self == other
616 return not self == other
618
617
619 def statislink(st):
618 def statislink(st):
620 '''check whether a stat result is a symlink'''
619 '''check whether a stat result is a symlink'''
621 return st and stat.S_ISLNK(st.st_mode)
620 return st and stat.S_ISLNK(st.st_mode)
622
621
623 def statisexec(st):
622 def statisexec(st):
624 '''check whether a stat result is an executable file'''
623 '''check whether a stat result is an executable file'''
625 return st and (st.st_mode & 0o100 != 0)
624 return st and (st.st_mode & 0o100 != 0)
626
625
627 def poll(fds):
626 def poll(fds):
628 """block until something happens on any file descriptor
627 """block until something happens on any file descriptor
629
628
630 This is a generic helper that will check for any activity
629 This is a generic helper that will check for any activity
631 (read, write. exception) and return the list of touched files.
630 (read, write. exception) and return the list of touched files.
632
631
633 In unsupported cases, it will raise a NotImplementedError"""
632 In unsupported cases, it will raise a NotImplementedError"""
634 try:
633 try:
635 while True:
634 while True:
636 try:
635 try:
637 res = select.select(fds, fds, fds)
636 res = select.select(fds, fds, fds)
638 break
637 break
639 except select.error as inst:
638 except select.error as inst:
640 if inst.args[0] == errno.EINTR:
639 if inst.args[0] == errno.EINTR:
641 continue
640 continue
642 raise
641 raise
643 except ValueError: # out of range file descriptor
642 except ValueError: # out of range file descriptor
644 raise NotImplementedError()
643 raise NotImplementedError()
645 return sorted(list(set(sum(res, []))))
644 return sorted(list(set(sum(res, []))))
646
645
647 def readpipe(pipe):
646 def readpipe(pipe):
648 """Read all available data from a pipe."""
647 """Read all available data from a pipe."""
649 # We can't fstat() a pipe because Linux will always report 0.
648 # We can't fstat() a pipe because Linux will always report 0.
650 # So, we set the pipe to non-blocking mode and read everything
649 # So, we set the pipe to non-blocking mode and read everything
651 # that's available.
650 # that's available.
652 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
651 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
653 flags |= os.O_NONBLOCK
652 flags |= os.O_NONBLOCK
654 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
653 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
655
654
656 try:
655 try:
657 chunks = []
656 chunks = []
658 while True:
657 while True:
659 try:
658 try:
660 s = pipe.read()
659 s = pipe.read()
661 if not s:
660 if not s:
662 break
661 break
663 chunks.append(s)
662 chunks.append(s)
664 except IOError:
663 except IOError:
665 break
664 break
666
665
667 return ''.join(chunks)
666 return ''.join(chunks)
668 finally:
667 finally:
669 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
668 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
670
669
671 def bindunixsocket(sock, path):
670 def bindunixsocket(sock, path):
672 """Bind the UNIX domain socket to the specified path"""
671 """Bind the UNIX domain socket to the specified path"""
673 # use relative path instead of full path at bind() if possible, since
672 # use relative path instead of full path at bind() if possible, since
674 # AF_UNIX path has very small length limit (107 chars) on common
673 # AF_UNIX path has very small length limit (107 chars) on common
675 # platforms (see sys/un.h)
674 # platforms (see sys/un.h)
676 dirname, basename = os.path.split(path)
675 dirname, basename = os.path.split(path)
677 bakwdfd = None
676 bakwdfd = None
678 if dirname:
677 if dirname:
679 bakwdfd = os.open('.', os.O_DIRECTORY)
678 bakwdfd = os.open('.', os.O_DIRECTORY)
680 os.chdir(dirname)
679 os.chdir(dirname)
681 sock.bind(basename)
680 sock.bind(basename)
682 if bakwdfd:
681 if bakwdfd:
683 os.fchdir(bakwdfd)
682 os.fchdir(bakwdfd)
684 os.close(bakwdfd)
683 os.close(bakwdfd)
@@ -1,394 +1,402 b''
1 # pycompat.py - portability shim for python 3
1 # pycompat.py - portability shim for python 3
2 #
2 #
3 # This software may be used and distributed according to the terms of the
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
4 # GNU General Public License version 2 or any later version.
5
5
6 """Mercurial portability shim for python 3.
6 """Mercurial portability shim for python 3.
7
7
8 This contains aliases to hide python version-specific details from the core.
8 This contains aliases to hide python version-specific details from the core.
9 """
9 """
10
10
11 from __future__ import absolute_import
11 from __future__ import absolute_import
12
12
13 import getopt
13 import getopt
14 import inspect
14 import inspect
15 import os
15 import os
16 import shlex
16 import shlex
17 import sys
17 import sys
18 import tempfile
18 import tempfile
19
19
20 ispy3 = (sys.version_info[0] >= 3)
20 ispy3 = (sys.version_info[0] >= 3)
21 ispypy = (r'__pypy__' in sys.builtin_module_names)
21 ispypy = (r'__pypy__' in sys.builtin_module_names)
22
22
23 if not ispy3:
23 if not ispy3:
24 import cookielib
24 import cookielib
25 import cPickle as pickle
25 import cPickle as pickle
26 import httplib
26 import httplib
27 import Queue as queue
27 import Queue as queue
28 import SocketServer as socketserver
28 import SocketServer as socketserver
29 import xmlrpclib
29 import xmlrpclib
30
30
31 from .thirdparty.concurrent import futures
31 from .thirdparty.concurrent import futures
32
32
33 def future_set_exception_info(f, exc_info):
33 def future_set_exception_info(f, exc_info):
34 f.set_exception_info(*exc_info)
34 f.set_exception_info(*exc_info)
35 else:
35 else:
36 import concurrent.futures as futures
36 import concurrent.futures as futures
37 import http.cookiejar as cookielib
37 import http.cookiejar as cookielib
38 import http.client as httplib
38 import http.client as httplib
39 import pickle
39 import pickle
40 import queue as queue
40 import queue as queue
41 import socketserver
41 import socketserver
42 import xmlrpc.client as xmlrpclib
42 import xmlrpc.client as xmlrpclib
43
43
44 def future_set_exception_info(f, exc_info):
44 def future_set_exception_info(f, exc_info):
45 f.set_exception(exc_info[0])
45 f.set_exception(exc_info[0])
46
46
47 def identity(a):
47 def identity(a):
48 return a
48 return a
49
49
50 if ispy3:
50 if ispy3:
51 import builtins
51 import builtins
52 import functools
52 import functools
53 import io
53 import io
54 import struct
54 import struct
55
55
56 fsencode = os.fsencode
56 fsencode = os.fsencode
57 fsdecode = os.fsdecode
57 fsdecode = os.fsdecode
58 oscurdir = os.curdir.encode('ascii')
58 oscurdir = os.curdir.encode('ascii')
59 oslinesep = os.linesep.encode('ascii')
59 oslinesep = os.linesep.encode('ascii')
60 osname = os.name.encode('ascii')
60 osname = os.name.encode('ascii')
61 ospathsep = os.pathsep.encode('ascii')
61 ospathsep = os.pathsep.encode('ascii')
62 ospardir = os.pardir.encode('ascii')
62 ospardir = os.pardir.encode('ascii')
63 ossep = os.sep.encode('ascii')
63 ossep = os.sep.encode('ascii')
64 osaltsep = os.altsep
64 osaltsep = os.altsep
65 if osaltsep:
65 if osaltsep:
66 osaltsep = osaltsep.encode('ascii')
66 osaltsep = osaltsep.encode('ascii')
67 # os.getcwd() on Python 3 returns string, but it has os.getcwdb() which
67 # os.getcwd() on Python 3 returns string, but it has os.getcwdb() which
68 # returns bytes.
68 # returns bytes.
69 getcwd = os.getcwdb
69 getcwd = os.getcwdb
70 sysplatform = sys.platform.encode('ascii')
70 sysplatform = sys.platform.encode('ascii')
71 sysexecutable = sys.executable
71 sysexecutable = sys.executable
72 if sysexecutable:
72 if sysexecutable:
73 sysexecutable = os.fsencode(sysexecutable)
73 sysexecutable = os.fsencode(sysexecutable)
74 bytesio = io.BytesIO
74 bytesio = io.BytesIO
75 # TODO deprecate stringio name, as it is a lie on Python 3.
75 # TODO deprecate stringio name, as it is a lie on Python 3.
76 stringio = bytesio
76 stringio = bytesio
77
77
78 def maplist(*args):
78 def maplist(*args):
79 return list(map(*args))
79 return list(map(*args))
80
80
81 def rangelist(*args):
81 def rangelist(*args):
82 return list(range(*args))
82 return list(range(*args))
83
83
84 def ziplist(*args):
84 def ziplist(*args):
85 return list(zip(*args))
85 return list(zip(*args))
86
86
87 rawinput = input
87 rawinput = input
88 getargspec = inspect.getfullargspec
88 getargspec = inspect.getfullargspec
89
89
90 # TODO: .buffer might not exist if std streams were replaced; we'll need
90 # TODO: .buffer might not exist if std streams were replaced; we'll need
91 # a silly wrapper to make a bytes stream backed by a unicode one.
91 # a silly wrapper to make a bytes stream backed by a unicode one.
92 stdin = sys.stdin.buffer
92 stdin = sys.stdin.buffer
93 stdout = sys.stdout.buffer
93 stdout = sys.stdout.buffer
94 stderr = sys.stderr.buffer
94 stderr = sys.stderr.buffer
95
95
96 # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix,
96 # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix,
97 # we can use os.fsencode() to get back bytes argv.
97 # we can use os.fsencode() to get back bytes argv.
98 #
98 #
99 # https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55
99 # https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55
100 #
100 #
101 # TODO: On Windows, the native argv is wchar_t, so we'll need a different
101 # TODO: On Windows, the native argv is wchar_t, so we'll need a different
102 # workaround to simulate the Python 2 (i.e. ANSI Win32 API) behavior.
102 # workaround to simulate the Python 2 (i.e. ANSI Win32 API) behavior.
103 if getattr(sys, 'argv', None) is not None:
103 if getattr(sys, 'argv', None) is not None:
104 sysargv = list(map(os.fsencode, sys.argv))
104 sysargv = list(map(os.fsencode, sys.argv))
105
105
106 bytechr = struct.Struct('>B').pack
106 bytechr = struct.Struct('>B').pack
107 byterepr = b'%r'.__mod__
107 byterepr = b'%r'.__mod__
108
108
109 class bytestr(bytes):
109 class bytestr(bytes):
110 """A bytes which mostly acts as a Python 2 str
110 """A bytes which mostly acts as a Python 2 str
111
111
112 >>> bytestr(), bytestr(bytearray(b'foo')), bytestr(u'ascii'), bytestr(1)
112 >>> bytestr(), bytestr(bytearray(b'foo')), bytestr(u'ascii'), bytestr(1)
113 ('', 'foo', 'ascii', '1')
113 ('', 'foo', 'ascii', '1')
114 >>> s = bytestr(b'foo')
114 >>> s = bytestr(b'foo')
115 >>> assert s is bytestr(s)
115 >>> assert s is bytestr(s)
116
116
117 __bytes__() should be called if provided:
117 __bytes__() should be called if provided:
118
118
119 >>> class bytesable(object):
119 >>> class bytesable(object):
120 ... def __bytes__(self):
120 ... def __bytes__(self):
121 ... return b'bytes'
121 ... return b'bytes'
122 >>> bytestr(bytesable())
122 >>> bytestr(bytesable())
123 'bytes'
123 'bytes'
124
124
125 There's no implicit conversion from non-ascii str as its encoding is
125 There's no implicit conversion from non-ascii str as its encoding is
126 unknown:
126 unknown:
127
127
128 >>> bytestr(chr(0x80)) # doctest: +ELLIPSIS
128 >>> bytestr(chr(0x80)) # doctest: +ELLIPSIS
129 Traceback (most recent call last):
129 Traceback (most recent call last):
130 ...
130 ...
131 UnicodeEncodeError: ...
131 UnicodeEncodeError: ...
132
132
133 Comparison between bytestr and bytes should work:
133 Comparison between bytestr and bytes should work:
134
134
135 >>> assert bytestr(b'foo') == b'foo'
135 >>> assert bytestr(b'foo') == b'foo'
136 >>> assert b'foo' == bytestr(b'foo')
136 >>> assert b'foo' == bytestr(b'foo')
137 >>> assert b'f' in bytestr(b'foo')
137 >>> assert b'f' in bytestr(b'foo')
138 >>> assert bytestr(b'f') in b'foo'
138 >>> assert bytestr(b'f') in b'foo'
139
139
140 Sliced elements should be bytes, not integer:
140 Sliced elements should be bytes, not integer:
141
141
142 >>> s[1], s[:2]
142 >>> s[1], s[:2]
143 (b'o', b'fo')
143 (b'o', b'fo')
144 >>> list(s), list(reversed(s))
144 >>> list(s), list(reversed(s))
145 ([b'f', b'o', b'o'], [b'o', b'o', b'f'])
145 ([b'f', b'o', b'o'], [b'o', b'o', b'f'])
146
146
147 As bytestr type isn't propagated across operations, you need to cast
147 As bytestr type isn't propagated across operations, you need to cast
148 bytes to bytestr explicitly:
148 bytes to bytestr explicitly:
149
149
150 >>> s = bytestr(b'foo').upper()
150 >>> s = bytestr(b'foo').upper()
151 >>> t = bytestr(s)
151 >>> t = bytestr(s)
152 >>> s[0], t[0]
152 >>> s[0], t[0]
153 (70, b'F')
153 (70, b'F')
154
154
155 Be careful to not pass a bytestr object to a function which expects
155 Be careful to not pass a bytestr object to a function which expects
156 bytearray-like behavior.
156 bytearray-like behavior.
157
157
158 >>> t = bytes(t) # cast to bytes
158 >>> t = bytes(t) # cast to bytes
159 >>> assert type(t) is bytes
159 >>> assert type(t) is bytes
160 """
160 """
161
161
162 def __new__(cls, s=b''):
162 def __new__(cls, s=b''):
163 if isinstance(s, bytestr):
163 if isinstance(s, bytestr):
164 return s
164 return s
165 if (not isinstance(s, (bytes, bytearray))
165 if (not isinstance(s, (bytes, bytearray))
166 and not hasattr(s, u'__bytes__')): # hasattr-py3-only
166 and not hasattr(s, u'__bytes__')): # hasattr-py3-only
167 s = str(s).encode(u'ascii')
167 s = str(s).encode(u'ascii')
168 return bytes.__new__(cls, s)
168 return bytes.__new__(cls, s)
169
169
170 def __getitem__(self, key):
170 def __getitem__(self, key):
171 s = bytes.__getitem__(self, key)
171 s = bytes.__getitem__(self, key)
172 if not isinstance(s, bytes):
172 if not isinstance(s, bytes):
173 s = bytechr(s)
173 s = bytechr(s)
174 return s
174 return s
175
175
176 def __iter__(self):
176 def __iter__(self):
177 return iterbytestr(bytes.__iter__(self))
177 return iterbytestr(bytes.__iter__(self))
178
178
179 def __repr__(self):
179 def __repr__(self):
180 return bytes.__repr__(self)[1:] # drop b''
180 return bytes.__repr__(self)[1:] # drop b''
181
181
182 def iterbytestr(s):
182 def iterbytestr(s):
183 """Iterate bytes as if it were a str object of Python 2"""
183 """Iterate bytes as if it were a str object of Python 2"""
184 return map(bytechr, s)
184 return map(bytechr, s)
185
185
186 def maybebytestr(s):
186 def maybebytestr(s):
187 """Promote bytes to bytestr"""
187 """Promote bytes to bytestr"""
188 if isinstance(s, bytes):
188 if isinstance(s, bytes):
189 return bytestr(s)
189 return bytestr(s)
190 return s
190 return s
191
191
192 def sysbytes(s):
192 def sysbytes(s):
193 """Convert an internal str (e.g. keyword, __doc__) back to bytes
193 """Convert an internal str (e.g. keyword, __doc__) back to bytes
194
194
195 This never raises UnicodeEncodeError, but only ASCII characters
195 This never raises UnicodeEncodeError, but only ASCII characters
196 can be round-trip by sysstr(sysbytes(s)).
196 can be round-trip by sysstr(sysbytes(s)).
197 """
197 """
198 return s.encode(u'utf-8')
198 return s.encode(u'utf-8')
199
199
200 def sysstr(s):
200 def sysstr(s):
201 """Return a keyword str to be passed to Python functions such as
201 """Return a keyword str to be passed to Python functions such as
202 getattr() and str.encode()
202 getattr() and str.encode()
203
203
204 This never raises UnicodeDecodeError. Non-ascii characters are
204 This never raises UnicodeDecodeError. Non-ascii characters are
205 considered invalid and mapped to arbitrary but unique code points
205 considered invalid and mapped to arbitrary but unique code points
206 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
206 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
207 """
207 """
208 if isinstance(s, builtins.str):
208 if isinstance(s, builtins.str):
209 return s
209 return s
210 return s.decode(u'latin-1')
210 return s.decode(u'latin-1')
211
211
212 def strurl(url):
212 def strurl(url):
213 """Converts a bytes url back to str"""
213 """Converts a bytes url back to str"""
214 if isinstance(url, bytes):
214 if isinstance(url, bytes):
215 return url.decode(u'ascii')
215 return url.decode(u'ascii')
216 return url
216 return url
217
217
218 def bytesurl(url):
218 def bytesurl(url):
219 """Converts a str url to bytes by encoding in ascii"""
219 """Converts a str url to bytes by encoding in ascii"""
220 if isinstance(url, str):
220 if isinstance(url, str):
221 return url.encode(u'ascii')
221 return url.encode(u'ascii')
222 return url
222 return url
223
223
224 def raisewithtb(exc, tb):
224 def raisewithtb(exc, tb):
225 """Raise exception with the given traceback"""
225 """Raise exception with the given traceback"""
226 raise exc.with_traceback(tb)
226 raise exc.with_traceback(tb)
227
227
228 def getdoc(obj):
228 def getdoc(obj):
229 """Get docstring as bytes; may be None so gettext() won't confuse it
229 """Get docstring as bytes; may be None so gettext() won't confuse it
230 with _('')"""
230 with _('')"""
231 doc = getattr(obj, u'__doc__', None)
231 doc = getattr(obj, u'__doc__', None)
232 if doc is None:
232 if doc is None:
233 return doc
233 return doc
234 return sysbytes(doc)
234 return sysbytes(doc)
235
235
236 def _wrapattrfunc(f):
236 def _wrapattrfunc(f):
237 @functools.wraps(f)
237 @functools.wraps(f)
238 def w(object, name, *args):
238 def w(object, name, *args):
239 return f(object, sysstr(name), *args)
239 return f(object, sysstr(name), *args)
240 return w
240 return w
241
241
242 # these wrappers are automagically imported by hgloader
242 # these wrappers are automagically imported by hgloader
243 delattr = _wrapattrfunc(builtins.delattr)
243 delattr = _wrapattrfunc(builtins.delattr)
244 getattr = _wrapattrfunc(builtins.getattr)
244 getattr = _wrapattrfunc(builtins.getattr)
245 hasattr = _wrapattrfunc(builtins.hasattr)
245 hasattr = _wrapattrfunc(builtins.hasattr)
246 setattr = _wrapattrfunc(builtins.setattr)
246 setattr = _wrapattrfunc(builtins.setattr)
247 xrange = builtins.range
247 xrange = builtins.range
248 unicode = str
248 unicode = str
249
249
250 def open(name, mode='r', buffering=-1, encoding=None):
250 def open(name, mode='r', buffering=-1, encoding=None):
251 return builtins.open(name, sysstr(mode), buffering, encoding)
251 return builtins.open(name, sysstr(mode), buffering, encoding)
252
252
253 safehasattr = _wrapattrfunc(builtins.hasattr)
253 safehasattr = _wrapattrfunc(builtins.hasattr)
254
254
255 def _getoptbwrapper(orig, args, shortlist, namelist):
255 def _getoptbwrapper(orig, args, shortlist, namelist):
256 """
256 """
257 Takes bytes arguments, converts them to unicode, pass them to
257 Takes bytes arguments, converts them to unicode, pass them to
258 getopt.getopt(), convert the returned values back to bytes and then
258 getopt.getopt(), convert the returned values back to bytes and then
259 return them for Python 3 compatibility as getopt.getopt() don't accepts
259 return them for Python 3 compatibility as getopt.getopt() don't accepts
260 bytes on Python 3.
260 bytes on Python 3.
261 """
261 """
262 args = [a.decode('latin-1') for a in args]
262 args = [a.decode('latin-1') for a in args]
263 shortlist = shortlist.decode('latin-1')
263 shortlist = shortlist.decode('latin-1')
264 namelist = [a.decode('latin-1') for a in namelist]
264 namelist = [a.decode('latin-1') for a in namelist]
265 opts, args = orig(args, shortlist, namelist)
265 opts, args = orig(args, shortlist, namelist)
266 opts = [(a[0].encode('latin-1'), a[1].encode('latin-1'))
266 opts = [(a[0].encode('latin-1'), a[1].encode('latin-1'))
267 for a in opts]
267 for a in opts]
268 args = [a.encode('latin-1') for a in args]
268 args = [a.encode('latin-1') for a in args]
269 return opts, args
269 return opts, args
270
270
271 def strkwargs(dic):
271 def strkwargs(dic):
272 """
272 """
273 Converts the keys of a python dictonary to str i.e. unicodes so that
273 Converts the keys of a python dictonary to str i.e. unicodes so that
274 they can be passed as keyword arguments as dictonaries with bytes keys
274 they can be passed as keyword arguments as dictonaries with bytes keys
275 can't be passed as keyword arguments to functions on Python 3.
275 can't be passed as keyword arguments to functions on Python 3.
276 """
276 """
277 dic = dict((k.decode('latin-1'), v) for k, v in dic.iteritems())
277 dic = dict((k.decode('latin-1'), v) for k, v in dic.iteritems())
278 return dic
278 return dic
279
279
280 def byteskwargs(dic):
280 def byteskwargs(dic):
281 """
281 """
282 Converts keys of python dictonaries to bytes as they were converted to
282 Converts keys of python dictonaries to bytes as they were converted to
283 str to pass that dictonary as a keyword argument on Python 3.
283 str to pass that dictonary as a keyword argument on Python 3.
284 """
284 """
285 dic = dict((k.encode('latin-1'), v) for k, v in dic.iteritems())
285 dic = dict((k.encode('latin-1'), v) for k, v in dic.iteritems())
286 return dic
286 return dic
287
287
288 # TODO: handle shlex.shlex().
288 # TODO: handle shlex.shlex().
289 def shlexsplit(s, comments=False, posix=True):
289 def shlexsplit(s, comments=False, posix=True):
290 """
290 """
291 Takes bytes argument, convert it to str i.e. unicodes, pass that into
291 Takes bytes argument, convert it to str i.e. unicodes, pass that into
292 shlex.split(), convert the returned value to bytes and return that for
292 shlex.split(), convert the returned value to bytes and return that for
293 Python 3 compatibility as shelx.split() don't accept bytes on Python 3.
293 Python 3 compatibility as shelx.split() don't accept bytes on Python 3.
294 """
294 """
295 ret = shlex.split(s.decode('latin-1'), comments, posix)
295 ret = shlex.split(s.decode('latin-1'), comments, posix)
296 return [a.encode('latin-1') for a in ret]
296 return [a.encode('latin-1') for a in ret]
297
297
298 def emailparser(*args, **kwargs):
298 def emailparser(*args, **kwargs):
299 import email.parser
299 import email.parser
300 return email.parser.BytesParser(*args, **kwargs)
300 return email.parser.BytesParser(*args, **kwargs)
301
301
302 else:
302 else:
303 import cStringIO
303 import cStringIO
304
304
305 bytechr = chr
305 bytechr = chr
306 byterepr = repr
306 byterepr = repr
307 bytestr = str
307 bytestr = str
308 iterbytestr = iter
308 iterbytestr = iter
309 maybebytestr = identity
309 maybebytestr = identity
310 sysbytes = identity
310 sysbytes = identity
311 sysstr = identity
311 sysstr = identity
312 strurl = identity
312 strurl = identity
313 bytesurl = identity
313 bytesurl = identity
314
314
315 # this can't be parsed on Python 3
315 # this can't be parsed on Python 3
316 exec('def raisewithtb(exc, tb):\n'
316 exec('def raisewithtb(exc, tb):\n'
317 ' raise exc, None, tb\n')
317 ' raise exc, None, tb\n')
318
318
319 def fsencode(filename):
319 def fsencode(filename):
320 """
320 """
321 Partial backport from os.py in Python 3, which only accepts bytes.
321 Partial backport from os.py in Python 3, which only accepts bytes.
322 In Python 2, our paths should only ever be bytes, a unicode path
322 In Python 2, our paths should only ever be bytes, a unicode path
323 indicates a bug.
323 indicates a bug.
324 """
324 """
325 if isinstance(filename, str):
325 if isinstance(filename, str):
326 return filename
326 return filename
327 else:
327 else:
328 raise TypeError(
328 raise TypeError(
329 "expect str, not %s" % type(filename).__name__)
329 "expect str, not %s" % type(filename).__name__)
330
330
331 # In Python 2, fsdecode() has a very chance to receive bytes. So it's
331 # In Python 2, fsdecode() has a very chance to receive bytes. So it's
332 # better not to touch Python 2 part as it's already working fine.
332 # better not to touch Python 2 part as it's already working fine.
333 fsdecode = identity
333 fsdecode = identity
334
334
335 def getdoc(obj):
335 def getdoc(obj):
336 return getattr(obj, '__doc__', None)
336 return getattr(obj, '__doc__', None)
337
337
338 _notset = object()
338 _notset = object()
339
339
340 def safehasattr(thing, attr):
340 def safehasattr(thing, attr):
341 return getattr(thing, attr, _notset) is not _notset
341 return getattr(thing, attr, _notset) is not _notset
342
342
343 def _getoptbwrapper(orig, args, shortlist, namelist):
343 def _getoptbwrapper(orig, args, shortlist, namelist):
344 return orig(args, shortlist, namelist)
344 return orig(args, shortlist, namelist)
345
345
346 strkwargs = identity
346 strkwargs = identity
347 byteskwargs = identity
347 byteskwargs = identity
348
348
349 oscurdir = os.curdir
349 oscurdir = os.curdir
350 oslinesep = os.linesep
350 oslinesep = os.linesep
351 osname = os.name
351 osname = os.name
352 ospathsep = os.pathsep
352 ospathsep = os.pathsep
353 ospardir = os.pardir
353 ospardir = os.pardir
354 ossep = os.sep
354 ossep = os.sep
355 osaltsep = os.altsep
355 osaltsep = os.altsep
356 stdin = sys.stdin
356 stdin = sys.stdin
357 stdout = sys.stdout
357 stdout = sys.stdout
358 stderr = sys.stderr
358 stderr = sys.stderr
359 if getattr(sys, 'argv', None) is not None:
359 if getattr(sys, 'argv', None) is not None:
360 sysargv = sys.argv
360 sysargv = sys.argv
361 sysplatform = sys.platform
361 sysplatform = sys.platform
362 getcwd = os.getcwd
362 getcwd = os.getcwd
363 sysexecutable = sys.executable
363 sysexecutable = sys.executable
364 shlexsplit = shlex.split
364 shlexsplit = shlex.split
365 bytesio = cStringIO.StringIO
365 bytesio = cStringIO.StringIO
366 stringio = bytesio
366 stringio = bytesio
367 maplist = map
367 maplist = map
368 rangelist = range
368 rangelist = range
369 ziplist = zip
369 ziplist = zip
370 rawinput = raw_input
370 rawinput = raw_input
371 getargspec = inspect.getargspec
371 getargspec = inspect.getargspec
372
372
373 def emailparser(*args, **kwargs):
373 def emailparser(*args, **kwargs):
374 import email.parser
374 import email.parser
375 return email.parser.Parser(*args, **kwargs)
375 return email.parser.Parser(*args, **kwargs)
376
376
377 isjython = sysplatform.startswith('java')
377 isjython = sysplatform.startswith('java')
378
378
379 isdarwin = sysplatform == 'darwin'
379 isdarwin = sysplatform == 'darwin'
380 isposix = osname == 'posix'
380 isposix = osname == 'posix'
381 iswindows = osname == 'nt'
381 iswindows = osname == 'nt'
382
382
383 def getoptb(args, shortlist, namelist):
383 def getoptb(args, shortlist, namelist):
384 return _getoptbwrapper(getopt.getopt, args, shortlist, namelist)
384 return _getoptbwrapper(getopt.getopt, args, shortlist, namelist)
385
385
386 def gnugetoptb(args, shortlist, namelist):
386 def gnugetoptb(args, shortlist, namelist):
387 return _getoptbwrapper(getopt.gnu_getopt, args, shortlist, namelist)
387 return _getoptbwrapper(getopt.gnu_getopt, args, shortlist, namelist)
388
388
389 def mkdtemp(suffix=b'', prefix=b'tmp', dir=None):
389 def mkdtemp(suffix=b'', prefix=b'tmp', dir=None):
390 return tempfile.mkdtemp(suffix, prefix, dir)
390 return tempfile.mkdtemp(suffix, prefix, dir)
391
391
392 # text=True is not supported; use util.from/tonativeeol() instead
392 # text=True is not supported; use util.from/tonativeeol() instead
393 def mkstemp(suffix=b'', prefix=b'tmp', dir=None):
393 def mkstemp(suffix=b'', prefix=b'tmp', dir=None):
394 return tempfile.mkstemp(suffix, prefix, dir)
394 return tempfile.mkstemp(suffix, prefix, dir)
395
396 # mode must include 'b'ytes as encoding= is not supported
397 def namedtempfile(mode=b'w+b', bufsize=-1, suffix=b'', prefix=b'tmp', dir=None,
398 delete=True):
399 mode = sysstr(mode)
400 assert r'b' in mode
401 return tempfile.NamedTemporaryFile(mode, bufsize, suffix=suffix,
402 prefix=prefix, dir=dir, delete=delete)
General Comments 0
You need to be logged in to leave comments. Login now