##// END OF EJS Templates
strip: move into core...
Valentin Gatien-Baron -
r46469:a46efd42 default draft
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,4299 +1,4300 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.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 '''manage a stack of patches
8 '''manage a stack of patches
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use :hg:`help COMMAND` for more details)::
17 Common tasks (use :hg:`help COMMAND` for more details)::
18
18
19 create new patch qnew
19 create new patch qnew
20 import existing patch qimport
20 import existing patch qimport
21
21
22 print patch series qseries
22 print patch series qseries
23 print applied patches qapplied
23 print applied patches qapplied
24
24
25 add known patch to applied stack qpush
25 add known patch to applied stack qpush
26 remove patch from applied stack qpop
26 remove patch from applied stack qpop
27 refresh contents of top applied patch qrefresh
27 refresh contents of top applied patch qrefresh
28
28
29 By default, mq will automatically use git patches when required to
29 By default, mq will automatically use git patches when required to
30 avoid losing file mode changes, copy records, binary files or empty
30 avoid losing file mode changes, copy records, binary files or empty
31 files creations or deletions. This behavior can be configured with::
31 files creations or deletions. This behavior can be configured with::
32
32
33 [mq]
33 [mq]
34 git = auto/keep/yes/no
34 git = auto/keep/yes/no
35
35
36 If set to 'keep', mq will obey the [diff] section configuration while
36 If set to 'keep', mq will obey the [diff] section configuration while
37 preserving existing git patches upon qrefresh. If set to 'yes' or
37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 'no', mq will override the [diff] section and always generate git or
38 'no', mq will override the [diff] section and always generate git or
39 regular patches, possibly losing data in the second case.
39 regular patches, possibly losing data in the second case.
40
40
41 It may be desirable for mq changesets to be kept in the secret phase (see
41 It may be desirable for mq changesets to be kept in the secret phase (see
42 :hg:`help phases`), which can be enabled with the following setting::
42 :hg:`help phases`), which can be enabled with the following setting::
43
43
44 [mq]
44 [mq]
45 secret = True
45 secret = True
46
46
47 You will by default be managing a patch queue named "patches". You can
47 You will by default be managing a patch queue named "patches". You can
48 create other, independent patch queues with the :hg:`qqueue` command.
48 create other, independent patch queues with the :hg:`qqueue` command.
49
49
50 If the working directory contains uncommitted files, qpush, qpop and
50 If the working directory contains uncommitted files, qpush, qpop and
51 qgoto abort immediately. If -f/--force is used, the changes are
51 qgoto abort immediately. If -f/--force is used, the changes are
52 discarded. Setting::
52 discarded. Setting::
53
53
54 [mq]
54 [mq]
55 keepchanges = True
55 keepchanges = True
56
56
57 make them behave as if --keep-changes were passed, and non-conflicting
57 make them behave as if --keep-changes were passed, and non-conflicting
58 local changes will be tolerated and preserved. If incompatible options
58 local changes will be tolerated and preserved. If incompatible options
59 such as -f/--force or --exact are passed, this setting is ignored.
59 such as -f/--force or --exact are passed, this setting is ignored.
60
60
61 This extension used to provide a strip command. This command now lives
61 This extension used to provide a strip command. This command now lives
62 in the strip extension.
62 in the strip extension.
63 '''
63 '''
64
64
65 from __future__ import absolute_import, print_function
65 from __future__ import absolute_import, print_function
66
66
67 import errno
67 import errno
68 import os
68 import os
69 import re
69 import re
70 import shutil
70 import shutil
71 import sys
71 import sys
72 from mercurial.i18n import _
72 from mercurial.i18n import _
73 from mercurial.node import (
73 from mercurial.node import (
74 bin,
74 bin,
75 hex,
75 hex,
76 nullid,
76 nullid,
77 nullrev,
77 nullrev,
78 short,
78 short,
79 )
79 )
80 from mercurial.pycompat import (
80 from mercurial.pycompat import (
81 delattr,
81 delattr,
82 getattr,
82 getattr,
83 open,
83 open,
84 )
84 )
85 from mercurial import (
85 from mercurial import (
86 cmdutil,
86 cmdutil,
87 commands,
87 commands,
88 dirstateguard,
88 dirstateguard,
89 encoding,
89 encoding,
90 error,
90 error,
91 extensions,
91 extensions,
92 hg,
92 hg,
93 localrepo,
93 localrepo,
94 lock as lockmod,
94 lock as lockmod,
95 logcmdutil,
95 logcmdutil,
96 patch as patchmod,
96 patch as patchmod,
97 phases,
97 phases,
98 pycompat,
98 pycompat,
99 registrar,
99 registrar,
100 revsetlang,
100 revsetlang,
101 scmutil,
101 scmutil,
102 smartset,
102 smartset,
103 strip,
103 subrepoutil,
104 subrepoutil,
104 util,
105 util,
105 vfs as vfsmod,
106 vfs as vfsmod,
106 )
107 )
107 from mercurial.utils import (
108 from mercurial.utils import (
108 dateutil,
109 dateutil,
109 stringutil,
110 stringutil,
110 )
111 )
111
112
112 release = lockmod.release
113 release = lockmod.release
113 seriesopts = [(b's', b'summary', None, _(b'print first line of patch header'))]
114 seriesopts = [(b's', b'summary', None, _(b'print first line of patch header'))]
114
115
115 cmdtable = {}
116 cmdtable = {}
116 command = registrar.command(cmdtable)
117 command = registrar.command(cmdtable)
117 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
118 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
118 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
119 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
119 # be specifying the version(s) of Mercurial they are tested with, or
120 # be specifying the version(s) of Mercurial they are tested with, or
120 # leave the attribute unspecified.
121 # leave the attribute unspecified.
121 testedwith = b'ships-with-hg-core'
122 testedwith = b'ships-with-hg-core'
122
123
123 configtable = {}
124 configtable = {}
124 configitem = registrar.configitem(configtable)
125 configitem = registrar.configitem(configtable)
125
126
126 configitem(
127 configitem(
127 b'mq', b'git', default=b'auto',
128 b'mq', b'git', default=b'auto',
128 )
129 )
129 configitem(
130 configitem(
130 b'mq', b'keepchanges', default=False,
131 b'mq', b'keepchanges', default=False,
131 )
132 )
132 configitem(
133 configitem(
133 b'mq', b'plain', default=False,
134 b'mq', b'plain', default=False,
134 )
135 )
135 configitem(
136 configitem(
136 b'mq', b'secret', default=False,
137 b'mq', b'secret', default=False,
137 )
138 )
138
139
139 # force load strip extension formerly included in mq and import some utility
140 # force load strip extension formerly included in mq and import some utility
140 try:
141 try:
141 stripext = extensions.find(b'strip')
142 extensions.find(b'strip')
142 except KeyError:
143 except KeyError:
143 # note: load is lazy so we could avoid the try-except,
144 # note: load is lazy so we could avoid the try-except,
144 # but I (marmoute) prefer this explicit code.
145 # but I (marmoute) prefer this explicit code.
145 class dummyui(object):
146 class dummyui(object):
146 def debug(self, msg):
147 def debug(self, msg):
147 pass
148 pass
148
149
149 def log(self, event, msgfmt, *msgargs, **opts):
150 def log(self, event, msgfmt, *msgargs, **opts):
150 pass
151 pass
151
152
152 stripext = extensions.load(dummyui(), b'strip', b'')
153 extensions.load(dummyui(), b'strip', b'')
153
154
154 strip = stripext.strip
155 strip = strip.strip
155
156
156
157
157 def checksubstate(repo, baserev=None):
158 def checksubstate(repo, baserev=None):
158 '''return list of subrepos at a different revision than substate.
159 '''return list of subrepos at a different revision than substate.
159 Abort if any subrepos have uncommitted changes.'''
160 Abort if any subrepos have uncommitted changes.'''
160 inclsubs = []
161 inclsubs = []
161 wctx = repo[None]
162 wctx = repo[None]
162 if baserev:
163 if baserev:
163 bctx = repo[baserev]
164 bctx = repo[baserev]
164 else:
165 else:
165 bctx = wctx.p1()
166 bctx = wctx.p1()
166 for s in sorted(wctx.substate):
167 for s in sorted(wctx.substate):
167 wctx.sub(s).bailifchanged(True)
168 wctx.sub(s).bailifchanged(True)
168 if s not in bctx.substate or bctx.sub(s).dirty():
169 if s not in bctx.substate or bctx.sub(s).dirty():
169 inclsubs.append(s)
170 inclsubs.append(s)
170 return inclsubs
171 return inclsubs
171
172
172
173
173 # Patch names looks like unix-file names.
174 # Patch names looks like unix-file names.
174 # They must be joinable with queue directory and result in the patch path.
175 # They must be joinable with queue directory and result in the patch path.
175 normname = util.normpath
176 normname = util.normpath
176
177
177
178
178 class statusentry(object):
179 class statusentry(object):
179 def __init__(self, node, name):
180 def __init__(self, node, name):
180 self.node, self.name = node, name
181 self.node, self.name = node, name
181
182
182 def __bytes__(self):
183 def __bytes__(self):
183 return hex(self.node) + b':' + self.name
184 return hex(self.node) + b':' + self.name
184
185
185 __str__ = encoding.strmethod(__bytes__)
186 __str__ = encoding.strmethod(__bytes__)
186 __repr__ = encoding.strmethod(__bytes__)
187 __repr__ = encoding.strmethod(__bytes__)
187
188
188
189
189 # The order of the headers in 'hg export' HG patches:
190 # The order of the headers in 'hg export' HG patches:
190 HGHEADERS = [
191 HGHEADERS = [
191 # '# HG changeset patch',
192 # '# HG changeset patch',
192 b'# User ',
193 b'# User ',
193 b'# Date ',
194 b'# Date ',
194 b'# ',
195 b'# ',
195 b'# Branch ',
196 b'# Branch ',
196 b'# Node ID ',
197 b'# Node ID ',
197 b'# Parent ', # can occur twice for merges - but that is not relevant for mq
198 b'# Parent ', # can occur twice for merges - but that is not relevant for mq
198 ]
199 ]
199 # The order of headers in plain 'mail style' patches:
200 # The order of headers in plain 'mail style' patches:
200 PLAINHEADERS = {
201 PLAINHEADERS = {
201 b'from': 0,
202 b'from': 0,
202 b'date': 1,
203 b'date': 1,
203 b'subject': 2,
204 b'subject': 2,
204 }
205 }
205
206
206
207
207 def inserthgheader(lines, header, value):
208 def inserthgheader(lines, header, value):
208 """Assuming lines contains a HG patch header, add a header line with value.
209 """Assuming lines contains a HG patch header, add a header line with value.
209 >>> try: inserthgheader([], b'# Date ', b'z')
210 >>> try: inserthgheader([], b'# Date ', b'z')
210 ... except ValueError as inst: print("oops")
211 ... except ValueError as inst: print("oops")
211 oops
212 oops
212 >>> inserthgheader([b'# HG changeset patch'], b'# Date ', b'z')
213 >>> inserthgheader([b'# HG changeset patch'], b'# Date ', b'z')
213 ['# HG changeset patch', '# Date z']
214 ['# HG changeset patch', '# Date z']
214 >>> inserthgheader([b'# HG changeset patch', b''], b'# Date ', b'z')
215 >>> inserthgheader([b'# HG changeset patch', b''], b'# Date ', b'z')
215 ['# HG changeset patch', '# Date z', '']
216 ['# HG changeset patch', '# Date z', '']
216 >>> inserthgheader([b'# HG changeset patch', b'# User y'], b'# Date ', b'z')
217 >>> inserthgheader([b'# HG changeset patch', b'# User y'], b'# Date ', b'z')
217 ['# HG changeset patch', '# User y', '# Date z']
218 ['# HG changeset patch', '# User y', '# Date z']
218 >>> inserthgheader([b'# HG changeset patch', b'# Date x', b'# User y'],
219 >>> inserthgheader([b'# HG changeset patch', b'# Date x', b'# User y'],
219 ... b'# User ', b'z')
220 ... b'# User ', b'z')
220 ['# HG changeset patch', '# Date x', '# User z']
221 ['# HG changeset patch', '# Date x', '# User z']
221 >>> inserthgheader([b'# HG changeset patch', b'# Date y'], b'# Date ', b'z')
222 >>> inserthgheader([b'# HG changeset patch', b'# Date y'], b'# Date ', b'z')
222 ['# HG changeset patch', '# Date z']
223 ['# HG changeset patch', '# Date z']
223 >>> inserthgheader([b'# HG changeset patch', b'', b'# Date y'],
224 >>> inserthgheader([b'# HG changeset patch', b'', b'# Date y'],
224 ... b'# Date ', b'z')
225 ... b'# Date ', b'z')
225 ['# HG changeset patch', '# Date z', '', '# Date y']
226 ['# HG changeset patch', '# Date z', '', '# Date y']
226 >>> inserthgheader([b'# HG changeset patch', b'# Parent y'],
227 >>> inserthgheader([b'# HG changeset patch', b'# Parent y'],
227 ... b'# Date ', b'z')
228 ... b'# Date ', b'z')
228 ['# HG changeset patch', '# Date z', '# Parent y']
229 ['# HG changeset patch', '# Date z', '# Parent y']
229 """
230 """
230 start = lines.index(b'# HG changeset patch') + 1
231 start = lines.index(b'# HG changeset patch') + 1
231 newindex = HGHEADERS.index(header)
232 newindex = HGHEADERS.index(header)
232 bestpos = len(lines)
233 bestpos = len(lines)
233 for i in range(start, len(lines)):
234 for i in range(start, len(lines)):
234 line = lines[i]
235 line = lines[i]
235 if not line.startswith(b'# '):
236 if not line.startswith(b'# '):
236 bestpos = min(bestpos, i)
237 bestpos = min(bestpos, i)
237 break
238 break
238 for lineindex, h in enumerate(HGHEADERS):
239 for lineindex, h in enumerate(HGHEADERS):
239 if line.startswith(h):
240 if line.startswith(h):
240 if lineindex == newindex:
241 if lineindex == newindex:
241 lines[i] = header + value
242 lines[i] = header + value
242 return lines
243 return lines
243 if lineindex > newindex:
244 if lineindex > newindex:
244 bestpos = min(bestpos, i)
245 bestpos = min(bestpos, i)
245 break # next line
246 break # next line
246 lines.insert(bestpos, header + value)
247 lines.insert(bestpos, header + value)
247 return lines
248 return lines
248
249
249
250
250 def insertplainheader(lines, header, value):
251 def insertplainheader(lines, header, value):
251 """For lines containing a plain patch header, add a header line with value.
252 """For lines containing a plain patch header, add a header line with value.
252 >>> insertplainheader([], b'Date', b'z')
253 >>> insertplainheader([], b'Date', b'z')
253 ['Date: z']
254 ['Date: z']
254 >>> insertplainheader([b''], b'Date', b'z')
255 >>> insertplainheader([b''], b'Date', b'z')
255 ['Date: z', '']
256 ['Date: z', '']
256 >>> insertplainheader([b'x'], b'Date', b'z')
257 >>> insertplainheader([b'x'], b'Date', b'z')
257 ['Date: z', '', 'x']
258 ['Date: z', '', 'x']
258 >>> insertplainheader([b'From: y', b'x'], b'Date', b'z')
259 >>> insertplainheader([b'From: y', b'x'], b'Date', b'z')
259 ['From: y', 'Date: z', '', 'x']
260 ['From: y', 'Date: z', '', 'x']
260 >>> insertplainheader([b' date : x', b' from : y', b''], b'From', b'z')
261 >>> insertplainheader([b' date : x', b' from : y', b''], b'From', b'z')
261 [' date : x', 'From: z', '']
262 [' date : x', 'From: z', '']
262 >>> insertplainheader([b'', b'Date: y'], b'Date', b'z')
263 >>> insertplainheader([b'', b'Date: y'], b'Date', b'z')
263 ['Date: z', '', 'Date: y']
264 ['Date: z', '', 'Date: y']
264 >>> insertplainheader([b'foo: bar', b'DATE: z', b'x'], b'From', b'y')
265 >>> insertplainheader([b'foo: bar', b'DATE: z', b'x'], b'From', b'y')
265 ['From: y', 'foo: bar', 'DATE: z', '', 'x']
266 ['From: y', 'foo: bar', 'DATE: z', '', 'x']
266 """
267 """
267 newprio = PLAINHEADERS[header.lower()]
268 newprio = PLAINHEADERS[header.lower()]
268 bestpos = len(lines)
269 bestpos = len(lines)
269 for i, line in enumerate(lines):
270 for i, line in enumerate(lines):
270 if b':' in line:
271 if b':' in line:
271 lheader = line.split(b':', 1)[0].strip().lower()
272 lheader = line.split(b':', 1)[0].strip().lower()
272 lprio = PLAINHEADERS.get(lheader, newprio + 1)
273 lprio = PLAINHEADERS.get(lheader, newprio + 1)
273 if lprio == newprio:
274 if lprio == newprio:
274 lines[i] = b'%s: %s' % (header, value)
275 lines[i] = b'%s: %s' % (header, value)
275 return lines
276 return lines
276 if lprio > newprio and i < bestpos:
277 if lprio > newprio and i < bestpos:
277 bestpos = i
278 bestpos = i
278 else:
279 else:
279 if line:
280 if line:
280 lines.insert(i, b'')
281 lines.insert(i, b'')
281 if i < bestpos:
282 if i < bestpos:
282 bestpos = i
283 bestpos = i
283 break
284 break
284 lines.insert(bestpos, b'%s: %s' % (header, value))
285 lines.insert(bestpos, b'%s: %s' % (header, value))
285 return lines
286 return lines
286
287
287
288
288 class patchheader(object):
289 class patchheader(object):
289 def __init__(self, pf, plainmode=False):
290 def __init__(self, pf, plainmode=False):
290 def eatdiff(lines):
291 def eatdiff(lines):
291 while lines:
292 while lines:
292 l = lines[-1]
293 l = lines[-1]
293 if (
294 if (
294 l.startswith(b"diff -")
295 l.startswith(b"diff -")
295 or l.startswith(b"Index:")
296 or l.startswith(b"Index:")
296 or l.startswith(b"===========")
297 or l.startswith(b"===========")
297 ):
298 ):
298 del lines[-1]
299 del lines[-1]
299 else:
300 else:
300 break
301 break
301
302
302 def eatempty(lines):
303 def eatempty(lines):
303 while lines:
304 while lines:
304 if not lines[-1].strip():
305 if not lines[-1].strip():
305 del lines[-1]
306 del lines[-1]
306 else:
307 else:
307 break
308 break
308
309
309 message = []
310 message = []
310 comments = []
311 comments = []
311 user = None
312 user = None
312 date = None
313 date = None
313 parent = None
314 parent = None
314 format = None
315 format = None
315 subject = None
316 subject = None
316 branch = None
317 branch = None
317 nodeid = None
318 nodeid = None
318 diffstart = 0
319 diffstart = 0
319
320
320 for line in open(pf, b'rb'):
321 for line in open(pf, b'rb'):
321 line = line.rstrip()
322 line = line.rstrip()
322 if line.startswith(b'diff --git') or (
323 if line.startswith(b'diff --git') or (
323 diffstart and line.startswith(b'+++ ')
324 diffstart and line.startswith(b'+++ ')
324 ):
325 ):
325 diffstart = 2
326 diffstart = 2
326 break
327 break
327 diffstart = 0 # reset
328 diffstart = 0 # reset
328 if line.startswith(b"--- "):
329 if line.startswith(b"--- "):
329 diffstart = 1
330 diffstart = 1
330 continue
331 continue
331 elif format == b"hgpatch":
332 elif format == b"hgpatch":
332 # parse values when importing the result of an hg export
333 # parse values when importing the result of an hg export
333 if line.startswith(b"# User "):
334 if line.startswith(b"# User "):
334 user = line[7:]
335 user = line[7:]
335 elif line.startswith(b"# Date "):
336 elif line.startswith(b"# Date "):
336 date = line[7:]
337 date = line[7:]
337 elif line.startswith(b"# Parent "):
338 elif line.startswith(b"# Parent "):
338 parent = line[9:].lstrip() # handle double trailing space
339 parent = line[9:].lstrip() # handle double trailing space
339 elif line.startswith(b"# Branch "):
340 elif line.startswith(b"# Branch "):
340 branch = line[9:]
341 branch = line[9:]
341 elif line.startswith(b"# Node ID "):
342 elif line.startswith(b"# Node ID "):
342 nodeid = line[10:]
343 nodeid = line[10:]
343 elif not line.startswith(b"# ") and line:
344 elif not line.startswith(b"# ") and line:
344 message.append(line)
345 message.append(line)
345 format = None
346 format = None
346 elif line == b'# HG changeset patch':
347 elif line == b'# HG changeset patch':
347 message = []
348 message = []
348 format = b"hgpatch"
349 format = b"hgpatch"
349 elif format != b"tagdone" and (
350 elif format != b"tagdone" and (
350 line.startswith(b"Subject: ") or line.startswith(b"subject: ")
351 line.startswith(b"Subject: ") or line.startswith(b"subject: ")
351 ):
352 ):
352 subject = line[9:]
353 subject = line[9:]
353 format = b"tag"
354 format = b"tag"
354 elif format != b"tagdone" and (
355 elif format != b"tagdone" and (
355 line.startswith(b"From: ") or line.startswith(b"from: ")
356 line.startswith(b"From: ") or line.startswith(b"from: ")
356 ):
357 ):
357 user = line[6:]
358 user = line[6:]
358 format = b"tag"
359 format = b"tag"
359 elif format != b"tagdone" and (
360 elif format != b"tagdone" and (
360 line.startswith(b"Date: ") or line.startswith(b"date: ")
361 line.startswith(b"Date: ") or line.startswith(b"date: ")
361 ):
362 ):
362 date = line[6:]
363 date = line[6:]
363 format = b"tag"
364 format = b"tag"
364 elif format == b"tag" and line == b"":
365 elif format == b"tag" and line == b"":
365 # when looking for tags (subject: from: etc) they
366 # when looking for tags (subject: from: etc) they
366 # end once you find a blank line in the source
367 # end once you find a blank line in the source
367 format = b"tagdone"
368 format = b"tagdone"
368 elif message or line:
369 elif message or line:
369 message.append(line)
370 message.append(line)
370 comments.append(line)
371 comments.append(line)
371
372
372 eatdiff(message)
373 eatdiff(message)
373 eatdiff(comments)
374 eatdiff(comments)
374 # Remember the exact starting line of the patch diffs before consuming
375 # Remember the exact starting line of the patch diffs before consuming
375 # empty lines, for external use by TortoiseHg and others
376 # empty lines, for external use by TortoiseHg and others
376 self.diffstartline = len(comments)
377 self.diffstartline = len(comments)
377 eatempty(message)
378 eatempty(message)
378 eatempty(comments)
379 eatempty(comments)
379
380
380 # make sure message isn't empty
381 # make sure message isn't empty
381 if format and format.startswith(b"tag") and subject:
382 if format and format.startswith(b"tag") and subject:
382 message.insert(0, subject)
383 message.insert(0, subject)
383
384
384 self.message = message
385 self.message = message
385 self.comments = comments
386 self.comments = comments
386 self.user = user
387 self.user = user
387 self.date = date
388 self.date = date
388 self.parent = parent
389 self.parent = parent
389 # nodeid and branch are for external use by TortoiseHg and others
390 # nodeid and branch are for external use by TortoiseHg and others
390 self.nodeid = nodeid
391 self.nodeid = nodeid
391 self.branch = branch
392 self.branch = branch
392 self.haspatch = diffstart > 1
393 self.haspatch = diffstart > 1
393 self.plainmode = (
394 self.plainmode = (
394 plainmode
395 plainmode
395 or b'# HG changeset patch' not in self.comments
396 or b'# HG changeset patch' not in self.comments
396 and any(
397 and any(
397 c.startswith(b'Date: ') or c.startswith(b'From: ')
398 c.startswith(b'Date: ') or c.startswith(b'From: ')
398 for c in self.comments
399 for c in self.comments
399 )
400 )
400 )
401 )
401
402
402 def setuser(self, user):
403 def setuser(self, user):
403 try:
404 try:
404 inserthgheader(self.comments, b'# User ', user)
405 inserthgheader(self.comments, b'# User ', user)
405 except ValueError:
406 except ValueError:
406 if self.plainmode:
407 if self.plainmode:
407 insertplainheader(self.comments, b'From', user)
408 insertplainheader(self.comments, b'From', user)
408 else:
409 else:
409 tmp = [b'# HG changeset patch', b'# User ' + user]
410 tmp = [b'# HG changeset patch', b'# User ' + user]
410 self.comments = tmp + self.comments
411 self.comments = tmp + self.comments
411 self.user = user
412 self.user = user
412
413
413 def setdate(self, date):
414 def setdate(self, date):
414 try:
415 try:
415 inserthgheader(self.comments, b'# Date ', date)
416 inserthgheader(self.comments, b'# Date ', date)
416 except ValueError:
417 except ValueError:
417 if self.plainmode:
418 if self.plainmode:
418 insertplainheader(self.comments, b'Date', date)
419 insertplainheader(self.comments, b'Date', date)
419 else:
420 else:
420 tmp = [b'# HG changeset patch', b'# Date ' + date]
421 tmp = [b'# HG changeset patch', b'# Date ' + date]
421 self.comments = tmp + self.comments
422 self.comments = tmp + self.comments
422 self.date = date
423 self.date = date
423
424
424 def setparent(self, parent):
425 def setparent(self, parent):
425 try:
426 try:
426 inserthgheader(self.comments, b'# Parent ', parent)
427 inserthgheader(self.comments, b'# Parent ', parent)
427 except ValueError:
428 except ValueError:
428 if not self.plainmode:
429 if not self.plainmode:
429 tmp = [b'# HG changeset patch', b'# Parent ' + parent]
430 tmp = [b'# HG changeset patch', b'# Parent ' + parent]
430 self.comments = tmp + self.comments
431 self.comments = tmp + self.comments
431 self.parent = parent
432 self.parent = parent
432
433
433 def setmessage(self, message):
434 def setmessage(self, message):
434 if self.comments:
435 if self.comments:
435 self._delmsg()
436 self._delmsg()
436 self.message = [message]
437 self.message = [message]
437 if message:
438 if message:
438 if self.plainmode and self.comments and self.comments[-1]:
439 if self.plainmode and self.comments and self.comments[-1]:
439 self.comments.append(b'')
440 self.comments.append(b'')
440 self.comments.append(message)
441 self.comments.append(message)
441
442
442 def __bytes__(self):
443 def __bytes__(self):
443 s = b'\n'.join(self.comments).rstrip()
444 s = b'\n'.join(self.comments).rstrip()
444 if not s:
445 if not s:
445 return b''
446 return b''
446 return s + b'\n\n'
447 return s + b'\n\n'
447
448
448 __str__ = encoding.strmethod(__bytes__)
449 __str__ = encoding.strmethod(__bytes__)
449
450
450 def _delmsg(self):
451 def _delmsg(self):
451 '''Remove existing message, keeping the rest of the comments fields.
452 '''Remove existing message, keeping the rest of the comments fields.
452 If comments contains 'subject: ', message will prepend
453 If comments contains 'subject: ', message will prepend
453 the field and a blank line.'''
454 the field and a blank line.'''
454 if self.message:
455 if self.message:
455 subj = b'subject: ' + self.message[0].lower()
456 subj = b'subject: ' + self.message[0].lower()
456 for i in pycompat.xrange(len(self.comments)):
457 for i in pycompat.xrange(len(self.comments)):
457 if subj == self.comments[i].lower():
458 if subj == self.comments[i].lower():
458 del self.comments[i]
459 del self.comments[i]
459 self.message = self.message[2:]
460 self.message = self.message[2:]
460 break
461 break
461 ci = 0
462 ci = 0
462 for mi in self.message:
463 for mi in self.message:
463 while mi != self.comments[ci]:
464 while mi != self.comments[ci]:
464 ci += 1
465 ci += 1
465 del self.comments[ci]
466 del self.comments[ci]
466
467
467
468
468 def newcommit(repo, phase, *args, **kwargs):
469 def newcommit(repo, phase, *args, **kwargs):
469 """helper dedicated to ensure a commit respect mq.secret setting
470 """helper dedicated to ensure a commit respect mq.secret setting
470
471
471 It should be used instead of repo.commit inside the mq source for operation
472 It should be used instead of repo.commit inside the mq source for operation
472 creating new changeset.
473 creating new changeset.
473 """
474 """
474 repo = repo.unfiltered()
475 repo = repo.unfiltered()
475 if phase is None:
476 if phase is None:
476 if repo.ui.configbool(b'mq', b'secret'):
477 if repo.ui.configbool(b'mq', b'secret'):
477 phase = phases.secret
478 phase = phases.secret
478 overrides = {(b'ui', b'allowemptycommit'): True}
479 overrides = {(b'ui', b'allowemptycommit'): True}
479 if phase is not None:
480 if phase is not None:
480 overrides[(b'phases', b'new-commit')] = phase
481 overrides[(b'phases', b'new-commit')] = phase
481 with repo.ui.configoverride(overrides, b'mq'):
482 with repo.ui.configoverride(overrides, b'mq'):
482 repo.ui.setconfig(b'ui', b'allowemptycommit', True)
483 repo.ui.setconfig(b'ui', b'allowemptycommit', True)
483 return repo.commit(*args, **kwargs)
484 return repo.commit(*args, **kwargs)
484
485
485
486
486 class AbortNoCleanup(error.Abort):
487 class AbortNoCleanup(error.Abort):
487 pass
488 pass
488
489
489
490
490 class queue(object):
491 class queue(object):
491 def __init__(self, ui, baseui, path, patchdir=None):
492 def __init__(self, ui, baseui, path, patchdir=None):
492 self.basepath = path
493 self.basepath = path
493 try:
494 try:
494 with open(os.path.join(path, b'patches.queue'), 'rb') as fh:
495 with open(os.path.join(path, b'patches.queue'), 'rb') as fh:
495 cur = fh.read().rstrip()
496 cur = fh.read().rstrip()
496
497
497 if not cur:
498 if not cur:
498 curpath = os.path.join(path, b'patches')
499 curpath = os.path.join(path, b'patches')
499 else:
500 else:
500 curpath = os.path.join(path, b'patches-' + cur)
501 curpath = os.path.join(path, b'patches-' + cur)
501 except IOError:
502 except IOError:
502 curpath = os.path.join(path, b'patches')
503 curpath = os.path.join(path, b'patches')
503 self.path = patchdir or curpath
504 self.path = patchdir or curpath
504 self.opener = vfsmod.vfs(self.path)
505 self.opener = vfsmod.vfs(self.path)
505 self.ui = ui
506 self.ui = ui
506 self.baseui = baseui
507 self.baseui = baseui
507 self.applieddirty = False
508 self.applieddirty = False
508 self.seriesdirty = False
509 self.seriesdirty = False
509 self.added = []
510 self.added = []
510 self.seriespath = b"series"
511 self.seriespath = b"series"
511 self.statuspath = b"status"
512 self.statuspath = b"status"
512 self.guardspath = b"guards"
513 self.guardspath = b"guards"
513 self.activeguards = None
514 self.activeguards = None
514 self.guardsdirty = False
515 self.guardsdirty = False
515 # Handle mq.git as a bool with extended values
516 # Handle mq.git as a bool with extended values
516 gitmode = ui.config(b'mq', b'git').lower()
517 gitmode = ui.config(b'mq', b'git').lower()
517 boolmode = stringutil.parsebool(gitmode)
518 boolmode = stringutil.parsebool(gitmode)
518 if boolmode is not None:
519 if boolmode is not None:
519 if boolmode:
520 if boolmode:
520 gitmode = b'yes'
521 gitmode = b'yes'
521 else:
522 else:
522 gitmode = b'no'
523 gitmode = b'no'
523 self.gitmode = gitmode
524 self.gitmode = gitmode
524 # deprecated config: mq.plain
525 # deprecated config: mq.plain
525 self.plainmode = ui.configbool(b'mq', b'plain')
526 self.plainmode = ui.configbool(b'mq', b'plain')
526 self.checkapplied = True
527 self.checkapplied = True
527
528
528 @util.propertycache
529 @util.propertycache
529 def applied(self):
530 def applied(self):
530 def parselines(lines):
531 def parselines(lines):
531 for l in lines:
532 for l in lines:
532 entry = l.split(b':', 1)
533 entry = l.split(b':', 1)
533 if len(entry) > 1:
534 if len(entry) > 1:
534 n, name = entry
535 n, name = entry
535 yield statusentry(bin(n), name)
536 yield statusentry(bin(n), name)
536 elif l.strip():
537 elif l.strip():
537 self.ui.warn(
538 self.ui.warn(
538 _(b'malformated mq status line: %s\n')
539 _(b'malformated mq status line: %s\n')
539 % stringutil.pprint(entry)
540 % stringutil.pprint(entry)
540 )
541 )
541 # else we ignore empty lines
542 # else we ignore empty lines
542
543
543 try:
544 try:
544 lines = self.opener.read(self.statuspath).splitlines()
545 lines = self.opener.read(self.statuspath).splitlines()
545 return list(parselines(lines))
546 return list(parselines(lines))
546 except IOError as e:
547 except IOError as e:
547 if e.errno == errno.ENOENT:
548 if e.errno == errno.ENOENT:
548 return []
549 return []
549 raise
550 raise
550
551
551 @util.propertycache
552 @util.propertycache
552 def fullseries(self):
553 def fullseries(self):
553 try:
554 try:
554 return self.opener.read(self.seriespath).splitlines()
555 return self.opener.read(self.seriespath).splitlines()
555 except IOError as e:
556 except IOError as e:
556 if e.errno == errno.ENOENT:
557 if e.errno == errno.ENOENT:
557 return []
558 return []
558 raise
559 raise
559
560
560 @util.propertycache
561 @util.propertycache
561 def series(self):
562 def series(self):
562 self.parseseries()
563 self.parseseries()
563 return self.series
564 return self.series
564
565
565 @util.propertycache
566 @util.propertycache
566 def seriesguards(self):
567 def seriesguards(self):
567 self.parseseries()
568 self.parseseries()
568 return self.seriesguards
569 return self.seriesguards
569
570
570 def invalidate(self):
571 def invalidate(self):
571 for a in 'applied fullseries series seriesguards'.split():
572 for a in 'applied fullseries series seriesguards'.split():
572 if a in self.__dict__:
573 if a in self.__dict__:
573 delattr(self, a)
574 delattr(self, a)
574 self.applieddirty = False
575 self.applieddirty = False
575 self.seriesdirty = False
576 self.seriesdirty = False
576 self.guardsdirty = False
577 self.guardsdirty = False
577 self.activeguards = None
578 self.activeguards = None
578
579
579 def diffopts(self, opts=None, patchfn=None, plain=False):
580 def diffopts(self, opts=None, patchfn=None, plain=False):
580 """Return diff options tweaked for this mq use, possibly upgrading to
581 """Return diff options tweaked for this mq use, possibly upgrading to
581 git format, and possibly plain and without lossy options."""
582 git format, and possibly plain and without lossy options."""
582 diffopts = patchmod.difffeatureopts(
583 diffopts = patchmod.difffeatureopts(
583 self.ui,
584 self.ui,
584 opts,
585 opts,
585 git=True,
586 git=True,
586 whitespace=not plain,
587 whitespace=not plain,
587 formatchanging=not plain,
588 formatchanging=not plain,
588 )
589 )
589 if self.gitmode == b'auto':
590 if self.gitmode == b'auto':
590 diffopts.upgrade = True
591 diffopts.upgrade = True
591 elif self.gitmode == b'keep':
592 elif self.gitmode == b'keep':
592 pass
593 pass
593 elif self.gitmode in (b'yes', b'no'):
594 elif self.gitmode in (b'yes', b'no'):
594 diffopts.git = self.gitmode == b'yes'
595 diffopts.git = self.gitmode == b'yes'
595 else:
596 else:
596 raise error.Abort(
597 raise error.Abort(
597 _(b'mq.git option can be auto/keep/yes/no got %s')
598 _(b'mq.git option can be auto/keep/yes/no got %s')
598 % self.gitmode
599 % self.gitmode
599 )
600 )
600 if patchfn:
601 if patchfn:
601 diffopts = self.patchopts(diffopts, patchfn)
602 diffopts = self.patchopts(diffopts, patchfn)
602 return diffopts
603 return diffopts
603
604
604 def patchopts(self, diffopts, *patches):
605 def patchopts(self, diffopts, *patches):
605 """Return a copy of input diff options with git set to true if
606 """Return a copy of input diff options with git set to true if
606 referenced patch is a git patch and should be preserved as such.
607 referenced patch is a git patch and should be preserved as such.
607 """
608 """
608 diffopts = diffopts.copy()
609 diffopts = diffopts.copy()
609 if not diffopts.git and self.gitmode == b'keep':
610 if not diffopts.git and self.gitmode == b'keep':
610 for patchfn in patches:
611 for patchfn in patches:
611 patchf = self.opener(patchfn, b'r')
612 patchf = self.opener(patchfn, b'r')
612 # if the patch was a git patch, refresh it as a git patch
613 # if the patch was a git patch, refresh it as a git patch
613 diffopts.git = any(
614 diffopts.git = any(
614 line.startswith(b'diff --git') for line in patchf
615 line.startswith(b'diff --git') for line in patchf
615 )
616 )
616 patchf.close()
617 patchf.close()
617 return diffopts
618 return diffopts
618
619
619 def join(self, *p):
620 def join(self, *p):
620 return os.path.join(self.path, *p)
621 return os.path.join(self.path, *p)
621
622
622 def findseries(self, patch):
623 def findseries(self, patch):
623 def matchpatch(l):
624 def matchpatch(l):
624 l = l.split(b'#', 1)[0]
625 l = l.split(b'#', 1)[0]
625 return l.strip() == patch
626 return l.strip() == patch
626
627
627 for index, l in enumerate(self.fullseries):
628 for index, l in enumerate(self.fullseries):
628 if matchpatch(l):
629 if matchpatch(l):
629 return index
630 return index
630 return None
631 return None
631
632
632 guard_re = re.compile(br'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
633 guard_re = re.compile(br'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
633
634
634 def parseseries(self):
635 def parseseries(self):
635 self.series = []
636 self.series = []
636 self.seriesguards = []
637 self.seriesguards = []
637 for l in self.fullseries:
638 for l in self.fullseries:
638 h = l.find(b'#')
639 h = l.find(b'#')
639 if h == -1:
640 if h == -1:
640 patch = l
641 patch = l
641 comment = b''
642 comment = b''
642 elif h == 0:
643 elif h == 0:
643 continue
644 continue
644 else:
645 else:
645 patch = l[:h]
646 patch = l[:h]
646 comment = l[h:]
647 comment = l[h:]
647 patch = patch.strip()
648 patch = patch.strip()
648 if patch:
649 if patch:
649 if patch in self.series:
650 if patch in self.series:
650 raise error.Abort(
651 raise error.Abort(
651 _(b'%s appears more than once in %s')
652 _(b'%s appears more than once in %s')
652 % (patch, self.join(self.seriespath))
653 % (patch, self.join(self.seriespath))
653 )
654 )
654 self.series.append(patch)
655 self.series.append(patch)
655 self.seriesguards.append(self.guard_re.findall(comment))
656 self.seriesguards.append(self.guard_re.findall(comment))
656
657
657 def checkguard(self, guard):
658 def checkguard(self, guard):
658 if not guard:
659 if not guard:
659 return _(b'guard cannot be an empty string')
660 return _(b'guard cannot be an empty string')
660 bad_chars = b'# \t\r\n\f'
661 bad_chars = b'# \t\r\n\f'
661 first = guard[0]
662 first = guard[0]
662 if first in b'-+':
663 if first in b'-+':
663 return _(b'guard %r starts with invalid character: %r') % (
664 return _(b'guard %r starts with invalid character: %r') % (
664 guard,
665 guard,
665 first,
666 first,
666 )
667 )
667 for c in bad_chars:
668 for c in bad_chars:
668 if c in guard:
669 if c in guard:
669 return _(b'invalid character in guard %r: %r') % (guard, c)
670 return _(b'invalid character in guard %r: %r') % (guard, c)
670
671
671 def setactive(self, guards):
672 def setactive(self, guards):
672 for guard in guards:
673 for guard in guards:
673 bad = self.checkguard(guard)
674 bad = self.checkguard(guard)
674 if bad:
675 if bad:
675 raise error.Abort(bad)
676 raise error.Abort(bad)
676 guards = sorted(set(guards))
677 guards = sorted(set(guards))
677 self.ui.debug(b'active guards: %s\n' % b' '.join(guards))
678 self.ui.debug(b'active guards: %s\n' % b' '.join(guards))
678 self.activeguards = guards
679 self.activeguards = guards
679 self.guardsdirty = True
680 self.guardsdirty = True
680
681
681 def active(self):
682 def active(self):
682 if self.activeguards is None:
683 if self.activeguards is None:
683 self.activeguards = []
684 self.activeguards = []
684 try:
685 try:
685 guards = self.opener.read(self.guardspath).split()
686 guards = self.opener.read(self.guardspath).split()
686 except IOError as err:
687 except IOError as err:
687 if err.errno != errno.ENOENT:
688 if err.errno != errno.ENOENT:
688 raise
689 raise
689 guards = []
690 guards = []
690 for i, guard in enumerate(guards):
691 for i, guard in enumerate(guards):
691 bad = self.checkguard(guard)
692 bad = self.checkguard(guard)
692 if bad:
693 if bad:
693 self.ui.warn(
694 self.ui.warn(
694 b'%s:%d: %s\n'
695 b'%s:%d: %s\n'
695 % (self.join(self.guardspath), i + 1, bad)
696 % (self.join(self.guardspath), i + 1, bad)
696 )
697 )
697 else:
698 else:
698 self.activeguards.append(guard)
699 self.activeguards.append(guard)
699 return self.activeguards
700 return self.activeguards
700
701
701 def setguards(self, idx, guards):
702 def setguards(self, idx, guards):
702 for g in guards:
703 for g in guards:
703 if len(g) < 2:
704 if len(g) < 2:
704 raise error.Abort(_(b'guard %r too short') % g)
705 raise error.Abort(_(b'guard %r too short') % g)
705 if g[0] not in b'-+':
706 if g[0] not in b'-+':
706 raise error.Abort(_(b'guard %r starts with invalid char') % g)
707 raise error.Abort(_(b'guard %r starts with invalid char') % g)
707 bad = self.checkguard(g[1:])
708 bad = self.checkguard(g[1:])
708 if bad:
709 if bad:
709 raise error.Abort(bad)
710 raise error.Abort(bad)
710 drop = self.guard_re.sub(b'', self.fullseries[idx])
711 drop = self.guard_re.sub(b'', self.fullseries[idx])
711 self.fullseries[idx] = drop + b''.join([b' #' + g for g in guards])
712 self.fullseries[idx] = drop + b''.join([b' #' + g for g in guards])
712 self.parseseries()
713 self.parseseries()
713 self.seriesdirty = True
714 self.seriesdirty = True
714
715
715 def pushable(self, idx):
716 def pushable(self, idx):
716 if isinstance(idx, bytes):
717 if isinstance(idx, bytes):
717 idx = self.series.index(idx)
718 idx = self.series.index(idx)
718 patchguards = self.seriesguards[idx]
719 patchguards = self.seriesguards[idx]
719 if not patchguards:
720 if not patchguards:
720 return True, None
721 return True, None
721 guards = self.active()
722 guards = self.active()
722 exactneg = [
723 exactneg = [
723 g for g in patchguards if g.startswith(b'-') and g[1:] in guards
724 g for g in patchguards if g.startswith(b'-') and g[1:] in guards
724 ]
725 ]
725 if exactneg:
726 if exactneg:
726 return False, stringutil.pprint(exactneg[0])
727 return False, stringutil.pprint(exactneg[0])
727 pos = [g for g in patchguards if g.startswith(b'+')]
728 pos = [g for g in patchguards if g.startswith(b'+')]
728 exactpos = [g for g in pos if g[1:] in guards]
729 exactpos = [g for g in pos if g[1:] in guards]
729 if pos:
730 if pos:
730 if exactpos:
731 if exactpos:
731 return True, stringutil.pprint(exactpos[0])
732 return True, stringutil.pprint(exactpos[0])
732 return False, b' '.join([stringutil.pprint(p) for p in pos])
733 return False, b' '.join([stringutil.pprint(p) for p in pos])
733 return True, b''
734 return True, b''
734
735
735 def explainpushable(self, idx, all_patches=False):
736 def explainpushable(self, idx, all_patches=False):
736 if all_patches:
737 if all_patches:
737 write = self.ui.write
738 write = self.ui.write
738 else:
739 else:
739 write = self.ui.warn
740 write = self.ui.warn
740
741
741 if all_patches or self.ui.verbose:
742 if all_patches or self.ui.verbose:
742 if isinstance(idx, bytes):
743 if isinstance(idx, bytes):
743 idx = self.series.index(idx)
744 idx = self.series.index(idx)
744 pushable, why = self.pushable(idx)
745 pushable, why = self.pushable(idx)
745 if all_patches and pushable:
746 if all_patches and pushable:
746 if why is None:
747 if why is None:
747 write(
748 write(
748 _(b'allowing %s - no guards in effect\n')
749 _(b'allowing %s - no guards in effect\n')
749 % self.series[idx]
750 % self.series[idx]
750 )
751 )
751 else:
752 else:
752 if not why:
753 if not why:
753 write(
754 write(
754 _(b'allowing %s - no matching negative guards\n')
755 _(b'allowing %s - no matching negative guards\n')
755 % self.series[idx]
756 % self.series[idx]
756 )
757 )
757 else:
758 else:
758 write(
759 write(
759 _(b'allowing %s - guarded by %s\n')
760 _(b'allowing %s - guarded by %s\n')
760 % (self.series[idx], why)
761 % (self.series[idx], why)
761 )
762 )
762 if not pushable:
763 if not pushable:
763 if why:
764 if why:
764 write(
765 write(
765 _(b'skipping %s - guarded by %s\n')
766 _(b'skipping %s - guarded by %s\n')
766 % (self.series[idx], why)
767 % (self.series[idx], why)
767 )
768 )
768 else:
769 else:
769 write(
770 write(
770 _(b'skipping %s - no matching guards\n')
771 _(b'skipping %s - no matching guards\n')
771 % self.series[idx]
772 % self.series[idx]
772 )
773 )
773
774
774 def savedirty(self):
775 def savedirty(self):
775 def writelist(items, path):
776 def writelist(items, path):
776 fp = self.opener(path, b'wb')
777 fp = self.opener(path, b'wb')
777 for i in items:
778 for i in items:
778 fp.write(b"%s\n" % i)
779 fp.write(b"%s\n" % i)
779 fp.close()
780 fp.close()
780
781
781 if self.applieddirty:
782 if self.applieddirty:
782 writelist(map(bytes, self.applied), self.statuspath)
783 writelist(map(bytes, self.applied), self.statuspath)
783 self.applieddirty = False
784 self.applieddirty = False
784 if self.seriesdirty:
785 if self.seriesdirty:
785 writelist(self.fullseries, self.seriespath)
786 writelist(self.fullseries, self.seriespath)
786 self.seriesdirty = False
787 self.seriesdirty = False
787 if self.guardsdirty:
788 if self.guardsdirty:
788 writelist(self.activeguards, self.guardspath)
789 writelist(self.activeguards, self.guardspath)
789 self.guardsdirty = False
790 self.guardsdirty = False
790 if self.added:
791 if self.added:
791 qrepo = self.qrepo()
792 qrepo = self.qrepo()
792 if qrepo:
793 if qrepo:
793 qrepo[None].add(f for f in self.added if f not in qrepo[None])
794 qrepo[None].add(f for f in self.added if f not in qrepo[None])
794 self.added = []
795 self.added = []
795
796
796 def removeundo(self, repo):
797 def removeundo(self, repo):
797 undo = repo.sjoin(b'undo')
798 undo = repo.sjoin(b'undo')
798 if not os.path.exists(undo):
799 if not os.path.exists(undo):
799 return
800 return
800 try:
801 try:
801 os.unlink(undo)
802 os.unlink(undo)
802 except OSError as inst:
803 except OSError as inst:
803 self.ui.warn(
804 self.ui.warn(
804 _(b'error removing undo: %s\n') % stringutil.forcebytestr(inst)
805 _(b'error removing undo: %s\n') % stringutil.forcebytestr(inst)
805 )
806 )
806
807
807 def backup(self, repo, files, copy=False):
808 def backup(self, repo, files, copy=False):
808 # backup local changes in --force case
809 # backup local changes in --force case
809 for f in sorted(files):
810 for f in sorted(files):
810 absf = repo.wjoin(f)
811 absf = repo.wjoin(f)
811 if os.path.lexists(absf):
812 if os.path.lexists(absf):
812 absorig = scmutil.backuppath(self.ui, repo, f)
813 absorig = scmutil.backuppath(self.ui, repo, f)
813 self.ui.note(
814 self.ui.note(
814 _(b'saving current version of %s as %s\n')
815 _(b'saving current version of %s as %s\n')
815 % (f, os.path.relpath(absorig))
816 % (f, os.path.relpath(absorig))
816 )
817 )
817
818
818 if copy:
819 if copy:
819 util.copyfile(absf, absorig)
820 util.copyfile(absf, absorig)
820 else:
821 else:
821 util.rename(absf, absorig)
822 util.rename(absf, absorig)
822
823
823 def printdiff(
824 def printdiff(
824 self,
825 self,
825 repo,
826 repo,
826 diffopts,
827 diffopts,
827 node1,
828 node1,
828 node2=None,
829 node2=None,
829 files=None,
830 files=None,
830 fp=None,
831 fp=None,
831 changes=None,
832 changes=None,
832 opts=None,
833 opts=None,
833 ):
834 ):
834 if opts is None:
835 if opts is None:
835 opts = {}
836 opts = {}
836 stat = opts.get(b'stat')
837 stat = opts.get(b'stat')
837 m = scmutil.match(repo[node1], files, opts)
838 m = scmutil.match(repo[node1], files, opts)
838 logcmdutil.diffordiffstat(
839 logcmdutil.diffordiffstat(
839 self.ui,
840 self.ui,
840 repo,
841 repo,
841 diffopts,
842 diffopts,
842 repo[node1],
843 repo[node1],
843 repo[node2],
844 repo[node2],
844 m,
845 m,
845 changes,
846 changes,
846 stat,
847 stat,
847 fp,
848 fp,
848 )
849 )
849
850
850 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
851 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
851 # first try just applying the patch
852 # first try just applying the patch
852 (err, n) = self.apply(
853 (err, n) = self.apply(
853 repo, [patch], update_status=False, strict=True, merge=rev
854 repo, [patch], update_status=False, strict=True, merge=rev
854 )
855 )
855
856
856 if err == 0:
857 if err == 0:
857 return (err, n)
858 return (err, n)
858
859
859 if n is None:
860 if n is None:
860 raise error.Abort(_(b"apply failed for patch %s") % patch)
861 raise error.Abort(_(b"apply failed for patch %s") % patch)
861
862
862 self.ui.warn(_(b"patch didn't work out, merging %s\n") % patch)
863 self.ui.warn(_(b"patch didn't work out, merging %s\n") % patch)
863
864
864 # apply failed, strip away that rev and merge.
865 # apply failed, strip away that rev and merge.
865 hg.clean(repo, head)
866 hg.clean(repo, head)
866 strip(self.ui, repo, [n], update=False, backup=False)
867 strip(self.ui, repo, [n], update=False, backup=False)
867
868
868 ctx = repo[rev]
869 ctx = repo[rev]
869 ret = hg.merge(ctx, remind=False)
870 ret = hg.merge(ctx, remind=False)
870 if ret:
871 if ret:
871 raise error.Abort(_(b"update returned %d") % ret)
872 raise error.Abort(_(b"update returned %d") % ret)
872 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
873 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
873 if n is None:
874 if n is None:
874 raise error.Abort(_(b"repo commit failed"))
875 raise error.Abort(_(b"repo commit failed"))
875 try:
876 try:
876 ph = patchheader(mergeq.join(patch), self.plainmode)
877 ph = patchheader(mergeq.join(patch), self.plainmode)
877 except Exception:
878 except Exception:
878 raise error.Abort(_(b"unable to read %s") % patch)
879 raise error.Abort(_(b"unable to read %s") % patch)
879
880
880 diffopts = self.patchopts(diffopts, patch)
881 diffopts = self.patchopts(diffopts, patch)
881 patchf = self.opener(patch, b"w")
882 patchf = self.opener(patch, b"w")
882 comments = bytes(ph)
883 comments = bytes(ph)
883 if comments:
884 if comments:
884 patchf.write(comments)
885 patchf.write(comments)
885 self.printdiff(repo, diffopts, head, n, fp=patchf)
886 self.printdiff(repo, diffopts, head, n, fp=patchf)
886 patchf.close()
887 patchf.close()
887 self.removeundo(repo)
888 self.removeundo(repo)
888 return (0, n)
889 return (0, n)
889
890
890 def qparents(self, repo, rev=None):
891 def qparents(self, repo, rev=None):
891 """return the mq handled parent or p1
892 """return the mq handled parent or p1
892
893
893 In some case where mq get himself in being the parent of a merge the
894 In some case where mq get himself in being the parent of a merge the
894 appropriate parent may be p2.
895 appropriate parent may be p2.
895 (eg: an in progress merge started with mq disabled)
896 (eg: an in progress merge started with mq disabled)
896
897
897 If no parent are managed by mq, p1 is returned.
898 If no parent are managed by mq, p1 is returned.
898 """
899 """
899 if rev is None:
900 if rev is None:
900 (p1, p2) = repo.dirstate.parents()
901 (p1, p2) = repo.dirstate.parents()
901 if p2 == nullid:
902 if p2 == nullid:
902 return p1
903 return p1
903 if not self.applied:
904 if not self.applied:
904 return None
905 return None
905 return self.applied[-1].node
906 return self.applied[-1].node
906 p1, p2 = repo.changelog.parents(rev)
907 p1, p2 = repo.changelog.parents(rev)
907 if p2 != nullid and p2 in [x.node for x in self.applied]:
908 if p2 != nullid and p2 in [x.node for x in self.applied]:
908 return p2
909 return p2
909 return p1
910 return p1
910
911
911 def mergepatch(self, repo, mergeq, series, diffopts):
912 def mergepatch(self, repo, mergeq, series, diffopts):
912 if not self.applied:
913 if not self.applied:
913 # each of the patches merged in will have two parents. This
914 # each of the patches merged in will have two parents. This
914 # can confuse the qrefresh, qdiff, and strip code because it
915 # can confuse the qrefresh, qdiff, and strip code because it
915 # needs to know which parent is actually in the patch queue.
916 # needs to know which parent is actually in the patch queue.
916 # so, we insert a merge marker with only one parent. This way
917 # so, we insert a merge marker with only one parent. This way
917 # the first patch in the queue is never a merge patch
918 # the first patch in the queue is never a merge patch
918 #
919 #
919 pname = b".hg.patches.merge.marker"
920 pname = b".hg.patches.merge.marker"
920 n = newcommit(repo, None, b'[mq]: merge marker', force=True)
921 n = newcommit(repo, None, b'[mq]: merge marker', force=True)
921 self.removeundo(repo)
922 self.removeundo(repo)
922 self.applied.append(statusentry(n, pname))
923 self.applied.append(statusentry(n, pname))
923 self.applieddirty = True
924 self.applieddirty = True
924
925
925 head = self.qparents(repo)
926 head = self.qparents(repo)
926
927
927 for patch in series:
928 for patch in series:
928 patch = mergeq.lookup(patch, strict=True)
929 patch = mergeq.lookup(patch, strict=True)
929 if not patch:
930 if not patch:
930 self.ui.warn(_(b"patch %s does not exist\n") % patch)
931 self.ui.warn(_(b"patch %s does not exist\n") % patch)
931 return (1, None)
932 return (1, None)
932 pushable, reason = self.pushable(patch)
933 pushable, reason = self.pushable(patch)
933 if not pushable:
934 if not pushable:
934 self.explainpushable(patch, all_patches=True)
935 self.explainpushable(patch, all_patches=True)
935 continue
936 continue
936 info = mergeq.isapplied(patch)
937 info = mergeq.isapplied(patch)
937 if not info:
938 if not info:
938 self.ui.warn(_(b"patch %s is not applied\n") % patch)
939 self.ui.warn(_(b"patch %s is not applied\n") % patch)
939 return (1, None)
940 return (1, None)
940 rev = info[1]
941 rev = info[1]
941 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
942 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
942 if head:
943 if head:
943 self.applied.append(statusentry(head, patch))
944 self.applied.append(statusentry(head, patch))
944 self.applieddirty = True
945 self.applieddirty = True
945 if err:
946 if err:
946 return (err, head)
947 return (err, head)
947 self.savedirty()
948 self.savedirty()
948 return (0, head)
949 return (0, head)
949
950
950 def patch(self, repo, patchfile):
951 def patch(self, repo, patchfile):
951 '''Apply patchfile to the working directory.
952 '''Apply patchfile to the working directory.
952 patchfile: name of patch file'''
953 patchfile: name of patch file'''
953 files = set()
954 files = set()
954 try:
955 try:
955 fuzz = patchmod.patch(
956 fuzz = patchmod.patch(
956 self.ui, repo, patchfile, strip=1, files=files, eolmode=None
957 self.ui, repo, patchfile, strip=1, files=files, eolmode=None
957 )
958 )
958 return (True, list(files), fuzz)
959 return (True, list(files), fuzz)
959 except Exception as inst:
960 except Exception as inst:
960 self.ui.note(stringutil.forcebytestr(inst) + b'\n')
961 self.ui.note(stringutil.forcebytestr(inst) + b'\n')
961 if not self.ui.verbose:
962 if not self.ui.verbose:
962 self.ui.warn(_(b"patch failed, unable to continue (try -v)\n"))
963 self.ui.warn(_(b"patch failed, unable to continue (try -v)\n"))
963 self.ui.traceback()
964 self.ui.traceback()
964 return (False, list(files), False)
965 return (False, list(files), False)
965
966
966 def apply(
967 def apply(
967 self,
968 self,
968 repo,
969 repo,
969 series,
970 series,
970 list=False,
971 list=False,
971 update_status=True,
972 update_status=True,
972 strict=False,
973 strict=False,
973 patchdir=None,
974 patchdir=None,
974 merge=None,
975 merge=None,
975 all_files=None,
976 all_files=None,
976 tobackup=None,
977 tobackup=None,
977 keepchanges=False,
978 keepchanges=False,
978 ):
979 ):
979 wlock = lock = tr = None
980 wlock = lock = tr = None
980 try:
981 try:
981 wlock = repo.wlock()
982 wlock = repo.wlock()
982 lock = repo.lock()
983 lock = repo.lock()
983 tr = repo.transaction(b"qpush")
984 tr = repo.transaction(b"qpush")
984 try:
985 try:
985 ret = self._apply(
986 ret = self._apply(
986 repo,
987 repo,
987 series,
988 series,
988 list,
989 list,
989 update_status,
990 update_status,
990 strict,
991 strict,
991 patchdir,
992 patchdir,
992 merge,
993 merge,
993 all_files=all_files,
994 all_files=all_files,
994 tobackup=tobackup,
995 tobackup=tobackup,
995 keepchanges=keepchanges,
996 keepchanges=keepchanges,
996 )
997 )
997 tr.close()
998 tr.close()
998 self.savedirty()
999 self.savedirty()
999 return ret
1000 return ret
1000 except AbortNoCleanup:
1001 except AbortNoCleanup:
1001 tr.close()
1002 tr.close()
1002 self.savedirty()
1003 self.savedirty()
1003 raise
1004 raise
1004 except: # re-raises
1005 except: # re-raises
1005 try:
1006 try:
1006 tr.abort()
1007 tr.abort()
1007 finally:
1008 finally:
1008 self.invalidate()
1009 self.invalidate()
1009 raise
1010 raise
1010 finally:
1011 finally:
1011 release(tr, lock, wlock)
1012 release(tr, lock, wlock)
1012 self.removeundo(repo)
1013 self.removeundo(repo)
1013
1014
1014 def _apply(
1015 def _apply(
1015 self,
1016 self,
1016 repo,
1017 repo,
1017 series,
1018 series,
1018 list=False,
1019 list=False,
1019 update_status=True,
1020 update_status=True,
1020 strict=False,
1021 strict=False,
1021 patchdir=None,
1022 patchdir=None,
1022 merge=None,
1023 merge=None,
1023 all_files=None,
1024 all_files=None,
1024 tobackup=None,
1025 tobackup=None,
1025 keepchanges=False,
1026 keepchanges=False,
1026 ):
1027 ):
1027 """returns (error, hash)
1028 """returns (error, hash)
1028
1029
1029 error = 1 for unable to read, 2 for patch failed, 3 for patch
1030 error = 1 for unable to read, 2 for patch failed, 3 for patch
1030 fuzz. tobackup is None or a set of files to backup before they
1031 fuzz. tobackup is None or a set of files to backup before they
1031 are modified by a patch.
1032 are modified by a patch.
1032 """
1033 """
1033 # TODO unify with commands.py
1034 # TODO unify with commands.py
1034 if not patchdir:
1035 if not patchdir:
1035 patchdir = self.path
1036 patchdir = self.path
1036 err = 0
1037 err = 0
1037 n = None
1038 n = None
1038 for patchname in series:
1039 for patchname in series:
1039 pushable, reason = self.pushable(patchname)
1040 pushable, reason = self.pushable(patchname)
1040 if not pushable:
1041 if not pushable:
1041 self.explainpushable(patchname, all_patches=True)
1042 self.explainpushable(patchname, all_patches=True)
1042 continue
1043 continue
1043 self.ui.status(_(b"applying %s\n") % patchname)
1044 self.ui.status(_(b"applying %s\n") % patchname)
1044 pf = os.path.join(patchdir, patchname)
1045 pf = os.path.join(patchdir, patchname)
1045
1046
1046 try:
1047 try:
1047 ph = patchheader(self.join(patchname), self.plainmode)
1048 ph = patchheader(self.join(patchname), self.plainmode)
1048 except IOError:
1049 except IOError:
1049 self.ui.warn(_(b"unable to read %s\n") % patchname)
1050 self.ui.warn(_(b"unable to read %s\n") % patchname)
1050 err = 1
1051 err = 1
1051 break
1052 break
1052
1053
1053 message = ph.message
1054 message = ph.message
1054 if not message:
1055 if not message:
1055 # The commit message should not be translated
1056 # The commit message should not be translated
1056 message = b"imported patch %s\n" % patchname
1057 message = b"imported patch %s\n" % patchname
1057 else:
1058 else:
1058 if list:
1059 if list:
1059 # The commit message should not be translated
1060 # The commit message should not be translated
1060 message.append(b"\nimported patch %s" % patchname)
1061 message.append(b"\nimported patch %s" % patchname)
1061 message = b'\n'.join(message)
1062 message = b'\n'.join(message)
1062
1063
1063 if ph.haspatch:
1064 if ph.haspatch:
1064 if tobackup:
1065 if tobackup:
1065 touched = patchmod.changedfiles(self.ui, repo, pf)
1066 touched = patchmod.changedfiles(self.ui, repo, pf)
1066 touched = set(touched) & tobackup
1067 touched = set(touched) & tobackup
1067 if touched and keepchanges:
1068 if touched and keepchanges:
1068 raise AbortNoCleanup(
1069 raise AbortNoCleanup(
1069 _(b"conflicting local changes found"),
1070 _(b"conflicting local changes found"),
1070 hint=_(b"did you forget to qrefresh?"),
1071 hint=_(b"did you forget to qrefresh?"),
1071 )
1072 )
1072 self.backup(repo, touched, copy=True)
1073 self.backup(repo, touched, copy=True)
1073 tobackup = tobackup - touched
1074 tobackup = tobackup - touched
1074 (patcherr, files, fuzz) = self.patch(repo, pf)
1075 (patcherr, files, fuzz) = self.patch(repo, pf)
1075 if all_files is not None:
1076 if all_files is not None:
1076 all_files.update(files)
1077 all_files.update(files)
1077 patcherr = not patcherr
1078 patcherr = not patcherr
1078 else:
1079 else:
1079 self.ui.warn(_(b"patch %s is empty\n") % patchname)
1080 self.ui.warn(_(b"patch %s is empty\n") % patchname)
1080 patcherr, files, fuzz = 0, [], 0
1081 patcherr, files, fuzz = 0, [], 0
1081
1082
1082 if merge and files:
1083 if merge and files:
1083 # Mark as removed/merged and update dirstate parent info
1084 # Mark as removed/merged and update dirstate parent info
1084 removed = []
1085 removed = []
1085 merged = []
1086 merged = []
1086 for f in files:
1087 for f in files:
1087 if os.path.lexists(repo.wjoin(f)):
1088 if os.path.lexists(repo.wjoin(f)):
1088 merged.append(f)
1089 merged.append(f)
1089 else:
1090 else:
1090 removed.append(f)
1091 removed.append(f)
1091 with repo.dirstate.parentchange():
1092 with repo.dirstate.parentchange():
1092 for f in removed:
1093 for f in removed:
1093 repo.dirstate.remove(f)
1094 repo.dirstate.remove(f)
1094 for f in merged:
1095 for f in merged:
1095 repo.dirstate.merge(f)
1096 repo.dirstate.merge(f)
1096 p1 = repo.dirstate.p1()
1097 p1 = repo.dirstate.p1()
1097 repo.setparents(p1, merge)
1098 repo.setparents(p1, merge)
1098
1099
1099 if all_files and b'.hgsubstate' in all_files:
1100 if all_files and b'.hgsubstate' in all_files:
1100 wctx = repo[None]
1101 wctx = repo[None]
1101 pctx = repo[b'.']
1102 pctx = repo[b'.']
1102 overwrite = False
1103 overwrite = False
1103 mergedsubstate = subrepoutil.submerge(
1104 mergedsubstate = subrepoutil.submerge(
1104 repo, pctx, wctx, wctx, overwrite
1105 repo, pctx, wctx, wctx, overwrite
1105 )
1106 )
1106 files += mergedsubstate.keys()
1107 files += mergedsubstate.keys()
1107
1108
1108 match = scmutil.matchfiles(repo, files or [])
1109 match = scmutil.matchfiles(repo, files or [])
1109 oldtip = repo.changelog.tip()
1110 oldtip = repo.changelog.tip()
1110 n = newcommit(
1111 n = newcommit(
1111 repo, None, message, ph.user, ph.date, match=match, force=True
1112 repo, None, message, ph.user, ph.date, match=match, force=True
1112 )
1113 )
1113 if repo.changelog.tip() == oldtip:
1114 if repo.changelog.tip() == oldtip:
1114 raise error.Abort(
1115 raise error.Abort(
1115 _(b"qpush exactly duplicates child changeset")
1116 _(b"qpush exactly duplicates child changeset")
1116 )
1117 )
1117 if n is None:
1118 if n is None:
1118 raise error.Abort(_(b"repository commit failed"))
1119 raise error.Abort(_(b"repository commit failed"))
1119
1120
1120 if update_status:
1121 if update_status:
1121 self.applied.append(statusentry(n, patchname))
1122 self.applied.append(statusentry(n, patchname))
1122
1123
1123 if patcherr:
1124 if patcherr:
1124 self.ui.warn(
1125 self.ui.warn(
1125 _(b"patch failed, rejects left in working directory\n")
1126 _(b"patch failed, rejects left in working directory\n")
1126 )
1127 )
1127 err = 2
1128 err = 2
1128 break
1129 break
1129
1130
1130 if fuzz and strict:
1131 if fuzz and strict:
1131 self.ui.warn(_(b"fuzz found when applying patch, stopping\n"))
1132 self.ui.warn(_(b"fuzz found when applying patch, stopping\n"))
1132 err = 3
1133 err = 3
1133 break
1134 break
1134 return (err, n)
1135 return (err, n)
1135
1136
1136 def _cleanup(self, patches, numrevs, keep=False):
1137 def _cleanup(self, patches, numrevs, keep=False):
1137 if not keep:
1138 if not keep:
1138 r = self.qrepo()
1139 r = self.qrepo()
1139 if r:
1140 if r:
1140 r[None].forget(patches)
1141 r[None].forget(patches)
1141 for p in patches:
1142 for p in patches:
1142 try:
1143 try:
1143 os.unlink(self.join(p))
1144 os.unlink(self.join(p))
1144 except OSError as inst:
1145 except OSError as inst:
1145 if inst.errno != errno.ENOENT:
1146 if inst.errno != errno.ENOENT:
1146 raise
1147 raise
1147
1148
1148 qfinished = []
1149 qfinished = []
1149 if numrevs:
1150 if numrevs:
1150 qfinished = self.applied[:numrevs]
1151 qfinished = self.applied[:numrevs]
1151 del self.applied[:numrevs]
1152 del self.applied[:numrevs]
1152 self.applieddirty = True
1153 self.applieddirty = True
1153
1154
1154 unknown = []
1155 unknown = []
1155
1156
1156 sortedseries = []
1157 sortedseries = []
1157 for p in patches:
1158 for p in patches:
1158 idx = self.findseries(p)
1159 idx = self.findseries(p)
1159 if idx is None:
1160 if idx is None:
1160 sortedseries.append((-1, p))
1161 sortedseries.append((-1, p))
1161 else:
1162 else:
1162 sortedseries.append((idx, p))
1163 sortedseries.append((idx, p))
1163
1164
1164 sortedseries.sort(reverse=True)
1165 sortedseries.sort(reverse=True)
1165 for (i, p) in sortedseries:
1166 for (i, p) in sortedseries:
1166 if i != -1:
1167 if i != -1:
1167 del self.fullseries[i]
1168 del self.fullseries[i]
1168 else:
1169 else:
1169 unknown.append(p)
1170 unknown.append(p)
1170
1171
1171 if unknown:
1172 if unknown:
1172 if numrevs:
1173 if numrevs:
1173 rev = {entry.name: entry.node for entry in qfinished}
1174 rev = {entry.name: entry.node for entry in qfinished}
1174 for p in unknown:
1175 for p in unknown:
1175 msg = _(b'revision %s refers to unknown patches: %s\n')
1176 msg = _(b'revision %s refers to unknown patches: %s\n')
1176 self.ui.warn(msg % (short(rev[p]), p))
1177 self.ui.warn(msg % (short(rev[p]), p))
1177 else:
1178 else:
1178 msg = _(b'unknown patches: %s\n')
1179 msg = _(b'unknown patches: %s\n')
1179 raise error.Abort(b''.join(msg % p for p in unknown))
1180 raise error.Abort(b''.join(msg % p for p in unknown))
1180
1181
1181 self.parseseries()
1182 self.parseseries()
1182 self.seriesdirty = True
1183 self.seriesdirty = True
1183 return [entry.node for entry in qfinished]
1184 return [entry.node for entry in qfinished]
1184
1185
1185 def _revpatches(self, repo, revs):
1186 def _revpatches(self, repo, revs):
1186 firstrev = repo[self.applied[0].node].rev()
1187 firstrev = repo[self.applied[0].node].rev()
1187 patches = []
1188 patches = []
1188 for i, rev in enumerate(revs):
1189 for i, rev in enumerate(revs):
1189
1190
1190 if rev < firstrev:
1191 if rev < firstrev:
1191 raise error.Abort(_(b'revision %d is not managed') % rev)
1192 raise error.Abort(_(b'revision %d is not managed') % rev)
1192
1193
1193 ctx = repo[rev]
1194 ctx = repo[rev]
1194 base = self.applied[i].node
1195 base = self.applied[i].node
1195 if ctx.node() != base:
1196 if ctx.node() != base:
1196 msg = _(b'cannot delete revision %d above applied patches')
1197 msg = _(b'cannot delete revision %d above applied patches')
1197 raise error.Abort(msg % rev)
1198 raise error.Abort(msg % rev)
1198
1199
1199 patch = self.applied[i].name
1200 patch = self.applied[i].name
1200 for fmt in (b'[mq]: %s', b'imported patch %s'):
1201 for fmt in (b'[mq]: %s', b'imported patch %s'):
1201 if ctx.description() == fmt % patch:
1202 if ctx.description() == fmt % patch:
1202 msg = _(b'patch %s finalized without changeset message\n')
1203 msg = _(b'patch %s finalized without changeset message\n')
1203 repo.ui.status(msg % patch)
1204 repo.ui.status(msg % patch)
1204 break
1205 break
1205
1206
1206 patches.append(patch)
1207 patches.append(patch)
1207 return patches
1208 return patches
1208
1209
1209 def finish(self, repo, revs):
1210 def finish(self, repo, revs):
1210 # Manually trigger phase computation to ensure phasedefaults is
1211 # Manually trigger phase computation to ensure phasedefaults is
1211 # executed before we remove the patches.
1212 # executed before we remove the patches.
1212 repo._phasecache
1213 repo._phasecache
1213 patches = self._revpatches(repo, sorted(revs))
1214 patches = self._revpatches(repo, sorted(revs))
1214 qfinished = self._cleanup(patches, len(patches))
1215 qfinished = self._cleanup(patches, len(patches))
1215 if qfinished and repo.ui.configbool(b'mq', b'secret'):
1216 if qfinished and repo.ui.configbool(b'mq', b'secret'):
1216 # only use this logic when the secret option is added
1217 # only use this logic when the secret option is added
1217 oldqbase = repo[qfinished[0]]
1218 oldqbase = repo[qfinished[0]]
1218 tphase = phases.newcommitphase(repo.ui)
1219 tphase = phases.newcommitphase(repo.ui)
1219 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
1220 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
1220 with repo.transaction(b'qfinish') as tr:
1221 with repo.transaction(b'qfinish') as tr:
1221 phases.advanceboundary(repo, tr, tphase, qfinished)
1222 phases.advanceboundary(repo, tr, tphase, qfinished)
1222
1223
1223 def delete(self, repo, patches, opts):
1224 def delete(self, repo, patches, opts):
1224 if not patches and not opts.get(b'rev'):
1225 if not patches and not opts.get(b'rev'):
1225 raise error.Abort(
1226 raise error.Abort(
1226 _(b'qdelete requires at least one revision or patch name')
1227 _(b'qdelete requires at least one revision or patch name')
1227 )
1228 )
1228
1229
1229 realpatches = []
1230 realpatches = []
1230 for patch in patches:
1231 for patch in patches:
1231 patch = self.lookup(patch, strict=True)
1232 patch = self.lookup(patch, strict=True)
1232 info = self.isapplied(patch)
1233 info = self.isapplied(patch)
1233 if info:
1234 if info:
1234 raise error.Abort(_(b"cannot delete applied patch %s") % patch)
1235 raise error.Abort(_(b"cannot delete applied patch %s") % patch)
1235 if patch not in self.series:
1236 if patch not in self.series:
1236 raise error.Abort(_(b"patch %s not in series file") % patch)
1237 raise error.Abort(_(b"patch %s not in series file") % patch)
1237 if patch not in realpatches:
1238 if patch not in realpatches:
1238 realpatches.append(patch)
1239 realpatches.append(patch)
1239
1240
1240 numrevs = 0
1241 numrevs = 0
1241 if opts.get(b'rev'):
1242 if opts.get(b'rev'):
1242 if not self.applied:
1243 if not self.applied:
1243 raise error.Abort(_(b'no patches applied'))
1244 raise error.Abort(_(b'no patches applied'))
1244 revs = scmutil.revrange(repo, opts.get(b'rev'))
1245 revs = scmutil.revrange(repo, opts.get(b'rev'))
1245 revs.sort()
1246 revs.sort()
1246 revpatches = self._revpatches(repo, revs)
1247 revpatches = self._revpatches(repo, revs)
1247 realpatches += revpatches
1248 realpatches += revpatches
1248 numrevs = len(revpatches)
1249 numrevs = len(revpatches)
1249
1250
1250 self._cleanup(realpatches, numrevs, opts.get(b'keep'))
1251 self._cleanup(realpatches, numrevs, opts.get(b'keep'))
1251
1252
1252 def checktoppatch(self, repo):
1253 def checktoppatch(self, repo):
1253 '''check that working directory is at qtip'''
1254 '''check that working directory is at qtip'''
1254 if self.applied:
1255 if self.applied:
1255 top = self.applied[-1].node
1256 top = self.applied[-1].node
1256 patch = self.applied[-1].name
1257 patch = self.applied[-1].name
1257 if repo.dirstate.p1() != top:
1258 if repo.dirstate.p1() != top:
1258 raise error.Abort(_(b"working directory revision is not qtip"))
1259 raise error.Abort(_(b"working directory revision is not qtip"))
1259 return top, patch
1260 return top, patch
1260 return None, None
1261 return None, None
1261
1262
1262 def putsubstate2changes(self, substatestate, changes):
1263 def putsubstate2changes(self, substatestate, changes):
1263 if isinstance(changes, list):
1264 if isinstance(changes, list):
1264 mar = changes[:3]
1265 mar = changes[:3]
1265 else:
1266 else:
1266 mar = (changes.modified, changes.added, changes.removed)
1267 mar = (changes.modified, changes.added, changes.removed)
1267 if any((b'.hgsubstate' in files for files in mar)):
1268 if any((b'.hgsubstate' in files for files in mar)):
1268 return # already listed up
1269 return # already listed up
1269 # not yet listed up
1270 # not yet listed up
1270 if substatestate in b'a?':
1271 if substatestate in b'a?':
1271 mar[1].append(b'.hgsubstate')
1272 mar[1].append(b'.hgsubstate')
1272 elif substatestate in b'r':
1273 elif substatestate in b'r':
1273 mar[2].append(b'.hgsubstate')
1274 mar[2].append(b'.hgsubstate')
1274 else: # modified
1275 else: # modified
1275 mar[0].append(b'.hgsubstate')
1276 mar[0].append(b'.hgsubstate')
1276
1277
1277 def checklocalchanges(self, repo, force=False, refresh=True):
1278 def checklocalchanges(self, repo, force=False, refresh=True):
1278 excsuffix = b''
1279 excsuffix = b''
1279 if refresh:
1280 if refresh:
1280 excsuffix = b', qrefresh first'
1281 excsuffix = b', qrefresh first'
1281 # plain versions for i18n tool to detect them
1282 # plain versions for i18n tool to detect them
1282 _(b"local changes found, qrefresh first")
1283 _(b"local changes found, qrefresh first")
1283 _(b"local changed subrepos found, qrefresh first")
1284 _(b"local changed subrepos found, qrefresh first")
1284
1285
1285 s = repo.status()
1286 s = repo.status()
1286 if not force:
1287 if not force:
1287 cmdutil.checkunfinished(repo)
1288 cmdutil.checkunfinished(repo)
1288 if s.modified or s.added or s.removed or s.deleted:
1289 if s.modified or s.added or s.removed or s.deleted:
1289 _(b"local changes found") # i18n tool detection
1290 _(b"local changes found") # i18n tool detection
1290 raise error.Abort(_(b"local changes found" + excsuffix))
1291 raise error.Abort(_(b"local changes found" + excsuffix))
1291 if checksubstate(repo):
1292 if checksubstate(repo):
1292 _(b"local changed subrepos found") # i18n tool detection
1293 _(b"local changed subrepos found") # i18n tool detection
1293 raise error.Abort(
1294 raise error.Abort(
1294 _(b"local changed subrepos found" + excsuffix)
1295 _(b"local changed subrepos found" + excsuffix)
1295 )
1296 )
1296 else:
1297 else:
1297 cmdutil.checkunfinished(repo, skipmerge=True)
1298 cmdutil.checkunfinished(repo, skipmerge=True)
1298 return s
1299 return s
1299
1300
1300 _reserved = (b'series', b'status', b'guards', b'.', b'..')
1301 _reserved = (b'series', b'status', b'guards', b'.', b'..')
1301
1302
1302 def checkreservedname(self, name):
1303 def checkreservedname(self, name):
1303 if name in self._reserved:
1304 if name in self._reserved:
1304 raise error.Abort(
1305 raise error.Abort(
1305 _(b'"%s" cannot be used as the name of a patch') % name
1306 _(b'"%s" cannot be used as the name of a patch') % name
1306 )
1307 )
1307 if name != name.strip():
1308 if name != name.strip():
1308 # whitespace is stripped by parseseries()
1309 # whitespace is stripped by parseseries()
1309 raise error.Abort(
1310 raise error.Abort(
1310 _(b'patch name cannot begin or end with whitespace')
1311 _(b'patch name cannot begin or end with whitespace')
1311 )
1312 )
1312 for prefix in (b'.hg', b'.mq'):
1313 for prefix in (b'.hg', b'.mq'):
1313 if name.startswith(prefix):
1314 if name.startswith(prefix):
1314 raise error.Abort(
1315 raise error.Abort(
1315 _(b'patch name cannot begin with "%s"') % prefix
1316 _(b'patch name cannot begin with "%s"') % prefix
1316 )
1317 )
1317 for c in (b'#', b':', b'\r', b'\n'):
1318 for c in (b'#', b':', b'\r', b'\n'):
1318 if c in name:
1319 if c in name:
1319 raise error.Abort(
1320 raise error.Abort(
1320 _(b'%r cannot be used in the name of a patch')
1321 _(b'%r cannot be used in the name of a patch')
1321 % pycompat.bytestr(c)
1322 % pycompat.bytestr(c)
1322 )
1323 )
1323
1324
1324 def checkpatchname(self, name, force=False):
1325 def checkpatchname(self, name, force=False):
1325 self.checkreservedname(name)
1326 self.checkreservedname(name)
1326 if not force and os.path.exists(self.join(name)):
1327 if not force and os.path.exists(self.join(name)):
1327 if os.path.isdir(self.join(name)):
1328 if os.path.isdir(self.join(name)):
1328 raise error.Abort(
1329 raise error.Abort(
1329 _(b'"%s" already exists as a directory') % name
1330 _(b'"%s" already exists as a directory') % name
1330 )
1331 )
1331 else:
1332 else:
1332 raise error.Abort(_(b'patch "%s" already exists') % name)
1333 raise error.Abort(_(b'patch "%s" already exists') % name)
1333
1334
1334 def makepatchname(self, title, fallbackname):
1335 def makepatchname(self, title, fallbackname):
1335 """Return a suitable filename for title, adding a suffix to make
1336 """Return a suitable filename for title, adding a suffix to make
1336 it unique in the existing list"""
1337 it unique in the existing list"""
1337 namebase = re.sub(br'[\s\W_]+', b'_', title.lower()).strip(b'_')
1338 namebase = re.sub(br'[\s\W_]+', b'_', title.lower()).strip(b'_')
1338 namebase = namebase[:75] # avoid too long name (issue5117)
1339 namebase = namebase[:75] # avoid too long name (issue5117)
1339 if namebase:
1340 if namebase:
1340 try:
1341 try:
1341 self.checkreservedname(namebase)
1342 self.checkreservedname(namebase)
1342 except error.Abort:
1343 except error.Abort:
1343 namebase = fallbackname
1344 namebase = fallbackname
1344 else:
1345 else:
1345 namebase = fallbackname
1346 namebase = fallbackname
1346 name = namebase
1347 name = namebase
1347 i = 0
1348 i = 0
1348 while True:
1349 while True:
1349 if name not in self.fullseries:
1350 if name not in self.fullseries:
1350 try:
1351 try:
1351 self.checkpatchname(name)
1352 self.checkpatchname(name)
1352 break
1353 break
1353 except error.Abort:
1354 except error.Abort:
1354 pass
1355 pass
1355 i += 1
1356 i += 1
1356 name = b'%s__%d' % (namebase, i)
1357 name = b'%s__%d' % (namebase, i)
1357 return name
1358 return name
1358
1359
1359 def checkkeepchanges(self, keepchanges, force):
1360 def checkkeepchanges(self, keepchanges, force):
1360 if force and keepchanges:
1361 if force and keepchanges:
1361 raise error.Abort(_(b'cannot use both --force and --keep-changes'))
1362 raise error.Abort(_(b'cannot use both --force and --keep-changes'))
1362
1363
1363 def new(self, repo, patchfn, *pats, **opts):
1364 def new(self, repo, patchfn, *pats, **opts):
1364 """options:
1365 """options:
1365 msg: a string or a no-argument function returning a string
1366 msg: a string or a no-argument function returning a string
1366 """
1367 """
1367 opts = pycompat.byteskwargs(opts)
1368 opts = pycompat.byteskwargs(opts)
1368 msg = opts.get(b'msg')
1369 msg = opts.get(b'msg')
1369 edit = opts.get(b'edit')
1370 edit = opts.get(b'edit')
1370 editform = opts.get(b'editform', b'mq.qnew')
1371 editform = opts.get(b'editform', b'mq.qnew')
1371 user = opts.get(b'user')
1372 user = opts.get(b'user')
1372 date = opts.get(b'date')
1373 date = opts.get(b'date')
1373 if date:
1374 if date:
1374 date = dateutil.parsedate(date)
1375 date = dateutil.parsedate(date)
1375 diffopts = self.diffopts({b'git': opts.get(b'git')}, plain=True)
1376 diffopts = self.diffopts({b'git': opts.get(b'git')}, plain=True)
1376 if opts.get(b'checkname', True):
1377 if opts.get(b'checkname', True):
1377 self.checkpatchname(patchfn)
1378 self.checkpatchname(patchfn)
1378 inclsubs = checksubstate(repo)
1379 inclsubs = checksubstate(repo)
1379 if inclsubs:
1380 if inclsubs:
1380 substatestate = repo.dirstate[b'.hgsubstate']
1381 substatestate = repo.dirstate[b'.hgsubstate']
1381 if opts.get(b'include') or opts.get(b'exclude') or pats:
1382 if opts.get(b'include') or opts.get(b'exclude') or pats:
1382 # detect missing files in pats
1383 # detect missing files in pats
1383 def badfn(f, msg):
1384 def badfn(f, msg):
1384 if f != b'.hgsubstate': # .hgsubstate is auto-created
1385 if f != b'.hgsubstate': # .hgsubstate is auto-created
1385 raise error.Abort(b'%s: %s' % (f, msg))
1386 raise error.Abort(b'%s: %s' % (f, msg))
1386
1387
1387 match = scmutil.match(repo[None], pats, opts, badfn=badfn)
1388 match = scmutil.match(repo[None], pats, opts, badfn=badfn)
1388 changes = repo.status(match=match)
1389 changes = repo.status(match=match)
1389 else:
1390 else:
1390 changes = self.checklocalchanges(repo, force=True)
1391 changes = self.checklocalchanges(repo, force=True)
1391 commitfiles = list(inclsubs)
1392 commitfiles = list(inclsubs)
1392 commitfiles.extend(changes.modified)
1393 commitfiles.extend(changes.modified)
1393 commitfiles.extend(changes.added)
1394 commitfiles.extend(changes.added)
1394 commitfiles.extend(changes.removed)
1395 commitfiles.extend(changes.removed)
1395 match = scmutil.matchfiles(repo, commitfiles)
1396 match = scmutil.matchfiles(repo, commitfiles)
1396 if len(repo[None].parents()) > 1:
1397 if len(repo[None].parents()) > 1:
1397 raise error.Abort(_(b'cannot manage merge changesets'))
1398 raise error.Abort(_(b'cannot manage merge changesets'))
1398 self.checktoppatch(repo)
1399 self.checktoppatch(repo)
1399 insert = self.fullseriesend()
1400 insert = self.fullseriesend()
1400 with repo.wlock():
1401 with repo.wlock():
1401 try:
1402 try:
1402 # if patch file write fails, abort early
1403 # if patch file write fails, abort early
1403 p = self.opener(patchfn, b"w")
1404 p = self.opener(patchfn, b"w")
1404 except IOError as e:
1405 except IOError as e:
1405 raise error.Abort(
1406 raise error.Abort(
1406 _(b'cannot write patch "%s": %s')
1407 _(b'cannot write patch "%s": %s')
1407 % (patchfn, encoding.strtolocal(e.strerror))
1408 % (patchfn, encoding.strtolocal(e.strerror))
1408 )
1409 )
1409 try:
1410 try:
1410 defaultmsg = b"[mq]: %s" % patchfn
1411 defaultmsg = b"[mq]: %s" % patchfn
1411 editor = cmdutil.getcommiteditor(editform=editform)
1412 editor = cmdutil.getcommiteditor(editform=editform)
1412 if edit:
1413 if edit:
1413
1414
1414 def finishdesc(desc):
1415 def finishdesc(desc):
1415 if desc.rstrip():
1416 if desc.rstrip():
1416 return desc
1417 return desc
1417 else:
1418 else:
1418 return defaultmsg
1419 return defaultmsg
1419
1420
1420 # i18n: this message is shown in editor with "HG: " prefix
1421 # i18n: this message is shown in editor with "HG: " prefix
1421 extramsg = _(b'Leave message empty to use default message.')
1422 extramsg = _(b'Leave message empty to use default message.')
1422 editor = cmdutil.getcommiteditor(
1423 editor = cmdutil.getcommiteditor(
1423 finishdesc=finishdesc,
1424 finishdesc=finishdesc,
1424 extramsg=extramsg,
1425 extramsg=extramsg,
1425 editform=editform,
1426 editform=editform,
1426 )
1427 )
1427 commitmsg = msg
1428 commitmsg = msg
1428 else:
1429 else:
1429 commitmsg = msg or defaultmsg
1430 commitmsg = msg or defaultmsg
1430
1431
1431 n = newcommit(
1432 n = newcommit(
1432 repo,
1433 repo,
1433 None,
1434 None,
1434 commitmsg,
1435 commitmsg,
1435 user,
1436 user,
1436 date,
1437 date,
1437 match=match,
1438 match=match,
1438 force=True,
1439 force=True,
1439 editor=editor,
1440 editor=editor,
1440 )
1441 )
1441 if n is None:
1442 if n is None:
1442 raise error.Abort(_(b"repo commit failed"))
1443 raise error.Abort(_(b"repo commit failed"))
1443 try:
1444 try:
1444 self.fullseries[insert:insert] = [patchfn]
1445 self.fullseries[insert:insert] = [patchfn]
1445 self.applied.append(statusentry(n, patchfn))
1446 self.applied.append(statusentry(n, patchfn))
1446 self.parseseries()
1447 self.parseseries()
1447 self.seriesdirty = True
1448 self.seriesdirty = True
1448 self.applieddirty = True
1449 self.applieddirty = True
1449 nctx = repo[n]
1450 nctx = repo[n]
1450 ph = patchheader(self.join(patchfn), self.plainmode)
1451 ph = patchheader(self.join(patchfn), self.plainmode)
1451 if user:
1452 if user:
1452 ph.setuser(user)
1453 ph.setuser(user)
1453 if date:
1454 if date:
1454 ph.setdate(b'%d %d' % date)
1455 ph.setdate(b'%d %d' % date)
1455 ph.setparent(hex(nctx.p1().node()))
1456 ph.setparent(hex(nctx.p1().node()))
1456 msg = nctx.description().strip()
1457 msg = nctx.description().strip()
1457 if msg == defaultmsg.strip():
1458 if msg == defaultmsg.strip():
1458 msg = b''
1459 msg = b''
1459 ph.setmessage(msg)
1460 ph.setmessage(msg)
1460 p.write(bytes(ph))
1461 p.write(bytes(ph))
1461 if commitfiles:
1462 if commitfiles:
1462 parent = self.qparents(repo, n)
1463 parent = self.qparents(repo, n)
1463 if inclsubs:
1464 if inclsubs:
1464 self.putsubstate2changes(substatestate, changes)
1465 self.putsubstate2changes(substatestate, changes)
1465 chunks = patchmod.diff(
1466 chunks = patchmod.diff(
1466 repo,
1467 repo,
1467 node1=parent,
1468 node1=parent,
1468 node2=n,
1469 node2=n,
1469 changes=changes,
1470 changes=changes,
1470 opts=diffopts,
1471 opts=diffopts,
1471 )
1472 )
1472 for chunk in chunks:
1473 for chunk in chunks:
1473 p.write(chunk)
1474 p.write(chunk)
1474 p.close()
1475 p.close()
1475 r = self.qrepo()
1476 r = self.qrepo()
1476 if r:
1477 if r:
1477 r[None].add([patchfn])
1478 r[None].add([patchfn])
1478 except: # re-raises
1479 except: # re-raises
1479 repo.rollback()
1480 repo.rollback()
1480 raise
1481 raise
1481 except Exception:
1482 except Exception:
1482 patchpath = self.join(patchfn)
1483 patchpath = self.join(patchfn)
1483 try:
1484 try:
1484 os.unlink(patchpath)
1485 os.unlink(patchpath)
1485 except OSError:
1486 except OSError:
1486 self.ui.warn(_(b'error unlinking %s\n') % patchpath)
1487 self.ui.warn(_(b'error unlinking %s\n') % patchpath)
1487 raise
1488 raise
1488 self.removeundo(repo)
1489 self.removeundo(repo)
1489
1490
1490 def isapplied(self, patch):
1491 def isapplied(self, patch):
1491 """returns (index, rev, patch)"""
1492 """returns (index, rev, patch)"""
1492 for i, a in enumerate(self.applied):
1493 for i, a in enumerate(self.applied):
1493 if a.name == patch:
1494 if a.name == patch:
1494 return (i, a.node, a.name)
1495 return (i, a.node, a.name)
1495 return None
1496 return None
1496
1497
1497 # if the exact patch name does not exist, we try a few
1498 # if the exact patch name does not exist, we try a few
1498 # variations. If strict is passed, we try only #1
1499 # variations. If strict is passed, we try only #1
1499 #
1500 #
1500 # 1) a number (as string) to indicate an offset in the series file
1501 # 1) a number (as string) to indicate an offset in the series file
1501 # 2) a unique substring of the patch name was given
1502 # 2) a unique substring of the patch name was given
1502 # 3) patchname[-+]num to indicate an offset in the series file
1503 # 3) patchname[-+]num to indicate an offset in the series file
1503 def lookup(self, patch, strict=False):
1504 def lookup(self, patch, strict=False):
1504 def partialname(s):
1505 def partialname(s):
1505 if s in self.series:
1506 if s in self.series:
1506 return s
1507 return s
1507 matches = [x for x in self.series if s in x]
1508 matches = [x for x in self.series if s in x]
1508 if len(matches) > 1:
1509 if len(matches) > 1:
1509 self.ui.warn(_(b'patch name "%s" is ambiguous:\n') % s)
1510 self.ui.warn(_(b'patch name "%s" is ambiguous:\n') % s)
1510 for m in matches:
1511 for m in matches:
1511 self.ui.warn(b' %s\n' % m)
1512 self.ui.warn(b' %s\n' % m)
1512 return None
1513 return None
1513 if matches:
1514 if matches:
1514 return matches[0]
1515 return matches[0]
1515 if self.series and self.applied:
1516 if self.series and self.applied:
1516 if s == b'qtip':
1517 if s == b'qtip':
1517 return self.series[self.seriesend(True) - 1]
1518 return self.series[self.seriesend(True) - 1]
1518 if s == b'qbase':
1519 if s == b'qbase':
1519 return self.series[0]
1520 return self.series[0]
1520 return None
1521 return None
1521
1522
1522 if patch in self.series:
1523 if patch in self.series:
1523 return patch
1524 return patch
1524
1525
1525 if not os.path.isfile(self.join(patch)):
1526 if not os.path.isfile(self.join(patch)):
1526 try:
1527 try:
1527 sno = int(patch)
1528 sno = int(patch)
1528 except (ValueError, OverflowError):
1529 except (ValueError, OverflowError):
1529 pass
1530 pass
1530 else:
1531 else:
1531 if -len(self.series) <= sno < len(self.series):
1532 if -len(self.series) <= sno < len(self.series):
1532 return self.series[sno]
1533 return self.series[sno]
1533
1534
1534 if not strict:
1535 if not strict:
1535 res = partialname(patch)
1536 res = partialname(patch)
1536 if res:
1537 if res:
1537 return res
1538 return res
1538 minus = patch.rfind(b'-')
1539 minus = patch.rfind(b'-')
1539 if minus >= 0:
1540 if minus >= 0:
1540 res = partialname(patch[:minus])
1541 res = partialname(patch[:minus])
1541 if res:
1542 if res:
1542 i = self.series.index(res)
1543 i = self.series.index(res)
1543 try:
1544 try:
1544 off = int(patch[minus + 1 :] or 1)
1545 off = int(patch[minus + 1 :] or 1)
1545 except (ValueError, OverflowError):
1546 except (ValueError, OverflowError):
1546 pass
1547 pass
1547 else:
1548 else:
1548 if i - off >= 0:
1549 if i - off >= 0:
1549 return self.series[i - off]
1550 return self.series[i - off]
1550 plus = patch.rfind(b'+')
1551 plus = patch.rfind(b'+')
1551 if plus >= 0:
1552 if plus >= 0:
1552 res = partialname(patch[:plus])
1553 res = partialname(patch[:plus])
1553 if res:
1554 if res:
1554 i = self.series.index(res)
1555 i = self.series.index(res)
1555 try:
1556 try:
1556 off = int(patch[plus + 1 :] or 1)
1557 off = int(patch[plus + 1 :] or 1)
1557 except (ValueError, OverflowError):
1558 except (ValueError, OverflowError):
1558 pass
1559 pass
1559 else:
1560 else:
1560 if i + off < len(self.series):
1561 if i + off < len(self.series):
1561 return self.series[i + off]
1562 return self.series[i + off]
1562 raise error.Abort(_(b"patch %s not in series") % patch)
1563 raise error.Abort(_(b"patch %s not in series") % patch)
1563
1564
1564 def push(
1565 def push(
1565 self,
1566 self,
1566 repo,
1567 repo,
1567 patch=None,
1568 patch=None,
1568 force=False,
1569 force=False,
1569 list=False,
1570 list=False,
1570 mergeq=None,
1571 mergeq=None,
1571 all=False,
1572 all=False,
1572 move=False,
1573 move=False,
1573 exact=False,
1574 exact=False,
1574 nobackup=False,
1575 nobackup=False,
1575 keepchanges=False,
1576 keepchanges=False,
1576 ):
1577 ):
1577 self.checkkeepchanges(keepchanges, force)
1578 self.checkkeepchanges(keepchanges, force)
1578 diffopts = self.diffopts()
1579 diffopts = self.diffopts()
1579 with repo.wlock():
1580 with repo.wlock():
1580 heads = []
1581 heads = []
1581 for hs in repo.branchmap().iterheads():
1582 for hs in repo.branchmap().iterheads():
1582 heads.extend(hs)
1583 heads.extend(hs)
1583 if not heads:
1584 if not heads:
1584 heads = [nullid]
1585 heads = [nullid]
1585 if repo.dirstate.p1() not in heads and not exact:
1586 if repo.dirstate.p1() not in heads and not exact:
1586 self.ui.status(_(b"(working directory not at a head)\n"))
1587 self.ui.status(_(b"(working directory not at a head)\n"))
1587
1588
1588 if not self.series:
1589 if not self.series:
1589 self.ui.warn(_(b'no patches in series\n'))
1590 self.ui.warn(_(b'no patches in series\n'))
1590 return 0
1591 return 0
1591
1592
1592 # Suppose our series file is: A B C and the current 'top'
1593 # Suppose our series file is: A B C and the current 'top'
1593 # patch is B. qpush C should be performed (moving forward)
1594 # patch is B. qpush C should be performed (moving forward)
1594 # qpush B is a NOP (no change) qpush A is an error (can't
1595 # qpush B is a NOP (no change) qpush A is an error (can't
1595 # go backwards with qpush)
1596 # go backwards with qpush)
1596 if patch:
1597 if patch:
1597 patch = self.lookup(patch)
1598 patch = self.lookup(patch)
1598 info = self.isapplied(patch)
1599 info = self.isapplied(patch)
1599 if info and info[0] >= len(self.applied) - 1:
1600 if info and info[0] >= len(self.applied) - 1:
1600 self.ui.warn(
1601 self.ui.warn(
1601 _(b'qpush: %s is already at the top\n') % patch
1602 _(b'qpush: %s is already at the top\n') % patch
1602 )
1603 )
1603 return 0
1604 return 0
1604
1605
1605 pushable, reason = self.pushable(patch)
1606 pushable, reason = self.pushable(patch)
1606 if pushable:
1607 if pushable:
1607 if self.series.index(patch) < self.seriesend():
1608 if self.series.index(patch) < self.seriesend():
1608 raise error.Abort(
1609 raise error.Abort(
1609 _(b"cannot push to a previous patch: %s") % patch
1610 _(b"cannot push to a previous patch: %s") % patch
1610 )
1611 )
1611 else:
1612 else:
1612 if reason:
1613 if reason:
1613 reason = _(b'guarded by %s') % reason
1614 reason = _(b'guarded by %s') % reason
1614 else:
1615 else:
1615 reason = _(b'no matching guards')
1616 reason = _(b'no matching guards')
1616 self.ui.warn(
1617 self.ui.warn(
1617 _(b"cannot push '%s' - %s\n") % (patch, reason)
1618 _(b"cannot push '%s' - %s\n") % (patch, reason)
1618 )
1619 )
1619 return 1
1620 return 1
1620 elif all:
1621 elif all:
1621 patch = self.series[-1]
1622 patch = self.series[-1]
1622 if self.isapplied(patch):
1623 if self.isapplied(patch):
1623 self.ui.warn(_(b'all patches are currently applied\n'))
1624 self.ui.warn(_(b'all patches are currently applied\n'))
1624 return 0
1625 return 0
1625
1626
1626 # Following the above example, starting at 'top' of B:
1627 # Following the above example, starting at 'top' of B:
1627 # qpush should be performed (pushes C), but a subsequent
1628 # qpush should be performed (pushes C), but a subsequent
1628 # qpush without an argument is an error (nothing to
1629 # qpush without an argument is an error (nothing to
1629 # apply). This allows a loop of "...while hg qpush..." to
1630 # apply). This allows a loop of "...while hg qpush..." to
1630 # work as it detects an error when done
1631 # work as it detects an error when done
1631 start = self.seriesend()
1632 start = self.seriesend()
1632 if start == len(self.series):
1633 if start == len(self.series):
1633 self.ui.warn(_(b'patch series already fully applied\n'))
1634 self.ui.warn(_(b'patch series already fully applied\n'))
1634 return 1
1635 return 1
1635 if not force and not keepchanges:
1636 if not force and not keepchanges:
1636 self.checklocalchanges(repo, refresh=self.applied)
1637 self.checklocalchanges(repo, refresh=self.applied)
1637
1638
1638 if exact:
1639 if exact:
1639 if keepchanges:
1640 if keepchanges:
1640 raise error.Abort(
1641 raise error.Abort(
1641 _(b"cannot use --exact and --keep-changes together")
1642 _(b"cannot use --exact and --keep-changes together")
1642 )
1643 )
1643 if move:
1644 if move:
1644 raise error.Abort(
1645 raise error.Abort(
1645 _(b'cannot use --exact and --move together')
1646 _(b'cannot use --exact and --move together')
1646 )
1647 )
1647 if self.applied:
1648 if self.applied:
1648 raise error.Abort(
1649 raise error.Abort(
1649 _(b'cannot push --exact with applied patches')
1650 _(b'cannot push --exact with applied patches')
1650 )
1651 )
1651 root = self.series[start]
1652 root = self.series[start]
1652 target = patchheader(self.join(root), self.plainmode).parent
1653 target = patchheader(self.join(root), self.plainmode).parent
1653 if not target:
1654 if not target:
1654 raise error.Abort(
1655 raise error.Abort(
1655 _(b"%s does not have a parent recorded") % root
1656 _(b"%s does not have a parent recorded") % root
1656 )
1657 )
1657 if not repo[target] == repo[b'.']:
1658 if not repo[target] == repo[b'.']:
1658 hg.update(repo, target)
1659 hg.update(repo, target)
1659
1660
1660 if move:
1661 if move:
1661 if not patch:
1662 if not patch:
1662 raise error.Abort(_(b"please specify the patch to move"))
1663 raise error.Abort(_(b"please specify the patch to move"))
1663 for fullstart, rpn in enumerate(self.fullseries):
1664 for fullstart, rpn in enumerate(self.fullseries):
1664 # strip markers for patch guards
1665 # strip markers for patch guards
1665 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1666 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1666 break
1667 break
1667 for i, rpn in enumerate(self.fullseries[fullstart:]):
1668 for i, rpn in enumerate(self.fullseries[fullstart:]):
1668 # strip markers for patch guards
1669 # strip markers for patch guards
1669 if self.guard_re.split(rpn, 1)[0] == patch:
1670 if self.guard_re.split(rpn, 1)[0] == patch:
1670 break
1671 break
1671 index = fullstart + i
1672 index = fullstart + i
1672 assert index < len(self.fullseries)
1673 assert index < len(self.fullseries)
1673 fullpatch = self.fullseries[index]
1674 fullpatch = self.fullseries[index]
1674 del self.fullseries[index]
1675 del self.fullseries[index]
1675 self.fullseries.insert(fullstart, fullpatch)
1676 self.fullseries.insert(fullstart, fullpatch)
1676 self.parseseries()
1677 self.parseseries()
1677 self.seriesdirty = True
1678 self.seriesdirty = True
1678
1679
1679 self.applieddirty = True
1680 self.applieddirty = True
1680 if start > 0:
1681 if start > 0:
1681 self.checktoppatch(repo)
1682 self.checktoppatch(repo)
1682 if not patch:
1683 if not patch:
1683 patch = self.series[start]
1684 patch = self.series[start]
1684 end = start + 1
1685 end = start + 1
1685 else:
1686 else:
1686 end = self.series.index(patch, start) + 1
1687 end = self.series.index(patch, start) + 1
1687
1688
1688 tobackup = set()
1689 tobackup = set()
1689 if (not nobackup and force) or keepchanges:
1690 if (not nobackup and force) or keepchanges:
1690 status = self.checklocalchanges(repo, force=True)
1691 status = self.checklocalchanges(repo, force=True)
1691 if keepchanges:
1692 if keepchanges:
1692 tobackup.update(
1693 tobackup.update(
1693 status.modified
1694 status.modified
1694 + status.added
1695 + status.added
1695 + status.removed
1696 + status.removed
1696 + status.deleted
1697 + status.deleted
1697 )
1698 )
1698 else:
1699 else:
1699 tobackup.update(status.modified + status.added)
1700 tobackup.update(status.modified + status.added)
1700
1701
1701 s = self.series[start:end]
1702 s = self.series[start:end]
1702 all_files = set()
1703 all_files = set()
1703 try:
1704 try:
1704 if mergeq:
1705 if mergeq:
1705 ret = self.mergepatch(repo, mergeq, s, diffopts)
1706 ret = self.mergepatch(repo, mergeq, s, diffopts)
1706 else:
1707 else:
1707 ret = self.apply(
1708 ret = self.apply(
1708 repo,
1709 repo,
1709 s,
1710 s,
1710 list,
1711 list,
1711 all_files=all_files,
1712 all_files=all_files,
1712 tobackup=tobackup,
1713 tobackup=tobackup,
1713 keepchanges=keepchanges,
1714 keepchanges=keepchanges,
1714 )
1715 )
1715 except AbortNoCleanup:
1716 except AbortNoCleanup:
1716 raise
1717 raise
1717 except: # re-raises
1718 except: # re-raises
1718 self.ui.warn(_(b'cleaning up working directory...\n'))
1719 self.ui.warn(_(b'cleaning up working directory...\n'))
1719 cmdutil.revert(
1720 cmdutil.revert(
1720 self.ui, repo, repo[b'.'], no_backup=True,
1721 self.ui, repo, repo[b'.'], no_backup=True,
1721 )
1722 )
1722 # only remove unknown files that we know we touched or
1723 # only remove unknown files that we know we touched or
1723 # created while patching
1724 # created while patching
1724 for f in all_files:
1725 for f in all_files:
1725 if f not in repo.dirstate:
1726 if f not in repo.dirstate:
1726 repo.wvfs.unlinkpath(f, ignoremissing=True)
1727 repo.wvfs.unlinkpath(f, ignoremissing=True)
1727 self.ui.warn(_(b'done\n'))
1728 self.ui.warn(_(b'done\n'))
1728 raise
1729 raise
1729
1730
1730 if not self.applied:
1731 if not self.applied:
1731 return ret[0]
1732 return ret[0]
1732 top = self.applied[-1].name
1733 top = self.applied[-1].name
1733 if ret[0] and ret[0] > 1:
1734 if ret[0] and ret[0] > 1:
1734 msg = _(b"errors during apply, please fix and qrefresh %s\n")
1735 msg = _(b"errors during apply, please fix and qrefresh %s\n")
1735 self.ui.write(msg % top)
1736 self.ui.write(msg % top)
1736 else:
1737 else:
1737 self.ui.write(_(b"now at: %s\n") % top)
1738 self.ui.write(_(b"now at: %s\n") % top)
1738 return ret[0]
1739 return ret[0]
1739
1740
1740 def pop(
1741 def pop(
1741 self,
1742 self,
1742 repo,
1743 repo,
1743 patch=None,
1744 patch=None,
1744 force=False,
1745 force=False,
1745 update=True,
1746 update=True,
1746 all=False,
1747 all=False,
1747 nobackup=False,
1748 nobackup=False,
1748 keepchanges=False,
1749 keepchanges=False,
1749 ):
1750 ):
1750 self.checkkeepchanges(keepchanges, force)
1751 self.checkkeepchanges(keepchanges, force)
1751 with repo.wlock():
1752 with repo.wlock():
1752 if patch:
1753 if patch:
1753 # index, rev, patch
1754 # index, rev, patch
1754 info = self.isapplied(patch)
1755 info = self.isapplied(patch)
1755 if not info:
1756 if not info:
1756 patch = self.lookup(patch)
1757 patch = self.lookup(patch)
1757 info = self.isapplied(patch)
1758 info = self.isapplied(patch)
1758 if not info:
1759 if not info:
1759 raise error.Abort(_(b"patch %s is not applied") % patch)
1760 raise error.Abort(_(b"patch %s is not applied") % patch)
1760
1761
1761 if not self.applied:
1762 if not self.applied:
1762 # Allow qpop -a to work repeatedly,
1763 # Allow qpop -a to work repeatedly,
1763 # but not qpop without an argument
1764 # but not qpop without an argument
1764 self.ui.warn(_(b"no patches applied\n"))
1765 self.ui.warn(_(b"no patches applied\n"))
1765 return not all
1766 return not all
1766
1767
1767 if all:
1768 if all:
1768 start = 0
1769 start = 0
1769 elif patch:
1770 elif patch:
1770 start = info[0] + 1
1771 start = info[0] + 1
1771 else:
1772 else:
1772 start = len(self.applied) - 1
1773 start = len(self.applied) - 1
1773
1774
1774 if start >= len(self.applied):
1775 if start >= len(self.applied):
1775 self.ui.warn(_(b"qpop: %s is already at the top\n") % patch)
1776 self.ui.warn(_(b"qpop: %s is already at the top\n") % patch)
1776 return
1777 return
1777
1778
1778 if not update:
1779 if not update:
1779 parents = repo.dirstate.parents()
1780 parents = repo.dirstate.parents()
1780 rr = [x.node for x in self.applied]
1781 rr = [x.node for x in self.applied]
1781 for p in parents:
1782 for p in parents:
1782 if p in rr:
1783 if p in rr:
1783 self.ui.warn(_(b"qpop: forcing dirstate update\n"))
1784 self.ui.warn(_(b"qpop: forcing dirstate update\n"))
1784 update = True
1785 update = True
1785 else:
1786 else:
1786 parents = [p.node() for p in repo[None].parents()]
1787 parents = [p.node() for p in repo[None].parents()]
1787 update = any(
1788 update = any(
1788 entry.node in parents for entry in self.applied[start:]
1789 entry.node in parents for entry in self.applied[start:]
1789 )
1790 )
1790
1791
1791 tobackup = set()
1792 tobackup = set()
1792 if update:
1793 if update:
1793 s = self.checklocalchanges(repo, force=force or keepchanges)
1794 s = self.checklocalchanges(repo, force=force or keepchanges)
1794 if force:
1795 if force:
1795 if not nobackup:
1796 if not nobackup:
1796 tobackup.update(s.modified + s.added)
1797 tobackup.update(s.modified + s.added)
1797 elif keepchanges:
1798 elif keepchanges:
1798 tobackup.update(
1799 tobackup.update(
1799 s.modified + s.added + s.removed + s.deleted
1800 s.modified + s.added + s.removed + s.deleted
1800 )
1801 )
1801
1802
1802 self.applieddirty = True
1803 self.applieddirty = True
1803 end = len(self.applied)
1804 end = len(self.applied)
1804 rev = self.applied[start].node
1805 rev = self.applied[start].node
1805
1806
1806 try:
1807 try:
1807 heads = repo.changelog.heads(rev)
1808 heads = repo.changelog.heads(rev)
1808 except error.LookupError:
1809 except error.LookupError:
1809 node = short(rev)
1810 node = short(rev)
1810 raise error.Abort(_(b'trying to pop unknown node %s') % node)
1811 raise error.Abort(_(b'trying to pop unknown node %s') % node)
1811
1812
1812 if heads != [self.applied[-1].node]:
1813 if heads != [self.applied[-1].node]:
1813 raise error.Abort(
1814 raise error.Abort(
1814 _(
1815 _(
1815 b"popping would remove a revision not "
1816 b"popping would remove a revision not "
1816 b"managed by this patch queue"
1817 b"managed by this patch queue"
1817 )
1818 )
1818 )
1819 )
1819 if not repo[self.applied[-1].node].mutable():
1820 if not repo[self.applied[-1].node].mutable():
1820 raise error.Abort(
1821 raise error.Abort(
1821 _(b"popping would remove a public revision"),
1822 _(b"popping would remove a public revision"),
1822 hint=_(b"see 'hg help phases' for details"),
1823 hint=_(b"see 'hg help phases' for details"),
1823 )
1824 )
1824
1825
1825 # we know there are no local changes, so we can make a simplified
1826 # we know there are no local changes, so we can make a simplified
1826 # form of hg.update.
1827 # form of hg.update.
1827 if update:
1828 if update:
1828 qp = self.qparents(repo, rev)
1829 qp = self.qparents(repo, rev)
1829 ctx = repo[qp]
1830 ctx = repo[qp]
1830 st = repo.status(qp, b'.')
1831 st = repo.status(qp, b'.')
1831 m, a, r, d = st.modified, st.added, st.removed, st.deleted
1832 m, a, r, d = st.modified, st.added, st.removed, st.deleted
1832 if d:
1833 if d:
1833 raise error.Abort(_(b"deletions found between repo revs"))
1834 raise error.Abort(_(b"deletions found between repo revs"))
1834
1835
1835 tobackup = set(a + m + r) & tobackup
1836 tobackup = set(a + m + r) & tobackup
1836 if keepchanges and tobackup:
1837 if keepchanges and tobackup:
1837 raise error.Abort(_(b"local changes found, qrefresh first"))
1838 raise error.Abort(_(b"local changes found, qrefresh first"))
1838 self.backup(repo, tobackup)
1839 self.backup(repo, tobackup)
1839 with repo.dirstate.parentchange():
1840 with repo.dirstate.parentchange():
1840 for f in a:
1841 for f in a:
1841 repo.wvfs.unlinkpath(f, ignoremissing=True)
1842 repo.wvfs.unlinkpath(f, ignoremissing=True)
1842 repo.dirstate.drop(f)
1843 repo.dirstate.drop(f)
1843 for f in m + r:
1844 for f in m + r:
1844 fctx = ctx[f]
1845 fctx = ctx[f]
1845 repo.wwrite(f, fctx.data(), fctx.flags())
1846 repo.wwrite(f, fctx.data(), fctx.flags())
1846 repo.dirstate.normal(f)
1847 repo.dirstate.normal(f)
1847 repo.setparents(qp, nullid)
1848 repo.setparents(qp, nullid)
1848 for patch in reversed(self.applied[start:end]):
1849 for patch in reversed(self.applied[start:end]):
1849 self.ui.status(_(b"popping %s\n") % patch.name)
1850 self.ui.status(_(b"popping %s\n") % patch.name)
1850 del self.applied[start:end]
1851 del self.applied[start:end]
1851 strip(self.ui, repo, [rev], update=False, backup=False)
1852 strip(self.ui, repo, [rev], update=False, backup=False)
1852 for s, state in repo[b'.'].substate.items():
1853 for s, state in repo[b'.'].substate.items():
1853 repo[b'.'].sub(s).get(state)
1854 repo[b'.'].sub(s).get(state)
1854 if self.applied:
1855 if self.applied:
1855 self.ui.write(_(b"now at: %s\n") % self.applied[-1].name)
1856 self.ui.write(_(b"now at: %s\n") % self.applied[-1].name)
1856 else:
1857 else:
1857 self.ui.write(_(b"patch queue now empty\n"))
1858 self.ui.write(_(b"patch queue now empty\n"))
1858
1859
1859 def diff(self, repo, pats, opts):
1860 def diff(self, repo, pats, opts):
1860 top, patch = self.checktoppatch(repo)
1861 top, patch = self.checktoppatch(repo)
1861 if not top:
1862 if not top:
1862 self.ui.write(_(b"no patches applied\n"))
1863 self.ui.write(_(b"no patches applied\n"))
1863 return
1864 return
1864 qp = self.qparents(repo, top)
1865 qp = self.qparents(repo, top)
1865 if opts.get(b'reverse'):
1866 if opts.get(b'reverse'):
1866 node1, node2 = None, qp
1867 node1, node2 = None, qp
1867 else:
1868 else:
1868 node1, node2 = qp, None
1869 node1, node2 = qp, None
1869 diffopts = self.diffopts(opts, patch)
1870 diffopts = self.diffopts(opts, patch)
1870 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1871 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1871
1872
1872 def refresh(self, repo, pats=None, **opts):
1873 def refresh(self, repo, pats=None, **opts):
1873 opts = pycompat.byteskwargs(opts)
1874 opts = pycompat.byteskwargs(opts)
1874 if not self.applied:
1875 if not self.applied:
1875 self.ui.write(_(b"no patches applied\n"))
1876 self.ui.write(_(b"no patches applied\n"))
1876 return 1
1877 return 1
1877 msg = opts.get(b'msg', b'').rstrip()
1878 msg = opts.get(b'msg', b'').rstrip()
1878 edit = opts.get(b'edit')
1879 edit = opts.get(b'edit')
1879 editform = opts.get(b'editform', b'mq.qrefresh')
1880 editform = opts.get(b'editform', b'mq.qrefresh')
1880 newuser = opts.get(b'user')
1881 newuser = opts.get(b'user')
1881 newdate = opts.get(b'date')
1882 newdate = opts.get(b'date')
1882 if newdate:
1883 if newdate:
1883 newdate = b'%d %d' % dateutil.parsedate(newdate)
1884 newdate = b'%d %d' % dateutil.parsedate(newdate)
1884 wlock = repo.wlock()
1885 wlock = repo.wlock()
1885
1886
1886 try:
1887 try:
1887 self.checktoppatch(repo)
1888 self.checktoppatch(repo)
1888 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1889 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1889 if repo.changelog.heads(top) != [top]:
1890 if repo.changelog.heads(top) != [top]:
1890 raise error.Abort(
1891 raise error.Abort(
1891 _(b"cannot qrefresh a revision with children")
1892 _(b"cannot qrefresh a revision with children")
1892 )
1893 )
1893 if not repo[top].mutable():
1894 if not repo[top].mutable():
1894 raise error.Abort(
1895 raise error.Abort(
1895 _(b"cannot qrefresh public revision"),
1896 _(b"cannot qrefresh public revision"),
1896 hint=_(b"see 'hg help phases' for details"),
1897 hint=_(b"see 'hg help phases' for details"),
1897 )
1898 )
1898
1899
1899 cparents = repo.changelog.parents(top)
1900 cparents = repo.changelog.parents(top)
1900 patchparent = self.qparents(repo, top)
1901 patchparent = self.qparents(repo, top)
1901
1902
1902 inclsubs = checksubstate(repo, patchparent)
1903 inclsubs = checksubstate(repo, patchparent)
1903 if inclsubs:
1904 if inclsubs:
1904 substatestate = repo.dirstate[b'.hgsubstate']
1905 substatestate = repo.dirstate[b'.hgsubstate']
1905
1906
1906 ph = patchheader(self.join(patchfn), self.plainmode)
1907 ph = patchheader(self.join(patchfn), self.plainmode)
1907 diffopts = self.diffopts(
1908 diffopts = self.diffopts(
1908 {b'git': opts.get(b'git')}, patchfn, plain=True
1909 {b'git': opts.get(b'git')}, patchfn, plain=True
1909 )
1910 )
1910 if newuser:
1911 if newuser:
1911 ph.setuser(newuser)
1912 ph.setuser(newuser)
1912 if newdate:
1913 if newdate:
1913 ph.setdate(newdate)
1914 ph.setdate(newdate)
1914 ph.setparent(hex(patchparent))
1915 ph.setparent(hex(patchparent))
1915
1916
1916 # only commit new patch when write is complete
1917 # only commit new patch when write is complete
1917 patchf = self.opener(patchfn, b'w', atomictemp=True)
1918 patchf = self.opener(patchfn, b'w', atomictemp=True)
1918
1919
1919 # update the dirstate in place, strip off the qtip commit
1920 # update the dirstate in place, strip off the qtip commit
1920 # and then commit.
1921 # and then commit.
1921 #
1922 #
1922 # this should really read:
1923 # this should really read:
1923 # st = repo.status(top, patchparent)
1924 # st = repo.status(top, patchparent)
1924 # but we do it backwards to take advantage of manifest/changelog
1925 # but we do it backwards to take advantage of manifest/changelog
1925 # caching against the next repo.status call
1926 # caching against the next repo.status call
1926 st = repo.status(patchparent, top)
1927 st = repo.status(patchparent, top)
1927 mm, aa, dd = st.modified, st.added, st.removed
1928 mm, aa, dd = st.modified, st.added, st.removed
1928 ctx = repo[top]
1929 ctx = repo[top]
1929 aaa = aa[:]
1930 aaa = aa[:]
1930 match1 = scmutil.match(repo[None], pats, opts)
1931 match1 = scmutil.match(repo[None], pats, opts)
1931 # in short mode, we only diff the files included in the
1932 # in short mode, we only diff the files included in the
1932 # patch already plus specified files
1933 # patch already plus specified files
1933 if opts.get(b'short'):
1934 if opts.get(b'short'):
1934 # if amending a patch, we start with existing
1935 # if amending a patch, we start with existing
1935 # files plus specified files - unfiltered
1936 # files plus specified files - unfiltered
1936 match = scmutil.matchfiles(repo, mm + aa + dd + match1.files())
1937 match = scmutil.matchfiles(repo, mm + aa + dd + match1.files())
1937 # filter with include/exclude options
1938 # filter with include/exclude options
1938 match1 = scmutil.match(repo[None], opts=opts)
1939 match1 = scmutil.match(repo[None], opts=opts)
1939 else:
1940 else:
1940 match = scmutil.matchall(repo)
1941 match = scmutil.matchall(repo)
1941 stb = repo.status(match=match)
1942 stb = repo.status(match=match)
1942 m, a, r, d = stb.modified, stb.added, stb.removed, stb.deleted
1943 m, a, r, d = stb.modified, stb.added, stb.removed, stb.deleted
1943 mm = set(mm)
1944 mm = set(mm)
1944 aa = set(aa)
1945 aa = set(aa)
1945 dd = set(dd)
1946 dd = set(dd)
1946
1947
1947 # we might end up with files that were added between
1948 # we might end up with files that were added between
1948 # qtip and the dirstate parent, but then changed in the
1949 # qtip and the dirstate parent, but then changed in the
1949 # local dirstate. in this case, we want them to only
1950 # local dirstate. in this case, we want them to only
1950 # show up in the added section
1951 # show up in the added section
1951 for x in m:
1952 for x in m:
1952 if x not in aa:
1953 if x not in aa:
1953 mm.add(x)
1954 mm.add(x)
1954 # we might end up with files added by the local dirstate that
1955 # we might end up with files added by the local dirstate that
1955 # were deleted by the patch. In this case, they should only
1956 # were deleted by the patch. In this case, they should only
1956 # show up in the changed section.
1957 # show up in the changed section.
1957 for x in a:
1958 for x in a:
1958 if x in dd:
1959 if x in dd:
1959 dd.remove(x)
1960 dd.remove(x)
1960 mm.add(x)
1961 mm.add(x)
1961 else:
1962 else:
1962 aa.add(x)
1963 aa.add(x)
1963 # make sure any files deleted in the local dirstate
1964 # make sure any files deleted in the local dirstate
1964 # are not in the add or change column of the patch
1965 # are not in the add or change column of the patch
1965 forget = []
1966 forget = []
1966 for x in d + r:
1967 for x in d + r:
1967 if x in aa:
1968 if x in aa:
1968 aa.remove(x)
1969 aa.remove(x)
1969 forget.append(x)
1970 forget.append(x)
1970 continue
1971 continue
1971 else:
1972 else:
1972 mm.discard(x)
1973 mm.discard(x)
1973 dd.add(x)
1974 dd.add(x)
1974
1975
1975 m = list(mm)
1976 m = list(mm)
1976 r = list(dd)
1977 r = list(dd)
1977 a = list(aa)
1978 a = list(aa)
1978
1979
1979 # create 'match' that includes the files to be recommitted.
1980 # create 'match' that includes the files to be recommitted.
1980 # apply match1 via repo.status to ensure correct case handling.
1981 # apply match1 via repo.status to ensure correct case handling.
1981 st = repo.status(patchparent, match=match1)
1982 st = repo.status(patchparent, match=match1)
1982 cm, ca, cr, cd = st.modified, st.added, st.removed, st.deleted
1983 cm, ca, cr, cd = st.modified, st.added, st.removed, st.deleted
1983 allmatches = set(cm + ca + cr + cd)
1984 allmatches = set(cm + ca + cr + cd)
1984 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1985 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1985
1986
1986 files = set(inclsubs)
1987 files = set(inclsubs)
1987 for x in refreshchanges:
1988 for x in refreshchanges:
1988 files.update(x)
1989 files.update(x)
1989 match = scmutil.matchfiles(repo, files)
1990 match = scmutil.matchfiles(repo, files)
1990
1991
1991 bmlist = repo[top].bookmarks()
1992 bmlist = repo[top].bookmarks()
1992
1993
1993 dsguard = None
1994 dsguard = None
1994 try:
1995 try:
1995 dsguard = dirstateguard.dirstateguard(repo, b'mq.refresh')
1996 dsguard = dirstateguard.dirstateguard(repo, b'mq.refresh')
1996 if diffopts.git or diffopts.upgrade:
1997 if diffopts.git or diffopts.upgrade:
1997 copies = {}
1998 copies = {}
1998 for dst in a:
1999 for dst in a:
1999 src = repo.dirstate.copied(dst)
2000 src = repo.dirstate.copied(dst)
2000 # during qfold, the source file for copies may
2001 # during qfold, the source file for copies may
2001 # be removed. Treat this as a simple add.
2002 # be removed. Treat this as a simple add.
2002 if src is not None and src in repo.dirstate:
2003 if src is not None and src in repo.dirstate:
2003 copies.setdefault(src, []).append(dst)
2004 copies.setdefault(src, []).append(dst)
2004 repo.dirstate.add(dst)
2005 repo.dirstate.add(dst)
2005 # remember the copies between patchparent and qtip
2006 # remember the copies between patchparent and qtip
2006 for dst in aaa:
2007 for dst in aaa:
2007 src = ctx[dst].copysource()
2008 src = ctx[dst].copysource()
2008 if src:
2009 if src:
2009 copies.setdefault(src, []).extend(
2010 copies.setdefault(src, []).extend(
2010 copies.get(dst, [])
2011 copies.get(dst, [])
2011 )
2012 )
2012 if dst in a:
2013 if dst in a:
2013 copies[src].append(dst)
2014 copies[src].append(dst)
2014 # we can't copy a file created by the patch itself
2015 # we can't copy a file created by the patch itself
2015 if dst in copies:
2016 if dst in copies:
2016 del copies[dst]
2017 del copies[dst]
2017 for src, dsts in pycompat.iteritems(copies):
2018 for src, dsts in pycompat.iteritems(copies):
2018 for dst in dsts:
2019 for dst in dsts:
2019 repo.dirstate.copy(src, dst)
2020 repo.dirstate.copy(src, dst)
2020 else:
2021 else:
2021 for dst in a:
2022 for dst in a:
2022 repo.dirstate.add(dst)
2023 repo.dirstate.add(dst)
2023 # Drop useless copy information
2024 # Drop useless copy information
2024 for f in list(repo.dirstate.copies()):
2025 for f in list(repo.dirstate.copies()):
2025 repo.dirstate.copy(None, f)
2026 repo.dirstate.copy(None, f)
2026 for f in r:
2027 for f in r:
2027 repo.dirstate.remove(f)
2028 repo.dirstate.remove(f)
2028 # if the patch excludes a modified file, mark that
2029 # if the patch excludes a modified file, mark that
2029 # file with mtime=0 so status can see it.
2030 # file with mtime=0 so status can see it.
2030 mm = []
2031 mm = []
2031 for i in pycompat.xrange(len(m) - 1, -1, -1):
2032 for i in pycompat.xrange(len(m) - 1, -1, -1):
2032 if not match1(m[i]):
2033 if not match1(m[i]):
2033 mm.append(m[i])
2034 mm.append(m[i])
2034 del m[i]
2035 del m[i]
2035 for f in m:
2036 for f in m:
2036 repo.dirstate.normal(f)
2037 repo.dirstate.normal(f)
2037 for f in mm:
2038 for f in mm:
2038 repo.dirstate.normallookup(f)
2039 repo.dirstate.normallookup(f)
2039 for f in forget:
2040 for f in forget:
2040 repo.dirstate.drop(f)
2041 repo.dirstate.drop(f)
2041
2042
2042 user = ph.user or ctx.user()
2043 user = ph.user or ctx.user()
2043
2044
2044 oldphase = repo[top].phase()
2045 oldphase = repo[top].phase()
2045
2046
2046 # assumes strip can roll itself back if interrupted
2047 # assumes strip can roll itself back if interrupted
2047 repo.setparents(*cparents)
2048 repo.setparents(*cparents)
2048 self.applied.pop()
2049 self.applied.pop()
2049 self.applieddirty = True
2050 self.applieddirty = True
2050 strip(self.ui, repo, [top], update=False, backup=False)
2051 strip(self.ui, repo, [top], update=False, backup=False)
2051 dsguard.close()
2052 dsguard.close()
2052 finally:
2053 finally:
2053 release(dsguard)
2054 release(dsguard)
2054
2055
2055 try:
2056 try:
2056 # might be nice to attempt to roll back strip after this
2057 # might be nice to attempt to roll back strip after this
2057
2058
2058 defaultmsg = b"[mq]: %s" % patchfn
2059 defaultmsg = b"[mq]: %s" % patchfn
2059 editor = cmdutil.getcommiteditor(editform=editform)
2060 editor = cmdutil.getcommiteditor(editform=editform)
2060 if edit:
2061 if edit:
2061
2062
2062 def finishdesc(desc):
2063 def finishdesc(desc):
2063 if desc.rstrip():
2064 if desc.rstrip():
2064 ph.setmessage(desc)
2065 ph.setmessage(desc)
2065 return desc
2066 return desc
2066 return defaultmsg
2067 return defaultmsg
2067
2068
2068 # i18n: this message is shown in editor with "HG: " prefix
2069 # i18n: this message is shown in editor with "HG: " prefix
2069 extramsg = _(b'Leave message empty to use default message.')
2070 extramsg = _(b'Leave message empty to use default message.')
2070 editor = cmdutil.getcommiteditor(
2071 editor = cmdutil.getcommiteditor(
2071 finishdesc=finishdesc,
2072 finishdesc=finishdesc,
2072 extramsg=extramsg,
2073 extramsg=extramsg,
2073 editform=editform,
2074 editform=editform,
2074 )
2075 )
2075 message = msg or b"\n".join(ph.message)
2076 message = msg or b"\n".join(ph.message)
2076 elif not msg:
2077 elif not msg:
2077 if not ph.message:
2078 if not ph.message:
2078 message = defaultmsg
2079 message = defaultmsg
2079 else:
2080 else:
2080 message = b"\n".join(ph.message)
2081 message = b"\n".join(ph.message)
2081 else:
2082 else:
2082 message = msg
2083 message = msg
2083 ph.setmessage(msg)
2084 ph.setmessage(msg)
2084
2085
2085 # Ensure we create a new changeset in the same phase than
2086 # Ensure we create a new changeset in the same phase than
2086 # the old one.
2087 # the old one.
2087 lock = tr = None
2088 lock = tr = None
2088 try:
2089 try:
2089 lock = repo.lock()
2090 lock = repo.lock()
2090 tr = repo.transaction(b'mq')
2091 tr = repo.transaction(b'mq')
2091 n = newcommit(
2092 n = newcommit(
2092 repo,
2093 repo,
2093 oldphase,
2094 oldphase,
2094 message,
2095 message,
2095 user,
2096 user,
2096 ph.date,
2097 ph.date,
2097 match=match,
2098 match=match,
2098 force=True,
2099 force=True,
2099 editor=editor,
2100 editor=editor,
2100 )
2101 )
2101 # only write patch after a successful commit
2102 # only write patch after a successful commit
2102 c = [list(x) for x in refreshchanges]
2103 c = [list(x) for x in refreshchanges]
2103 if inclsubs:
2104 if inclsubs:
2104 self.putsubstate2changes(substatestate, c)
2105 self.putsubstate2changes(substatestate, c)
2105 chunks = patchmod.diff(
2106 chunks = patchmod.diff(
2106 repo, patchparent, changes=c, opts=diffopts
2107 repo, patchparent, changes=c, opts=diffopts
2107 )
2108 )
2108 comments = bytes(ph)
2109 comments = bytes(ph)
2109 if comments:
2110 if comments:
2110 patchf.write(comments)
2111 patchf.write(comments)
2111 for chunk in chunks:
2112 for chunk in chunks:
2112 patchf.write(chunk)
2113 patchf.write(chunk)
2113 patchf.close()
2114 patchf.close()
2114
2115
2115 marks = repo._bookmarks
2116 marks = repo._bookmarks
2116 marks.applychanges(repo, tr, [(bm, n) for bm in bmlist])
2117 marks.applychanges(repo, tr, [(bm, n) for bm in bmlist])
2117 tr.close()
2118 tr.close()
2118
2119
2119 self.applied.append(statusentry(n, patchfn))
2120 self.applied.append(statusentry(n, patchfn))
2120 finally:
2121 finally:
2121 lockmod.release(tr, lock)
2122 lockmod.release(tr, lock)
2122 except: # re-raises
2123 except: # re-raises
2123 ctx = repo[cparents[0]]
2124 ctx = repo[cparents[0]]
2124 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2125 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2125 self.savedirty()
2126 self.savedirty()
2126 self.ui.warn(
2127 self.ui.warn(
2127 _(
2128 _(
2128 b'qrefresh interrupted while patch was popped! '
2129 b'qrefresh interrupted while patch was popped! '
2129 b'(revert --all, qpush to recover)\n'
2130 b'(revert --all, qpush to recover)\n'
2130 )
2131 )
2131 )
2132 )
2132 raise
2133 raise
2133 finally:
2134 finally:
2134 wlock.release()
2135 wlock.release()
2135 self.removeundo(repo)
2136 self.removeundo(repo)
2136
2137
2137 def init(self, repo, create=False):
2138 def init(self, repo, create=False):
2138 if not create and os.path.isdir(self.path):
2139 if not create and os.path.isdir(self.path):
2139 raise error.Abort(_(b"patch queue directory already exists"))
2140 raise error.Abort(_(b"patch queue directory already exists"))
2140 try:
2141 try:
2141 os.mkdir(self.path)
2142 os.mkdir(self.path)
2142 except OSError as inst:
2143 except OSError as inst:
2143 if inst.errno != errno.EEXIST or not create:
2144 if inst.errno != errno.EEXIST or not create:
2144 raise
2145 raise
2145 if create:
2146 if create:
2146 return self.qrepo(create=True)
2147 return self.qrepo(create=True)
2147
2148
2148 def unapplied(self, repo, patch=None):
2149 def unapplied(self, repo, patch=None):
2149 if patch and patch not in self.series:
2150 if patch and patch not in self.series:
2150 raise error.Abort(_(b"patch %s is not in series file") % patch)
2151 raise error.Abort(_(b"patch %s is not in series file") % patch)
2151 if not patch:
2152 if not patch:
2152 start = self.seriesend()
2153 start = self.seriesend()
2153 else:
2154 else:
2154 start = self.series.index(patch) + 1
2155 start = self.series.index(patch) + 1
2155 unapplied = []
2156 unapplied = []
2156 for i in pycompat.xrange(start, len(self.series)):
2157 for i in pycompat.xrange(start, len(self.series)):
2157 pushable, reason = self.pushable(i)
2158 pushable, reason = self.pushable(i)
2158 if pushable:
2159 if pushable:
2159 unapplied.append((i, self.series[i]))
2160 unapplied.append((i, self.series[i]))
2160 self.explainpushable(i)
2161 self.explainpushable(i)
2161 return unapplied
2162 return unapplied
2162
2163
2163 def qseries(
2164 def qseries(
2164 self,
2165 self,
2165 repo,
2166 repo,
2166 missing=None,
2167 missing=None,
2167 start=0,
2168 start=0,
2168 length=None,
2169 length=None,
2169 status=None,
2170 status=None,
2170 summary=False,
2171 summary=False,
2171 ):
2172 ):
2172 def displayname(pfx, patchname, state):
2173 def displayname(pfx, patchname, state):
2173 if pfx:
2174 if pfx:
2174 self.ui.write(pfx)
2175 self.ui.write(pfx)
2175 if summary:
2176 if summary:
2176 ph = patchheader(self.join(patchname), self.plainmode)
2177 ph = patchheader(self.join(patchname), self.plainmode)
2177 if ph.message:
2178 if ph.message:
2178 msg = ph.message[0]
2179 msg = ph.message[0]
2179 else:
2180 else:
2180 msg = b''
2181 msg = b''
2181
2182
2182 if self.ui.formatted():
2183 if self.ui.formatted():
2183 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
2184 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
2184 if width > 0:
2185 if width > 0:
2185 msg = stringutil.ellipsis(msg, width)
2186 msg = stringutil.ellipsis(msg, width)
2186 else:
2187 else:
2187 msg = b''
2188 msg = b''
2188 self.ui.write(patchname, label=b'qseries.' + state)
2189 self.ui.write(patchname, label=b'qseries.' + state)
2189 self.ui.write(b': ')
2190 self.ui.write(b': ')
2190 self.ui.write(msg, label=b'qseries.message.' + state)
2191 self.ui.write(msg, label=b'qseries.message.' + state)
2191 else:
2192 else:
2192 self.ui.write(patchname, label=b'qseries.' + state)
2193 self.ui.write(patchname, label=b'qseries.' + state)
2193 self.ui.write(b'\n')
2194 self.ui.write(b'\n')
2194
2195
2195 applied = {p.name for p in self.applied}
2196 applied = {p.name for p in self.applied}
2196 if length is None:
2197 if length is None:
2197 length = len(self.series) - start
2198 length = len(self.series) - start
2198 if not missing:
2199 if not missing:
2199 if self.ui.verbose:
2200 if self.ui.verbose:
2200 idxwidth = len(b"%d" % (start + length - 1))
2201 idxwidth = len(b"%d" % (start + length - 1))
2201 for i in pycompat.xrange(start, start + length):
2202 for i in pycompat.xrange(start, start + length):
2202 patch = self.series[i]
2203 patch = self.series[i]
2203 if patch in applied:
2204 if patch in applied:
2204 char, state = b'A', b'applied'
2205 char, state = b'A', b'applied'
2205 elif self.pushable(i)[0]:
2206 elif self.pushable(i)[0]:
2206 char, state = b'U', b'unapplied'
2207 char, state = b'U', b'unapplied'
2207 else:
2208 else:
2208 char, state = b'G', b'guarded'
2209 char, state = b'G', b'guarded'
2209 pfx = b''
2210 pfx = b''
2210 if self.ui.verbose:
2211 if self.ui.verbose:
2211 pfx = b'%*d %s ' % (idxwidth, i, char)
2212 pfx = b'%*d %s ' % (idxwidth, i, char)
2212 elif status and status != char:
2213 elif status and status != char:
2213 continue
2214 continue
2214 displayname(pfx, patch, state)
2215 displayname(pfx, patch, state)
2215 else:
2216 else:
2216 msng_list = []
2217 msng_list = []
2217 for root, dirs, files in os.walk(self.path):
2218 for root, dirs, files in os.walk(self.path):
2218 d = root[len(self.path) + 1 :]
2219 d = root[len(self.path) + 1 :]
2219 for f in files:
2220 for f in files:
2220 fl = os.path.join(d, f)
2221 fl = os.path.join(d, f)
2221 if (
2222 if (
2222 fl not in self.series
2223 fl not in self.series
2223 and fl
2224 and fl
2224 not in (
2225 not in (
2225 self.statuspath,
2226 self.statuspath,
2226 self.seriespath,
2227 self.seriespath,
2227 self.guardspath,
2228 self.guardspath,
2228 )
2229 )
2229 and not fl.startswith(b'.')
2230 and not fl.startswith(b'.')
2230 ):
2231 ):
2231 msng_list.append(fl)
2232 msng_list.append(fl)
2232 for x in sorted(msng_list):
2233 for x in sorted(msng_list):
2233 pfx = self.ui.verbose and b'D ' or b''
2234 pfx = self.ui.verbose and b'D ' or b''
2234 displayname(pfx, x, b'missing')
2235 displayname(pfx, x, b'missing')
2235
2236
2236 def issaveline(self, l):
2237 def issaveline(self, l):
2237 if l.name == b'.hg.patches.save.line':
2238 if l.name == b'.hg.patches.save.line':
2238 return True
2239 return True
2239
2240
2240 def qrepo(self, create=False):
2241 def qrepo(self, create=False):
2241 ui = self.baseui.copy()
2242 ui = self.baseui.copy()
2242 # copy back attributes set by ui.pager()
2243 # copy back attributes set by ui.pager()
2243 if self.ui.pageractive and not ui.pageractive:
2244 if self.ui.pageractive and not ui.pageractive:
2244 ui.pageractive = self.ui.pageractive
2245 ui.pageractive = self.ui.pageractive
2245 # internal config: ui.formatted
2246 # internal config: ui.formatted
2246 ui.setconfig(
2247 ui.setconfig(
2247 b'ui',
2248 b'ui',
2248 b'formatted',
2249 b'formatted',
2249 self.ui.config(b'ui', b'formatted'),
2250 self.ui.config(b'ui', b'formatted'),
2250 b'mqpager',
2251 b'mqpager',
2251 )
2252 )
2252 ui.setconfig(
2253 ui.setconfig(
2253 b'ui',
2254 b'ui',
2254 b'interactive',
2255 b'interactive',
2255 self.ui.config(b'ui', b'interactive'),
2256 self.ui.config(b'ui', b'interactive'),
2256 b'mqpager',
2257 b'mqpager',
2257 )
2258 )
2258 if create or os.path.isdir(self.join(b".hg")):
2259 if create or os.path.isdir(self.join(b".hg")):
2259 return hg.repository(ui, path=self.path, create=create)
2260 return hg.repository(ui, path=self.path, create=create)
2260
2261
2261 def restore(self, repo, rev, delete=None, qupdate=None):
2262 def restore(self, repo, rev, delete=None, qupdate=None):
2262 desc = repo[rev].description().strip()
2263 desc = repo[rev].description().strip()
2263 lines = desc.splitlines()
2264 lines = desc.splitlines()
2264 datastart = None
2265 datastart = None
2265 series = []
2266 series = []
2266 applied = []
2267 applied = []
2267 qpp = None
2268 qpp = None
2268 for i, line in enumerate(lines):
2269 for i, line in enumerate(lines):
2269 if line == b'Patch Data:':
2270 if line == b'Patch Data:':
2270 datastart = i + 1
2271 datastart = i + 1
2271 elif line.startswith(b'Dirstate:'):
2272 elif line.startswith(b'Dirstate:'):
2272 l = line.rstrip()
2273 l = line.rstrip()
2273 l = l[10:].split(b' ')
2274 l = l[10:].split(b' ')
2274 qpp = [bin(x) for x in l]
2275 qpp = [bin(x) for x in l]
2275 elif datastart is not None:
2276 elif datastart is not None:
2276 l = line.rstrip()
2277 l = line.rstrip()
2277 n, name = l.split(b':', 1)
2278 n, name = l.split(b':', 1)
2278 if n:
2279 if n:
2279 applied.append(statusentry(bin(n), name))
2280 applied.append(statusentry(bin(n), name))
2280 else:
2281 else:
2281 series.append(l)
2282 series.append(l)
2282 if datastart is None:
2283 if datastart is None:
2283 self.ui.warn(_(b"no saved patch data found\n"))
2284 self.ui.warn(_(b"no saved patch data found\n"))
2284 return 1
2285 return 1
2285 self.ui.warn(_(b"restoring status: %s\n") % lines[0])
2286 self.ui.warn(_(b"restoring status: %s\n") % lines[0])
2286 self.fullseries = series
2287 self.fullseries = series
2287 self.applied = applied
2288 self.applied = applied
2288 self.parseseries()
2289 self.parseseries()
2289 self.seriesdirty = True
2290 self.seriesdirty = True
2290 self.applieddirty = True
2291 self.applieddirty = True
2291 heads = repo.changelog.heads()
2292 heads = repo.changelog.heads()
2292 if delete:
2293 if delete:
2293 if rev not in heads:
2294 if rev not in heads:
2294 self.ui.warn(_(b"save entry has children, leaving it alone\n"))
2295 self.ui.warn(_(b"save entry has children, leaving it alone\n"))
2295 else:
2296 else:
2296 self.ui.warn(_(b"removing save entry %s\n") % short(rev))
2297 self.ui.warn(_(b"removing save entry %s\n") % short(rev))
2297 pp = repo.dirstate.parents()
2298 pp = repo.dirstate.parents()
2298 if rev in pp:
2299 if rev in pp:
2299 update = True
2300 update = True
2300 else:
2301 else:
2301 update = False
2302 update = False
2302 strip(self.ui, repo, [rev], update=update, backup=False)
2303 strip(self.ui, repo, [rev], update=update, backup=False)
2303 if qpp:
2304 if qpp:
2304 self.ui.warn(
2305 self.ui.warn(
2305 _(b"saved queue repository parents: %s %s\n")
2306 _(b"saved queue repository parents: %s %s\n")
2306 % (short(qpp[0]), short(qpp[1]))
2307 % (short(qpp[0]), short(qpp[1]))
2307 )
2308 )
2308 if qupdate:
2309 if qupdate:
2309 self.ui.status(_(b"updating queue directory\n"))
2310 self.ui.status(_(b"updating queue directory\n"))
2310 r = self.qrepo()
2311 r = self.qrepo()
2311 if not r:
2312 if not r:
2312 self.ui.warn(_(b"unable to load queue repository\n"))
2313 self.ui.warn(_(b"unable to load queue repository\n"))
2313 return 1
2314 return 1
2314 hg.clean(r, qpp[0])
2315 hg.clean(r, qpp[0])
2315
2316
2316 def save(self, repo, msg=None):
2317 def save(self, repo, msg=None):
2317 if not self.applied:
2318 if not self.applied:
2318 self.ui.warn(_(b"save: no patches applied, exiting\n"))
2319 self.ui.warn(_(b"save: no patches applied, exiting\n"))
2319 return 1
2320 return 1
2320 if self.issaveline(self.applied[-1]):
2321 if self.issaveline(self.applied[-1]):
2321 self.ui.warn(_(b"status is already saved\n"))
2322 self.ui.warn(_(b"status is already saved\n"))
2322 return 1
2323 return 1
2323
2324
2324 if not msg:
2325 if not msg:
2325 msg = _(b"hg patches saved state")
2326 msg = _(b"hg patches saved state")
2326 else:
2327 else:
2327 msg = b"hg patches: " + msg.rstrip(b'\r\n')
2328 msg = b"hg patches: " + msg.rstrip(b'\r\n')
2328 r = self.qrepo()
2329 r = self.qrepo()
2329 if r:
2330 if r:
2330 pp = r.dirstate.parents()
2331 pp = r.dirstate.parents()
2331 msg += b"\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
2332 msg += b"\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
2332 msg += b"\n\nPatch Data:\n"
2333 msg += b"\n\nPatch Data:\n"
2333 msg += b''.join(b'%s\n' % x for x in self.applied)
2334 msg += b''.join(b'%s\n' % x for x in self.applied)
2334 msg += b''.join(b':%s\n' % x for x in self.fullseries)
2335 msg += b''.join(b':%s\n' % x for x in self.fullseries)
2335 n = repo.commit(msg, force=True)
2336 n = repo.commit(msg, force=True)
2336 if not n:
2337 if not n:
2337 self.ui.warn(_(b"repo commit failed\n"))
2338 self.ui.warn(_(b"repo commit failed\n"))
2338 return 1
2339 return 1
2339 self.applied.append(statusentry(n, b'.hg.patches.save.line'))
2340 self.applied.append(statusentry(n, b'.hg.patches.save.line'))
2340 self.applieddirty = True
2341 self.applieddirty = True
2341 self.removeundo(repo)
2342 self.removeundo(repo)
2342
2343
2343 def fullseriesend(self):
2344 def fullseriesend(self):
2344 if self.applied:
2345 if self.applied:
2345 p = self.applied[-1].name
2346 p = self.applied[-1].name
2346 end = self.findseries(p)
2347 end = self.findseries(p)
2347 if end is None:
2348 if end is None:
2348 return len(self.fullseries)
2349 return len(self.fullseries)
2349 return end + 1
2350 return end + 1
2350 return 0
2351 return 0
2351
2352
2352 def seriesend(self, all_patches=False):
2353 def seriesend(self, all_patches=False):
2353 """If all_patches is False, return the index of the next pushable patch
2354 """If all_patches is False, return the index of the next pushable patch
2354 in the series, or the series length. If all_patches is True, return the
2355 in the series, or the series length. If all_patches is True, return the
2355 index of the first patch past the last applied one.
2356 index of the first patch past the last applied one.
2356 """
2357 """
2357 end = 0
2358 end = 0
2358
2359
2359 def nextpatch(start):
2360 def nextpatch(start):
2360 if all_patches or start >= len(self.series):
2361 if all_patches or start >= len(self.series):
2361 return start
2362 return start
2362 for i in pycompat.xrange(start, len(self.series)):
2363 for i in pycompat.xrange(start, len(self.series)):
2363 p, reason = self.pushable(i)
2364 p, reason = self.pushable(i)
2364 if p:
2365 if p:
2365 return i
2366 return i
2366 self.explainpushable(i)
2367 self.explainpushable(i)
2367 return len(self.series)
2368 return len(self.series)
2368
2369
2369 if self.applied:
2370 if self.applied:
2370 p = self.applied[-1].name
2371 p = self.applied[-1].name
2371 try:
2372 try:
2372 end = self.series.index(p)
2373 end = self.series.index(p)
2373 except ValueError:
2374 except ValueError:
2374 return 0
2375 return 0
2375 return nextpatch(end + 1)
2376 return nextpatch(end + 1)
2376 return nextpatch(end)
2377 return nextpatch(end)
2377
2378
2378 def appliedname(self, index):
2379 def appliedname(self, index):
2379 pname = self.applied[index].name
2380 pname = self.applied[index].name
2380 if not self.ui.verbose:
2381 if not self.ui.verbose:
2381 p = pname
2382 p = pname
2382 else:
2383 else:
2383 p = (b"%d" % self.series.index(pname)) + b" " + pname
2384 p = (b"%d" % self.series.index(pname)) + b" " + pname
2384 return p
2385 return p
2385
2386
2386 def qimport(
2387 def qimport(
2387 self,
2388 self,
2388 repo,
2389 repo,
2389 files,
2390 files,
2390 patchname=None,
2391 patchname=None,
2391 rev=None,
2392 rev=None,
2392 existing=None,
2393 existing=None,
2393 force=None,
2394 force=None,
2394 git=False,
2395 git=False,
2395 ):
2396 ):
2396 def checkseries(patchname):
2397 def checkseries(patchname):
2397 if patchname in self.series:
2398 if patchname in self.series:
2398 raise error.Abort(
2399 raise error.Abort(
2399 _(b'patch %s is already in the series file') % patchname
2400 _(b'patch %s is already in the series file') % patchname
2400 )
2401 )
2401
2402
2402 if rev:
2403 if rev:
2403 if files:
2404 if files:
2404 raise error.Abort(
2405 raise error.Abort(
2405 _(b'option "-r" not valid when importing files')
2406 _(b'option "-r" not valid when importing files')
2406 )
2407 )
2407 rev = scmutil.revrange(repo, rev)
2408 rev = scmutil.revrange(repo, rev)
2408 rev.sort(reverse=True)
2409 rev.sort(reverse=True)
2409 elif not files:
2410 elif not files:
2410 raise error.Abort(_(b'no files or revisions specified'))
2411 raise error.Abort(_(b'no files or revisions specified'))
2411 if (len(files) > 1 or len(rev) > 1) and patchname:
2412 if (len(files) > 1 or len(rev) > 1) and patchname:
2412 raise error.Abort(
2413 raise error.Abort(
2413 _(b'option "-n" not valid when importing multiple patches')
2414 _(b'option "-n" not valid when importing multiple patches')
2414 )
2415 )
2415 imported = []
2416 imported = []
2416 if rev:
2417 if rev:
2417 # If mq patches are applied, we can only import revisions
2418 # If mq patches are applied, we can only import revisions
2418 # that form a linear path to qbase.
2419 # that form a linear path to qbase.
2419 # Otherwise, they should form a linear path to a head.
2420 # Otherwise, they should form a linear path to a head.
2420 heads = repo.changelog.heads(repo.changelog.node(rev.first()))
2421 heads = repo.changelog.heads(repo.changelog.node(rev.first()))
2421 if len(heads) > 1:
2422 if len(heads) > 1:
2422 raise error.Abort(
2423 raise error.Abort(
2423 _(b'revision %d is the root of more than one branch')
2424 _(b'revision %d is the root of more than one branch')
2424 % rev.last()
2425 % rev.last()
2425 )
2426 )
2426 if self.applied:
2427 if self.applied:
2427 base = repo.changelog.node(rev.first())
2428 base = repo.changelog.node(rev.first())
2428 if base in [n.node for n in self.applied]:
2429 if base in [n.node for n in self.applied]:
2429 raise error.Abort(
2430 raise error.Abort(
2430 _(b'revision %d is already managed') % rev.first()
2431 _(b'revision %d is already managed') % rev.first()
2431 )
2432 )
2432 if heads != [self.applied[-1].node]:
2433 if heads != [self.applied[-1].node]:
2433 raise error.Abort(
2434 raise error.Abort(
2434 _(b'revision %d is not the parent of the queue')
2435 _(b'revision %d is not the parent of the queue')
2435 % rev.first()
2436 % rev.first()
2436 )
2437 )
2437 base = repo.changelog.rev(self.applied[0].node)
2438 base = repo.changelog.rev(self.applied[0].node)
2438 lastparent = repo.changelog.parentrevs(base)[0]
2439 lastparent = repo.changelog.parentrevs(base)[0]
2439 else:
2440 else:
2440 if heads != [repo.changelog.node(rev.first())]:
2441 if heads != [repo.changelog.node(rev.first())]:
2441 raise error.Abort(
2442 raise error.Abort(
2442 _(b'revision %d has unmanaged children') % rev.first()
2443 _(b'revision %d has unmanaged children') % rev.first()
2443 )
2444 )
2444 lastparent = None
2445 lastparent = None
2445
2446
2446 diffopts = self.diffopts({b'git': git})
2447 diffopts = self.diffopts({b'git': git})
2447 with repo.transaction(b'qimport') as tr:
2448 with repo.transaction(b'qimport') as tr:
2448 for r in rev:
2449 for r in rev:
2449 if not repo[r].mutable():
2450 if not repo[r].mutable():
2450 raise error.Abort(
2451 raise error.Abort(
2451 _(b'revision %d is not mutable') % r,
2452 _(b'revision %d is not mutable') % r,
2452 hint=_(b"see 'hg help phases' " b'for details'),
2453 hint=_(b"see 'hg help phases' " b'for details'),
2453 )
2454 )
2454 p1, p2 = repo.changelog.parentrevs(r)
2455 p1, p2 = repo.changelog.parentrevs(r)
2455 n = repo.changelog.node(r)
2456 n = repo.changelog.node(r)
2456 if p2 != nullrev:
2457 if p2 != nullrev:
2457 raise error.Abort(
2458 raise error.Abort(
2458 _(b'cannot import merge revision %d') % r
2459 _(b'cannot import merge revision %d') % r
2459 )
2460 )
2460 if lastparent and lastparent != r:
2461 if lastparent and lastparent != r:
2461 raise error.Abort(
2462 raise error.Abort(
2462 _(b'revision %d is not the parent of %d')
2463 _(b'revision %d is not the parent of %d')
2463 % (r, lastparent)
2464 % (r, lastparent)
2464 )
2465 )
2465 lastparent = p1
2466 lastparent = p1
2466
2467
2467 if not patchname:
2468 if not patchname:
2468 patchname = self.makepatchname(
2469 patchname = self.makepatchname(
2469 repo[r].description().split(b'\n', 1)[0],
2470 repo[r].description().split(b'\n', 1)[0],
2470 b'%d.diff' % r,
2471 b'%d.diff' % r,
2471 )
2472 )
2472 checkseries(patchname)
2473 checkseries(patchname)
2473 self.checkpatchname(patchname, force)
2474 self.checkpatchname(patchname, force)
2474 self.fullseries.insert(0, patchname)
2475 self.fullseries.insert(0, patchname)
2475
2476
2476 with self.opener(patchname, b"w") as fp:
2477 with self.opener(patchname, b"w") as fp:
2477 cmdutil.exportfile(repo, [n], fp, opts=diffopts)
2478 cmdutil.exportfile(repo, [n], fp, opts=diffopts)
2478
2479
2479 se = statusentry(n, patchname)
2480 se = statusentry(n, patchname)
2480 self.applied.insert(0, se)
2481 self.applied.insert(0, se)
2481
2482
2482 self.added.append(patchname)
2483 self.added.append(patchname)
2483 imported.append(patchname)
2484 imported.append(patchname)
2484 patchname = None
2485 patchname = None
2485 if rev and repo.ui.configbool(b'mq', b'secret'):
2486 if rev and repo.ui.configbool(b'mq', b'secret'):
2486 # if we added anything with --rev, move the secret root
2487 # if we added anything with --rev, move the secret root
2487 phases.retractboundary(repo, tr, phases.secret, [n])
2488 phases.retractboundary(repo, tr, phases.secret, [n])
2488 self.parseseries()
2489 self.parseseries()
2489 self.applieddirty = True
2490 self.applieddirty = True
2490 self.seriesdirty = True
2491 self.seriesdirty = True
2491
2492
2492 for i, filename in enumerate(files):
2493 for i, filename in enumerate(files):
2493 if existing:
2494 if existing:
2494 if filename == b'-':
2495 if filename == b'-':
2495 raise error.Abort(
2496 raise error.Abort(
2496 _(b'-e is incompatible with import from -')
2497 _(b'-e is incompatible with import from -')
2497 )
2498 )
2498 filename = normname(filename)
2499 filename = normname(filename)
2499 self.checkreservedname(filename)
2500 self.checkreservedname(filename)
2500 if util.url(filename).islocal():
2501 if util.url(filename).islocal():
2501 originpath = self.join(filename)
2502 originpath = self.join(filename)
2502 if not os.path.isfile(originpath):
2503 if not os.path.isfile(originpath):
2503 raise error.Abort(
2504 raise error.Abort(
2504 _(b"patch %s does not exist") % filename
2505 _(b"patch %s does not exist") % filename
2505 )
2506 )
2506
2507
2507 if patchname:
2508 if patchname:
2508 self.checkpatchname(patchname, force)
2509 self.checkpatchname(patchname, force)
2509
2510
2510 self.ui.write(
2511 self.ui.write(
2511 _(b'renaming %s to %s\n') % (filename, patchname)
2512 _(b'renaming %s to %s\n') % (filename, patchname)
2512 )
2513 )
2513 util.rename(originpath, self.join(patchname))
2514 util.rename(originpath, self.join(patchname))
2514 else:
2515 else:
2515 patchname = filename
2516 patchname = filename
2516
2517
2517 else:
2518 else:
2518 if filename == b'-' and not patchname:
2519 if filename == b'-' and not patchname:
2519 raise error.Abort(
2520 raise error.Abort(
2520 _(b'need --name to import a patch from -')
2521 _(b'need --name to import a patch from -')
2521 )
2522 )
2522 elif not patchname:
2523 elif not patchname:
2523 patchname = normname(
2524 patchname = normname(
2524 os.path.basename(filename.rstrip(b'/'))
2525 os.path.basename(filename.rstrip(b'/'))
2525 )
2526 )
2526 self.checkpatchname(patchname, force)
2527 self.checkpatchname(patchname, force)
2527 try:
2528 try:
2528 if filename == b'-':
2529 if filename == b'-':
2529 text = self.ui.fin.read()
2530 text = self.ui.fin.read()
2530 else:
2531 else:
2531 fp = hg.openpath(self.ui, filename)
2532 fp = hg.openpath(self.ui, filename)
2532 text = fp.read()
2533 text = fp.read()
2533 fp.close()
2534 fp.close()
2534 except (OSError, IOError):
2535 except (OSError, IOError):
2535 raise error.Abort(_(b"unable to read file %s") % filename)
2536 raise error.Abort(_(b"unable to read file %s") % filename)
2536 patchf = self.opener(patchname, b"w")
2537 patchf = self.opener(patchname, b"w")
2537 patchf.write(text)
2538 patchf.write(text)
2538 patchf.close()
2539 patchf.close()
2539 if not force:
2540 if not force:
2540 checkseries(patchname)
2541 checkseries(patchname)
2541 if patchname not in self.series:
2542 if patchname not in self.series:
2542 index = self.fullseriesend() + i
2543 index = self.fullseriesend() + i
2543 self.fullseries[index:index] = [patchname]
2544 self.fullseries[index:index] = [patchname]
2544 self.parseseries()
2545 self.parseseries()
2545 self.seriesdirty = True
2546 self.seriesdirty = True
2546 self.ui.warn(_(b"adding %s to series file\n") % patchname)
2547 self.ui.warn(_(b"adding %s to series file\n") % patchname)
2547 self.added.append(patchname)
2548 self.added.append(patchname)
2548 imported.append(patchname)
2549 imported.append(patchname)
2549 patchname = None
2550 patchname = None
2550
2551
2551 self.removeundo(repo)
2552 self.removeundo(repo)
2552 return imported
2553 return imported
2553
2554
2554
2555
2555 def fixkeepchangesopts(ui, opts):
2556 def fixkeepchangesopts(ui, opts):
2556 if (
2557 if (
2557 not ui.configbool(b'mq', b'keepchanges')
2558 not ui.configbool(b'mq', b'keepchanges')
2558 or opts.get(b'force')
2559 or opts.get(b'force')
2559 or opts.get(b'exact')
2560 or opts.get(b'exact')
2560 ):
2561 ):
2561 return opts
2562 return opts
2562 opts = dict(opts)
2563 opts = dict(opts)
2563 opts[b'keep_changes'] = True
2564 opts[b'keep_changes'] = True
2564 return opts
2565 return opts
2565
2566
2566
2567
2567 @command(
2568 @command(
2568 b"qdelete|qremove|qrm",
2569 b"qdelete|qremove|qrm",
2569 [
2570 [
2570 (b'k', b'keep', None, _(b'keep patch file')),
2571 (b'k', b'keep', None, _(b'keep patch file')),
2571 (
2572 (
2572 b'r',
2573 b'r',
2573 b'rev',
2574 b'rev',
2574 [],
2575 [],
2575 _(b'stop managing a revision (DEPRECATED)'),
2576 _(b'stop managing a revision (DEPRECATED)'),
2576 _(b'REV'),
2577 _(b'REV'),
2577 ),
2578 ),
2578 ],
2579 ],
2579 _(b'hg qdelete [-k] [PATCH]...'),
2580 _(b'hg qdelete [-k] [PATCH]...'),
2580 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
2581 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
2581 )
2582 )
2582 def delete(ui, repo, *patches, **opts):
2583 def delete(ui, repo, *patches, **opts):
2583 """remove patches from queue
2584 """remove patches from queue
2584
2585
2585 The patches must not be applied, and at least one patch is required. Exact
2586 The patches must not be applied, and at least one patch is required. Exact
2586 patch identifiers must be given. With -k/--keep, the patch files are
2587 patch identifiers must be given. With -k/--keep, the patch files are
2587 preserved in the patch directory.
2588 preserved in the patch directory.
2588
2589
2589 To stop managing a patch and move it into permanent history,
2590 To stop managing a patch and move it into permanent history,
2590 use the :hg:`qfinish` command."""
2591 use the :hg:`qfinish` command."""
2591 q = repo.mq
2592 q = repo.mq
2592 q.delete(repo, patches, pycompat.byteskwargs(opts))
2593 q.delete(repo, patches, pycompat.byteskwargs(opts))
2593 q.savedirty()
2594 q.savedirty()
2594 return 0
2595 return 0
2595
2596
2596
2597
2597 @command(
2598 @command(
2598 b"qapplied",
2599 b"qapplied",
2599 [(b'1', b'last', None, _(b'show only the preceding applied patch'))]
2600 [(b'1', b'last', None, _(b'show only the preceding applied patch'))]
2600 + seriesopts,
2601 + seriesopts,
2601 _(b'hg qapplied [-1] [-s] [PATCH]'),
2602 _(b'hg qapplied [-1] [-s] [PATCH]'),
2602 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
2603 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
2603 )
2604 )
2604 def applied(ui, repo, patch=None, **opts):
2605 def applied(ui, repo, patch=None, **opts):
2605 """print the patches already applied
2606 """print the patches already applied
2606
2607
2607 Returns 0 on success."""
2608 Returns 0 on success."""
2608
2609
2609 q = repo.mq
2610 q = repo.mq
2610 opts = pycompat.byteskwargs(opts)
2611 opts = pycompat.byteskwargs(opts)
2611
2612
2612 if patch:
2613 if patch:
2613 if patch not in q.series:
2614 if patch not in q.series:
2614 raise error.Abort(_(b"patch %s is not in series file") % patch)
2615 raise error.Abort(_(b"patch %s is not in series file") % patch)
2615 end = q.series.index(patch) + 1
2616 end = q.series.index(patch) + 1
2616 else:
2617 else:
2617 end = q.seriesend(True)
2618 end = q.seriesend(True)
2618
2619
2619 if opts.get(b'last') and not end:
2620 if opts.get(b'last') and not end:
2620 ui.write(_(b"no patches applied\n"))
2621 ui.write(_(b"no patches applied\n"))
2621 return 1
2622 return 1
2622 elif opts.get(b'last') and end == 1:
2623 elif opts.get(b'last') and end == 1:
2623 ui.write(_(b"only one patch applied\n"))
2624 ui.write(_(b"only one patch applied\n"))
2624 return 1
2625 return 1
2625 elif opts.get(b'last'):
2626 elif opts.get(b'last'):
2626 start = end - 2
2627 start = end - 2
2627 end = 1
2628 end = 1
2628 else:
2629 else:
2629 start = 0
2630 start = 0
2630
2631
2631 q.qseries(
2632 q.qseries(
2632 repo, length=end, start=start, status=b'A', summary=opts.get(b'summary')
2633 repo, length=end, start=start, status=b'A', summary=opts.get(b'summary')
2633 )
2634 )
2634
2635
2635
2636
2636 @command(
2637 @command(
2637 b"qunapplied",
2638 b"qunapplied",
2638 [(b'1', b'first', None, _(b'show only the first patch'))] + seriesopts,
2639 [(b'1', b'first', None, _(b'show only the first patch'))] + seriesopts,
2639 _(b'hg qunapplied [-1] [-s] [PATCH]'),
2640 _(b'hg qunapplied [-1] [-s] [PATCH]'),
2640 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
2641 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
2641 )
2642 )
2642 def unapplied(ui, repo, patch=None, **opts):
2643 def unapplied(ui, repo, patch=None, **opts):
2643 """print the patches not yet applied
2644 """print the patches not yet applied
2644
2645
2645 Returns 0 on success."""
2646 Returns 0 on success."""
2646
2647
2647 q = repo.mq
2648 q = repo.mq
2648 opts = pycompat.byteskwargs(opts)
2649 opts = pycompat.byteskwargs(opts)
2649 if patch:
2650 if patch:
2650 if patch not in q.series:
2651 if patch not in q.series:
2651 raise error.Abort(_(b"patch %s is not in series file") % patch)
2652 raise error.Abort(_(b"patch %s is not in series file") % patch)
2652 start = q.series.index(patch) + 1
2653 start = q.series.index(patch) + 1
2653 else:
2654 else:
2654 start = q.seriesend(True)
2655 start = q.seriesend(True)
2655
2656
2656 if start == len(q.series) and opts.get(b'first'):
2657 if start == len(q.series) and opts.get(b'first'):
2657 ui.write(_(b"all patches applied\n"))
2658 ui.write(_(b"all patches applied\n"))
2658 return 1
2659 return 1
2659
2660
2660 if opts.get(b'first'):
2661 if opts.get(b'first'):
2661 length = 1
2662 length = 1
2662 else:
2663 else:
2663 length = None
2664 length = None
2664 q.qseries(
2665 q.qseries(
2665 repo,
2666 repo,
2666 start=start,
2667 start=start,
2667 length=length,
2668 length=length,
2668 status=b'U',
2669 status=b'U',
2669 summary=opts.get(b'summary'),
2670 summary=opts.get(b'summary'),
2670 )
2671 )
2671
2672
2672
2673
2673 @command(
2674 @command(
2674 b"qimport",
2675 b"qimport",
2675 [
2676 [
2676 (b'e', b'existing', None, _(b'import file in patch directory')),
2677 (b'e', b'existing', None, _(b'import file in patch directory')),
2677 (b'n', b'name', b'', _(b'name of patch file'), _(b'NAME')),
2678 (b'n', b'name', b'', _(b'name of patch file'), _(b'NAME')),
2678 (b'f', b'force', None, _(b'overwrite existing files')),
2679 (b'f', b'force', None, _(b'overwrite existing files')),
2679 (
2680 (
2680 b'r',
2681 b'r',
2681 b'rev',
2682 b'rev',
2682 [],
2683 [],
2683 _(b'place existing revisions under mq control'),
2684 _(b'place existing revisions under mq control'),
2684 _(b'REV'),
2685 _(b'REV'),
2685 ),
2686 ),
2686 (b'g', b'git', None, _(b'use git extended diff format')),
2687 (b'g', b'git', None, _(b'use git extended diff format')),
2687 (b'P', b'push', None, _(b'qpush after importing')),
2688 (b'P', b'push', None, _(b'qpush after importing')),
2688 ],
2689 ],
2689 _(b'hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'),
2690 _(b'hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'),
2690 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2691 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2691 )
2692 )
2692 def qimport(ui, repo, *filename, **opts):
2693 def qimport(ui, repo, *filename, **opts):
2693 """import a patch or existing changeset
2694 """import a patch or existing changeset
2694
2695
2695 The patch is inserted into the series after the last applied
2696 The patch is inserted into the series after the last applied
2696 patch. If no patches have been applied, qimport prepends the patch
2697 patch. If no patches have been applied, qimport prepends the patch
2697 to the series.
2698 to the series.
2698
2699
2699 The patch will have the same name as its source file unless you
2700 The patch will have the same name as its source file unless you
2700 give it a new one with -n/--name.
2701 give it a new one with -n/--name.
2701
2702
2702 You can register an existing patch inside the patch directory with
2703 You can register an existing patch inside the patch directory with
2703 the -e/--existing flag.
2704 the -e/--existing flag.
2704
2705
2705 With -f/--force, an existing patch of the same name will be
2706 With -f/--force, an existing patch of the same name will be
2706 overwritten.
2707 overwritten.
2707
2708
2708 An existing changeset may be placed under mq control with -r/--rev
2709 An existing changeset may be placed under mq control with -r/--rev
2709 (e.g. qimport --rev . -n patch will place the current revision
2710 (e.g. qimport --rev . -n patch will place the current revision
2710 under mq control). With -g/--git, patches imported with --rev will
2711 under mq control). With -g/--git, patches imported with --rev will
2711 use the git diff format. See the diffs help topic for information
2712 use the git diff format. See the diffs help topic for information
2712 on why this is important for preserving rename/copy information
2713 on why this is important for preserving rename/copy information
2713 and permission changes. Use :hg:`qfinish` to remove changesets
2714 and permission changes. Use :hg:`qfinish` to remove changesets
2714 from mq control.
2715 from mq control.
2715
2716
2716 To import a patch from standard input, pass - as the patch file.
2717 To import a patch from standard input, pass - as the patch file.
2717 When importing from standard input, a patch name must be specified
2718 When importing from standard input, a patch name must be specified
2718 using the --name flag.
2719 using the --name flag.
2719
2720
2720 To import an existing patch while renaming it::
2721 To import an existing patch while renaming it::
2721
2722
2722 hg qimport -e existing-patch -n new-name
2723 hg qimport -e existing-patch -n new-name
2723
2724
2724 Returns 0 if import succeeded.
2725 Returns 0 if import succeeded.
2725 """
2726 """
2726 opts = pycompat.byteskwargs(opts)
2727 opts = pycompat.byteskwargs(opts)
2727 with repo.lock(): # cause this may move phase
2728 with repo.lock(): # cause this may move phase
2728 q = repo.mq
2729 q = repo.mq
2729 try:
2730 try:
2730 imported = q.qimport(
2731 imported = q.qimport(
2731 repo,
2732 repo,
2732 filename,
2733 filename,
2733 patchname=opts.get(b'name'),
2734 patchname=opts.get(b'name'),
2734 existing=opts.get(b'existing'),
2735 existing=opts.get(b'existing'),
2735 force=opts.get(b'force'),
2736 force=opts.get(b'force'),
2736 rev=opts.get(b'rev'),
2737 rev=opts.get(b'rev'),
2737 git=opts.get(b'git'),
2738 git=opts.get(b'git'),
2738 )
2739 )
2739 finally:
2740 finally:
2740 q.savedirty()
2741 q.savedirty()
2741
2742
2742 if imported and opts.get(b'push') and not opts.get(b'rev'):
2743 if imported and opts.get(b'push') and not opts.get(b'rev'):
2743 return q.push(repo, imported[-1])
2744 return q.push(repo, imported[-1])
2744 return 0
2745 return 0
2745
2746
2746
2747
2747 def qinit(ui, repo, create):
2748 def qinit(ui, repo, create):
2748 """initialize a new queue repository
2749 """initialize a new queue repository
2749
2750
2750 This command also creates a series file for ordering patches, and
2751 This command also creates a series file for ordering patches, and
2751 an mq-specific .hgignore file in the queue repository, to exclude
2752 an mq-specific .hgignore file in the queue repository, to exclude
2752 the status and guards files (these contain mostly transient state).
2753 the status and guards files (these contain mostly transient state).
2753
2754
2754 Returns 0 if initialization succeeded."""
2755 Returns 0 if initialization succeeded."""
2755 q = repo.mq
2756 q = repo.mq
2756 r = q.init(repo, create)
2757 r = q.init(repo, create)
2757 q.savedirty()
2758 q.savedirty()
2758 if r:
2759 if r:
2759 if not os.path.exists(r.wjoin(b'.hgignore')):
2760 if not os.path.exists(r.wjoin(b'.hgignore')):
2760 fp = r.wvfs(b'.hgignore', b'w')
2761 fp = r.wvfs(b'.hgignore', b'w')
2761 fp.write(b'^\\.hg\n')
2762 fp.write(b'^\\.hg\n')
2762 fp.write(b'^\\.mq\n')
2763 fp.write(b'^\\.mq\n')
2763 fp.write(b'syntax: glob\n')
2764 fp.write(b'syntax: glob\n')
2764 fp.write(b'status\n')
2765 fp.write(b'status\n')
2765 fp.write(b'guards\n')
2766 fp.write(b'guards\n')
2766 fp.close()
2767 fp.close()
2767 if not os.path.exists(r.wjoin(b'series')):
2768 if not os.path.exists(r.wjoin(b'series')):
2768 r.wvfs(b'series', b'w').close()
2769 r.wvfs(b'series', b'w').close()
2769 r[None].add([b'.hgignore', b'series'])
2770 r[None].add([b'.hgignore', b'series'])
2770 commands.add(ui, r)
2771 commands.add(ui, r)
2771 return 0
2772 return 0
2772
2773
2773
2774
2774 @command(
2775 @command(
2775 b"qinit",
2776 b"qinit",
2776 [(b'c', b'create-repo', None, _(b'create queue repository'))],
2777 [(b'c', b'create-repo', None, _(b'create queue repository'))],
2777 _(b'hg qinit [-c]'),
2778 _(b'hg qinit [-c]'),
2778 helpcategory=command.CATEGORY_REPO_CREATION,
2779 helpcategory=command.CATEGORY_REPO_CREATION,
2779 helpbasic=True,
2780 helpbasic=True,
2780 )
2781 )
2781 def init(ui, repo, **opts):
2782 def init(ui, repo, **opts):
2782 """init a new queue repository (DEPRECATED)
2783 """init a new queue repository (DEPRECATED)
2783
2784
2784 The queue repository is unversioned by default. If
2785 The queue repository is unversioned by default. If
2785 -c/--create-repo is specified, qinit will create a separate nested
2786 -c/--create-repo is specified, qinit will create a separate nested
2786 repository for patches (qinit -c may also be run later to convert
2787 repository for patches (qinit -c may also be run later to convert
2787 an unversioned patch repository into a versioned one). You can use
2788 an unversioned patch repository into a versioned one). You can use
2788 qcommit to commit changes to this queue repository.
2789 qcommit to commit changes to this queue repository.
2789
2790
2790 This command is deprecated. Without -c, it's implied by other relevant
2791 This command is deprecated. Without -c, it's implied by other relevant
2791 commands. With -c, use :hg:`init --mq` instead."""
2792 commands. With -c, use :hg:`init --mq` instead."""
2792 return qinit(ui, repo, create=opts.get('create_repo'))
2793 return qinit(ui, repo, create=opts.get('create_repo'))
2793
2794
2794
2795
2795 @command(
2796 @command(
2796 b"qclone",
2797 b"qclone",
2797 [
2798 [
2798 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
2799 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
2799 (
2800 (
2800 b'U',
2801 b'U',
2801 b'noupdate',
2802 b'noupdate',
2802 None,
2803 None,
2803 _(b'do not update the new working directories'),
2804 _(b'do not update the new working directories'),
2804 ),
2805 ),
2805 (
2806 (
2806 b'',
2807 b'',
2807 b'uncompressed',
2808 b'uncompressed',
2808 None,
2809 None,
2809 _(b'use uncompressed transfer (fast over LAN)'),
2810 _(b'use uncompressed transfer (fast over LAN)'),
2810 ),
2811 ),
2811 (
2812 (
2812 b'p',
2813 b'p',
2813 b'patches',
2814 b'patches',
2814 b'',
2815 b'',
2815 _(b'location of source patch repository'),
2816 _(b'location of source patch repository'),
2816 _(b'REPO'),
2817 _(b'REPO'),
2817 ),
2818 ),
2818 ]
2819 ]
2819 + cmdutil.remoteopts,
2820 + cmdutil.remoteopts,
2820 _(b'hg qclone [OPTION]... SOURCE [DEST]'),
2821 _(b'hg qclone [OPTION]... SOURCE [DEST]'),
2821 helpcategory=command.CATEGORY_REPO_CREATION,
2822 helpcategory=command.CATEGORY_REPO_CREATION,
2822 norepo=True,
2823 norepo=True,
2823 )
2824 )
2824 def clone(ui, source, dest=None, **opts):
2825 def clone(ui, source, dest=None, **opts):
2825 '''clone main and patch repository at same time
2826 '''clone main and patch repository at same time
2826
2827
2827 If source is local, destination will have no patches applied. If
2828 If source is local, destination will have no patches applied. If
2828 source is remote, this command can not check if patches are
2829 source is remote, this command can not check if patches are
2829 applied in source, so cannot guarantee that patches are not
2830 applied in source, so cannot guarantee that patches are not
2830 applied in destination. If you clone remote repository, be sure
2831 applied in destination. If you clone remote repository, be sure
2831 before that it has no patches applied.
2832 before that it has no patches applied.
2832
2833
2833 Source patch repository is looked for in <src>/.hg/patches by
2834 Source patch repository is looked for in <src>/.hg/patches by
2834 default. Use -p <url> to change.
2835 default. Use -p <url> to change.
2835
2836
2836 The patch directory must be a nested Mercurial repository, as
2837 The patch directory must be a nested Mercurial repository, as
2837 would be created by :hg:`init --mq`.
2838 would be created by :hg:`init --mq`.
2838
2839
2839 Return 0 on success.
2840 Return 0 on success.
2840 '''
2841 '''
2841 opts = pycompat.byteskwargs(opts)
2842 opts = pycompat.byteskwargs(opts)
2842
2843
2843 def patchdir(repo):
2844 def patchdir(repo):
2844 """compute a patch repo url from a repo object"""
2845 """compute a patch repo url from a repo object"""
2845 url = repo.url()
2846 url = repo.url()
2846 if url.endswith(b'/'):
2847 if url.endswith(b'/'):
2847 url = url[:-1]
2848 url = url[:-1]
2848 return url + b'/.hg/patches'
2849 return url + b'/.hg/patches'
2849
2850
2850 # main repo (destination and sources)
2851 # main repo (destination and sources)
2851 if dest is None:
2852 if dest is None:
2852 dest = hg.defaultdest(source)
2853 dest = hg.defaultdest(source)
2853 sr = hg.peer(ui, opts, ui.expandpath(source))
2854 sr = hg.peer(ui, opts, ui.expandpath(source))
2854
2855
2855 # patches repo (source only)
2856 # patches repo (source only)
2856 if opts.get(b'patches'):
2857 if opts.get(b'patches'):
2857 patchespath = ui.expandpath(opts.get(b'patches'))
2858 patchespath = ui.expandpath(opts.get(b'patches'))
2858 else:
2859 else:
2859 patchespath = patchdir(sr)
2860 patchespath = patchdir(sr)
2860 try:
2861 try:
2861 hg.peer(ui, opts, patchespath)
2862 hg.peer(ui, opts, patchespath)
2862 except error.RepoError:
2863 except error.RepoError:
2863 raise error.Abort(
2864 raise error.Abort(
2864 _(b'versioned patch repository not found (see init --mq)')
2865 _(b'versioned patch repository not found (see init --mq)')
2865 )
2866 )
2866 qbase, destrev = None, None
2867 qbase, destrev = None, None
2867 if sr.local():
2868 if sr.local():
2868 repo = sr.local()
2869 repo = sr.local()
2869 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2870 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2870 qbase = repo.mq.applied[0].node
2871 qbase = repo.mq.applied[0].node
2871 if not hg.islocal(dest):
2872 if not hg.islocal(dest):
2872 heads = set(repo.heads())
2873 heads = set(repo.heads())
2873 destrev = list(heads.difference(repo.heads(qbase)))
2874 destrev = list(heads.difference(repo.heads(qbase)))
2874 destrev.append(repo.changelog.parents(qbase)[0])
2875 destrev.append(repo.changelog.parents(qbase)[0])
2875 elif sr.capable(b'lookup'):
2876 elif sr.capable(b'lookup'):
2876 try:
2877 try:
2877 qbase = sr.lookup(b'qbase')
2878 qbase = sr.lookup(b'qbase')
2878 except error.RepoError:
2879 except error.RepoError:
2879 pass
2880 pass
2880
2881
2881 ui.note(_(b'cloning main repository\n'))
2882 ui.note(_(b'cloning main repository\n'))
2882 sr, dr = hg.clone(
2883 sr, dr = hg.clone(
2883 ui,
2884 ui,
2884 opts,
2885 opts,
2885 sr.url(),
2886 sr.url(),
2886 dest,
2887 dest,
2887 pull=opts.get(b'pull'),
2888 pull=opts.get(b'pull'),
2888 revs=destrev,
2889 revs=destrev,
2889 update=False,
2890 update=False,
2890 stream=opts.get(b'uncompressed'),
2891 stream=opts.get(b'uncompressed'),
2891 )
2892 )
2892
2893
2893 ui.note(_(b'cloning patch repository\n'))
2894 ui.note(_(b'cloning patch repository\n'))
2894 hg.clone(
2895 hg.clone(
2895 ui,
2896 ui,
2896 opts,
2897 opts,
2897 opts.get(b'patches') or patchdir(sr),
2898 opts.get(b'patches') or patchdir(sr),
2898 patchdir(dr),
2899 patchdir(dr),
2899 pull=opts.get(b'pull'),
2900 pull=opts.get(b'pull'),
2900 update=not opts.get(b'noupdate'),
2901 update=not opts.get(b'noupdate'),
2901 stream=opts.get(b'uncompressed'),
2902 stream=opts.get(b'uncompressed'),
2902 )
2903 )
2903
2904
2904 if dr.local():
2905 if dr.local():
2905 repo = dr.local()
2906 repo = dr.local()
2906 if qbase:
2907 if qbase:
2907 ui.note(
2908 ui.note(
2908 _(
2909 _(
2909 b'stripping applied patches from destination '
2910 b'stripping applied patches from destination '
2910 b'repository\n'
2911 b'repository\n'
2911 )
2912 )
2912 )
2913 )
2913 strip(ui, repo, [qbase], update=False, backup=None)
2914 strip(ui, repo, [qbase], update=False, backup=None)
2914 if not opts.get(b'noupdate'):
2915 if not opts.get(b'noupdate'):
2915 ui.note(_(b'updating destination repository\n'))
2916 ui.note(_(b'updating destination repository\n'))
2916 hg.update(repo, repo.changelog.tip())
2917 hg.update(repo, repo.changelog.tip())
2917
2918
2918
2919
2919 @command(
2920 @command(
2920 b"qcommit|qci",
2921 b"qcommit|qci",
2921 commands.table[b"commit|ci"][1],
2922 commands.table[b"commit|ci"][1],
2922 _(b'hg qcommit [OPTION]... [FILE]...'),
2923 _(b'hg qcommit [OPTION]... [FILE]...'),
2923 helpcategory=command.CATEGORY_COMMITTING,
2924 helpcategory=command.CATEGORY_COMMITTING,
2924 inferrepo=True,
2925 inferrepo=True,
2925 )
2926 )
2926 def commit(ui, repo, *pats, **opts):
2927 def commit(ui, repo, *pats, **opts):
2927 """commit changes in the queue repository (DEPRECATED)
2928 """commit changes in the queue repository (DEPRECATED)
2928
2929
2929 This command is deprecated; use :hg:`commit --mq` instead."""
2930 This command is deprecated; use :hg:`commit --mq` instead."""
2930 q = repo.mq
2931 q = repo.mq
2931 r = q.qrepo()
2932 r = q.qrepo()
2932 if not r:
2933 if not r:
2933 raise error.Abort(b'no queue repository')
2934 raise error.Abort(b'no queue repository')
2934 commands.commit(r.ui, r, *pats, **opts)
2935 commands.commit(r.ui, r, *pats, **opts)
2935
2936
2936
2937
2937 @command(
2938 @command(
2938 b"qseries",
2939 b"qseries",
2939 [(b'm', b'missing', None, _(b'print patches not in series')),] + seriesopts,
2940 [(b'm', b'missing', None, _(b'print patches not in series')),] + seriesopts,
2940 _(b'hg qseries [-ms]'),
2941 _(b'hg qseries [-ms]'),
2941 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
2942 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
2942 )
2943 )
2943 def series(ui, repo, **opts):
2944 def series(ui, repo, **opts):
2944 """print the entire series file
2945 """print the entire series file
2945
2946
2946 Returns 0 on success."""
2947 Returns 0 on success."""
2947 repo.mq.qseries(
2948 repo.mq.qseries(
2948 repo, missing=opts.get('missing'), summary=opts.get('summary')
2949 repo, missing=opts.get('missing'), summary=opts.get('summary')
2949 )
2950 )
2950 return 0
2951 return 0
2951
2952
2952
2953
2953 @command(
2954 @command(
2954 b"qtop",
2955 b"qtop",
2955 seriesopts,
2956 seriesopts,
2956 _(b'hg qtop [-s]'),
2957 _(b'hg qtop [-s]'),
2957 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
2958 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
2958 )
2959 )
2959 def top(ui, repo, **opts):
2960 def top(ui, repo, **opts):
2960 """print the name of the current patch
2961 """print the name of the current patch
2961
2962
2962 Returns 0 on success."""
2963 Returns 0 on success."""
2963 q = repo.mq
2964 q = repo.mq
2964 if q.applied:
2965 if q.applied:
2965 t = q.seriesend(True)
2966 t = q.seriesend(True)
2966 else:
2967 else:
2967 t = 0
2968 t = 0
2968
2969
2969 if t:
2970 if t:
2970 q.qseries(
2971 q.qseries(
2971 repo,
2972 repo,
2972 start=t - 1,
2973 start=t - 1,
2973 length=1,
2974 length=1,
2974 status=b'A',
2975 status=b'A',
2975 summary=opts.get('summary'),
2976 summary=opts.get('summary'),
2976 )
2977 )
2977 else:
2978 else:
2978 ui.write(_(b"no patches applied\n"))
2979 ui.write(_(b"no patches applied\n"))
2979 return 1
2980 return 1
2980
2981
2981
2982
2982 @command(
2983 @command(
2983 b"qnext",
2984 b"qnext",
2984 seriesopts,
2985 seriesopts,
2985 _(b'hg qnext [-s]'),
2986 _(b'hg qnext [-s]'),
2986 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
2987 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
2987 )
2988 )
2988 def next(ui, repo, **opts):
2989 def next(ui, repo, **opts):
2989 """print the name of the next pushable patch
2990 """print the name of the next pushable patch
2990
2991
2991 Returns 0 on success."""
2992 Returns 0 on success."""
2992 q = repo.mq
2993 q = repo.mq
2993 end = q.seriesend()
2994 end = q.seriesend()
2994 if end == len(q.series):
2995 if end == len(q.series):
2995 ui.write(_(b"all patches applied\n"))
2996 ui.write(_(b"all patches applied\n"))
2996 return 1
2997 return 1
2997 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2998 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2998
2999
2999
3000
3000 @command(
3001 @command(
3001 b"qprev",
3002 b"qprev",
3002 seriesopts,
3003 seriesopts,
3003 _(b'hg qprev [-s]'),
3004 _(b'hg qprev [-s]'),
3004 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3005 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3005 )
3006 )
3006 def prev(ui, repo, **opts):
3007 def prev(ui, repo, **opts):
3007 """print the name of the preceding applied patch
3008 """print the name of the preceding applied patch
3008
3009
3009 Returns 0 on success."""
3010 Returns 0 on success."""
3010 q = repo.mq
3011 q = repo.mq
3011 l = len(q.applied)
3012 l = len(q.applied)
3012 if l == 1:
3013 if l == 1:
3013 ui.write(_(b"only one patch applied\n"))
3014 ui.write(_(b"only one patch applied\n"))
3014 return 1
3015 return 1
3015 if not l:
3016 if not l:
3016 ui.write(_(b"no patches applied\n"))
3017 ui.write(_(b"no patches applied\n"))
3017 return 1
3018 return 1
3018 idx = q.series.index(q.applied[-2].name)
3019 idx = q.series.index(q.applied[-2].name)
3019 q.qseries(
3020 q.qseries(
3020 repo, start=idx, length=1, status=b'A', summary=opts.get('summary')
3021 repo, start=idx, length=1, status=b'A', summary=opts.get('summary')
3021 )
3022 )
3022
3023
3023
3024
3024 def setupheaderopts(ui, opts):
3025 def setupheaderopts(ui, opts):
3025 if not opts.get(b'user') and opts.get(b'currentuser'):
3026 if not opts.get(b'user') and opts.get(b'currentuser'):
3026 opts[b'user'] = ui.username()
3027 opts[b'user'] = ui.username()
3027 if not opts.get(b'date') and opts.get(b'currentdate'):
3028 if not opts.get(b'date') and opts.get(b'currentdate'):
3028 opts[b'date'] = b"%d %d" % dateutil.makedate()
3029 opts[b'date'] = b"%d %d" % dateutil.makedate()
3029
3030
3030
3031
3031 @command(
3032 @command(
3032 b"qnew",
3033 b"qnew",
3033 [
3034 [
3034 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
3035 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
3035 (b'f', b'force', None, _(b'import uncommitted changes (DEPRECATED)')),
3036 (b'f', b'force', None, _(b'import uncommitted changes (DEPRECATED)')),
3036 (b'g', b'git', None, _(b'use git extended diff format')),
3037 (b'g', b'git', None, _(b'use git extended diff format')),
3037 (b'U', b'currentuser', None, _(b'add "From: <current user>" to patch')),
3038 (b'U', b'currentuser', None, _(b'add "From: <current user>" to patch')),
3038 (b'u', b'user', b'', _(b'add "From: <USER>" to patch'), _(b'USER')),
3039 (b'u', b'user', b'', _(b'add "From: <USER>" to patch'), _(b'USER')),
3039 (b'D', b'currentdate', None, _(b'add "Date: <current date>" to patch')),
3040 (b'D', b'currentdate', None, _(b'add "Date: <current date>" to patch')),
3040 (b'd', b'date', b'', _(b'add "Date: <DATE>" to patch'), _(b'DATE')),
3041 (b'd', b'date', b'', _(b'add "Date: <DATE>" to patch'), _(b'DATE')),
3041 ]
3042 ]
3042 + cmdutil.walkopts
3043 + cmdutil.walkopts
3043 + cmdutil.commitopts,
3044 + cmdutil.commitopts,
3044 _(b'hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'),
3045 _(b'hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'),
3045 helpcategory=command.CATEGORY_COMMITTING,
3046 helpcategory=command.CATEGORY_COMMITTING,
3046 helpbasic=True,
3047 helpbasic=True,
3047 inferrepo=True,
3048 inferrepo=True,
3048 )
3049 )
3049 def new(ui, repo, patch, *args, **opts):
3050 def new(ui, repo, patch, *args, **opts):
3050 """create a new patch
3051 """create a new patch
3051
3052
3052 qnew creates a new patch on top of the currently-applied patch (if
3053 qnew creates a new patch on top of the currently-applied patch (if
3053 any). The patch will be initialized with any outstanding changes
3054 any). The patch will be initialized with any outstanding changes
3054 in the working directory. You may also use -I/--include,
3055 in the working directory. You may also use -I/--include,
3055 -X/--exclude, and/or a list of files after the patch name to add
3056 -X/--exclude, and/or a list of files after the patch name to add
3056 only changes to matching files to the new patch, leaving the rest
3057 only changes to matching files to the new patch, leaving the rest
3057 as uncommitted modifications.
3058 as uncommitted modifications.
3058
3059
3059 -u/--user and -d/--date can be used to set the (given) user and
3060 -u/--user and -d/--date can be used to set the (given) user and
3060 date, respectively. -U/--currentuser and -D/--currentdate set user
3061 date, respectively. -U/--currentuser and -D/--currentdate set user
3061 to current user and date to current date.
3062 to current user and date to current date.
3062
3063
3063 -e/--edit, -m/--message or -l/--logfile set the patch header as
3064 -e/--edit, -m/--message or -l/--logfile set the patch header as
3064 well as the commit message. If none is specified, the header is
3065 well as the commit message. If none is specified, the header is
3065 empty and the commit message is '[mq]: PATCH'.
3066 empty and the commit message is '[mq]: PATCH'.
3066
3067
3067 Use the -g/--git option to keep the patch in the git extended diff
3068 Use the -g/--git option to keep the patch in the git extended diff
3068 format. Read the diffs help topic for more information on why this
3069 format. Read the diffs help topic for more information on why this
3069 is important for preserving permission changes and copy/rename
3070 is important for preserving permission changes and copy/rename
3070 information.
3071 information.
3071
3072
3072 Returns 0 on successful creation of a new patch.
3073 Returns 0 on successful creation of a new patch.
3073 """
3074 """
3074 opts = pycompat.byteskwargs(opts)
3075 opts = pycompat.byteskwargs(opts)
3075 msg = cmdutil.logmessage(ui, opts)
3076 msg = cmdutil.logmessage(ui, opts)
3076 q = repo.mq
3077 q = repo.mq
3077 opts[b'msg'] = msg
3078 opts[b'msg'] = msg
3078 setupheaderopts(ui, opts)
3079 setupheaderopts(ui, opts)
3079 q.new(repo, patch, *args, **pycompat.strkwargs(opts))
3080 q.new(repo, patch, *args, **pycompat.strkwargs(opts))
3080 q.savedirty()
3081 q.savedirty()
3081 return 0
3082 return 0
3082
3083
3083
3084
3084 @command(
3085 @command(
3085 b"qrefresh",
3086 b"qrefresh",
3086 [
3087 [
3087 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
3088 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
3088 (b'g', b'git', None, _(b'use git extended diff format')),
3089 (b'g', b'git', None, _(b'use git extended diff format')),
3089 (
3090 (
3090 b's',
3091 b's',
3091 b'short',
3092 b'short',
3092 None,
3093 None,
3093 _(b'refresh only files already in the patch and specified files'),
3094 _(b'refresh only files already in the patch and specified files'),
3094 ),
3095 ),
3095 (
3096 (
3096 b'U',
3097 b'U',
3097 b'currentuser',
3098 b'currentuser',
3098 None,
3099 None,
3099 _(b'add/update author field in patch with current user'),
3100 _(b'add/update author field in patch with current user'),
3100 ),
3101 ),
3101 (
3102 (
3102 b'u',
3103 b'u',
3103 b'user',
3104 b'user',
3104 b'',
3105 b'',
3105 _(b'add/update author field in patch with given user'),
3106 _(b'add/update author field in patch with given user'),
3106 _(b'USER'),
3107 _(b'USER'),
3107 ),
3108 ),
3108 (
3109 (
3109 b'D',
3110 b'D',
3110 b'currentdate',
3111 b'currentdate',
3111 None,
3112 None,
3112 _(b'add/update date field in patch with current date'),
3113 _(b'add/update date field in patch with current date'),
3113 ),
3114 ),
3114 (
3115 (
3115 b'd',
3116 b'd',
3116 b'date',
3117 b'date',
3117 b'',
3118 b'',
3118 _(b'add/update date field in patch with given date'),
3119 _(b'add/update date field in patch with given date'),
3119 _(b'DATE'),
3120 _(b'DATE'),
3120 ),
3121 ),
3121 ]
3122 ]
3122 + cmdutil.walkopts
3123 + cmdutil.walkopts
3123 + cmdutil.commitopts,
3124 + cmdutil.commitopts,
3124 _(b'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'),
3125 _(b'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'),
3125 helpcategory=command.CATEGORY_COMMITTING,
3126 helpcategory=command.CATEGORY_COMMITTING,
3126 helpbasic=True,
3127 helpbasic=True,
3127 inferrepo=True,
3128 inferrepo=True,
3128 )
3129 )
3129 def refresh(ui, repo, *pats, **opts):
3130 def refresh(ui, repo, *pats, **opts):
3130 """update the current patch
3131 """update the current patch
3131
3132
3132 If any file patterns are provided, the refreshed patch will
3133 If any file patterns are provided, the refreshed patch will
3133 contain only the modifications that match those patterns; the
3134 contain only the modifications that match those patterns; the
3134 remaining modifications will remain in the working directory.
3135 remaining modifications will remain in the working directory.
3135
3136
3136 If -s/--short is specified, files currently included in the patch
3137 If -s/--short is specified, files currently included in the patch
3137 will be refreshed just like matched files and remain in the patch.
3138 will be refreshed just like matched files and remain in the patch.
3138
3139
3139 If -e/--edit is specified, Mercurial will start your configured editor for
3140 If -e/--edit is specified, Mercurial will start your configured editor for
3140 you to enter a message. In case qrefresh fails, you will find a backup of
3141 you to enter a message. In case qrefresh fails, you will find a backup of
3141 your message in ``.hg/last-message.txt``.
3142 your message in ``.hg/last-message.txt``.
3142
3143
3143 hg add/remove/copy/rename work as usual, though you might want to
3144 hg add/remove/copy/rename work as usual, though you might want to
3144 use git-style patches (-g/--git or [diff] git=1) to track copies
3145 use git-style patches (-g/--git or [diff] git=1) to track copies
3145 and renames. See the diffs help topic for more information on the
3146 and renames. See the diffs help topic for more information on the
3146 git diff format.
3147 git diff format.
3147
3148
3148 Returns 0 on success.
3149 Returns 0 on success.
3149 """
3150 """
3150 opts = pycompat.byteskwargs(opts)
3151 opts = pycompat.byteskwargs(opts)
3151 q = repo.mq
3152 q = repo.mq
3152 message = cmdutil.logmessage(ui, opts)
3153 message = cmdutil.logmessage(ui, opts)
3153 setupheaderopts(ui, opts)
3154 setupheaderopts(ui, opts)
3154 with repo.wlock():
3155 with repo.wlock():
3155 ret = q.refresh(repo, pats, msg=message, **pycompat.strkwargs(opts))
3156 ret = q.refresh(repo, pats, msg=message, **pycompat.strkwargs(opts))
3156 q.savedirty()
3157 q.savedirty()
3157 return ret
3158 return ret
3158
3159
3159
3160
3160 @command(
3161 @command(
3161 b"qdiff",
3162 b"qdiff",
3162 cmdutil.diffopts + cmdutil.diffopts2 + cmdutil.walkopts,
3163 cmdutil.diffopts + cmdutil.diffopts2 + cmdutil.walkopts,
3163 _(b'hg qdiff [OPTION]... [FILE]...'),
3164 _(b'hg qdiff [OPTION]... [FILE]...'),
3164 helpcategory=command.CATEGORY_FILE_CONTENTS,
3165 helpcategory=command.CATEGORY_FILE_CONTENTS,
3165 helpbasic=True,
3166 helpbasic=True,
3166 inferrepo=True,
3167 inferrepo=True,
3167 )
3168 )
3168 def diff(ui, repo, *pats, **opts):
3169 def diff(ui, repo, *pats, **opts):
3169 """diff of the current patch and subsequent modifications
3170 """diff of the current patch and subsequent modifications
3170
3171
3171 Shows a diff which includes the current patch as well as any
3172 Shows a diff which includes the current patch as well as any
3172 changes which have been made in the working directory since the
3173 changes which have been made in the working directory since the
3173 last refresh (thus showing what the current patch would become
3174 last refresh (thus showing what the current patch would become
3174 after a qrefresh).
3175 after a qrefresh).
3175
3176
3176 Use :hg:`diff` if you only want to see the changes made since the
3177 Use :hg:`diff` if you only want to see the changes made since the
3177 last qrefresh, or :hg:`export qtip` if you want to see changes
3178 last qrefresh, or :hg:`export qtip` if you want to see changes
3178 made by the current patch without including changes made since the
3179 made by the current patch without including changes made since the
3179 qrefresh.
3180 qrefresh.
3180
3181
3181 Returns 0 on success.
3182 Returns 0 on success.
3182 """
3183 """
3183 ui.pager(b'qdiff')
3184 ui.pager(b'qdiff')
3184 repo.mq.diff(repo, pats, pycompat.byteskwargs(opts))
3185 repo.mq.diff(repo, pats, pycompat.byteskwargs(opts))
3185 return 0
3186 return 0
3186
3187
3187
3188
3188 @command(
3189 @command(
3189 b'qfold',
3190 b'qfold',
3190 [
3191 [
3191 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
3192 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
3192 (b'k', b'keep', None, _(b'keep folded patch files')),
3193 (b'k', b'keep', None, _(b'keep folded patch files')),
3193 ]
3194 ]
3194 + cmdutil.commitopts,
3195 + cmdutil.commitopts,
3195 _(b'hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'),
3196 _(b'hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'),
3196 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
3197 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
3197 )
3198 )
3198 def fold(ui, repo, *files, **opts):
3199 def fold(ui, repo, *files, **opts):
3199 """fold the named patches into the current patch
3200 """fold the named patches into the current patch
3200
3201
3201 Patches must not yet be applied. Each patch will be successively
3202 Patches must not yet be applied. Each patch will be successively
3202 applied to the current patch in the order given. If all the
3203 applied to the current patch in the order given. If all the
3203 patches apply successfully, the current patch will be refreshed
3204 patches apply successfully, the current patch will be refreshed
3204 with the new cumulative patch, and the folded patches will be
3205 with the new cumulative patch, and the folded patches will be
3205 deleted. With -k/--keep, the folded patch files will not be
3206 deleted. With -k/--keep, the folded patch files will not be
3206 removed afterwards.
3207 removed afterwards.
3207
3208
3208 The header for each folded patch will be concatenated with the
3209 The header for each folded patch will be concatenated with the
3209 current patch header, separated by a line of ``* * *``.
3210 current patch header, separated by a line of ``* * *``.
3210
3211
3211 Returns 0 on success."""
3212 Returns 0 on success."""
3212 opts = pycompat.byteskwargs(opts)
3213 opts = pycompat.byteskwargs(opts)
3213 q = repo.mq
3214 q = repo.mq
3214 if not files:
3215 if not files:
3215 raise error.Abort(_(b'qfold requires at least one patch name'))
3216 raise error.Abort(_(b'qfold requires at least one patch name'))
3216 if not q.checktoppatch(repo)[0]:
3217 if not q.checktoppatch(repo)[0]:
3217 raise error.Abort(_(b'no patches applied'))
3218 raise error.Abort(_(b'no patches applied'))
3218 q.checklocalchanges(repo)
3219 q.checklocalchanges(repo)
3219
3220
3220 message = cmdutil.logmessage(ui, opts)
3221 message = cmdutil.logmessage(ui, opts)
3221
3222
3222 parent = q.lookup(b'qtip')
3223 parent = q.lookup(b'qtip')
3223 patches = []
3224 patches = []
3224 messages = []
3225 messages = []
3225 for f in files:
3226 for f in files:
3226 p = q.lookup(f)
3227 p = q.lookup(f)
3227 if p in patches or p == parent:
3228 if p in patches or p == parent:
3228 ui.warn(_(b'skipping already folded patch %s\n') % p)
3229 ui.warn(_(b'skipping already folded patch %s\n') % p)
3229 if q.isapplied(p):
3230 if q.isapplied(p):
3230 raise error.Abort(
3231 raise error.Abort(
3231 _(b'qfold cannot fold already applied patch %s') % p
3232 _(b'qfold cannot fold already applied patch %s') % p
3232 )
3233 )
3233 patches.append(p)
3234 patches.append(p)
3234
3235
3235 for p in patches:
3236 for p in patches:
3236 if not message:
3237 if not message:
3237 ph = patchheader(q.join(p), q.plainmode)
3238 ph = patchheader(q.join(p), q.plainmode)
3238 if ph.message:
3239 if ph.message:
3239 messages.append(ph.message)
3240 messages.append(ph.message)
3240 pf = q.join(p)
3241 pf = q.join(p)
3241 (patchsuccess, files, fuzz) = q.patch(repo, pf)
3242 (patchsuccess, files, fuzz) = q.patch(repo, pf)
3242 if not patchsuccess:
3243 if not patchsuccess:
3243 raise error.Abort(_(b'error folding patch %s') % p)
3244 raise error.Abort(_(b'error folding patch %s') % p)
3244
3245
3245 if not message:
3246 if not message:
3246 ph = patchheader(q.join(parent), q.plainmode)
3247 ph = patchheader(q.join(parent), q.plainmode)
3247 message = ph.message
3248 message = ph.message
3248 for msg in messages:
3249 for msg in messages:
3249 if msg:
3250 if msg:
3250 if message:
3251 if message:
3251 message.append(b'* * *')
3252 message.append(b'* * *')
3252 message.extend(msg)
3253 message.extend(msg)
3253 message = b'\n'.join(message)
3254 message = b'\n'.join(message)
3254
3255
3255 diffopts = q.patchopts(q.diffopts(), *patches)
3256 diffopts = q.patchopts(q.diffopts(), *patches)
3256 with repo.wlock():
3257 with repo.wlock():
3257 q.refresh(
3258 q.refresh(
3258 repo,
3259 repo,
3259 msg=message,
3260 msg=message,
3260 git=diffopts.git,
3261 git=diffopts.git,
3261 edit=opts.get(b'edit'),
3262 edit=opts.get(b'edit'),
3262 editform=b'mq.qfold',
3263 editform=b'mq.qfold',
3263 )
3264 )
3264 q.delete(repo, patches, opts)
3265 q.delete(repo, patches, opts)
3265 q.savedirty()
3266 q.savedirty()
3266
3267
3267
3268
3268 @command(
3269 @command(
3269 b"qgoto",
3270 b"qgoto",
3270 [
3271 [
3271 (
3272 (
3272 b'',
3273 b'',
3273 b'keep-changes',
3274 b'keep-changes',
3274 None,
3275 None,
3275 _(b'tolerate non-conflicting local changes'),
3276 _(b'tolerate non-conflicting local changes'),
3276 ),
3277 ),
3277 (b'f', b'force', None, _(b'overwrite any local changes')),
3278 (b'f', b'force', None, _(b'overwrite any local changes')),
3278 (b'', b'no-backup', None, _(b'do not save backup copies of files')),
3279 (b'', b'no-backup', None, _(b'do not save backup copies of files')),
3279 ],
3280 ],
3280 _(b'hg qgoto [OPTION]... PATCH'),
3281 _(b'hg qgoto [OPTION]... PATCH'),
3281 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3282 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3282 )
3283 )
3283 def goto(ui, repo, patch, **opts):
3284 def goto(ui, repo, patch, **opts):
3284 '''push or pop patches until named patch is at top of stack
3285 '''push or pop patches until named patch is at top of stack
3285
3286
3286 Returns 0 on success.'''
3287 Returns 0 on success.'''
3287 opts = pycompat.byteskwargs(opts)
3288 opts = pycompat.byteskwargs(opts)
3288 opts = fixkeepchangesopts(ui, opts)
3289 opts = fixkeepchangesopts(ui, opts)
3289 q = repo.mq
3290 q = repo.mq
3290 patch = q.lookup(patch)
3291 patch = q.lookup(patch)
3291 nobackup = opts.get(b'no_backup')
3292 nobackup = opts.get(b'no_backup')
3292 keepchanges = opts.get(b'keep_changes')
3293 keepchanges = opts.get(b'keep_changes')
3293 if q.isapplied(patch):
3294 if q.isapplied(patch):
3294 ret = q.pop(
3295 ret = q.pop(
3295 repo,
3296 repo,
3296 patch,
3297 patch,
3297 force=opts.get(b'force'),
3298 force=opts.get(b'force'),
3298 nobackup=nobackup,
3299 nobackup=nobackup,
3299 keepchanges=keepchanges,
3300 keepchanges=keepchanges,
3300 )
3301 )
3301 else:
3302 else:
3302 ret = q.push(
3303 ret = q.push(
3303 repo,
3304 repo,
3304 patch,
3305 patch,
3305 force=opts.get(b'force'),
3306 force=opts.get(b'force'),
3306 nobackup=nobackup,
3307 nobackup=nobackup,
3307 keepchanges=keepchanges,
3308 keepchanges=keepchanges,
3308 )
3309 )
3309 q.savedirty()
3310 q.savedirty()
3310 return ret
3311 return ret
3311
3312
3312
3313
3313 @command(
3314 @command(
3314 b"qguard",
3315 b"qguard",
3315 [
3316 [
3316 (b'l', b'list', None, _(b'list all patches and guards')),
3317 (b'l', b'list', None, _(b'list all patches and guards')),
3317 (b'n', b'none', None, _(b'drop all guards')),
3318 (b'n', b'none', None, _(b'drop all guards')),
3318 ],
3319 ],
3319 _(b'hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'),
3320 _(b'hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'),
3320 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3321 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3321 )
3322 )
3322 def guard(ui, repo, *args, **opts):
3323 def guard(ui, repo, *args, **opts):
3323 '''set or print guards for a patch
3324 '''set or print guards for a patch
3324
3325
3325 Guards control whether a patch can be pushed. A patch with no
3326 Guards control whether a patch can be pushed. A patch with no
3326 guards is always pushed. A patch with a positive guard ("+foo") is
3327 guards is always pushed. A patch with a positive guard ("+foo") is
3327 pushed only if the :hg:`qselect` command has activated it. A patch with
3328 pushed only if the :hg:`qselect` command has activated it. A patch with
3328 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
3329 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
3329 has activated it.
3330 has activated it.
3330
3331
3331 With no arguments, print the currently active guards.
3332 With no arguments, print the currently active guards.
3332 With arguments, set guards for the named patch.
3333 With arguments, set guards for the named patch.
3333
3334
3334 .. note::
3335 .. note::
3335
3336
3336 Specifying negative guards now requires '--'.
3337 Specifying negative guards now requires '--'.
3337
3338
3338 To set guards on another patch::
3339 To set guards on another patch::
3339
3340
3340 hg qguard other.patch -- +2.6.17 -stable
3341 hg qguard other.patch -- +2.6.17 -stable
3341
3342
3342 Returns 0 on success.
3343 Returns 0 on success.
3343 '''
3344 '''
3344
3345
3345 def status(idx):
3346 def status(idx):
3346 guards = q.seriesguards[idx] or [b'unguarded']
3347 guards = q.seriesguards[idx] or [b'unguarded']
3347 if q.series[idx] in applied:
3348 if q.series[idx] in applied:
3348 state = b'applied'
3349 state = b'applied'
3349 elif q.pushable(idx)[0]:
3350 elif q.pushable(idx)[0]:
3350 state = b'unapplied'
3351 state = b'unapplied'
3351 else:
3352 else:
3352 state = b'guarded'
3353 state = b'guarded'
3353 label = b'qguard.patch qguard.%s qseries.%s' % (state, state)
3354 label = b'qguard.patch qguard.%s qseries.%s' % (state, state)
3354 ui.write(b'%s: ' % ui.label(q.series[idx], label))
3355 ui.write(b'%s: ' % ui.label(q.series[idx], label))
3355
3356
3356 for i, guard in enumerate(guards):
3357 for i, guard in enumerate(guards):
3357 if guard.startswith(b'+'):
3358 if guard.startswith(b'+'):
3358 ui.write(guard, label=b'qguard.positive')
3359 ui.write(guard, label=b'qguard.positive')
3359 elif guard.startswith(b'-'):
3360 elif guard.startswith(b'-'):
3360 ui.write(guard, label=b'qguard.negative')
3361 ui.write(guard, label=b'qguard.negative')
3361 else:
3362 else:
3362 ui.write(guard, label=b'qguard.unguarded')
3363 ui.write(guard, label=b'qguard.unguarded')
3363 if i != len(guards) - 1:
3364 if i != len(guards) - 1:
3364 ui.write(b' ')
3365 ui.write(b' ')
3365 ui.write(b'\n')
3366 ui.write(b'\n')
3366
3367
3367 q = repo.mq
3368 q = repo.mq
3368 applied = {p.name for p in q.applied}
3369 applied = {p.name for p in q.applied}
3369 patch = None
3370 patch = None
3370 args = list(args)
3371 args = list(args)
3371 if opts.get('list'):
3372 if opts.get('list'):
3372 if args or opts.get('none'):
3373 if args or opts.get('none'):
3373 raise error.Abort(
3374 raise error.Abort(
3374 _(b'cannot mix -l/--list with options or arguments')
3375 _(b'cannot mix -l/--list with options or arguments')
3375 )
3376 )
3376 for i in pycompat.xrange(len(q.series)):
3377 for i in pycompat.xrange(len(q.series)):
3377 status(i)
3378 status(i)
3378 return
3379 return
3379 if not args or args[0][0:1] in b'-+':
3380 if not args or args[0][0:1] in b'-+':
3380 if not q.applied:
3381 if not q.applied:
3381 raise error.Abort(_(b'no patches applied'))
3382 raise error.Abort(_(b'no patches applied'))
3382 patch = q.applied[-1].name
3383 patch = q.applied[-1].name
3383 if patch is None and args[0][0:1] not in b'-+':
3384 if patch is None and args[0][0:1] not in b'-+':
3384 patch = args.pop(0)
3385 patch = args.pop(0)
3385 if patch is None:
3386 if patch is None:
3386 raise error.Abort(_(b'no patch to work with'))
3387 raise error.Abort(_(b'no patch to work with'))
3387 if args or opts.get('none'):
3388 if args or opts.get('none'):
3388 idx = q.findseries(patch)
3389 idx = q.findseries(patch)
3389 if idx is None:
3390 if idx is None:
3390 raise error.Abort(_(b'no patch named %s') % patch)
3391 raise error.Abort(_(b'no patch named %s') % patch)
3391 q.setguards(idx, args)
3392 q.setguards(idx, args)
3392 q.savedirty()
3393 q.savedirty()
3393 else:
3394 else:
3394 status(q.series.index(q.lookup(patch)))
3395 status(q.series.index(q.lookup(patch)))
3395
3396
3396
3397
3397 @command(
3398 @command(
3398 b"qheader",
3399 b"qheader",
3399 [],
3400 [],
3400 _(b'hg qheader [PATCH]'),
3401 _(b'hg qheader [PATCH]'),
3401 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3402 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3402 )
3403 )
3403 def header(ui, repo, patch=None):
3404 def header(ui, repo, patch=None):
3404 """print the header of the topmost or specified patch
3405 """print the header of the topmost or specified patch
3405
3406
3406 Returns 0 on success."""
3407 Returns 0 on success."""
3407 q = repo.mq
3408 q = repo.mq
3408
3409
3409 if patch:
3410 if patch:
3410 patch = q.lookup(patch)
3411 patch = q.lookup(patch)
3411 else:
3412 else:
3412 if not q.applied:
3413 if not q.applied:
3413 ui.write(_(b'no patches applied\n'))
3414 ui.write(_(b'no patches applied\n'))
3414 return 1
3415 return 1
3415 patch = q.lookup(b'qtip')
3416 patch = q.lookup(b'qtip')
3416 ph = patchheader(q.join(patch), q.plainmode)
3417 ph = patchheader(q.join(patch), q.plainmode)
3417
3418
3418 ui.write(b'\n'.join(ph.message) + b'\n')
3419 ui.write(b'\n'.join(ph.message) + b'\n')
3419
3420
3420
3421
3421 def lastsavename(path):
3422 def lastsavename(path):
3422 (directory, base) = os.path.split(path)
3423 (directory, base) = os.path.split(path)
3423 names = os.listdir(directory)
3424 names = os.listdir(directory)
3424 namere = re.compile(b"%s.([0-9]+)" % base)
3425 namere = re.compile(b"%s.([0-9]+)" % base)
3425 maxindex = None
3426 maxindex = None
3426 maxname = None
3427 maxname = None
3427 for f in names:
3428 for f in names:
3428 m = namere.match(f)
3429 m = namere.match(f)
3429 if m:
3430 if m:
3430 index = int(m.group(1))
3431 index = int(m.group(1))
3431 if maxindex is None or index > maxindex:
3432 if maxindex is None or index > maxindex:
3432 maxindex = index
3433 maxindex = index
3433 maxname = f
3434 maxname = f
3434 if maxname:
3435 if maxname:
3435 return (os.path.join(directory, maxname), maxindex)
3436 return (os.path.join(directory, maxname), maxindex)
3436 return (None, None)
3437 return (None, None)
3437
3438
3438
3439
3439 def savename(path):
3440 def savename(path):
3440 (last, index) = lastsavename(path)
3441 (last, index) = lastsavename(path)
3441 if last is None:
3442 if last is None:
3442 index = 0
3443 index = 0
3443 newpath = path + b".%d" % (index + 1)
3444 newpath = path + b".%d" % (index + 1)
3444 return newpath
3445 return newpath
3445
3446
3446
3447
3447 @command(
3448 @command(
3448 b"qpush",
3449 b"qpush",
3449 [
3450 [
3450 (
3451 (
3451 b'',
3452 b'',
3452 b'keep-changes',
3453 b'keep-changes',
3453 None,
3454 None,
3454 _(b'tolerate non-conflicting local changes'),
3455 _(b'tolerate non-conflicting local changes'),
3455 ),
3456 ),
3456 (b'f', b'force', None, _(b'apply on top of local changes')),
3457 (b'f', b'force', None, _(b'apply on top of local changes')),
3457 (
3458 (
3458 b'e',
3459 b'e',
3459 b'exact',
3460 b'exact',
3460 None,
3461 None,
3461 _(b'apply the target patch to its recorded parent'),
3462 _(b'apply the target patch to its recorded parent'),
3462 ),
3463 ),
3463 (b'l', b'list', None, _(b'list patch name in commit text')),
3464 (b'l', b'list', None, _(b'list patch name in commit text')),
3464 (b'a', b'all', None, _(b'apply all patches')),
3465 (b'a', b'all', None, _(b'apply all patches')),
3465 (b'm', b'merge', None, _(b'merge from another queue (DEPRECATED)')),
3466 (b'm', b'merge', None, _(b'merge from another queue (DEPRECATED)')),
3466 (b'n', b'name', b'', _(b'merge queue name (DEPRECATED)'), _(b'NAME')),
3467 (b'n', b'name', b'', _(b'merge queue name (DEPRECATED)'), _(b'NAME')),
3467 (
3468 (
3468 b'',
3469 b'',
3469 b'move',
3470 b'move',
3470 None,
3471 None,
3471 _(b'reorder patch series and apply only the patch'),
3472 _(b'reorder patch series and apply only the patch'),
3472 ),
3473 ),
3473 (b'', b'no-backup', None, _(b'do not save backup copies of files')),
3474 (b'', b'no-backup', None, _(b'do not save backup copies of files')),
3474 ],
3475 ],
3475 _(b'hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'),
3476 _(b'hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'),
3476 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3477 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3477 helpbasic=True,
3478 helpbasic=True,
3478 )
3479 )
3479 def push(ui, repo, patch=None, **opts):
3480 def push(ui, repo, patch=None, **opts):
3480 """push the next patch onto the stack
3481 """push the next patch onto the stack
3481
3482
3482 By default, abort if the working directory contains uncommitted
3483 By default, abort if the working directory contains uncommitted
3483 changes. With --keep-changes, abort only if the uncommitted files
3484 changes. With --keep-changes, abort only if the uncommitted files
3484 overlap with patched files. With -f/--force, backup and patch over
3485 overlap with patched files. With -f/--force, backup and patch over
3485 uncommitted changes.
3486 uncommitted changes.
3486
3487
3487 Return 0 on success.
3488 Return 0 on success.
3488 """
3489 """
3489 q = repo.mq
3490 q = repo.mq
3490 mergeq = None
3491 mergeq = None
3491
3492
3492 opts = pycompat.byteskwargs(opts)
3493 opts = pycompat.byteskwargs(opts)
3493 opts = fixkeepchangesopts(ui, opts)
3494 opts = fixkeepchangesopts(ui, opts)
3494 if opts.get(b'merge'):
3495 if opts.get(b'merge'):
3495 if opts.get(b'name'):
3496 if opts.get(b'name'):
3496 newpath = repo.vfs.join(opts.get(b'name'))
3497 newpath = repo.vfs.join(opts.get(b'name'))
3497 else:
3498 else:
3498 newpath, i = lastsavename(q.path)
3499 newpath, i = lastsavename(q.path)
3499 if not newpath:
3500 if not newpath:
3500 ui.warn(_(b"no saved queues found, please use -n\n"))
3501 ui.warn(_(b"no saved queues found, please use -n\n"))
3501 return 1
3502 return 1
3502 mergeq = queue(ui, repo.baseui, repo.path, newpath)
3503 mergeq = queue(ui, repo.baseui, repo.path, newpath)
3503 ui.warn(_(b"merging with queue at: %s\n") % mergeq.path)
3504 ui.warn(_(b"merging with queue at: %s\n") % mergeq.path)
3504 ret = q.push(
3505 ret = q.push(
3505 repo,
3506 repo,
3506 patch,
3507 patch,
3507 force=opts.get(b'force'),
3508 force=opts.get(b'force'),
3508 list=opts.get(b'list'),
3509 list=opts.get(b'list'),
3509 mergeq=mergeq,
3510 mergeq=mergeq,
3510 all=opts.get(b'all'),
3511 all=opts.get(b'all'),
3511 move=opts.get(b'move'),
3512 move=opts.get(b'move'),
3512 exact=opts.get(b'exact'),
3513 exact=opts.get(b'exact'),
3513 nobackup=opts.get(b'no_backup'),
3514 nobackup=opts.get(b'no_backup'),
3514 keepchanges=opts.get(b'keep_changes'),
3515 keepchanges=opts.get(b'keep_changes'),
3515 )
3516 )
3516 return ret
3517 return ret
3517
3518
3518
3519
3519 @command(
3520 @command(
3520 b"qpop",
3521 b"qpop",
3521 [
3522 [
3522 (b'a', b'all', None, _(b'pop all patches')),
3523 (b'a', b'all', None, _(b'pop all patches')),
3523 (b'n', b'name', b'', _(b'queue name to pop (DEPRECATED)'), _(b'NAME')),
3524 (b'n', b'name', b'', _(b'queue name to pop (DEPRECATED)'), _(b'NAME')),
3524 (
3525 (
3525 b'',
3526 b'',
3526 b'keep-changes',
3527 b'keep-changes',
3527 None,
3528 None,
3528 _(b'tolerate non-conflicting local changes'),
3529 _(b'tolerate non-conflicting local changes'),
3529 ),
3530 ),
3530 (b'f', b'force', None, _(b'forget any local changes to patched files')),
3531 (b'f', b'force', None, _(b'forget any local changes to patched files')),
3531 (b'', b'no-backup', None, _(b'do not save backup copies of files')),
3532 (b'', b'no-backup', None, _(b'do not save backup copies of files')),
3532 ],
3533 ],
3533 _(b'hg qpop [-a] [-f] [PATCH | INDEX]'),
3534 _(b'hg qpop [-a] [-f] [PATCH | INDEX]'),
3534 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3535 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3535 helpbasic=True,
3536 helpbasic=True,
3536 )
3537 )
3537 def pop(ui, repo, patch=None, **opts):
3538 def pop(ui, repo, patch=None, **opts):
3538 """pop the current patch off the stack
3539 """pop the current patch off the stack
3539
3540
3540 Without argument, pops off the top of the patch stack. If given a
3541 Without argument, pops off the top of the patch stack. If given a
3541 patch name, keeps popping off patches until the named patch is at
3542 patch name, keeps popping off patches until the named patch is at
3542 the top of the stack.
3543 the top of the stack.
3543
3544
3544 By default, abort if the working directory contains uncommitted
3545 By default, abort if the working directory contains uncommitted
3545 changes. With --keep-changes, abort only if the uncommitted files
3546 changes. With --keep-changes, abort only if the uncommitted files
3546 overlap with patched files. With -f/--force, backup and discard
3547 overlap with patched files. With -f/--force, backup and discard
3547 changes made to such files.
3548 changes made to such files.
3548
3549
3549 Return 0 on success.
3550 Return 0 on success.
3550 """
3551 """
3551 opts = pycompat.byteskwargs(opts)
3552 opts = pycompat.byteskwargs(opts)
3552 opts = fixkeepchangesopts(ui, opts)
3553 opts = fixkeepchangesopts(ui, opts)
3553 localupdate = True
3554 localupdate = True
3554 if opts.get(b'name'):
3555 if opts.get(b'name'):
3555 q = queue(ui, repo.baseui, repo.path, repo.vfs.join(opts.get(b'name')))
3556 q = queue(ui, repo.baseui, repo.path, repo.vfs.join(opts.get(b'name')))
3556 ui.warn(_(b'using patch queue: %s\n') % q.path)
3557 ui.warn(_(b'using patch queue: %s\n') % q.path)
3557 localupdate = False
3558 localupdate = False
3558 else:
3559 else:
3559 q = repo.mq
3560 q = repo.mq
3560 ret = q.pop(
3561 ret = q.pop(
3561 repo,
3562 repo,
3562 patch,
3563 patch,
3563 force=opts.get(b'force'),
3564 force=opts.get(b'force'),
3564 update=localupdate,
3565 update=localupdate,
3565 all=opts.get(b'all'),
3566 all=opts.get(b'all'),
3566 nobackup=opts.get(b'no_backup'),
3567 nobackup=opts.get(b'no_backup'),
3567 keepchanges=opts.get(b'keep_changes'),
3568 keepchanges=opts.get(b'keep_changes'),
3568 )
3569 )
3569 q.savedirty()
3570 q.savedirty()
3570 return ret
3571 return ret
3571
3572
3572
3573
3573 @command(
3574 @command(
3574 b"qrename|qmv",
3575 b"qrename|qmv",
3575 [],
3576 [],
3576 _(b'hg qrename PATCH1 [PATCH2]'),
3577 _(b'hg qrename PATCH1 [PATCH2]'),
3577 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3578 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3578 )
3579 )
3579 def rename(ui, repo, patch, name=None, **opts):
3580 def rename(ui, repo, patch, name=None, **opts):
3580 """rename a patch
3581 """rename a patch
3581
3582
3582 With one argument, renames the current patch to PATCH1.
3583 With one argument, renames the current patch to PATCH1.
3583 With two arguments, renames PATCH1 to PATCH2.
3584 With two arguments, renames PATCH1 to PATCH2.
3584
3585
3585 Returns 0 on success."""
3586 Returns 0 on success."""
3586 q = repo.mq
3587 q = repo.mq
3587 if not name:
3588 if not name:
3588 name = patch
3589 name = patch
3589 patch = None
3590 patch = None
3590
3591
3591 if patch:
3592 if patch:
3592 patch = q.lookup(patch)
3593 patch = q.lookup(patch)
3593 else:
3594 else:
3594 if not q.applied:
3595 if not q.applied:
3595 ui.write(_(b'no patches applied\n'))
3596 ui.write(_(b'no patches applied\n'))
3596 return
3597 return
3597 patch = q.lookup(b'qtip')
3598 patch = q.lookup(b'qtip')
3598 absdest = q.join(name)
3599 absdest = q.join(name)
3599 if os.path.isdir(absdest):
3600 if os.path.isdir(absdest):
3600 name = normname(os.path.join(name, os.path.basename(patch)))
3601 name = normname(os.path.join(name, os.path.basename(patch)))
3601 absdest = q.join(name)
3602 absdest = q.join(name)
3602 q.checkpatchname(name)
3603 q.checkpatchname(name)
3603
3604
3604 ui.note(_(b'renaming %s to %s\n') % (patch, name))
3605 ui.note(_(b'renaming %s to %s\n') % (patch, name))
3605 i = q.findseries(patch)
3606 i = q.findseries(patch)
3606 guards = q.guard_re.findall(q.fullseries[i])
3607 guards = q.guard_re.findall(q.fullseries[i])
3607 q.fullseries[i] = name + b''.join([b' #' + g for g in guards])
3608 q.fullseries[i] = name + b''.join([b' #' + g for g in guards])
3608 q.parseseries()
3609 q.parseseries()
3609 q.seriesdirty = True
3610 q.seriesdirty = True
3610
3611
3611 info = q.isapplied(patch)
3612 info = q.isapplied(patch)
3612 if info:
3613 if info:
3613 q.applied[info[0]] = statusentry(info[1], name)
3614 q.applied[info[0]] = statusentry(info[1], name)
3614 q.applieddirty = True
3615 q.applieddirty = True
3615
3616
3616 destdir = os.path.dirname(absdest)
3617 destdir = os.path.dirname(absdest)
3617 if not os.path.isdir(destdir):
3618 if not os.path.isdir(destdir):
3618 os.makedirs(destdir)
3619 os.makedirs(destdir)
3619 util.rename(q.join(patch), absdest)
3620 util.rename(q.join(patch), absdest)
3620 r = q.qrepo()
3621 r = q.qrepo()
3621 if r and patch in r.dirstate:
3622 if r and patch in r.dirstate:
3622 wctx = r[None]
3623 wctx = r[None]
3623 with r.wlock():
3624 with r.wlock():
3624 if r.dirstate[patch] == b'a':
3625 if r.dirstate[patch] == b'a':
3625 r.dirstate.drop(patch)
3626 r.dirstate.drop(patch)
3626 r.dirstate.add(name)
3627 r.dirstate.add(name)
3627 else:
3628 else:
3628 wctx.copy(patch, name)
3629 wctx.copy(patch, name)
3629 wctx.forget([patch])
3630 wctx.forget([patch])
3630
3631
3631 q.savedirty()
3632 q.savedirty()
3632
3633
3633
3634
3634 @command(
3635 @command(
3635 b"qrestore",
3636 b"qrestore",
3636 [
3637 [
3637 (b'd', b'delete', None, _(b'delete save entry')),
3638 (b'd', b'delete', None, _(b'delete save entry')),
3638 (b'u', b'update', None, _(b'update queue working directory')),
3639 (b'u', b'update', None, _(b'update queue working directory')),
3639 ],
3640 ],
3640 _(b'hg qrestore [-d] [-u] REV'),
3641 _(b'hg qrestore [-d] [-u] REV'),
3641 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3642 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3642 )
3643 )
3643 def restore(ui, repo, rev, **opts):
3644 def restore(ui, repo, rev, **opts):
3644 """restore the queue state saved by a revision (DEPRECATED)
3645 """restore the queue state saved by a revision (DEPRECATED)
3645
3646
3646 This command is deprecated, use :hg:`rebase` instead."""
3647 This command is deprecated, use :hg:`rebase` instead."""
3647 rev = repo.lookup(rev)
3648 rev = repo.lookup(rev)
3648 q = repo.mq
3649 q = repo.mq
3649 q.restore(repo, rev, delete=opts.get('delete'), qupdate=opts.get('update'))
3650 q.restore(repo, rev, delete=opts.get('delete'), qupdate=opts.get('update'))
3650 q.savedirty()
3651 q.savedirty()
3651 return 0
3652 return 0
3652
3653
3653
3654
3654 @command(
3655 @command(
3655 b"qsave",
3656 b"qsave",
3656 [
3657 [
3657 (b'c', b'copy', None, _(b'copy patch directory')),
3658 (b'c', b'copy', None, _(b'copy patch directory')),
3658 (b'n', b'name', b'', _(b'copy directory name'), _(b'NAME')),
3659 (b'n', b'name', b'', _(b'copy directory name'), _(b'NAME')),
3659 (b'e', b'empty', None, _(b'clear queue status file')),
3660 (b'e', b'empty', None, _(b'clear queue status file')),
3660 (b'f', b'force', None, _(b'force copy')),
3661 (b'f', b'force', None, _(b'force copy')),
3661 ]
3662 ]
3662 + cmdutil.commitopts,
3663 + cmdutil.commitopts,
3663 _(b'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
3664 _(b'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
3664 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3665 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3665 )
3666 )
3666 def save(ui, repo, **opts):
3667 def save(ui, repo, **opts):
3667 """save current queue state (DEPRECATED)
3668 """save current queue state (DEPRECATED)
3668
3669
3669 This command is deprecated, use :hg:`rebase` instead."""
3670 This command is deprecated, use :hg:`rebase` instead."""
3670 q = repo.mq
3671 q = repo.mq
3671 opts = pycompat.byteskwargs(opts)
3672 opts = pycompat.byteskwargs(opts)
3672 message = cmdutil.logmessage(ui, opts)
3673 message = cmdutil.logmessage(ui, opts)
3673 ret = q.save(repo, msg=message)
3674 ret = q.save(repo, msg=message)
3674 if ret:
3675 if ret:
3675 return ret
3676 return ret
3676 q.savedirty() # save to .hg/patches before copying
3677 q.savedirty() # save to .hg/patches before copying
3677 if opts.get(b'copy'):
3678 if opts.get(b'copy'):
3678 path = q.path
3679 path = q.path
3679 if opts.get(b'name'):
3680 if opts.get(b'name'):
3680 newpath = os.path.join(q.basepath, opts.get(b'name'))
3681 newpath = os.path.join(q.basepath, opts.get(b'name'))
3681 if os.path.exists(newpath):
3682 if os.path.exists(newpath):
3682 if not os.path.isdir(newpath):
3683 if not os.path.isdir(newpath):
3683 raise error.Abort(
3684 raise error.Abort(
3684 _(b'destination %s exists and is not a directory')
3685 _(b'destination %s exists and is not a directory')
3685 % newpath
3686 % newpath
3686 )
3687 )
3687 if not opts.get(b'force'):
3688 if not opts.get(b'force'):
3688 raise error.Abort(
3689 raise error.Abort(
3689 _(b'destination %s exists, use -f to force') % newpath
3690 _(b'destination %s exists, use -f to force') % newpath
3690 )
3691 )
3691 else:
3692 else:
3692 newpath = savename(path)
3693 newpath = savename(path)
3693 ui.warn(_(b"copy %s to %s\n") % (path, newpath))
3694 ui.warn(_(b"copy %s to %s\n") % (path, newpath))
3694 util.copyfiles(path, newpath)
3695 util.copyfiles(path, newpath)
3695 if opts.get(b'empty'):
3696 if opts.get(b'empty'):
3696 del q.applied[:]
3697 del q.applied[:]
3697 q.applieddirty = True
3698 q.applieddirty = True
3698 q.savedirty()
3699 q.savedirty()
3699 return 0
3700 return 0
3700
3701
3701
3702
3702 @command(
3703 @command(
3703 b"qselect",
3704 b"qselect",
3704 [
3705 [
3705 (b'n', b'none', None, _(b'disable all guards')),
3706 (b'n', b'none', None, _(b'disable all guards')),
3706 (b's', b'series', None, _(b'list all guards in series file')),
3707 (b's', b'series', None, _(b'list all guards in series file')),
3707 (b'', b'pop', None, _(b'pop to before first guarded applied patch')),
3708 (b'', b'pop', None, _(b'pop to before first guarded applied patch')),
3708 (b'', b'reapply', None, _(b'pop, then reapply patches')),
3709 (b'', b'reapply', None, _(b'pop, then reapply patches')),
3709 ],
3710 ],
3710 _(b'hg qselect [OPTION]... [GUARD]...'),
3711 _(b'hg qselect [OPTION]... [GUARD]...'),
3711 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3712 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3712 )
3713 )
3713 def select(ui, repo, *args, **opts):
3714 def select(ui, repo, *args, **opts):
3714 '''set or print guarded patches to push
3715 '''set or print guarded patches to push
3715
3716
3716 Use the :hg:`qguard` command to set or print guards on patch, then use
3717 Use the :hg:`qguard` command to set or print guards on patch, then use
3717 qselect to tell mq which guards to use. A patch will be pushed if
3718 qselect to tell mq which guards to use. A patch will be pushed if
3718 it has no guards or any positive guards match the currently
3719 it has no guards or any positive guards match the currently
3719 selected guard, but will not be pushed if any negative guards
3720 selected guard, but will not be pushed if any negative guards
3720 match the current guard. For example::
3721 match the current guard. For example::
3721
3722
3722 qguard foo.patch -- -stable (negative guard)
3723 qguard foo.patch -- -stable (negative guard)
3723 qguard bar.patch +stable (positive guard)
3724 qguard bar.patch +stable (positive guard)
3724 qselect stable
3725 qselect stable
3725
3726
3726 This activates the "stable" guard. mq will skip foo.patch (because
3727 This activates the "stable" guard. mq will skip foo.patch (because
3727 it has a negative match) but push bar.patch (because it has a
3728 it has a negative match) but push bar.patch (because it has a
3728 positive match).
3729 positive match).
3729
3730
3730 With no arguments, prints the currently active guards.
3731 With no arguments, prints the currently active guards.
3731 With one argument, sets the active guard.
3732 With one argument, sets the active guard.
3732
3733
3733 Use -n/--none to deactivate guards (no other arguments needed).
3734 Use -n/--none to deactivate guards (no other arguments needed).
3734 When no guards are active, patches with positive guards are
3735 When no guards are active, patches with positive guards are
3735 skipped and patches with negative guards are pushed.
3736 skipped and patches with negative guards are pushed.
3736
3737
3737 qselect can change the guards on applied patches. It does not pop
3738 qselect can change the guards on applied patches. It does not pop
3738 guarded patches by default. Use --pop to pop back to the last
3739 guarded patches by default. Use --pop to pop back to the last
3739 applied patch that is not guarded. Use --reapply (which implies
3740 applied patch that is not guarded. Use --reapply (which implies
3740 --pop) to push back to the current patch afterwards, but skip
3741 --pop) to push back to the current patch afterwards, but skip
3741 guarded patches.
3742 guarded patches.
3742
3743
3743 Use -s/--series to print a list of all guards in the series file
3744 Use -s/--series to print a list of all guards in the series file
3744 (no other arguments needed). Use -v for more information.
3745 (no other arguments needed). Use -v for more information.
3745
3746
3746 Returns 0 on success.'''
3747 Returns 0 on success.'''
3747
3748
3748 q = repo.mq
3749 q = repo.mq
3749 opts = pycompat.byteskwargs(opts)
3750 opts = pycompat.byteskwargs(opts)
3750 guards = q.active()
3751 guards = q.active()
3751 pushable = lambda i: q.pushable(q.applied[i].name)[0]
3752 pushable = lambda i: q.pushable(q.applied[i].name)[0]
3752 if args or opts.get(b'none'):
3753 if args or opts.get(b'none'):
3753 old_unapplied = q.unapplied(repo)
3754 old_unapplied = q.unapplied(repo)
3754 old_guarded = [
3755 old_guarded = [
3755 i for i in pycompat.xrange(len(q.applied)) if not pushable(i)
3756 i for i in pycompat.xrange(len(q.applied)) if not pushable(i)
3756 ]
3757 ]
3757 q.setactive(args)
3758 q.setactive(args)
3758 q.savedirty()
3759 q.savedirty()
3759 if not args:
3760 if not args:
3760 ui.status(_(b'guards deactivated\n'))
3761 ui.status(_(b'guards deactivated\n'))
3761 if not opts.get(b'pop') and not opts.get(b'reapply'):
3762 if not opts.get(b'pop') and not opts.get(b'reapply'):
3762 unapplied = q.unapplied(repo)
3763 unapplied = q.unapplied(repo)
3763 guarded = [
3764 guarded = [
3764 i for i in pycompat.xrange(len(q.applied)) if not pushable(i)
3765 i for i in pycompat.xrange(len(q.applied)) if not pushable(i)
3765 ]
3766 ]
3766 if len(unapplied) != len(old_unapplied):
3767 if len(unapplied) != len(old_unapplied):
3767 ui.status(
3768 ui.status(
3768 _(
3769 _(
3769 b'number of unguarded, unapplied patches has '
3770 b'number of unguarded, unapplied patches has '
3770 b'changed from %d to %d\n'
3771 b'changed from %d to %d\n'
3771 )
3772 )
3772 % (len(old_unapplied), len(unapplied))
3773 % (len(old_unapplied), len(unapplied))
3773 )
3774 )
3774 if len(guarded) != len(old_guarded):
3775 if len(guarded) != len(old_guarded):
3775 ui.status(
3776 ui.status(
3776 _(
3777 _(
3777 b'number of guarded, applied patches has changed '
3778 b'number of guarded, applied patches has changed '
3778 b'from %d to %d\n'
3779 b'from %d to %d\n'
3779 )
3780 )
3780 % (len(old_guarded), len(guarded))
3781 % (len(old_guarded), len(guarded))
3781 )
3782 )
3782 elif opts.get(b'series'):
3783 elif opts.get(b'series'):
3783 guards = {}
3784 guards = {}
3784 noguards = 0
3785 noguards = 0
3785 for gs in q.seriesguards:
3786 for gs in q.seriesguards:
3786 if not gs:
3787 if not gs:
3787 noguards += 1
3788 noguards += 1
3788 for g in gs:
3789 for g in gs:
3789 guards.setdefault(g, 0)
3790 guards.setdefault(g, 0)
3790 guards[g] += 1
3791 guards[g] += 1
3791 if ui.verbose:
3792 if ui.verbose:
3792 guards[b'NONE'] = noguards
3793 guards[b'NONE'] = noguards
3793 guards = list(guards.items())
3794 guards = list(guards.items())
3794 guards.sort(key=lambda x: x[0][1:])
3795 guards.sort(key=lambda x: x[0][1:])
3795 if guards:
3796 if guards:
3796 ui.note(_(b'guards in series file:\n'))
3797 ui.note(_(b'guards in series file:\n'))
3797 for guard, count in guards:
3798 for guard, count in guards:
3798 ui.note(b'%2d ' % count)
3799 ui.note(b'%2d ' % count)
3799 ui.write(guard, b'\n')
3800 ui.write(guard, b'\n')
3800 else:
3801 else:
3801 ui.note(_(b'no guards in series file\n'))
3802 ui.note(_(b'no guards in series file\n'))
3802 else:
3803 else:
3803 if guards:
3804 if guards:
3804 ui.note(_(b'active guards:\n'))
3805 ui.note(_(b'active guards:\n'))
3805 for g in guards:
3806 for g in guards:
3806 ui.write(g, b'\n')
3807 ui.write(g, b'\n')
3807 else:
3808 else:
3808 ui.write(_(b'no active guards\n'))
3809 ui.write(_(b'no active guards\n'))
3809 reapply = opts.get(b'reapply') and q.applied and q.applied[-1].name
3810 reapply = opts.get(b'reapply') and q.applied and q.applied[-1].name
3810 popped = False
3811 popped = False
3811 if opts.get(b'pop') or opts.get(b'reapply'):
3812 if opts.get(b'pop') or opts.get(b'reapply'):
3812 for i in pycompat.xrange(len(q.applied)):
3813 for i in pycompat.xrange(len(q.applied)):
3813 if not pushable(i):
3814 if not pushable(i):
3814 ui.status(_(b'popping guarded patches\n'))
3815 ui.status(_(b'popping guarded patches\n'))
3815 popped = True
3816 popped = True
3816 if i == 0:
3817 if i == 0:
3817 q.pop(repo, all=True)
3818 q.pop(repo, all=True)
3818 else:
3819 else:
3819 q.pop(repo, q.applied[i - 1].name)
3820 q.pop(repo, q.applied[i - 1].name)
3820 break
3821 break
3821 if popped:
3822 if popped:
3822 try:
3823 try:
3823 if reapply:
3824 if reapply:
3824 ui.status(_(b'reapplying unguarded patches\n'))
3825 ui.status(_(b'reapplying unguarded patches\n'))
3825 q.push(repo, reapply)
3826 q.push(repo, reapply)
3826 finally:
3827 finally:
3827 q.savedirty()
3828 q.savedirty()
3828
3829
3829
3830
3830 @command(
3831 @command(
3831 b"qfinish",
3832 b"qfinish",
3832 [(b'a', b'applied', None, _(b'finish all applied changesets'))],
3833 [(b'a', b'applied', None, _(b'finish all applied changesets'))],
3833 _(b'hg qfinish [-a] [REV]...'),
3834 _(b'hg qfinish [-a] [REV]...'),
3834 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3835 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3835 )
3836 )
3836 def finish(ui, repo, *revrange, **opts):
3837 def finish(ui, repo, *revrange, **opts):
3837 """move applied patches into repository history
3838 """move applied patches into repository history
3838
3839
3839 Finishes the specified revisions (corresponding to applied
3840 Finishes the specified revisions (corresponding to applied
3840 patches) by moving them out of mq control into regular repository
3841 patches) by moving them out of mq control into regular repository
3841 history.
3842 history.
3842
3843
3843 Accepts a revision range or the -a/--applied option. If --applied
3844 Accepts a revision range or the -a/--applied option. If --applied
3844 is specified, all applied mq revisions are removed from mq
3845 is specified, all applied mq revisions are removed from mq
3845 control. Otherwise, the given revisions must be at the base of the
3846 control. Otherwise, the given revisions must be at the base of the
3846 stack of applied patches.
3847 stack of applied patches.
3847
3848
3848 This can be especially useful if your changes have been applied to
3849 This can be especially useful if your changes have been applied to
3849 an upstream repository, or if you are about to push your changes
3850 an upstream repository, or if you are about to push your changes
3850 to upstream.
3851 to upstream.
3851
3852
3852 Returns 0 on success.
3853 Returns 0 on success.
3853 """
3854 """
3854 if not opts.get('applied') and not revrange:
3855 if not opts.get('applied') and not revrange:
3855 raise error.Abort(_(b'no revisions specified'))
3856 raise error.Abort(_(b'no revisions specified'))
3856 elif opts.get('applied'):
3857 elif opts.get('applied'):
3857 revrange = (b'qbase::qtip',) + revrange
3858 revrange = (b'qbase::qtip',) + revrange
3858
3859
3859 q = repo.mq
3860 q = repo.mq
3860 if not q.applied:
3861 if not q.applied:
3861 ui.status(_(b'no patches applied\n'))
3862 ui.status(_(b'no patches applied\n'))
3862 return 0
3863 return 0
3863
3864
3864 revs = scmutil.revrange(repo, revrange)
3865 revs = scmutil.revrange(repo, revrange)
3865 if repo[b'.'].rev() in revs and repo[None].files():
3866 if repo[b'.'].rev() in revs and repo[None].files():
3866 ui.warn(_(b'warning: uncommitted changes in the working directory\n'))
3867 ui.warn(_(b'warning: uncommitted changes in the working directory\n'))
3867 # queue.finish may changes phases but leave the responsibility to lock the
3868 # queue.finish may changes phases but leave the responsibility to lock the
3868 # repo to the caller to avoid deadlock with wlock. This command code is
3869 # repo to the caller to avoid deadlock with wlock. This command code is
3869 # responsibility for this locking.
3870 # responsibility for this locking.
3870 with repo.lock():
3871 with repo.lock():
3871 q.finish(repo, revs)
3872 q.finish(repo, revs)
3872 q.savedirty()
3873 q.savedirty()
3873 return 0
3874 return 0
3874
3875
3875
3876
3876 @command(
3877 @command(
3877 b"qqueue",
3878 b"qqueue",
3878 [
3879 [
3879 (b'l', b'list', False, _(b'list all available queues')),
3880 (b'l', b'list', False, _(b'list all available queues')),
3880 (b'', b'active', False, _(b'print name of active queue')),
3881 (b'', b'active', False, _(b'print name of active queue')),
3881 (b'c', b'create', False, _(b'create new queue')),
3882 (b'c', b'create', False, _(b'create new queue')),
3882 (b'', b'rename', False, _(b'rename active queue')),
3883 (b'', b'rename', False, _(b'rename active queue')),
3883 (b'', b'delete', False, _(b'delete reference to queue')),
3884 (b'', b'delete', False, _(b'delete reference to queue')),
3884 (b'', b'purge', False, _(b'delete queue, and remove patch dir')),
3885 (b'', b'purge', False, _(b'delete queue, and remove patch dir')),
3885 ],
3886 ],
3886 _(b'[OPTION] [QUEUE]'),
3887 _(b'[OPTION] [QUEUE]'),
3887 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3888 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
3888 )
3889 )
3889 def qqueue(ui, repo, name=None, **opts):
3890 def qqueue(ui, repo, name=None, **opts):
3890 '''manage multiple patch queues
3891 '''manage multiple patch queues
3891
3892
3892 Supports switching between different patch queues, as well as creating
3893 Supports switching between different patch queues, as well as creating
3893 new patch queues and deleting existing ones.
3894 new patch queues and deleting existing ones.
3894
3895
3895 Omitting a queue name or specifying -l/--list will show you the registered
3896 Omitting a queue name or specifying -l/--list will show you the registered
3896 queues - by default the "normal" patches queue is registered. The currently
3897 queues - by default the "normal" patches queue is registered. The currently
3897 active queue will be marked with "(active)". Specifying --active will print
3898 active queue will be marked with "(active)". Specifying --active will print
3898 only the name of the active queue.
3899 only the name of the active queue.
3899
3900
3900 To create a new queue, use -c/--create. The queue is automatically made
3901 To create a new queue, use -c/--create. The queue is automatically made
3901 active, except in the case where there are applied patches from the
3902 active, except in the case where there are applied patches from the
3902 currently active queue in the repository. Then the queue will only be
3903 currently active queue in the repository. Then the queue will only be
3903 created and switching will fail.
3904 created and switching will fail.
3904
3905
3905 To delete an existing queue, use --delete. You cannot delete the currently
3906 To delete an existing queue, use --delete. You cannot delete the currently
3906 active queue.
3907 active queue.
3907
3908
3908 Returns 0 on success.
3909 Returns 0 on success.
3909 '''
3910 '''
3910 q = repo.mq
3911 q = repo.mq
3911 _defaultqueue = b'patches'
3912 _defaultqueue = b'patches'
3912 _allqueues = b'patches.queues'
3913 _allqueues = b'patches.queues'
3913 _activequeue = b'patches.queue'
3914 _activequeue = b'patches.queue'
3914
3915
3915 def _getcurrent():
3916 def _getcurrent():
3916 cur = os.path.basename(q.path)
3917 cur = os.path.basename(q.path)
3917 if cur.startswith(b'patches-'):
3918 if cur.startswith(b'patches-'):
3918 cur = cur[8:]
3919 cur = cur[8:]
3919 return cur
3920 return cur
3920
3921
3921 def _noqueues():
3922 def _noqueues():
3922 try:
3923 try:
3923 fh = repo.vfs(_allqueues, b'r')
3924 fh = repo.vfs(_allqueues, b'r')
3924 fh.close()
3925 fh.close()
3925 except IOError:
3926 except IOError:
3926 return True
3927 return True
3927
3928
3928 return False
3929 return False
3929
3930
3930 def _getqueues():
3931 def _getqueues():
3931 current = _getcurrent()
3932 current = _getcurrent()
3932
3933
3933 try:
3934 try:
3934 fh = repo.vfs(_allqueues, b'r')
3935 fh = repo.vfs(_allqueues, b'r')
3935 queues = [queue.strip() for queue in fh if queue.strip()]
3936 queues = [queue.strip() for queue in fh if queue.strip()]
3936 fh.close()
3937 fh.close()
3937 if current not in queues:
3938 if current not in queues:
3938 queues.append(current)
3939 queues.append(current)
3939 except IOError:
3940 except IOError:
3940 queues = [_defaultqueue]
3941 queues = [_defaultqueue]
3941
3942
3942 return sorted(queues)
3943 return sorted(queues)
3943
3944
3944 def _setactive(name):
3945 def _setactive(name):
3945 if q.applied:
3946 if q.applied:
3946 raise error.Abort(
3947 raise error.Abort(
3947 _(
3948 _(
3948 b'new queue created, but cannot make active '
3949 b'new queue created, but cannot make active '
3949 b'as patches are applied'
3950 b'as patches are applied'
3950 )
3951 )
3951 )
3952 )
3952 _setactivenocheck(name)
3953 _setactivenocheck(name)
3953
3954
3954 def _setactivenocheck(name):
3955 def _setactivenocheck(name):
3955 fh = repo.vfs(_activequeue, b'w')
3956 fh = repo.vfs(_activequeue, b'w')
3956 if name != b'patches':
3957 if name != b'patches':
3957 fh.write(name)
3958 fh.write(name)
3958 fh.close()
3959 fh.close()
3959
3960
3960 def _addqueue(name):
3961 def _addqueue(name):
3961 fh = repo.vfs(_allqueues, b'a')
3962 fh = repo.vfs(_allqueues, b'a')
3962 fh.write(b'%s\n' % (name,))
3963 fh.write(b'%s\n' % (name,))
3963 fh.close()
3964 fh.close()
3964
3965
3965 def _queuedir(name):
3966 def _queuedir(name):
3966 if name == b'patches':
3967 if name == b'patches':
3967 return repo.vfs.join(b'patches')
3968 return repo.vfs.join(b'patches')
3968 else:
3969 else:
3969 return repo.vfs.join(b'patches-' + name)
3970 return repo.vfs.join(b'patches-' + name)
3970
3971
3971 def _validname(name):
3972 def _validname(name):
3972 for n in name:
3973 for n in name:
3973 if n in b':\\/.':
3974 if n in b':\\/.':
3974 return False
3975 return False
3975 return True
3976 return True
3976
3977
3977 def _delete(name):
3978 def _delete(name):
3978 if name not in existing:
3979 if name not in existing:
3979 raise error.Abort(_(b'cannot delete queue that does not exist'))
3980 raise error.Abort(_(b'cannot delete queue that does not exist'))
3980
3981
3981 current = _getcurrent()
3982 current = _getcurrent()
3982
3983
3983 if name == current:
3984 if name == current:
3984 raise error.Abort(_(b'cannot delete currently active queue'))
3985 raise error.Abort(_(b'cannot delete currently active queue'))
3985
3986
3986 fh = repo.vfs(b'patches.queues.new', b'w')
3987 fh = repo.vfs(b'patches.queues.new', b'w')
3987 for queue in existing:
3988 for queue in existing:
3988 if queue == name:
3989 if queue == name:
3989 continue
3990 continue
3990 fh.write(b'%s\n' % (queue,))
3991 fh.write(b'%s\n' % (queue,))
3991 fh.close()
3992 fh.close()
3992 repo.vfs.rename(b'patches.queues.new', _allqueues)
3993 repo.vfs.rename(b'patches.queues.new', _allqueues)
3993
3994
3994 opts = pycompat.byteskwargs(opts)
3995 opts = pycompat.byteskwargs(opts)
3995 if not name or opts.get(b'list') or opts.get(b'active'):
3996 if not name or opts.get(b'list') or opts.get(b'active'):
3996 current = _getcurrent()
3997 current = _getcurrent()
3997 if opts.get(b'active'):
3998 if opts.get(b'active'):
3998 ui.write(b'%s\n' % (current,))
3999 ui.write(b'%s\n' % (current,))
3999 return
4000 return
4000 for queue in _getqueues():
4001 for queue in _getqueues():
4001 ui.write(b'%s' % (queue,))
4002 ui.write(b'%s' % (queue,))
4002 if queue == current and not ui.quiet:
4003 if queue == current and not ui.quiet:
4003 ui.write(_(b' (active)\n'))
4004 ui.write(_(b' (active)\n'))
4004 else:
4005 else:
4005 ui.write(b'\n')
4006 ui.write(b'\n')
4006 return
4007 return
4007
4008
4008 if not _validname(name):
4009 if not _validname(name):
4009 raise error.Abort(
4010 raise error.Abort(
4010 _(b'invalid queue name, may not contain the characters ":\\/."')
4011 _(b'invalid queue name, may not contain the characters ":\\/."')
4011 )
4012 )
4012
4013
4013 with repo.wlock():
4014 with repo.wlock():
4014 existing = _getqueues()
4015 existing = _getqueues()
4015
4016
4016 if opts.get(b'create'):
4017 if opts.get(b'create'):
4017 if name in existing:
4018 if name in existing:
4018 raise error.Abort(_(b'queue "%s" already exists') % name)
4019 raise error.Abort(_(b'queue "%s" already exists') % name)
4019 if _noqueues():
4020 if _noqueues():
4020 _addqueue(_defaultqueue)
4021 _addqueue(_defaultqueue)
4021 _addqueue(name)
4022 _addqueue(name)
4022 _setactive(name)
4023 _setactive(name)
4023 elif opts.get(b'rename'):
4024 elif opts.get(b'rename'):
4024 current = _getcurrent()
4025 current = _getcurrent()
4025 if name == current:
4026 if name == current:
4026 raise error.Abort(
4027 raise error.Abort(
4027 _(b'can\'t rename "%s" to its current name') % name
4028 _(b'can\'t rename "%s" to its current name') % name
4028 )
4029 )
4029 if name in existing:
4030 if name in existing:
4030 raise error.Abort(_(b'queue "%s" already exists') % name)
4031 raise error.Abort(_(b'queue "%s" already exists') % name)
4031
4032
4032 olddir = _queuedir(current)
4033 olddir = _queuedir(current)
4033 newdir = _queuedir(name)
4034 newdir = _queuedir(name)
4034
4035
4035 if os.path.exists(newdir):
4036 if os.path.exists(newdir):
4036 raise error.Abort(
4037 raise error.Abort(
4037 _(b'non-queue directory "%s" already exists') % newdir
4038 _(b'non-queue directory "%s" already exists') % newdir
4038 )
4039 )
4039
4040
4040 fh = repo.vfs(b'patches.queues.new', b'w')
4041 fh = repo.vfs(b'patches.queues.new', b'w')
4041 for queue in existing:
4042 for queue in existing:
4042 if queue == current:
4043 if queue == current:
4043 fh.write(b'%s\n' % (name,))
4044 fh.write(b'%s\n' % (name,))
4044 if os.path.exists(olddir):
4045 if os.path.exists(olddir):
4045 util.rename(olddir, newdir)
4046 util.rename(olddir, newdir)
4046 else:
4047 else:
4047 fh.write(b'%s\n' % (queue,))
4048 fh.write(b'%s\n' % (queue,))
4048 fh.close()
4049 fh.close()
4049 repo.vfs.rename(b'patches.queues.new', _allqueues)
4050 repo.vfs.rename(b'patches.queues.new', _allqueues)
4050 _setactivenocheck(name)
4051 _setactivenocheck(name)
4051 elif opts.get(b'delete'):
4052 elif opts.get(b'delete'):
4052 _delete(name)
4053 _delete(name)
4053 elif opts.get(b'purge'):
4054 elif opts.get(b'purge'):
4054 if name in existing:
4055 if name in existing:
4055 _delete(name)
4056 _delete(name)
4056 qdir = _queuedir(name)
4057 qdir = _queuedir(name)
4057 if os.path.exists(qdir):
4058 if os.path.exists(qdir):
4058 shutil.rmtree(qdir)
4059 shutil.rmtree(qdir)
4059 else:
4060 else:
4060 if name not in existing:
4061 if name not in existing:
4061 raise error.Abort(_(b'use --create to create a new queue'))
4062 raise error.Abort(_(b'use --create to create a new queue'))
4062 _setactive(name)
4063 _setactive(name)
4063
4064
4064
4065
4065 def mqphasedefaults(repo, roots):
4066 def mqphasedefaults(repo, roots):
4066 """callback used to set mq changeset as secret when no phase data exists"""
4067 """callback used to set mq changeset as secret when no phase data exists"""
4067 if repo.mq.applied:
4068 if repo.mq.applied:
4068 if repo.ui.configbool(b'mq', b'secret'):
4069 if repo.ui.configbool(b'mq', b'secret'):
4069 mqphase = phases.secret
4070 mqphase = phases.secret
4070 else:
4071 else:
4071 mqphase = phases.draft
4072 mqphase = phases.draft
4072 qbase = repo[repo.mq.applied[0].node]
4073 qbase = repo[repo.mq.applied[0].node]
4073 roots[mqphase].add(qbase.node())
4074 roots[mqphase].add(qbase.node())
4074 return roots
4075 return roots
4075
4076
4076
4077
4077 def reposetup(ui, repo):
4078 def reposetup(ui, repo):
4078 class mqrepo(repo.__class__):
4079 class mqrepo(repo.__class__):
4079 @localrepo.unfilteredpropertycache
4080 @localrepo.unfilteredpropertycache
4080 def mq(self):
4081 def mq(self):
4081 return queue(self.ui, self.baseui, self.path)
4082 return queue(self.ui, self.baseui, self.path)
4082
4083
4083 def invalidateall(self):
4084 def invalidateall(self):
4084 super(mqrepo, self).invalidateall()
4085 super(mqrepo, self).invalidateall()
4085 if localrepo.hasunfilteredcache(self, 'mq'):
4086 if localrepo.hasunfilteredcache(self, 'mq'):
4086 # recreate mq in case queue path was changed
4087 # recreate mq in case queue path was changed
4087 delattr(self.unfiltered(), 'mq')
4088 delattr(self.unfiltered(), 'mq')
4088
4089
4089 def abortifwdirpatched(self, errmsg, force=False):
4090 def abortifwdirpatched(self, errmsg, force=False):
4090 if self.mq.applied and self.mq.checkapplied and not force:
4091 if self.mq.applied and self.mq.checkapplied and not force:
4091 parents = self.dirstate.parents()
4092 parents = self.dirstate.parents()
4092 patches = [s.node for s in self.mq.applied]
4093 patches = [s.node for s in self.mq.applied]
4093 if any(p in patches for p in parents):
4094 if any(p in patches for p in parents):
4094 raise error.Abort(errmsg)
4095 raise error.Abort(errmsg)
4095
4096
4096 def commit(
4097 def commit(
4097 self,
4098 self,
4098 text=b"",
4099 text=b"",
4099 user=None,
4100 user=None,
4100 date=None,
4101 date=None,
4101 match=None,
4102 match=None,
4102 force=False,
4103 force=False,
4103 editor=False,
4104 editor=False,
4104 extra=None,
4105 extra=None,
4105 ):
4106 ):
4106 if extra is None:
4107 if extra is None:
4107 extra = {}
4108 extra = {}
4108 self.abortifwdirpatched(
4109 self.abortifwdirpatched(
4109 _(b'cannot commit over an applied mq patch'), force
4110 _(b'cannot commit over an applied mq patch'), force
4110 )
4111 )
4111
4112
4112 return super(mqrepo, self).commit(
4113 return super(mqrepo, self).commit(
4113 text, user, date, match, force, editor, extra
4114 text, user, date, match, force, editor, extra
4114 )
4115 )
4115
4116
4116 def checkpush(self, pushop):
4117 def checkpush(self, pushop):
4117 if self.mq.applied and self.mq.checkapplied and not pushop.force:
4118 if self.mq.applied and self.mq.checkapplied and not pushop.force:
4118 outapplied = [e.node for e in self.mq.applied]
4119 outapplied = [e.node for e in self.mq.applied]
4119 if pushop.revs:
4120 if pushop.revs:
4120 # Assume applied patches have no non-patch descendants and
4121 # Assume applied patches have no non-patch descendants and
4121 # are not on remote already. Filtering any changeset not
4122 # are not on remote already. Filtering any changeset not
4122 # pushed.
4123 # pushed.
4123 heads = set(pushop.revs)
4124 heads = set(pushop.revs)
4124 for node in reversed(outapplied):
4125 for node in reversed(outapplied):
4125 if node in heads:
4126 if node in heads:
4126 break
4127 break
4127 else:
4128 else:
4128 outapplied.pop()
4129 outapplied.pop()
4129 # looking for pushed and shared changeset
4130 # looking for pushed and shared changeset
4130 for node in outapplied:
4131 for node in outapplied:
4131 if self[node].phase() < phases.secret:
4132 if self[node].phase() < phases.secret:
4132 raise error.Abort(_(b'source has mq patches applied'))
4133 raise error.Abort(_(b'source has mq patches applied'))
4133 # no non-secret patches pushed
4134 # no non-secret patches pushed
4134 super(mqrepo, self).checkpush(pushop)
4135 super(mqrepo, self).checkpush(pushop)
4135
4136
4136 def _findtags(self):
4137 def _findtags(self):
4137 '''augment tags from base class with patch tags'''
4138 '''augment tags from base class with patch tags'''
4138 result = super(mqrepo, self)._findtags()
4139 result = super(mqrepo, self)._findtags()
4139
4140
4140 q = self.mq
4141 q = self.mq
4141 if not q.applied:
4142 if not q.applied:
4142 return result
4143 return result
4143
4144
4144 mqtags = [(patch.node, patch.name) for patch in q.applied]
4145 mqtags = [(patch.node, patch.name) for patch in q.applied]
4145
4146
4146 try:
4147 try:
4147 # for now ignore filtering business
4148 # for now ignore filtering business
4148 self.unfiltered().changelog.rev(mqtags[-1][0])
4149 self.unfiltered().changelog.rev(mqtags[-1][0])
4149 except error.LookupError:
4150 except error.LookupError:
4150 self.ui.warn(
4151 self.ui.warn(
4151 _(b'mq status file refers to unknown node %s\n')
4152 _(b'mq status file refers to unknown node %s\n')
4152 % short(mqtags[-1][0])
4153 % short(mqtags[-1][0])
4153 )
4154 )
4154 return result
4155 return result
4155
4156
4156 # do not add fake tags for filtered revisions
4157 # do not add fake tags for filtered revisions
4157 included = self.changelog.hasnode
4158 included = self.changelog.hasnode
4158 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
4159 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
4159 if not mqtags:
4160 if not mqtags:
4160 return result
4161 return result
4161
4162
4162 mqtags.append((mqtags[-1][0], b'qtip'))
4163 mqtags.append((mqtags[-1][0], b'qtip'))
4163 mqtags.append((mqtags[0][0], b'qbase'))
4164 mqtags.append((mqtags[0][0], b'qbase'))
4164 mqtags.append((self.changelog.parents(mqtags[0][0])[0], b'qparent'))
4165 mqtags.append((self.changelog.parents(mqtags[0][0])[0], b'qparent'))
4165 tags = result[0]
4166 tags = result[0]
4166 for patch in mqtags:
4167 for patch in mqtags:
4167 if patch[1] in tags:
4168 if patch[1] in tags:
4168 self.ui.warn(
4169 self.ui.warn(
4169 _(b'tag %s overrides mq patch of the same name\n')
4170 _(b'tag %s overrides mq patch of the same name\n')
4170 % patch[1]
4171 % patch[1]
4171 )
4172 )
4172 else:
4173 else:
4173 tags[patch[1]] = patch[0]
4174 tags[patch[1]] = patch[0]
4174
4175
4175 return result
4176 return result
4176
4177
4177 if repo.local():
4178 if repo.local():
4178 repo.__class__ = mqrepo
4179 repo.__class__ = mqrepo
4179
4180
4180 repo._phasedefaults.append(mqphasedefaults)
4181 repo._phasedefaults.append(mqphasedefaults)
4181
4182
4182
4183
4183 def mqimport(orig, ui, repo, *args, **kwargs):
4184 def mqimport(orig, ui, repo, *args, **kwargs):
4184 if util.safehasattr(repo, b'abortifwdirpatched') and not kwargs.get(
4185 if util.safehasattr(repo, b'abortifwdirpatched') and not kwargs.get(
4185 'no_commit', False
4186 'no_commit', False
4186 ):
4187 ):
4187 repo.abortifwdirpatched(
4188 repo.abortifwdirpatched(
4188 _(b'cannot import over an applied patch'), kwargs.get('force')
4189 _(b'cannot import over an applied patch'), kwargs.get('force')
4189 )
4190 )
4190 return orig(ui, repo, *args, **kwargs)
4191 return orig(ui, repo, *args, **kwargs)
4191
4192
4192
4193
4193 def mqinit(orig, ui, *args, **kwargs):
4194 def mqinit(orig, ui, *args, **kwargs):
4194 mq = kwargs.pop('mq', None)
4195 mq = kwargs.pop('mq', None)
4195
4196
4196 if not mq:
4197 if not mq:
4197 return orig(ui, *args, **kwargs)
4198 return orig(ui, *args, **kwargs)
4198
4199
4199 if args:
4200 if args:
4200 repopath = args[0]
4201 repopath = args[0]
4201 if not hg.islocal(repopath):
4202 if not hg.islocal(repopath):
4202 raise error.Abort(
4203 raise error.Abort(
4203 _(b'only a local queue repository may be initialized')
4204 _(b'only a local queue repository may be initialized')
4204 )
4205 )
4205 else:
4206 else:
4206 repopath = cmdutil.findrepo(encoding.getcwd())
4207 repopath = cmdutil.findrepo(encoding.getcwd())
4207 if not repopath:
4208 if not repopath:
4208 raise error.Abort(
4209 raise error.Abort(
4209 _(b'there is no Mercurial repository here (.hg not found)')
4210 _(b'there is no Mercurial repository here (.hg not found)')
4210 )
4211 )
4211 repo = hg.repository(ui, repopath)
4212 repo = hg.repository(ui, repopath)
4212 return qinit(ui, repo, True)
4213 return qinit(ui, repo, True)
4213
4214
4214
4215
4215 def mqcommand(orig, ui, repo, *args, **kwargs):
4216 def mqcommand(orig, ui, repo, *args, **kwargs):
4216 """Add --mq option to operate on patch repository instead of main"""
4217 """Add --mq option to operate on patch repository instead of main"""
4217
4218
4218 # some commands do not like getting unknown options
4219 # some commands do not like getting unknown options
4219 mq = kwargs.pop('mq', None)
4220 mq = kwargs.pop('mq', None)
4220
4221
4221 if not mq:
4222 if not mq:
4222 return orig(ui, repo, *args, **kwargs)
4223 return orig(ui, repo, *args, **kwargs)
4223
4224
4224 q = repo.mq
4225 q = repo.mq
4225 r = q.qrepo()
4226 r = q.qrepo()
4226 if not r:
4227 if not r:
4227 raise error.Abort(_(b'no queue repository'))
4228 raise error.Abort(_(b'no queue repository'))
4228 return orig(r.ui, r, *args, **kwargs)
4229 return orig(r.ui, r, *args, **kwargs)
4229
4230
4230
4231
4231 def summaryhook(ui, repo):
4232 def summaryhook(ui, repo):
4232 q = repo.mq
4233 q = repo.mq
4233 m = []
4234 m = []
4234 a, u = len(q.applied), len(q.unapplied(repo))
4235 a, u = len(q.applied), len(q.unapplied(repo))
4235 if a:
4236 if a:
4236 m.append(ui.label(_(b"%d applied"), b'qseries.applied') % a)
4237 m.append(ui.label(_(b"%d applied"), b'qseries.applied') % a)
4237 if u:
4238 if u:
4238 m.append(ui.label(_(b"%d unapplied"), b'qseries.unapplied') % u)
4239 m.append(ui.label(_(b"%d unapplied"), b'qseries.unapplied') % u)
4239 if m:
4240 if m:
4240 # i18n: column positioning for "hg summary"
4241 # i18n: column positioning for "hg summary"
4241 ui.write(_(b"mq: %s\n") % b', '.join(m))
4242 ui.write(_(b"mq: %s\n") % b', '.join(m))
4242 else:
4243 else:
4243 # i18n: column positioning for "hg summary"
4244 # i18n: column positioning for "hg summary"
4244 ui.note(_(b"mq: (empty queue)\n"))
4245 ui.note(_(b"mq: (empty queue)\n"))
4245
4246
4246
4247
4247 revsetpredicate = registrar.revsetpredicate()
4248 revsetpredicate = registrar.revsetpredicate()
4248
4249
4249
4250
4250 @revsetpredicate(b'mq()')
4251 @revsetpredicate(b'mq()')
4251 def revsetmq(repo, subset, x):
4252 def revsetmq(repo, subset, x):
4252 """Changesets managed by MQ.
4253 """Changesets managed by MQ.
4253 """
4254 """
4254 revsetlang.getargs(x, 0, 0, _(b"mq takes no arguments"))
4255 revsetlang.getargs(x, 0, 0, _(b"mq takes no arguments"))
4255 applied = {repo[r.node].rev() for r in repo.mq.applied}
4256 applied = {repo[r.node].rev() for r in repo.mq.applied}
4256 return smartset.baseset([r for r in subset if r in applied])
4257 return smartset.baseset([r for r in subset if r in applied])
4257
4258
4258
4259
4259 # tell hggettext to extract docstrings from these functions:
4260 # tell hggettext to extract docstrings from these functions:
4260 i18nfunctions = [revsetmq]
4261 i18nfunctions = [revsetmq]
4261
4262
4262
4263
4263 def extsetup(ui):
4264 def extsetup(ui):
4264 # Ensure mq wrappers are called first, regardless of extension load order by
4265 # Ensure mq wrappers are called first, regardless of extension load order by
4265 # NOT wrapping in uisetup() and instead deferring to init stage two here.
4266 # NOT wrapping in uisetup() and instead deferring to init stage two here.
4266 mqopt = [(b'', b'mq', None, _(b"operate on patch repository"))]
4267 mqopt = [(b'', b'mq', None, _(b"operate on patch repository"))]
4267
4268
4268 extensions.wrapcommand(commands.table, b'import', mqimport)
4269 extensions.wrapcommand(commands.table, b'import', mqimport)
4269 cmdutil.summaryhooks.add(b'mq', summaryhook)
4270 cmdutil.summaryhooks.add(b'mq', summaryhook)
4270
4271
4271 entry = extensions.wrapcommand(commands.table, b'init', mqinit)
4272 entry = extensions.wrapcommand(commands.table, b'init', mqinit)
4272 entry[1].extend(mqopt)
4273 entry[1].extend(mqopt)
4273
4274
4274 def dotable(cmdtable):
4275 def dotable(cmdtable):
4275 for cmd, entry in pycompat.iteritems(cmdtable):
4276 for cmd, entry in pycompat.iteritems(cmdtable):
4276 cmd = cmdutil.parsealiases(cmd)[0]
4277 cmd = cmdutil.parsealiases(cmd)[0]
4277 func = entry[0]
4278 func = entry[0]
4278 if func.norepo:
4279 if func.norepo:
4279 continue
4280 continue
4280 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
4281 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
4281 entry[1].extend(mqopt)
4282 entry[1].extend(mqopt)
4282
4283
4283 dotable(commands.table)
4284 dotable(commands.table)
4284
4285
4285 thismodule = sys.modules["hgext.mq"]
4286 thismodule = sys.modules["hgext.mq"]
4286 for extname, extmodule in extensions.extensions():
4287 for extname, extmodule in extensions.extensions():
4287 if extmodule != thismodule:
4288 if extmodule != thismodule:
4288 dotable(getattr(extmodule, 'cmdtable', {}))
4289 dotable(getattr(extmodule, 'cmdtable', {}))
4289
4290
4290
4291
4291 colortable = {
4292 colortable = {
4292 b'qguard.negative': b'red',
4293 b'qguard.negative': b'red',
4293 b'qguard.positive': b'yellow',
4294 b'qguard.positive': b'yellow',
4294 b'qguard.unguarded': b'green',
4295 b'qguard.unguarded': b'green',
4295 b'qseries.applied': b'blue bold underline',
4296 b'qseries.applied': b'blue bold underline',
4296 b'qseries.guarded': b'black bold',
4297 b'qseries.guarded': b'black bold',
4297 b'qseries.missing': b'red bold',
4298 b'qseries.missing': b'red bold',
4298 b'qseries.unapplied': b'black bold',
4299 b'qseries.unapplied': b'black bold',
4299 }
4300 }
@@ -1,287 +1,24 b''
1 """strip changesets and their descendants from history
1 """strip changesets and their descendants from history (DEPRECATED)
2
3 The functionality of this extension has been included in core Mercurial
4 since version 5.7. Please use :hg:`debugstrip ...` instead.
2
5
3 This extension allows you to strip changesets and all their descendants from the
6 This extension allows you to strip changesets and all their descendants from the
4 repository. See the command help for details.
7 repository. See the command help for details.
5 """
8 """
6 from __future__ import absolute_import
9 from __future__ import absolute_import
7
10
8 from mercurial.i18n import _
9 from mercurial.pycompat import getattr
10 from mercurial import (
11 from mercurial import (
11 bookmarks as bookmarksmod,
12 commands,
12 cmdutil,
13 error,
14 hg,
15 lock as lockmod,
16 mergestate as mergestatemod,
17 node as nodemod,
18 pycompat,
19 registrar,
20 repair,
21 scmutil,
22 util,
23 )
13 )
24
14
25 nullid = nodemod.nullid
26 release = lockmod.release
27
28 cmdtable = {}
29 command = registrar.command(cmdtable)
30 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
15 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
31 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
16 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
32 # be specifying the version(s) of Mercurial they are tested with, or
17 # be specifying the version(s) of Mercurial they are tested with, or
33 # leave the attribute unspecified.
18 # leave the attribute unspecified.
34 testedwith = b'ships-with-hg-core'
19 testedwith = b'ships-with-hg-core'
35
20
36
21 # This is a bit ugly, but a uisetup function that defines strip as an
37 def checklocalchanges(repo, force=False):
22 # alias for debugstrip would override any user alias for strip,
38 s = repo.status()
23 # including aliases like "strip = strip --no-backup".
39 if not force:
24 commands.command.rename(old=b'debugstrip', new=b'debugstrip|strip')
40 cmdutil.checkunfinished(repo)
41 cmdutil.bailifchanged(repo)
42 else:
43 cmdutil.checkunfinished(repo, skipmerge=True)
44 return s
45
46
47 def _findupdatetarget(repo, nodes):
48 unode, p2 = repo.changelog.parents(nodes[0])
49 currentbranch = repo[None].branch()
50
51 if (
52 util.safehasattr(repo, b'mq')
53 and p2 != nullid
54 and p2 in [x.node for x in repo.mq.applied]
55 ):
56 unode = p2
57 elif currentbranch != repo[unode].branch():
58 pwdir = b'parents(wdir())'
59 revset = b'max(((parents(%ln::%r) + %r) - %ln::%r) and branch(%s))'
60 branchtarget = repo.revs(
61 revset, nodes, pwdir, pwdir, nodes, pwdir, currentbranch
62 )
63 if branchtarget:
64 cl = repo.changelog
65 unode = cl.node(branchtarget.first())
66
67 return unode
68
69
70 def strip(
71 ui,
72 repo,
73 revs,
74 update=True,
75 backup=True,
76 force=None,
77 bookmarks=None,
78 soft=False,
79 ):
80 with repo.wlock(), repo.lock():
81
82 if update:
83 checklocalchanges(repo, force=force)
84 urev = _findupdatetarget(repo, revs)
85 hg.clean(repo, urev)
86 repo.dirstate.write(repo.currenttransaction())
87
88 if soft:
89 repair.softstrip(ui, repo, revs, backup)
90 else:
91 repair.strip(ui, repo, revs, backup)
92
93 repomarks = repo._bookmarks
94 if bookmarks:
95 with repo.transaction(b'strip') as tr:
96 if repo._activebookmark in bookmarks:
97 bookmarksmod.deactivate(repo)
98 repomarks.applychanges(repo, tr, [(b, None) for b in bookmarks])
99 for bookmark in sorted(bookmarks):
100 ui.write(_(b"bookmark '%s' deleted\n") % bookmark)
101
102
103 @command(
104 b"strip",
105 [
106 (
107 b'r',
108 b'rev',
109 [],
110 _(
111 b'strip specified revision (optional, '
112 b'can specify revisions without this '
113 b'option)'
114 ),
115 _(b'REV'),
116 ),
117 (
118 b'f',
119 b'force',
120 None,
121 _(
122 b'force removal of changesets, discard '
123 b'uncommitted changes (no backup)'
124 ),
125 ),
126 (b'', b'no-backup', None, _(b'do not save backup bundle')),
127 (b'', b'nobackup', None, _(b'do not save backup bundle (DEPRECATED)'),),
128 (b'n', b'', None, _(b'ignored (DEPRECATED)')),
129 (
130 b'k',
131 b'keep',
132 None,
133 _(b"do not modify working directory during strip"),
134 ),
135 (
136 b'B',
137 b'bookmark',
138 [],
139 _(b"remove revs only reachable from given bookmark"),
140 _(b'BOOKMARK'),
141 ),
142 (
143 b'',
144 b'soft',
145 None,
146 _(b"simply drop changesets from visible history (EXPERIMENTAL)"),
147 ),
148 ],
149 _(b'hg strip [-k] [-f] [-B bookmark] [-r] REV...'),
150 helpcategory=command.CATEGORY_MAINTENANCE,
151 )
152 def stripcmd(ui, repo, *revs, **opts):
153 """strip changesets and all their descendants from the repository
154
155 The strip command removes the specified changesets and all their
156 descendants. If the working directory has uncommitted changes, the
157 operation is aborted unless the --force flag is supplied, in which
158 case changes will be discarded.
159
160 If a parent of the working directory is stripped, then the working
161 directory will automatically be updated to the most recent
162 available ancestor of the stripped parent after the operation
163 completes.
164
165 Any stripped changesets are stored in ``.hg/strip-backup`` as a
166 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
167 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
168 where BUNDLE is the bundle file created by the strip. Note that
169 the local revision numbers will in general be different after the
170 restore.
171
172 Use the --no-backup option to discard the backup bundle once the
173 operation completes.
174
175 Strip is not a history-rewriting operation and can be used on
176 changesets in the public phase. But if the stripped changesets have
177 been pushed to a remote repository you will likely pull them again.
178
179 Return 0 on success.
180 """
181 opts = pycompat.byteskwargs(opts)
182 backup = True
183 if opts.get(b'no_backup') or opts.get(b'nobackup'):
184 backup = False
185
186 cl = repo.changelog
187 revs = list(revs) + opts.get(b'rev')
188 revs = set(scmutil.revrange(repo, revs))
189
190 with repo.wlock():
191 bookmarks = set(opts.get(b'bookmark'))
192 if bookmarks:
193 repomarks = repo._bookmarks
194 if not bookmarks.issubset(repomarks):
195 raise error.Abort(
196 _(b"bookmark '%s' not found")
197 % b','.join(sorted(bookmarks - set(repomarks.keys())))
198 )
199
200 # If the requested bookmark is not the only one pointing to a
201 # a revision we have to only delete the bookmark and not strip
202 # anything. revsets cannot detect that case.
203 nodetobookmarks = {}
204 for mark, node in pycompat.iteritems(repomarks):
205 nodetobookmarks.setdefault(node, []).append(mark)
206 for marks in nodetobookmarks.values():
207 if bookmarks.issuperset(marks):
208 rsrevs = scmutil.bookmarkrevs(repo, marks[0])
209 revs.update(set(rsrevs))
210 if not revs:
211 with repo.lock(), repo.transaction(b'bookmark') as tr:
212 bmchanges = [(b, None) for b in bookmarks]
213 repomarks.applychanges(repo, tr, bmchanges)
214 for bookmark in sorted(bookmarks):
215 ui.write(_(b"bookmark '%s' deleted\n") % bookmark)
216
217 if not revs:
218 raise error.Abort(_(b'empty revision set'))
219
220 descendants = set(cl.descendants(revs))
221 strippedrevs = revs.union(descendants)
222 roots = revs.difference(descendants)
223
224 # if one of the wdir parent is stripped we'll need
225 # to update away to an earlier revision
226 update = any(
227 p != nullid and cl.rev(p) in strippedrevs
228 for p in repo.dirstate.parents()
229 )
230
231 rootnodes = {cl.node(r) for r in roots}
232
233 q = getattr(repo, 'mq', None)
234 if q is not None and q.applied:
235 # refresh queue state if we're about to strip
236 # applied patches
237 if cl.rev(repo.lookup(b'qtip')) in strippedrevs:
238 q.applieddirty = True
239 start = 0
240 end = len(q.applied)
241 for i, statusentry in enumerate(q.applied):
242 if statusentry.node in rootnodes:
243 # if one of the stripped roots is an applied
244 # patch, only part of the queue is stripped
245 start = i
246 break
247 del q.applied[start:end]
248 q.savedirty()
249
250 revs = sorted(rootnodes)
251 if update and opts.get(b'keep'):
252 urev = _findupdatetarget(repo, revs)
253 uctx = repo[urev]
254
255 # only reset the dirstate for files that would actually change
256 # between the working context and uctx
257 descendantrevs = repo.revs(b"only(., %d)", uctx.rev())
258 changedfiles = []
259 for rev in descendantrevs:
260 # blindly reset the files, regardless of what actually changed
261 changedfiles.extend(repo[rev].files())
262
263 # reset files that only changed in the dirstate too
264 dirstate = repo.dirstate
265 dirchanges = [f for f in dirstate if dirstate[f] != b'n']
266 changedfiles.extend(dirchanges)
267
268 repo.dirstate.rebuild(urev, uctx.manifest(), changedfiles)
269 repo.dirstate.write(repo.currenttransaction())
270
271 # clear resolve state
272 mergestatemod.mergestate.clean(repo)
273
274 update = False
275
276 strip(
277 ui,
278 repo,
279 revs,
280 backup=backup,
281 update=update,
282 force=opts.get(b'force'),
283 bookmarks=bookmarks,
284 soft=opts[b'soft'],
285 )
286
287 return 0
@@ -1,4578 +1,4580 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 glob
14 import glob
15 import operator
15 import operator
16 import os
16 import os
17 import platform
17 import platform
18 import random
18 import random
19 import re
19 import re
20 import socket
20 import socket
21 import ssl
21 import ssl
22 import stat
22 import stat
23 import string
23 import string
24 import subprocess
24 import subprocess
25 import sys
25 import sys
26 import time
26 import time
27
27
28 from .i18n import _
28 from .i18n import _
29 from .node import (
29 from .node import (
30 bin,
30 bin,
31 hex,
31 hex,
32 nullid,
32 nullid,
33 nullrev,
33 nullrev,
34 short,
34 short,
35 )
35 )
36 from .pycompat import (
36 from .pycompat import (
37 getattr,
37 getattr,
38 open,
38 open,
39 )
39 )
40 from . import (
40 from . import (
41 bundle2,
41 bundle2,
42 bundlerepo,
42 bundlerepo,
43 changegroup,
43 changegroup,
44 cmdutil,
44 cmdutil,
45 color,
45 color,
46 context,
46 context,
47 copies,
47 copies,
48 dagparser,
48 dagparser,
49 encoding,
49 encoding,
50 error,
50 error,
51 exchange,
51 exchange,
52 extensions,
52 extensions,
53 filemerge,
53 filemerge,
54 filesetlang,
54 filesetlang,
55 formatter,
55 formatter,
56 hg,
56 hg,
57 httppeer,
57 httppeer,
58 localrepo,
58 localrepo,
59 lock as lockmod,
59 lock as lockmod,
60 logcmdutil,
60 logcmdutil,
61 mergestate as mergestatemod,
61 mergestate as mergestatemod,
62 metadata,
62 metadata,
63 obsolete,
63 obsolete,
64 obsutil,
64 obsutil,
65 pathutil,
65 pathutil,
66 phases,
66 phases,
67 policy,
67 policy,
68 pvec,
68 pvec,
69 pycompat,
69 pycompat,
70 registrar,
70 registrar,
71 repair,
71 repair,
72 revlog,
72 revlog,
73 revset,
73 revset,
74 revsetlang,
74 revsetlang,
75 scmutil,
75 scmutil,
76 setdiscovery,
76 setdiscovery,
77 simplemerge,
77 simplemerge,
78 sshpeer,
78 sshpeer,
79 sslutil,
79 sslutil,
80 streamclone,
80 streamclone,
81 strip,
81 tags as tagsmod,
82 tags as tagsmod,
82 templater,
83 templater,
83 treediscovery,
84 treediscovery,
84 upgrade,
85 upgrade,
85 url as urlmod,
86 url as urlmod,
86 util,
87 util,
87 vfs as vfsmod,
88 vfs as vfsmod,
88 wireprotoframing,
89 wireprotoframing,
89 wireprotoserver,
90 wireprotoserver,
90 wireprotov2peer,
91 wireprotov2peer,
91 )
92 )
92 from .utils import (
93 from .utils import (
93 cborutil,
94 cborutil,
94 compression,
95 compression,
95 dateutil,
96 dateutil,
96 procutil,
97 procutil,
97 stringutil,
98 stringutil,
98 )
99 )
99
100
100 from .revlogutils import (
101 from .revlogutils import (
101 deltas as deltautil,
102 deltas as deltautil,
102 nodemap,
103 nodemap,
103 sidedata,
104 sidedata,
104 )
105 )
105
106
106 release = lockmod.release
107 release = lockmod.release
107
108
108 command = registrar.command()
109 table = {}
109
110 table.update(strip.command._table)
111 command = registrar.command(table)
110
112
111 @command(b'debugancestor', [], _(b'[INDEX] REV1 REV2'), optionalrepo=True)
113 @command(b'debugancestor', [], _(b'[INDEX] REV1 REV2'), optionalrepo=True)
112 def debugancestor(ui, repo, *args):
114 def debugancestor(ui, repo, *args):
113 """find the ancestor revision of two revisions in a given index"""
115 """find the ancestor revision of two revisions in a given index"""
114 if len(args) == 3:
116 if len(args) == 3:
115 index, rev1, rev2 = args
117 index, rev1, rev2 = args
116 r = revlog.revlog(vfsmod.vfs(encoding.getcwd(), audit=False), index)
118 r = revlog.revlog(vfsmod.vfs(encoding.getcwd(), audit=False), index)
117 lookup = r.lookup
119 lookup = r.lookup
118 elif len(args) == 2:
120 elif len(args) == 2:
119 if not repo:
121 if not repo:
120 raise error.Abort(
122 raise error.Abort(
121 _(b'there is no Mercurial repository here (.hg not found)')
123 _(b'there is no Mercurial repository here (.hg not found)')
122 )
124 )
123 rev1, rev2 = args
125 rev1, rev2 = args
124 r = repo.changelog
126 r = repo.changelog
125 lookup = repo.lookup
127 lookup = repo.lookup
126 else:
128 else:
127 raise error.Abort(_(b'either two or three arguments required'))
129 raise error.Abort(_(b'either two or three arguments required'))
128 a = r.ancestor(lookup(rev1), lookup(rev2))
130 a = r.ancestor(lookup(rev1), lookup(rev2))
129 ui.write(b'%d:%s\n' % (r.rev(a), hex(a)))
131 ui.write(b'%d:%s\n' % (r.rev(a), hex(a)))
130
132
131
133
132 @command(b'debugantivirusrunning', [])
134 @command(b'debugantivirusrunning', [])
133 def debugantivirusrunning(ui, repo):
135 def debugantivirusrunning(ui, repo):
134 """attempt to trigger an antivirus scanner to see if one is active"""
136 """attempt to trigger an antivirus scanner to see if one is active"""
135 with repo.cachevfs.open('eicar-test-file.com', b'wb') as f:
137 with repo.cachevfs.open('eicar-test-file.com', b'wb') as f:
136 f.write(
138 f.write(
137 util.b85decode(
139 util.b85decode(
138 # This is a base85-armored version of the EICAR test file. See
140 # This is a base85-armored version of the EICAR test file. See
139 # https://en.wikipedia.org/wiki/EICAR_test_file for details.
141 # https://en.wikipedia.org/wiki/EICAR_test_file for details.
140 b'ST#=}P$fV?P+K%yP+C|uG$>GBDK|qyDK~v2MM*<JQY}+dK~6+LQba95P'
142 b'ST#=}P$fV?P+K%yP+C|uG$>GBDK|qyDK~v2MM*<JQY}+dK~6+LQba95P'
141 b'E<)&Nm5l)EmTEQR4qnHOhq9iNGnJx'
143 b'E<)&Nm5l)EmTEQR4qnHOhq9iNGnJx'
142 )
144 )
143 )
145 )
144 # Give an AV engine time to scan the file.
146 # Give an AV engine time to scan the file.
145 time.sleep(2)
147 time.sleep(2)
146 util.unlink(repo.cachevfs.join('eicar-test-file.com'))
148 util.unlink(repo.cachevfs.join('eicar-test-file.com'))
147
149
148
150
149 @command(b'debugapplystreamclonebundle', [], b'FILE')
151 @command(b'debugapplystreamclonebundle', [], b'FILE')
150 def debugapplystreamclonebundle(ui, repo, fname):
152 def debugapplystreamclonebundle(ui, repo, fname):
151 """apply a stream clone bundle file"""
153 """apply a stream clone bundle file"""
152 f = hg.openpath(ui, fname)
154 f = hg.openpath(ui, fname)
153 gen = exchange.readbundle(ui, f, fname)
155 gen = exchange.readbundle(ui, f, fname)
154 gen.apply(repo)
156 gen.apply(repo)
155
157
156
158
157 @command(
159 @command(
158 b'debugbuilddag',
160 b'debugbuilddag',
159 [
161 [
160 (
162 (
161 b'm',
163 b'm',
162 b'mergeable-file',
164 b'mergeable-file',
163 None,
165 None,
164 _(b'add single file mergeable changes'),
166 _(b'add single file mergeable changes'),
165 ),
167 ),
166 (
168 (
167 b'o',
169 b'o',
168 b'overwritten-file',
170 b'overwritten-file',
169 None,
171 None,
170 _(b'add single file all revs overwrite'),
172 _(b'add single file all revs overwrite'),
171 ),
173 ),
172 (b'n', b'new-file', None, _(b'add new file at each rev')),
174 (b'n', b'new-file', None, _(b'add new file at each rev')),
173 ],
175 ],
174 _(b'[OPTION]... [TEXT]'),
176 _(b'[OPTION]... [TEXT]'),
175 )
177 )
176 def debugbuilddag(
178 def debugbuilddag(
177 ui,
179 ui,
178 repo,
180 repo,
179 text=None,
181 text=None,
180 mergeable_file=False,
182 mergeable_file=False,
181 overwritten_file=False,
183 overwritten_file=False,
182 new_file=False,
184 new_file=False,
183 ):
185 ):
184 """builds a repo with a given DAG from scratch in the current empty repo
186 """builds a repo with a given DAG from scratch in the current empty repo
185
187
186 The description of the DAG is read from stdin if not given on the
188 The description of the DAG is read from stdin if not given on the
187 command line.
189 command line.
188
190
189 Elements:
191 Elements:
190
192
191 - "+n" is a linear run of n nodes based on the current default parent
193 - "+n" is a linear run of n nodes based on the current default parent
192 - "." is a single node based on the current default parent
194 - "." is a single node based on the current default parent
193 - "$" resets the default parent to null (implied at the start);
195 - "$" resets the default parent to null (implied at the start);
194 otherwise the default parent is always the last node created
196 otherwise the default parent is always the last node created
195 - "<p" sets the default parent to the backref p
197 - "<p" sets the default parent to the backref p
196 - "*p" is a fork at parent p, which is a backref
198 - "*p" is a fork at parent p, which is a backref
197 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
199 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
198 - "/p2" is a merge of the preceding node and p2
200 - "/p2" is a merge of the preceding node and p2
199 - ":tag" defines a local tag for the preceding node
201 - ":tag" defines a local tag for the preceding node
200 - "@branch" sets the named branch for subsequent nodes
202 - "@branch" sets the named branch for subsequent nodes
201 - "#...\\n" is a comment up to the end of the line
203 - "#...\\n" is a comment up to the end of the line
202
204
203 Whitespace between the above elements is ignored.
205 Whitespace between the above elements is ignored.
204
206
205 A backref is either
207 A backref is either
206
208
207 - a number n, which references the node curr-n, where curr is the current
209 - a number n, which references the node curr-n, where curr is the current
208 node, or
210 node, or
209 - the name of a local tag you placed earlier using ":tag", or
211 - the name of a local tag you placed earlier using ":tag", or
210 - empty to denote the default parent.
212 - empty to denote the default parent.
211
213
212 All string valued-elements are either strictly alphanumeric, or must
214 All string valued-elements are either strictly alphanumeric, or must
213 be enclosed in double quotes ("..."), with "\\" as escape character.
215 be enclosed in double quotes ("..."), with "\\" as escape character.
214 """
216 """
215
217
216 if text is None:
218 if text is None:
217 ui.status(_(b"reading DAG from stdin\n"))
219 ui.status(_(b"reading DAG from stdin\n"))
218 text = ui.fin.read()
220 text = ui.fin.read()
219
221
220 cl = repo.changelog
222 cl = repo.changelog
221 if len(cl) > 0:
223 if len(cl) > 0:
222 raise error.Abort(_(b'repository is not empty'))
224 raise error.Abort(_(b'repository is not empty'))
223
225
224 # determine number of revs in DAG
226 # determine number of revs in DAG
225 total = 0
227 total = 0
226 for type, data in dagparser.parsedag(text):
228 for type, data in dagparser.parsedag(text):
227 if type == b'n':
229 if type == b'n':
228 total += 1
230 total += 1
229
231
230 if mergeable_file:
232 if mergeable_file:
231 linesperrev = 2
233 linesperrev = 2
232 # make a file with k lines per rev
234 # make a file with k lines per rev
233 initialmergedlines = [
235 initialmergedlines = [
234 b'%d' % i for i in pycompat.xrange(0, total * linesperrev)
236 b'%d' % i for i in pycompat.xrange(0, total * linesperrev)
235 ]
237 ]
236 initialmergedlines.append(b"")
238 initialmergedlines.append(b"")
237
239
238 tags = []
240 tags = []
239 progress = ui.makeprogress(
241 progress = ui.makeprogress(
240 _(b'building'), unit=_(b'revisions'), total=total
242 _(b'building'), unit=_(b'revisions'), total=total
241 )
243 )
242 with progress, repo.wlock(), repo.lock(), repo.transaction(b"builddag"):
244 with progress, repo.wlock(), repo.lock(), repo.transaction(b"builddag"):
243 at = -1
245 at = -1
244 atbranch = b'default'
246 atbranch = b'default'
245 nodeids = []
247 nodeids = []
246 id = 0
248 id = 0
247 progress.update(id)
249 progress.update(id)
248 for type, data in dagparser.parsedag(text):
250 for type, data in dagparser.parsedag(text):
249 if type == b'n':
251 if type == b'n':
250 ui.note((b'node %s\n' % pycompat.bytestr(data)))
252 ui.note((b'node %s\n' % pycompat.bytestr(data)))
251 id, ps = data
253 id, ps = data
252
254
253 files = []
255 files = []
254 filecontent = {}
256 filecontent = {}
255
257
256 p2 = None
258 p2 = None
257 if mergeable_file:
259 if mergeable_file:
258 fn = b"mf"
260 fn = b"mf"
259 p1 = repo[ps[0]]
261 p1 = repo[ps[0]]
260 if len(ps) > 1:
262 if len(ps) > 1:
261 p2 = repo[ps[1]]
263 p2 = repo[ps[1]]
262 pa = p1.ancestor(p2)
264 pa = p1.ancestor(p2)
263 base, local, other = [
265 base, local, other = [
264 x[fn].data() for x in (pa, p1, p2)
266 x[fn].data() for x in (pa, p1, p2)
265 ]
267 ]
266 m3 = simplemerge.Merge3Text(base, local, other)
268 m3 = simplemerge.Merge3Text(base, local, other)
267 ml = [l.strip() for l in m3.merge_lines()]
269 ml = [l.strip() for l in m3.merge_lines()]
268 ml.append(b"")
270 ml.append(b"")
269 elif at > 0:
271 elif at > 0:
270 ml = p1[fn].data().split(b"\n")
272 ml = p1[fn].data().split(b"\n")
271 else:
273 else:
272 ml = initialmergedlines
274 ml = initialmergedlines
273 ml[id * linesperrev] += b" r%i" % id
275 ml[id * linesperrev] += b" r%i" % id
274 mergedtext = b"\n".join(ml)
276 mergedtext = b"\n".join(ml)
275 files.append(fn)
277 files.append(fn)
276 filecontent[fn] = mergedtext
278 filecontent[fn] = mergedtext
277
279
278 if overwritten_file:
280 if overwritten_file:
279 fn = b"of"
281 fn = b"of"
280 files.append(fn)
282 files.append(fn)
281 filecontent[fn] = b"r%i\n" % id
283 filecontent[fn] = b"r%i\n" % id
282
284
283 if new_file:
285 if new_file:
284 fn = b"nf%i" % id
286 fn = b"nf%i" % id
285 files.append(fn)
287 files.append(fn)
286 filecontent[fn] = b"r%i\n" % id
288 filecontent[fn] = b"r%i\n" % id
287 if len(ps) > 1:
289 if len(ps) > 1:
288 if not p2:
290 if not p2:
289 p2 = repo[ps[1]]
291 p2 = repo[ps[1]]
290 for fn in p2:
292 for fn in p2:
291 if fn.startswith(b"nf"):
293 if fn.startswith(b"nf"):
292 files.append(fn)
294 files.append(fn)
293 filecontent[fn] = p2[fn].data()
295 filecontent[fn] = p2[fn].data()
294
296
295 def fctxfn(repo, cx, path):
297 def fctxfn(repo, cx, path):
296 if path in filecontent:
298 if path in filecontent:
297 return context.memfilectx(
299 return context.memfilectx(
298 repo, cx, path, filecontent[path]
300 repo, cx, path, filecontent[path]
299 )
301 )
300 return None
302 return None
301
303
302 if len(ps) == 0 or ps[0] < 0:
304 if len(ps) == 0 or ps[0] < 0:
303 pars = [None, None]
305 pars = [None, None]
304 elif len(ps) == 1:
306 elif len(ps) == 1:
305 pars = [nodeids[ps[0]], None]
307 pars = [nodeids[ps[0]], None]
306 else:
308 else:
307 pars = [nodeids[p] for p in ps]
309 pars = [nodeids[p] for p in ps]
308 cx = context.memctx(
310 cx = context.memctx(
309 repo,
311 repo,
310 pars,
312 pars,
311 b"r%i" % id,
313 b"r%i" % id,
312 files,
314 files,
313 fctxfn,
315 fctxfn,
314 date=(id, 0),
316 date=(id, 0),
315 user=b"debugbuilddag",
317 user=b"debugbuilddag",
316 extra={b'branch': atbranch},
318 extra={b'branch': atbranch},
317 )
319 )
318 nodeid = repo.commitctx(cx)
320 nodeid = repo.commitctx(cx)
319 nodeids.append(nodeid)
321 nodeids.append(nodeid)
320 at = id
322 at = id
321 elif type == b'l':
323 elif type == b'l':
322 id, name = data
324 id, name = data
323 ui.note((b'tag %s\n' % name))
325 ui.note((b'tag %s\n' % name))
324 tags.append(b"%s %s\n" % (hex(repo.changelog.node(id)), name))
326 tags.append(b"%s %s\n" % (hex(repo.changelog.node(id)), name))
325 elif type == b'a':
327 elif type == b'a':
326 ui.note((b'branch %s\n' % data))
328 ui.note((b'branch %s\n' % data))
327 atbranch = data
329 atbranch = data
328 progress.update(id)
330 progress.update(id)
329
331
330 if tags:
332 if tags:
331 repo.vfs.write(b"localtags", b"".join(tags))
333 repo.vfs.write(b"localtags", b"".join(tags))
332
334
333
335
334 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
336 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
335 indent_string = b' ' * indent
337 indent_string = b' ' * indent
336 if all:
338 if all:
337 ui.writenoi18n(
339 ui.writenoi18n(
338 b"%sformat: id, p1, p2, cset, delta base, len(delta)\n"
340 b"%sformat: id, p1, p2, cset, delta base, len(delta)\n"
339 % indent_string
341 % indent_string
340 )
342 )
341
343
342 def showchunks(named):
344 def showchunks(named):
343 ui.write(b"\n%s%s\n" % (indent_string, named))
345 ui.write(b"\n%s%s\n" % (indent_string, named))
344 for deltadata in gen.deltaiter():
346 for deltadata in gen.deltaiter():
345 node, p1, p2, cs, deltabase, delta, flags = deltadata
347 node, p1, p2, cs, deltabase, delta, flags = deltadata
346 ui.write(
348 ui.write(
347 b"%s%s %s %s %s %s %d\n"
349 b"%s%s %s %s %s %s %d\n"
348 % (
350 % (
349 indent_string,
351 indent_string,
350 hex(node),
352 hex(node),
351 hex(p1),
353 hex(p1),
352 hex(p2),
354 hex(p2),
353 hex(cs),
355 hex(cs),
354 hex(deltabase),
356 hex(deltabase),
355 len(delta),
357 len(delta),
356 )
358 )
357 )
359 )
358
360
359 gen.changelogheader()
361 gen.changelogheader()
360 showchunks(b"changelog")
362 showchunks(b"changelog")
361 gen.manifestheader()
363 gen.manifestheader()
362 showchunks(b"manifest")
364 showchunks(b"manifest")
363 for chunkdata in iter(gen.filelogheader, {}):
365 for chunkdata in iter(gen.filelogheader, {}):
364 fname = chunkdata[b'filename']
366 fname = chunkdata[b'filename']
365 showchunks(fname)
367 showchunks(fname)
366 else:
368 else:
367 if isinstance(gen, bundle2.unbundle20):
369 if isinstance(gen, bundle2.unbundle20):
368 raise error.Abort(_(b'use debugbundle2 for this file'))
370 raise error.Abort(_(b'use debugbundle2 for this file'))
369 gen.changelogheader()
371 gen.changelogheader()
370 for deltadata in gen.deltaiter():
372 for deltadata in gen.deltaiter():
371 node, p1, p2, cs, deltabase, delta, flags = deltadata
373 node, p1, p2, cs, deltabase, delta, flags = deltadata
372 ui.write(b"%s%s\n" % (indent_string, hex(node)))
374 ui.write(b"%s%s\n" % (indent_string, hex(node)))
373
375
374
376
375 def _debugobsmarkers(ui, part, indent=0, **opts):
377 def _debugobsmarkers(ui, part, indent=0, **opts):
376 """display version and markers contained in 'data'"""
378 """display version and markers contained in 'data'"""
377 opts = pycompat.byteskwargs(opts)
379 opts = pycompat.byteskwargs(opts)
378 data = part.read()
380 data = part.read()
379 indent_string = b' ' * indent
381 indent_string = b' ' * indent
380 try:
382 try:
381 version, markers = obsolete._readmarkers(data)
383 version, markers = obsolete._readmarkers(data)
382 except error.UnknownVersion as exc:
384 except error.UnknownVersion as exc:
383 msg = b"%sunsupported version: %s (%d bytes)\n"
385 msg = b"%sunsupported version: %s (%d bytes)\n"
384 msg %= indent_string, exc.version, len(data)
386 msg %= indent_string, exc.version, len(data)
385 ui.write(msg)
387 ui.write(msg)
386 else:
388 else:
387 msg = b"%sversion: %d (%d bytes)\n"
389 msg = b"%sversion: %d (%d bytes)\n"
388 msg %= indent_string, version, len(data)
390 msg %= indent_string, version, len(data)
389 ui.write(msg)
391 ui.write(msg)
390 fm = ui.formatter(b'debugobsolete', opts)
392 fm = ui.formatter(b'debugobsolete', opts)
391 for rawmarker in sorted(markers):
393 for rawmarker in sorted(markers):
392 m = obsutil.marker(None, rawmarker)
394 m = obsutil.marker(None, rawmarker)
393 fm.startitem()
395 fm.startitem()
394 fm.plain(indent_string)
396 fm.plain(indent_string)
395 cmdutil.showmarker(fm, m)
397 cmdutil.showmarker(fm, m)
396 fm.end()
398 fm.end()
397
399
398
400
399 def _debugphaseheads(ui, data, indent=0):
401 def _debugphaseheads(ui, data, indent=0):
400 """display version and markers contained in 'data'"""
402 """display version and markers contained in 'data'"""
401 indent_string = b' ' * indent
403 indent_string = b' ' * indent
402 headsbyphase = phases.binarydecode(data)
404 headsbyphase = phases.binarydecode(data)
403 for phase in phases.allphases:
405 for phase in phases.allphases:
404 for head in headsbyphase[phase]:
406 for head in headsbyphase[phase]:
405 ui.write(indent_string)
407 ui.write(indent_string)
406 ui.write(b'%s %s\n' % (hex(head), phases.phasenames[phase]))
408 ui.write(b'%s %s\n' % (hex(head), phases.phasenames[phase]))
407
409
408
410
409 def _quasirepr(thing):
411 def _quasirepr(thing):
410 if isinstance(thing, (dict, util.sortdict, collections.OrderedDict)):
412 if isinstance(thing, (dict, util.sortdict, collections.OrderedDict)):
411 return b'{%s}' % (
413 return b'{%s}' % (
412 b', '.join(b'%s: %s' % (k, thing[k]) for k in sorted(thing))
414 b', '.join(b'%s: %s' % (k, thing[k]) for k in sorted(thing))
413 )
415 )
414 return pycompat.bytestr(repr(thing))
416 return pycompat.bytestr(repr(thing))
415
417
416
418
417 def _debugbundle2(ui, gen, all=None, **opts):
419 def _debugbundle2(ui, gen, all=None, **opts):
418 """lists the contents of a bundle2"""
420 """lists the contents of a bundle2"""
419 if not isinstance(gen, bundle2.unbundle20):
421 if not isinstance(gen, bundle2.unbundle20):
420 raise error.Abort(_(b'not a bundle2 file'))
422 raise error.Abort(_(b'not a bundle2 file'))
421 ui.write((b'Stream params: %s\n' % _quasirepr(gen.params)))
423 ui.write((b'Stream params: %s\n' % _quasirepr(gen.params)))
422 parttypes = opts.get('part_type', [])
424 parttypes = opts.get('part_type', [])
423 for part in gen.iterparts():
425 for part in gen.iterparts():
424 if parttypes and part.type not in parttypes:
426 if parttypes and part.type not in parttypes:
425 continue
427 continue
426 msg = b'%s -- %s (mandatory: %r)\n'
428 msg = b'%s -- %s (mandatory: %r)\n'
427 ui.write((msg % (part.type, _quasirepr(part.params), part.mandatory)))
429 ui.write((msg % (part.type, _quasirepr(part.params), part.mandatory)))
428 if part.type == b'changegroup':
430 if part.type == b'changegroup':
429 version = part.params.get(b'version', b'01')
431 version = part.params.get(b'version', b'01')
430 cg = changegroup.getunbundler(version, part, b'UN')
432 cg = changegroup.getunbundler(version, part, b'UN')
431 if not ui.quiet:
433 if not ui.quiet:
432 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
434 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
433 if part.type == b'obsmarkers':
435 if part.type == b'obsmarkers':
434 if not ui.quiet:
436 if not ui.quiet:
435 _debugobsmarkers(ui, part, indent=4, **opts)
437 _debugobsmarkers(ui, part, indent=4, **opts)
436 if part.type == b'phase-heads':
438 if part.type == b'phase-heads':
437 if not ui.quiet:
439 if not ui.quiet:
438 _debugphaseheads(ui, part, indent=4)
440 _debugphaseheads(ui, part, indent=4)
439
441
440
442
441 @command(
443 @command(
442 b'debugbundle',
444 b'debugbundle',
443 [
445 [
444 (b'a', b'all', None, _(b'show all details')),
446 (b'a', b'all', None, _(b'show all details')),
445 (b'', b'part-type', [], _(b'show only the named part type')),
447 (b'', b'part-type', [], _(b'show only the named part type')),
446 (b'', b'spec', None, _(b'print the bundlespec of the bundle')),
448 (b'', b'spec', None, _(b'print the bundlespec of the bundle')),
447 ],
449 ],
448 _(b'FILE'),
450 _(b'FILE'),
449 norepo=True,
451 norepo=True,
450 )
452 )
451 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
453 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
452 """lists the contents of a bundle"""
454 """lists the contents of a bundle"""
453 with hg.openpath(ui, bundlepath) as f:
455 with hg.openpath(ui, bundlepath) as f:
454 if spec:
456 if spec:
455 spec = exchange.getbundlespec(ui, f)
457 spec = exchange.getbundlespec(ui, f)
456 ui.write(b'%s\n' % spec)
458 ui.write(b'%s\n' % spec)
457 return
459 return
458
460
459 gen = exchange.readbundle(ui, f, bundlepath)
461 gen = exchange.readbundle(ui, f, bundlepath)
460 if isinstance(gen, bundle2.unbundle20):
462 if isinstance(gen, bundle2.unbundle20):
461 return _debugbundle2(ui, gen, all=all, **opts)
463 return _debugbundle2(ui, gen, all=all, **opts)
462 _debugchangegroup(ui, gen, all=all, **opts)
464 _debugchangegroup(ui, gen, all=all, **opts)
463
465
464
466
465 @command(b'debugcapabilities', [], _(b'PATH'), norepo=True)
467 @command(b'debugcapabilities', [], _(b'PATH'), norepo=True)
466 def debugcapabilities(ui, path, **opts):
468 def debugcapabilities(ui, path, **opts):
467 """lists the capabilities of a remote peer"""
469 """lists the capabilities of a remote peer"""
468 opts = pycompat.byteskwargs(opts)
470 opts = pycompat.byteskwargs(opts)
469 peer = hg.peer(ui, opts, path)
471 peer = hg.peer(ui, opts, path)
470 caps = peer.capabilities()
472 caps = peer.capabilities()
471 ui.writenoi18n(b'Main capabilities:\n')
473 ui.writenoi18n(b'Main capabilities:\n')
472 for c in sorted(caps):
474 for c in sorted(caps):
473 ui.write(b' %s\n' % c)
475 ui.write(b' %s\n' % c)
474 b2caps = bundle2.bundle2caps(peer)
476 b2caps = bundle2.bundle2caps(peer)
475 if b2caps:
477 if b2caps:
476 ui.writenoi18n(b'Bundle2 capabilities:\n')
478 ui.writenoi18n(b'Bundle2 capabilities:\n')
477 for key, values in sorted(pycompat.iteritems(b2caps)):
479 for key, values in sorted(pycompat.iteritems(b2caps)):
478 ui.write(b' %s\n' % key)
480 ui.write(b' %s\n' % key)
479 for v in values:
481 for v in values:
480 ui.write(b' %s\n' % v)
482 ui.write(b' %s\n' % v)
481
483
482
484
483 @command(b'debugchangedfiles', [], b'REV')
485 @command(b'debugchangedfiles', [], b'REV')
484 def debugchangedfiles(ui, repo, rev):
486 def debugchangedfiles(ui, repo, rev):
485 """list the stored files changes for a revision"""
487 """list the stored files changes for a revision"""
486 ctx = scmutil.revsingle(repo, rev, None)
488 ctx = scmutil.revsingle(repo, rev, None)
487 sd = repo.changelog.sidedata(ctx.rev())
489 sd = repo.changelog.sidedata(ctx.rev())
488 files_block = sd.get(sidedata.SD_FILES)
490 files_block = sd.get(sidedata.SD_FILES)
489 if files_block is not None:
491 if files_block is not None:
490 files = metadata.decode_files_sidedata(sd)
492 files = metadata.decode_files_sidedata(sd)
491 for f in sorted(files.touched):
493 for f in sorted(files.touched):
492 if f in files.added:
494 if f in files.added:
493 action = b"added"
495 action = b"added"
494 elif f in files.removed:
496 elif f in files.removed:
495 action = b"removed"
497 action = b"removed"
496 elif f in files.merged:
498 elif f in files.merged:
497 action = b"merged"
499 action = b"merged"
498 elif f in files.salvaged:
500 elif f in files.salvaged:
499 action = b"salvaged"
501 action = b"salvaged"
500 else:
502 else:
501 action = b"touched"
503 action = b"touched"
502
504
503 copy_parent = b""
505 copy_parent = b""
504 copy_source = b""
506 copy_source = b""
505 if f in files.copied_from_p1:
507 if f in files.copied_from_p1:
506 copy_parent = b"p1"
508 copy_parent = b"p1"
507 copy_source = files.copied_from_p1[f]
509 copy_source = files.copied_from_p1[f]
508 elif f in files.copied_from_p2:
510 elif f in files.copied_from_p2:
509 copy_parent = b"p2"
511 copy_parent = b"p2"
510 copy_source = files.copied_from_p2[f]
512 copy_source = files.copied_from_p2[f]
511
513
512 data = (action, copy_parent, f, copy_source)
514 data = (action, copy_parent, f, copy_source)
513 template = b"%-8s %2s: %s, %s;\n"
515 template = b"%-8s %2s: %s, %s;\n"
514 ui.write(template % data)
516 ui.write(template % data)
515
517
516
518
517 @command(b'debugcheckstate', [], b'')
519 @command(b'debugcheckstate', [], b'')
518 def debugcheckstate(ui, repo):
520 def debugcheckstate(ui, repo):
519 """validate the correctness of the current dirstate"""
521 """validate the correctness of the current dirstate"""
520 parent1, parent2 = repo.dirstate.parents()
522 parent1, parent2 = repo.dirstate.parents()
521 m1 = repo[parent1].manifest()
523 m1 = repo[parent1].manifest()
522 m2 = repo[parent2].manifest()
524 m2 = repo[parent2].manifest()
523 errors = 0
525 errors = 0
524 for f in repo.dirstate:
526 for f in repo.dirstate:
525 state = repo.dirstate[f]
527 state = repo.dirstate[f]
526 if state in b"nr" and f not in m1:
528 if state in b"nr" and f not in m1:
527 ui.warn(_(b"%s in state %s, but not in manifest1\n") % (f, state))
529 ui.warn(_(b"%s in state %s, but not in manifest1\n") % (f, state))
528 errors += 1
530 errors += 1
529 if state in b"a" and f in m1:
531 if state in b"a" and f in m1:
530 ui.warn(_(b"%s in state %s, but also in manifest1\n") % (f, state))
532 ui.warn(_(b"%s in state %s, but also in manifest1\n") % (f, state))
531 errors += 1
533 errors += 1
532 if state in b"m" and f not in m1 and f not in m2:
534 if state in b"m" and f not in m1 and f not in m2:
533 ui.warn(
535 ui.warn(
534 _(b"%s in state %s, but not in either manifest\n") % (f, state)
536 _(b"%s in state %s, but not in either manifest\n") % (f, state)
535 )
537 )
536 errors += 1
538 errors += 1
537 for f in m1:
539 for f in m1:
538 state = repo.dirstate[f]
540 state = repo.dirstate[f]
539 if state not in b"nrm":
541 if state not in b"nrm":
540 ui.warn(_(b"%s in manifest1, but listed as state %s") % (f, state))
542 ui.warn(_(b"%s in manifest1, but listed as state %s") % (f, state))
541 errors += 1
543 errors += 1
542 if errors:
544 if errors:
543 errstr = _(b".hg/dirstate inconsistent with current parent's manifest")
545 errstr = _(b".hg/dirstate inconsistent with current parent's manifest")
544 raise error.Abort(errstr)
546 raise error.Abort(errstr)
545
547
546
548
547 @command(
549 @command(
548 b'debugcolor',
550 b'debugcolor',
549 [(b'', b'style', None, _(b'show all configured styles'))],
551 [(b'', b'style', None, _(b'show all configured styles'))],
550 b'hg debugcolor',
552 b'hg debugcolor',
551 )
553 )
552 def debugcolor(ui, repo, **opts):
554 def debugcolor(ui, repo, **opts):
553 """show available color, effects or style"""
555 """show available color, effects or style"""
554 ui.writenoi18n(b'color mode: %s\n' % stringutil.pprint(ui._colormode))
556 ui.writenoi18n(b'color mode: %s\n' % stringutil.pprint(ui._colormode))
555 if opts.get('style'):
557 if opts.get('style'):
556 return _debugdisplaystyle(ui)
558 return _debugdisplaystyle(ui)
557 else:
559 else:
558 return _debugdisplaycolor(ui)
560 return _debugdisplaycolor(ui)
559
561
560
562
561 def _debugdisplaycolor(ui):
563 def _debugdisplaycolor(ui):
562 ui = ui.copy()
564 ui = ui.copy()
563 ui._styles.clear()
565 ui._styles.clear()
564 for effect in color._activeeffects(ui).keys():
566 for effect in color._activeeffects(ui).keys():
565 ui._styles[effect] = effect
567 ui._styles[effect] = effect
566 if ui._terminfoparams:
568 if ui._terminfoparams:
567 for k, v in ui.configitems(b'color'):
569 for k, v in ui.configitems(b'color'):
568 if k.startswith(b'color.'):
570 if k.startswith(b'color.'):
569 ui._styles[k] = k[6:]
571 ui._styles[k] = k[6:]
570 elif k.startswith(b'terminfo.'):
572 elif k.startswith(b'terminfo.'):
571 ui._styles[k] = k[9:]
573 ui._styles[k] = k[9:]
572 ui.write(_(b'available colors:\n'))
574 ui.write(_(b'available colors:\n'))
573 # sort label with a '_' after the other to group '_background' entry.
575 # sort label with a '_' after the other to group '_background' entry.
574 items = sorted(ui._styles.items(), key=lambda i: (b'_' in i[0], i[0], i[1]))
576 items = sorted(ui._styles.items(), key=lambda i: (b'_' in i[0], i[0], i[1]))
575 for colorname, label in items:
577 for colorname, label in items:
576 ui.write(b'%s\n' % colorname, label=label)
578 ui.write(b'%s\n' % colorname, label=label)
577
579
578
580
579 def _debugdisplaystyle(ui):
581 def _debugdisplaystyle(ui):
580 ui.write(_(b'available style:\n'))
582 ui.write(_(b'available style:\n'))
581 if not ui._styles:
583 if not ui._styles:
582 return
584 return
583 width = max(len(s) for s in ui._styles)
585 width = max(len(s) for s in ui._styles)
584 for label, effects in sorted(ui._styles.items()):
586 for label, effects in sorted(ui._styles.items()):
585 ui.write(b'%s' % label, label=label)
587 ui.write(b'%s' % label, label=label)
586 if effects:
588 if effects:
587 # 50
589 # 50
588 ui.write(b': ')
590 ui.write(b': ')
589 ui.write(b' ' * (max(0, width - len(label))))
591 ui.write(b' ' * (max(0, width - len(label))))
590 ui.write(b', '.join(ui.label(e, e) for e in effects.split()))
592 ui.write(b', '.join(ui.label(e, e) for e in effects.split()))
591 ui.write(b'\n')
593 ui.write(b'\n')
592
594
593
595
594 @command(b'debugcreatestreamclonebundle', [], b'FILE')
596 @command(b'debugcreatestreamclonebundle', [], b'FILE')
595 def debugcreatestreamclonebundle(ui, repo, fname):
597 def debugcreatestreamclonebundle(ui, repo, fname):
596 """create a stream clone bundle file
598 """create a stream clone bundle file
597
599
598 Stream bundles are special bundles that are essentially archives of
600 Stream bundles are special bundles that are essentially archives of
599 revlog files. They are commonly used for cloning very quickly.
601 revlog files. They are commonly used for cloning very quickly.
600 """
602 """
601 # TODO we may want to turn this into an abort when this functionality
603 # TODO we may want to turn this into an abort when this functionality
602 # is moved into `hg bundle`.
604 # is moved into `hg bundle`.
603 if phases.hassecret(repo):
605 if phases.hassecret(repo):
604 ui.warn(
606 ui.warn(
605 _(
607 _(
606 b'(warning: stream clone bundle will contain secret '
608 b'(warning: stream clone bundle will contain secret '
607 b'revisions)\n'
609 b'revisions)\n'
608 )
610 )
609 )
611 )
610
612
611 requirements, gen = streamclone.generatebundlev1(repo)
613 requirements, gen = streamclone.generatebundlev1(repo)
612 changegroup.writechunks(ui, gen, fname)
614 changegroup.writechunks(ui, gen, fname)
613
615
614 ui.write(_(b'bundle requirements: %s\n') % b', '.join(sorted(requirements)))
616 ui.write(_(b'bundle requirements: %s\n') % b', '.join(sorted(requirements)))
615
617
616
618
617 @command(
619 @command(
618 b'debugdag',
620 b'debugdag',
619 [
621 [
620 (b't', b'tags', None, _(b'use tags as labels')),
622 (b't', b'tags', None, _(b'use tags as labels')),
621 (b'b', b'branches', None, _(b'annotate with branch names')),
623 (b'b', b'branches', None, _(b'annotate with branch names')),
622 (b'', b'dots', None, _(b'use dots for runs')),
624 (b'', b'dots', None, _(b'use dots for runs')),
623 (b's', b'spaces', None, _(b'separate elements by spaces')),
625 (b's', b'spaces', None, _(b'separate elements by spaces')),
624 ],
626 ],
625 _(b'[OPTION]... [FILE [REV]...]'),
627 _(b'[OPTION]... [FILE [REV]...]'),
626 optionalrepo=True,
628 optionalrepo=True,
627 )
629 )
628 def debugdag(ui, repo, file_=None, *revs, **opts):
630 def debugdag(ui, repo, file_=None, *revs, **opts):
629 """format the changelog or an index DAG as a concise textual description
631 """format the changelog or an index DAG as a concise textual description
630
632
631 If you pass a revlog index, the revlog's DAG is emitted. If you list
633 If you pass a revlog index, the revlog's DAG is emitted. If you list
632 revision numbers, they get labeled in the output as rN.
634 revision numbers, they get labeled in the output as rN.
633
635
634 Otherwise, the changelog DAG of the current repo is emitted.
636 Otherwise, the changelog DAG of the current repo is emitted.
635 """
637 """
636 spaces = opts.get('spaces')
638 spaces = opts.get('spaces')
637 dots = opts.get('dots')
639 dots = opts.get('dots')
638 if file_:
640 if file_:
639 rlog = revlog.revlog(vfsmod.vfs(encoding.getcwd(), audit=False), file_)
641 rlog = revlog.revlog(vfsmod.vfs(encoding.getcwd(), audit=False), file_)
640 revs = {int(r) for r in revs}
642 revs = {int(r) for r in revs}
641
643
642 def events():
644 def events():
643 for r in rlog:
645 for r in rlog:
644 yield b'n', (r, list(p for p in rlog.parentrevs(r) if p != -1))
646 yield b'n', (r, list(p for p in rlog.parentrevs(r) if p != -1))
645 if r in revs:
647 if r in revs:
646 yield b'l', (r, b"r%i" % r)
648 yield b'l', (r, b"r%i" % r)
647
649
648 elif repo:
650 elif repo:
649 cl = repo.changelog
651 cl = repo.changelog
650 tags = opts.get('tags')
652 tags = opts.get('tags')
651 branches = opts.get('branches')
653 branches = opts.get('branches')
652 if tags:
654 if tags:
653 labels = {}
655 labels = {}
654 for l, n in repo.tags().items():
656 for l, n in repo.tags().items():
655 labels.setdefault(cl.rev(n), []).append(l)
657 labels.setdefault(cl.rev(n), []).append(l)
656
658
657 def events():
659 def events():
658 b = b"default"
660 b = b"default"
659 for r in cl:
661 for r in cl:
660 if branches:
662 if branches:
661 newb = cl.read(cl.node(r))[5][b'branch']
663 newb = cl.read(cl.node(r))[5][b'branch']
662 if newb != b:
664 if newb != b:
663 yield b'a', newb
665 yield b'a', newb
664 b = newb
666 b = newb
665 yield b'n', (r, list(p for p in cl.parentrevs(r) if p != -1))
667 yield b'n', (r, list(p for p in cl.parentrevs(r) if p != -1))
666 if tags:
668 if tags:
667 ls = labels.get(r)
669 ls = labels.get(r)
668 if ls:
670 if ls:
669 for l in ls:
671 for l in ls:
670 yield b'l', (r, l)
672 yield b'l', (r, l)
671
673
672 else:
674 else:
673 raise error.Abort(_(b'need repo for changelog dag'))
675 raise error.Abort(_(b'need repo for changelog dag'))
674
676
675 for line in dagparser.dagtextlines(
677 for line in dagparser.dagtextlines(
676 events(),
678 events(),
677 addspaces=spaces,
679 addspaces=spaces,
678 wraplabels=True,
680 wraplabels=True,
679 wrapannotations=True,
681 wrapannotations=True,
680 wrapnonlinear=dots,
682 wrapnonlinear=dots,
681 usedots=dots,
683 usedots=dots,
682 maxlinewidth=70,
684 maxlinewidth=70,
683 ):
685 ):
684 ui.write(line)
686 ui.write(line)
685 ui.write(b"\n")
687 ui.write(b"\n")
686
688
687
689
688 @command(b'debugdata', cmdutil.debugrevlogopts, _(b'-c|-m|FILE REV'))
690 @command(b'debugdata', cmdutil.debugrevlogopts, _(b'-c|-m|FILE REV'))
689 def debugdata(ui, repo, file_, rev=None, **opts):
691 def debugdata(ui, repo, file_, rev=None, **opts):
690 """dump the contents of a data file revision"""
692 """dump the contents of a data file revision"""
691 opts = pycompat.byteskwargs(opts)
693 opts = pycompat.byteskwargs(opts)
692 if opts.get(b'changelog') or opts.get(b'manifest') or opts.get(b'dir'):
694 if opts.get(b'changelog') or opts.get(b'manifest') or opts.get(b'dir'):
693 if rev is not None:
695 if rev is not None:
694 raise error.CommandError(b'debugdata', _(b'invalid arguments'))
696 raise error.CommandError(b'debugdata', _(b'invalid arguments'))
695 file_, rev = None, file_
697 file_, rev = None, file_
696 elif rev is None:
698 elif rev is None:
697 raise error.CommandError(b'debugdata', _(b'invalid arguments'))
699 raise error.CommandError(b'debugdata', _(b'invalid arguments'))
698 r = cmdutil.openstorage(repo, b'debugdata', file_, opts)
700 r = cmdutil.openstorage(repo, b'debugdata', file_, opts)
699 try:
701 try:
700 ui.write(r.rawdata(r.lookup(rev)))
702 ui.write(r.rawdata(r.lookup(rev)))
701 except KeyError:
703 except KeyError:
702 raise error.Abort(_(b'invalid revision identifier %s') % rev)
704 raise error.Abort(_(b'invalid revision identifier %s') % rev)
703
705
704
706
705 @command(
707 @command(
706 b'debugdate',
708 b'debugdate',
707 [(b'e', b'extended', None, _(b'try extended date formats'))],
709 [(b'e', b'extended', None, _(b'try extended date formats'))],
708 _(b'[-e] DATE [RANGE]'),
710 _(b'[-e] DATE [RANGE]'),
709 norepo=True,
711 norepo=True,
710 optionalrepo=True,
712 optionalrepo=True,
711 )
713 )
712 def debugdate(ui, date, range=None, **opts):
714 def debugdate(ui, date, range=None, **opts):
713 """parse and display a date"""
715 """parse and display a date"""
714 if opts["extended"]:
716 if opts["extended"]:
715 d = dateutil.parsedate(date, dateutil.extendeddateformats)
717 d = dateutil.parsedate(date, dateutil.extendeddateformats)
716 else:
718 else:
717 d = dateutil.parsedate(date)
719 d = dateutil.parsedate(date)
718 ui.writenoi18n(b"internal: %d %d\n" % d)
720 ui.writenoi18n(b"internal: %d %d\n" % d)
719 ui.writenoi18n(b"standard: %s\n" % dateutil.datestr(d))
721 ui.writenoi18n(b"standard: %s\n" % dateutil.datestr(d))
720 if range:
722 if range:
721 m = dateutil.matchdate(range)
723 m = dateutil.matchdate(range)
722 ui.writenoi18n(b"match: %s\n" % m(d[0]))
724 ui.writenoi18n(b"match: %s\n" % m(d[0]))
723
725
724
726
725 @command(
727 @command(
726 b'debugdeltachain',
728 b'debugdeltachain',
727 cmdutil.debugrevlogopts + cmdutil.formatteropts,
729 cmdutil.debugrevlogopts + cmdutil.formatteropts,
728 _(b'-c|-m|FILE'),
730 _(b'-c|-m|FILE'),
729 optionalrepo=True,
731 optionalrepo=True,
730 )
732 )
731 def debugdeltachain(ui, repo, file_=None, **opts):
733 def debugdeltachain(ui, repo, file_=None, **opts):
732 """dump information about delta chains in a revlog
734 """dump information about delta chains in a revlog
733
735
734 Output can be templatized. Available template keywords are:
736 Output can be templatized. Available template keywords are:
735
737
736 :``rev``: revision number
738 :``rev``: revision number
737 :``chainid``: delta chain identifier (numbered by unique base)
739 :``chainid``: delta chain identifier (numbered by unique base)
738 :``chainlen``: delta chain length to this revision
740 :``chainlen``: delta chain length to this revision
739 :``prevrev``: previous revision in delta chain
741 :``prevrev``: previous revision in delta chain
740 :``deltatype``: role of delta / how it was computed
742 :``deltatype``: role of delta / how it was computed
741 :``compsize``: compressed size of revision
743 :``compsize``: compressed size of revision
742 :``uncompsize``: uncompressed size of revision
744 :``uncompsize``: uncompressed size of revision
743 :``chainsize``: total size of compressed revisions in chain
745 :``chainsize``: total size of compressed revisions in chain
744 :``chainratio``: total chain size divided by uncompressed revision size
746 :``chainratio``: total chain size divided by uncompressed revision size
745 (new delta chains typically start at ratio 2.00)
747 (new delta chains typically start at ratio 2.00)
746 :``lindist``: linear distance from base revision in delta chain to end
748 :``lindist``: linear distance from base revision in delta chain to end
747 of this revision
749 of this revision
748 :``extradist``: total size of revisions not part of this delta chain from
750 :``extradist``: total size of revisions not part of this delta chain from
749 base of delta chain to end of this revision; a measurement
751 base of delta chain to end of this revision; a measurement
750 of how much extra data we need to read/seek across to read
752 of how much extra data we need to read/seek across to read
751 the delta chain for this revision
753 the delta chain for this revision
752 :``extraratio``: extradist divided by chainsize; another representation of
754 :``extraratio``: extradist divided by chainsize; another representation of
753 how much unrelated data is needed to load this delta chain
755 how much unrelated data is needed to load this delta chain
754
756
755 If the repository is configured to use the sparse read, additional keywords
757 If the repository is configured to use the sparse read, additional keywords
756 are available:
758 are available:
757
759
758 :``readsize``: total size of data read from the disk for a revision
760 :``readsize``: total size of data read from the disk for a revision
759 (sum of the sizes of all the blocks)
761 (sum of the sizes of all the blocks)
760 :``largestblock``: size of the largest block of data read from the disk
762 :``largestblock``: size of the largest block of data read from the disk
761 :``readdensity``: density of useful bytes in the data read from the disk
763 :``readdensity``: density of useful bytes in the data read from the disk
762 :``srchunks``: in how many data hunks the whole revision would be read
764 :``srchunks``: in how many data hunks the whole revision would be read
763
765
764 The sparse read can be enabled with experimental.sparse-read = True
766 The sparse read can be enabled with experimental.sparse-read = True
765 """
767 """
766 opts = pycompat.byteskwargs(opts)
768 opts = pycompat.byteskwargs(opts)
767 r = cmdutil.openrevlog(repo, b'debugdeltachain', file_, opts)
769 r = cmdutil.openrevlog(repo, b'debugdeltachain', file_, opts)
768 index = r.index
770 index = r.index
769 start = r.start
771 start = r.start
770 length = r.length
772 length = r.length
771 generaldelta = r.version & revlog.FLAG_GENERALDELTA
773 generaldelta = r.version & revlog.FLAG_GENERALDELTA
772 withsparseread = getattr(r, '_withsparseread', False)
774 withsparseread = getattr(r, '_withsparseread', False)
773
775
774 def revinfo(rev):
776 def revinfo(rev):
775 e = index[rev]
777 e = index[rev]
776 compsize = e[1]
778 compsize = e[1]
777 uncompsize = e[2]
779 uncompsize = e[2]
778 chainsize = 0
780 chainsize = 0
779
781
780 if generaldelta:
782 if generaldelta:
781 if e[3] == e[5]:
783 if e[3] == e[5]:
782 deltatype = b'p1'
784 deltatype = b'p1'
783 elif e[3] == e[6]:
785 elif e[3] == e[6]:
784 deltatype = b'p2'
786 deltatype = b'p2'
785 elif e[3] == rev - 1:
787 elif e[3] == rev - 1:
786 deltatype = b'prev'
788 deltatype = b'prev'
787 elif e[3] == rev:
789 elif e[3] == rev:
788 deltatype = b'base'
790 deltatype = b'base'
789 else:
791 else:
790 deltatype = b'other'
792 deltatype = b'other'
791 else:
793 else:
792 if e[3] == rev:
794 if e[3] == rev:
793 deltatype = b'base'
795 deltatype = b'base'
794 else:
796 else:
795 deltatype = b'prev'
797 deltatype = b'prev'
796
798
797 chain = r._deltachain(rev)[0]
799 chain = r._deltachain(rev)[0]
798 for iterrev in chain:
800 for iterrev in chain:
799 e = index[iterrev]
801 e = index[iterrev]
800 chainsize += e[1]
802 chainsize += e[1]
801
803
802 return compsize, uncompsize, deltatype, chain, chainsize
804 return compsize, uncompsize, deltatype, chain, chainsize
803
805
804 fm = ui.formatter(b'debugdeltachain', opts)
806 fm = ui.formatter(b'debugdeltachain', opts)
805
807
806 fm.plain(
808 fm.plain(
807 b' rev chain# chainlen prev delta '
809 b' rev chain# chainlen prev delta '
808 b'size rawsize chainsize ratio lindist extradist '
810 b'size rawsize chainsize ratio lindist extradist '
809 b'extraratio'
811 b'extraratio'
810 )
812 )
811 if withsparseread:
813 if withsparseread:
812 fm.plain(b' readsize largestblk rddensity srchunks')
814 fm.plain(b' readsize largestblk rddensity srchunks')
813 fm.plain(b'\n')
815 fm.plain(b'\n')
814
816
815 chainbases = {}
817 chainbases = {}
816 for rev in r:
818 for rev in r:
817 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
819 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
818 chainbase = chain[0]
820 chainbase = chain[0]
819 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
821 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
820 basestart = start(chainbase)
822 basestart = start(chainbase)
821 revstart = start(rev)
823 revstart = start(rev)
822 lineardist = revstart + comp - basestart
824 lineardist = revstart + comp - basestart
823 extradist = lineardist - chainsize
825 extradist = lineardist - chainsize
824 try:
826 try:
825 prevrev = chain[-2]
827 prevrev = chain[-2]
826 except IndexError:
828 except IndexError:
827 prevrev = -1
829 prevrev = -1
828
830
829 if uncomp != 0:
831 if uncomp != 0:
830 chainratio = float(chainsize) / float(uncomp)
832 chainratio = float(chainsize) / float(uncomp)
831 else:
833 else:
832 chainratio = chainsize
834 chainratio = chainsize
833
835
834 if chainsize != 0:
836 if chainsize != 0:
835 extraratio = float(extradist) / float(chainsize)
837 extraratio = float(extradist) / float(chainsize)
836 else:
838 else:
837 extraratio = extradist
839 extraratio = extradist
838
840
839 fm.startitem()
841 fm.startitem()
840 fm.write(
842 fm.write(
841 b'rev chainid chainlen prevrev deltatype compsize '
843 b'rev chainid chainlen prevrev deltatype compsize '
842 b'uncompsize chainsize chainratio lindist extradist '
844 b'uncompsize chainsize chainratio lindist extradist '
843 b'extraratio',
845 b'extraratio',
844 b'%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f',
846 b'%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f',
845 rev,
847 rev,
846 chainid,
848 chainid,
847 len(chain),
849 len(chain),
848 prevrev,
850 prevrev,
849 deltatype,
851 deltatype,
850 comp,
852 comp,
851 uncomp,
853 uncomp,
852 chainsize,
854 chainsize,
853 chainratio,
855 chainratio,
854 lineardist,
856 lineardist,
855 extradist,
857 extradist,
856 extraratio,
858 extraratio,
857 rev=rev,
859 rev=rev,
858 chainid=chainid,
860 chainid=chainid,
859 chainlen=len(chain),
861 chainlen=len(chain),
860 prevrev=prevrev,
862 prevrev=prevrev,
861 deltatype=deltatype,
863 deltatype=deltatype,
862 compsize=comp,
864 compsize=comp,
863 uncompsize=uncomp,
865 uncompsize=uncomp,
864 chainsize=chainsize,
866 chainsize=chainsize,
865 chainratio=chainratio,
867 chainratio=chainratio,
866 lindist=lineardist,
868 lindist=lineardist,
867 extradist=extradist,
869 extradist=extradist,
868 extraratio=extraratio,
870 extraratio=extraratio,
869 )
871 )
870 if withsparseread:
872 if withsparseread:
871 readsize = 0
873 readsize = 0
872 largestblock = 0
874 largestblock = 0
873 srchunks = 0
875 srchunks = 0
874
876
875 for revschunk in deltautil.slicechunk(r, chain):
877 for revschunk in deltautil.slicechunk(r, chain):
876 srchunks += 1
878 srchunks += 1
877 blkend = start(revschunk[-1]) + length(revschunk[-1])
879 blkend = start(revschunk[-1]) + length(revschunk[-1])
878 blksize = blkend - start(revschunk[0])
880 blksize = blkend - start(revschunk[0])
879
881
880 readsize += blksize
882 readsize += blksize
881 if largestblock < blksize:
883 if largestblock < blksize:
882 largestblock = blksize
884 largestblock = blksize
883
885
884 if readsize:
886 if readsize:
885 readdensity = float(chainsize) / float(readsize)
887 readdensity = float(chainsize) / float(readsize)
886 else:
888 else:
887 readdensity = 1
889 readdensity = 1
888
890
889 fm.write(
891 fm.write(
890 b'readsize largestblock readdensity srchunks',
892 b'readsize largestblock readdensity srchunks',
891 b' %10d %10d %9.5f %8d',
893 b' %10d %10d %9.5f %8d',
892 readsize,
894 readsize,
893 largestblock,
895 largestblock,
894 readdensity,
896 readdensity,
895 srchunks,
897 srchunks,
896 readsize=readsize,
898 readsize=readsize,
897 largestblock=largestblock,
899 largestblock=largestblock,
898 readdensity=readdensity,
900 readdensity=readdensity,
899 srchunks=srchunks,
901 srchunks=srchunks,
900 )
902 )
901
903
902 fm.plain(b'\n')
904 fm.plain(b'\n')
903
905
904 fm.end()
906 fm.end()
905
907
906
908
907 @command(
909 @command(
908 b'debugdirstate|debugstate',
910 b'debugdirstate|debugstate',
909 [
911 [
910 (
912 (
911 b'',
913 b'',
912 b'nodates',
914 b'nodates',
913 None,
915 None,
914 _(b'do not display the saved mtime (DEPRECATED)'),
916 _(b'do not display the saved mtime (DEPRECATED)'),
915 ),
917 ),
916 (b'', b'dates', True, _(b'display the saved mtime')),
918 (b'', b'dates', True, _(b'display the saved mtime')),
917 (b'', b'datesort', None, _(b'sort by saved mtime')),
919 (b'', b'datesort', None, _(b'sort by saved mtime')),
918 ],
920 ],
919 _(b'[OPTION]...'),
921 _(b'[OPTION]...'),
920 )
922 )
921 def debugstate(ui, repo, **opts):
923 def debugstate(ui, repo, **opts):
922 """show the contents of the current dirstate"""
924 """show the contents of the current dirstate"""
923
925
924 nodates = not opts['dates']
926 nodates = not opts['dates']
925 if opts.get('nodates') is not None:
927 if opts.get('nodates') is not None:
926 nodates = True
928 nodates = True
927 datesort = opts.get('datesort')
929 datesort = opts.get('datesort')
928
930
929 if datesort:
931 if datesort:
930 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
932 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
931 else:
933 else:
932 keyfunc = None # sort by filename
934 keyfunc = None # sort by filename
933 for file_, ent in sorted(pycompat.iteritems(repo.dirstate), key=keyfunc):
935 for file_, ent in sorted(pycompat.iteritems(repo.dirstate), key=keyfunc):
934 if ent[3] == -1:
936 if ent[3] == -1:
935 timestr = b'unset '
937 timestr = b'unset '
936 elif nodates:
938 elif nodates:
937 timestr = b'set '
939 timestr = b'set '
938 else:
940 else:
939 timestr = time.strftime(
941 timestr = time.strftime(
940 "%Y-%m-%d %H:%M:%S ", time.localtime(ent[3])
942 "%Y-%m-%d %H:%M:%S ", time.localtime(ent[3])
941 )
943 )
942 timestr = encoding.strtolocal(timestr)
944 timestr = encoding.strtolocal(timestr)
943 if ent[1] & 0o20000:
945 if ent[1] & 0o20000:
944 mode = b'lnk'
946 mode = b'lnk'
945 else:
947 else:
946 mode = b'%3o' % (ent[1] & 0o777 & ~util.umask)
948 mode = b'%3o' % (ent[1] & 0o777 & ~util.umask)
947 ui.write(b"%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
949 ui.write(b"%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
948 for f in repo.dirstate.copies():
950 for f in repo.dirstate.copies():
949 ui.write(_(b"copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
951 ui.write(_(b"copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
950
952
951
953
952 @command(
954 @command(
953 b'debugdiscovery',
955 b'debugdiscovery',
954 [
956 [
955 (b'', b'old', None, _(b'use old-style discovery')),
957 (b'', b'old', None, _(b'use old-style discovery')),
956 (
958 (
957 b'',
959 b'',
958 b'nonheads',
960 b'nonheads',
959 None,
961 None,
960 _(b'use old-style discovery with non-heads included'),
962 _(b'use old-style discovery with non-heads included'),
961 ),
963 ),
962 (b'', b'rev', [], b'restrict discovery to this set of revs'),
964 (b'', b'rev', [], b'restrict discovery to this set of revs'),
963 (b'', b'seed', b'12323', b'specify the random seed use for discovery'),
965 (b'', b'seed', b'12323', b'specify the random seed use for discovery'),
964 ]
966 ]
965 + cmdutil.remoteopts,
967 + cmdutil.remoteopts,
966 _(b'[--rev REV] [OTHER]'),
968 _(b'[--rev REV] [OTHER]'),
967 )
969 )
968 def debugdiscovery(ui, repo, remoteurl=b"default", **opts):
970 def debugdiscovery(ui, repo, remoteurl=b"default", **opts):
969 """runs the changeset discovery protocol in isolation"""
971 """runs the changeset discovery protocol in isolation"""
970 opts = pycompat.byteskwargs(opts)
972 opts = pycompat.byteskwargs(opts)
971 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl))
973 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl))
972 remote = hg.peer(repo, opts, remoteurl)
974 remote = hg.peer(repo, opts, remoteurl)
973 ui.status(_(b'comparing with %s\n') % util.hidepassword(remoteurl))
975 ui.status(_(b'comparing with %s\n') % util.hidepassword(remoteurl))
974
976
975 # make sure tests are repeatable
977 # make sure tests are repeatable
976 random.seed(int(opts[b'seed']))
978 random.seed(int(opts[b'seed']))
977
979
978 if opts.get(b'old'):
980 if opts.get(b'old'):
979
981
980 def doit(pushedrevs, remoteheads, remote=remote):
982 def doit(pushedrevs, remoteheads, remote=remote):
981 if not util.safehasattr(remote, b'branches'):
983 if not util.safehasattr(remote, b'branches'):
982 # enable in-client legacy support
984 # enable in-client legacy support
983 remote = localrepo.locallegacypeer(remote.local())
985 remote = localrepo.locallegacypeer(remote.local())
984 common, _in, hds = treediscovery.findcommonincoming(
986 common, _in, hds = treediscovery.findcommonincoming(
985 repo, remote, force=True
987 repo, remote, force=True
986 )
988 )
987 common = set(common)
989 common = set(common)
988 if not opts.get(b'nonheads'):
990 if not opts.get(b'nonheads'):
989 ui.writenoi18n(
991 ui.writenoi18n(
990 b"unpruned common: %s\n"
992 b"unpruned common: %s\n"
991 % b" ".join(sorted(short(n) for n in common))
993 % b" ".join(sorted(short(n) for n in common))
992 )
994 )
993
995
994 clnode = repo.changelog.node
996 clnode = repo.changelog.node
995 common = repo.revs(b'heads(::%ln)', common)
997 common = repo.revs(b'heads(::%ln)', common)
996 common = {clnode(r) for r in common}
998 common = {clnode(r) for r in common}
997 return common, hds
999 return common, hds
998
1000
999 else:
1001 else:
1000
1002
1001 def doit(pushedrevs, remoteheads, remote=remote):
1003 def doit(pushedrevs, remoteheads, remote=remote):
1002 nodes = None
1004 nodes = None
1003 if pushedrevs:
1005 if pushedrevs:
1004 revs = scmutil.revrange(repo, pushedrevs)
1006 revs = scmutil.revrange(repo, pushedrevs)
1005 nodes = [repo[r].node() for r in revs]
1007 nodes = [repo[r].node() for r in revs]
1006 common, any, hds = setdiscovery.findcommonheads(
1008 common, any, hds = setdiscovery.findcommonheads(
1007 ui, repo, remote, ancestorsof=nodes
1009 ui, repo, remote, ancestorsof=nodes
1008 )
1010 )
1009 return common, hds
1011 return common, hds
1010
1012
1011 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches, revs=None)
1013 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches, revs=None)
1012 localrevs = opts[b'rev']
1014 localrevs = opts[b'rev']
1013 with util.timedcm('debug-discovery') as t:
1015 with util.timedcm('debug-discovery') as t:
1014 common, hds = doit(localrevs, remoterevs)
1016 common, hds = doit(localrevs, remoterevs)
1015
1017
1016 # compute all statistics
1018 # compute all statistics
1017 common = set(common)
1019 common = set(common)
1018 rheads = set(hds)
1020 rheads = set(hds)
1019 lheads = set(repo.heads())
1021 lheads = set(repo.heads())
1020
1022
1021 data = {}
1023 data = {}
1022 data[b'elapsed'] = t.elapsed
1024 data[b'elapsed'] = t.elapsed
1023 data[b'nb-common'] = len(common)
1025 data[b'nb-common'] = len(common)
1024 data[b'nb-common-local'] = len(common & lheads)
1026 data[b'nb-common-local'] = len(common & lheads)
1025 data[b'nb-common-remote'] = len(common & rheads)
1027 data[b'nb-common-remote'] = len(common & rheads)
1026 data[b'nb-common-both'] = len(common & rheads & lheads)
1028 data[b'nb-common-both'] = len(common & rheads & lheads)
1027 data[b'nb-local'] = len(lheads)
1029 data[b'nb-local'] = len(lheads)
1028 data[b'nb-local-missing'] = data[b'nb-local'] - data[b'nb-common-local']
1030 data[b'nb-local-missing'] = data[b'nb-local'] - data[b'nb-common-local']
1029 data[b'nb-remote'] = len(rheads)
1031 data[b'nb-remote'] = len(rheads)
1030 data[b'nb-remote-unknown'] = data[b'nb-remote'] - data[b'nb-common-remote']
1032 data[b'nb-remote-unknown'] = data[b'nb-remote'] - data[b'nb-common-remote']
1031 data[b'nb-revs'] = len(repo.revs(b'all()'))
1033 data[b'nb-revs'] = len(repo.revs(b'all()'))
1032 data[b'nb-revs-common'] = len(repo.revs(b'::%ln', common))
1034 data[b'nb-revs-common'] = len(repo.revs(b'::%ln', common))
1033 data[b'nb-revs-missing'] = data[b'nb-revs'] - data[b'nb-revs-common']
1035 data[b'nb-revs-missing'] = data[b'nb-revs'] - data[b'nb-revs-common']
1034
1036
1035 # display discovery summary
1037 # display discovery summary
1036 ui.writenoi18n(b"elapsed time: %(elapsed)f seconds\n" % data)
1038 ui.writenoi18n(b"elapsed time: %(elapsed)f seconds\n" % data)
1037 ui.writenoi18n(b"heads summary:\n")
1039 ui.writenoi18n(b"heads summary:\n")
1038 ui.writenoi18n(b" total common heads: %(nb-common)9d\n" % data)
1040 ui.writenoi18n(b" total common heads: %(nb-common)9d\n" % data)
1039 ui.writenoi18n(b" also local heads: %(nb-common-local)9d\n" % data)
1041 ui.writenoi18n(b" also local heads: %(nb-common-local)9d\n" % data)
1040 ui.writenoi18n(b" also remote heads: %(nb-common-remote)9d\n" % data)
1042 ui.writenoi18n(b" also remote heads: %(nb-common-remote)9d\n" % data)
1041 ui.writenoi18n(b" both: %(nb-common-both)9d\n" % data)
1043 ui.writenoi18n(b" both: %(nb-common-both)9d\n" % data)
1042 ui.writenoi18n(b" local heads: %(nb-local)9d\n" % data)
1044 ui.writenoi18n(b" local heads: %(nb-local)9d\n" % data)
1043 ui.writenoi18n(b" common: %(nb-common-local)9d\n" % data)
1045 ui.writenoi18n(b" common: %(nb-common-local)9d\n" % data)
1044 ui.writenoi18n(b" missing: %(nb-local-missing)9d\n" % data)
1046 ui.writenoi18n(b" missing: %(nb-local-missing)9d\n" % data)
1045 ui.writenoi18n(b" remote heads: %(nb-remote)9d\n" % data)
1047 ui.writenoi18n(b" remote heads: %(nb-remote)9d\n" % data)
1046 ui.writenoi18n(b" common: %(nb-common-remote)9d\n" % data)
1048 ui.writenoi18n(b" common: %(nb-common-remote)9d\n" % data)
1047 ui.writenoi18n(b" unknown: %(nb-remote-unknown)9d\n" % data)
1049 ui.writenoi18n(b" unknown: %(nb-remote-unknown)9d\n" % data)
1048 ui.writenoi18n(b"local changesets: %(nb-revs)9d\n" % data)
1050 ui.writenoi18n(b"local changesets: %(nb-revs)9d\n" % data)
1049 ui.writenoi18n(b" common: %(nb-revs-common)9d\n" % data)
1051 ui.writenoi18n(b" common: %(nb-revs-common)9d\n" % data)
1050 ui.writenoi18n(b" missing: %(nb-revs-missing)9d\n" % data)
1052 ui.writenoi18n(b" missing: %(nb-revs-missing)9d\n" % data)
1051
1053
1052 if ui.verbose:
1054 if ui.verbose:
1053 ui.writenoi18n(
1055 ui.writenoi18n(
1054 b"common heads: %s\n" % b" ".join(sorted(short(n) for n in common))
1056 b"common heads: %s\n" % b" ".join(sorted(short(n) for n in common))
1055 )
1057 )
1056
1058
1057
1059
1058 _chunksize = 4 << 10
1060 _chunksize = 4 << 10
1059
1061
1060
1062
1061 @command(
1063 @command(
1062 b'debugdownload', [(b'o', b'output', b'', _(b'path')),], optionalrepo=True
1064 b'debugdownload', [(b'o', b'output', b'', _(b'path')),], optionalrepo=True
1063 )
1065 )
1064 def debugdownload(ui, repo, url, output=None, **opts):
1066 def debugdownload(ui, repo, url, output=None, **opts):
1065 """download a resource using Mercurial logic and config
1067 """download a resource using Mercurial logic and config
1066 """
1068 """
1067 fh = urlmod.open(ui, url, output)
1069 fh = urlmod.open(ui, url, output)
1068
1070
1069 dest = ui
1071 dest = ui
1070 if output:
1072 if output:
1071 dest = open(output, b"wb", _chunksize)
1073 dest = open(output, b"wb", _chunksize)
1072 try:
1074 try:
1073 data = fh.read(_chunksize)
1075 data = fh.read(_chunksize)
1074 while data:
1076 while data:
1075 dest.write(data)
1077 dest.write(data)
1076 data = fh.read(_chunksize)
1078 data = fh.read(_chunksize)
1077 finally:
1079 finally:
1078 if output:
1080 if output:
1079 dest.close()
1081 dest.close()
1080
1082
1081
1083
1082 @command(b'debugextensions', cmdutil.formatteropts, [], optionalrepo=True)
1084 @command(b'debugextensions', cmdutil.formatteropts, [], optionalrepo=True)
1083 def debugextensions(ui, repo, **opts):
1085 def debugextensions(ui, repo, **opts):
1084 '''show information about active extensions'''
1086 '''show information about active extensions'''
1085 opts = pycompat.byteskwargs(opts)
1087 opts = pycompat.byteskwargs(opts)
1086 exts = extensions.extensions(ui)
1088 exts = extensions.extensions(ui)
1087 hgver = util.version()
1089 hgver = util.version()
1088 fm = ui.formatter(b'debugextensions', opts)
1090 fm = ui.formatter(b'debugextensions', opts)
1089 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
1091 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
1090 isinternal = extensions.ismoduleinternal(extmod)
1092 isinternal = extensions.ismoduleinternal(extmod)
1091 extsource = None
1093 extsource = None
1092
1094
1093 if util.safehasattr(extmod, '__file__'):
1095 if util.safehasattr(extmod, '__file__'):
1094 extsource = pycompat.fsencode(extmod.__file__)
1096 extsource = pycompat.fsencode(extmod.__file__)
1095 elif getattr(sys, 'oxidized', False):
1097 elif getattr(sys, 'oxidized', False):
1096 extsource = pycompat.sysexecutable
1098 extsource = pycompat.sysexecutable
1097 if isinternal:
1099 if isinternal:
1098 exttestedwith = [] # never expose magic string to users
1100 exttestedwith = [] # never expose magic string to users
1099 else:
1101 else:
1100 exttestedwith = getattr(extmod, 'testedwith', b'').split()
1102 exttestedwith = getattr(extmod, 'testedwith', b'').split()
1101 extbuglink = getattr(extmod, 'buglink', None)
1103 extbuglink = getattr(extmod, 'buglink', None)
1102
1104
1103 fm.startitem()
1105 fm.startitem()
1104
1106
1105 if ui.quiet or ui.verbose:
1107 if ui.quiet or ui.verbose:
1106 fm.write(b'name', b'%s\n', extname)
1108 fm.write(b'name', b'%s\n', extname)
1107 else:
1109 else:
1108 fm.write(b'name', b'%s', extname)
1110 fm.write(b'name', b'%s', extname)
1109 if isinternal or hgver in exttestedwith:
1111 if isinternal or hgver in exttestedwith:
1110 fm.plain(b'\n')
1112 fm.plain(b'\n')
1111 elif not exttestedwith:
1113 elif not exttestedwith:
1112 fm.plain(_(b' (untested!)\n'))
1114 fm.plain(_(b' (untested!)\n'))
1113 else:
1115 else:
1114 lasttestedversion = exttestedwith[-1]
1116 lasttestedversion = exttestedwith[-1]
1115 fm.plain(b' (%s!)\n' % lasttestedversion)
1117 fm.plain(b' (%s!)\n' % lasttestedversion)
1116
1118
1117 fm.condwrite(
1119 fm.condwrite(
1118 ui.verbose and extsource,
1120 ui.verbose and extsource,
1119 b'source',
1121 b'source',
1120 _(b' location: %s\n'),
1122 _(b' location: %s\n'),
1121 extsource or b"",
1123 extsource or b"",
1122 )
1124 )
1123
1125
1124 if ui.verbose:
1126 if ui.verbose:
1125 fm.plain(_(b' bundled: %s\n') % [b'no', b'yes'][isinternal])
1127 fm.plain(_(b' bundled: %s\n') % [b'no', b'yes'][isinternal])
1126 fm.data(bundled=isinternal)
1128 fm.data(bundled=isinternal)
1127
1129
1128 fm.condwrite(
1130 fm.condwrite(
1129 ui.verbose and exttestedwith,
1131 ui.verbose and exttestedwith,
1130 b'testedwith',
1132 b'testedwith',
1131 _(b' tested with: %s\n'),
1133 _(b' tested with: %s\n'),
1132 fm.formatlist(exttestedwith, name=b'ver'),
1134 fm.formatlist(exttestedwith, name=b'ver'),
1133 )
1135 )
1134
1136
1135 fm.condwrite(
1137 fm.condwrite(
1136 ui.verbose and extbuglink,
1138 ui.verbose and extbuglink,
1137 b'buglink',
1139 b'buglink',
1138 _(b' bug reporting: %s\n'),
1140 _(b' bug reporting: %s\n'),
1139 extbuglink or b"",
1141 extbuglink or b"",
1140 )
1142 )
1141
1143
1142 fm.end()
1144 fm.end()
1143
1145
1144
1146
1145 @command(
1147 @command(
1146 b'debugfileset',
1148 b'debugfileset',
1147 [
1149 [
1148 (
1150 (
1149 b'r',
1151 b'r',
1150 b'rev',
1152 b'rev',
1151 b'',
1153 b'',
1152 _(b'apply the filespec on this revision'),
1154 _(b'apply the filespec on this revision'),
1153 _(b'REV'),
1155 _(b'REV'),
1154 ),
1156 ),
1155 (
1157 (
1156 b'',
1158 b'',
1157 b'all-files',
1159 b'all-files',
1158 False,
1160 False,
1159 _(b'test files from all revisions and working directory'),
1161 _(b'test files from all revisions and working directory'),
1160 ),
1162 ),
1161 (
1163 (
1162 b's',
1164 b's',
1163 b'show-matcher',
1165 b'show-matcher',
1164 None,
1166 None,
1165 _(b'print internal representation of matcher'),
1167 _(b'print internal representation of matcher'),
1166 ),
1168 ),
1167 (
1169 (
1168 b'p',
1170 b'p',
1169 b'show-stage',
1171 b'show-stage',
1170 [],
1172 [],
1171 _(b'print parsed tree at the given stage'),
1173 _(b'print parsed tree at the given stage'),
1172 _(b'NAME'),
1174 _(b'NAME'),
1173 ),
1175 ),
1174 ],
1176 ],
1175 _(b'[-r REV] [--all-files] [OPTION]... FILESPEC'),
1177 _(b'[-r REV] [--all-files] [OPTION]... FILESPEC'),
1176 )
1178 )
1177 def debugfileset(ui, repo, expr, **opts):
1179 def debugfileset(ui, repo, expr, **opts):
1178 '''parse and apply a fileset specification'''
1180 '''parse and apply a fileset specification'''
1179 from . import fileset
1181 from . import fileset
1180
1182
1181 fileset.symbols # force import of fileset so we have predicates to optimize
1183 fileset.symbols # force import of fileset so we have predicates to optimize
1182 opts = pycompat.byteskwargs(opts)
1184 opts = pycompat.byteskwargs(opts)
1183 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
1185 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
1184
1186
1185 stages = [
1187 stages = [
1186 (b'parsed', pycompat.identity),
1188 (b'parsed', pycompat.identity),
1187 (b'analyzed', filesetlang.analyze),
1189 (b'analyzed', filesetlang.analyze),
1188 (b'optimized', filesetlang.optimize),
1190 (b'optimized', filesetlang.optimize),
1189 ]
1191 ]
1190 stagenames = {n for n, f in stages}
1192 stagenames = {n for n, f in stages}
1191
1193
1192 showalways = set()
1194 showalways = set()
1193 if ui.verbose and not opts[b'show_stage']:
1195 if ui.verbose and not opts[b'show_stage']:
1194 # show parsed tree by --verbose (deprecated)
1196 # show parsed tree by --verbose (deprecated)
1195 showalways.add(b'parsed')
1197 showalways.add(b'parsed')
1196 if opts[b'show_stage'] == [b'all']:
1198 if opts[b'show_stage'] == [b'all']:
1197 showalways.update(stagenames)
1199 showalways.update(stagenames)
1198 else:
1200 else:
1199 for n in opts[b'show_stage']:
1201 for n in opts[b'show_stage']:
1200 if n not in stagenames:
1202 if n not in stagenames:
1201 raise error.Abort(_(b'invalid stage name: %s') % n)
1203 raise error.Abort(_(b'invalid stage name: %s') % n)
1202 showalways.update(opts[b'show_stage'])
1204 showalways.update(opts[b'show_stage'])
1203
1205
1204 tree = filesetlang.parse(expr)
1206 tree = filesetlang.parse(expr)
1205 for n, f in stages:
1207 for n, f in stages:
1206 tree = f(tree)
1208 tree = f(tree)
1207 if n in showalways:
1209 if n in showalways:
1208 if opts[b'show_stage'] or n != b'parsed':
1210 if opts[b'show_stage'] or n != b'parsed':
1209 ui.write(b"* %s:\n" % n)
1211 ui.write(b"* %s:\n" % n)
1210 ui.write(filesetlang.prettyformat(tree), b"\n")
1212 ui.write(filesetlang.prettyformat(tree), b"\n")
1211
1213
1212 files = set()
1214 files = set()
1213 if opts[b'all_files']:
1215 if opts[b'all_files']:
1214 for r in repo:
1216 for r in repo:
1215 c = repo[r]
1217 c = repo[r]
1216 files.update(c.files())
1218 files.update(c.files())
1217 files.update(c.substate)
1219 files.update(c.substate)
1218 if opts[b'all_files'] or ctx.rev() is None:
1220 if opts[b'all_files'] or ctx.rev() is None:
1219 wctx = repo[None]
1221 wctx = repo[None]
1220 files.update(
1222 files.update(
1221 repo.dirstate.walk(
1223 repo.dirstate.walk(
1222 scmutil.matchall(repo),
1224 scmutil.matchall(repo),
1223 subrepos=list(wctx.substate),
1225 subrepos=list(wctx.substate),
1224 unknown=True,
1226 unknown=True,
1225 ignored=True,
1227 ignored=True,
1226 )
1228 )
1227 )
1229 )
1228 files.update(wctx.substate)
1230 files.update(wctx.substate)
1229 else:
1231 else:
1230 files.update(ctx.files())
1232 files.update(ctx.files())
1231 files.update(ctx.substate)
1233 files.update(ctx.substate)
1232
1234
1233 m = ctx.matchfileset(repo.getcwd(), expr)
1235 m = ctx.matchfileset(repo.getcwd(), expr)
1234 if opts[b'show_matcher'] or (opts[b'show_matcher'] is None and ui.verbose):
1236 if opts[b'show_matcher'] or (opts[b'show_matcher'] is None and ui.verbose):
1235 ui.writenoi18n(b'* matcher:\n', stringutil.prettyrepr(m), b'\n')
1237 ui.writenoi18n(b'* matcher:\n', stringutil.prettyrepr(m), b'\n')
1236 for f in sorted(files):
1238 for f in sorted(files):
1237 if not m(f):
1239 if not m(f):
1238 continue
1240 continue
1239 ui.write(b"%s\n" % f)
1241 ui.write(b"%s\n" % f)
1240
1242
1241
1243
1242 @command(b'debugformat', [] + cmdutil.formatteropts)
1244 @command(b'debugformat', [] + cmdutil.formatteropts)
1243 def debugformat(ui, repo, **opts):
1245 def debugformat(ui, repo, **opts):
1244 """display format information about the current repository
1246 """display format information about the current repository
1245
1247
1246 Use --verbose to get extra information about current config value and
1248 Use --verbose to get extra information about current config value and
1247 Mercurial default."""
1249 Mercurial default."""
1248 opts = pycompat.byteskwargs(opts)
1250 opts = pycompat.byteskwargs(opts)
1249 maxvariantlength = max(len(fv.name) for fv in upgrade.allformatvariant)
1251 maxvariantlength = max(len(fv.name) for fv in upgrade.allformatvariant)
1250 maxvariantlength = max(len(b'format-variant'), maxvariantlength)
1252 maxvariantlength = max(len(b'format-variant'), maxvariantlength)
1251
1253
1252 def makeformatname(name):
1254 def makeformatname(name):
1253 return b'%s:' + (b' ' * (maxvariantlength - len(name)))
1255 return b'%s:' + (b' ' * (maxvariantlength - len(name)))
1254
1256
1255 fm = ui.formatter(b'debugformat', opts)
1257 fm = ui.formatter(b'debugformat', opts)
1256 if fm.isplain():
1258 if fm.isplain():
1257
1259
1258 def formatvalue(value):
1260 def formatvalue(value):
1259 if util.safehasattr(value, b'startswith'):
1261 if util.safehasattr(value, b'startswith'):
1260 return value
1262 return value
1261 if value:
1263 if value:
1262 return b'yes'
1264 return b'yes'
1263 else:
1265 else:
1264 return b'no'
1266 return b'no'
1265
1267
1266 else:
1268 else:
1267 formatvalue = pycompat.identity
1269 formatvalue = pycompat.identity
1268
1270
1269 fm.plain(b'format-variant')
1271 fm.plain(b'format-variant')
1270 fm.plain(b' ' * (maxvariantlength - len(b'format-variant')))
1272 fm.plain(b' ' * (maxvariantlength - len(b'format-variant')))
1271 fm.plain(b' repo')
1273 fm.plain(b' repo')
1272 if ui.verbose:
1274 if ui.verbose:
1273 fm.plain(b' config default')
1275 fm.plain(b' config default')
1274 fm.plain(b'\n')
1276 fm.plain(b'\n')
1275 for fv in upgrade.allformatvariant:
1277 for fv in upgrade.allformatvariant:
1276 fm.startitem()
1278 fm.startitem()
1277 repovalue = fv.fromrepo(repo)
1279 repovalue = fv.fromrepo(repo)
1278 configvalue = fv.fromconfig(repo)
1280 configvalue = fv.fromconfig(repo)
1279
1281
1280 if repovalue != configvalue:
1282 if repovalue != configvalue:
1281 namelabel = b'formatvariant.name.mismatchconfig'
1283 namelabel = b'formatvariant.name.mismatchconfig'
1282 repolabel = b'formatvariant.repo.mismatchconfig'
1284 repolabel = b'formatvariant.repo.mismatchconfig'
1283 elif repovalue != fv.default:
1285 elif repovalue != fv.default:
1284 namelabel = b'formatvariant.name.mismatchdefault'
1286 namelabel = b'formatvariant.name.mismatchdefault'
1285 repolabel = b'formatvariant.repo.mismatchdefault'
1287 repolabel = b'formatvariant.repo.mismatchdefault'
1286 else:
1288 else:
1287 namelabel = b'formatvariant.name.uptodate'
1289 namelabel = b'formatvariant.name.uptodate'
1288 repolabel = b'formatvariant.repo.uptodate'
1290 repolabel = b'formatvariant.repo.uptodate'
1289
1291
1290 fm.write(b'name', makeformatname(fv.name), fv.name, label=namelabel)
1292 fm.write(b'name', makeformatname(fv.name), fv.name, label=namelabel)
1291 fm.write(b'repo', b' %3s', formatvalue(repovalue), label=repolabel)
1293 fm.write(b'repo', b' %3s', formatvalue(repovalue), label=repolabel)
1292 if fv.default != configvalue:
1294 if fv.default != configvalue:
1293 configlabel = b'formatvariant.config.special'
1295 configlabel = b'formatvariant.config.special'
1294 else:
1296 else:
1295 configlabel = b'formatvariant.config.default'
1297 configlabel = b'formatvariant.config.default'
1296 fm.condwrite(
1298 fm.condwrite(
1297 ui.verbose,
1299 ui.verbose,
1298 b'config',
1300 b'config',
1299 b' %6s',
1301 b' %6s',
1300 formatvalue(configvalue),
1302 formatvalue(configvalue),
1301 label=configlabel,
1303 label=configlabel,
1302 )
1304 )
1303 fm.condwrite(
1305 fm.condwrite(
1304 ui.verbose,
1306 ui.verbose,
1305 b'default',
1307 b'default',
1306 b' %7s',
1308 b' %7s',
1307 formatvalue(fv.default),
1309 formatvalue(fv.default),
1308 label=b'formatvariant.default',
1310 label=b'formatvariant.default',
1309 )
1311 )
1310 fm.plain(b'\n')
1312 fm.plain(b'\n')
1311 fm.end()
1313 fm.end()
1312
1314
1313
1315
1314 @command(b'debugfsinfo', [], _(b'[PATH]'), norepo=True)
1316 @command(b'debugfsinfo', [], _(b'[PATH]'), norepo=True)
1315 def debugfsinfo(ui, path=b"."):
1317 def debugfsinfo(ui, path=b"."):
1316 """show information detected about current filesystem"""
1318 """show information detected about current filesystem"""
1317 ui.writenoi18n(b'path: %s\n' % path)
1319 ui.writenoi18n(b'path: %s\n' % path)
1318 ui.writenoi18n(
1320 ui.writenoi18n(
1319 b'mounted on: %s\n' % (util.getfsmountpoint(path) or b'(unknown)')
1321 b'mounted on: %s\n' % (util.getfsmountpoint(path) or b'(unknown)')
1320 )
1322 )
1321 ui.writenoi18n(b'exec: %s\n' % (util.checkexec(path) and b'yes' or b'no'))
1323 ui.writenoi18n(b'exec: %s\n' % (util.checkexec(path) and b'yes' or b'no'))
1322 ui.writenoi18n(b'fstype: %s\n' % (util.getfstype(path) or b'(unknown)'))
1324 ui.writenoi18n(b'fstype: %s\n' % (util.getfstype(path) or b'(unknown)'))
1323 ui.writenoi18n(
1325 ui.writenoi18n(
1324 b'symlink: %s\n' % (util.checklink(path) and b'yes' or b'no')
1326 b'symlink: %s\n' % (util.checklink(path) and b'yes' or b'no')
1325 )
1327 )
1326 ui.writenoi18n(
1328 ui.writenoi18n(
1327 b'hardlink: %s\n' % (util.checknlink(path) and b'yes' or b'no')
1329 b'hardlink: %s\n' % (util.checknlink(path) and b'yes' or b'no')
1328 )
1330 )
1329 casesensitive = b'(unknown)'
1331 casesensitive = b'(unknown)'
1330 try:
1332 try:
1331 with pycompat.namedtempfile(prefix=b'.debugfsinfo', dir=path) as f:
1333 with pycompat.namedtempfile(prefix=b'.debugfsinfo', dir=path) as f:
1332 casesensitive = util.fscasesensitive(f.name) and b'yes' or b'no'
1334 casesensitive = util.fscasesensitive(f.name) and b'yes' or b'no'
1333 except OSError:
1335 except OSError:
1334 pass
1336 pass
1335 ui.writenoi18n(b'case-sensitive: %s\n' % casesensitive)
1337 ui.writenoi18n(b'case-sensitive: %s\n' % casesensitive)
1336
1338
1337
1339
1338 @command(
1340 @command(
1339 b'debuggetbundle',
1341 b'debuggetbundle',
1340 [
1342 [
1341 (b'H', b'head', [], _(b'id of head node'), _(b'ID')),
1343 (b'H', b'head', [], _(b'id of head node'), _(b'ID')),
1342 (b'C', b'common', [], _(b'id of common node'), _(b'ID')),
1344 (b'C', b'common', [], _(b'id of common node'), _(b'ID')),
1343 (
1345 (
1344 b't',
1346 b't',
1345 b'type',
1347 b'type',
1346 b'bzip2',
1348 b'bzip2',
1347 _(b'bundle compression type to use'),
1349 _(b'bundle compression type to use'),
1348 _(b'TYPE'),
1350 _(b'TYPE'),
1349 ),
1351 ),
1350 ],
1352 ],
1351 _(b'REPO FILE [-H|-C ID]...'),
1353 _(b'REPO FILE [-H|-C ID]...'),
1352 norepo=True,
1354 norepo=True,
1353 )
1355 )
1354 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1356 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1355 """retrieves a bundle from a repo
1357 """retrieves a bundle from a repo
1356
1358
1357 Every ID must be a full-length hex node id string. Saves the bundle to the
1359 Every ID must be a full-length hex node id string. Saves the bundle to the
1358 given file.
1360 given file.
1359 """
1361 """
1360 opts = pycompat.byteskwargs(opts)
1362 opts = pycompat.byteskwargs(opts)
1361 repo = hg.peer(ui, opts, repopath)
1363 repo = hg.peer(ui, opts, repopath)
1362 if not repo.capable(b'getbundle'):
1364 if not repo.capable(b'getbundle'):
1363 raise error.Abort(b"getbundle() not supported by target repository")
1365 raise error.Abort(b"getbundle() not supported by target repository")
1364 args = {}
1366 args = {}
1365 if common:
1367 if common:
1366 args['common'] = [bin(s) for s in common]
1368 args['common'] = [bin(s) for s in common]
1367 if head:
1369 if head:
1368 args['heads'] = [bin(s) for s in head]
1370 args['heads'] = [bin(s) for s in head]
1369 # TODO: get desired bundlecaps from command line.
1371 # TODO: get desired bundlecaps from command line.
1370 args['bundlecaps'] = None
1372 args['bundlecaps'] = None
1371 bundle = repo.getbundle(b'debug', **args)
1373 bundle = repo.getbundle(b'debug', **args)
1372
1374
1373 bundletype = opts.get(b'type', b'bzip2').lower()
1375 bundletype = opts.get(b'type', b'bzip2').lower()
1374 btypes = {
1376 btypes = {
1375 b'none': b'HG10UN',
1377 b'none': b'HG10UN',
1376 b'bzip2': b'HG10BZ',
1378 b'bzip2': b'HG10BZ',
1377 b'gzip': b'HG10GZ',
1379 b'gzip': b'HG10GZ',
1378 b'bundle2': b'HG20',
1380 b'bundle2': b'HG20',
1379 }
1381 }
1380 bundletype = btypes.get(bundletype)
1382 bundletype = btypes.get(bundletype)
1381 if bundletype not in bundle2.bundletypes:
1383 if bundletype not in bundle2.bundletypes:
1382 raise error.Abort(_(b'unknown bundle type specified with --type'))
1384 raise error.Abort(_(b'unknown bundle type specified with --type'))
1383 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
1385 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
1384
1386
1385
1387
1386 @command(b'debugignore', [], b'[FILE]')
1388 @command(b'debugignore', [], b'[FILE]')
1387 def debugignore(ui, repo, *files, **opts):
1389 def debugignore(ui, repo, *files, **opts):
1388 """display the combined ignore pattern and information about ignored files
1390 """display the combined ignore pattern and information about ignored files
1389
1391
1390 With no argument display the combined ignore pattern.
1392 With no argument display the combined ignore pattern.
1391
1393
1392 Given space separated file names, shows if the given file is ignored and
1394 Given space separated file names, shows if the given file is ignored and
1393 if so, show the ignore rule (file and line number) that matched it.
1395 if so, show the ignore rule (file and line number) that matched it.
1394 """
1396 """
1395 ignore = repo.dirstate._ignore
1397 ignore = repo.dirstate._ignore
1396 if not files:
1398 if not files:
1397 # Show all the patterns
1399 # Show all the patterns
1398 ui.write(b"%s\n" % pycompat.byterepr(ignore))
1400 ui.write(b"%s\n" % pycompat.byterepr(ignore))
1399 else:
1401 else:
1400 m = scmutil.match(repo[None], pats=files)
1402 m = scmutil.match(repo[None], pats=files)
1401 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
1403 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
1402 for f in m.files():
1404 for f in m.files():
1403 nf = util.normpath(f)
1405 nf = util.normpath(f)
1404 ignored = None
1406 ignored = None
1405 ignoredata = None
1407 ignoredata = None
1406 if nf != b'.':
1408 if nf != b'.':
1407 if ignore(nf):
1409 if ignore(nf):
1408 ignored = nf
1410 ignored = nf
1409 ignoredata = repo.dirstate._ignorefileandline(nf)
1411 ignoredata = repo.dirstate._ignorefileandline(nf)
1410 else:
1412 else:
1411 for p in pathutil.finddirs(nf):
1413 for p in pathutil.finddirs(nf):
1412 if ignore(p):
1414 if ignore(p):
1413 ignored = p
1415 ignored = p
1414 ignoredata = repo.dirstate._ignorefileandline(p)
1416 ignoredata = repo.dirstate._ignorefileandline(p)
1415 break
1417 break
1416 if ignored:
1418 if ignored:
1417 if ignored == nf:
1419 if ignored == nf:
1418 ui.write(_(b"%s is ignored\n") % uipathfn(f))
1420 ui.write(_(b"%s is ignored\n") % uipathfn(f))
1419 else:
1421 else:
1420 ui.write(
1422 ui.write(
1421 _(
1423 _(
1422 b"%s is ignored because of "
1424 b"%s is ignored because of "
1423 b"containing directory %s\n"
1425 b"containing directory %s\n"
1424 )
1426 )
1425 % (uipathfn(f), ignored)
1427 % (uipathfn(f), ignored)
1426 )
1428 )
1427 ignorefile, lineno, line = ignoredata
1429 ignorefile, lineno, line = ignoredata
1428 ui.write(
1430 ui.write(
1429 _(b"(ignore rule in %s, line %d: '%s')\n")
1431 _(b"(ignore rule in %s, line %d: '%s')\n")
1430 % (ignorefile, lineno, line)
1432 % (ignorefile, lineno, line)
1431 )
1433 )
1432 else:
1434 else:
1433 ui.write(_(b"%s is not ignored\n") % uipathfn(f))
1435 ui.write(_(b"%s is not ignored\n") % uipathfn(f))
1434
1436
1435
1437
1436 @command(
1438 @command(
1437 b'debugindex',
1439 b'debugindex',
1438 cmdutil.debugrevlogopts + cmdutil.formatteropts,
1440 cmdutil.debugrevlogopts + cmdutil.formatteropts,
1439 _(b'-c|-m|FILE'),
1441 _(b'-c|-m|FILE'),
1440 )
1442 )
1441 def debugindex(ui, repo, file_=None, **opts):
1443 def debugindex(ui, repo, file_=None, **opts):
1442 """dump index data for a storage primitive"""
1444 """dump index data for a storage primitive"""
1443 opts = pycompat.byteskwargs(opts)
1445 opts = pycompat.byteskwargs(opts)
1444 store = cmdutil.openstorage(repo, b'debugindex', file_, opts)
1446 store = cmdutil.openstorage(repo, b'debugindex', file_, opts)
1445
1447
1446 if ui.debugflag:
1448 if ui.debugflag:
1447 shortfn = hex
1449 shortfn = hex
1448 else:
1450 else:
1449 shortfn = short
1451 shortfn = short
1450
1452
1451 idlen = 12
1453 idlen = 12
1452 for i in store:
1454 for i in store:
1453 idlen = len(shortfn(store.node(i)))
1455 idlen = len(shortfn(store.node(i)))
1454 break
1456 break
1455
1457
1456 fm = ui.formatter(b'debugindex', opts)
1458 fm = ui.formatter(b'debugindex', opts)
1457 fm.plain(
1459 fm.plain(
1458 b' rev linkrev %s %s p2\n'
1460 b' rev linkrev %s %s p2\n'
1459 % (b'nodeid'.ljust(idlen), b'p1'.ljust(idlen))
1461 % (b'nodeid'.ljust(idlen), b'p1'.ljust(idlen))
1460 )
1462 )
1461
1463
1462 for rev in store:
1464 for rev in store:
1463 node = store.node(rev)
1465 node = store.node(rev)
1464 parents = store.parents(node)
1466 parents = store.parents(node)
1465
1467
1466 fm.startitem()
1468 fm.startitem()
1467 fm.write(b'rev', b'%6d ', rev)
1469 fm.write(b'rev', b'%6d ', rev)
1468 fm.write(b'linkrev', b'%7d ', store.linkrev(rev))
1470 fm.write(b'linkrev', b'%7d ', store.linkrev(rev))
1469 fm.write(b'node', b'%s ', shortfn(node))
1471 fm.write(b'node', b'%s ', shortfn(node))
1470 fm.write(b'p1', b'%s ', shortfn(parents[0]))
1472 fm.write(b'p1', b'%s ', shortfn(parents[0]))
1471 fm.write(b'p2', b'%s', shortfn(parents[1]))
1473 fm.write(b'p2', b'%s', shortfn(parents[1]))
1472 fm.plain(b'\n')
1474 fm.plain(b'\n')
1473
1475
1474 fm.end()
1476 fm.end()
1475
1477
1476
1478
1477 @command(
1479 @command(
1478 b'debugindexdot',
1480 b'debugindexdot',
1479 cmdutil.debugrevlogopts,
1481 cmdutil.debugrevlogopts,
1480 _(b'-c|-m|FILE'),
1482 _(b'-c|-m|FILE'),
1481 optionalrepo=True,
1483 optionalrepo=True,
1482 )
1484 )
1483 def debugindexdot(ui, repo, file_=None, **opts):
1485 def debugindexdot(ui, repo, file_=None, **opts):
1484 """dump an index DAG as a graphviz dot file"""
1486 """dump an index DAG as a graphviz dot file"""
1485 opts = pycompat.byteskwargs(opts)
1487 opts = pycompat.byteskwargs(opts)
1486 r = cmdutil.openstorage(repo, b'debugindexdot', file_, opts)
1488 r = cmdutil.openstorage(repo, b'debugindexdot', file_, opts)
1487 ui.writenoi18n(b"digraph G {\n")
1489 ui.writenoi18n(b"digraph G {\n")
1488 for i in r:
1490 for i in r:
1489 node = r.node(i)
1491 node = r.node(i)
1490 pp = r.parents(node)
1492 pp = r.parents(node)
1491 ui.write(b"\t%d -> %d\n" % (r.rev(pp[0]), i))
1493 ui.write(b"\t%d -> %d\n" % (r.rev(pp[0]), i))
1492 if pp[1] != nullid:
1494 if pp[1] != nullid:
1493 ui.write(b"\t%d -> %d\n" % (r.rev(pp[1]), i))
1495 ui.write(b"\t%d -> %d\n" % (r.rev(pp[1]), i))
1494 ui.write(b"}\n")
1496 ui.write(b"}\n")
1495
1497
1496
1498
1497 @command(b'debugindexstats', [])
1499 @command(b'debugindexstats', [])
1498 def debugindexstats(ui, repo):
1500 def debugindexstats(ui, repo):
1499 """show stats related to the changelog index"""
1501 """show stats related to the changelog index"""
1500 repo.changelog.shortest(nullid, 1)
1502 repo.changelog.shortest(nullid, 1)
1501 index = repo.changelog.index
1503 index = repo.changelog.index
1502 if not util.safehasattr(index, b'stats'):
1504 if not util.safehasattr(index, b'stats'):
1503 raise error.Abort(_(b'debugindexstats only works with native code'))
1505 raise error.Abort(_(b'debugindexstats only works with native code'))
1504 for k, v in sorted(index.stats().items()):
1506 for k, v in sorted(index.stats().items()):
1505 ui.write(b'%s: %d\n' % (k, v))
1507 ui.write(b'%s: %d\n' % (k, v))
1506
1508
1507
1509
1508 @command(b'debuginstall', [] + cmdutil.formatteropts, b'', norepo=True)
1510 @command(b'debuginstall', [] + cmdutil.formatteropts, b'', norepo=True)
1509 def debuginstall(ui, **opts):
1511 def debuginstall(ui, **opts):
1510 '''test Mercurial installation
1512 '''test Mercurial installation
1511
1513
1512 Returns 0 on success.
1514 Returns 0 on success.
1513 '''
1515 '''
1514 opts = pycompat.byteskwargs(opts)
1516 opts = pycompat.byteskwargs(opts)
1515
1517
1516 problems = 0
1518 problems = 0
1517
1519
1518 fm = ui.formatter(b'debuginstall', opts)
1520 fm = ui.formatter(b'debuginstall', opts)
1519 fm.startitem()
1521 fm.startitem()
1520
1522
1521 # encoding might be unknown or wrong. don't translate these messages.
1523 # encoding might be unknown or wrong. don't translate these messages.
1522 fm.write(b'encoding', b"checking encoding (%s)...\n", encoding.encoding)
1524 fm.write(b'encoding', b"checking encoding (%s)...\n", encoding.encoding)
1523 err = None
1525 err = None
1524 try:
1526 try:
1525 codecs.lookup(pycompat.sysstr(encoding.encoding))
1527 codecs.lookup(pycompat.sysstr(encoding.encoding))
1526 except LookupError as inst:
1528 except LookupError as inst:
1527 err = stringutil.forcebytestr(inst)
1529 err = stringutil.forcebytestr(inst)
1528 problems += 1
1530 problems += 1
1529 fm.condwrite(
1531 fm.condwrite(
1530 err,
1532 err,
1531 b'encodingerror',
1533 b'encodingerror',
1532 b" %s\n (check that your locale is properly set)\n",
1534 b" %s\n (check that your locale is properly set)\n",
1533 err,
1535 err,
1534 )
1536 )
1535
1537
1536 # Python
1538 # Python
1537 pythonlib = None
1539 pythonlib = None
1538 if util.safehasattr(os, '__file__'):
1540 if util.safehasattr(os, '__file__'):
1539 pythonlib = os.path.dirname(pycompat.fsencode(os.__file__))
1541 pythonlib = os.path.dirname(pycompat.fsencode(os.__file__))
1540 elif getattr(sys, 'oxidized', False):
1542 elif getattr(sys, 'oxidized', False):
1541 pythonlib = pycompat.sysexecutable
1543 pythonlib = pycompat.sysexecutable
1542
1544
1543 fm.write(
1545 fm.write(
1544 b'pythonexe',
1546 b'pythonexe',
1545 _(b"checking Python executable (%s)\n"),
1547 _(b"checking Python executable (%s)\n"),
1546 pycompat.sysexecutable or _(b"unknown"),
1548 pycompat.sysexecutable or _(b"unknown"),
1547 )
1549 )
1548 fm.write(
1550 fm.write(
1549 b'pythonimplementation',
1551 b'pythonimplementation',
1550 _(b"checking Python implementation (%s)\n"),
1552 _(b"checking Python implementation (%s)\n"),
1551 pycompat.sysbytes(platform.python_implementation()),
1553 pycompat.sysbytes(platform.python_implementation()),
1552 )
1554 )
1553 fm.write(
1555 fm.write(
1554 b'pythonver',
1556 b'pythonver',
1555 _(b"checking Python version (%s)\n"),
1557 _(b"checking Python version (%s)\n"),
1556 (b"%d.%d.%d" % sys.version_info[:3]),
1558 (b"%d.%d.%d" % sys.version_info[:3]),
1557 )
1559 )
1558 fm.write(
1560 fm.write(
1559 b'pythonlib',
1561 b'pythonlib',
1560 _(b"checking Python lib (%s)...\n"),
1562 _(b"checking Python lib (%s)...\n"),
1561 pythonlib or _(b"unknown"),
1563 pythonlib or _(b"unknown"),
1562 )
1564 )
1563
1565
1564 try:
1566 try:
1565 from . import rustext
1567 from . import rustext
1566
1568
1567 rustext.__doc__ # trigger lazy import
1569 rustext.__doc__ # trigger lazy import
1568 except ImportError:
1570 except ImportError:
1569 rustext = None
1571 rustext = None
1570
1572
1571 security = set(sslutil.supportedprotocols)
1573 security = set(sslutil.supportedprotocols)
1572 if sslutil.hassni:
1574 if sslutil.hassni:
1573 security.add(b'sni')
1575 security.add(b'sni')
1574
1576
1575 fm.write(
1577 fm.write(
1576 b'pythonsecurity',
1578 b'pythonsecurity',
1577 _(b"checking Python security support (%s)\n"),
1579 _(b"checking Python security support (%s)\n"),
1578 fm.formatlist(sorted(security), name=b'protocol', fmt=b'%s', sep=b','),
1580 fm.formatlist(sorted(security), name=b'protocol', fmt=b'%s', sep=b','),
1579 )
1581 )
1580
1582
1581 # These are warnings, not errors. So don't increment problem count. This
1583 # These are warnings, not errors. So don't increment problem count. This
1582 # may change in the future.
1584 # may change in the future.
1583 if b'tls1.2' not in security:
1585 if b'tls1.2' not in security:
1584 fm.plain(
1586 fm.plain(
1585 _(
1587 _(
1586 b' TLS 1.2 not supported by Python install; '
1588 b' TLS 1.2 not supported by Python install; '
1587 b'network connections lack modern security\n'
1589 b'network connections lack modern security\n'
1588 )
1590 )
1589 )
1591 )
1590 if b'sni' not in security:
1592 if b'sni' not in security:
1591 fm.plain(
1593 fm.plain(
1592 _(
1594 _(
1593 b' SNI not supported by Python install; may have '
1595 b' SNI not supported by Python install; may have '
1594 b'connectivity issues with some servers\n'
1596 b'connectivity issues with some servers\n'
1595 )
1597 )
1596 )
1598 )
1597
1599
1598 fm.plain(
1600 fm.plain(
1599 _(
1601 _(
1600 b"checking Rust extensions (%s)\n"
1602 b"checking Rust extensions (%s)\n"
1601 % (b'missing' if rustext is None else b'installed')
1603 % (b'missing' if rustext is None else b'installed')
1602 ),
1604 ),
1603 )
1605 )
1604
1606
1605 # TODO print CA cert info
1607 # TODO print CA cert info
1606
1608
1607 # hg version
1609 # hg version
1608 hgver = util.version()
1610 hgver = util.version()
1609 fm.write(
1611 fm.write(
1610 b'hgver', _(b"checking Mercurial version (%s)\n"), hgver.split(b'+')[0]
1612 b'hgver', _(b"checking Mercurial version (%s)\n"), hgver.split(b'+')[0]
1611 )
1613 )
1612 fm.write(
1614 fm.write(
1613 b'hgverextra',
1615 b'hgverextra',
1614 _(b"checking Mercurial custom build (%s)\n"),
1616 _(b"checking Mercurial custom build (%s)\n"),
1615 b'+'.join(hgver.split(b'+')[1:]),
1617 b'+'.join(hgver.split(b'+')[1:]),
1616 )
1618 )
1617
1619
1618 # compiled modules
1620 # compiled modules
1619 hgmodules = None
1621 hgmodules = None
1620 if util.safehasattr(sys.modules[__name__], '__file__'):
1622 if util.safehasattr(sys.modules[__name__], '__file__'):
1621 hgmodules = os.path.dirname(pycompat.fsencode(__file__))
1623 hgmodules = os.path.dirname(pycompat.fsencode(__file__))
1622 elif getattr(sys, 'oxidized', False):
1624 elif getattr(sys, 'oxidized', False):
1623 hgmodules = pycompat.sysexecutable
1625 hgmodules = pycompat.sysexecutable
1624
1626
1625 fm.write(
1627 fm.write(
1626 b'hgmodulepolicy', _(b"checking module policy (%s)\n"), policy.policy
1628 b'hgmodulepolicy', _(b"checking module policy (%s)\n"), policy.policy
1627 )
1629 )
1628 fm.write(
1630 fm.write(
1629 b'hgmodules',
1631 b'hgmodules',
1630 _(b"checking installed modules (%s)...\n"),
1632 _(b"checking installed modules (%s)...\n"),
1631 hgmodules or _(b"unknown"),
1633 hgmodules or _(b"unknown"),
1632 )
1634 )
1633
1635
1634 rustandc = policy.policy in (b'rust+c', b'rust+c-allow')
1636 rustandc = policy.policy in (b'rust+c', b'rust+c-allow')
1635 rustext = rustandc # for now, that's the only case
1637 rustext = rustandc # for now, that's the only case
1636 cext = policy.policy in (b'c', b'allow') or rustandc
1638 cext = policy.policy in (b'c', b'allow') or rustandc
1637 nopure = cext or rustext
1639 nopure = cext or rustext
1638 if nopure:
1640 if nopure:
1639 err = None
1641 err = None
1640 try:
1642 try:
1641 if cext:
1643 if cext:
1642 from .cext import ( # pytype: disable=import-error
1644 from .cext import ( # pytype: disable=import-error
1643 base85,
1645 base85,
1644 bdiff,
1646 bdiff,
1645 mpatch,
1647 mpatch,
1646 osutil,
1648 osutil,
1647 )
1649 )
1648
1650
1649 # quiet pyflakes
1651 # quiet pyflakes
1650 dir(bdiff), dir(mpatch), dir(base85), dir(osutil)
1652 dir(bdiff), dir(mpatch), dir(base85), dir(osutil)
1651 if rustext:
1653 if rustext:
1652 from .rustext import ( # pytype: disable=import-error
1654 from .rustext import ( # pytype: disable=import-error
1653 ancestor,
1655 ancestor,
1654 dirstate,
1656 dirstate,
1655 )
1657 )
1656
1658
1657 dir(ancestor), dir(dirstate) # quiet pyflakes
1659 dir(ancestor), dir(dirstate) # quiet pyflakes
1658 except Exception as inst:
1660 except Exception as inst:
1659 err = stringutil.forcebytestr(inst)
1661 err = stringutil.forcebytestr(inst)
1660 problems += 1
1662 problems += 1
1661 fm.condwrite(err, b'extensionserror', b" %s\n", err)
1663 fm.condwrite(err, b'extensionserror', b" %s\n", err)
1662
1664
1663 compengines = util.compengines._engines.values()
1665 compengines = util.compengines._engines.values()
1664 fm.write(
1666 fm.write(
1665 b'compengines',
1667 b'compengines',
1666 _(b'checking registered compression engines (%s)\n'),
1668 _(b'checking registered compression engines (%s)\n'),
1667 fm.formatlist(
1669 fm.formatlist(
1668 sorted(e.name() for e in compengines),
1670 sorted(e.name() for e in compengines),
1669 name=b'compengine',
1671 name=b'compengine',
1670 fmt=b'%s',
1672 fmt=b'%s',
1671 sep=b', ',
1673 sep=b', ',
1672 ),
1674 ),
1673 )
1675 )
1674 fm.write(
1676 fm.write(
1675 b'compenginesavail',
1677 b'compenginesavail',
1676 _(b'checking available compression engines (%s)\n'),
1678 _(b'checking available compression engines (%s)\n'),
1677 fm.formatlist(
1679 fm.formatlist(
1678 sorted(e.name() for e in compengines if e.available()),
1680 sorted(e.name() for e in compengines if e.available()),
1679 name=b'compengine',
1681 name=b'compengine',
1680 fmt=b'%s',
1682 fmt=b'%s',
1681 sep=b', ',
1683 sep=b', ',
1682 ),
1684 ),
1683 )
1685 )
1684 wirecompengines = compression.compengines.supportedwireengines(
1686 wirecompengines = compression.compengines.supportedwireengines(
1685 compression.SERVERROLE
1687 compression.SERVERROLE
1686 )
1688 )
1687 fm.write(
1689 fm.write(
1688 b'compenginesserver',
1690 b'compenginesserver',
1689 _(
1691 _(
1690 b'checking available compression engines '
1692 b'checking available compression engines '
1691 b'for wire protocol (%s)\n'
1693 b'for wire protocol (%s)\n'
1692 ),
1694 ),
1693 fm.formatlist(
1695 fm.formatlist(
1694 [e.name() for e in wirecompengines if e.wireprotosupport()],
1696 [e.name() for e in wirecompengines if e.wireprotosupport()],
1695 name=b'compengine',
1697 name=b'compengine',
1696 fmt=b'%s',
1698 fmt=b'%s',
1697 sep=b', ',
1699 sep=b', ',
1698 ),
1700 ),
1699 )
1701 )
1700 re2 = b'missing'
1702 re2 = b'missing'
1701 if util._re2:
1703 if util._re2:
1702 re2 = b'available'
1704 re2 = b'available'
1703 fm.plain(_(b'checking "re2" regexp engine (%s)\n') % re2)
1705 fm.plain(_(b'checking "re2" regexp engine (%s)\n') % re2)
1704 fm.data(re2=bool(util._re2))
1706 fm.data(re2=bool(util._re2))
1705
1707
1706 # templates
1708 # templates
1707 p = templater.templatedir()
1709 p = templater.templatedir()
1708 fm.write(b'templatedirs', b'checking templates (%s)...\n', p or b'')
1710 fm.write(b'templatedirs', b'checking templates (%s)...\n', p or b'')
1709 fm.condwrite(not p, b'', _(b" no template directories found\n"))
1711 fm.condwrite(not p, b'', _(b" no template directories found\n"))
1710 if p:
1712 if p:
1711 (m, fp) = templater.try_open_template(b"map-cmdline.default")
1713 (m, fp) = templater.try_open_template(b"map-cmdline.default")
1712 if m:
1714 if m:
1713 # template found, check if it is working
1715 # template found, check if it is working
1714 err = None
1716 err = None
1715 try:
1717 try:
1716 templater.templater.frommapfile(m)
1718 templater.templater.frommapfile(m)
1717 except Exception as inst:
1719 except Exception as inst:
1718 err = stringutil.forcebytestr(inst)
1720 err = stringutil.forcebytestr(inst)
1719 p = None
1721 p = None
1720 fm.condwrite(err, b'defaulttemplateerror', b" %s\n", err)
1722 fm.condwrite(err, b'defaulttemplateerror', b" %s\n", err)
1721 else:
1723 else:
1722 p = None
1724 p = None
1723 fm.condwrite(
1725 fm.condwrite(
1724 p, b'defaulttemplate', _(b"checking default template (%s)\n"), m
1726 p, b'defaulttemplate', _(b"checking default template (%s)\n"), m
1725 )
1727 )
1726 fm.condwrite(
1728 fm.condwrite(
1727 not m,
1729 not m,
1728 b'defaulttemplatenotfound',
1730 b'defaulttemplatenotfound',
1729 _(b" template '%s' not found\n"),
1731 _(b" template '%s' not found\n"),
1730 b"default",
1732 b"default",
1731 )
1733 )
1732 if not p:
1734 if not p:
1733 problems += 1
1735 problems += 1
1734 fm.condwrite(
1736 fm.condwrite(
1735 not p, b'', _(b" (templates seem to have been installed incorrectly)\n")
1737 not p, b'', _(b" (templates seem to have been installed incorrectly)\n")
1736 )
1738 )
1737
1739
1738 # editor
1740 # editor
1739 editor = ui.geteditor()
1741 editor = ui.geteditor()
1740 editor = util.expandpath(editor)
1742 editor = util.expandpath(editor)
1741 editorbin = procutil.shellsplit(editor)[0]
1743 editorbin = procutil.shellsplit(editor)[0]
1742 fm.write(b'editor', _(b"checking commit editor... (%s)\n"), editorbin)
1744 fm.write(b'editor', _(b"checking commit editor... (%s)\n"), editorbin)
1743 cmdpath = procutil.findexe(editorbin)
1745 cmdpath = procutil.findexe(editorbin)
1744 fm.condwrite(
1746 fm.condwrite(
1745 not cmdpath and editor == b'vi',
1747 not cmdpath and editor == b'vi',
1746 b'vinotfound',
1748 b'vinotfound',
1747 _(
1749 _(
1748 b" No commit editor set and can't find %s in PATH\n"
1750 b" No commit editor set and can't find %s in PATH\n"
1749 b" (specify a commit editor in your configuration"
1751 b" (specify a commit editor in your configuration"
1750 b" file)\n"
1752 b" file)\n"
1751 ),
1753 ),
1752 not cmdpath and editor == b'vi' and editorbin,
1754 not cmdpath and editor == b'vi' and editorbin,
1753 )
1755 )
1754 fm.condwrite(
1756 fm.condwrite(
1755 not cmdpath and editor != b'vi',
1757 not cmdpath and editor != b'vi',
1756 b'editornotfound',
1758 b'editornotfound',
1757 _(
1759 _(
1758 b" Can't find editor '%s' in PATH\n"
1760 b" Can't find editor '%s' in PATH\n"
1759 b" (specify a commit editor in your configuration"
1761 b" (specify a commit editor in your configuration"
1760 b" file)\n"
1762 b" file)\n"
1761 ),
1763 ),
1762 not cmdpath and editorbin,
1764 not cmdpath and editorbin,
1763 )
1765 )
1764 if not cmdpath and editor != b'vi':
1766 if not cmdpath and editor != b'vi':
1765 problems += 1
1767 problems += 1
1766
1768
1767 # check username
1769 # check username
1768 username = None
1770 username = None
1769 err = None
1771 err = None
1770 try:
1772 try:
1771 username = ui.username()
1773 username = ui.username()
1772 except error.Abort as e:
1774 except error.Abort as e:
1773 err = e.message
1775 err = e.message
1774 problems += 1
1776 problems += 1
1775
1777
1776 fm.condwrite(
1778 fm.condwrite(
1777 username, b'username', _(b"checking username (%s)\n"), username
1779 username, b'username', _(b"checking username (%s)\n"), username
1778 )
1780 )
1779 fm.condwrite(
1781 fm.condwrite(
1780 err,
1782 err,
1781 b'usernameerror',
1783 b'usernameerror',
1782 _(
1784 _(
1783 b"checking username...\n %s\n"
1785 b"checking username...\n %s\n"
1784 b" (specify a username in your configuration file)\n"
1786 b" (specify a username in your configuration file)\n"
1785 ),
1787 ),
1786 err,
1788 err,
1787 )
1789 )
1788
1790
1789 for name, mod in extensions.extensions():
1791 for name, mod in extensions.extensions():
1790 handler = getattr(mod, 'debuginstall', None)
1792 handler = getattr(mod, 'debuginstall', None)
1791 if handler is not None:
1793 if handler is not None:
1792 problems += handler(ui, fm)
1794 problems += handler(ui, fm)
1793
1795
1794 fm.condwrite(not problems, b'', _(b"no problems detected\n"))
1796 fm.condwrite(not problems, b'', _(b"no problems detected\n"))
1795 if not problems:
1797 if not problems:
1796 fm.data(problems=problems)
1798 fm.data(problems=problems)
1797 fm.condwrite(
1799 fm.condwrite(
1798 problems,
1800 problems,
1799 b'problems',
1801 b'problems',
1800 _(b"%d problems detected, please check your install!\n"),
1802 _(b"%d problems detected, please check your install!\n"),
1801 problems,
1803 problems,
1802 )
1804 )
1803 fm.end()
1805 fm.end()
1804
1806
1805 return problems
1807 return problems
1806
1808
1807
1809
1808 @command(b'debugknown', [], _(b'REPO ID...'), norepo=True)
1810 @command(b'debugknown', [], _(b'REPO ID...'), norepo=True)
1809 def debugknown(ui, repopath, *ids, **opts):
1811 def debugknown(ui, repopath, *ids, **opts):
1810 """test whether node ids are known to a repo
1812 """test whether node ids are known to a repo
1811
1813
1812 Every ID must be a full-length hex node id string. Returns a list of 0s
1814 Every ID must be a full-length hex node id string. Returns a list of 0s
1813 and 1s indicating unknown/known.
1815 and 1s indicating unknown/known.
1814 """
1816 """
1815 opts = pycompat.byteskwargs(opts)
1817 opts = pycompat.byteskwargs(opts)
1816 repo = hg.peer(ui, opts, repopath)
1818 repo = hg.peer(ui, opts, repopath)
1817 if not repo.capable(b'known'):
1819 if not repo.capable(b'known'):
1818 raise error.Abort(b"known() not supported by target repository")
1820 raise error.Abort(b"known() not supported by target repository")
1819 flags = repo.known([bin(s) for s in ids])
1821 flags = repo.known([bin(s) for s in ids])
1820 ui.write(b"%s\n" % (b"".join([f and b"1" or b"0" for f in flags])))
1822 ui.write(b"%s\n" % (b"".join([f and b"1" or b"0" for f in flags])))
1821
1823
1822
1824
1823 @command(b'debuglabelcomplete', [], _(b'LABEL...'))
1825 @command(b'debuglabelcomplete', [], _(b'LABEL...'))
1824 def debuglabelcomplete(ui, repo, *args):
1826 def debuglabelcomplete(ui, repo, *args):
1825 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
1827 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
1826 debugnamecomplete(ui, repo, *args)
1828 debugnamecomplete(ui, repo, *args)
1827
1829
1828
1830
1829 @command(
1831 @command(
1830 b'debuglocks',
1832 b'debuglocks',
1831 [
1833 [
1832 (b'L', b'force-lock', None, _(b'free the store lock (DANGEROUS)')),
1834 (b'L', b'force-lock', None, _(b'free the store lock (DANGEROUS)')),
1833 (
1835 (
1834 b'W',
1836 b'W',
1835 b'force-wlock',
1837 b'force-wlock',
1836 None,
1838 None,
1837 _(b'free the working state lock (DANGEROUS)'),
1839 _(b'free the working state lock (DANGEROUS)'),
1838 ),
1840 ),
1839 (b's', b'set-lock', None, _(b'set the store lock until stopped')),
1841 (b's', b'set-lock', None, _(b'set the store lock until stopped')),
1840 (
1842 (
1841 b'S',
1843 b'S',
1842 b'set-wlock',
1844 b'set-wlock',
1843 None,
1845 None,
1844 _(b'set the working state lock until stopped'),
1846 _(b'set the working state lock until stopped'),
1845 ),
1847 ),
1846 ],
1848 ],
1847 _(b'[OPTION]...'),
1849 _(b'[OPTION]...'),
1848 )
1850 )
1849 def debuglocks(ui, repo, **opts):
1851 def debuglocks(ui, repo, **opts):
1850 """show or modify state of locks
1852 """show or modify state of locks
1851
1853
1852 By default, this command will show which locks are held. This
1854 By default, this command will show which locks are held. This
1853 includes the user and process holding the lock, the amount of time
1855 includes the user and process holding the lock, the amount of time
1854 the lock has been held, and the machine name where the process is
1856 the lock has been held, and the machine name where the process is
1855 running if it's not local.
1857 running if it's not local.
1856
1858
1857 Locks protect the integrity of Mercurial's data, so should be
1859 Locks protect the integrity of Mercurial's data, so should be
1858 treated with care. System crashes or other interruptions may cause
1860 treated with care. System crashes or other interruptions may cause
1859 locks to not be properly released, though Mercurial will usually
1861 locks to not be properly released, though Mercurial will usually
1860 detect and remove such stale locks automatically.
1862 detect and remove such stale locks automatically.
1861
1863
1862 However, detecting stale locks may not always be possible (for
1864 However, detecting stale locks may not always be possible (for
1863 instance, on a shared filesystem). Removing locks may also be
1865 instance, on a shared filesystem). Removing locks may also be
1864 blocked by filesystem permissions.
1866 blocked by filesystem permissions.
1865
1867
1866 Setting a lock will prevent other commands from changing the data.
1868 Setting a lock will prevent other commands from changing the data.
1867 The command will wait until an interruption (SIGINT, SIGTERM, ...) occurs.
1869 The command will wait until an interruption (SIGINT, SIGTERM, ...) occurs.
1868 The set locks are removed when the command exits.
1870 The set locks are removed when the command exits.
1869
1871
1870 Returns 0 if no locks are held.
1872 Returns 0 if no locks are held.
1871
1873
1872 """
1874 """
1873
1875
1874 if opts.get('force_lock'):
1876 if opts.get('force_lock'):
1875 repo.svfs.unlink(b'lock')
1877 repo.svfs.unlink(b'lock')
1876 if opts.get('force_wlock'):
1878 if opts.get('force_wlock'):
1877 repo.vfs.unlink(b'wlock')
1879 repo.vfs.unlink(b'wlock')
1878 if opts.get('force_lock') or opts.get('force_wlock'):
1880 if opts.get('force_lock') or opts.get('force_wlock'):
1879 return 0
1881 return 0
1880
1882
1881 locks = []
1883 locks = []
1882 try:
1884 try:
1883 if opts.get('set_wlock'):
1885 if opts.get('set_wlock'):
1884 try:
1886 try:
1885 locks.append(repo.wlock(False))
1887 locks.append(repo.wlock(False))
1886 except error.LockHeld:
1888 except error.LockHeld:
1887 raise error.Abort(_(b'wlock is already held'))
1889 raise error.Abort(_(b'wlock is already held'))
1888 if opts.get('set_lock'):
1890 if opts.get('set_lock'):
1889 try:
1891 try:
1890 locks.append(repo.lock(False))
1892 locks.append(repo.lock(False))
1891 except error.LockHeld:
1893 except error.LockHeld:
1892 raise error.Abort(_(b'lock is already held'))
1894 raise error.Abort(_(b'lock is already held'))
1893 if len(locks):
1895 if len(locks):
1894 ui.promptchoice(_(b"ready to release the lock (y)? $$ &Yes"))
1896 ui.promptchoice(_(b"ready to release the lock (y)? $$ &Yes"))
1895 return 0
1897 return 0
1896 finally:
1898 finally:
1897 release(*locks)
1899 release(*locks)
1898
1900
1899 now = time.time()
1901 now = time.time()
1900 held = 0
1902 held = 0
1901
1903
1902 def report(vfs, name, method):
1904 def report(vfs, name, method):
1903 # this causes stale locks to get reaped for more accurate reporting
1905 # this causes stale locks to get reaped for more accurate reporting
1904 try:
1906 try:
1905 l = method(False)
1907 l = method(False)
1906 except error.LockHeld:
1908 except error.LockHeld:
1907 l = None
1909 l = None
1908
1910
1909 if l:
1911 if l:
1910 l.release()
1912 l.release()
1911 else:
1913 else:
1912 try:
1914 try:
1913 st = vfs.lstat(name)
1915 st = vfs.lstat(name)
1914 age = now - st[stat.ST_MTIME]
1916 age = now - st[stat.ST_MTIME]
1915 user = util.username(st.st_uid)
1917 user = util.username(st.st_uid)
1916 locker = vfs.readlock(name)
1918 locker = vfs.readlock(name)
1917 if b":" in locker:
1919 if b":" in locker:
1918 host, pid = locker.split(b':')
1920 host, pid = locker.split(b':')
1919 if host == socket.gethostname():
1921 if host == socket.gethostname():
1920 locker = b'user %s, process %s' % (user or b'None', pid)
1922 locker = b'user %s, process %s' % (user or b'None', pid)
1921 else:
1923 else:
1922 locker = b'user %s, process %s, host %s' % (
1924 locker = b'user %s, process %s, host %s' % (
1923 user or b'None',
1925 user or b'None',
1924 pid,
1926 pid,
1925 host,
1927 host,
1926 )
1928 )
1927 ui.writenoi18n(b"%-6s %s (%ds)\n" % (name + b":", locker, age))
1929 ui.writenoi18n(b"%-6s %s (%ds)\n" % (name + b":", locker, age))
1928 return 1
1930 return 1
1929 except OSError as e:
1931 except OSError as e:
1930 if e.errno != errno.ENOENT:
1932 if e.errno != errno.ENOENT:
1931 raise
1933 raise
1932
1934
1933 ui.writenoi18n(b"%-6s free\n" % (name + b":"))
1935 ui.writenoi18n(b"%-6s free\n" % (name + b":"))
1934 return 0
1936 return 0
1935
1937
1936 held += report(repo.svfs, b"lock", repo.lock)
1938 held += report(repo.svfs, b"lock", repo.lock)
1937 held += report(repo.vfs, b"wlock", repo.wlock)
1939 held += report(repo.vfs, b"wlock", repo.wlock)
1938
1940
1939 return held
1941 return held
1940
1942
1941
1943
1942 @command(
1944 @command(
1943 b'debugmanifestfulltextcache',
1945 b'debugmanifestfulltextcache',
1944 [
1946 [
1945 (b'', b'clear', False, _(b'clear the cache')),
1947 (b'', b'clear', False, _(b'clear the cache')),
1946 (
1948 (
1947 b'a',
1949 b'a',
1948 b'add',
1950 b'add',
1949 [],
1951 [],
1950 _(b'add the given manifest nodes to the cache'),
1952 _(b'add the given manifest nodes to the cache'),
1951 _(b'NODE'),
1953 _(b'NODE'),
1952 ),
1954 ),
1953 ],
1955 ],
1954 b'',
1956 b'',
1955 )
1957 )
1956 def debugmanifestfulltextcache(ui, repo, add=(), **opts):
1958 def debugmanifestfulltextcache(ui, repo, add=(), **opts):
1957 """show, clear or amend the contents of the manifest fulltext cache"""
1959 """show, clear or amend the contents of the manifest fulltext cache"""
1958
1960
1959 def getcache():
1961 def getcache():
1960 r = repo.manifestlog.getstorage(b'')
1962 r = repo.manifestlog.getstorage(b'')
1961 try:
1963 try:
1962 return r._fulltextcache
1964 return r._fulltextcache
1963 except AttributeError:
1965 except AttributeError:
1964 msg = _(
1966 msg = _(
1965 b"Current revlog implementation doesn't appear to have a "
1967 b"Current revlog implementation doesn't appear to have a "
1966 b"manifest fulltext cache\n"
1968 b"manifest fulltext cache\n"
1967 )
1969 )
1968 raise error.Abort(msg)
1970 raise error.Abort(msg)
1969
1971
1970 if opts.get('clear'):
1972 if opts.get('clear'):
1971 with repo.wlock():
1973 with repo.wlock():
1972 cache = getcache()
1974 cache = getcache()
1973 cache.clear(clear_persisted_data=True)
1975 cache.clear(clear_persisted_data=True)
1974 return
1976 return
1975
1977
1976 if add:
1978 if add:
1977 with repo.wlock():
1979 with repo.wlock():
1978 m = repo.manifestlog
1980 m = repo.manifestlog
1979 store = m.getstorage(b'')
1981 store = m.getstorage(b'')
1980 for n in add:
1982 for n in add:
1981 try:
1983 try:
1982 manifest = m[store.lookup(n)]
1984 manifest = m[store.lookup(n)]
1983 except error.LookupError as e:
1985 except error.LookupError as e:
1984 raise error.Abort(e, hint=b"Check your manifest node id")
1986 raise error.Abort(e, hint=b"Check your manifest node id")
1985 manifest.read() # stores revisision in cache too
1987 manifest.read() # stores revisision in cache too
1986 return
1988 return
1987
1989
1988 cache = getcache()
1990 cache = getcache()
1989 if not len(cache):
1991 if not len(cache):
1990 ui.write(_(b'cache empty\n'))
1992 ui.write(_(b'cache empty\n'))
1991 else:
1993 else:
1992 ui.write(
1994 ui.write(
1993 _(
1995 _(
1994 b'cache contains %d manifest entries, in order of most to '
1996 b'cache contains %d manifest entries, in order of most to '
1995 b'least recent:\n'
1997 b'least recent:\n'
1996 )
1998 )
1997 % (len(cache),)
1999 % (len(cache),)
1998 )
2000 )
1999 totalsize = 0
2001 totalsize = 0
2000 for nodeid in cache:
2002 for nodeid in cache:
2001 # Use cache.get to not update the LRU order
2003 # Use cache.get to not update the LRU order
2002 data = cache.peek(nodeid)
2004 data = cache.peek(nodeid)
2003 size = len(data)
2005 size = len(data)
2004 totalsize += size + 24 # 20 bytes nodeid, 4 bytes size
2006 totalsize += size + 24 # 20 bytes nodeid, 4 bytes size
2005 ui.write(
2007 ui.write(
2006 _(b'id: %s, size %s\n') % (hex(nodeid), util.bytecount(size))
2008 _(b'id: %s, size %s\n') % (hex(nodeid), util.bytecount(size))
2007 )
2009 )
2008 ondisk = cache._opener.stat(b'manifestfulltextcache').st_size
2010 ondisk = cache._opener.stat(b'manifestfulltextcache').st_size
2009 ui.write(
2011 ui.write(
2010 _(b'total cache data size %s, on-disk %s\n')
2012 _(b'total cache data size %s, on-disk %s\n')
2011 % (util.bytecount(totalsize), util.bytecount(ondisk))
2013 % (util.bytecount(totalsize), util.bytecount(ondisk))
2012 )
2014 )
2013
2015
2014
2016
2015 @command(b'debugmergestate', [] + cmdutil.templateopts, b'')
2017 @command(b'debugmergestate', [] + cmdutil.templateopts, b'')
2016 def debugmergestate(ui, repo, *args, **opts):
2018 def debugmergestate(ui, repo, *args, **opts):
2017 """print merge state
2019 """print merge state
2018
2020
2019 Use --verbose to print out information about whether v1 or v2 merge state
2021 Use --verbose to print out information about whether v1 or v2 merge state
2020 was chosen."""
2022 was chosen."""
2021
2023
2022 if ui.verbose:
2024 if ui.verbose:
2023 ms = mergestatemod.mergestate(repo)
2025 ms = mergestatemod.mergestate(repo)
2024
2026
2025 # sort so that reasonable information is on top
2027 # sort so that reasonable information is on top
2026 v1records = ms._readrecordsv1()
2028 v1records = ms._readrecordsv1()
2027 v2records = ms._readrecordsv2()
2029 v2records = ms._readrecordsv2()
2028
2030
2029 if not v1records and not v2records:
2031 if not v1records and not v2records:
2030 pass
2032 pass
2031 elif not v2records:
2033 elif not v2records:
2032 ui.writenoi18n(b'no version 2 merge state\n')
2034 ui.writenoi18n(b'no version 2 merge state\n')
2033 elif ms._v1v2match(v1records, v2records):
2035 elif ms._v1v2match(v1records, v2records):
2034 ui.writenoi18n(b'v1 and v2 states match: using v2\n')
2036 ui.writenoi18n(b'v1 and v2 states match: using v2\n')
2035 else:
2037 else:
2036 ui.writenoi18n(b'v1 and v2 states mismatch: using v1\n')
2038 ui.writenoi18n(b'v1 and v2 states mismatch: using v1\n')
2037
2039
2038 opts = pycompat.byteskwargs(opts)
2040 opts = pycompat.byteskwargs(opts)
2039 if not opts[b'template']:
2041 if not opts[b'template']:
2040 opts[b'template'] = (
2042 opts[b'template'] = (
2041 b'{if(commits, "", "no merge state found\n")}'
2043 b'{if(commits, "", "no merge state found\n")}'
2042 b'{commits % "{name}{if(label, " ({label})")}: {node}\n"}'
2044 b'{commits % "{name}{if(label, " ({label})")}: {node}\n"}'
2043 b'{files % "file: {path} (state \\"{state}\\")\n'
2045 b'{files % "file: {path} (state \\"{state}\\")\n'
2044 b'{if(local_path, "'
2046 b'{if(local_path, "'
2045 b' local path: {local_path} (hash {local_key}, flags \\"{local_flags}\\")\n'
2047 b' local path: {local_path} (hash {local_key}, flags \\"{local_flags}\\")\n'
2046 b' ancestor path: {ancestor_path} (node {ancestor_node})\n'
2048 b' ancestor path: {ancestor_path} (node {ancestor_node})\n'
2047 b' other path: {other_path} (node {other_node})\n'
2049 b' other path: {other_path} (node {other_node})\n'
2048 b'")}'
2050 b'")}'
2049 b'{if(rename_side, "'
2051 b'{if(rename_side, "'
2050 b' rename side: {rename_side}\n'
2052 b' rename side: {rename_side}\n'
2051 b' renamed path: {renamed_path}\n'
2053 b' renamed path: {renamed_path}\n'
2052 b'")}'
2054 b'")}'
2053 b'{extras % " extra: {key} = {value}\n"}'
2055 b'{extras % " extra: {key} = {value}\n"}'
2054 b'"}'
2056 b'"}'
2055 b'{extras % "extra: {file} ({key} = {value})\n"}'
2057 b'{extras % "extra: {file} ({key} = {value})\n"}'
2056 )
2058 )
2057
2059
2058 ms = mergestatemod.mergestate.read(repo)
2060 ms = mergestatemod.mergestate.read(repo)
2059
2061
2060 fm = ui.formatter(b'debugmergestate', opts)
2062 fm = ui.formatter(b'debugmergestate', opts)
2061 fm.startitem()
2063 fm.startitem()
2062
2064
2063 fm_commits = fm.nested(b'commits')
2065 fm_commits = fm.nested(b'commits')
2064 if ms.active():
2066 if ms.active():
2065 for name, node, label_index in (
2067 for name, node, label_index in (
2066 (b'local', ms.local, 0),
2068 (b'local', ms.local, 0),
2067 (b'other', ms.other, 1),
2069 (b'other', ms.other, 1),
2068 ):
2070 ):
2069 fm_commits.startitem()
2071 fm_commits.startitem()
2070 fm_commits.data(name=name)
2072 fm_commits.data(name=name)
2071 fm_commits.data(node=hex(node))
2073 fm_commits.data(node=hex(node))
2072 if ms._labels and len(ms._labels) > label_index:
2074 if ms._labels and len(ms._labels) > label_index:
2073 fm_commits.data(label=ms._labels[label_index])
2075 fm_commits.data(label=ms._labels[label_index])
2074 fm_commits.end()
2076 fm_commits.end()
2075
2077
2076 fm_files = fm.nested(b'files')
2078 fm_files = fm.nested(b'files')
2077 if ms.active():
2079 if ms.active():
2078 for f in ms:
2080 for f in ms:
2079 fm_files.startitem()
2081 fm_files.startitem()
2080 fm_files.data(path=f)
2082 fm_files.data(path=f)
2081 state = ms._state[f]
2083 state = ms._state[f]
2082 fm_files.data(state=state[0])
2084 fm_files.data(state=state[0])
2083 if state[0] in (
2085 if state[0] in (
2084 mergestatemod.MERGE_RECORD_UNRESOLVED,
2086 mergestatemod.MERGE_RECORD_UNRESOLVED,
2085 mergestatemod.MERGE_RECORD_RESOLVED,
2087 mergestatemod.MERGE_RECORD_RESOLVED,
2086 ):
2088 ):
2087 fm_files.data(local_key=state[1])
2089 fm_files.data(local_key=state[1])
2088 fm_files.data(local_path=state[2])
2090 fm_files.data(local_path=state[2])
2089 fm_files.data(ancestor_path=state[3])
2091 fm_files.data(ancestor_path=state[3])
2090 fm_files.data(ancestor_node=state[4])
2092 fm_files.data(ancestor_node=state[4])
2091 fm_files.data(other_path=state[5])
2093 fm_files.data(other_path=state[5])
2092 fm_files.data(other_node=state[6])
2094 fm_files.data(other_node=state[6])
2093 fm_files.data(local_flags=state[7])
2095 fm_files.data(local_flags=state[7])
2094 elif state[0] in (
2096 elif state[0] in (
2095 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
2097 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
2096 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
2098 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
2097 ):
2099 ):
2098 fm_files.data(renamed_path=state[1])
2100 fm_files.data(renamed_path=state[1])
2099 fm_files.data(rename_side=state[2])
2101 fm_files.data(rename_side=state[2])
2100 fm_extras = fm_files.nested(b'extras')
2102 fm_extras = fm_files.nested(b'extras')
2101 for k, v in sorted(ms.extras(f).items()):
2103 for k, v in sorted(ms.extras(f).items()):
2102 fm_extras.startitem()
2104 fm_extras.startitem()
2103 fm_extras.data(key=k)
2105 fm_extras.data(key=k)
2104 fm_extras.data(value=v)
2106 fm_extras.data(value=v)
2105 fm_extras.end()
2107 fm_extras.end()
2106
2108
2107 fm_files.end()
2109 fm_files.end()
2108
2110
2109 fm_extras = fm.nested(b'extras')
2111 fm_extras = fm.nested(b'extras')
2110 for f, d in sorted(pycompat.iteritems(ms.allextras())):
2112 for f, d in sorted(pycompat.iteritems(ms.allextras())):
2111 if f in ms:
2113 if f in ms:
2112 # If file is in mergestate, we have already processed it's extras
2114 # If file is in mergestate, we have already processed it's extras
2113 continue
2115 continue
2114 for k, v in pycompat.iteritems(d):
2116 for k, v in pycompat.iteritems(d):
2115 fm_extras.startitem()
2117 fm_extras.startitem()
2116 fm_extras.data(file=f)
2118 fm_extras.data(file=f)
2117 fm_extras.data(key=k)
2119 fm_extras.data(key=k)
2118 fm_extras.data(value=v)
2120 fm_extras.data(value=v)
2119 fm_extras.end()
2121 fm_extras.end()
2120
2122
2121 fm.end()
2123 fm.end()
2122
2124
2123
2125
2124 @command(b'debugnamecomplete', [], _(b'NAME...'))
2126 @command(b'debugnamecomplete', [], _(b'NAME...'))
2125 def debugnamecomplete(ui, repo, *args):
2127 def debugnamecomplete(ui, repo, *args):
2126 '''complete "names" - tags, open branch names, bookmark names'''
2128 '''complete "names" - tags, open branch names, bookmark names'''
2127
2129
2128 names = set()
2130 names = set()
2129 # since we previously only listed open branches, we will handle that
2131 # since we previously only listed open branches, we will handle that
2130 # specially (after this for loop)
2132 # specially (after this for loop)
2131 for name, ns in pycompat.iteritems(repo.names):
2133 for name, ns in pycompat.iteritems(repo.names):
2132 if name != b'branches':
2134 if name != b'branches':
2133 names.update(ns.listnames(repo))
2135 names.update(ns.listnames(repo))
2134 names.update(
2136 names.update(
2135 tag
2137 tag
2136 for (tag, heads, tip, closed) in repo.branchmap().iterbranches()
2138 for (tag, heads, tip, closed) in repo.branchmap().iterbranches()
2137 if not closed
2139 if not closed
2138 )
2140 )
2139 completions = set()
2141 completions = set()
2140 if not args:
2142 if not args:
2141 args = [b'']
2143 args = [b'']
2142 for a in args:
2144 for a in args:
2143 completions.update(n for n in names if n.startswith(a))
2145 completions.update(n for n in names if n.startswith(a))
2144 ui.write(b'\n'.join(sorted(completions)))
2146 ui.write(b'\n'.join(sorted(completions)))
2145 ui.write(b'\n')
2147 ui.write(b'\n')
2146
2148
2147
2149
2148 @command(
2150 @command(
2149 b'debugnodemap',
2151 b'debugnodemap',
2150 [
2152 [
2151 (
2153 (
2152 b'',
2154 b'',
2153 b'dump-new',
2155 b'dump-new',
2154 False,
2156 False,
2155 _(b'write a (new) persistent binary nodemap on stdin'),
2157 _(b'write a (new) persistent binary nodemap on stdin'),
2156 ),
2158 ),
2157 (b'', b'dump-disk', False, _(b'dump on-disk data on stdin')),
2159 (b'', b'dump-disk', False, _(b'dump on-disk data on stdin')),
2158 (
2160 (
2159 b'',
2161 b'',
2160 b'check',
2162 b'check',
2161 False,
2163 False,
2162 _(b'check that the data on disk data are correct.'),
2164 _(b'check that the data on disk data are correct.'),
2163 ),
2165 ),
2164 (
2166 (
2165 b'',
2167 b'',
2166 b'metadata',
2168 b'metadata',
2167 False,
2169 False,
2168 _(b'display the on disk meta data for the nodemap'),
2170 _(b'display the on disk meta data for the nodemap'),
2169 ),
2171 ),
2170 ],
2172 ],
2171 )
2173 )
2172 def debugnodemap(ui, repo, **opts):
2174 def debugnodemap(ui, repo, **opts):
2173 """write and inspect on disk nodemap
2175 """write and inspect on disk nodemap
2174 """
2176 """
2175 if opts['dump_new']:
2177 if opts['dump_new']:
2176 unfi = repo.unfiltered()
2178 unfi = repo.unfiltered()
2177 cl = unfi.changelog
2179 cl = unfi.changelog
2178 if util.safehasattr(cl.index, "nodemap_data_all"):
2180 if util.safehasattr(cl.index, "nodemap_data_all"):
2179 data = cl.index.nodemap_data_all()
2181 data = cl.index.nodemap_data_all()
2180 else:
2182 else:
2181 data = nodemap.persistent_data(cl.index)
2183 data = nodemap.persistent_data(cl.index)
2182 ui.write(data)
2184 ui.write(data)
2183 elif opts['dump_disk']:
2185 elif opts['dump_disk']:
2184 unfi = repo.unfiltered()
2186 unfi = repo.unfiltered()
2185 cl = unfi.changelog
2187 cl = unfi.changelog
2186 nm_data = nodemap.persisted_data(cl)
2188 nm_data = nodemap.persisted_data(cl)
2187 if nm_data is not None:
2189 if nm_data is not None:
2188 docket, data = nm_data
2190 docket, data = nm_data
2189 ui.write(data[:])
2191 ui.write(data[:])
2190 elif opts['check']:
2192 elif opts['check']:
2191 unfi = repo.unfiltered()
2193 unfi = repo.unfiltered()
2192 cl = unfi.changelog
2194 cl = unfi.changelog
2193 nm_data = nodemap.persisted_data(cl)
2195 nm_data = nodemap.persisted_data(cl)
2194 if nm_data is not None:
2196 if nm_data is not None:
2195 docket, data = nm_data
2197 docket, data = nm_data
2196 return nodemap.check_data(ui, cl.index, data)
2198 return nodemap.check_data(ui, cl.index, data)
2197 elif opts['metadata']:
2199 elif opts['metadata']:
2198 unfi = repo.unfiltered()
2200 unfi = repo.unfiltered()
2199 cl = unfi.changelog
2201 cl = unfi.changelog
2200 nm_data = nodemap.persisted_data(cl)
2202 nm_data = nodemap.persisted_data(cl)
2201 if nm_data is not None:
2203 if nm_data is not None:
2202 docket, data = nm_data
2204 docket, data = nm_data
2203 ui.write((b"uid: %s\n") % docket.uid)
2205 ui.write((b"uid: %s\n") % docket.uid)
2204 ui.write((b"tip-rev: %d\n") % docket.tip_rev)
2206 ui.write((b"tip-rev: %d\n") % docket.tip_rev)
2205 ui.write((b"tip-node: %s\n") % hex(docket.tip_node))
2207 ui.write((b"tip-node: %s\n") % hex(docket.tip_node))
2206 ui.write((b"data-length: %d\n") % docket.data_length)
2208 ui.write((b"data-length: %d\n") % docket.data_length)
2207 ui.write((b"data-unused: %d\n") % docket.data_unused)
2209 ui.write((b"data-unused: %d\n") % docket.data_unused)
2208 unused_perc = docket.data_unused * 100.0 / docket.data_length
2210 unused_perc = docket.data_unused * 100.0 / docket.data_length
2209 ui.write((b"data-unused: %2.3f%%\n") % unused_perc)
2211 ui.write((b"data-unused: %2.3f%%\n") % unused_perc)
2210
2212
2211
2213
2212 @command(
2214 @command(
2213 b'debugobsolete',
2215 b'debugobsolete',
2214 [
2216 [
2215 (b'', b'flags', 0, _(b'markers flag')),
2217 (b'', b'flags', 0, _(b'markers flag')),
2216 (
2218 (
2217 b'',
2219 b'',
2218 b'record-parents',
2220 b'record-parents',
2219 False,
2221 False,
2220 _(b'record parent information for the precursor'),
2222 _(b'record parent information for the precursor'),
2221 ),
2223 ),
2222 (b'r', b'rev', [], _(b'display markers relevant to REV')),
2224 (b'r', b'rev', [], _(b'display markers relevant to REV')),
2223 (
2225 (
2224 b'',
2226 b'',
2225 b'exclusive',
2227 b'exclusive',
2226 False,
2228 False,
2227 _(b'restrict display to markers only relevant to REV'),
2229 _(b'restrict display to markers only relevant to REV'),
2228 ),
2230 ),
2229 (b'', b'index', False, _(b'display index of the marker')),
2231 (b'', b'index', False, _(b'display index of the marker')),
2230 (b'', b'delete', [], _(b'delete markers specified by indices')),
2232 (b'', b'delete', [], _(b'delete markers specified by indices')),
2231 ]
2233 ]
2232 + cmdutil.commitopts2
2234 + cmdutil.commitopts2
2233 + cmdutil.formatteropts,
2235 + cmdutil.formatteropts,
2234 _(b'[OBSOLETED [REPLACEMENT ...]]'),
2236 _(b'[OBSOLETED [REPLACEMENT ...]]'),
2235 )
2237 )
2236 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2238 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2237 """create arbitrary obsolete marker
2239 """create arbitrary obsolete marker
2238
2240
2239 With no arguments, displays the list of obsolescence markers."""
2241 With no arguments, displays the list of obsolescence markers."""
2240
2242
2241 opts = pycompat.byteskwargs(opts)
2243 opts = pycompat.byteskwargs(opts)
2242
2244
2243 def parsenodeid(s):
2245 def parsenodeid(s):
2244 try:
2246 try:
2245 # We do not use revsingle/revrange functions here to accept
2247 # We do not use revsingle/revrange functions here to accept
2246 # arbitrary node identifiers, possibly not present in the
2248 # arbitrary node identifiers, possibly not present in the
2247 # local repository.
2249 # local repository.
2248 n = bin(s)
2250 n = bin(s)
2249 if len(n) != len(nullid):
2251 if len(n) != len(nullid):
2250 raise TypeError()
2252 raise TypeError()
2251 return n
2253 return n
2252 except TypeError:
2254 except TypeError:
2253 raise error.Abort(
2255 raise error.Abort(
2254 b'changeset references must be full hexadecimal '
2256 b'changeset references must be full hexadecimal '
2255 b'node identifiers'
2257 b'node identifiers'
2256 )
2258 )
2257
2259
2258 if opts.get(b'delete'):
2260 if opts.get(b'delete'):
2259 indices = []
2261 indices = []
2260 for v in opts.get(b'delete'):
2262 for v in opts.get(b'delete'):
2261 try:
2263 try:
2262 indices.append(int(v))
2264 indices.append(int(v))
2263 except ValueError:
2265 except ValueError:
2264 raise error.Abort(
2266 raise error.Abort(
2265 _(b'invalid index value: %r') % v,
2267 _(b'invalid index value: %r') % v,
2266 hint=_(b'use integers for indices'),
2268 hint=_(b'use integers for indices'),
2267 )
2269 )
2268
2270
2269 if repo.currenttransaction():
2271 if repo.currenttransaction():
2270 raise error.Abort(
2272 raise error.Abort(
2271 _(b'cannot delete obsmarkers in the middle of transaction.')
2273 _(b'cannot delete obsmarkers in the middle of transaction.')
2272 )
2274 )
2273
2275
2274 with repo.lock():
2276 with repo.lock():
2275 n = repair.deleteobsmarkers(repo.obsstore, indices)
2277 n = repair.deleteobsmarkers(repo.obsstore, indices)
2276 ui.write(_(b'deleted %i obsolescence markers\n') % n)
2278 ui.write(_(b'deleted %i obsolescence markers\n') % n)
2277
2279
2278 return
2280 return
2279
2281
2280 if precursor is not None:
2282 if precursor is not None:
2281 if opts[b'rev']:
2283 if opts[b'rev']:
2282 raise error.Abort(b'cannot select revision when creating marker')
2284 raise error.Abort(b'cannot select revision when creating marker')
2283 metadata = {}
2285 metadata = {}
2284 metadata[b'user'] = encoding.fromlocal(opts[b'user'] or ui.username())
2286 metadata[b'user'] = encoding.fromlocal(opts[b'user'] or ui.username())
2285 succs = tuple(parsenodeid(succ) for succ in successors)
2287 succs = tuple(parsenodeid(succ) for succ in successors)
2286 l = repo.lock()
2288 l = repo.lock()
2287 try:
2289 try:
2288 tr = repo.transaction(b'debugobsolete')
2290 tr = repo.transaction(b'debugobsolete')
2289 try:
2291 try:
2290 date = opts.get(b'date')
2292 date = opts.get(b'date')
2291 if date:
2293 if date:
2292 date = dateutil.parsedate(date)
2294 date = dateutil.parsedate(date)
2293 else:
2295 else:
2294 date = None
2296 date = None
2295 prec = parsenodeid(precursor)
2297 prec = parsenodeid(precursor)
2296 parents = None
2298 parents = None
2297 if opts[b'record_parents']:
2299 if opts[b'record_parents']:
2298 if prec not in repo.unfiltered():
2300 if prec not in repo.unfiltered():
2299 raise error.Abort(
2301 raise error.Abort(
2300 b'cannot used --record-parents on '
2302 b'cannot used --record-parents on '
2301 b'unknown changesets'
2303 b'unknown changesets'
2302 )
2304 )
2303 parents = repo.unfiltered()[prec].parents()
2305 parents = repo.unfiltered()[prec].parents()
2304 parents = tuple(p.node() for p in parents)
2306 parents = tuple(p.node() for p in parents)
2305 repo.obsstore.create(
2307 repo.obsstore.create(
2306 tr,
2308 tr,
2307 prec,
2309 prec,
2308 succs,
2310 succs,
2309 opts[b'flags'],
2311 opts[b'flags'],
2310 parents=parents,
2312 parents=parents,
2311 date=date,
2313 date=date,
2312 metadata=metadata,
2314 metadata=metadata,
2313 ui=ui,
2315 ui=ui,
2314 )
2316 )
2315 tr.close()
2317 tr.close()
2316 except ValueError as exc:
2318 except ValueError as exc:
2317 raise error.Abort(
2319 raise error.Abort(
2318 _(b'bad obsmarker input: %s') % pycompat.bytestr(exc)
2320 _(b'bad obsmarker input: %s') % pycompat.bytestr(exc)
2319 )
2321 )
2320 finally:
2322 finally:
2321 tr.release()
2323 tr.release()
2322 finally:
2324 finally:
2323 l.release()
2325 l.release()
2324 else:
2326 else:
2325 if opts[b'rev']:
2327 if opts[b'rev']:
2326 revs = scmutil.revrange(repo, opts[b'rev'])
2328 revs = scmutil.revrange(repo, opts[b'rev'])
2327 nodes = [repo[r].node() for r in revs]
2329 nodes = [repo[r].node() for r in revs]
2328 markers = list(
2330 markers = list(
2329 obsutil.getmarkers(
2331 obsutil.getmarkers(
2330 repo, nodes=nodes, exclusive=opts[b'exclusive']
2332 repo, nodes=nodes, exclusive=opts[b'exclusive']
2331 )
2333 )
2332 )
2334 )
2333 markers.sort(key=lambda x: x._data)
2335 markers.sort(key=lambda x: x._data)
2334 else:
2336 else:
2335 markers = obsutil.getmarkers(repo)
2337 markers = obsutil.getmarkers(repo)
2336
2338
2337 markerstoiter = markers
2339 markerstoiter = markers
2338 isrelevant = lambda m: True
2340 isrelevant = lambda m: True
2339 if opts.get(b'rev') and opts.get(b'index'):
2341 if opts.get(b'rev') and opts.get(b'index'):
2340 markerstoiter = obsutil.getmarkers(repo)
2342 markerstoiter = obsutil.getmarkers(repo)
2341 markerset = set(markers)
2343 markerset = set(markers)
2342 isrelevant = lambda m: m in markerset
2344 isrelevant = lambda m: m in markerset
2343
2345
2344 fm = ui.formatter(b'debugobsolete', opts)
2346 fm = ui.formatter(b'debugobsolete', opts)
2345 for i, m in enumerate(markerstoiter):
2347 for i, m in enumerate(markerstoiter):
2346 if not isrelevant(m):
2348 if not isrelevant(m):
2347 # marker can be irrelevant when we're iterating over a set
2349 # marker can be irrelevant when we're iterating over a set
2348 # of markers (markerstoiter) which is bigger than the set
2350 # of markers (markerstoiter) which is bigger than the set
2349 # of markers we want to display (markers)
2351 # of markers we want to display (markers)
2350 # this can happen if both --index and --rev options are
2352 # this can happen if both --index and --rev options are
2351 # provided and thus we need to iterate over all of the markers
2353 # provided and thus we need to iterate over all of the markers
2352 # to get the correct indices, but only display the ones that
2354 # to get the correct indices, but only display the ones that
2353 # are relevant to --rev value
2355 # are relevant to --rev value
2354 continue
2356 continue
2355 fm.startitem()
2357 fm.startitem()
2356 ind = i if opts.get(b'index') else None
2358 ind = i if opts.get(b'index') else None
2357 cmdutil.showmarker(fm, m, index=ind)
2359 cmdutil.showmarker(fm, m, index=ind)
2358 fm.end()
2360 fm.end()
2359
2361
2360
2362
2361 @command(
2363 @command(
2362 b'debugp1copies',
2364 b'debugp1copies',
2363 [(b'r', b'rev', b'', _(b'revision to debug'), _(b'REV'))],
2365 [(b'r', b'rev', b'', _(b'revision to debug'), _(b'REV'))],
2364 _(b'[-r REV]'),
2366 _(b'[-r REV]'),
2365 )
2367 )
2366 def debugp1copies(ui, repo, **opts):
2368 def debugp1copies(ui, repo, **opts):
2367 """dump copy information compared to p1"""
2369 """dump copy information compared to p1"""
2368
2370
2369 opts = pycompat.byteskwargs(opts)
2371 opts = pycompat.byteskwargs(opts)
2370 ctx = scmutil.revsingle(repo, opts.get(b'rev'), default=None)
2372 ctx = scmutil.revsingle(repo, opts.get(b'rev'), default=None)
2371 for dst, src in ctx.p1copies().items():
2373 for dst, src in ctx.p1copies().items():
2372 ui.write(b'%s -> %s\n' % (src, dst))
2374 ui.write(b'%s -> %s\n' % (src, dst))
2373
2375
2374
2376
2375 @command(
2377 @command(
2376 b'debugp2copies',
2378 b'debugp2copies',
2377 [(b'r', b'rev', b'', _(b'revision to debug'), _(b'REV'))],
2379 [(b'r', b'rev', b'', _(b'revision to debug'), _(b'REV'))],
2378 _(b'[-r REV]'),
2380 _(b'[-r REV]'),
2379 )
2381 )
2380 def debugp1copies(ui, repo, **opts):
2382 def debugp1copies(ui, repo, **opts):
2381 """dump copy information compared to p2"""
2383 """dump copy information compared to p2"""
2382
2384
2383 opts = pycompat.byteskwargs(opts)
2385 opts = pycompat.byteskwargs(opts)
2384 ctx = scmutil.revsingle(repo, opts.get(b'rev'), default=None)
2386 ctx = scmutil.revsingle(repo, opts.get(b'rev'), default=None)
2385 for dst, src in ctx.p2copies().items():
2387 for dst, src in ctx.p2copies().items():
2386 ui.write(b'%s -> %s\n' % (src, dst))
2388 ui.write(b'%s -> %s\n' % (src, dst))
2387
2389
2388
2390
2389 @command(
2391 @command(
2390 b'debugpathcomplete',
2392 b'debugpathcomplete',
2391 [
2393 [
2392 (b'f', b'full', None, _(b'complete an entire path')),
2394 (b'f', b'full', None, _(b'complete an entire path')),
2393 (b'n', b'normal', None, _(b'show only normal files')),
2395 (b'n', b'normal', None, _(b'show only normal files')),
2394 (b'a', b'added', None, _(b'show only added files')),
2396 (b'a', b'added', None, _(b'show only added files')),
2395 (b'r', b'removed', None, _(b'show only removed files')),
2397 (b'r', b'removed', None, _(b'show only removed files')),
2396 ],
2398 ],
2397 _(b'FILESPEC...'),
2399 _(b'FILESPEC...'),
2398 )
2400 )
2399 def debugpathcomplete(ui, repo, *specs, **opts):
2401 def debugpathcomplete(ui, repo, *specs, **opts):
2400 '''complete part or all of a tracked path
2402 '''complete part or all of a tracked path
2401
2403
2402 This command supports shells that offer path name completion. It
2404 This command supports shells that offer path name completion. It
2403 currently completes only files already known to the dirstate.
2405 currently completes only files already known to the dirstate.
2404
2406
2405 Completion extends only to the next path segment unless
2407 Completion extends only to the next path segment unless
2406 --full is specified, in which case entire paths are used.'''
2408 --full is specified, in which case entire paths are used.'''
2407
2409
2408 def complete(path, acceptable):
2410 def complete(path, acceptable):
2409 dirstate = repo.dirstate
2411 dirstate = repo.dirstate
2410 spec = os.path.normpath(os.path.join(encoding.getcwd(), path))
2412 spec = os.path.normpath(os.path.join(encoding.getcwd(), path))
2411 rootdir = repo.root + pycompat.ossep
2413 rootdir = repo.root + pycompat.ossep
2412 if spec != repo.root and not spec.startswith(rootdir):
2414 if spec != repo.root and not spec.startswith(rootdir):
2413 return [], []
2415 return [], []
2414 if os.path.isdir(spec):
2416 if os.path.isdir(spec):
2415 spec += b'/'
2417 spec += b'/'
2416 spec = spec[len(rootdir) :]
2418 spec = spec[len(rootdir) :]
2417 fixpaths = pycompat.ossep != b'/'
2419 fixpaths = pycompat.ossep != b'/'
2418 if fixpaths:
2420 if fixpaths:
2419 spec = spec.replace(pycompat.ossep, b'/')
2421 spec = spec.replace(pycompat.ossep, b'/')
2420 speclen = len(spec)
2422 speclen = len(spec)
2421 fullpaths = opts['full']
2423 fullpaths = opts['full']
2422 files, dirs = set(), set()
2424 files, dirs = set(), set()
2423 adddir, addfile = dirs.add, files.add
2425 adddir, addfile = dirs.add, files.add
2424 for f, st in pycompat.iteritems(dirstate):
2426 for f, st in pycompat.iteritems(dirstate):
2425 if f.startswith(spec) and st[0] in acceptable:
2427 if f.startswith(spec) and st[0] in acceptable:
2426 if fixpaths:
2428 if fixpaths:
2427 f = f.replace(b'/', pycompat.ossep)
2429 f = f.replace(b'/', pycompat.ossep)
2428 if fullpaths:
2430 if fullpaths:
2429 addfile(f)
2431 addfile(f)
2430 continue
2432 continue
2431 s = f.find(pycompat.ossep, speclen)
2433 s = f.find(pycompat.ossep, speclen)
2432 if s >= 0:
2434 if s >= 0:
2433 adddir(f[:s])
2435 adddir(f[:s])
2434 else:
2436 else:
2435 addfile(f)
2437 addfile(f)
2436 return files, dirs
2438 return files, dirs
2437
2439
2438 acceptable = b''
2440 acceptable = b''
2439 if opts['normal']:
2441 if opts['normal']:
2440 acceptable += b'nm'
2442 acceptable += b'nm'
2441 if opts['added']:
2443 if opts['added']:
2442 acceptable += b'a'
2444 acceptable += b'a'
2443 if opts['removed']:
2445 if opts['removed']:
2444 acceptable += b'r'
2446 acceptable += b'r'
2445 cwd = repo.getcwd()
2447 cwd = repo.getcwd()
2446 if not specs:
2448 if not specs:
2447 specs = [b'.']
2449 specs = [b'.']
2448
2450
2449 files, dirs = set(), set()
2451 files, dirs = set(), set()
2450 for spec in specs:
2452 for spec in specs:
2451 f, d = complete(spec, acceptable or b'nmar')
2453 f, d = complete(spec, acceptable or b'nmar')
2452 files.update(f)
2454 files.update(f)
2453 dirs.update(d)
2455 dirs.update(d)
2454 files.update(dirs)
2456 files.update(dirs)
2455 ui.write(b'\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2457 ui.write(b'\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2456 ui.write(b'\n')
2458 ui.write(b'\n')
2457
2459
2458
2460
2459 @command(
2461 @command(
2460 b'debugpathcopies',
2462 b'debugpathcopies',
2461 cmdutil.walkopts,
2463 cmdutil.walkopts,
2462 b'hg debugpathcopies REV1 REV2 [FILE]',
2464 b'hg debugpathcopies REV1 REV2 [FILE]',
2463 inferrepo=True,
2465 inferrepo=True,
2464 )
2466 )
2465 def debugpathcopies(ui, repo, rev1, rev2, *pats, **opts):
2467 def debugpathcopies(ui, repo, rev1, rev2, *pats, **opts):
2466 """show copies between two revisions"""
2468 """show copies between two revisions"""
2467 ctx1 = scmutil.revsingle(repo, rev1)
2469 ctx1 = scmutil.revsingle(repo, rev1)
2468 ctx2 = scmutil.revsingle(repo, rev2)
2470 ctx2 = scmutil.revsingle(repo, rev2)
2469 m = scmutil.match(ctx1, pats, opts)
2471 m = scmutil.match(ctx1, pats, opts)
2470 for dst, src in sorted(copies.pathcopies(ctx1, ctx2, m).items()):
2472 for dst, src in sorted(copies.pathcopies(ctx1, ctx2, m).items()):
2471 ui.write(b'%s -> %s\n' % (src, dst))
2473 ui.write(b'%s -> %s\n' % (src, dst))
2472
2474
2473
2475
2474 @command(b'debugpeer', [], _(b'PATH'), norepo=True)
2476 @command(b'debugpeer', [], _(b'PATH'), norepo=True)
2475 def debugpeer(ui, path):
2477 def debugpeer(ui, path):
2476 """establish a connection to a peer repository"""
2478 """establish a connection to a peer repository"""
2477 # Always enable peer request logging. Requires --debug to display
2479 # Always enable peer request logging. Requires --debug to display
2478 # though.
2480 # though.
2479 overrides = {
2481 overrides = {
2480 (b'devel', b'debug.peer-request'): True,
2482 (b'devel', b'debug.peer-request'): True,
2481 }
2483 }
2482
2484
2483 with ui.configoverride(overrides):
2485 with ui.configoverride(overrides):
2484 peer = hg.peer(ui, {}, path)
2486 peer = hg.peer(ui, {}, path)
2485
2487
2486 local = peer.local() is not None
2488 local = peer.local() is not None
2487 canpush = peer.canpush()
2489 canpush = peer.canpush()
2488
2490
2489 ui.write(_(b'url: %s\n') % peer.url())
2491 ui.write(_(b'url: %s\n') % peer.url())
2490 ui.write(_(b'local: %s\n') % (_(b'yes') if local else _(b'no')))
2492 ui.write(_(b'local: %s\n') % (_(b'yes') if local else _(b'no')))
2491 ui.write(_(b'pushable: %s\n') % (_(b'yes') if canpush else _(b'no')))
2493 ui.write(_(b'pushable: %s\n') % (_(b'yes') if canpush else _(b'no')))
2492
2494
2493
2495
2494 @command(
2496 @command(
2495 b'debugpickmergetool',
2497 b'debugpickmergetool',
2496 [
2498 [
2497 (b'r', b'rev', b'', _(b'check for files in this revision'), _(b'REV')),
2499 (b'r', b'rev', b'', _(b'check for files in this revision'), _(b'REV')),
2498 (b'', b'changedelete', None, _(b'emulate merging change and delete')),
2500 (b'', b'changedelete', None, _(b'emulate merging change and delete')),
2499 ]
2501 ]
2500 + cmdutil.walkopts
2502 + cmdutil.walkopts
2501 + cmdutil.mergetoolopts,
2503 + cmdutil.mergetoolopts,
2502 _(b'[PATTERN]...'),
2504 _(b'[PATTERN]...'),
2503 inferrepo=True,
2505 inferrepo=True,
2504 )
2506 )
2505 def debugpickmergetool(ui, repo, *pats, **opts):
2507 def debugpickmergetool(ui, repo, *pats, **opts):
2506 """examine which merge tool is chosen for specified file
2508 """examine which merge tool is chosen for specified file
2507
2509
2508 As described in :hg:`help merge-tools`, Mercurial examines
2510 As described in :hg:`help merge-tools`, Mercurial examines
2509 configurations below in this order to decide which merge tool is
2511 configurations below in this order to decide which merge tool is
2510 chosen for specified file.
2512 chosen for specified file.
2511
2513
2512 1. ``--tool`` option
2514 1. ``--tool`` option
2513 2. ``HGMERGE`` environment variable
2515 2. ``HGMERGE`` environment variable
2514 3. configurations in ``merge-patterns`` section
2516 3. configurations in ``merge-patterns`` section
2515 4. configuration of ``ui.merge``
2517 4. configuration of ``ui.merge``
2516 5. configurations in ``merge-tools`` section
2518 5. configurations in ``merge-tools`` section
2517 6. ``hgmerge`` tool (for historical reason only)
2519 6. ``hgmerge`` tool (for historical reason only)
2518 7. default tool for fallback (``:merge`` or ``:prompt``)
2520 7. default tool for fallback (``:merge`` or ``:prompt``)
2519
2521
2520 This command writes out examination result in the style below::
2522 This command writes out examination result in the style below::
2521
2523
2522 FILE = MERGETOOL
2524 FILE = MERGETOOL
2523
2525
2524 By default, all files known in the first parent context of the
2526 By default, all files known in the first parent context of the
2525 working directory are examined. Use file patterns and/or -I/-X
2527 working directory are examined. Use file patterns and/or -I/-X
2526 options to limit target files. -r/--rev is also useful to examine
2528 options to limit target files. -r/--rev is also useful to examine
2527 files in another context without actual updating to it.
2529 files in another context without actual updating to it.
2528
2530
2529 With --debug, this command shows warning messages while matching
2531 With --debug, this command shows warning messages while matching
2530 against ``merge-patterns`` and so on, too. It is recommended to
2532 against ``merge-patterns`` and so on, too. It is recommended to
2531 use this option with explicit file patterns and/or -I/-X options,
2533 use this option with explicit file patterns and/or -I/-X options,
2532 because this option increases amount of output per file according
2534 because this option increases amount of output per file according
2533 to configurations in hgrc.
2535 to configurations in hgrc.
2534
2536
2535 With -v/--verbose, this command shows configurations below at
2537 With -v/--verbose, this command shows configurations below at
2536 first (only if specified).
2538 first (only if specified).
2537
2539
2538 - ``--tool`` option
2540 - ``--tool`` option
2539 - ``HGMERGE`` environment variable
2541 - ``HGMERGE`` environment variable
2540 - configuration of ``ui.merge``
2542 - configuration of ``ui.merge``
2541
2543
2542 If merge tool is chosen before matching against
2544 If merge tool is chosen before matching against
2543 ``merge-patterns``, this command can't show any helpful
2545 ``merge-patterns``, this command can't show any helpful
2544 information, even with --debug. In such case, information above is
2546 information, even with --debug. In such case, information above is
2545 useful to know why a merge tool is chosen.
2547 useful to know why a merge tool is chosen.
2546 """
2548 """
2547 opts = pycompat.byteskwargs(opts)
2549 opts = pycompat.byteskwargs(opts)
2548 overrides = {}
2550 overrides = {}
2549 if opts[b'tool']:
2551 if opts[b'tool']:
2550 overrides[(b'ui', b'forcemerge')] = opts[b'tool']
2552 overrides[(b'ui', b'forcemerge')] = opts[b'tool']
2551 ui.notenoi18n(b'with --tool %r\n' % (pycompat.bytestr(opts[b'tool'])))
2553 ui.notenoi18n(b'with --tool %r\n' % (pycompat.bytestr(opts[b'tool'])))
2552
2554
2553 with ui.configoverride(overrides, b'debugmergepatterns'):
2555 with ui.configoverride(overrides, b'debugmergepatterns'):
2554 hgmerge = encoding.environ.get(b"HGMERGE")
2556 hgmerge = encoding.environ.get(b"HGMERGE")
2555 if hgmerge is not None:
2557 if hgmerge is not None:
2556 ui.notenoi18n(b'with HGMERGE=%r\n' % (pycompat.bytestr(hgmerge)))
2558 ui.notenoi18n(b'with HGMERGE=%r\n' % (pycompat.bytestr(hgmerge)))
2557 uimerge = ui.config(b"ui", b"merge")
2559 uimerge = ui.config(b"ui", b"merge")
2558 if uimerge:
2560 if uimerge:
2559 ui.notenoi18n(b'with ui.merge=%r\n' % (pycompat.bytestr(uimerge)))
2561 ui.notenoi18n(b'with ui.merge=%r\n' % (pycompat.bytestr(uimerge)))
2560
2562
2561 ctx = scmutil.revsingle(repo, opts.get(b'rev'))
2563 ctx = scmutil.revsingle(repo, opts.get(b'rev'))
2562 m = scmutil.match(ctx, pats, opts)
2564 m = scmutil.match(ctx, pats, opts)
2563 changedelete = opts[b'changedelete']
2565 changedelete = opts[b'changedelete']
2564 for path in ctx.walk(m):
2566 for path in ctx.walk(m):
2565 fctx = ctx[path]
2567 fctx = ctx[path]
2566 try:
2568 try:
2567 if not ui.debugflag:
2569 if not ui.debugflag:
2568 ui.pushbuffer(error=True)
2570 ui.pushbuffer(error=True)
2569 tool, toolpath = filemerge._picktool(
2571 tool, toolpath = filemerge._picktool(
2570 repo,
2572 repo,
2571 ui,
2573 ui,
2572 path,
2574 path,
2573 fctx.isbinary(),
2575 fctx.isbinary(),
2574 b'l' in fctx.flags(),
2576 b'l' in fctx.flags(),
2575 changedelete,
2577 changedelete,
2576 )
2578 )
2577 finally:
2579 finally:
2578 if not ui.debugflag:
2580 if not ui.debugflag:
2579 ui.popbuffer()
2581 ui.popbuffer()
2580 ui.write(b'%s = %s\n' % (path, tool))
2582 ui.write(b'%s = %s\n' % (path, tool))
2581
2583
2582
2584
2583 @command(b'debugpushkey', [], _(b'REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2585 @command(b'debugpushkey', [], _(b'REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2584 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2586 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2585 '''access the pushkey key/value protocol
2587 '''access the pushkey key/value protocol
2586
2588
2587 With two args, list the keys in the given namespace.
2589 With two args, list the keys in the given namespace.
2588
2590
2589 With five args, set a key to new if it currently is set to old.
2591 With five args, set a key to new if it currently is set to old.
2590 Reports success or failure.
2592 Reports success or failure.
2591 '''
2593 '''
2592
2594
2593 target = hg.peer(ui, {}, repopath)
2595 target = hg.peer(ui, {}, repopath)
2594 if keyinfo:
2596 if keyinfo:
2595 key, old, new = keyinfo
2597 key, old, new = keyinfo
2596 with target.commandexecutor() as e:
2598 with target.commandexecutor() as e:
2597 r = e.callcommand(
2599 r = e.callcommand(
2598 b'pushkey',
2600 b'pushkey',
2599 {
2601 {
2600 b'namespace': namespace,
2602 b'namespace': namespace,
2601 b'key': key,
2603 b'key': key,
2602 b'old': old,
2604 b'old': old,
2603 b'new': new,
2605 b'new': new,
2604 },
2606 },
2605 ).result()
2607 ).result()
2606
2608
2607 ui.status(pycompat.bytestr(r) + b'\n')
2609 ui.status(pycompat.bytestr(r) + b'\n')
2608 return not r
2610 return not r
2609 else:
2611 else:
2610 for k, v in sorted(pycompat.iteritems(target.listkeys(namespace))):
2612 for k, v in sorted(pycompat.iteritems(target.listkeys(namespace))):
2611 ui.write(
2613 ui.write(
2612 b"%s\t%s\n" % (stringutil.escapestr(k), stringutil.escapestr(v))
2614 b"%s\t%s\n" % (stringutil.escapestr(k), stringutil.escapestr(v))
2613 )
2615 )
2614
2616
2615
2617
2616 @command(b'debugpvec', [], _(b'A B'))
2618 @command(b'debugpvec', [], _(b'A B'))
2617 def debugpvec(ui, repo, a, b=None):
2619 def debugpvec(ui, repo, a, b=None):
2618 ca = scmutil.revsingle(repo, a)
2620 ca = scmutil.revsingle(repo, a)
2619 cb = scmutil.revsingle(repo, b)
2621 cb = scmutil.revsingle(repo, b)
2620 pa = pvec.ctxpvec(ca)
2622 pa = pvec.ctxpvec(ca)
2621 pb = pvec.ctxpvec(cb)
2623 pb = pvec.ctxpvec(cb)
2622 if pa == pb:
2624 if pa == pb:
2623 rel = b"="
2625 rel = b"="
2624 elif pa > pb:
2626 elif pa > pb:
2625 rel = b">"
2627 rel = b">"
2626 elif pa < pb:
2628 elif pa < pb:
2627 rel = b"<"
2629 rel = b"<"
2628 elif pa | pb:
2630 elif pa | pb:
2629 rel = b"|"
2631 rel = b"|"
2630 ui.write(_(b"a: %s\n") % pa)
2632 ui.write(_(b"a: %s\n") % pa)
2631 ui.write(_(b"b: %s\n") % pb)
2633 ui.write(_(b"b: %s\n") % pb)
2632 ui.write(_(b"depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2634 ui.write(_(b"depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2633 ui.write(
2635 ui.write(
2634 _(b"delta: %d hdist: %d distance: %d relation: %s\n")
2636 _(b"delta: %d hdist: %d distance: %d relation: %s\n")
2635 % (
2637 % (
2636 abs(pa._depth - pb._depth),
2638 abs(pa._depth - pb._depth),
2637 pvec._hamming(pa._vec, pb._vec),
2639 pvec._hamming(pa._vec, pb._vec),
2638 pa.distance(pb),
2640 pa.distance(pb),
2639 rel,
2641 rel,
2640 )
2642 )
2641 )
2643 )
2642
2644
2643
2645
2644 @command(
2646 @command(
2645 b'debugrebuilddirstate|debugrebuildstate',
2647 b'debugrebuilddirstate|debugrebuildstate',
2646 [
2648 [
2647 (b'r', b'rev', b'', _(b'revision to rebuild to'), _(b'REV')),
2649 (b'r', b'rev', b'', _(b'revision to rebuild to'), _(b'REV')),
2648 (
2650 (
2649 b'',
2651 b'',
2650 b'minimal',
2652 b'minimal',
2651 None,
2653 None,
2652 _(
2654 _(
2653 b'only rebuild files that are inconsistent with '
2655 b'only rebuild files that are inconsistent with '
2654 b'the working copy parent'
2656 b'the working copy parent'
2655 ),
2657 ),
2656 ),
2658 ),
2657 ],
2659 ],
2658 _(b'[-r REV]'),
2660 _(b'[-r REV]'),
2659 )
2661 )
2660 def debugrebuilddirstate(ui, repo, rev, **opts):
2662 def debugrebuilddirstate(ui, repo, rev, **opts):
2661 """rebuild the dirstate as it would look like for the given revision
2663 """rebuild the dirstate as it would look like for the given revision
2662
2664
2663 If no revision is specified the first current parent will be used.
2665 If no revision is specified the first current parent will be used.
2664
2666
2665 The dirstate will be set to the files of the given revision.
2667 The dirstate will be set to the files of the given revision.
2666 The actual working directory content or existing dirstate
2668 The actual working directory content or existing dirstate
2667 information such as adds or removes is not considered.
2669 information such as adds or removes is not considered.
2668
2670
2669 ``minimal`` will only rebuild the dirstate status for files that claim to be
2671 ``minimal`` will only rebuild the dirstate status for files that claim to be
2670 tracked but are not in the parent manifest, or that exist in the parent
2672 tracked but are not in the parent manifest, or that exist in the parent
2671 manifest but are not in the dirstate. It will not change adds, removes, or
2673 manifest but are not in the dirstate. It will not change adds, removes, or
2672 modified files that are in the working copy parent.
2674 modified files that are in the working copy parent.
2673
2675
2674 One use of this command is to make the next :hg:`status` invocation
2676 One use of this command is to make the next :hg:`status` invocation
2675 check the actual file content.
2677 check the actual file content.
2676 """
2678 """
2677 ctx = scmutil.revsingle(repo, rev)
2679 ctx = scmutil.revsingle(repo, rev)
2678 with repo.wlock():
2680 with repo.wlock():
2679 dirstate = repo.dirstate
2681 dirstate = repo.dirstate
2680 changedfiles = None
2682 changedfiles = None
2681 # See command doc for what minimal does.
2683 # See command doc for what minimal does.
2682 if opts.get('minimal'):
2684 if opts.get('minimal'):
2683 manifestfiles = set(ctx.manifest().keys())
2685 manifestfiles = set(ctx.manifest().keys())
2684 dirstatefiles = set(dirstate)
2686 dirstatefiles = set(dirstate)
2685 manifestonly = manifestfiles - dirstatefiles
2687 manifestonly = manifestfiles - dirstatefiles
2686 dsonly = dirstatefiles - manifestfiles
2688 dsonly = dirstatefiles - manifestfiles
2687 dsnotadded = {f for f in dsonly if dirstate[f] != b'a'}
2689 dsnotadded = {f for f in dsonly if dirstate[f] != b'a'}
2688 changedfiles = manifestonly | dsnotadded
2690 changedfiles = manifestonly | dsnotadded
2689
2691
2690 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
2692 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
2691
2693
2692
2694
2693 @command(b'debugrebuildfncache', [], b'')
2695 @command(b'debugrebuildfncache', [], b'')
2694 def debugrebuildfncache(ui, repo):
2696 def debugrebuildfncache(ui, repo):
2695 """rebuild the fncache file"""
2697 """rebuild the fncache file"""
2696 repair.rebuildfncache(ui, repo)
2698 repair.rebuildfncache(ui, repo)
2697
2699
2698
2700
2699 @command(
2701 @command(
2700 b'debugrename',
2702 b'debugrename',
2701 [(b'r', b'rev', b'', _(b'revision to debug'), _(b'REV'))],
2703 [(b'r', b'rev', b'', _(b'revision to debug'), _(b'REV'))],
2702 _(b'[-r REV] [FILE]...'),
2704 _(b'[-r REV] [FILE]...'),
2703 )
2705 )
2704 def debugrename(ui, repo, *pats, **opts):
2706 def debugrename(ui, repo, *pats, **opts):
2705 """dump rename information"""
2707 """dump rename information"""
2706
2708
2707 opts = pycompat.byteskwargs(opts)
2709 opts = pycompat.byteskwargs(opts)
2708 ctx = scmutil.revsingle(repo, opts.get(b'rev'))
2710 ctx = scmutil.revsingle(repo, opts.get(b'rev'))
2709 m = scmutil.match(ctx, pats, opts)
2711 m = scmutil.match(ctx, pats, opts)
2710 for abs in ctx.walk(m):
2712 for abs in ctx.walk(m):
2711 fctx = ctx[abs]
2713 fctx = ctx[abs]
2712 o = fctx.filelog().renamed(fctx.filenode())
2714 o = fctx.filelog().renamed(fctx.filenode())
2713 rel = repo.pathto(abs)
2715 rel = repo.pathto(abs)
2714 if o:
2716 if o:
2715 ui.write(_(b"%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2717 ui.write(_(b"%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2716 else:
2718 else:
2717 ui.write(_(b"%s not renamed\n") % rel)
2719 ui.write(_(b"%s not renamed\n") % rel)
2718
2720
2719
2721
2720 @command(b'debugrequires|debugrequirements', [], b'')
2722 @command(b'debugrequires|debugrequirements', [], b'')
2721 def debugrequirements(ui, repo):
2723 def debugrequirements(ui, repo):
2722 """ print the current repo requirements """
2724 """ print the current repo requirements """
2723 for r in sorted(repo.requirements):
2725 for r in sorted(repo.requirements):
2724 ui.write(b"%s\n" % r)
2726 ui.write(b"%s\n" % r)
2725
2727
2726
2728
2727 @command(
2729 @command(
2728 b'debugrevlog',
2730 b'debugrevlog',
2729 cmdutil.debugrevlogopts + [(b'd', b'dump', False, _(b'dump index data'))],
2731 cmdutil.debugrevlogopts + [(b'd', b'dump', False, _(b'dump index data'))],
2730 _(b'-c|-m|FILE'),
2732 _(b'-c|-m|FILE'),
2731 optionalrepo=True,
2733 optionalrepo=True,
2732 )
2734 )
2733 def debugrevlog(ui, repo, file_=None, **opts):
2735 def debugrevlog(ui, repo, file_=None, **opts):
2734 """show data and statistics about a revlog"""
2736 """show data and statistics about a revlog"""
2735 opts = pycompat.byteskwargs(opts)
2737 opts = pycompat.byteskwargs(opts)
2736 r = cmdutil.openrevlog(repo, b'debugrevlog', file_, opts)
2738 r = cmdutil.openrevlog(repo, b'debugrevlog', file_, opts)
2737
2739
2738 if opts.get(b"dump"):
2740 if opts.get(b"dump"):
2739 numrevs = len(r)
2741 numrevs = len(r)
2740 ui.write(
2742 ui.write(
2741 (
2743 (
2742 b"# rev p1rev p2rev start end deltastart base p1 p2"
2744 b"# rev p1rev p2rev start end deltastart base p1 p2"
2743 b" rawsize totalsize compression heads chainlen\n"
2745 b" rawsize totalsize compression heads chainlen\n"
2744 )
2746 )
2745 )
2747 )
2746 ts = 0
2748 ts = 0
2747 heads = set()
2749 heads = set()
2748
2750
2749 for rev in pycompat.xrange(numrevs):
2751 for rev in pycompat.xrange(numrevs):
2750 dbase = r.deltaparent(rev)
2752 dbase = r.deltaparent(rev)
2751 if dbase == -1:
2753 if dbase == -1:
2752 dbase = rev
2754 dbase = rev
2753 cbase = r.chainbase(rev)
2755 cbase = r.chainbase(rev)
2754 clen = r.chainlen(rev)
2756 clen = r.chainlen(rev)
2755 p1, p2 = r.parentrevs(rev)
2757 p1, p2 = r.parentrevs(rev)
2756 rs = r.rawsize(rev)
2758 rs = r.rawsize(rev)
2757 ts = ts + rs
2759 ts = ts + rs
2758 heads -= set(r.parentrevs(rev))
2760 heads -= set(r.parentrevs(rev))
2759 heads.add(rev)
2761 heads.add(rev)
2760 try:
2762 try:
2761 compression = ts / r.end(rev)
2763 compression = ts / r.end(rev)
2762 except ZeroDivisionError:
2764 except ZeroDivisionError:
2763 compression = 0
2765 compression = 0
2764 ui.write(
2766 ui.write(
2765 b"%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2767 b"%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2766 b"%11d %5d %8d\n"
2768 b"%11d %5d %8d\n"
2767 % (
2769 % (
2768 rev,
2770 rev,
2769 p1,
2771 p1,
2770 p2,
2772 p2,
2771 r.start(rev),
2773 r.start(rev),
2772 r.end(rev),
2774 r.end(rev),
2773 r.start(dbase),
2775 r.start(dbase),
2774 r.start(cbase),
2776 r.start(cbase),
2775 r.start(p1),
2777 r.start(p1),
2776 r.start(p2),
2778 r.start(p2),
2777 rs,
2779 rs,
2778 ts,
2780 ts,
2779 compression,
2781 compression,
2780 len(heads),
2782 len(heads),
2781 clen,
2783 clen,
2782 )
2784 )
2783 )
2785 )
2784 return 0
2786 return 0
2785
2787
2786 v = r.version
2788 v = r.version
2787 format = v & 0xFFFF
2789 format = v & 0xFFFF
2788 flags = []
2790 flags = []
2789 gdelta = False
2791 gdelta = False
2790 if v & revlog.FLAG_INLINE_DATA:
2792 if v & revlog.FLAG_INLINE_DATA:
2791 flags.append(b'inline')
2793 flags.append(b'inline')
2792 if v & revlog.FLAG_GENERALDELTA:
2794 if v & revlog.FLAG_GENERALDELTA:
2793 gdelta = True
2795 gdelta = True
2794 flags.append(b'generaldelta')
2796 flags.append(b'generaldelta')
2795 if not flags:
2797 if not flags:
2796 flags = [b'(none)']
2798 flags = [b'(none)']
2797
2799
2798 ### tracks merge vs single parent
2800 ### tracks merge vs single parent
2799 nummerges = 0
2801 nummerges = 0
2800
2802
2801 ### tracks ways the "delta" are build
2803 ### tracks ways the "delta" are build
2802 # nodelta
2804 # nodelta
2803 numempty = 0
2805 numempty = 0
2804 numemptytext = 0
2806 numemptytext = 0
2805 numemptydelta = 0
2807 numemptydelta = 0
2806 # full file content
2808 # full file content
2807 numfull = 0
2809 numfull = 0
2808 # intermediate snapshot against a prior snapshot
2810 # intermediate snapshot against a prior snapshot
2809 numsemi = 0
2811 numsemi = 0
2810 # snapshot count per depth
2812 # snapshot count per depth
2811 numsnapdepth = collections.defaultdict(lambda: 0)
2813 numsnapdepth = collections.defaultdict(lambda: 0)
2812 # delta against previous revision
2814 # delta against previous revision
2813 numprev = 0
2815 numprev = 0
2814 # delta against first or second parent (not prev)
2816 # delta against first or second parent (not prev)
2815 nump1 = 0
2817 nump1 = 0
2816 nump2 = 0
2818 nump2 = 0
2817 # delta against neither prev nor parents
2819 # delta against neither prev nor parents
2818 numother = 0
2820 numother = 0
2819 # delta against prev that are also first or second parent
2821 # delta against prev that are also first or second parent
2820 # (details of `numprev`)
2822 # (details of `numprev`)
2821 nump1prev = 0
2823 nump1prev = 0
2822 nump2prev = 0
2824 nump2prev = 0
2823
2825
2824 # data about delta chain of each revs
2826 # data about delta chain of each revs
2825 chainlengths = []
2827 chainlengths = []
2826 chainbases = []
2828 chainbases = []
2827 chainspans = []
2829 chainspans = []
2828
2830
2829 # data about each revision
2831 # data about each revision
2830 datasize = [None, 0, 0]
2832 datasize = [None, 0, 0]
2831 fullsize = [None, 0, 0]
2833 fullsize = [None, 0, 0]
2832 semisize = [None, 0, 0]
2834 semisize = [None, 0, 0]
2833 # snapshot count per depth
2835 # snapshot count per depth
2834 snapsizedepth = collections.defaultdict(lambda: [None, 0, 0])
2836 snapsizedepth = collections.defaultdict(lambda: [None, 0, 0])
2835 deltasize = [None, 0, 0]
2837 deltasize = [None, 0, 0]
2836 chunktypecounts = {}
2838 chunktypecounts = {}
2837 chunktypesizes = {}
2839 chunktypesizes = {}
2838
2840
2839 def addsize(size, l):
2841 def addsize(size, l):
2840 if l[0] is None or size < l[0]:
2842 if l[0] is None or size < l[0]:
2841 l[0] = size
2843 l[0] = size
2842 if size > l[1]:
2844 if size > l[1]:
2843 l[1] = size
2845 l[1] = size
2844 l[2] += size
2846 l[2] += size
2845
2847
2846 numrevs = len(r)
2848 numrevs = len(r)
2847 for rev in pycompat.xrange(numrevs):
2849 for rev in pycompat.xrange(numrevs):
2848 p1, p2 = r.parentrevs(rev)
2850 p1, p2 = r.parentrevs(rev)
2849 delta = r.deltaparent(rev)
2851 delta = r.deltaparent(rev)
2850 if format > 0:
2852 if format > 0:
2851 addsize(r.rawsize(rev), datasize)
2853 addsize(r.rawsize(rev), datasize)
2852 if p2 != nullrev:
2854 if p2 != nullrev:
2853 nummerges += 1
2855 nummerges += 1
2854 size = r.length(rev)
2856 size = r.length(rev)
2855 if delta == nullrev:
2857 if delta == nullrev:
2856 chainlengths.append(0)
2858 chainlengths.append(0)
2857 chainbases.append(r.start(rev))
2859 chainbases.append(r.start(rev))
2858 chainspans.append(size)
2860 chainspans.append(size)
2859 if size == 0:
2861 if size == 0:
2860 numempty += 1
2862 numempty += 1
2861 numemptytext += 1
2863 numemptytext += 1
2862 else:
2864 else:
2863 numfull += 1
2865 numfull += 1
2864 numsnapdepth[0] += 1
2866 numsnapdepth[0] += 1
2865 addsize(size, fullsize)
2867 addsize(size, fullsize)
2866 addsize(size, snapsizedepth[0])
2868 addsize(size, snapsizedepth[0])
2867 else:
2869 else:
2868 chainlengths.append(chainlengths[delta] + 1)
2870 chainlengths.append(chainlengths[delta] + 1)
2869 baseaddr = chainbases[delta]
2871 baseaddr = chainbases[delta]
2870 revaddr = r.start(rev)
2872 revaddr = r.start(rev)
2871 chainbases.append(baseaddr)
2873 chainbases.append(baseaddr)
2872 chainspans.append((revaddr - baseaddr) + size)
2874 chainspans.append((revaddr - baseaddr) + size)
2873 if size == 0:
2875 if size == 0:
2874 numempty += 1
2876 numempty += 1
2875 numemptydelta += 1
2877 numemptydelta += 1
2876 elif r.issnapshot(rev):
2878 elif r.issnapshot(rev):
2877 addsize(size, semisize)
2879 addsize(size, semisize)
2878 numsemi += 1
2880 numsemi += 1
2879 depth = r.snapshotdepth(rev)
2881 depth = r.snapshotdepth(rev)
2880 numsnapdepth[depth] += 1
2882 numsnapdepth[depth] += 1
2881 addsize(size, snapsizedepth[depth])
2883 addsize(size, snapsizedepth[depth])
2882 else:
2884 else:
2883 addsize(size, deltasize)
2885 addsize(size, deltasize)
2884 if delta == rev - 1:
2886 if delta == rev - 1:
2885 numprev += 1
2887 numprev += 1
2886 if delta == p1:
2888 if delta == p1:
2887 nump1prev += 1
2889 nump1prev += 1
2888 elif delta == p2:
2890 elif delta == p2:
2889 nump2prev += 1
2891 nump2prev += 1
2890 elif delta == p1:
2892 elif delta == p1:
2891 nump1 += 1
2893 nump1 += 1
2892 elif delta == p2:
2894 elif delta == p2:
2893 nump2 += 1
2895 nump2 += 1
2894 elif delta != nullrev:
2896 elif delta != nullrev:
2895 numother += 1
2897 numother += 1
2896
2898
2897 # Obtain data on the raw chunks in the revlog.
2899 # Obtain data on the raw chunks in the revlog.
2898 if util.safehasattr(r, b'_getsegmentforrevs'):
2900 if util.safehasattr(r, b'_getsegmentforrevs'):
2899 segment = r._getsegmentforrevs(rev, rev)[1]
2901 segment = r._getsegmentforrevs(rev, rev)[1]
2900 else:
2902 else:
2901 segment = r._revlog._getsegmentforrevs(rev, rev)[1]
2903 segment = r._revlog._getsegmentforrevs(rev, rev)[1]
2902 if segment:
2904 if segment:
2903 chunktype = bytes(segment[0:1])
2905 chunktype = bytes(segment[0:1])
2904 else:
2906 else:
2905 chunktype = b'empty'
2907 chunktype = b'empty'
2906
2908
2907 if chunktype not in chunktypecounts:
2909 if chunktype not in chunktypecounts:
2908 chunktypecounts[chunktype] = 0
2910 chunktypecounts[chunktype] = 0
2909 chunktypesizes[chunktype] = 0
2911 chunktypesizes[chunktype] = 0
2910
2912
2911 chunktypecounts[chunktype] += 1
2913 chunktypecounts[chunktype] += 1
2912 chunktypesizes[chunktype] += size
2914 chunktypesizes[chunktype] += size
2913
2915
2914 # Adjust size min value for empty cases
2916 # Adjust size min value for empty cases
2915 for size in (datasize, fullsize, semisize, deltasize):
2917 for size in (datasize, fullsize, semisize, deltasize):
2916 if size[0] is None:
2918 if size[0] is None:
2917 size[0] = 0
2919 size[0] = 0
2918
2920
2919 numdeltas = numrevs - numfull - numempty - numsemi
2921 numdeltas = numrevs - numfull - numempty - numsemi
2920 numoprev = numprev - nump1prev - nump2prev
2922 numoprev = numprev - nump1prev - nump2prev
2921 totalrawsize = datasize[2]
2923 totalrawsize = datasize[2]
2922 datasize[2] /= numrevs
2924 datasize[2] /= numrevs
2923 fulltotal = fullsize[2]
2925 fulltotal = fullsize[2]
2924 if numfull == 0:
2926 if numfull == 0:
2925 fullsize[2] = 0
2927 fullsize[2] = 0
2926 else:
2928 else:
2927 fullsize[2] /= numfull
2929 fullsize[2] /= numfull
2928 semitotal = semisize[2]
2930 semitotal = semisize[2]
2929 snaptotal = {}
2931 snaptotal = {}
2930 if numsemi > 0:
2932 if numsemi > 0:
2931 semisize[2] /= numsemi
2933 semisize[2] /= numsemi
2932 for depth in snapsizedepth:
2934 for depth in snapsizedepth:
2933 snaptotal[depth] = snapsizedepth[depth][2]
2935 snaptotal[depth] = snapsizedepth[depth][2]
2934 snapsizedepth[depth][2] /= numsnapdepth[depth]
2936 snapsizedepth[depth][2] /= numsnapdepth[depth]
2935
2937
2936 deltatotal = deltasize[2]
2938 deltatotal = deltasize[2]
2937 if numdeltas > 0:
2939 if numdeltas > 0:
2938 deltasize[2] /= numdeltas
2940 deltasize[2] /= numdeltas
2939 totalsize = fulltotal + semitotal + deltatotal
2941 totalsize = fulltotal + semitotal + deltatotal
2940 avgchainlen = sum(chainlengths) / numrevs
2942 avgchainlen = sum(chainlengths) / numrevs
2941 maxchainlen = max(chainlengths)
2943 maxchainlen = max(chainlengths)
2942 maxchainspan = max(chainspans)
2944 maxchainspan = max(chainspans)
2943 compratio = 1
2945 compratio = 1
2944 if totalsize:
2946 if totalsize:
2945 compratio = totalrawsize / totalsize
2947 compratio = totalrawsize / totalsize
2946
2948
2947 basedfmtstr = b'%%%dd\n'
2949 basedfmtstr = b'%%%dd\n'
2948 basepcfmtstr = b'%%%dd %s(%%5.2f%%%%)\n'
2950 basepcfmtstr = b'%%%dd %s(%%5.2f%%%%)\n'
2949
2951
2950 def dfmtstr(max):
2952 def dfmtstr(max):
2951 return basedfmtstr % len(str(max))
2953 return basedfmtstr % len(str(max))
2952
2954
2953 def pcfmtstr(max, padding=0):
2955 def pcfmtstr(max, padding=0):
2954 return basepcfmtstr % (len(str(max)), b' ' * padding)
2956 return basepcfmtstr % (len(str(max)), b' ' * padding)
2955
2957
2956 def pcfmt(value, total):
2958 def pcfmt(value, total):
2957 if total:
2959 if total:
2958 return (value, 100 * float(value) / total)
2960 return (value, 100 * float(value) / total)
2959 else:
2961 else:
2960 return value, 100.0
2962 return value, 100.0
2961
2963
2962 ui.writenoi18n(b'format : %d\n' % format)
2964 ui.writenoi18n(b'format : %d\n' % format)
2963 ui.writenoi18n(b'flags : %s\n' % b', '.join(flags))
2965 ui.writenoi18n(b'flags : %s\n' % b', '.join(flags))
2964
2966
2965 ui.write(b'\n')
2967 ui.write(b'\n')
2966 fmt = pcfmtstr(totalsize)
2968 fmt = pcfmtstr(totalsize)
2967 fmt2 = dfmtstr(totalsize)
2969 fmt2 = dfmtstr(totalsize)
2968 ui.writenoi18n(b'revisions : ' + fmt2 % numrevs)
2970 ui.writenoi18n(b'revisions : ' + fmt2 % numrevs)
2969 ui.writenoi18n(b' merges : ' + fmt % pcfmt(nummerges, numrevs))
2971 ui.writenoi18n(b' merges : ' + fmt % pcfmt(nummerges, numrevs))
2970 ui.writenoi18n(
2972 ui.writenoi18n(
2971 b' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs)
2973 b' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs)
2972 )
2974 )
2973 ui.writenoi18n(b'revisions : ' + fmt2 % numrevs)
2975 ui.writenoi18n(b'revisions : ' + fmt2 % numrevs)
2974 ui.writenoi18n(b' empty : ' + fmt % pcfmt(numempty, numrevs))
2976 ui.writenoi18n(b' empty : ' + fmt % pcfmt(numempty, numrevs))
2975 ui.writenoi18n(
2977 ui.writenoi18n(
2976 b' text : '
2978 b' text : '
2977 + fmt % pcfmt(numemptytext, numemptytext + numemptydelta)
2979 + fmt % pcfmt(numemptytext, numemptytext + numemptydelta)
2978 )
2980 )
2979 ui.writenoi18n(
2981 ui.writenoi18n(
2980 b' delta : '
2982 b' delta : '
2981 + fmt % pcfmt(numemptydelta, numemptytext + numemptydelta)
2983 + fmt % pcfmt(numemptydelta, numemptytext + numemptydelta)
2982 )
2984 )
2983 ui.writenoi18n(
2985 ui.writenoi18n(
2984 b' snapshot : ' + fmt % pcfmt(numfull + numsemi, numrevs)
2986 b' snapshot : ' + fmt % pcfmt(numfull + numsemi, numrevs)
2985 )
2987 )
2986 for depth in sorted(numsnapdepth):
2988 for depth in sorted(numsnapdepth):
2987 ui.write(
2989 ui.write(
2988 (b' lvl-%-3d : ' % depth)
2990 (b' lvl-%-3d : ' % depth)
2989 + fmt % pcfmt(numsnapdepth[depth], numrevs)
2991 + fmt % pcfmt(numsnapdepth[depth], numrevs)
2990 )
2992 )
2991 ui.writenoi18n(b' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2993 ui.writenoi18n(b' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2992 ui.writenoi18n(b'revision size : ' + fmt2 % totalsize)
2994 ui.writenoi18n(b'revision size : ' + fmt2 % totalsize)
2993 ui.writenoi18n(
2995 ui.writenoi18n(
2994 b' snapshot : ' + fmt % pcfmt(fulltotal + semitotal, totalsize)
2996 b' snapshot : ' + fmt % pcfmt(fulltotal + semitotal, totalsize)
2995 )
2997 )
2996 for depth in sorted(numsnapdepth):
2998 for depth in sorted(numsnapdepth):
2997 ui.write(
2999 ui.write(
2998 (b' lvl-%-3d : ' % depth)
3000 (b' lvl-%-3d : ' % depth)
2999 + fmt % pcfmt(snaptotal[depth], totalsize)
3001 + fmt % pcfmt(snaptotal[depth], totalsize)
3000 )
3002 )
3001 ui.writenoi18n(b' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
3003 ui.writenoi18n(b' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
3002
3004
3003 def fmtchunktype(chunktype):
3005 def fmtchunktype(chunktype):
3004 if chunktype == b'empty':
3006 if chunktype == b'empty':
3005 return b' %s : ' % chunktype
3007 return b' %s : ' % chunktype
3006 elif chunktype in pycompat.bytestr(string.ascii_letters):
3008 elif chunktype in pycompat.bytestr(string.ascii_letters):
3007 return b' 0x%s (%s) : ' % (hex(chunktype), chunktype)
3009 return b' 0x%s (%s) : ' % (hex(chunktype), chunktype)
3008 else:
3010 else:
3009 return b' 0x%s : ' % hex(chunktype)
3011 return b' 0x%s : ' % hex(chunktype)
3010
3012
3011 ui.write(b'\n')
3013 ui.write(b'\n')
3012 ui.writenoi18n(b'chunks : ' + fmt2 % numrevs)
3014 ui.writenoi18n(b'chunks : ' + fmt2 % numrevs)
3013 for chunktype in sorted(chunktypecounts):
3015 for chunktype in sorted(chunktypecounts):
3014 ui.write(fmtchunktype(chunktype))
3016 ui.write(fmtchunktype(chunktype))
3015 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
3017 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
3016 ui.writenoi18n(b'chunks size : ' + fmt2 % totalsize)
3018 ui.writenoi18n(b'chunks size : ' + fmt2 % totalsize)
3017 for chunktype in sorted(chunktypecounts):
3019 for chunktype in sorted(chunktypecounts):
3018 ui.write(fmtchunktype(chunktype))
3020 ui.write(fmtchunktype(chunktype))
3019 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
3021 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
3020
3022
3021 ui.write(b'\n')
3023 ui.write(b'\n')
3022 fmt = dfmtstr(max(avgchainlen, maxchainlen, maxchainspan, compratio))
3024 fmt = dfmtstr(max(avgchainlen, maxchainlen, maxchainspan, compratio))
3023 ui.writenoi18n(b'avg chain length : ' + fmt % avgchainlen)
3025 ui.writenoi18n(b'avg chain length : ' + fmt % avgchainlen)
3024 ui.writenoi18n(b'max chain length : ' + fmt % maxchainlen)
3026 ui.writenoi18n(b'max chain length : ' + fmt % maxchainlen)
3025 ui.writenoi18n(b'max chain reach : ' + fmt % maxchainspan)
3027 ui.writenoi18n(b'max chain reach : ' + fmt % maxchainspan)
3026 ui.writenoi18n(b'compression ratio : ' + fmt % compratio)
3028 ui.writenoi18n(b'compression ratio : ' + fmt % compratio)
3027
3029
3028 if format > 0:
3030 if format > 0:
3029 ui.write(b'\n')
3031 ui.write(b'\n')
3030 ui.writenoi18n(
3032 ui.writenoi18n(
3031 b'uncompressed data size (min/max/avg) : %d / %d / %d\n'
3033 b'uncompressed data size (min/max/avg) : %d / %d / %d\n'
3032 % tuple(datasize)
3034 % tuple(datasize)
3033 )
3035 )
3034 ui.writenoi18n(
3036 ui.writenoi18n(
3035 b'full revision size (min/max/avg) : %d / %d / %d\n'
3037 b'full revision size (min/max/avg) : %d / %d / %d\n'
3036 % tuple(fullsize)
3038 % tuple(fullsize)
3037 )
3039 )
3038 ui.writenoi18n(
3040 ui.writenoi18n(
3039 b'inter-snapshot size (min/max/avg) : %d / %d / %d\n'
3041 b'inter-snapshot size (min/max/avg) : %d / %d / %d\n'
3040 % tuple(semisize)
3042 % tuple(semisize)
3041 )
3043 )
3042 for depth in sorted(snapsizedepth):
3044 for depth in sorted(snapsizedepth):
3043 if depth == 0:
3045 if depth == 0:
3044 continue
3046 continue
3045 ui.writenoi18n(
3047 ui.writenoi18n(
3046 b' level-%-3d (min/max/avg) : %d / %d / %d\n'
3048 b' level-%-3d (min/max/avg) : %d / %d / %d\n'
3047 % ((depth,) + tuple(snapsizedepth[depth]))
3049 % ((depth,) + tuple(snapsizedepth[depth]))
3048 )
3050 )
3049 ui.writenoi18n(
3051 ui.writenoi18n(
3050 b'delta size (min/max/avg) : %d / %d / %d\n'
3052 b'delta size (min/max/avg) : %d / %d / %d\n'
3051 % tuple(deltasize)
3053 % tuple(deltasize)
3052 )
3054 )
3053
3055
3054 if numdeltas > 0:
3056 if numdeltas > 0:
3055 ui.write(b'\n')
3057 ui.write(b'\n')
3056 fmt = pcfmtstr(numdeltas)
3058 fmt = pcfmtstr(numdeltas)
3057 fmt2 = pcfmtstr(numdeltas, 4)
3059 fmt2 = pcfmtstr(numdeltas, 4)
3058 ui.writenoi18n(
3060 ui.writenoi18n(
3059 b'deltas against prev : ' + fmt % pcfmt(numprev, numdeltas)
3061 b'deltas against prev : ' + fmt % pcfmt(numprev, numdeltas)
3060 )
3062 )
3061 if numprev > 0:
3063 if numprev > 0:
3062 ui.writenoi18n(
3064 ui.writenoi18n(
3063 b' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev)
3065 b' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev)
3064 )
3066 )
3065 ui.writenoi18n(
3067 ui.writenoi18n(
3066 b' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev)
3068 b' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev)
3067 )
3069 )
3068 ui.writenoi18n(
3070 ui.writenoi18n(
3069 b' other : ' + fmt2 % pcfmt(numoprev, numprev)
3071 b' other : ' + fmt2 % pcfmt(numoprev, numprev)
3070 )
3072 )
3071 if gdelta:
3073 if gdelta:
3072 ui.writenoi18n(
3074 ui.writenoi18n(
3073 b'deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas)
3075 b'deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas)
3074 )
3076 )
3075 ui.writenoi18n(
3077 ui.writenoi18n(
3076 b'deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas)
3078 b'deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas)
3077 )
3079 )
3078 ui.writenoi18n(
3080 ui.writenoi18n(
3079 b'deltas against other : ' + fmt % pcfmt(numother, numdeltas)
3081 b'deltas against other : ' + fmt % pcfmt(numother, numdeltas)
3080 )
3082 )
3081
3083
3082
3084
3083 @command(
3085 @command(
3084 b'debugrevlogindex',
3086 b'debugrevlogindex',
3085 cmdutil.debugrevlogopts
3087 cmdutil.debugrevlogopts
3086 + [(b'f', b'format', 0, _(b'revlog format'), _(b'FORMAT'))],
3088 + [(b'f', b'format', 0, _(b'revlog format'), _(b'FORMAT'))],
3087 _(b'[-f FORMAT] -c|-m|FILE'),
3089 _(b'[-f FORMAT] -c|-m|FILE'),
3088 optionalrepo=True,
3090 optionalrepo=True,
3089 )
3091 )
3090 def debugrevlogindex(ui, repo, file_=None, **opts):
3092 def debugrevlogindex(ui, repo, file_=None, **opts):
3091 """dump the contents of a revlog index"""
3093 """dump the contents of a revlog index"""
3092 opts = pycompat.byteskwargs(opts)
3094 opts = pycompat.byteskwargs(opts)
3093 r = cmdutil.openrevlog(repo, b'debugrevlogindex', file_, opts)
3095 r = cmdutil.openrevlog(repo, b'debugrevlogindex', file_, opts)
3094 format = opts.get(b'format', 0)
3096 format = opts.get(b'format', 0)
3095 if format not in (0, 1):
3097 if format not in (0, 1):
3096 raise error.Abort(_(b"unknown format %d") % format)
3098 raise error.Abort(_(b"unknown format %d") % format)
3097
3099
3098 if ui.debugflag:
3100 if ui.debugflag:
3099 shortfn = hex
3101 shortfn = hex
3100 else:
3102 else:
3101 shortfn = short
3103 shortfn = short
3102
3104
3103 # There might not be anything in r, so have a sane default
3105 # There might not be anything in r, so have a sane default
3104 idlen = 12
3106 idlen = 12
3105 for i in r:
3107 for i in r:
3106 idlen = len(shortfn(r.node(i)))
3108 idlen = len(shortfn(r.node(i)))
3107 break
3109 break
3108
3110
3109 if format == 0:
3111 if format == 0:
3110 if ui.verbose:
3112 if ui.verbose:
3111 ui.writenoi18n(
3113 ui.writenoi18n(
3112 b" rev offset length linkrev %s %s p2\n"
3114 b" rev offset length linkrev %s %s p2\n"
3113 % (b"nodeid".ljust(idlen), b"p1".ljust(idlen))
3115 % (b"nodeid".ljust(idlen), b"p1".ljust(idlen))
3114 )
3116 )
3115 else:
3117 else:
3116 ui.writenoi18n(
3118 ui.writenoi18n(
3117 b" rev linkrev %s %s p2\n"
3119 b" rev linkrev %s %s p2\n"
3118 % (b"nodeid".ljust(idlen), b"p1".ljust(idlen))
3120 % (b"nodeid".ljust(idlen), b"p1".ljust(idlen))
3119 )
3121 )
3120 elif format == 1:
3122 elif format == 1:
3121 if ui.verbose:
3123 if ui.verbose:
3122 ui.writenoi18n(
3124 ui.writenoi18n(
3123 (
3125 (
3124 b" rev flag offset length size link p1"
3126 b" rev flag offset length size link p1"
3125 b" p2 %s\n"
3127 b" p2 %s\n"
3126 )
3128 )
3127 % b"nodeid".rjust(idlen)
3129 % b"nodeid".rjust(idlen)
3128 )
3130 )
3129 else:
3131 else:
3130 ui.writenoi18n(
3132 ui.writenoi18n(
3131 b" rev flag size link p1 p2 %s\n"
3133 b" rev flag size link p1 p2 %s\n"
3132 % b"nodeid".rjust(idlen)
3134 % b"nodeid".rjust(idlen)
3133 )
3135 )
3134
3136
3135 for i in r:
3137 for i in r:
3136 node = r.node(i)
3138 node = r.node(i)
3137 if format == 0:
3139 if format == 0:
3138 try:
3140 try:
3139 pp = r.parents(node)
3141 pp = r.parents(node)
3140 except Exception:
3142 except Exception:
3141 pp = [nullid, nullid]
3143 pp = [nullid, nullid]
3142 if ui.verbose:
3144 if ui.verbose:
3143 ui.write(
3145 ui.write(
3144 b"% 6d % 9d % 7d % 7d %s %s %s\n"
3146 b"% 6d % 9d % 7d % 7d %s %s %s\n"
3145 % (
3147 % (
3146 i,
3148 i,
3147 r.start(i),
3149 r.start(i),
3148 r.length(i),
3150 r.length(i),
3149 r.linkrev(i),
3151 r.linkrev(i),
3150 shortfn(node),
3152 shortfn(node),
3151 shortfn(pp[0]),
3153 shortfn(pp[0]),
3152 shortfn(pp[1]),
3154 shortfn(pp[1]),
3153 )
3155 )
3154 )
3156 )
3155 else:
3157 else:
3156 ui.write(
3158 ui.write(
3157 b"% 6d % 7d %s %s %s\n"
3159 b"% 6d % 7d %s %s %s\n"
3158 % (
3160 % (
3159 i,
3161 i,
3160 r.linkrev(i),
3162 r.linkrev(i),
3161 shortfn(node),
3163 shortfn(node),
3162 shortfn(pp[0]),
3164 shortfn(pp[0]),
3163 shortfn(pp[1]),
3165 shortfn(pp[1]),
3164 )
3166 )
3165 )
3167 )
3166 elif format == 1:
3168 elif format == 1:
3167 pr = r.parentrevs(i)
3169 pr = r.parentrevs(i)
3168 if ui.verbose:
3170 if ui.verbose:
3169 ui.write(
3171 ui.write(
3170 b"% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d %s\n"
3172 b"% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d %s\n"
3171 % (
3173 % (
3172 i,
3174 i,
3173 r.flags(i),
3175 r.flags(i),
3174 r.start(i),
3176 r.start(i),
3175 r.length(i),
3177 r.length(i),
3176 r.rawsize(i),
3178 r.rawsize(i),
3177 r.linkrev(i),
3179 r.linkrev(i),
3178 pr[0],
3180 pr[0],
3179 pr[1],
3181 pr[1],
3180 shortfn(node),
3182 shortfn(node),
3181 )
3183 )
3182 )
3184 )
3183 else:
3185 else:
3184 ui.write(
3186 ui.write(
3185 b"% 6d %04x % 8d % 6d % 6d % 6d %s\n"
3187 b"% 6d %04x % 8d % 6d % 6d % 6d %s\n"
3186 % (
3188 % (
3187 i,
3189 i,
3188 r.flags(i),
3190 r.flags(i),
3189 r.rawsize(i),
3191 r.rawsize(i),
3190 r.linkrev(i),
3192 r.linkrev(i),
3191 pr[0],
3193 pr[0],
3192 pr[1],
3194 pr[1],
3193 shortfn(node),
3195 shortfn(node),
3194 )
3196 )
3195 )
3197 )
3196
3198
3197
3199
3198 @command(
3200 @command(
3199 b'debugrevspec',
3201 b'debugrevspec',
3200 [
3202 [
3201 (
3203 (
3202 b'',
3204 b'',
3203 b'optimize',
3205 b'optimize',
3204 None,
3206 None,
3205 _(b'print parsed tree after optimizing (DEPRECATED)'),
3207 _(b'print parsed tree after optimizing (DEPRECATED)'),
3206 ),
3208 ),
3207 (
3209 (
3208 b'',
3210 b'',
3209 b'show-revs',
3211 b'show-revs',
3210 True,
3212 True,
3211 _(b'print list of result revisions (default)'),
3213 _(b'print list of result revisions (default)'),
3212 ),
3214 ),
3213 (
3215 (
3214 b's',
3216 b's',
3215 b'show-set',
3217 b'show-set',
3216 None,
3218 None,
3217 _(b'print internal representation of result set'),
3219 _(b'print internal representation of result set'),
3218 ),
3220 ),
3219 (
3221 (
3220 b'p',
3222 b'p',
3221 b'show-stage',
3223 b'show-stage',
3222 [],
3224 [],
3223 _(b'print parsed tree at the given stage'),
3225 _(b'print parsed tree at the given stage'),
3224 _(b'NAME'),
3226 _(b'NAME'),
3225 ),
3227 ),
3226 (b'', b'no-optimized', False, _(b'evaluate tree without optimization')),
3228 (b'', b'no-optimized', False, _(b'evaluate tree without optimization')),
3227 (b'', b'verify-optimized', False, _(b'verify optimized result')),
3229 (b'', b'verify-optimized', False, _(b'verify optimized result')),
3228 ],
3230 ],
3229 b'REVSPEC',
3231 b'REVSPEC',
3230 )
3232 )
3231 def debugrevspec(ui, repo, expr, **opts):
3233 def debugrevspec(ui, repo, expr, **opts):
3232 """parse and apply a revision specification
3234 """parse and apply a revision specification
3233
3235
3234 Use -p/--show-stage option to print the parsed tree at the given stages.
3236 Use -p/--show-stage option to print the parsed tree at the given stages.
3235 Use -p all to print tree at every stage.
3237 Use -p all to print tree at every stage.
3236
3238
3237 Use --no-show-revs option with -s or -p to print only the set
3239 Use --no-show-revs option with -s or -p to print only the set
3238 representation or the parsed tree respectively.
3240 representation or the parsed tree respectively.
3239
3241
3240 Use --verify-optimized to compare the optimized result with the unoptimized
3242 Use --verify-optimized to compare the optimized result with the unoptimized
3241 one. Returns 1 if the optimized result differs.
3243 one. Returns 1 if the optimized result differs.
3242 """
3244 """
3243 opts = pycompat.byteskwargs(opts)
3245 opts = pycompat.byteskwargs(opts)
3244 aliases = ui.configitems(b'revsetalias')
3246 aliases = ui.configitems(b'revsetalias')
3245 stages = [
3247 stages = [
3246 (b'parsed', lambda tree: tree),
3248 (b'parsed', lambda tree: tree),
3247 (
3249 (
3248 b'expanded',
3250 b'expanded',
3249 lambda tree: revsetlang.expandaliases(tree, aliases, ui.warn),
3251 lambda tree: revsetlang.expandaliases(tree, aliases, ui.warn),
3250 ),
3252 ),
3251 (b'concatenated', revsetlang.foldconcat),
3253 (b'concatenated', revsetlang.foldconcat),
3252 (b'analyzed', revsetlang.analyze),
3254 (b'analyzed', revsetlang.analyze),
3253 (b'optimized', revsetlang.optimize),
3255 (b'optimized', revsetlang.optimize),
3254 ]
3256 ]
3255 if opts[b'no_optimized']:
3257 if opts[b'no_optimized']:
3256 stages = stages[:-1]
3258 stages = stages[:-1]
3257 if opts[b'verify_optimized'] and opts[b'no_optimized']:
3259 if opts[b'verify_optimized'] and opts[b'no_optimized']:
3258 raise error.Abort(
3260 raise error.Abort(
3259 _(b'cannot use --verify-optimized with --no-optimized')
3261 _(b'cannot use --verify-optimized with --no-optimized')
3260 )
3262 )
3261 stagenames = {n for n, f in stages}
3263 stagenames = {n for n, f in stages}
3262
3264
3263 showalways = set()
3265 showalways = set()
3264 showchanged = set()
3266 showchanged = set()
3265 if ui.verbose and not opts[b'show_stage']:
3267 if ui.verbose and not opts[b'show_stage']:
3266 # show parsed tree by --verbose (deprecated)
3268 # show parsed tree by --verbose (deprecated)
3267 showalways.add(b'parsed')
3269 showalways.add(b'parsed')
3268 showchanged.update([b'expanded', b'concatenated'])
3270 showchanged.update([b'expanded', b'concatenated'])
3269 if opts[b'optimize']:
3271 if opts[b'optimize']:
3270 showalways.add(b'optimized')
3272 showalways.add(b'optimized')
3271 if opts[b'show_stage'] and opts[b'optimize']:
3273 if opts[b'show_stage'] and opts[b'optimize']:
3272 raise error.Abort(_(b'cannot use --optimize with --show-stage'))
3274 raise error.Abort(_(b'cannot use --optimize with --show-stage'))
3273 if opts[b'show_stage'] == [b'all']:
3275 if opts[b'show_stage'] == [b'all']:
3274 showalways.update(stagenames)
3276 showalways.update(stagenames)
3275 else:
3277 else:
3276 for n in opts[b'show_stage']:
3278 for n in opts[b'show_stage']:
3277 if n not in stagenames:
3279 if n not in stagenames:
3278 raise error.Abort(_(b'invalid stage name: %s') % n)
3280 raise error.Abort(_(b'invalid stage name: %s') % n)
3279 showalways.update(opts[b'show_stage'])
3281 showalways.update(opts[b'show_stage'])
3280
3282
3281 treebystage = {}
3283 treebystage = {}
3282 printedtree = None
3284 printedtree = None
3283 tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo))
3285 tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo))
3284 for n, f in stages:
3286 for n, f in stages:
3285 treebystage[n] = tree = f(tree)
3287 treebystage[n] = tree = f(tree)
3286 if n in showalways or (n in showchanged and tree != printedtree):
3288 if n in showalways or (n in showchanged and tree != printedtree):
3287 if opts[b'show_stage'] or n != b'parsed':
3289 if opts[b'show_stage'] or n != b'parsed':
3288 ui.write(b"* %s:\n" % n)
3290 ui.write(b"* %s:\n" % n)
3289 ui.write(revsetlang.prettyformat(tree), b"\n")
3291 ui.write(revsetlang.prettyformat(tree), b"\n")
3290 printedtree = tree
3292 printedtree = tree
3291
3293
3292 if opts[b'verify_optimized']:
3294 if opts[b'verify_optimized']:
3293 arevs = revset.makematcher(treebystage[b'analyzed'])(repo)
3295 arevs = revset.makematcher(treebystage[b'analyzed'])(repo)
3294 brevs = revset.makematcher(treebystage[b'optimized'])(repo)
3296 brevs = revset.makematcher(treebystage[b'optimized'])(repo)
3295 if opts[b'show_set'] or (opts[b'show_set'] is None and ui.verbose):
3297 if opts[b'show_set'] or (opts[b'show_set'] is None and ui.verbose):
3296 ui.writenoi18n(
3298 ui.writenoi18n(
3297 b"* analyzed set:\n", stringutil.prettyrepr(arevs), b"\n"
3299 b"* analyzed set:\n", stringutil.prettyrepr(arevs), b"\n"
3298 )
3300 )
3299 ui.writenoi18n(
3301 ui.writenoi18n(
3300 b"* optimized set:\n", stringutil.prettyrepr(brevs), b"\n"
3302 b"* optimized set:\n", stringutil.prettyrepr(brevs), b"\n"
3301 )
3303 )
3302 arevs = list(arevs)
3304 arevs = list(arevs)
3303 brevs = list(brevs)
3305 brevs = list(brevs)
3304 if arevs == brevs:
3306 if arevs == brevs:
3305 return 0
3307 return 0
3306 ui.writenoi18n(b'--- analyzed\n', label=b'diff.file_a')
3308 ui.writenoi18n(b'--- analyzed\n', label=b'diff.file_a')
3307 ui.writenoi18n(b'+++ optimized\n', label=b'diff.file_b')
3309 ui.writenoi18n(b'+++ optimized\n', label=b'diff.file_b')
3308 sm = difflib.SequenceMatcher(None, arevs, brevs)
3310 sm = difflib.SequenceMatcher(None, arevs, brevs)
3309 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3311 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3310 if tag in ('delete', 'replace'):
3312 if tag in ('delete', 'replace'):
3311 for c in arevs[alo:ahi]:
3313 for c in arevs[alo:ahi]:
3312 ui.write(b'-%d\n' % c, label=b'diff.deleted')
3314 ui.write(b'-%d\n' % c, label=b'diff.deleted')
3313 if tag in ('insert', 'replace'):
3315 if tag in ('insert', 'replace'):
3314 for c in brevs[blo:bhi]:
3316 for c in brevs[blo:bhi]:
3315 ui.write(b'+%d\n' % c, label=b'diff.inserted')
3317 ui.write(b'+%d\n' % c, label=b'diff.inserted')
3316 if tag == 'equal':
3318 if tag == 'equal':
3317 for c in arevs[alo:ahi]:
3319 for c in arevs[alo:ahi]:
3318 ui.write(b' %d\n' % c)
3320 ui.write(b' %d\n' % c)
3319 return 1
3321 return 1
3320
3322
3321 func = revset.makematcher(tree)
3323 func = revset.makematcher(tree)
3322 revs = func(repo)
3324 revs = func(repo)
3323 if opts[b'show_set'] or (opts[b'show_set'] is None and ui.verbose):
3325 if opts[b'show_set'] or (opts[b'show_set'] is None and ui.verbose):
3324 ui.writenoi18n(b"* set:\n", stringutil.prettyrepr(revs), b"\n")
3326 ui.writenoi18n(b"* set:\n", stringutil.prettyrepr(revs), b"\n")
3325 if not opts[b'show_revs']:
3327 if not opts[b'show_revs']:
3326 return
3328 return
3327 for c in revs:
3329 for c in revs:
3328 ui.write(b"%d\n" % c)
3330 ui.write(b"%d\n" % c)
3329
3331
3330
3332
3331 @command(
3333 @command(
3332 b'debugserve',
3334 b'debugserve',
3333 [
3335 [
3334 (
3336 (
3335 b'',
3337 b'',
3336 b'sshstdio',
3338 b'sshstdio',
3337 False,
3339 False,
3338 _(b'run an SSH server bound to process handles'),
3340 _(b'run an SSH server bound to process handles'),
3339 ),
3341 ),
3340 (b'', b'logiofd', b'', _(b'file descriptor to log server I/O to')),
3342 (b'', b'logiofd', b'', _(b'file descriptor to log server I/O to')),
3341 (b'', b'logiofile', b'', _(b'file to log server I/O to')),
3343 (b'', b'logiofile', b'', _(b'file to log server I/O to')),
3342 ],
3344 ],
3343 b'',
3345 b'',
3344 )
3346 )
3345 def debugserve(ui, repo, **opts):
3347 def debugserve(ui, repo, **opts):
3346 """run a server with advanced settings
3348 """run a server with advanced settings
3347
3349
3348 This command is similar to :hg:`serve`. It exists partially as a
3350 This command is similar to :hg:`serve`. It exists partially as a
3349 workaround to the fact that ``hg serve --stdio`` must have specific
3351 workaround to the fact that ``hg serve --stdio`` must have specific
3350 arguments for security reasons.
3352 arguments for security reasons.
3351 """
3353 """
3352 opts = pycompat.byteskwargs(opts)
3354 opts = pycompat.byteskwargs(opts)
3353
3355
3354 if not opts[b'sshstdio']:
3356 if not opts[b'sshstdio']:
3355 raise error.Abort(_(b'only --sshstdio is currently supported'))
3357 raise error.Abort(_(b'only --sshstdio is currently supported'))
3356
3358
3357 logfh = None
3359 logfh = None
3358
3360
3359 if opts[b'logiofd'] and opts[b'logiofile']:
3361 if opts[b'logiofd'] and opts[b'logiofile']:
3360 raise error.Abort(_(b'cannot use both --logiofd and --logiofile'))
3362 raise error.Abort(_(b'cannot use both --logiofd and --logiofile'))
3361
3363
3362 if opts[b'logiofd']:
3364 if opts[b'logiofd']:
3363 # Ideally we would be line buffered. But line buffering in binary
3365 # Ideally we would be line buffered. But line buffering in binary
3364 # mode isn't supported and emits a warning in Python 3.8+. Disabling
3366 # mode isn't supported and emits a warning in Python 3.8+. Disabling
3365 # buffering could have performance impacts. But since this isn't
3367 # buffering could have performance impacts. But since this isn't
3366 # performance critical code, it should be fine.
3368 # performance critical code, it should be fine.
3367 try:
3369 try:
3368 logfh = os.fdopen(int(opts[b'logiofd']), 'ab', 0)
3370 logfh = os.fdopen(int(opts[b'logiofd']), 'ab', 0)
3369 except OSError as e:
3371 except OSError as e:
3370 if e.errno != errno.ESPIPE:
3372 if e.errno != errno.ESPIPE:
3371 raise
3373 raise
3372 # can't seek a pipe, so `ab` mode fails on py3
3374 # can't seek a pipe, so `ab` mode fails on py3
3373 logfh = os.fdopen(int(opts[b'logiofd']), 'wb', 0)
3375 logfh = os.fdopen(int(opts[b'logiofd']), 'wb', 0)
3374 elif opts[b'logiofile']:
3376 elif opts[b'logiofile']:
3375 logfh = open(opts[b'logiofile'], b'ab', 0)
3377 logfh = open(opts[b'logiofile'], b'ab', 0)
3376
3378
3377 s = wireprotoserver.sshserver(ui, repo, logfh=logfh)
3379 s = wireprotoserver.sshserver(ui, repo, logfh=logfh)
3378 s.serve_forever()
3380 s.serve_forever()
3379
3381
3380
3382
3381 @command(b'debugsetparents', [], _(b'REV1 [REV2]'))
3383 @command(b'debugsetparents', [], _(b'REV1 [REV2]'))
3382 def debugsetparents(ui, repo, rev1, rev2=None):
3384 def debugsetparents(ui, repo, rev1, rev2=None):
3383 """manually set the parents of the current working directory
3385 """manually set the parents of the current working directory
3384
3386
3385 This is useful for writing repository conversion tools, but should
3387 This is useful for writing repository conversion tools, but should
3386 be used with care. For example, neither the working directory nor the
3388 be used with care. For example, neither the working directory nor the
3387 dirstate is updated, so file status may be incorrect after running this
3389 dirstate is updated, so file status may be incorrect after running this
3388 command.
3390 command.
3389
3391
3390 Returns 0 on success.
3392 Returns 0 on success.
3391 """
3393 """
3392
3394
3393 node1 = scmutil.revsingle(repo, rev1).node()
3395 node1 = scmutil.revsingle(repo, rev1).node()
3394 node2 = scmutil.revsingle(repo, rev2, b'null').node()
3396 node2 = scmutil.revsingle(repo, rev2, b'null').node()
3395
3397
3396 with repo.wlock():
3398 with repo.wlock():
3397 repo.setparents(node1, node2)
3399 repo.setparents(node1, node2)
3398
3400
3399
3401
3400 @command(b'debugsidedata', cmdutil.debugrevlogopts, _(b'-c|-m|FILE REV'))
3402 @command(b'debugsidedata', cmdutil.debugrevlogopts, _(b'-c|-m|FILE REV'))
3401 def debugsidedata(ui, repo, file_, rev=None, **opts):
3403 def debugsidedata(ui, repo, file_, rev=None, **opts):
3402 """dump the side data for a cl/manifest/file revision
3404 """dump the side data for a cl/manifest/file revision
3403
3405
3404 Use --verbose to dump the sidedata content."""
3406 Use --verbose to dump the sidedata content."""
3405 opts = pycompat.byteskwargs(opts)
3407 opts = pycompat.byteskwargs(opts)
3406 if opts.get(b'changelog') or opts.get(b'manifest') or opts.get(b'dir'):
3408 if opts.get(b'changelog') or opts.get(b'manifest') or opts.get(b'dir'):
3407 if rev is not None:
3409 if rev is not None:
3408 raise error.CommandError(b'debugdata', _(b'invalid arguments'))
3410 raise error.CommandError(b'debugdata', _(b'invalid arguments'))
3409 file_, rev = None, file_
3411 file_, rev = None, file_
3410 elif rev is None:
3412 elif rev is None:
3411 raise error.CommandError(b'debugdata', _(b'invalid arguments'))
3413 raise error.CommandError(b'debugdata', _(b'invalid arguments'))
3412 r = cmdutil.openstorage(repo, b'debugdata', file_, opts)
3414 r = cmdutil.openstorage(repo, b'debugdata', file_, opts)
3413 r = getattr(r, '_revlog', r)
3415 r = getattr(r, '_revlog', r)
3414 try:
3416 try:
3415 sidedata = r.sidedata(r.lookup(rev))
3417 sidedata = r.sidedata(r.lookup(rev))
3416 except KeyError:
3418 except KeyError:
3417 raise error.Abort(_(b'invalid revision identifier %s') % rev)
3419 raise error.Abort(_(b'invalid revision identifier %s') % rev)
3418 if sidedata:
3420 if sidedata:
3419 sidedata = list(sidedata.items())
3421 sidedata = list(sidedata.items())
3420 sidedata.sort()
3422 sidedata.sort()
3421 ui.writenoi18n(b'%d sidedata entries\n' % len(sidedata))
3423 ui.writenoi18n(b'%d sidedata entries\n' % len(sidedata))
3422 for key, value in sidedata:
3424 for key, value in sidedata:
3423 ui.writenoi18n(b' entry-%04o size %d\n' % (key, len(value)))
3425 ui.writenoi18n(b' entry-%04o size %d\n' % (key, len(value)))
3424 if ui.verbose:
3426 if ui.verbose:
3425 ui.writenoi18n(b' %s\n' % stringutil.pprint(value))
3427 ui.writenoi18n(b' %s\n' % stringutil.pprint(value))
3426
3428
3427
3429
3428 @command(b'debugssl', [], b'[SOURCE]', optionalrepo=True)
3430 @command(b'debugssl', [], b'[SOURCE]', optionalrepo=True)
3429 def debugssl(ui, repo, source=None, **opts):
3431 def debugssl(ui, repo, source=None, **opts):
3430 '''test a secure connection to a server
3432 '''test a secure connection to a server
3431
3433
3432 This builds the certificate chain for the server on Windows, installing the
3434 This builds the certificate chain for the server on Windows, installing the
3433 missing intermediates and trusted root via Windows Update if necessary. It
3435 missing intermediates and trusted root via Windows Update if necessary. It
3434 does nothing on other platforms.
3436 does nothing on other platforms.
3435
3437
3436 If SOURCE is omitted, the 'default' path will be used. If a URL is given,
3438 If SOURCE is omitted, the 'default' path will be used. If a URL is given,
3437 that server is used. See :hg:`help urls` for more information.
3439 that server is used. See :hg:`help urls` for more information.
3438
3440
3439 If the update succeeds, retry the original operation. Otherwise, the cause
3441 If the update succeeds, retry the original operation. Otherwise, the cause
3440 of the SSL error is likely another issue.
3442 of the SSL error is likely another issue.
3441 '''
3443 '''
3442 if not pycompat.iswindows:
3444 if not pycompat.iswindows:
3443 raise error.Abort(
3445 raise error.Abort(
3444 _(b'certificate chain building is only possible on Windows')
3446 _(b'certificate chain building is only possible on Windows')
3445 )
3447 )
3446
3448
3447 if not source:
3449 if not source:
3448 if not repo:
3450 if not repo:
3449 raise error.Abort(
3451 raise error.Abort(
3450 _(
3452 _(
3451 b"there is no Mercurial repository here, and no "
3453 b"there is no Mercurial repository here, and no "
3452 b"server specified"
3454 b"server specified"
3453 )
3455 )
3454 )
3456 )
3455 source = b"default"
3457 source = b"default"
3456
3458
3457 source, branches = hg.parseurl(ui.expandpath(source))
3459 source, branches = hg.parseurl(ui.expandpath(source))
3458 url = util.url(source)
3460 url = util.url(source)
3459
3461
3460 defaultport = {b'https': 443, b'ssh': 22}
3462 defaultport = {b'https': 443, b'ssh': 22}
3461 if url.scheme in defaultport:
3463 if url.scheme in defaultport:
3462 try:
3464 try:
3463 addr = (url.host, int(url.port or defaultport[url.scheme]))
3465 addr = (url.host, int(url.port or defaultport[url.scheme]))
3464 except ValueError:
3466 except ValueError:
3465 raise error.Abort(_(b"malformed port number in URL"))
3467 raise error.Abort(_(b"malformed port number in URL"))
3466 else:
3468 else:
3467 raise error.Abort(_(b"only https and ssh connections are supported"))
3469 raise error.Abort(_(b"only https and ssh connections are supported"))
3468
3470
3469 from . import win32
3471 from . import win32
3470
3472
3471 s = ssl.wrap_socket(
3473 s = ssl.wrap_socket(
3472 socket.socket(),
3474 socket.socket(),
3473 ssl_version=ssl.PROTOCOL_TLS,
3475 ssl_version=ssl.PROTOCOL_TLS,
3474 cert_reqs=ssl.CERT_NONE,
3476 cert_reqs=ssl.CERT_NONE,
3475 ca_certs=None,
3477 ca_certs=None,
3476 )
3478 )
3477
3479
3478 try:
3480 try:
3479 s.connect(addr)
3481 s.connect(addr)
3480 cert = s.getpeercert(True)
3482 cert = s.getpeercert(True)
3481
3483
3482 ui.status(_(b'checking the certificate chain for %s\n') % url.host)
3484 ui.status(_(b'checking the certificate chain for %s\n') % url.host)
3483
3485
3484 complete = win32.checkcertificatechain(cert, build=False)
3486 complete = win32.checkcertificatechain(cert, build=False)
3485
3487
3486 if not complete:
3488 if not complete:
3487 ui.status(_(b'certificate chain is incomplete, updating... '))
3489 ui.status(_(b'certificate chain is incomplete, updating... '))
3488
3490
3489 if not win32.checkcertificatechain(cert):
3491 if not win32.checkcertificatechain(cert):
3490 ui.status(_(b'failed.\n'))
3492 ui.status(_(b'failed.\n'))
3491 else:
3493 else:
3492 ui.status(_(b'done.\n'))
3494 ui.status(_(b'done.\n'))
3493 else:
3495 else:
3494 ui.status(_(b'full certificate chain is available\n'))
3496 ui.status(_(b'full certificate chain is available\n'))
3495 finally:
3497 finally:
3496 s.close()
3498 s.close()
3497
3499
3498
3500
3499 @command(
3501 @command(
3500 b"debugbackupbundle",
3502 b"debugbackupbundle",
3501 [
3503 [
3502 (
3504 (
3503 b"",
3505 b"",
3504 b"recover",
3506 b"recover",
3505 b"",
3507 b"",
3506 b"brings the specified changeset back into the repository",
3508 b"brings the specified changeset back into the repository",
3507 )
3509 )
3508 ]
3510 ]
3509 + cmdutil.logopts,
3511 + cmdutil.logopts,
3510 _(b"hg debugbackupbundle [--recover HASH]"),
3512 _(b"hg debugbackupbundle [--recover HASH]"),
3511 )
3513 )
3512 def debugbackupbundle(ui, repo, *pats, **opts):
3514 def debugbackupbundle(ui, repo, *pats, **opts):
3513 """lists the changesets available in backup bundles
3515 """lists the changesets available in backup bundles
3514
3516
3515 Without any arguments, this command prints a list of the changesets in each
3517 Without any arguments, this command prints a list of the changesets in each
3516 backup bundle.
3518 backup bundle.
3517
3519
3518 --recover takes a changeset hash and unbundles the first bundle that
3520 --recover takes a changeset hash and unbundles the first bundle that
3519 contains that hash, which puts that changeset back in your repository.
3521 contains that hash, which puts that changeset back in your repository.
3520
3522
3521 --verbose will print the entire commit message and the bundle path for that
3523 --verbose will print the entire commit message and the bundle path for that
3522 backup.
3524 backup.
3523 """
3525 """
3524 backups = list(
3526 backups = list(
3525 filter(
3527 filter(
3526 os.path.isfile, glob.glob(repo.vfs.join(b"strip-backup") + b"/*.hg")
3528 os.path.isfile, glob.glob(repo.vfs.join(b"strip-backup") + b"/*.hg")
3527 )
3529 )
3528 )
3530 )
3529 backups.sort(key=lambda x: os.path.getmtime(x), reverse=True)
3531 backups.sort(key=lambda x: os.path.getmtime(x), reverse=True)
3530
3532
3531 opts = pycompat.byteskwargs(opts)
3533 opts = pycompat.byteskwargs(opts)
3532 opts[b"bundle"] = b""
3534 opts[b"bundle"] = b""
3533 opts[b"force"] = None
3535 opts[b"force"] = None
3534 limit = logcmdutil.getlimit(opts)
3536 limit = logcmdutil.getlimit(opts)
3535
3537
3536 def display(other, chlist, displayer):
3538 def display(other, chlist, displayer):
3537 if opts.get(b"newest_first"):
3539 if opts.get(b"newest_first"):
3538 chlist.reverse()
3540 chlist.reverse()
3539 count = 0
3541 count = 0
3540 for n in chlist:
3542 for n in chlist:
3541 if limit is not None and count >= limit:
3543 if limit is not None and count >= limit:
3542 break
3544 break
3543 parents = [True for p in other.changelog.parents(n) if p != nullid]
3545 parents = [True for p in other.changelog.parents(n) if p != nullid]
3544 if opts.get(b"no_merges") and len(parents) == 2:
3546 if opts.get(b"no_merges") and len(parents) == 2:
3545 continue
3547 continue
3546 count += 1
3548 count += 1
3547 displayer.show(other[n])
3549 displayer.show(other[n])
3548
3550
3549 recovernode = opts.get(b"recover")
3551 recovernode = opts.get(b"recover")
3550 if recovernode:
3552 if recovernode:
3551 if scmutil.isrevsymbol(repo, recovernode):
3553 if scmutil.isrevsymbol(repo, recovernode):
3552 ui.warn(_(b"%s already exists in the repo\n") % recovernode)
3554 ui.warn(_(b"%s already exists in the repo\n") % recovernode)
3553 return
3555 return
3554 elif backups:
3556 elif backups:
3555 msg = _(
3557 msg = _(
3556 b"Recover changesets using: hg debugbackupbundle --recover "
3558 b"Recover changesets using: hg debugbackupbundle --recover "
3557 b"<changeset hash>\n\nAvailable backup changesets:"
3559 b"<changeset hash>\n\nAvailable backup changesets:"
3558 )
3560 )
3559 ui.status(msg, label=b"status.removed")
3561 ui.status(msg, label=b"status.removed")
3560 else:
3562 else:
3561 ui.status(_(b"no backup changesets found\n"))
3563 ui.status(_(b"no backup changesets found\n"))
3562 return
3564 return
3563
3565
3564 for backup in backups:
3566 for backup in backups:
3565 # Much of this is copied from the hg incoming logic
3567 # Much of this is copied from the hg incoming logic
3566 source = ui.expandpath(os.path.relpath(backup, encoding.getcwd()))
3568 source = ui.expandpath(os.path.relpath(backup, encoding.getcwd()))
3567 source, branches = hg.parseurl(source, opts.get(b"branch"))
3569 source, branches = hg.parseurl(source, opts.get(b"branch"))
3568 try:
3570 try:
3569 other = hg.peer(repo, opts, source)
3571 other = hg.peer(repo, opts, source)
3570 except error.LookupError as ex:
3572 except error.LookupError as ex:
3571 msg = _(b"\nwarning: unable to open bundle %s") % source
3573 msg = _(b"\nwarning: unable to open bundle %s") % source
3572 hint = _(b"\n(missing parent rev %s)\n") % short(ex.name)
3574 hint = _(b"\n(missing parent rev %s)\n") % short(ex.name)
3573 ui.warn(msg, hint=hint)
3575 ui.warn(msg, hint=hint)
3574 continue
3576 continue
3575 revs, checkout = hg.addbranchrevs(
3577 revs, checkout = hg.addbranchrevs(
3576 repo, other, branches, opts.get(b"rev")
3578 repo, other, branches, opts.get(b"rev")
3577 )
3579 )
3578
3580
3579 if revs:
3581 if revs:
3580 revs = [other.lookup(rev) for rev in revs]
3582 revs = [other.lookup(rev) for rev in revs]
3581
3583
3582 quiet = ui.quiet
3584 quiet = ui.quiet
3583 try:
3585 try:
3584 ui.quiet = True
3586 ui.quiet = True
3585 other, chlist, cleanupfn = bundlerepo.getremotechanges(
3587 other, chlist, cleanupfn = bundlerepo.getremotechanges(
3586 ui, repo, other, revs, opts[b"bundle"], opts[b"force"]
3588 ui, repo, other, revs, opts[b"bundle"], opts[b"force"]
3587 )
3589 )
3588 except error.LookupError:
3590 except error.LookupError:
3589 continue
3591 continue
3590 finally:
3592 finally:
3591 ui.quiet = quiet
3593 ui.quiet = quiet
3592
3594
3593 try:
3595 try:
3594 if not chlist:
3596 if not chlist:
3595 continue
3597 continue
3596 if recovernode:
3598 if recovernode:
3597 with repo.lock(), repo.transaction(b"unbundle") as tr:
3599 with repo.lock(), repo.transaction(b"unbundle") as tr:
3598 if scmutil.isrevsymbol(other, recovernode):
3600 if scmutil.isrevsymbol(other, recovernode):
3599 ui.status(_(b"Unbundling %s\n") % (recovernode))
3601 ui.status(_(b"Unbundling %s\n") % (recovernode))
3600 f = hg.openpath(ui, source)
3602 f = hg.openpath(ui, source)
3601 gen = exchange.readbundle(ui, f, source)
3603 gen = exchange.readbundle(ui, f, source)
3602 if isinstance(gen, bundle2.unbundle20):
3604 if isinstance(gen, bundle2.unbundle20):
3603 bundle2.applybundle(
3605 bundle2.applybundle(
3604 repo,
3606 repo,
3605 gen,
3607 gen,
3606 tr,
3608 tr,
3607 source=b"unbundle",
3609 source=b"unbundle",
3608 url=b"bundle:" + source,
3610 url=b"bundle:" + source,
3609 )
3611 )
3610 else:
3612 else:
3611 gen.apply(repo, b"unbundle", b"bundle:" + source)
3613 gen.apply(repo, b"unbundle", b"bundle:" + source)
3612 break
3614 break
3613 else:
3615 else:
3614 backupdate = encoding.strtolocal(
3616 backupdate = encoding.strtolocal(
3615 time.strftime(
3617 time.strftime(
3616 "%a %H:%M, %Y-%m-%d",
3618 "%a %H:%M, %Y-%m-%d",
3617 time.localtime(os.path.getmtime(source)),
3619 time.localtime(os.path.getmtime(source)),
3618 )
3620 )
3619 )
3621 )
3620 ui.status(b"\n%s\n" % (backupdate.ljust(50)))
3622 ui.status(b"\n%s\n" % (backupdate.ljust(50)))
3621 if ui.verbose:
3623 if ui.verbose:
3622 ui.status(b"%s%s\n" % (b"bundle:".ljust(13), source))
3624 ui.status(b"%s%s\n" % (b"bundle:".ljust(13), source))
3623 else:
3625 else:
3624 opts[
3626 opts[
3625 b"template"
3627 b"template"
3626 ] = b"{label('status.modified', node|short)} {desc|firstline}\n"
3628 ] = b"{label('status.modified', node|short)} {desc|firstline}\n"
3627 displayer = logcmdutil.changesetdisplayer(
3629 displayer = logcmdutil.changesetdisplayer(
3628 ui, other, opts, False
3630 ui, other, opts, False
3629 )
3631 )
3630 display(other, chlist, displayer)
3632 display(other, chlist, displayer)
3631 displayer.close()
3633 displayer.close()
3632 finally:
3634 finally:
3633 cleanupfn()
3635 cleanupfn()
3634
3636
3635
3637
3636 @command(
3638 @command(
3637 b'debugsub',
3639 b'debugsub',
3638 [(b'r', b'rev', b'', _(b'revision to check'), _(b'REV'))],
3640 [(b'r', b'rev', b'', _(b'revision to check'), _(b'REV'))],
3639 _(b'[-r REV] [REV]'),
3641 _(b'[-r REV] [REV]'),
3640 )
3642 )
3641 def debugsub(ui, repo, rev=None):
3643 def debugsub(ui, repo, rev=None):
3642 ctx = scmutil.revsingle(repo, rev, None)
3644 ctx = scmutil.revsingle(repo, rev, None)
3643 for k, v in sorted(ctx.substate.items()):
3645 for k, v in sorted(ctx.substate.items()):
3644 ui.writenoi18n(b'path %s\n' % k)
3646 ui.writenoi18n(b'path %s\n' % k)
3645 ui.writenoi18n(b' source %s\n' % v[0])
3647 ui.writenoi18n(b' source %s\n' % v[0])
3646 ui.writenoi18n(b' revision %s\n' % v[1])
3648 ui.writenoi18n(b' revision %s\n' % v[1])
3647
3649
3648
3650
3649 @command(
3651 @command(
3650 b'debugsuccessorssets',
3652 b'debugsuccessorssets',
3651 [(b'', b'closest', False, _(b'return closest successors sets only'))],
3653 [(b'', b'closest', False, _(b'return closest successors sets only'))],
3652 _(b'[REV]'),
3654 _(b'[REV]'),
3653 )
3655 )
3654 def debugsuccessorssets(ui, repo, *revs, **opts):
3656 def debugsuccessorssets(ui, repo, *revs, **opts):
3655 """show set of successors for revision
3657 """show set of successors for revision
3656
3658
3657 A successors set of changeset A is a consistent group of revisions that
3659 A successors set of changeset A is a consistent group of revisions that
3658 succeed A. It contains non-obsolete changesets only unless closests
3660 succeed A. It contains non-obsolete changesets only unless closests
3659 successors set is set.
3661 successors set is set.
3660
3662
3661 In most cases a changeset A has a single successors set containing a single
3663 In most cases a changeset A has a single successors set containing a single
3662 successor (changeset A replaced by A').
3664 successor (changeset A replaced by A').
3663
3665
3664 A changeset that is made obsolete with no successors are called "pruned".
3666 A changeset that is made obsolete with no successors are called "pruned".
3665 Such changesets have no successors sets at all.
3667 Such changesets have no successors sets at all.
3666
3668
3667 A changeset that has been "split" will have a successors set containing
3669 A changeset that has been "split" will have a successors set containing
3668 more than one successor.
3670 more than one successor.
3669
3671
3670 A changeset that has been rewritten in multiple different ways is called
3672 A changeset that has been rewritten in multiple different ways is called
3671 "divergent". Such changesets have multiple successor sets (each of which
3673 "divergent". Such changesets have multiple successor sets (each of which
3672 may also be split, i.e. have multiple successors).
3674 may also be split, i.e. have multiple successors).
3673
3675
3674 Results are displayed as follows::
3676 Results are displayed as follows::
3675
3677
3676 <rev1>
3678 <rev1>
3677 <successors-1A>
3679 <successors-1A>
3678 <rev2>
3680 <rev2>
3679 <successors-2A>
3681 <successors-2A>
3680 <successors-2B1> <successors-2B2> <successors-2B3>
3682 <successors-2B1> <successors-2B2> <successors-2B3>
3681
3683
3682 Here rev2 has two possible (i.e. divergent) successors sets. The first
3684 Here rev2 has two possible (i.e. divergent) successors sets. The first
3683 holds one element, whereas the second holds three (i.e. the changeset has
3685 holds one element, whereas the second holds three (i.e. the changeset has
3684 been split).
3686 been split).
3685 """
3687 """
3686 # passed to successorssets caching computation from one call to another
3688 # passed to successorssets caching computation from one call to another
3687 cache = {}
3689 cache = {}
3688 ctx2str = bytes
3690 ctx2str = bytes
3689 node2str = short
3691 node2str = short
3690 for rev in scmutil.revrange(repo, revs):
3692 for rev in scmutil.revrange(repo, revs):
3691 ctx = repo[rev]
3693 ctx = repo[rev]
3692 ui.write(b'%s\n' % ctx2str(ctx))
3694 ui.write(b'%s\n' % ctx2str(ctx))
3693 for succsset in obsutil.successorssets(
3695 for succsset in obsutil.successorssets(
3694 repo, ctx.node(), closest=opts['closest'], cache=cache
3696 repo, ctx.node(), closest=opts['closest'], cache=cache
3695 ):
3697 ):
3696 if succsset:
3698 if succsset:
3697 ui.write(b' ')
3699 ui.write(b' ')
3698 ui.write(node2str(succsset[0]))
3700 ui.write(node2str(succsset[0]))
3699 for node in succsset[1:]:
3701 for node in succsset[1:]:
3700 ui.write(b' ')
3702 ui.write(b' ')
3701 ui.write(node2str(node))
3703 ui.write(node2str(node))
3702 ui.write(b'\n')
3704 ui.write(b'\n')
3703
3705
3704
3706
3705 @command(b'debugtagscache', [])
3707 @command(b'debugtagscache', [])
3706 def debugtagscache(ui, repo):
3708 def debugtagscache(ui, repo):
3707 """display the contents of .hg/cache/hgtagsfnodes1"""
3709 """display the contents of .hg/cache/hgtagsfnodes1"""
3708 cache = tagsmod.hgtagsfnodescache(repo.unfiltered())
3710 cache = tagsmod.hgtagsfnodescache(repo.unfiltered())
3709 for r in repo:
3711 for r in repo:
3710 node = repo[r].node()
3712 node = repo[r].node()
3711 tagsnode = cache.getfnode(node, computemissing=False)
3713 tagsnode = cache.getfnode(node, computemissing=False)
3712 tagsnodedisplay = hex(tagsnode) if tagsnode else b'missing/invalid'
3714 tagsnodedisplay = hex(tagsnode) if tagsnode else b'missing/invalid'
3713 ui.write(b'%d %s %s\n' % (r, hex(node), tagsnodedisplay))
3715 ui.write(b'%d %s %s\n' % (r, hex(node), tagsnodedisplay))
3714
3716
3715
3717
3716 @command(
3718 @command(
3717 b'debugtemplate',
3719 b'debugtemplate',
3718 [
3720 [
3719 (b'r', b'rev', [], _(b'apply template on changesets'), _(b'REV')),
3721 (b'r', b'rev', [], _(b'apply template on changesets'), _(b'REV')),
3720 (b'D', b'define', [], _(b'define template keyword'), _(b'KEY=VALUE')),
3722 (b'D', b'define', [], _(b'define template keyword'), _(b'KEY=VALUE')),
3721 ],
3723 ],
3722 _(b'[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
3724 _(b'[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
3723 optionalrepo=True,
3725 optionalrepo=True,
3724 )
3726 )
3725 def debugtemplate(ui, repo, tmpl, **opts):
3727 def debugtemplate(ui, repo, tmpl, **opts):
3726 """parse and apply a template
3728 """parse and apply a template
3727
3729
3728 If -r/--rev is given, the template is processed as a log template and
3730 If -r/--rev is given, the template is processed as a log template and
3729 applied to the given changesets. Otherwise, it is processed as a generic
3731 applied to the given changesets. Otherwise, it is processed as a generic
3730 template.
3732 template.
3731
3733
3732 Use --verbose to print the parsed tree.
3734 Use --verbose to print the parsed tree.
3733 """
3735 """
3734 revs = None
3736 revs = None
3735 if opts['rev']:
3737 if opts['rev']:
3736 if repo is None:
3738 if repo is None:
3737 raise error.RepoError(
3739 raise error.RepoError(
3738 _(b'there is no Mercurial repository here (.hg not found)')
3740 _(b'there is no Mercurial repository here (.hg not found)')
3739 )
3741 )
3740 revs = scmutil.revrange(repo, opts['rev'])
3742 revs = scmutil.revrange(repo, opts['rev'])
3741
3743
3742 props = {}
3744 props = {}
3743 for d in opts['define']:
3745 for d in opts['define']:
3744 try:
3746 try:
3745 k, v = (e.strip() for e in d.split(b'=', 1))
3747 k, v = (e.strip() for e in d.split(b'=', 1))
3746 if not k or k == b'ui':
3748 if not k or k == b'ui':
3747 raise ValueError
3749 raise ValueError
3748 props[k] = v
3750 props[k] = v
3749 except ValueError:
3751 except ValueError:
3750 raise error.Abort(_(b'malformed keyword definition: %s') % d)
3752 raise error.Abort(_(b'malformed keyword definition: %s') % d)
3751
3753
3752 if ui.verbose:
3754 if ui.verbose:
3753 aliases = ui.configitems(b'templatealias')
3755 aliases = ui.configitems(b'templatealias')
3754 tree = templater.parse(tmpl)
3756 tree = templater.parse(tmpl)
3755 ui.note(templater.prettyformat(tree), b'\n')
3757 ui.note(templater.prettyformat(tree), b'\n')
3756 newtree = templater.expandaliases(tree, aliases)
3758 newtree = templater.expandaliases(tree, aliases)
3757 if newtree != tree:
3759 if newtree != tree:
3758 ui.notenoi18n(
3760 ui.notenoi18n(
3759 b"* expanded:\n", templater.prettyformat(newtree), b'\n'
3761 b"* expanded:\n", templater.prettyformat(newtree), b'\n'
3760 )
3762 )
3761
3763
3762 if revs is None:
3764 if revs is None:
3763 tres = formatter.templateresources(ui, repo)
3765 tres = formatter.templateresources(ui, repo)
3764 t = formatter.maketemplater(ui, tmpl, resources=tres)
3766 t = formatter.maketemplater(ui, tmpl, resources=tres)
3765 if ui.verbose:
3767 if ui.verbose:
3766 kwds, funcs = t.symbolsuseddefault()
3768 kwds, funcs = t.symbolsuseddefault()
3767 ui.writenoi18n(b"* keywords: %s\n" % b', '.join(sorted(kwds)))
3769 ui.writenoi18n(b"* keywords: %s\n" % b', '.join(sorted(kwds)))
3768 ui.writenoi18n(b"* functions: %s\n" % b', '.join(sorted(funcs)))
3770 ui.writenoi18n(b"* functions: %s\n" % b', '.join(sorted(funcs)))
3769 ui.write(t.renderdefault(props))
3771 ui.write(t.renderdefault(props))
3770 else:
3772 else:
3771 displayer = logcmdutil.maketemplater(ui, repo, tmpl)
3773 displayer = logcmdutil.maketemplater(ui, repo, tmpl)
3772 if ui.verbose:
3774 if ui.verbose:
3773 kwds, funcs = displayer.t.symbolsuseddefault()
3775 kwds, funcs = displayer.t.symbolsuseddefault()
3774 ui.writenoi18n(b"* keywords: %s\n" % b', '.join(sorted(kwds)))
3776 ui.writenoi18n(b"* keywords: %s\n" % b', '.join(sorted(kwds)))
3775 ui.writenoi18n(b"* functions: %s\n" % b', '.join(sorted(funcs)))
3777 ui.writenoi18n(b"* functions: %s\n" % b', '.join(sorted(funcs)))
3776 for r in revs:
3778 for r in revs:
3777 displayer.show(repo[r], **pycompat.strkwargs(props))
3779 displayer.show(repo[r], **pycompat.strkwargs(props))
3778 displayer.close()
3780 displayer.close()
3779
3781
3780
3782
3781 @command(
3783 @command(
3782 b'debuguigetpass',
3784 b'debuguigetpass',
3783 [(b'p', b'prompt', b'', _(b'prompt text'), _(b'TEXT')),],
3785 [(b'p', b'prompt', b'', _(b'prompt text'), _(b'TEXT')),],
3784 _(b'[-p TEXT]'),
3786 _(b'[-p TEXT]'),
3785 norepo=True,
3787 norepo=True,
3786 )
3788 )
3787 def debuguigetpass(ui, prompt=b''):
3789 def debuguigetpass(ui, prompt=b''):
3788 """show prompt to type password"""
3790 """show prompt to type password"""
3789 r = ui.getpass(prompt)
3791 r = ui.getpass(prompt)
3790 ui.writenoi18n(b'response: %s\n' % r)
3792 ui.writenoi18n(b'response: %s\n' % r)
3791
3793
3792
3794
3793 @command(
3795 @command(
3794 b'debuguiprompt',
3796 b'debuguiprompt',
3795 [(b'p', b'prompt', b'', _(b'prompt text'), _(b'TEXT')),],
3797 [(b'p', b'prompt', b'', _(b'prompt text'), _(b'TEXT')),],
3796 _(b'[-p TEXT]'),
3798 _(b'[-p TEXT]'),
3797 norepo=True,
3799 norepo=True,
3798 )
3800 )
3799 def debuguiprompt(ui, prompt=b''):
3801 def debuguiprompt(ui, prompt=b''):
3800 """show plain prompt"""
3802 """show plain prompt"""
3801 r = ui.prompt(prompt)
3803 r = ui.prompt(prompt)
3802 ui.writenoi18n(b'response: %s\n' % r)
3804 ui.writenoi18n(b'response: %s\n' % r)
3803
3805
3804
3806
3805 @command(b'debugupdatecaches', [])
3807 @command(b'debugupdatecaches', [])
3806 def debugupdatecaches(ui, repo, *pats, **opts):
3808 def debugupdatecaches(ui, repo, *pats, **opts):
3807 """warm all known caches in the repository"""
3809 """warm all known caches in the repository"""
3808 with repo.wlock(), repo.lock():
3810 with repo.wlock(), repo.lock():
3809 repo.updatecaches(full=True)
3811 repo.updatecaches(full=True)
3810
3812
3811
3813
3812 @command(
3814 @command(
3813 b'debugupgraderepo',
3815 b'debugupgraderepo',
3814 [
3816 [
3815 (
3817 (
3816 b'o',
3818 b'o',
3817 b'optimize',
3819 b'optimize',
3818 [],
3820 [],
3819 _(b'extra optimization to perform'),
3821 _(b'extra optimization to perform'),
3820 _(b'NAME'),
3822 _(b'NAME'),
3821 ),
3823 ),
3822 (b'', b'run', False, _(b'performs an upgrade')),
3824 (b'', b'run', False, _(b'performs an upgrade')),
3823 (b'', b'backup', True, _(b'keep the old repository content around')),
3825 (b'', b'backup', True, _(b'keep the old repository content around')),
3824 (b'', b'changelog', None, _(b'select the changelog for upgrade')),
3826 (b'', b'changelog', None, _(b'select the changelog for upgrade')),
3825 (b'', b'manifest', None, _(b'select the manifest for upgrade')),
3827 (b'', b'manifest', None, _(b'select the manifest for upgrade')),
3826 ],
3828 ],
3827 )
3829 )
3828 def debugupgraderepo(ui, repo, run=False, optimize=None, backup=True, **opts):
3830 def debugupgraderepo(ui, repo, run=False, optimize=None, backup=True, **opts):
3829 """upgrade a repository to use different features
3831 """upgrade a repository to use different features
3830
3832
3831 If no arguments are specified, the repository is evaluated for upgrade
3833 If no arguments are specified, the repository is evaluated for upgrade
3832 and a list of problems and potential optimizations is printed.
3834 and a list of problems and potential optimizations is printed.
3833
3835
3834 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
3836 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
3835 can be influenced via additional arguments. More details will be provided
3837 can be influenced via additional arguments. More details will be provided
3836 by the command output when run without ``--run``.
3838 by the command output when run without ``--run``.
3837
3839
3838 During the upgrade, the repository will be locked and no writes will be
3840 During the upgrade, the repository will be locked and no writes will be
3839 allowed.
3841 allowed.
3840
3842
3841 At the end of the upgrade, the repository may not be readable while new
3843 At the end of the upgrade, the repository may not be readable while new
3842 repository data is swapped in. This window will be as long as it takes to
3844 repository data is swapped in. This window will be as long as it takes to
3843 rename some directories inside the ``.hg`` directory. On most machines, this
3845 rename some directories inside the ``.hg`` directory. On most machines, this
3844 should complete almost instantaneously and the chances of a consumer being
3846 should complete almost instantaneously and the chances of a consumer being
3845 unable to access the repository should be low.
3847 unable to access the repository should be low.
3846
3848
3847 By default, all revlog will be upgraded. You can restrict this using flag
3849 By default, all revlog will be upgraded. You can restrict this using flag
3848 such as `--manifest`:
3850 such as `--manifest`:
3849
3851
3850 * `--manifest`: only optimize the manifest
3852 * `--manifest`: only optimize the manifest
3851 * `--no-manifest`: optimize all revlog but the manifest
3853 * `--no-manifest`: optimize all revlog but the manifest
3852 * `--changelog`: optimize the changelog only
3854 * `--changelog`: optimize the changelog only
3853 * `--no-changelog --no-manifest`: optimize filelogs only
3855 * `--no-changelog --no-manifest`: optimize filelogs only
3854 """
3856 """
3855 return upgrade.upgraderepo(
3857 return upgrade.upgraderepo(
3856 ui, repo, run=run, optimize=optimize, backup=backup, **opts
3858 ui, repo, run=run, optimize=optimize, backup=backup, **opts
3857 )
3859 )
3858
3860
3859
3861
3860 @command(
3862 @command(
3861 b'debugwalk', cmdutil.walkopts, _(b'[OPTION]... [FILE]...'), inferrepo=True
3863 b'debugwalk', cmdutil.walkopts, _(b'[OPTION]... [FILE]...'), inferrepo=True
3862 )
3864 )
3863 def debugwalk(ui, repo, *pats, **opts):
3865 def debugwalk(ui, repo, *pats, **opts):
3864 """show how files match on given patterns"""
3866 """show how files match on given patterns"""
3865 opts = pycompat.byteskwargs(opts)
3867 opts = pycompat.byteskwargs(opts)
3866 m = scmutil.match(repo[None], pats, opts)
3868 m = scmutil.match(repo[None], pats, opts)
3867 if ui.verbose:
3869 if ui.verbose:
3868 ui.writenoi18n(b'* matcher:\n', stringutil.prettyrepr(m), b'\n')
3870 ui.writenoi18n(b'* matcher:\n', stringutil.prettyrepr(m), b'\n')
3869 items = list(repo[None].walk(m))
3871 items = list(repo[None].walk(m))
3870 if not items:
3872 if not items:
3871 return
3873 return
3872 f = lambda fn: fn
3874 f = lambda fn: fn
3873 if ui.configbool(b'ui', b'slash') and pycompat.ossep != b'/':
3875 if ui.configbool(b'ui', b'slash') and pycompat.ossep != b'/':
3874 f = lambda fn: util.normpath(fn)
3876 f = lambda fn: util.normpath(fn)
3875 fmt = b'f %%-%ds %%-%ds %%s' % (
3877 fmt = b'f %%-%ds %%-%ds %%s' % (
3876 max([len(abs) for abs in items]),
3878 max([len(abs) for abs in items]),
3877 max([len(repo.pathto(abs)) for abs in items]),
3879 max([len(repo.pathto(abs)) for abs in items]),
3878 )
3880 )
3879 for abs in items:
3881 for abs in items:
3880 line = fmt % (
3882 line = fmt % (
3881 abs,
3883 abs,
3882 f(repo.pathto(abs)),
3884 f(repo.pathto(abs)),
3883 m.exact(abs) and b'exact' or b'',
3885 m.exact(abs) and b'exact' or b'',
3884 )
3886 )
3885 ui.write(b"%s\n" % line.rstrip())
3887 ui.write(b"%s\n" % line.rstrip())
3886
3888
3887
3889
3888 @command(b'debugwhyunstable', [], _(b'REV'))
3890 @command(b'debugwhyunstable', [], _(b'REV'))
3889 def debugwhyunstable(ui, repo, rev):
3891 def debugwhyunstable(ui, repo, rev):
3890 """explain instabilities of a changeset"""
3892 """explain instabilities of a changeset"""
3891 for entry in obsutil.whyunstable(repo, scmutil.revsingle(repo, rev)):
3893 for entry in obsutil.whyunstable(repo, scmutil.revsingle(repo, rev)):
3892 dnodes = b''
3894 dnodes = b''
3893 if entry.get(b'divergentnodes'):
3895 if entry.get(b'divergentnodes'):
3894 dnodes = (
3896 dnodes = (
3895 b' '.join(
3897 b' '.join(
3896 b'%s (%s)' % (ctx.hex(), ctx.phasestr())
3898 b'%s (%s)' % (ctx.hex(), ctx.phasestr())
3897 for ctx in entry[b'divergentnodes']
3899 for ctx in entry[b'divergentnodes']
3898 )
3900 )
3899 + b' '
3901 + b' '
3900 )
3902 )
3901 ui.write(
3903 ui.write(
3902 b'%s: %s%s %s\n'
3904 b'%s: %s%s %s\n'
3903 % (entry[b'instability'], dnodes, entry[b'reason'], entry[b'node'])
3905 % (entry[b'instability'], dnodes, entry[b'reason'], entry[b'node'])
3904 )
3906 )
3905
3907
3906
3908
3907 @command(
3909 @command(
3908 b'debugwireargs',
3910 b'debugwireargs',
3909 [
3911 [
3910 (b'', b'three', b'', b'three'),
3912 (b'', b'three', b'', b'three'),
3911 (b'', b'four', b'', b'four'),
3913 (b'', b'four', b'', b'four'),
3912 (b'', b'five', b'', b'five'),
3914 (b'', b'five', b'', b'five'),
3913 ]
3915 ]
3914 + cmdutil.remoteopts,
3916 + cmdutil.remoteopts,
3915 _(b'REPO [OPTIONS]... [ONE [TWO]]'),
3917 _(b'REPO [OPTIONS]... [ONE [TWO]]'),
3916 norepo=True,
3918 norepo=True,
3917 )
3919 )
3918 def debugwireargs(ui, repopath, *vals, **opts):
3920 def debugwireargs(ui, repopath, *vals, **opts):
3919 opts = pycompat.byteskwargs(opts)
3921 opts = pycompat.byteskwargs(opts)
3920 repo = hg.peer(ui, opts, repopath)
3922 repo = hg.peer(ui, opts, repopath)
3921 for opt in cmdutil.remoteopts:
3923 for opt in cmdutil.remoteopts:
3922 del opts[opt[1]]
3924 del opts[opt[1]]
3923 args = {}
3925 args = {}
3924 for k, v in pycompat.iteritems(opts):
3926 for k, v in pycompat.iteritems(opts):
3925 if v:
3927 if v:
3926 args[k] = v
3928 args[k] = v
3927 args = pycompat.strkwargs(args)
3929 args = pycompat.strkwargs(args)
3928 # run twice to check that we don't mess up the stream for the next command
3930 # run twice to check that we don't mess up the stream for the next command
3929 res1 = repo.debugwireargs(*vals, **args)
3931 res1 = repo.debugwireargs(*vals, **args)
3930 res2 = repo.debugwireargs(*vals, **args)
3932 res2 = repo.debugwireargs(*vals, **args)
3931 ui.write(b"%s\n" % res1)
3933 ui.write(b"%s\n" % res1)
3932 if res1 != res2:
3934 if res1 != res2:
3933 ui.warn(b"%s\n" % res2)
3935 ui.warn(b"%s\n" % res2)
3934
3936
3935
3937
3936 def _parsewirelangblocks(fh):
3938 def _parsewirelangblocks(fh):
3937 activeaction = None
3939 activeaction = None
3938 blocklines = []
3940 blocklines = []
3939 lastindent = 0
3941 lastindent = 0
3940
3942
3941 for line in fh:
3943 for line in fh:
3942 line = line.rstrip()
3944 line = line.rstrip()
3943 if not line:
3945 if not line:
3944 continue
3946 continue
3945
3947
3946 if line.startswith(b'#'):
3948 if line.startswith(b'#'):
3947 continue
3949 continue
3948
3950
3949 if not line.startswith(b' '):
3951 if not line.startswith(b' '):
3950 # New block. Flush previous one.
3952 # New block. Flush previous one.
3951 if activeaction:
3953 if activeaction:
3952 yield activeaction, blocklines
3954 yield activeaction, blocklines
3953
3955
3954 activeaction = line
3956 activeaction = line
3955 blocklines = []
3957 blocklines = []
3956 lastindent = 0
3958 lastindent = 0
3957 continue
3959 continue
3958
3960
3959 # Else we start with an indent.
3961 # Else we start with an indent.
3960
3962
3961 if not activeaction:
3963 if not activeaction:
3962 raise error.Abort(_(b'indented line outside of block'))
3964 raise error.Abort(_(b'indented line outside of block'))
3963
3965
3964 indent = len(line) - len(line.lstrip())
3966 indent = len(line) - len(line.lstrip())
3965
3967
3966 # If this line is indented more than the last line, concatenate it.
3968 # If this line is indented more than the last line, concatenate it.
3967 if indent > lastindent and blocklines:
3969 if indent > lastindent and blocklines:
3968 blocklines[-1] += line.lstrip()
3970 blocklines[-1] += line.lstrip()
3969 else:
3971 else:
3970 blocklines.append(line)
3972 blocklines.append(line)
3971 lastindent = indent
3973 lastindent = indent
3972
3974
3973 # Flush last block.
3975 # Flush last block.
3974 if activeaction:
3976 if activeaction:
3975 yield activeaction, blocklines
3977 yield activeaction, blocklines
3976
3978
3977
3979
3978 @command(
3980 @command(
3979 b'debugwireproto',
3981 b'debugwireproto',
3980 [
3982 [
3981 (b'', b'localssh', False, _(b'start an SSH server for this repo')),
3983 (b'', b'localssh', False, _(b'start an SSH server for this repo')),
3982 (b'', b'peer', b'', _(b'construct a specific version of the peer')),
3984 (b'', b'peer', b'', _(b'construct a specific version of the peer')),
3983 (
3985 (
3984 b'',
3986 b'',
3985 b'noreadstderr',
3987 b'noreadstderr',
3986 False,
3988 False,
3987 _(b'do not read from stderr of the remote'),
3989 _(b'do not read from stderr of the remote'),
3988 ),
3990 ),
3989 (
3991 (
3990 b'',
3992 b'',
3991 b'nologhandshake',
3993 b'nologhandshake',
3992 False,
3994 False,
3993 _(b'do not log I/O related to the peer handshake'),
3995 _(b'do not log I/O related to the peer handshake'),
3994 ),
3996 ),
3995 ]
3997 ]
3996 + cmdutil.remoteopts,
3998 + cmdutil.remoteopts,
3997 _(b'[PATH]'),
3999 _(b'[PATH]'),
3998 optionalrepo=True,
4000 optionalrepo=True,
3999 )
4001 )
4000 def debugwireproto(ui, repo, path=None, **opts):
4002 def debugwireproto(ui, repo, path=None, **opts):
4001 """send wire protocol commands to a server
4003 """send wire protocol commands to a server
4002
4004
4003 This command can be used to issue wire protocol commands to remote
4005 This command can be used to issue wire protocol commands to remote
4004 peers and to debug the raw data being exchanged.
4006 peers and to debug the raw data being exchanged.
4005
4007
4006 ``--localssh`` will start an SSH server against the current repository
4008 ``--localssh`` will start an SSH server against the current repository
4007 and connect to that. By default, the connection will perform a handshake
4009 and connect to that. By default, the connection will perform a handshake
4008 and establish an appropriate peer instance.
4010 and establish an appropriate peer instance.
4009
4011
4010 ``--peer`` can be used to bypass the handshake protocol and construct a
4012 ``--peer`` can be used to bypass the handshake protocol and construct a
4011 peer instance using the specified class type. Valid values are ``raw``,
4013 peer instance using the specified class type. Valid values are ``raw``,
4012 ``http2``, ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending
4014 ``http2``, ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending
4013 raw data payloads and don't support higher-level command actions.
4015 raw data payloads and don't support higher-level command actions.
4014
4016
4015 ``--noreadstderr`` can be used to disable automatic reading from stderr
4017 ``--noreadstderr`` can be used to disable automatic reading from stderr
4016 of the peer (for SSH connections only). Disabling automatic reading of
4018 of the peer (for SSH connections only). Disabling automatic reading of
4017 stderr is useful for making output more deterministic.
4019 stderr is useful for making output more deterministic.
4018
4020
4019 Commands are issued via a mini language which is specified via stdin.
4021 Commands are issued via a mini language which is specified via stdin.
4020 The language consists of individual actions to perform. An action is
4022 The language consists of individual actions to perform. An action is
4021 defined by a block. A block is defined as a line with no leading
4023 defined by a block. A block is defined as a line with no leading
4022 space followed by 0 or more lines with leading space. Blocks are
4024 space followed by 0 or more lines with leading space. Blocks are
4023 effectively a high-level command with additional metadata.
4025 effectively a high-level command with additional metadata.
4024
4026
4025 Lines beginning with ``#`` are ignored.
4027 Lines beginning with ``#`` are ignored.
4026
4028
4027 The following sections denote available actions.
4029 The following sections denote available actions.
4028
4030
4029 raw
4031 raw
4030 ---
4032 ---
4031
4033
4032 Send raw data to the server.
4034 Send raw data to the server.
4033
4035
4034 The block payload contains the raw data to send as one atomic send
4036 The block payload contains the raw data to send as one atomic send
4035 operation. The data may not actually be delivered in a single system
4037 operation. The data may not actually be delivered in a single system
4036 call: it depends on the abilities of the transport being used.
4038 call: it depends on the abilities of the transport being used.
4037
4039
4038 Each line in the block is de-indented and concatenated. Then, that
4040 Each line in the block is de-indented and concatenated. Then, that
4039 value is evaluated as a Python b'' literal. This allows the use of
4041 value is evaluated as a Python b'' literal. This allows the use of
4040 backslash escaping, etc.
4042 backslash escaping, etc.
4041
4043
4042 raw+
4044 raw+
4043 ----
4045 ----
4044
4046
4045 Behaves like ``raw`` except flushes output afterwards.
4047 Behaves like ``raw`` except flushes output afterwards.
4046
4048
4047 command <X>
4049 command <X>
4048 -----------
4050 -----------
4049
4051
4050 Send a request to run a named command, whose name follows the ``command``
4052 Send a request to run a named command, whose name follows the ``command``
4051 string.
4053 string.
4052
4054
4053 Arguments to the command are defined as lines in this block. The format of
4055 Arguments to the command are defined as lines in this block. The format of
4054 each line is ``<key> <value>``. e.g.::
4056 each line is ``<key> <value>``. e.g.::
4055
4057
4056 command listkeys
4058 command listkeys
4057 namespace bookmarks
4059 namespace bookmarks
4058
4060
4059 If the value begins with ``eval:``, it will be interpreted as a Python
4061 If the value begins with ``eval:``, it will be interpreted as a Python
4060 literal expression. Otherwise values are interpreted as Python b'' literals.
4062 literal expression. Otherwise values are interpreted as Python b'' literals.
4061 This allows sending complex types and encoding special byte sequences via
4063 This allows sending complex types and encoding special byte sequences via
4062 backslash escaping.
4064 backslash escaping.
4063
4065
4064 The following arguments have special meaning:
4066 The following arguments have special meaning:
4065
4067
4066 ``PUSHFILE``
4068 ``PUSHFILE``
4067 When defined, the *push* mechanism of the peer will be used instead
4069 When defined, the *push* mechanism of the peer will be used instead
4068 of the static request-response mechanism and the content of the
4070 of the static request-response mechanism and the content of the
4069 file specified in the value of this argument will be sent as the
4071 file specified in the value of this argument will be sent as the
4070 command payload.
4072 command payload.
4071
4073
4072 This can be used to submit a local bundle file to the remote.
4074 This can be used to submit a local bundle file to the remote.
4073
4075
4074 batchbegin
4076 batchbegin
4075 ----------
4077 ----------
4076
4078
4077 Instruct the peer to begin a batched send.
4079 Instruct the peer to begin a batched send.
4078
4080
4079 All ``command`` blocks are queued for execution until the next
4081 All ``command`` blocks are queued for execution until the next
4080 ``batchsubmit`` block.
4082 ``batchsubmit`` block.
4081
4083
4082 batchsubmit
4084 batchsubmit
4083 -----------
4085 -----------
4084
4086
4085 Submit previously queued ``command`` blocks as a batch request.
4087 Submit previously queued ``command`` blocks as a batch request.
4086
4088
4087 This action MUST be paired with a ``batchbegin`` action.
4089 This action MUST be paired with a ``batchbegin`` action.
4088
4090
4089 httprequest <method> <path>
4091 httprequest <method> <path>
4090 ---------------------------
4092 ---------------------------
4091
4093
4092 (HTTP peer only)
4094 (HTTP peer only)
4093
4095
4094 Send an HTTP request to the peer.
4096 Send an HTTP request to the peer.
4095
4097
4096 The HTTP request line follows the ``httprequest`` action. e.g. ``GET /foo``.
4098 The HTTP request line follows the ``httprequest`` action. e.g. ``GET /foo``.
4097
4099
4098 Arguments of the form ``<key>: <value>`` are interpreted as HTTP request
4100 Arguments of the form ``<key>: <value>`` are interpreted as HTTP request
4099 headers to add to the request. e.g. ``Accept: foo``.
4101 headers to add to the request. e.g. ``Accept: foo``.
4100
4102
4101 The following arguments are special:
4103 The following arguments are special:
4102
4104
4103 ``BODYFILE``
4105 ``BODYFILE``
4104 The content of the file defined as the value to this argument will be
4106 The content of the file defined as the value to this argument will be
4105 transferred verbatim as the HTTP request body.
4107 transferred verbatim as the HTTP request body.
4106
4108
4107 ``frame <type> <flags> <payload>``
4109 ``frame <type> <flags> <payload>``
4108 Send a unified protocol frame as part of the request body.
4110 Send a unified protocol frame as part of the request body.
4109
4111
4110 All frames will be collected and sent as the body to the HTTP
4112 All frames will be collected and sent as the body to the HTTP
4111 request.
4113 request.
4112
4114
4113 close
4115 close
4114 -----
4116 -----
4115
4117
4116 Close the connection to the server.
4118 Close the connection to the server.
4117
4119
4118 flush
4120 flush
4119 -----
4121 -----
4120
4122
4121 Flush data written to the server.
4123 Flush data written to the server.
4122
4124
4123 readavailable
4125 readavailable
4124 -------------
4126 -------------
4125
4127
4126 Close the write end of the connection and read all available data from
4128 Close the write end of the connection and read all available data from
4127 the server.
4129 the server.
4128
4130
4129 If the connection to the server encompasses multiple pipes, we poll both
4131 If the connection to the server encompasses multiple pipes, we poll both
4130 pipes and read available data.
4132 pipes and read available data.
4131
4133
4132 readline
4134 readline
4133 --------
4135 --------
4134
4136
4135 Read a line of output from the server. If there are multiple output
4137 Read a line of output from the server. If there are multiple output
4136 pipes, reads only the main pipe.
4138 pipes, reads only the main pipe.
4137
4139
4138 ereadline
4140 ereadline
4139 ---------
4141 ---------
4140
4142
4141 Like ``readline``, but read from the stderr pipe, if available.
4143 Like ``readline``, but read from the stderr pipe, if available.
4142
4144
4143 read <X>
4145 read <X>
4144 --------
4146 --------
4145
4147
4146 ``read()`` N bytes from the server's main output pipe.
4148 ``read()`` N bytes from the server's main output pipe.
4147
4149
4148 eread <X>
4150 eread <X>
4149 ---------
4151 ---------
4150
4152
4151 ``read()`` N bytes from the server's stderr pipe, if available.
4153 ``read()`` N bytes from the server's stderr pipe, if available.
4152
4154
4153 Specifying Unified Frame-Based Protocol Frames
4155 Specifying Unified Frame-Based Protocol Frames
4154 ----------------------------------------------
4156 ----------------------------------------------
4155
4157
4156 It is possible to emit a *Unified Frame-Based Protocol* by using special
4158 It is possible to emit a *Unified Frame-Based Protocol* by using special
4157 syntax.
4159 syntax.
4158
4160
4159 A frame is composed as a type, flags, and payload. These can be parsed
4161 A frame is composed as a type, flags, and payload. These can be parsed
4160 from a string of the form:
4162 from a string of the form:
4161
4163
4162 <request-id> <stream-id> <stream-flags> <type> <flags> <payload>
4164 <request-id> <stream-id> <stream-flags> <type> <flags> <payload>
4163
4165
4164 ``request-id`` and ``stream-id`` are integers defining the request and
4166 ``request-id`` and ``stream-id`` are integers defining the request and
4165 stream identifiers.
4167 stream identifiers.
4166
4168
4167 ``type`` can be an integer value for the frame type or the string name
4169 ``type`` can be an integer value for the frame type or the string name
4168 of the type. The strings are defined in ``wireprotoframing.py``. e.g.
4170 of the type. The strings are defined in ``wireprotoframing.py``. e.g.
4169 ``command-name``.
4171 ``command-name``.
4170
4172
4171 ``stream-flags`` and ``flags`` are a ``|`` delimited list of flag
4173 ``stream-flags`` and ``flags`` are a ``|`` delimited list of flag
4172 components. Each component (and there can be just one) can be an integer
4174 components. Each component (and there can be just one) can be an integer
4173 or a flag name for stream flags or frame flags, respectively. Values are
4175 or a flag name for stream flags or frame flags, respectively. Values are
4174 resolved to integers and then bitwise OR'd together.
4176 resolved to integers and then bitwise OR'd together.
4175
4177
4176 ``payload`` represents the raw frame payload. If it begins with
4178 ``payload`` represents the raw frame payload. If it begins with
4177 ``cbor:``, the following string is evaluated as Python code and the
4179 ``cbor:``, the following string is evaluated as Python code and the
4178 resulting object is fed into a CBOR encoder. Otherwise it is interpreted
4180 resulting object is fed into a CBOR encoder. Otherwise it is interpreted
4179 as a Python byte string literal.
4181 as a Python byte string literal.
4180 """
4182 """
4181 opts = pycompat.byteskwargs(opts)
4183 opts = pycompat.byteskwargs(opts)
4182
4184
4183 if opts[b'localssh'] and not repo:
4185 if opts[b'localssh'] and not repo:
4184 raise error.Abort(_(b'--localssh requires a repository'))
4186 raise error.Abort(_(b'--localssh requires a repository'))
4185
4187
4186 if opts[b'peer'] and opts[b'peer'] not in (
4188 if opts[b'peer'] and opts[b'peer'] not in (
4187 b'raw',
4189 b'raw',
4188 b'http2',
4190 b'http2',
4189 b'ssh1',
4191 b'ssh1',
4190 b'ssh2',
4192 b'ssh2',
4191 ):
4193 ):
4192 raise error.Abort(
4194 raise error.Abort(
4193 _(b'invalid value for --peer'),
4195 _(b'invalid value for --peer'),
4194 hint=_(b'valid values are "raw", "ssh1", and "ssh2"'),
4196 hint=_(b'valid values are "raw", "ssh1", and "ssh2"'),
4195 )
4197 )
4196
4198
4197 if path and opts[b'localssh']:
4199 if path and opts[b'localssh']:
4198 raise error.Abort(_(b'cannot specify --localssh with an explicit path'))
4200 raise error.Abort(_(b'cannot specify --localssh with an explicit path'))
4199
4201
4200 if ui.interactive():
4202 if ui.interactive():
4201 ui.write(_(b'(waiting for commands on stdin)\n'))
4203 ui.write(_(b'(waiting for commands on stdin)\n'))
4202
4204
4203 blocks = list(_parsewirelangblocks(ui.fin))
4205 blocks = list(_parsewirelangblocks(ui.fin))
4204
4206
4205 proc = None
4207 proc = None
4206 stdin = None
4208 stdin = None
4207 stdout = None
4209 stdout = None
4208 stderr = None
4210 stderr = None
4209 opener = None
4211 opener = None
4210
4212
4211 if opts[b'localssh']:
4213 if opts[b'localssh']:
4212 # We start the SSH server in its own process so there is process
4214 # We start the SSH server in its own process so there is process
4213 # separation. This prevents a whole class of potential bugs around
4215 # separation. This prevents a whole class of potential bugs around
4214 # shared state from interfering with server operation.
4216 # shared state from interfering with server operation.
4215 args = procutil.hgcmd() + [
4217 args = procutil.hgcmd() + [
4216 b'-R',
4218 b'-R',
4217 repo.root,
4219 repo.root,
4218 b'debugserve',
4220 b'debugserve',
4219 b'--sshstdio',
4221 b'--sshstdio',
4220 ]
4222 ]
4221 proc = subprocess.Popen(
4223 proc = subprocess.Popen(
4222 pycompat.rapply(procutil.tonativestr, args),
4224 pycompat.rapply(procutil.tonativestr, args),
4223 stdin=subprocess.PIPE,
4225 stdin=subprocess.PIPE,
4224 stdout=subprocess.PIPE,
4226 stdout=subprocess.PIPE,
4225 stderr=subprocess.PIPE,
4227 stderr=subprocess.PIPE,
4226 bufsize=0,
4228 bufsize=0,
4227 )
4229 )
4228
4230
4229 stdin = proc.stdin
4231 stdin = proc.stdin
4230 stdout = proc.stdout
4232 stdout = proc.stdout
4231 stderr = proc.stderr
4233 stderr = proc.stderr
4232
4234
4233 # We turn the pipes into observers so we can log I/O.
4235 # We turn the pipes into observers so we can log I/O.
4234 if ui.verbose or opts[b'peer'] == b'raw':
4236 if ui.verbose or opts[b'peer'] == b'raw':
4235 stdin = util.makeloggingfileobject(
4237 stdin = util.makeloggingfileobject(
4236 ui, proc.stdin, b'i', logdata=True
4238 ui, proc.stdin, b'i', logdata=True
4237 )
4239 )
4238 stdout = util.makeloggingfileobject(
4240 stdout = util.makeloggingfileobject(
4239 ui, proc.stdout, b'o', logdata=True
4241 ui, proc.stdout, b'o', logdata=True
4240 )
4242 )
4241 stderr = util.makeloggingfileobject(
4243 stderr = util.makeloggingfileobject(
4242 ui, proc.stderr, b'e', logdata=True
4244 ui, proc.stderr, b'e', logdata=True
4243 )
4245 )
4244
4246
4245 # --localssh also implies the peer connection settings.
4247 # --localssh also implies the peer connection settings.
4246
4248
4247 url = b'ssh://localserver'
4249 url = b'ssh://localserver'
4248 autoreadstderr = not opts[b'noreadstderr']
4250 autoreadstderr = not opts[b'noreadstderr']
4249
4251
4250 if opts[b'peer'] == b'ssh1':
4252 if opts[b'peer'] == b'ssh1':
4251 ui.write(_(b'creating ssh peer for wire protocol version 1\n'))
4253 ui.write(_(b'creating ssh peer for wire protocol version 1\n'))
4252 peer = sshpeer.sshv1peer(
4254 peer = sshpeer.sshv1peer(
4253 ui,
4255 ui,
4254 url,
4256 url,
4255 proc,
4257 proc,
4256 stdin,
4258 stdin,
4257 stdout,
4259 stdout,
4258 stderr,
4260 stderr,
4259 None,
4261 None,
4260 autoreadstderr=autoreadstderr,
4262 autoreadstderr=autoreadstderr,
4261 )
4263 )
4262 elif opts[b'peer'] == b'ssh2':
4264 elif opts[b'peer'] == b'ssh2':
4263 ui.write(_(b'creating ssh peer for wire protocol version 2\n'))
4265 ui.write(_(b'creating ssh peer for wire protocol version 2\n'))
4264 peer = sshpeer.sshv2peer(
4266 peer = sshpeer.sshv2peer(
4265 ui,
4267 ui,
4266 url,
4268 url,
4267 proc,
4269 proc,
4268 stdin,
4270 stdin,
4269 stdout,
4271 stdout,
4270 stderr,
4272 stderr,
4271 None,
4273 None,
4272 autoreadstderr=autoreadstderr,
4274 autoreadstderr=autoreadstderr,
4273 )
4275 )
4274 elif opts[b'peer'] == b'raw':
4276 elif opts[b'peer'] == b'raw':
4275 ui.write(_(b'using raw connection to peer\n'))
4277 ui.write(_(b'using raw connection to peer\n'))
4276 peer = None
4278 peer = None
4277 else:
4279 else:
4278 ui.write(_(b'creating ssh peer from handshake results\n'))
4280 ui.write(_(b'creating ssh peer from handshake results\n'))
4279 peer = sshpeer.makepeer(
4281 peer = sshpeer.makepeer(
4280 ui,
4282 ui,
4281 url,
4283 url,
4282 proc,
4284 proc,
4283 stdin,
4285 stdin,
4284 stdout,
4286 stdout,
4285 stderr,
4287 stderr,
4286 autoreadstderr=autoreadstderr,
4288 autoreadstderr=autoreadstderr,
4287 )
4289 )
4288
4290
4289 elif path:
4291 elif path:
4290 # We bypass hg.peer() so we can proxy the sockets.
4292 # We bypass hg.peer() so we can proxy the sockets.
4291 # TODO consider not doing this because we skip
4293 # TODO consider not doing this because we skip
4292 # ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
4294 # ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
4293 u = util.url(path)
4295 u = util.url(path)
4294 if u.scheme != b'http':
4296 if u.scheme != b'http':
4295 raise error.Abort(_(b'only http:// paths are currently supported'))
4297 raise error.Abort(_(b'only http:// paths are currently supported'))
4296
4298
4297 url, authinfo = u.authinfo()
4299 url, authinfo = u.authinfo()
4298 openerargs = {
4300 openerargs = {
4299 'useragent': b'Mercurial debugwireproto',
4301 'useragent': b'Mercurial debugwireproto',
4300 }
4302 }
4301
4303
4302 # Turn pipes/sockets into observers so we can log I/O.
4304 # Turn pipes/sockets into observers so we can log I/O.
4303 if ui.verbose:
4305 if ui.verbose:
4304 openerargs.update(
4306 openerargs.update(
4305 {
4307 {
4306 'loggingfh': ui,
4308 'loggingfh': ui,
4307 'loggingname': b's',
4309 'loggingname': b's',
4308 'loggingopts': {'logdata': True, 'logdataapis': False,},
4310 'loggingopts': {'logdata': True, 'logdataapis': False,},
4309 }
4311 }
4310 )
4312 )
4311
4313
4312 if ui.debugflag:
4314 if ui.debugflag:
4313 openerargs['loggingopts']['logdataapis'] = True
4315 openerargs['loggingopts']['logdataapis'] = True
4314
4316
4315 # Don't send default headers when in raw mode. This allows us to
4317 # Don't send default headers when in raw mode. This allows us to
4316 # bypass most of the behavior of our URL handling code so we can
4318 # bypass most of the behavior of our URL handling code so we can
4317 # have near complete control over what's sent on the wire.
4319 # have near complete control over what's sent on the wire.
4318 if opts[b'peer'] == b'raw':
4320 if opts[b'peer'] == b'raw':
4319 openerargs['sendaccept'] = False
4321 openerargs['sendaccept'] = False
4320
4322
4321 opener = urlmod.opener(ui, authinfo, **openerargs)
4323 opener = urlmod.opener(ui, authinfo, **openerargs)
4322
4324
4323 if opts[b'peer'] == b'http2':
4325 if opts[b'peer'] == b'http2':
4324 ui.write(_(b'creating http peer for wire protocol version 2\n'))
4326 ui.write(_(b'creating http peer for wire protocol version 2\n'))
4325 # We go through makepeer() because we need an API descriptor for
4327 # We go through makepeer() because we need an API descriptor for
4326 # the peer instance to be useful.
4328 # the peer instance to be useful.
4327 with ui.configoverride(
4329 with ui.configoverride(
4328 {(b'experimental', b'httppeer.advertise-v2'): True}
4330 {(b'experimental', b'httppeer.advertise-v2'): True}
4329 ):
4331 ):
4330 if opts[b'nologhandshake']:
4332 if opts[b'nologhandshake']:
4331 ui.pushbuffer()
4333 ui.pushbuffer()
4332
4334
4333 peer = httppeer.makepeer(ui, path, opener=opener)
4335 peer = httppeer.makepeer(ui, path, opener=opener)
4334
4336
4335 if opts[b'nologhandshake']:
4337 if opts[b'nologhandshake']:
4336 ui.popbuffer()
4338 ui.popbuffer()
4337
4339
4338 if not isinstance(peer, httppeer.httpv2peer):
4340 if not isinstance(peer, httppeer.httpv2peer):
4339 raise error.Abort(
4341 raise error.Abort(
4340 _(
4342 _(
4341 b'could not instantiate HTTP peer for '
4343 b'could not instantiate HTTP peer for '
4342 b'wire protocol version 2'
4344 b'wire protocol version 2'
4343 ),
4345 ),
4344 hint=_(
4346 hint=_(
4345 b'the server may not have the feature '
4347 b'the server may not have the feature '
4346 b'enabled or is not allowing this '
4348 b'enabled or is not allowing this '
4347 b'client version'
4349 b'client version'
4348 ),
4350 ),
4349 )
4351 )
4350
4352
4351 elif opts[b'peer'] == b'raw':
4353 elif opts[b'peer'] == b'raw':
4352 ui.write(_(b'using raw connection to peer\n'))
4354 ui.write(_(b'using raw connection to peer\n'))
4353 peer = None
4355 peer = None
4354 elif opts[b'peer']:
4356 elif opts[b'peer']:
4355 raise error.Abort(
4357 raise error.Abort(
4356 _(b'--peer %s not supported with HTTP peers') % opts[b'peer']
4358 _(b'--peer %s not supported with HTTP peers') % opts[b'peer']
4357 )
4359 )
4358 else:
4360 else:
4359 peer = httppeer.makepeer(ui, path, opener=opener)
4361 peer = httppeer.makepeer(ui, path, opener=opener)
4360
4362
4361 # We /could/ populate stdin/stdout with sock.makefile()...
4363 # We /could/ populate stdin/stdout with sock.makefile()...
4362 else:
4364 else:
4363 raise error.Abort(_(b'unsupported connection configuration'))
4365 raise error.Abort(_(b'unsupported connection configuration'))
4364
4366
4365 batchedcommands = None
4367 batchedcommands = None
4366
4368
4367 # Now perform actions based on the parsed wire language instructions.
4369 # Now perform actions based on the parsed wire language instructions.
4368 for action, lines in blocks:
4370 for action, lines in blocks:
4369 if action in (b'raw', b'raw+'):
4371 if action in (b'raw', b'raw+'):
4370 if not stdin:
4372 if not stdin:
4371 raise error.Abort(_(b'cannot call raw/raw+ on this peer'))
4373 raise error.Abort(_(b'cannot call raw/raw+ on this peer'))
4372
4374
4373 # Concatenate the data together.
4375 # Concatenate the data together.
4374 data = b''.join(l.lstrip() for l in lines)
4376 data = b''.join(l.lstrip() for l in lines)
4375 data = stringutil.unescapestr(data)
4377 data = stringutil.unescapestr(data)
4376 stdin.write(data)
4378 stdin.write(data)
4377
4379
4378 if action == b'raw+':
4380 if action == b'raw+':
4379 stdin.flush()
4381 stdin.flush()
4380 elif action == b'flush':
4382 elif action == b'flush':
4381 if not stdin:
4383 if not stdin:
4382 raise error.Abort(_(b'cannot call flush on this peer'))
4384 raise error.Abort(_(b'cannot call flush on this peer'))
4383 stdin.flush()
4385 stdin.flush()
4384 elif action.startswith(b'command'):
4386 elif action.startswith(b'command'):
4385 if not peer:
4387 if not peer:
4386 raise error.Abort(
4388 raise error.Abort(
4387 _(
4389 _(
4388 b'cannot send commands unless peer instance '
4390 b'cannot send commands unless peer instance '
4389 b'is available'
4391 b'is available'
4390 )
4392 )
4391 )
4393 )
4392
4394
4393 command = action.split(b' ', 1)[1]
4395 command = action.split(b' ', 1)[1]
4394
4396
4395 args = {}
4397 args = {}
4396 for line in lines:
4398 for line in lines:
4397 # We need to allow empty values.
4399 # We need to allow empty values.
4398 fields = line.lstrip().split(b' ', 1)
4400 fields = line.lstrip().split(b' ', 1)
4399 if len(fields) == 1:
4401 if len(fields) == 1:
4400 key = fields[0]
4402 key = fields[0]
4401 value = b''
4403 value = b''
4402 else:
4404 else:
4403 key, value = fields
4405 key, value = fields
4404
4406
4405 if value.startswith(b'eval:'):
4407 if value.startswith(b'eval:'):
4406 value = stringutil.evalpythonliteral(value[5:])
4408 value = stringutil.evalpythonliteral(value[5:])
4407 else:
4409 else:
4408 value = stringutil.unescapestr(value)
4410 value = stringutil.unescapestr(value)
4409
4411
4410 args[key] = value
4412 args[key] = value
4411
4413
4412 if batchedcommands is not None:
4414 if batchedcommands is not None:
4413 batchedcommands.append((command, args))
4415 batchedcommands.append((command, args))
4414 continue
4416 continue
4415
4417
4416 ui.status(_(b'sending %s command\n') % command)
4418 ui.status(_(b'sending %s command\n') % command)
4417
4419
4418 if b'PUSHFILE' in args:
4420 if b'PUSHFILE' in args:
4419 with open(args[b'PUSHFILE'], 'rb') as fh:
4421 with open(args[b'PUSHFILE'], 'rb') as fh:
4420 del args[b'PUSHFILE']
4422 del args[b'PUSHFILE']
4421 res, output = peer._callpush(
4423 res, output = peer._callpush(
4422 command, fh, **pycompat.strkwargs(args)
4424 command, fh, **pycompat.strkwargs(args)
4423 )
4425 )
4424 ui.status(_(b'result: %s\n') % stringutil.escapestr(res))
4426 ui.status(_(b'result: %s\n') % stringutil.escapestr(res))
4425 ui.status(
4427 ui.status(
4426 _(b'remote output: %s\n') % stringutil.escapestr(output)
4428 _(b'remote output: %s\n') % stringutil.escapestr(output)
4427 )
4429 )
4428 else:
4430 else:
4429 with peer.commandexecutor() as e:
4431 with peer.commandexecutor() as e:
4430 res = e.callcommand(command, args).result()
4432 res = e.callcommand(command, args).result()
4431
4433
4432 if isinstance(res, wireprotov2peer.commandresponse):
4434 if isinstance(res, wireprotov2peer.commandresponse):
4433 val = res.objects()
4435 val = res.objects()
4434 ui.status(
4436 ui.status(
4435 _(b'response: %s\n')
4437 _(b'response: %s\n')
4436 % stringutil.pprint(val, bprefix=True, indent=2)
4438 % stringutil.pprint(val, bprefix=True, indent=2)
4437 )
4439 )
4438 else:
4440 else:
4439 ui.status(
4441 ui.status(
4440 _(b'response: %s\n')
4442 _(b'response: %s\n')
4441 % stringutil.pprint(res, bprefix=True, indent=2)
4443 % stringutil.pprint(res, bprefix=True, indent=2)
4442 )
4444 )
4443
4445
4444 elif action == b'batchbegin':
4446 elif action == b'batchbegin':
4445 if batchedcommands is not None:
4447 if batchedcommands is not None:
4446 raise error.Abort(_(b'nested batchbegin not allowed'))
4448 raise error.Abort(_(b'nested batchbegin not allowed'))
4447
4449
4448 batchedcommands = []
4450 batchedcommands = []
4449 elif action == b'batchsubmit':
4451 elif action == b'batchsubmit':
4450 # There is a batching API we could go through. But it would be
4452 # There is a batching API we could go through. But it would be
4451 # difficult to normalize requests into function calls. It is easier
4453 # difficult to normalize requests into function calls. It is easier
4452 # to bypass this layer and normalize to commands + args.
4454 # to bypass this layer and normalize to commands + args.
4453 ui.status(
4455 ui.status(
4454 _(b'sending batch with %d sub-commands\n')
4456 _(b'sending batch with %d sub-commands\n')
4455 % len(batchedcommands)
4457 % len(batchedcommands)
4456 )
4458 )
4457 assert peer is not None
4459 assert peer is not None
4458 for i, chunk in enumerate(peer._submitbatch(batchedcommands)):
4460 for i, chunk in enumerate(peer._submitbatch(batchedcommands)):
4459 ui.status(
4461 ui.status(
4460 _(b'response #%d: %s\n') % (i, stringutil.escapestr(chunk))
4462 _(b'response #%d: %s\n') % (i, stringutil.escapestr(chunk))
4461 )
4463 )
4462
4464
4463 batchedcommands = None
4465 batchedcommands = None
4464
4466
4465 elif action.startswith(b'httprequest '):
4467 elif action.startswith(b'httprequest '):
4466 if not opener:
4468 if not opener:
4467 raise error.Abort(
4469 raise error.Abort(
4468 _(b'cannot use httprequest without an HTTP peer')
4470 _(b'cannot use httprequest without an HTTP peer')
4469 )
4471 )
4470
4472
4471 request = action.split(b' ', 2)
4473 request = action.split(b' ', 2)
4472 if len(request) != 3:
4474 if len(request) != 3:
4473 raise error.Abort(
4475 raise error.Abort(
4474 _(
4476 _(
4475 b'invalid httprequest: expected format is '
4477 b'invalid httprequest: expected format is '
4476 b'"httprequest <method> <path>'
4478 b'"httprequest <method> <path>'
4477 )
4479 )
4478 )
4480 )
4479
4481
4480 method, httppath = request[1:]
4482 method, httppath = request[1:]
4481 headers = {}
4483 headers = {}
4482 body = None
4484 body = None
4483 frames = []
4485 frames = []
4484 for line in lines:
4486 for line in lines:
4485 line = line.lstrip()
4487 line = line.lstrip()
4486 m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
4488 m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
4487 if m:
4489 if m:
4488 # Headers need to use native strings.
4490 # Headers need to use native strings.
4489 key = pycompat.strurl(m.group(1))
4491 key = pycompat.strurl(m.group(1))
4490 value = pycompat.strurl(m.group(2))
4492 value = pycompat.strurl(m.group(2))
4491 headers[key] = value
4493 headers[key] = value
4492 continue
4494 continue
4493
4495
4494 if line.startswith(b'BODYFILE '):
4496 if line.startswith(b'BODYFILE '):
4495 with open(line.split(b' ', 1), b'rb') as fh:
4497 with open(line.split(b' ', 1), b'rb') as fh:
4496 body = fh.read()
4498 body = fh.read()
4497 elif line.startswith(b'frame '):
4499 elif line.startswith(b'frame '):
4498 frame = wireprotoframing.makeframefromhumanstring(
4500 frame = wireprotoframing.makeframefromhumanstring(
4499 line[len(b'frame ') :]
4501 line[len(b'frame ') :]
4500 )
4502 )
4501
4503
4502 frames.append(frame)
4504 frames.append(frame)
4503 else:
4505 else:
4504 raise error.Abort(
4506 raise error.Abort(
4505 _(b'unknown argument to httprequest: %s') % line
4507 _(b'unknown argument to httprequest: %s') % line
4506 )
4508 )
4507
4509
4508 url = path + httppath
4510 url = path + httppath
4509
4511
4510 if frames:
4512 if frames:
4511 body = b''.join(bytes(f) for f in frames)
4513 body = b''.join(bytes(f) for f in frames)
4512
4514
4513 req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
4515 req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
4514
4516
4515 # urllib.Request insists on using has_data() as a proxy for
4517 # urllib.Request insists on using has_data() as a proxy for
4516 # determining the request method. Override that to use our
4518 # determining the request method. Override that to use our
4517 # explicitly requested method.
4519 # explicitly requested method.
4518 req.get_method = lambda: pycompat.sysstr(method)
4520 req.get_method = lambda: pycompat.sysstr(method)
4519
4521
4520 try:
4522 try:
4521 res = opener.open(req)
4523 res = opener.open(req)
4522 body = res.read()
4524 body = res.read()
4523 except util.urlerr.urlerror as e:
4525 except util.urlerr.urlerror as e:
4524 # read() method must be called, but only exists in Python 2
4526 # read() method must be called, but only exists in Python 2
4525 getattr(e, 'read', lambda: None)()
4527 getattr(e, 'read', lambda: None)()
4526 continue
4528 continue
4527
4529
4528 ct = res.headers.get('Content-Type')
4530 ct = res.headers.get('Content-Type')
4529 if ct == 'application/mercurial-cbor':
4531 if ct == 'application/mercurial-cbor':
4530 ui.write(
4532 ui.write(
4531 _(b'cbor> %s\n')
4533 _(b'cbor> %s\n')
4532 % stringutil.pprint(
4534 % stringutil.pprint(
4533 cborutil.decodeall(body), bprefix=True, indent=2
4535 cborutil.decodeall(body), bprefix=True, indent=2
4534 )
4536 )
4535 )
4537 )
4536
4538
4537 elif action == b'close':
4539 elif action == b'close':
4538 assert peer is not None
4540 assert peer is not None
4539 peer.close()
4541 peer.close()
4540 elif action == b'readavailable':
4542 elif action == b'readavailable':
4541 if not stdout or not stderr:
4543 if not stdout or not stderr:
4542 raise error.Abort(
4544 raise error.Abort(
4543 _(b'readavailable not available on this peer')
4545 _(b'readavailable not available on this peer')
4544 )
4546 )
4545
4547
4546 stdin.close()
4548 stdin.close()
4547 stdout.read()
4549 stdout.read()
4548 stderr.read()
4550 stderr.read()
4549
4551
4550 elif action == b'readline':
4552 elif action == b'readline':
4551 if not stdout:
4553 if not stdout:
4552 raise error.Abort(_(b'readline not available on this peer'))
4554 raise error.Abort(_(b'readline not available on this peer'))
4553 stdout.readline()
4555 stdout.readline()
4554 elif action == b'ereadline':
4556 elif action == b'ereadline':
4555 if not stderr:
4557 if not stderr:
4556 raise error.Abort(_(b'ereadline not available on this peer'))
4558 raise error.Abort(_(b'ereadline not available on this peer'))
4557 stderr.readline()
4559 stderr.readline()
4558 elif action.startswith(b'read '):
4560 elif action.startswith(b'read '):
4559 count = int(action.split(b' ', 1)[1])
4561 count = int(action.split(b' ', 1)[1])
4560 if not stdout:
4562 if not stdout:
4561 raise error.Abort(_(b'read not available on this peer'))
4563 raise error.Abort(_(b'read not available on this peer'))
4562 stdout.read(count)
4564 stdout.read(count)
4563 elif action.startswith(b'eread '):
4565 elif action.startswith(b'eread '):
4564 count = int(action.split(b' ', 1)[1])
4566 count = int(action.split(b' ', 1)[1])
4565 if not stderr:
4567 if not stderr:
4566 raise error.Abort(_(b'eread not available on this peer'))
4568 raise error.Abort(_(b'eread not available on this peer'))
4567 stderr.read(count)
4569 stderr.read(count)
4568 else:
4570 else:
4569 raise error.Abort(_(b'unknown action: %s') % action)
4571 raise error.Abort(_(b'unknown action: %s') % action)
4570
4572
4571 if batchedcommands is not None:
4573 if batchedcommands is not None:
4572 raise error.Abort(_(b'unclosed "batchbegin" request'))
4574 raise error.Abort(_(b'unclosed "batchbegin" request'))
4573
4575
4574 if peer:
4576 if peer:
4575 peer.close()
4577 peer.close()
4576
4578
4577 if proc:
4579 if proc:
4578 proc.kill()
4580 proc.kill()
@@ -1,534 +1,539 b''
1 # registrar.py - utilities to register function for specific purpose
1 # registrar.py - utilities to register function for specific purpose
2 #
2 #
3 # Copyright FUJIWARA Katsunori <foozy@lares.dti.ne.jp> and others
3 # Copyright FUJIWARA Katsunori <foozy@lares.dti.ne.jp> 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 from . import (
10 from . import (
11 configitems,
11 configitems,
12 error,
12 error,
13 pycompat,
13 pycompat,
14 util,
14 util,
15 )
15 )
16
16
17 # unlike the other registered items, config options are neither functions or
17 # unlike the other registered items, config options are neither functions or
18 # classes. Registering the option is just small function call.
18 # classes. Registering the option is just small function call.
19 #
19 #
20 # We still add the official API to the registrar module for consistency with
20 # We still add the official API to the registrar module for consistency with
21 # the other items extensions want might to register.
21 # the other items extensions want might to register.
22 configitem = configitems.getitemregister
22 configitem = configitems.getitemregister
23
23
24
24
25 class _funcregistrarbase(object):
25 class _funcregistrarbase(object):
26 """Base of decorator to register a function for specific purpose
26 """Base of decorator to register a function for specific purpose
27
27
28 This decorator stores decorated functions into own dict 'table'.
28 This decorator stores decorated functions into own dict 'table'.
29
29
30 The least derived class can be defined by overriding 'formatdoc',
30 The least derived class can be defined by overriding 'formatdoc',
31 for example::
31 for example::
32
32
33 class keyword(_funcregistrarbase):
33 class keyword(_funcregistrarbase):
34 _docformat = ":%s: %s"
34 _docformat = ":%s: %s"
35
35
36 This should be used as below:
36 This should be used as below:
37
37
38 keyword = registrar.keyword()
38 keyword = registrar.keyword()
39
39
40 @keyword('bar')
40 @keyword('bar')
41 def barfunc(*args, **kwargs):
41 def barfunc(*args, **kwargs):
42 '''Explanation of bar keyword ....
42 '''Explanation of bar keyword ....
43 '''
43 '''
44 pass
44 pass
45
45
46 In this case:
46 In this case:
47
47
48 - 'barfunc' is stored as 'bar' in '_table' of an instance 'keyword' above
48 - 'barfunc' is stored as 'bar' in '_table' of an instance 'keyword' above
49 - 'barfunc.__doc__' becomes ":bar: Explanation of bar keyword"
49 - 'barfunc.__doc__' becomes ":bar: Explanation of bar keyword"
50 """
50 """
51
51
52 def __init__(self, table=None):
52 def __init__(self, table=None):
53 if table is None:
53 if table is None:
54 self._table = {}
54 self._table = {}
55 else:
55 else:
56 self._table = table
56 self._table = table
57
57
58 def __call__(self, decl, *args, **kwargs):
58 def __call__(self, decl, *args, **kwargs):
59 return lambda func: self._doregister(func, decl, *args, **kwargs)
59 return lambda func: self._doregister(func, decl, *args, **kwargs)
60
60
61 def _doregister(self, func, decl, *args, **kwargs):
61 def _doregister(self, func, decl, *args, **kwargs):
62 name = self._getname(decl)
62 name = self._getname(decl)
63
63
64 if name in self._table:
64 if name in self._table:
65 msg = b'duplicate registration for name: "%s"' % name
65 msg = b'duplicate registration for name: "%s"' % name
66 raise error.ProgrammingError(msg)
66 raise error.ProgrammingError(msg)
67
67
68 if func.__doc__ and not util.safehasattr(func, '_origdoc'):
68 if func.__doc__ and not util.safehasattr(func, '_origdoc'):
69 func._origdoc = func.__doc__.strip()
69 func._origdoc = func.__doc__.strip()
70 doc = pycompat.sysbytes(func._origdoc)
70 doc = pycompat.sysbytes(func._origdoc)
71 func.__doc__ = pycompat.sysstr(self._formatdoc(decl, doc))
71 func.__doc__ = pycompat.sysstr(self._formatdoc(decl, doc))
72
72
73 self._table[name] = func
73 self._table[name] = func
74 self._extrasetup(name, func, *args, **kwargs)
74 self._extrasetup(name, func, *args, **kwargs)
75
75
76 return func
76 return func
77
77
78 def _merge(self, registrarbase):
78 def _merge(self, registrarbase):
79 """Merge the entries of the given registrar object into this one.
79 """Merge the entries of the given registrar object into this one.
80
80
81 The other registrar object must not contain any entries already in the
81 The other registrar object must not contain any entries already in the
82 current one, or a ProgrammmingError is raised. Additionally, the types
82 current one, or a ProgrammmingError is raised. Additionally, the types
83 of the two registrars must match.
83 of the two registrars must match.
84 """
84 """
85 if not isinstance(registrarbase, type(self)):
85 if not isinstance(registrarbase, type(self)):
86 msg = b"cannot merge different types of registrar"
86 msg = b"cannot merge different types of registrar"
87 raise error.ProgrammingError(msg)
87 raise error.ProgrammingError(msg)
88
88
89 dups = set(registrarbase._table).intersection(self._table)
89 dups = set(registrarbase._table).intersection(self._table)
90
90
91 if dups:
91 if dups:
92 msg = b'duplicate registration for names: "%s"' % b'", "'.join(dups)
92 msg = b'duplicate registration for names: "%s"' % b'", "'.join(dups)
93 raise error.ProgrammingError(msg)
93 raise error.ProgrammingError(msg)
94
94
95 self._table.update(registrarbase._table)
95 self._table.update(registrarbase._table)
96
96
97 def _parsefuncdecl(self, decl):
97 def _parsefuncdecl(self, decl):
98 """Parse function declaration and return the name of function in it
98 """Parse function declaration and return the name of function in it
99 """
99 """
100 i = decl.find(b'(')
100 i = decl.find(b'(')
101 if i >= 0:
101 if i >= 0:
102 return decl[:i]
102 return decl[:i]
103 else:
103 else:
104 return decl
104 return decl
105
105
106 def _getname(self, decl):
106 def _getname(self, decl):
107 """Return the name of the registered function from decl
107 """Return the name of the registered function from decl
108
108
109 Derived class should override this, if it allows more
109 Derived class should override this, if it allows more
110 descriptive 'decl' string than just a name.
110 descriptive 'decl' string than just a name.
111 """
111 """
112 return decl
112 return decl
113
113
114 _docformat = None
114 _docformat = None
115
115
116 def _formatdoc(self, decl, doc):
116 def _formatdoc(self, decl, doc):
117 """Return formatted document of the registered function for help
117 """Return formatted document of the registered function for help
118
118
119 'doc' is '__doc__.strip()' of the registered function.
119 'doc' is '__doc__.strip()' of the registered function.
120 """
120 """
121 return self._docformat % (decl, doc)
121 return self._docformat % (decl, doc)
122
122
123 def _extrasetup(self, name, func):
123 def _extrasetup(self, name, func):
124 """Execute extra setup for registered function, if needed
124 """Execute extra setup for registered function, if needed
125 """
125 """
126
126
127
127
128 class command(_funcregistrarbase):
128 class command(_funcregistrarbase):
129 """Decorator to register a command function to table
129 """Decorator to register a command function to table
130
130
131 This class receives a command table as its argument. The table should
131 This class receives a command table as its argument. The table should
132 be a dict.
132 be a dict.
133
133
134 The created object can be used as a decorator for adding commands to
134 The created object can be used as a decorator for adding commands to
135 that command table. This accepts multiple arguments to define a command.
135 that command table. This accepts multiple arguments to define a command.
136
136
137 The first argument is the command name (as bytes).
137 The first argument is the command name (as bytes).
138
138
139 The `options` keyword argument is an iterable of tuples defining command
139 The `options` keyword argument is an iterable of tuples defining command
140 arguments. See ``mercurial.fancyopts.fancyopts()`` for the format of each
140 arguments. See ``mercurial.fancyopts.fancyopts()`` for the format of each
141 tuple.
141 tuple.
142
142
143 The `synopsis` argument defines a short, one line summary of how to use the
143 The `synopsis` argument defines a short, one line summary of how to use the
144 command. This shows up in the help output.
144 command. This shows up in the help output.
145
145
146 There are three arguments that control what repository (if any) is found
146 There are three arguments that control what repository (if any) is found
147 and passed to the decorated function: `norepo`, `optionalrepo`, and
147 and passed to the decorated function: `norepo`, `optionalrepo`, and
148 `inferrepo`.
148 `inferrepo`.
149
149
150 The `norepo` argument defines whether the command does not require a
150 The `norepo` argument defines whether the command does not require a
151 local repository. Most commands operate against a repository, thus the
151 local repository. Most commands operate against a repository, thus the
152 default is False. When True, no repository will be passed.
152 default is False. When True, no repository will be passed.
153
153
154 The `optionalrepo` argument defines whether the command optionally requires
154 The `optionalrepo` argument defines whether the command optionally requires
155 a local repository. If no repository can be found, None will be passed
155 a local repository. If no repository can be found, None will be passed
156 to the decorated function.
156 to the decorated function.
157
157
158 The `inferrepo` argument defines whether to try to find a repository from
158 The `inferrepo` argument defines whether to try to find a repository from
159 the command line arguments. If True, arguments will be examined for
159 the command line arguments. If True, arguments will be examined for
160 potential repository locations. See ``findrepo()``. If a repository is
160 potential repository locations. See ``findrepo()``. If a repository is
161 found, it will be used and passed to the decorated function.
161 found, it will be used and passed to the decorated function.
162
162
163 The `intents` argument defines a set of intended actions or capabilities
163 The `intents` argument defines a set of intended actions or capabilities
164 the command is taking. These intents can be used to affect the construction
164 the command is taking. These intents can be used to affect the construction
165 of the repository object passed to the command. For example, commands
165 of the repository object passed to the command. For example, commands
166 declaring that they are read-only could receive a repository that doesn't
166 declaring that they are read-only could receive a repository that doesn't
167 have any methods allowing repository mutation. Other intents could be used
167 have any methods allowing repository mutation. Other intents could be used
168 to prevent the command from running if the requested intent could not be
168 to prevent the command from running if the requested intent could not be
169 fulfilled.
169 fulfilled.
170
170
171 If `helpcategory` is set (usually to one of the constants in the help
171 If `helpcategory` is set (usually to one of the constants in the help
172 module), the command will be displayed under that category in the help's
172 module), the command will be displayed under that category in the help's
173 list of commands.
173 list of commands.
174
174
175 The following intents are defined:
175 The following intents are defined:
176
176
177 readonly
177 readonly
178 The command is read-only
178 The command is read-only
179
179
180 The signature of the decorated function looks like this:
180 The signature of the decorated function looks like this:
181 def cmd(ui[, repo] [, <args>] [, <options>])
181 def cmd(ui[, repo] [, <args>] [, <options>])
182
182
183 `repo` is required if `norepo` is False.
183 `repo` is required if `norepo` is False.
184 `<args>` are positional args (or `*args`) arguments, of non-option
184 `<args>` are positional args (or `*args`) arguments, of non-option
185 arguments from the command line.
185 arguments from the command line.
186 `<options>` are keyword arguments (or `**options`) of option arguments
186 `<options>` are keyword arguments (or `**options`) of option arguments
187 from the command line.
187 from the command line.
188
188
189 See the WritingExtensions and MercurialApi documentation for more exhaustive
189 See the WritingExtensions and MercurialApi documentation for more exhaustive
190 descriptions and examples.
190 descriptions and examples.
191 """
191 """
192
192
193 # Command categories for grouping them in help output.
193 # Command categories for grouping them in help output.
194 # These can also be specified for aliases, like:
194 # These can also be specified for aliases, like:
195 # [alias]
195 # [alias]
196 # myalias = something
196 # myalias = something
197 # myalias:category = repo
197 # myalias:category = repo
198 CATEGORY_REPO_CREATION = b'repo'
198 CATEGORY_REPO_CREATION = b'repo'
199 CATEGORY_REMOTE_REPO_MANAGEMENT = b'remote'
199 CATEGORY_REMOTE_REPO_MANAGEMENT = b'remote'
200 CATEGORY_COMMITTING = b'commit'
200 CATEGORY_COMMITTING = b'commit'
201 CATEGORY_CHANGE_MANAGEMENT = b'management'
201 CATEGORY_CHANGE_MANAGEMENT = b'management'
202 CATEGORY_CHANGE_ORGANIZATION = b'organization'
202 CATEGORY_CHANGE_ORGANIZATION = b'organization'
203 CATEGORY_FILE_CONTENTS = b'files'
203 CATEGORY_FILE_CONTENTS = b'files'
204 CATEGORY_CHANGE_NAVIGATION = b'navigation'
204 CATEGORY_CHANGE_NAVIGATION = b'navigation'
205 CATEGORY_WORKING_DIRECTORY = b'wdir'
205 CATEGORY_WORKING_DIRECTORY = b'wdir'
206 CATEGORY_IMPORT_EXPORT = b'import'
206 CATEGORY_IMPORT_EXPORT = b'import'
207 CATEGORY_MAINTENANCE = b'maintenance'
207 CATEGORY_MAINTENANCE = b'maintenance'
208 CATEGORY_HELP = b'help'
208 CATEGORY_HELP = b'help'
209 CATEGORY_MISC = b'misc'
209 CATEGORY_MISC = b'misc'
210 CATEGORY_NONE = b'none'
210 CATEGORY_NONE = b'none'
211
211
212 def _doregister(
212 def _doregister(
213 self,
213 self,
214 func,
214 func,
215 name,
215 name,
216 options=(),
216 options=(),
217 synopsis=None,
217 synopsis=None,
218 norepo=False,
218 norepo=False,
219 optionalrepo=False,
219 optionalrepo=False,
220 inferrepo=False,
220 inferrepo=False,
221 intents=None,
221 intents=None,
222 helpcategory=None,
222 helpcategory=None,
223 helpbasic=False,
223 helpbasic=False,
224 ):
224 ):
225 func.norepo = norepo
225 func.norepo = norepo
226 func.optionalrepo = optionalrepo
226 func.optionalrepo = optionalrepo
227 func.inferrepo = inferrepo
227 func.inferrepo = inferrepo
228 func.intents = intents or set()
228 func.intents = intents or set()
229 func.helpcategory = helpcategory
229 func.helpcategory = helpcategory
230 func.helpbasic = helpbasic
230 func.helpbasic = helpbasic
231 if synopsis:
231 if synopsis:
232 self._table[name] = func, list(options), synopsis
232 self._table[name] = func, list(options), synopsis
233 else:
233 else:
234 self._table[name] = func, list(options)
234 self._table[name] = func, list(options)
235 return func
235 return func
236
236
237 def rename(self, old, new):
238 """rename a command. Used to add aliases, debugstrip ->
239 debugstrip|strip
240 """
241 self._table[new] = self._table.pop(old)
237
242
238 INTENT_READONLY = b'readonly'
243 INTENT_READONLY = b'readonly'
239
244
240
245
241 class revsetpredicate(_funcregistrarbase):
246 class revsetpredicate(_funcregistrarbase):
242 """Decorator to register revset predicate
247 """Decorator to register revset predicate
243
248
244 Usage::
249 Usage::
245
250
246 revsetpredicate = registrar.revsetpredicate()
251 revsetpredicate = registrar.revsetpredicate()
247
252
248 @revsetpredicate('mypredicate(arg1, arg2[, arg3])')
253 @revsetpredicate('mypredicate(arg1, arg2[, arg3])')
249 def mypredicatefunc(repo, subset, x):
254 def mypredicatefunc(repo, subset, x):
250 '''Explanation of this revset predicate ....
255 '''Explanation of this revset predicate ....
251 '''
256 '''
252 pass
257 pass
253
258
254 The first string argument is used also in online help.
259 The first string argument is used also in online help.
255
260
256 Optional argument 'safe' indicates whether a predicate is safe for
261 Optional argument 'safe' indicates whether a predicate is safe for
257 DoS attack (False by default).
262 DoS attack (False by default).
258
263
259 Optional argument 'takeorder' indicates whether a predicate function
264 Optional argument 'takeorder' indicates whether a predicate function
260 takes ordering policy as the last argument.
265 takes ordering policy as the last argument.
261
266
262 Optional argument 'weight' indicates the estimated run-time cost, useful
267 Optional argument 'weight' indicates the estimated run-time cost, useful
263 for static optimization, default is 1. Higher weight means more expensive.
268 for static optimization, default is 1. Higher weight means more expensive.
264 Usually, revsets that are fast and return only one revision has a weight of
269 Usually, revsets that are fast and return only one revision has a weight of
265 0.5 (ex. a symbol); revsets with O(changelog) complexity and read only the
270 0.5 (ex. a symbol); revsets with O(changelog) complexity and read only the
266 changelog have weight 10 (ex. author); revsets reading manifest deltas have
271 changelog have weight 10 (ex. author); revsets reading manifest deltas have
267 weight 30 (ex. adds); revset reading manifest contents have weight 100
272 weight 30 (ex. adds); revset reading manifest contents have weight 100
268 (ex. contains). Note: those values are flexible. If the revset has a
273 (ex. contains). Note: those values are flexible. If the revset has a
269 same big-O time complexity as 'contains', but with a smaller constant, it
274 same big-O time complexity as 'contains', but with a smaller constant, it
270 might have a weight of 90.
275 might have a weight of 90.
271
276
272 'revsetpredicate' instance in example above can be used to
277 'revsetpredicate' instance in example above can be used to
273 decorate multiple functions.
278 decorate multiple functions.
274
279
275 Decorated functions are registered automatically at loading
280 Decorated functions are registered automatically at loading
276 extension, if an instance named as 'revsetpredicate' is used for
281 extension, if an instance named as 'revsetpredicate' is used for
277 decorating in extension.
282 decorating in extension.
278
283
279 Otherwise, explicit 'revset.loadpredicate()' is needed.
284 Otherwise, explicit 'revset.loadpredicate()' is needed.
280 """
285 """
281
286
282 _getname = _funcregistrarbase._parsefuncdecl
287 _getname = _funcregistrarbase._parsefuncdecl
283 _docformat = b"``%s``\n %s"
288 _docformat = b"``%s``\n %s"
284
289
285 def _extrasetup(self, name, func, safe=False, takeorder=False, weight=1):
290 def _extrasetup(self, name, func, safe=False, takeorder=False, weight=1):
286 func._safe = safe
291 func._safe = safe
287 func._takeorder = takeorder
292 func._takeorder = takeorder
288 func._weight = weight
293 func._weight = weight
289
294
290
295
291 class filesetpredicate(_funcregistrarbase):
296 class filesetpredicate(_funcregistrarbase):
292 """Decorator to register fileset predicate
297 """Decorator to register fileset predicate
293
298
294 Usage::
299 Usage::
295
300
296 filesetpredicate = registrar.filesetpredicate()
301 filesetpredicate = registrar.filesetpredicate()
297
302
298 @filesetpredicate('mypredicate()')
303 @filesetpredicate('mypredicate()')
299 def mypredicatefunc(mctx, x):
304 def mypredicatefunc(mctx, x):
300 '''Explanation of this fileset predicate ....
305 '''Explanation of this fileset predicate ....
301 '''
306 '''
302 pass
307 pass
303
308
304 The first string argument is used also in online help.
309 The first string argument is used also in online help.
305
310
306 Optional argument 'callstatus' indicates whether a predicate
311 Optional argument 'callstatus' indicates whether a predicate
307 implies 'matchctx.status()' at runtime or not (False, by
312 implies 'matchctx.status()' at runtime or not (False, by
308 default).
313 default).
309
314
310 Optional argument 'weight' indicates the estimated run-time cost, useful
315 Optional argument 'weight' indicates the estimated run-time cost, useful
311 for static optimization, default is 1. Higher weight means more expensive.
316 for static optimization, default is 1. Higher weight means more expensive.
312 There are predefined weights in the 'filesetlang' module.
317 There are predefined weights in the 'filesetlang' module.
313
318
314 ====== =============================================================
319 ====== =============================================================
315 Weight Description and examples
320 Weight Description and examples
316 ====== =============================================================
321 ====== =============================================================
317 0.5 basic match patterns (e.g. a symbol)
322 0.5 basic match patterns (e.g. a symbol)
318 10 computing status (e.g. added()) or accessing a few files
323 10 computing status (e.g. added()) or accessing a few files
319 30 reading file content for each (e.g. grep())
324 30 reading file content for each (e.g. grep())
320 50 scanning working directory (ignored())
325 50 scanning working directory (ignored())
321 ====== =============================================================
326 ====== =============================================================
322
327
323 'filesetpredicate' instance in example above can be used to
328 'filesetpredicate' instance in example above can be used to
324 decorate multiple functions.
329 decorate multiple functions.
325
330
326 Decorated functions are registered automatically at loading
331 Decorated functions are registered automatically at loading
327 extension, if an instance named as 'filesetpredicate' is used for
332 extension, if an instance named as 'filesetpredicate' is used for
328 decorating in extension.
333 decorating in extension.
329
334
330 Otherwise, explicit 'fileset.loadpredicate()' is needed.
335 Otherwise, explicit 'fileset.loadpredicate()' is needed.
331 """
336 """
332
337
333 _getname = _funcregistrarbase._parsefuncdecl
338 _getname = _funcregistrarbase._parsefuncdecl
334 _docformat = b"``%s``\n %s"
339 _docformat = b"``%s``\n %s"
335
340
336 def _extrasetup(self, name, func, callstatus=False, weight=1):
341 def _extrasetup(self, name, func, callstatus=False, weight=1):
337 func._callstatus = callstatus
342 func._callstatus = callstatus
338 func._weight = weight
343 func._weight = weight
339
344
340
345
341 class _templateregistrarbase(_funcregistrarbase):
346 class _templateregistrarbase(_funcregistrarbase):
342 """Base of decorator to register functions as template specific one
347 """Base of decorator to register functions as template specific one
343 """
348 """
344
349
345 _docformat = b":%s: %s"
350 _docformat = b":%s: %s"
346
351
347
352
348 class templatekeyword(_templateregistrarbase):
353 class templatekeyword(_templateregistrarbase):
349 """Decorator to register template keyword
354 """Decorator to register template keyword
350
355
351 Usage::
356 Usage::
352
357
353 templatekeyword = registrar.templatekeyword()
358 templatekeyword = registrar.templatekeyword()
354
359
355 # new API (since Mercurial 4.6)
360 # new API (since Mercurial 4.6)
356 @templatekeyword('mykeyword', requires={'repo', 'ctx'})
361 @templatekeyword('mykeyword', requires={'repo', 'ctx'})
357 def mykeywordfunc(context, mapping):
362 def mykeywordfunc(context, mapping):
358 '''Explanation of this template keyword ....
363 '''Explanation of this template keyword ....
359 '''
364 '''
360 pass
365 pass
361
366
362 The first string argument is used also in online help.
367 The first string argument is used also in online help.
363
368
364 Optional argument 'requires' should be a collection of resource names
369 Optional argument 'requires' should be a collection of resource names
365 which the template keyword depends on.
370 which the template keyword depends on.
366
371
367 'templatekeyword' instance in example above can be used to
372 'templatekeyword' instance in example above can be used to
368 decorate multiple functions.
373 decorate multiple functions.
369
374
370 Decorated functions are registered automatically at loading
375 Decorated functions are registered automatically at loading
371 extension, if an instance named as 'templatekeyword' is used for
376 extension, if an instance named as 'templatekeyword' is used for
372 decorating in extension.
377 decorating in extension.
373
378
374 Otherwise, explicit 'templatekw.loadkeyword()' is needed.
379 Otherwise, explicit 'templatekw.loadkeyword()' is needed.
375 """
380 """
376
381
377 def _extrasetup(self, name, func, requires=()):
382 def _extrasetup(self, name, func, requires=()):
378 func._requires = requires
383 func._requires = requires
379
384
380
385
381 class templatefilter(_templateregistrarbase):
386 class templatefilter(_templateregistrarbase):
382 """Decorator to register template filer
387 """Decorator to register template filer
383
388
384 Usage::
389 Usage::
385
390
386 templatefilter = registrar.templatefilter()
391 templatefilter = registrar.templatefilter()
387
392
388 @templatefilter('myfilter', intype=bytes)
393 @templatefilter('myfilter', intype=bytes)
389 def myfilterfunc(text):
394 def myfilterfunc(text):
390 '''Explanation of this template filter ....
395 '''Explanation of this template filter ....
391 '''
396 '''
392 pass
397 pass
393
398
394 The first string argument is used also in online help.
399 The first string argument is used also in online help.
395
400
396 Optional argument 'intype' defines the type of the input argument,
401 Optional argument 'intype' defines the type of the input argument,
397 which should be (bytes, int, templateutil.date, or None for any.)
402 which should be (bytes, int, templateutil.date, or None for any.)
398
403
399 'templatefilter' instance in example above can be used to
404 'templatefilter' instance in example above can be used to
400 decorate multiple functions.
405 decorate multiple functions.
401
406
402 Decorated functions are registered automatically at loading
407 Decorated functions are registered automatically at loading
403 extension, if an instance named as 'templatefilter' is used for
408 extension, if an instance named as 'templatefilter' is used for
404 decorating in extension.
409 decorating in extension.
405
410
406 Otherwise, explicit 'templatefilters.loadkeyword()' is needed.
411 Otherwise, explicit 'templatefilters.loadkeyword()' is needed.
407 """
412 """
408
413
409 def _extrasetup(self, name, func, intype=None):
414 def _extrasetup(self, name, func, intype=None):
410 func._intype = intype
415 func._intype = intype
411
416
412
417
413 class templatefunc(_templateregistrarbase):
418 class templatefunc(_templateregistrarbase):
414 """Decorator to register template function
419 """Decorator to register template function
415
420
416 Usage::
421 Usage::
417
422
418 templatefunc = registrar.templatefunc()
423 templatefunc = registrar.templatefunc()
419
424
420 @templatefunc('myfunc(arg1, arg2[, arg3])', argspec='arg1 arg2 arg3',
425 @templatefunc('myfunc(arg1, arg2[, arg3])', argspec='arg1 arg2 arg3',
421 requires={'ctx'})
426 requires={'ctx'})
422 def myfuncfunc(context, mapping, args):
427 def myfuncfunc(context, mapping, args):
423 '''Explanation of this template function ....
428 '''Explanation of this template function ....
424 '''
429 '''
425 pass
430 pass
426
431
427 The first string argument is used also in online help.
432 The first string argument is used also in online help.
428
433
429 If optional 'argspec' is defined, the function will receive 'args' as
434 If optional 'argspec' is defined, the function will receive 'args' as
430 a dict of named arguments. Otherwise 'args' is a list of positional
435 a dict of named arguments. Otherwise 'args' is a list of positional
431 arguments.
436 arguments.
432
437
433 Optional argument 'requires' should be a collection of resource names
438 Optional argument 'requires' should be a collection of resource names
434 which the template function depends on.
439 which the template function depends on.
435
440
436 'templatefunc' instance in example above can be used to
441 'templatefunc' instance in example above can be used to
437 decorate multiple functions.
442 decorate multiple functions.
438
443
439 Decorated functions are registered automatically at loading
444 Decorated functions are registered automatically at loading
440 extension, if an instance named as 'templatefunc' is used for
445 extension, if an instance named as 'templatefunc' is used for
441 decorating in extension.
446 decorating in extension.
442
447
443 Otherwise, explicit 'templatefuncs.loadfunction()' is needed.
448 Otherwise, explicit 'templatefuncs.loadfunction()' is needed.
444 """
449 """
445
450
446 _getname = _funcregistrarbase._parsefuncdecl
451 _getname = _funcregistrarbase._parsefuncdecl
447
452
448 def _extrasetup(self, name, func, argspec=None, requires=()):
453 def _extrasetup(self, name, func, argspec=None, requires=()):
449 func._argspec = argspec
454 func._argspec = argspec
450 func._requires = requires
455 func._requires = requires
451
456
452
457
453 class internalmerge(_funcregistrarbase):
458 class internalmerge(_funcregistrarbase):
454 """Decorator to register in-process merge tool
459 """Decorator to register in-process merge tool
455
460
456 Usage::
461 Usage::
457
462
458 internalmerge = registrar.internalmerge()
463 internalmerge = registrar.internalmerge()
459
464
460 @internalmerge('mymerge', internalmerge.mergeonly,
465 @internalmerge('mymerge', internalmerge.mergeonly,
461 onfailure=None, precheck=None,
466 onfailure=None, precheck=None,
462 binary=False, symlink=False):
467 binary=False, symlink=False):
463 def mymergefunc(repo, mynode, orig, fcd, fco, fca,
468 def mymergefunc(repo, mynode, orig, fcd, fco, fca,
464 toolconf, files, labels=None):
469 toolconf, files, labels=None):
465 '''Explanation of this internal merge tool ....
470 '''Explanation of this internal merge tool ....
466 '''
471 '''
467 return 1, False # means "conflicted", "no deletion needed"
472 return 1, False # means "conflicted", "no deletion needed"
468
473
469 The first string argument is used to compose actual merge tool name,
474 The first string argument is used to compose actual merge tool name,
470 ":name" and "internal:name" (the latter is historical one).
475 ":name" and "internal:name" (the latter is historical one).
471
476
472 The second argument is one of merge types below:
477 The second argument is one of merge types below:
473
478
474 ========== ======== ======== =========
479 ========== ======== ======== =========
475 merge type precheck premerge fullmerge
480 merge type precheck premerge fullmerge
476 ========== ======== ======== =========
481 ========== ======== ======== =========
477 nomerge x x x
482 nomerge x x x
478 mergeonly o x o
483 mergeonly o x o
479 fullmerge o o o
484 fullmerge o o o
480 ========== ======== ======== =========
485 ========== ======== ======== =========
481
486
482 Optional argument 'onfailure' is the format of warning message
487 Optional argument 'onfailure' is the format of warning message
483 to be used at failure of merging (target filename is specified
488 to be used at failure of merging (target filename is specified
484 at formatting). Or, None or so, if warning message should be
489 at formatting). Or, None or so, if warning message should be
485 suppressed.
490 suppressed.
486
491
487 Optional argument 'precheck' is the function to be used
492 Optional argument 'precheck' is the function to be used
488 before actual invocation of internal merge tool itself.
493 before actual invocation of internal merge tool itself.
489 It takes as same arguments as internal merge tool does, other than
494 It takes as same arguments as internal merge tool does, other than
490 'files' and 'labels'. If it returns false value, merging is aborted
495 'files' and 'labels'. If it returns false value, merging is aborted
491 immediately (and file is marked as "unresolved").
496 immediately (and file is marked as "unresolved").
492
497
493 Optional argument 'binary' is a binary files capability of internal
498 Optional argument 'binary' is a binary files capability of internal
494 merge tool. 'nomerge' merge type implies binary=True.
499 merge tool. 'nomerge' merge type implies binary=True.
495
500
496 Optional argument 'symlink' is a symlinks capability of inetrnal
501 Optional argument 'symlink' is a symlinks capability of inetrnal
497 merge function. 'nomerge' merge type implies symlink=True.
502 merge function. 'nomerge' merge type implies symlink=True.
498
503
499 'internalmerge' instance in example above can be used to
504 'internalmerge' instance in example above can be used to
500 decorate multiple functions.
505 decorate multiple functions.
501
506
502 Decorated functions are registered automatically at loading
507 Decorated functions are registered automatically at loading
503 extension, if an instance named as 'internalmerge' is used for
508 extension, if an instance named as 'internalmerge' is used for
504 decorating in extension.
509 decorating in extension.
505
510
506 Otherwise, explicit 'filemerge.loadinternalmerge()' is needed.
511 Otherwise, explicit 'filemerge.loadinternalmerge()' is needed.
507 """
512 """
508
513
509 _docformat = b"``:%s``\n %s"
514 _docformat = b"``:%s``\n %s"
510
515
511 # merge type definitions:
516 # merge type definitions:
512 nomerge = None
517 nomerge = None
513 mergeonly = b'mergeonly' # just the full merge, no premerge
518 mergeonly = b'mergeonly' # just the full merge, no premerge
514 fullmerge = b'fullmerge' # both premerge and merge
519 fullmerge = b'fullmerge' # both premerge and merge
515
520
516 def _extrasetup(
521 def _extrasetup(
517 self,
522 self,
518 name,
523 name,
519 func,
524 func,
520 mergetype,
525 mergetype,
521 onfailure=None,
526 onfailure=None,
522 precheck=None,
527 precheck=None,
523 binary=False,
528 binary=False,
524 symlink=False,
529 symlink=False,
525 ):
530 ):
526 func.mergetype = mergetype
531 func.mergetype = mergetype
527 func.onfailure = onfailure
532 func.onfailure = onfailure
528 func.precheck = precheck
533 func.precheck = precheck
529
534
530 binarycap = binary or mergetype == self.nomerge
535 binarycap = binary or mergetype == self.nomerge
531 symlinkcap = symlink or mergetype == self.nomerge
536 symlinkcap = symlink or mergetype == self.nomerge
532
537
533 # actual capabilities, which this internal merge tool has
538 # actual capabilities, which this internal merge tool has
534 func.capabilities = {b"binary": binarycap, b"symlink": symlinkcap}
539 func.capabilities = {b"binary": binarycap, b"symlink": symlinkcap}
@@ -1,287 +1,277 b''
1 """strip changesets and their descendants from history
2
3 This extension allows you to strip changesets and all their descendants from the
4 repository. See the command help for details.
5 """
6 from __future__ import absolute_import
1 from __future__ import absolute_import
7
2
8 from mercurial.i18n import _
3 from .i18n import _
9 from mercurial.pycompat import getattr
4 from .pycompat import getattr
10 from mercurial import (
5 from . import (
11 bookmarks as bookmarksmod,
6 bookmarks as bookmarksmod,
12 cmdutil,
7 cmdutil,
13 error,
8 error,
14 hg,
9 hg,
15 lock as lockmod,
10 lock as lockmod,
16 mergestate as mergestatemod,
11 mergestate as mergestatemod,
17 node as nodemod,
12 node as nodemod,
18 pycompat,
13 pycompat,
19 registrar,
14 registrar,
20 repair,
15 repair,
21 scmutil,
16 scmutil,
22 util,
17 util,
23 )
18 )
24
19
25 nullid = nodemod.nullid
20 nullid = nodemod.nullid
26 release = lockmod.release
21 release = lockmod.release
27
22
28 cmdtable = {}
23 cmdtable = {}
29 command = registrar.command(cmdtable)
24 command = registrar.command(cmdtable)
30 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
31 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
32 # be specifying the version(s) of Mercurial they are tested with, or
33 # leave the attribute unspecified.
34 testedwith = b'ships-with-hg-core'
35
25
36
26
37 def checklocalchanges(repo, force=False):
27 def checklocalchanges(repo, force=False):
38 s = repo.status()
28 s = repo.status()
39 if not force:
29 if not force:
40 cmdutil.checkunfinished(repo)
30 cmdutil.checkunfinished(repo)
41 cmdutil.bailifchanged(repo)
31 cmdutil.bailifchanged(repo)
42 else:
32 else:
43 cmdutil.checkunfinished(repo, skipmerge=True)
33 cmdutil.checkunfinished(repo, skipmerge=True)
44 return s
34 return s
45
35
46
36
47 def _findupdatetarget(repo, nodes):
37 def _findupdatetarget(repo, nodes):
48 unode, p2 = repo.changelog.parents(nodes[0])
38 unode, p2 = repo.changelog.parents(nodes[0])
49 currentbranch = repo[None].branch()
39 currentbranch = repo[None].branch()
50
40
51 if (
41 if (
52 util.safehasattr(repo, b'mq')
42 util.safehasattr(repo, b'mq')
53 and p2 != nullid
43 and p2 != nullid
54 and p2 in [x.node for x in repo.mq.applied]
44 and p2 in [x.node for x in repo.mq.applied]
55 ):
45 ):
56 unode = p2
46 unode = p2
57 elif currentbranch != repo[unode].branch():
47 elif currentbranch != repo[unode].branch():
58 pwdir = b'parents(wdir())'
48 pwdir = b'parents(wdir())'
59 revset = b'max(((parents(%ln::%r) + %r) - %ln::%r) and branch(%s))'
49 revset = b'max(((parents(%ln::%r) + %r) - %ln::%r) and branch(%s))'
60 branchtarget = repo.revs(
50 branchtarget = repo.revs(
61 revset, nodes, pwdir, pwdir, nodes, pwdir, currentbranch
51 revset, nodes, pwdir, pwdir, nodes, pwdir, currentbranch
62 )
52 )
63 if branchtarget:
53 if branchtarget:
64 cl = repo.changelog
54 cl = repo.changelog
65 unode = cl.node(branchtarget.first())
55 unode = cl.node(branchtarget.first())
66
56
67 return unode
57 return unode
68
58
69
59
70 def strip(
60 def strip(
71 ui,
61 ui,
72 repo,
62 repo,
73 revs,
63 revs,
74 update=True,
64 update=True,
75 backup=True,
65 backup=True,
76 force=None,
66 force=None,
77 bookmarks=None,
67 bookmarks=None,
78 soft=False,
68 soft=False,
79 ):
69 ):
80 with repo.wlock(), repo.lock():
70 with repo.wlock(), repo.lock():
81
71
82 if update:
72 if update:
83 checklocalchanges(repo, force=force)
73 checklocalchanges(repo, force=force)
84 urev = _findupdatetarget(repo, revs)
74 urev = _findupdatetarget(repo, revs)
85 hg.clean(repo, urev)
75 hg.clean(repo, urev)
86 repo.dirstate.write(repo.currenttransaction())
76 repo.dirstate.write(repo.currenttransaction())
87
77
88 if soft:
78 if soft:
89 repair.softstrip(ui, repo, revs, backup)
79 repair.softstrip(ui, repo, revs, backup)
90 else:
80 else:
91 repair.strip(ui, repo, revs, backup)
81 repair.strip(ui, repo, revs, backup)
92
82
93 repomarks = repo._bookmarks
83 repomarks = repo._bookmarks
94 if bookmarks:
84 if bookmarks:
95 with repo.transaction(b'strip') as tr:
85 with repo.transaction(b'strip') as tr:
96 if repo._activebookmark in bookmarks:
86 if repo._activebookmark in bookmarks:
97 bookmarksmod.deactivate(repo)
87 bookmarksmod.deactivate(repo)
98 repomarks.applychanges(repo, tr, [(b, None) for b in bookmarks])
88 repomarks.applychanges(repo, tr, [(b, None) for b in bookmarks])
99 for bookmark in sorted(bookmarks):
89 for bookmark in sorted(bookmarks):
100 ui.write(_(b"bookmark '%s' deleted\n") % bookmark)
90 ui.write(_(b"bookmark '%s' deleted\n") % bookmark)
101
91
102
92
103 @command(
93 @command(
104 b"strip",
94 b"debugstrip",
105 [
95 [
106 (
96 (
107 b'r',
97 b'r',
108 b'rev',
98 b'rev',
109 [],
99 [],
110 _(
100 _(
111 b'strip specified revision (optional, '
101 b'strip specified revision (optional, '
112 b'can specify revisions without this '
102 b'can specify revisions without this '
113 b'option)'
103 b'option)'
114 ),
104 ),
115 _(b'REV'),
105 _(b'REV'),
116 ),
106 ),
117 (
107 (
118 b'f',
108 b'f',
119 b'force',
109 b'force',
120 None,
110 None,
121 _(
111 _(
122 b'force removal of changesets, discard '
112 b'force removal of changesets, discard '
123 b'uncommitted changes (no backup)'
113 b'uncommitted changes (no backup)'
124 ),
114 ),
125 ),
115 ),
126 (b'', b'no-backup', None, _(b'do not save backup bundle')),
116 (b'', b'no-backup', None, _(b'do not save backup bundle')),
127 (b'', b'nobackup', None, _(b'do not save backup bundle (DEPRECATED)'),),
117 (b'', b'nobackup', None, _(b'do not save backup bundle (DEPRECATED)'),),
128 (b'n', b'', None, _(b'ignored (DEPRECATED)')),
118 (b'n', b'', None, _(b'ignored (DEPRECATED)')),
129 (
119 (
130 b'k',
120 b'k',
131 b'keep',
121 b'keep',
132 None,
122 None,
133 _(b"do not modify working directory during strip"),
123 _(b"do not modify working directory during strip"),
134 ),
124 ),
135 (
125 (
136 b'B',
126 b'B',
137 b'bookmark',
127 b'bookmark',
138 [],
128 [],
139 _(b"remove revs only reachable from given bookmark"),
129 _(b"remove revs only reachable from given bookmark"),
140 _(b'BOOKMARK'),
130 _(b'BOOKMARK'),
141 ),
131 ),
142 (
132 (
143 b'',
133 b'',
144 b'soft',
134 b'soft',
145 None,
135 None,
146 _(b"simply drop changesets from visible history (EXPERIMENTAL)"),
136 _(b"simply drop changesets from visible history (EXPERIMENTAL)"),
147 ),
137 ),
148 ],
138 ],
149 _(b'hg strip [-k] [-f] [-B bookmark] [-r] REV...'),
139 _(b'hg debugstrip [-k] [-f] [-B bookmark] [-r] REV...'),
150 helpcategory=command.CATEGORY_MAINTENANCE,
140 helpcategory=command.CATEGORY_MAINTENANCE,
151 )
141 )
152 def stripcmd(ui, repo, *revs, **opts):
142 def debugstrip(ui, repo, *revs, **opts):
153 """strip changesets and all their descendants from the repository
143 """strip changesets and all their descendants from the repository
154
144
155 The strip command removes the specified changesets and all their
145 The strip command removes the specified changesets and all their
156 descendants. If the working directory has uncommitted changes, the
146 descendants. If the working directory has uncommitted changes, the
157 operation is aborted unless the --force flag is supplied, in which
147 operation is aborted unless the --force flag is supplied, in which
158 case changes will be discarded.
148 case changes will be discarded.
159
149
160 If a parent of the working directory is stripped, then the working
150 If a parent of the working directory is stripped, then the working
161 directory will automatically be updated to the most recent
151 directory will automatically be updated to the most recent
162 available ancestor of the stripped parent after the operation
152 available ancestor of the stripped parent after the operation
163 completes.
153 completes.
164
154
165 Any stripped changesets are stored in ``.hg/strip-backup`` as a
155 Any stripped changesets are stored in ``.hg/strip-backup`` as a
166 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
156 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
167 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
157 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
168 where BUNDLE is the bundle file created by the strip. Note that
158 where BUNDLE is the bundle file created by the strip. Note that
169 the local revision numbers will in general be different after the
159 the local revision numbers will in general be different after the
170 restore.
160 restore.
171
161
172 Use the --no-backup option to discard the backup bundle once the
162 Use the --no-backup option to discard the backup bundle once the
173 operation completes.
163 operation completes.
174
164
175 Strip is not a history-rewriting operation and can be used on
165 Strip is not a history-rewriting operation and can be used on
176 changesets in the public phase. But if the stripped changesets have
166 changesets in the public phase. But if the stripped changesets have
177 been pushed to a remote repository you will likely pull them again.
167 been pushed to a remote repository you will likely pull them again.
178
168
179 Return 0 on success.
169 Return 0 on success.
180 """
170 """
181 opts = pycompat.byteskwargs(opts)
171 opts = pycompat.byteskwargs(opts)
182 backup = True
172 backup = True
183 if opts.get(b'no_backup') or opts.get(b'nobackup'):
173 if opts.get(b'no_backup') or opts.get(b'nobackup'):
184 backup = False
174 backup = False
185
175
186 cl = repo.changelog
176 cl = repo.changelog
187 revs = list(revs) + opts.get(b'rev')
177 revs = list(revs) + opts.get(b'rev')
188 revs = set(scmutil.revrange(repo, revs))
178 revs = set(scmutil.revrange(repo, revs))
189
179
190 with repo.wlock():
180 with repo.wlock():
191 bookmarks = set(opts.get(b'bookmark'))
181 bookmarks = set(opts.get(b'bookmark'))
192 if bookmarks:
182 if bookmarks:
193 repomarks = repo._bookmarks
183 repomarks = repo._bookmarks
194 if not bookmarks.issubset(repomarks):
184 if not bookmarks.issubset(repomarks):
195 raise error.Abort(
185 raise error.Abort(
196 _(b"bookmark '%s' not found")
186 _(b"bookmark '%s' not found")
197 % b','.join(sorted(bookmarks - set(repomarks.keys())))
187 % b','.join(sorted(bookmarks - set(repomarks.keys())))
198 )
188 )
199
189
200 # If the requested bookmark is not the only one pointing to a
190 # If the requested bookmark is not the only one pointing to a
201 # a revision we have to only delete the bookmark and not strip
191 # a revision we have to only delete the bookmark and not strip
202 # anything. revsets cannot detect that case.
192 # anything. revsets cannot detect that case.
203 nodetobookmarks = {}
193 nodetobookmarks = {}
204 for mark, node in pycompat.iteritems(repomarks):
194 for mark, node in pycompat.iteritems(repomarks):
205 nodetobookmarks.setdefault(node, []).append(mark)
195 nodetobookmarks.setdefault(node, []).append(mark)
206 for marks in nodetobookmarks.values():
196 for marks in nodetobookmarks.values():
207 if bookmarks.issuperset(marks):
197 if bookmarks.issuperset(marks):
208 rsrevs = scmutil.bookmarkrevs(repo, marks[0])
198 rsrevs = scmutil.bookmarkrevs(repo, marks[0])
209 revs.update(set(rsrevs))
199 revs.update(set(rsrevs))
210 if not revs:
200 if not revs:
211 with repo.lock(), repo.transaction(b'bookmark') as tr:
201 with repo.lock(), repo.transaction(b'bookmark') as tr:
212 bmchanges = [(b, None) for b in bookmarks]
202 bmchanges = [(b, None) for b in bookmarks]
213 repomarks.applychanges(repo, tr, bmchanges)
203 repomarks.applychanges(repo, tr, bmchanges)
214 for bookmark in sorted(bookmarks):
204 for bookmark in sorted(bookmarks):
215 ui.write(_(b"bookmark '%s' deleted\n") % bookmark)
205 ui.write(_(b"bookmark '%s' deleted\n") % bookmark)
216
206
217 if not revs:
207 if not revs:
218 raise error.Abort(_(b'empty revision set'))
208 raise error.Abort(_(b'empty revision set'))
219
209
220 descendants = set(cl.descendants(revs))
210 descendants = set(cl.descendants(revs))
221 strippedrevs = revs.union(descendants)
211 strippedrevs = revs.union(descendants)
222 roots = revs.difference(descendants)
212 roots = revs.difference(descendants)
223
213
224 # if one of the wdir parent is stripped we'll need
214 # if one of the wdir parent is stripped we'll need
225 # to update away to an earlier revision
215 # to update away to an earlier revision
226 update = any(
216 update = any(
227 p != nullid and cl.rev(p) in strippedrevs
217 p != nullid and cl.rev(p) in strippedrevs
228 for p in repo.dirstate.parents()
218 for p in repo.dirstate.parents()
229 )
219 )
230
220
231 rootnodes = {cl.node(r) for r in roots}
221 rootnodes = {cl.node(r) for r in roots}
232
222
233 q = getattr(repo, 'mq', None)
223 q = getattr(repo, 'mq', None)
234 if q is not None and q.applied:
224 if q is not None and q.applied:
235 # refresh queue state if we're about to strip
225 # refresh queue state if we're about to strip
236 # applied patches
226 # applied patches
237 if cl.rev(repo.lookup(b'qtip')) in strippedrevs:
227 if cl.rev(repo.lookup(b'qtip')) in strippedrevs:
238 q.applieddirty = True
228 q.applieddirty = True
239 start = 0
229 start = 0
240 end = len(q.applied)
230 end = len(q.applied)
241 for i, statusentry in enumerate(q.applied):
231 for i, statusentry in enumerate(q.applied):
242 if statusentry.node in rootnodes:
232 if statusentry.node in rootnodes:
243 # if one of the stripped roots is an applied
233 # if one of the stripped roots is an applied
244 # patch, only part of the queue is stripped
234 # patch, only part of the queue is stripped
245 start = i
235 start = i
246 break
236 break
247 del q.applied[start:end]
237 del q.applied[start:end]
248 q.savedirty()
238 q.savedirty()
249
239
250 revs = sorted(rootnodes)
240 revs = sorted(rootnodes)
251 if update and opts.get(b'keep'):
241 if update and opts.get(b'keep'):
252 urev = _findupdatetarget(repo, revs)
242 urev = _findupdatetarget(repo, revs)
253 uctx = repo[urev]
243 uctx = repo[urev]
254
244
255 # only reset the dirstate for files that would actually change
245 # only reset the dirstate for files that would actually change
256 # between the working context and uctx
246 # between the working context and uctx
257 descendantrevs = repo.revs(b"only(., %d)", uctx.rev())
247 descendantrevs = repo.revs(b"only(., %d)", uctx.rev())
258 changedfiles = []
248 changedfiles = []
259 for rev in descendantrevs:
249 for rev in descendantrevs:
260 # blindly reset the files, regardless of what actually changed
250 # blindly reset the files, regardless of what actually changed
261 changedfiles.extend(repo[rev].files())
251 changedfiles.extend(repo[rev].files())
262
252
263 # reset files that only changed in the dirstate too
253 # reset files that only changed in the dirstate too
264 dirstate = repo.dirstate
254 dirstate = repo.dirstate
265 dirchanges = [f for f in dirstate if dirstate[f] != b'n']
255 dirchanges = [f for f in dirstate if dirstate[f] != b'n']
266 changedfiles.extend(dirchanges)
256 changedfiles.extend(dirchanges)
267
257
268 repo.dirstate.rebuild(urev, uctx.manifest(), changedfiles)
258 repo.dirstate.rebuild(urev, uctx.manifest(), changedfiles)
269 repo.dirstate.write(repo.currenttransaction())
259 repo.dirstate.write(repo.currenttransaction())
270
260
271 # clear resolve state
261 # clear resolve state
272 mergestatemod.mergestate.clean(repo)
262 mergestatemod.mergestate.clean(repo)
273
263
274 update = False
264 update = False
275
265
276 strip(
266 strip(
277 ui,
267 ui,
278 repo,
268 repo,
279 revs,
269 revs,
280 backup=backup,
270 backup=backup,
281 update=update,
271 update=update,
282 force=opts.get(b'force'),
272 force=opts.get(b'force'),
283 bookmarks=bookmarks,
273 bookmarks=bookmarks,
284 soft=opts[b'soft'],
274 soft=opts[b'soft'],
285 )
275 )
286
276
287 return 0
277 return 0
@@ -1,19 +1,22 b''
1 == New Features ==
1 == New Features ==
2
2
3
3
4
4
5 * `hg strip`, from the strip extension, is now a core command, `hg
6 debugstrip`. The extension remains for compatibility.
7
5 == New Experimental Features ==
8 == New Experimental Features ==
6
9
7
10
8
11
9 == Bug Fixes ==
12 == Bug Fixes ==
10
13
11
14
12
15
13 == Backwards Compatibility Changes ==
16 == Backwards Compatibility Changes ==
14
17
15
18
16
19
17 == Internal API Changes ==
20 == Internal API Changes ==
18
21
19
22
@@ -1,437 +1,439 b''
1 Show all commands except debug commands
1 Show all commands except debug commands
2 $ hg debugcomplete
2 $ hg debugcomplete
3 abort
3 abort
4 add
4 add
5 addremove
5 addremove
6 annotate
6 annotate
7 archive
7 archive
8 backout
8 backout
9 bisect
9 bisect
10 bookmarks
10 bookmarks
11 branch
11 branch
12 branches
12 branches
13 bundle
13 bundle
14 cat
14 cat
15 clone
15 clone
16 commit
16 commit
17 config
17 config
18 continue
18 continue
19 copy
19 copy
20 diff
20 diff
21 export
21 export
22 files
22 files
23 forget
23 forget
24 graft
24 graft
25 grep
25 grep
26 heads
26 heads
27 help
27 help
28 identify
28 identify
29 import
29 import
30 incoming
30 incoming
31 init
31 init
32 locate
32 locate
33 log
33 log
34 manifest
34 manifest
35 merge
35 merge
36 outgoing
36 outgoing
37 parents
37 parents
38 paths
38 paths
39 phase
39 phase
40 pull
40 pull
41 push
41 push
42 recover
42 recover
43 remove
43 remove
44 rename
44 rename
45 resolve
45 resolve
46 revert
46 revert
47 rollback
47 rollback
48 root
48 root
49 serve
49 serve
50 shelve
50 shelve
51 status
51 status
52 summary
52 summary
53 tag
53 tag
54 tags
54 tags
55 tip
55 tip
56 unbundle
56 unbundle
57 unshelve
57 unshelve
58 update
58 update
59 verify
59 verify
60 version
60 version
61
61
62 Show all commands that start with "a"
62 Show all commands that start with "a"
63 $ hg debugcomplete a
63 $ hg debugcomplete a
64 abort
64 abort
65 add
65 add
66 addremove
66 addremove
67 annotate
67 annotate
68 archive
68 archive
69
69
70 Do not show debug commands if there are other candidates
70 Do not show debug commands if there are other candidates
71 $ hg debugcomplete d
71 $ hg debugcomplete d
72 diff
72 diff
73
73
74 Show debug commands if there are no other candidates
74 Show debug commands if there are no other candidates
75 $ hg debugcomplete debug
75 $ hg debugcomplete debug
76 debugancestor
76 debugancestor
77 debugantivirusrunning
77 debugantivirusrunning
78 debugapplystreamclonebundle
78 debugapplystreamclonebundle
79 debugbackupbundle
79 debugbackupbundle
80 debugbuilddag
80 debugbuilddag
81 debugbundle
81 debugbundle
82 debugcapabilities
82 debugcapabilities
83 debugchangedfiles
83 debugchangedfiles
84 debugcheckstate
84 debugcheckstate
85 debugcolor
85 debugcolor
86 debugcommands
86 debugcommands
87 debugcomplete
87 debugcomplete
88 debugconfig
88 debugconfig
89 debugcreatestreamclonebundle
89 debugcreatestreamclonebundle
90 debugdag
90 debugdag
91 debugdata
91 debugdata
92 debugdate
92 debugdate
93 debugdeltachain
93 debugdeltachain
94 debugdirstate
94 debugdirstate
95 debugdiscovery
95 debugdiscovery
96 debugdownload
96 debugdownload
97 debugextensions
97 debugextensions
98 debugfileset
98 debugfileset
99 debugformat
99 debugformat
100 debugfsinfo
100 debugfsinfo
101 debuggetbundle
101 debuggetbundle
102 debugignore
102 debugignore
103 debugindex
103 debugindex
104 debugindexdot
104 debugindexdot
105 debugindexstats
105 debugindexstats
106 debuginstall
106 debuginstall
107 debugknown
107 debugknown
108 debuglabelcomplete
108 debuglabelcomplete
109 debuglocks
109 debuglocks
110 debugmanifestfulltextcache
110 debugmanifestfulltextcache
111 debugmergestate
111 debugmergestate
112 debugnamecomplete
112 debugnamecomplete
113 debugnodemap
113 debugnodemap
114 debugobsolete
114 debugobsolete
115 debugp1copies
115 debugp1copies
116 debugp2copies
116 debugp2copies
117 debugpathcomplete
117 debugpathcomplete
118 debugpathcopies
118 debugpathcopies
119 debugpeer
119 debugpeer
120 debugpickmergetool
120 debugpickmergetool
121 debugpushkey
121 debugpushkey
122 debugpvec
122 debugpvec
123 debugrebuilddirstate
123 debugrebuilddirstate
124 debugrebuildfncache
124 debugrebuildfncache
125 debugrename
125 debugrename
126 debugrequires
126 debugrequires
127 debugrevlog
127 debugrevlog
128 debugrevlogindex
128 debugrevlogindex
129 debugrevspec
129 debugrevspec
130 debugserve
130 debugserve
131 debugsetparents
131 debugsetparents
132 debugsidedata
132 debugsidedata
133 debugssl
133 debugssl
134 debugstrip
134 debugsub
135 debugsub
135 debugsuccessorssets
136 debugsuccessorssets
136 debugtagscache
137 debugtagscache
137 debugtemplate
138 debugtemplate
138 debuguigetpass
139 debuguigetpass
139 debuguiprompt
140 debuguiprompt
140 debugupdatecaches
141 debugupdatecaches
141 debugupgraderepo
142 debugupgraderepo
142 debugwalk
143 debugwalk
143 debugwhyunstable
144 debugwhyunstable
144 debugwireargs
145 debugwireargs
145 debugwireproto
146 debugwireproto
146
147
147 Do not show the alias of a debug command if there are other candidates
148 Do not show the alias of a debug command if there are other candidates
148 (this should hide rawcommit)
149 (this should hide rawcommit)
149 $ hg debugcomplete r
150 $ hg debugcomplete r
150 recover
151 recover
151 remove
152 remove
152 rename
153 rename
153 resolve
154 resolve
154 revert
155 revert
155 rollback
156 rollback
156 root
157 root
157 Show the alias of a debug command if there are no other candidates
158 Show the alias of a debug command if there are no other candidates
158 $ hg debugcomplete rawc
159 $ hg debugcomplete rawc
159
160
160
161
161 Show the global options
162 Show the global options
162 $ hg debugcomplete --options | sort
163 $ hg debugcomplete --options | sort
163 --color
164 --color
164 --config
165 --config
165 --cwd
166 --cwd
166 --debug
167 --debug
167 --debugger
168 --debugger
168 --encoding
169 --encoding
169 --encodingmode
170 --encodingmode
170 --help
171 --help
171 --hidden
172 --hidden
172 --noninteractive
173 --noninteractive
173 --pager
174 --pager
174 --profile
175 --profile
175 --quiet
176 --quiet
176 --repository
177 --repository
177 --time
178 --time
178 --traceback
179 --traceback
179 --verbose
180 --verbose
180 --version
181 --version
181 -R
182 -R
182 -h
183 -h
183 -q
184 -q
184 -v
185 -v
185 -y
186 -y
186
187
187 Show the options for the "serve" command
188 Show the options for the "serve" command
188 $ hg debugcomplete --options serve | sort
189 $ hg debugcomplete --options serve | sort
189 --accesslog
190 --accesslog
190 --address
191 --address
191 --certificate
192 --certificate
192 --cmdserver
193 --cmdserver
193 --color
194 --color
194 --config
195 --config
195 --cwd
196 --cwd
196 --daemon
197 --daemon
197 --daemon-postexec
198 --daemon-postexec
198 --debug
199 --debug
199 --debugger
200 --debugger
200 --encoding
201 --encoding
201 --encodingmode
202 --encodingmode
202 --errorlog
203 --errorlog
203 --help
204 --help
204 --hidden
205 --hidden
205 --ipv6
206 --ipv6
206 --name
207 --name
207 --noninteractive
208 --noninteractive
208 --pager
209 --pager
209 --pid-file
210 --pid-file
210 --port
211 --port
211 --prefix
212 --prefix
212 --print-url
213 --print-url
213 --profile
214 --profile
214 --quiet
215 --quiet
215 --repository
216 --repository
216 --stdio
217 --stdio
217 --style
218 --style
218 --subrepos
219 --subrepos
219 --templates
220 --templates
220 --time
221 --time
221 --traceback
222 --traceback
222 --verbose
223 --verbose
223 --version
224 --version
224 --web-conf
225 --web-conf
225 -6
226 -6
226 -A
227 -A
227 -E
228 -E
228 -R
229 -R
229 -S
230 -S
230 -a
231 -a
231 -d
232 -d
232 -h
233 -h
233 -n
234 -n
234 -p
235 -p
235 -q
236 -q
236 -t
237 -t
237 -v
238 -v
238 -y
239 -y
239
240
240 Show an error if we use --options with an ambiguous abbreviation
241 Show an error if we use --options with an ambiguous abbreviation
241 $ hg debugcomplete --options s
242 $ hg debugcomplete --options s
242 hg: command 's' is ambiguous:
243 hg: command 's' is ambiguous:
243 serve shelve showconfig status summary
244 serve shelve showconfig status summary
244 [255]
245 [255]
245
246
246 Show all commands + options
247 Show all commands + options
247 $ hg debugcommands
248 $ hg debugcommands
248 abort: dry-run
249 abort: dry-run
249 add: include, exclude, subrepos, dry-run
250 add: include, exclude, subrepos, dry-run
250 addremove: similarity, subrepos, include, exclude, dry-run
251 addremove: similarity, subrepos, include, exclude, dry-run
251 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, skip, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, include, exclude, template
252 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, skip, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, include, exclude, template
252 archive: no-decode, prefix, rev, type, subrepos, include, exclude
253 archive: no-decode, prefix, rev, type, subrepos, include, exclude
253 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
254 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
254 bisect: reset, good, bad, skip, extend, command, noupdate
255 bisect: reset, good, bad, skip, extend, command, noupdate
255 bookmarks: force, rev, delete, rename, inactive, list, template
256 bookmarks: force, rev, delete, rename, inactive, list, template
256 branch: force, clean, rev
257 branch: force, clean, rev
257 branches: active, closed, rev, template
258 branches: active, closed, rev, template
258 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
259 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
259 cat: output, rev, decode, include, exclude, template
260 cat: output, rev, decode, include, exclude, template
260 clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure
261 clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure
261 commit: addremove, close-branch, amend, secret, edit, force-close-branch, interactive, include, exclude, message, logfile, date, user, subrepos
262 commit: addremove, close-branch, amend, secret, edit, force-close-branch, interactive, include, exclude, message, logfile, date, user, subrepos
262 config: untrusted, edit, local, shared, non-shared, global, template
263 config: untrusted, edit, local, shared, non-shared, global, template
263 continue: dry-run
264 continue: dry-run
264 copy: forget, after, at-rev, force, include, exclude, dry-run
265 copy: forget, after, at-rev, force, include, exclude, dry-run
265 debugancestor:
266 debugancestor:
266 debugantivirusrunning:
267 debugantivirusrunning:
267 debugapplystreamclonebundle:
268 debugapplystreamclonebundle:
268 debugbackupbundle: recover, patch, git, limit, no-merges, stat, graph, style, template
269 debugbackupbundle: recover, patch, git, limit, no-merges, stat, graph, style, template
269 debugbuilddag: mergeable-file, overwritten-file, new-file
270 debugbuilddag: mergeable-file, overwritten-file, new-file
270 debugbundle: all, part-type, spec
271 debugbundle: all, part-type, spec
271 debugcapabilities:
272 debugcapabilities:
272 debugchangedfiles:
273 debugchangedfiles:
273 debugcheckstate:
274 debugcheckstate:
274 debugcolor: style
275 debugcolor: style
275 debugcommands:
276 debugcommands:
276 debugcomplete: options
277 debugcomplete: options
277 debugcreatestreamclonebundle:
278 debugcreatestreamclonebundle:
278 debugdag: tags, branches, dots, spaces
279 debugdag: tags, branches, dots, spaces
279 debugdata: changelog, manifest, dir
280 debugdata: changelog, manifest, dir
280 debugdate: extended
281 debugdate: extended
281 debugdeltachain: changelog, manifest, dir, template
282 debugdeltachain: changelog, manifest, dir, template
282 debugdirstate: nodates, dates, datesort
283 debugdirstate: nodates, dates, datesort
283 debugdiscovery: old, nonheads, rev, seed, ssh, remotecmd, insecure
284 debugdiscovery: old, nonheads, rev, seed, ssh, remotecmd, insecure
284 debugdownload: output
285 debugdownload: output
285 debugextensions: template
286 debugextensions: template
286 debugfileset: rev, all-files, show-matcher, show-stage
287 debugfileset: rev, all-files, show-matcher, show-stage
287 debugformat: template
288 debugformat: template
288 debugfsinfo:
289 debugfsinfo:
289 debuggetbundle: head, common, type
290 debuggetbundle: head, common, type
290 debugignore:
291 debugignore:
291 debugindex: changelog, manifest, dir, template
292 debugindex: changelog, manifest, dir, template
292 debugindexdot: changelog, manifest, dir
293 debugindexdot: changelog, manifest, dir
293 debugindexstats:
294 debugindexstats:
294 debuginstall: template
295 debuginstall: template
295 debugknown:
296 debugknown:
296 debuglabelcomplete:
297 debuglabelcomplete:
297 debuglocks: force-lock, force-wlock, set-lock, set-wlock
298 debuglocks: force-lock, force-wlock, set-lock, set-wlock
298 debugmanifestfulltextcache: clear, add
299 debugmanifestfulltextcache: clear, add
299 debugmergestate: style, template
300 debugmergestate: style, template
300 debugnamecomplete:
301 debugnamecomplete:
301 debugnodemap: dump-new, dump-disk, check, metadata
302 debugnodemap: dump-new, dump-disk, check, metadata
302 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
303 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
303 debugp1copies: rev
304 debugp1copies: rev
304 debugp2copies: rev
305 debugp2copies: rev
305 debugpathcomplete: full, normal, added, removed
306 debugpathcomplete: full, normal, added, removed
306 debugpathcopies: include, exclude
307 debugpathcopies: include, exclude
307 debugpeer:
308 debugpeer:
308 debugpickmergetool: rev, changedelete, include, exclude, tool
309 debugpickmergetool: rev, changedelete, include, exclude, tool
309 debugpushkey:
310 debugpushkey:
310 debugpvec:
311 debugpvec:
311 debugrebuilddirstate: rev, minimal
312 debugrebuilddirstate: rev, minimal
312 debugrebuildfncache:
313 debugrebuildfncache:
313 debugrename: rev
314 debugrename: rev
314 debugrequires:
315 debugrequires:
315 debugrevlog: changelog, manifest, dir, dump
316 debugrevlog: changelog, manifest, dir, dump
316 debugrevlogindex: changelog, manifest, dir, format
317 debugrevlogindex: changelog, manifest, dir, format
317 debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
318 debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
318 debugserve: sshstdio, logiofd, logiofile
319 debugserve: sshstdio, logiofd, logiofile
319 debugsetparents:
320 debugsetparents:
320 debugsidedata: changelog, manifest, dir
321 debugsidedata: changelog, manifest, dir
321 debugssl:
322 debugssl:
323 debugstrip: rev, force, no-backup, nobackup, , keep, bookmark, soft
322 debugsub: rev
324 debugsub: rev
323 debugsuccessorssets: closest
325 debugsuccessorssets: closest
324 debugtagscache:
326 debugtagscache:
325 debugtemplate: rev, define
327 debugtemplate: rev, define
326 debuguigetpass: prompt
328 debuguigetpass: prompt
327 debuguiprompt: prompt
329 debuguiprompt: prompt
328 debugupdatecaches:
330 debugupdatecaches:
329 debugupgraderepo: optimize, run, backup, changelog, manifest
331 debugupgraderepo: optimize, run, backup, changelog, manifest
330 debugwalk: include, exclude
332 debugwalk: include, exclude
331 debugwhyunstable:
333 debugwhyunstable:
332 debugwireargs: three, four, five, ssh, remotecmd, insecure
334 debugwireargs: three, four, five, ssh, remotecmd, insecure
333 debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, remotecmd, insecure
335 debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, remotecmd, insecure
334 diff: rev, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, unified, stat, root, include, exclude, subrepos
336 diff: rev, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, unified, stat, root, include, exclude, subrepos
335 export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template
337 export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template
336 files: rev, print0, include, exclude, template, subrepos
338 files: rev, print0, include, exclude, template, subrepos
337 forget: interactive, include, exclude, dry-run
339 forget: interactive, include, exclude, dry-run
338 graft: rev, base, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
340 graft: rev, base, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
339 grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude
341 grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude
340 heads: rev, topo, active, closed, style, template
342 heads: rev, topo, active, closed, style, template
341 help: extension, command, keyword, system
343 help: extension, command, keyword, system
342 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
344 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
343 import: strip, base, secret, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
345 import: strip, base, secret, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
344 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
346 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
345 init: ssh, remotecmd, insecure
347 init: ssh, remotecmd, insecure
346 locate: rev, print0, fullpath, include, exclude
348 locate: rev, print0, fullpath, include, exclude
347 log: follow, follow-first, date, copies, keyword, rev, line-range, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
349 log: follow, follow-first, date, copies, keyword, rev, line-range, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
348 manifest: rev, all, template
350 manifest: rev, all, template
349 merge: force, rev, preview, abort, tool
351 merge: force, rev, preview, abort, tool
350 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
352 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
351 parents: rev, style, template
353 parents: rev, style, template
352 paths: template
354 paths: template
353 phase: public, draft, secret, force, rev
355 phase: public, draft, secret, force, rev
354 pull: update, force, confirm, rev, bookmark, branch, ssh, remotecmd, insecure
356 pull: update, force, confirm, rev, bookmark, branch, ssh, remotecmd, insecure
355 push: force, rev, bookmark, all-bookmarks, branch, new-branch, pushvars, publish, ssh, remotecmd, insecure
357 push: force, rev, bookmark, all-bookmarks, branch, new-branch, pushvars, publish, ssh, remotecmd, insecure
356 recover: verify
358 recover: verify
357 remove: after, force, subrepos, include, exclude, dry-run
359 remove: after, force, subrepos, include, exclude, dry-run
358 rename: after, at-rev, force, include, exclude, dry-run
360 rename: after, at-rev, force, include, exclude, dry-run
359 resolve: all, list, mark, unmark, no-status, re-merge, tool, include, exclude, template
361 resolve: all, list, mark, unmark, no-status, re-merge, tool, include, exclude, template
360 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
362 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
361 rollback: dry-run, force
363 rollback: dry-run, force
362 root: template
364 root: template
363 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, print-url, subrepos
365 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, print-url, subrepos
364 shelve: addremove, unknown, cleanup, date, delete, edit, keep, list, message, name, patch, interactive, stat, include, exclude
366 shelve: addremove, unknown, cleanup, date, delete, edit, keep, list, message, name, patch, interactive, stat, include, exclude
365 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
367 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
366 summary: remote
368 summary: remote
367 tag: force, local, rev, remove, edit, message, date, user
369 tag: force, local, rev, remove, edit, message, date, user
368 tags: template
370 tags: template
369 tip: patch, git, style, template
371 tip: patch, git, style, template
370 unbundle: update
372 unbundle: update
371 unshelve: abort, continue, interactive, keep, name, tool, date
373 unshelve: abort, continue, interactive, keep, name, tool, date
372 update: clean, check, merge, date, rev, tool
374 update: clean, check, merge, date, rev, tool
373 verify: full
375 verify: full
374 version: template
376 version: template
375
377
376 $ hg init a
378 $ hg init a
377 $ cd a
379 $ cd a
378 $ echo fee > fee
380 $ echo fee > fee
379 $ hg ci -q -Amfee
381 $ hg ci -q -Amfee
380 $ hg tag fee
382 $ hg tag fee
381 $ mkdir fie
383 $ mkdir fie
382 $ echo dead > fie/dead
384 $ echo dead > fie/dead
383 $ echo live > fie/live
385 $ echo live > fie/live
384 $ hg bookmark fo
386 $ hg bookmark fo
385 $ hg branch -q fie
387 $ hg branch -q fie
386 $ hg ci -q -Amfie
388 $ hg ci -q -Amfie
387 $ echo fo > fo
389 $ echo fo > fo
388 $ hg branch -qf default
390 $ hg branch -qf default
389 $ hg ci -q -Amfo
391 $ hg ci -q -Amfo
390 $ echo Fum > Fum
392 $ echo Fum > Fum
391 $ hg ci -q -AmFum
393 $ hg ci -q -AmFum
392 $ hg bookmark Fum
394 $ hg bookmark Fum
393
395
394 Test debugpathcomplete
396 Test debugpathcomplete
395
397
396 $ hg debugpathcomplete f
398 $ hg debugpathcomplete f
397 fee
399 fee
398 fie
400 fie
399 fo
401 fo
400 $ hg debugpathcomplete -f f
402 $ hg debugpathcomplete -f f
401 fee
403 fee
402 fie/dead
404 fie/dead
403 fie/live
405 fie/live
404 fo
406 fo
405
407
406 $ hg rm Fum
408 $ hg rm Fum
407 $ hg debugpathcomplete -r F
409 $ hg debugpathcomplete -r F
408 Fum
410 Fum
409
411
410 Test debugnamecomplete
412 Test debugnamecomplete
411
413
412 $ hg debugnamecomplete
414 $ hg debugnamecomplete
413 Fum
415 Fum
414 default
416 default
415 fee
417 fee
416 fie
418 fie
417 fo
419 fo
418 tip
420 tip
419 $ hg debugnamecomplete f
421 $ hg debugnamecomplete f
420 fee
422 fee
421 fie
423 fie
422 fo
424 fo
423
425
424 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
426 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
425 used for completions in some shells.
427 used for completions in some shells.
426
428
427 $ hg debuglabelcomplete
429 $ hg debuglabelcomplete
428 Fum
430 Fum
429 default
431 default
430 fee
432 fee
431 fie
433 fie
432 fo
434 fo
433 tip
435 tip
434 $ hg debuglabelcomplete f
436 $ hg debuglabelcomplete f
435 fee
437 fee
436 fie
438 fie
437 fo
439 fo
@@ -1,3919 +1,3919 b''
1 Short help:
1 Short help:
2
2
3 $ hg
3 $ hg
4 Mercurial Distributed SCM
4 Mercurial Distributed SCM
5
5
6 basic commands:
6 basic commands:
7
7
8 add add the specified files on the next commit
8 add add the specified files on the next commit
9 annotate show changeset information by line for each file
9 annotate show changeset information by line for each file
10 clone make a copy of an existing repository
10 clone make a copy of an existing repository
11 commit commit the specified files or all outstanding changes
11 commit commit the specified files or all outstanding changes
12 diff diff repository (or selected files)
12 diff diff repository (or selected files)
13 export dump the header and diffs for one or more changesets
13 export dump the header and diffs for one or more changesets
14 forget forget the specified files on the next commit
14 forget forget the specified files on the next commit
15 init create a new repository in the given directory
15 init create a new repository in the given directory
16 log show revision history of entire repository or files
16 log show revision history of entire repository or files
17 merge merge another revision into working directory
17 merge merge another revision into working directory
18 pull pull changes from the specified source
18 pull pull changes from the specified source
19 push push changes to the specified destination
19 push push changes to the specified destination
20 remove remove the specified files on the next commit
20 remove remove the specified files on the next commit
21 serve start stand-alone webserver
21 serve start stand-alone webserver
22 status show changed files in the working directory
22 status show changed files in the working directory
23 summary summarize working directory state
23 summary summarize working directory state
24 update update working directory (or switch revisions)
24 update update working directory (or switch revisions)
25
25
26 (use 'hg help' for the full list of commands or 'hg -v' for details)
26 (use 'hg help' for the full list of commands or 'hg -v' for details)
27
27
28 $ hg -q
28 $ hg -q
29 add add the specified files on the next commit
29 add add the specified files on the next commit
30 annotate show changeset information by line for each file
30 annotate show changeset information by line for each file
31 clone make a copy of an existing repository
31 clone make a copy of an existing repository
32 commit commit the specified files or all outstanding changes
32 commit commit the specified files or all outstanding changes
33 diff diff repository (or selected files)
33 diff diff repository (or selected files)
34 export dump the header and diffs for one or more changesets
34 export dump the header and diffs for one or more changesets
35 forget forget the specified files on the next commit
35 forget forget the specified files on the next commit
36 init create a new repository in the given directory
36 init create a new repository in the given directory
37 log show revision history of entire repository or files
37 log show revision history of entire repository or files
38 merge merge another revision into working directory
38 merge merge another revision into working directory
39 pull pull changes from the specified source
39 pull pull changes from the specified source
40 push push changes to the specified destination
40 push push changes to the specified destination
41 remove remove the specified files on the next commit
41 remove remove the specified files on the next commit
42 serve start stand-alone webserver
42 serve start stand-alone webserver
43 status show changed files in the working directory
43 status show changed files in the working directory
44 summary summarize working directory state
44 summary summarize working directory state
45 update update working directory (or switch revisions)
45 update update working directory (or switch revisions)
46
46
47 Extra extensions will be printed in help output in a non-reliable order since
47 Extra extensions will be printed in help output in a non-reliable order since
48 the extension is unknown.
48 the extension is unknown.
49 #if no-extraextensions
49 #if no-extraextensions
50
50
51 $ hg help
51 $ hg help
52 Mercurial Distributed SCM
52 Mercurial Distributed SCM
53
53
54 list of commands:
54 list of commands:
55
55
56 Repository creation:
56 Repository creation:
57
57
58 clone make a copy of an existing repository
58 clone make a copy of an existing repository
59 init create a new repository in the given directory
59 init create a new repository in the given directory
60
60
61 Remote repository management:
61 Remote repository management:
62
62
63 incoming show new changesets found in source
63 incoming show new changesets found in source
64 outgoing show changesets not found in the destination
64 outgoing show changesets not found in the destination
65 paths show aliases for remote repositories
65 paths show aliases for remote repositories
66 pull pull changes from the specified source
66 pull pull changes from the specified source
67 push push changes to the specified destination
67 push push changes to the specified destination
68 serve start stand-alone webserver
68 serve start stand-alone webserver
69
69
70 Change creation:
70 Change creation:
71
71
72 commit commit the specified files or all outstanding changes
72 commit commit the specified files or all outstanding changes
73
73
74 Change manipulation:
74 Change manipulation:
75
75
76 backout reverse effect of earlier changeset
76 backout reverse effect of earlier changeset
77 graft copy changes from other branches onto the current branch
77 graft copy changes from other branches onto the current branch
78 merge merge another revision into working directory
78 merge merge another revision into working directory
79
79
80 Change organization:
80 Change organization:
81
81
82 bookmarks create a new bookmark or list existing bookmarks
82 bookmarks create a new bookmark or list existing bookmarks
83 branch set or show the current branch name
83 branch set or show the current branch name
84 branches list repository named branches
84 branches list repository named branches
85 phase set or show the current phase name
85 phase set or show the current phase name
86 tag add one or more tags for the current or given revision
86 tag add one or more tags for the current or given revision
87 tags list repository tags
87 tags list repository tags
88
88
89 File content management:
89 File content management:
90
90
91 annotate show changeset information by line for each file
91 annotate show changeset information by line for each file
92 cat output the current or given revision of files
92 cat output the current or given revision of files
93 copy mark files as copied for the next commit
93 copy mark files as copied for the next commit
94 diff diff repository (or selected files)
94 diff diff repository (or selected files)
95 grep search for a pattern in specified files
95 grep search for a pattern in specified files
96
96
97 Change navigation:
97 Change navigation:
98
98
99 bisect subdivision search of changesets
99 bisect subdivision search of changesets
100 heads show branch heads
100 heads show branch heads
101 identify identify the working directory or specified revision
101 identify identify the working directory or specified revision
102 log show revision history of entire repository or files
102 log show revision history of entire repository or files
103
103
104 Working directory management:
104 Working directory management:
105
105
106 add add the specified files on the next commit
106 add add the specified files on the next commit
107 addremove add all new files, delete all missing files
107 addremove add all new files, delete all missing files
108 files list tracked files
108 files list tracked files
109 forget forget the specified files on the next commit
109 forget forget the specified files on the next commit
110 remove remove the specified files on the next commit
110 remove remove the specified files on the next commit
111 rename rename files; equivalent of copy + remove
111 rename rename files; equivalent of copy + remove
112 resolve redo merges or set/view the merge status of files
112 resolve redo merges or set/view the merge status of files
113 revert restore files to their checkout state
113 revert restore files to their checkout state
114 root print the root (top) of the current working directory
114 root print the root (top) of the current working directory
115 shelve save and set aside changes from the working directory
115 shelve save and set aside changes from the working directory
116 status show changed files in the working directory
116 status show changed files in the working directory
117 summary summarize working directory state
117 summary summarize working directory state
118 unshelve restore a shelved change to the working directory
118 unshelve restore a shelved change to the working directory
119 update update working directory (or switch revisions)
119 update update working directory (or switch revisions)
120
120
121 Change import/export:
121 Change import/export:
122
122
123 archive create an unversioned archive of a repository revision
123 archive create an unversioned archive of a repository revision
124 bundle create a bundle file
124 bundle create a bundle file
125 export dump the header and diffs for one or more changesets
125 export dump the header and diffs for one or more changesets
126 import import an ordered set of patches
126 import import an ordered set of patches
127 unbundle apply one or more bundle files
127 unbundle apply one or more bundle files
128
128
129 Repository maintenance:
129 Repository maintenance:
130
130
131 manifest output the current or given revision of the project manifest
131 manifest output the current or given revision of the project manifest
132 recover roll back an interrupted transaction
132 recover roll back an interrupted transaction
133 verify verify the integrity of the repository
133 verify verify the integrity of the repository
134
134
135 Help:
135 Help:
136
136
137 config show combined config settings from all hgrc files
137 config show combined config settings from all hgrc files
138 help show help for a given topic or a help overview
138 help show help for a given topic or a help overview
139 version output version and copyright information
139 version output version and copyright information
140
140
141 additional help topics:
141 additional help topics:
142
142
143 Mercurial identifiers:
143 Mercurial identifiers:
144
144
145 filesets Specifying File Sets
145 filesets Specifying File Sets
146 hgignore Syntax for Mercurial Ignore Files
146 hgignore Syntax for Mercurial Ignore Files
147 patterns File Name Patterns
147 patterns File Name Patterns
148 revisions Specifying Revisions
148 revisions Specifying Revisions
149 urls URL Paths
149 urls URL Paths
150
150
151 Mercurial output:
151 Mercurial output:
152
152
153 color Colorizing Outputs
153 color Colorizing Outputs
154 dates Date Formats
154 dates Date Formats
155 diffs Diff Formats
155 diffs Diff Formats
156 templating Template Usage
156 templating Template Usage
157
157
158 Mercurial configuration:
158 Mercurial configuration:
159
159
160 config Configuration Files
160 config Configuration Files
161 environment Environment Variables
161 environment Environment Variables
162 extensions Using Additional Features
162 extensions Using Additional Features
163 flags Command-line flags
163 flags Command-line flags
164 hgweb Configuring hgweb
164 hgweb Configuring hgweb
165 merge-tools Merge Tools
165 merge-tools Merge Tools
166 pager Pager Support
166 pager Pager Support
167
167
168 Concepts:
168 Concepts:
169
169
170 bundlespec Bundle File Formats
170 bundlespec Bundle File Formats
171 glossary Glossary
171 glossary Glossary
172 phases Working with Phases
172 phases Working with Phases
173 subrepos Subrepositories
173 subrepos Subrepositories
174
174
175 Miscellaneous:
175 Miscellaneous:
176
176
177 deprecated Deprecated Features
177 deprecated Deprecated Features
178 internals Technical implementation topics
178 internals Technical implementation topics
179 scripting Using Mercurial from scripts and automation
179 scripting Using Mercurial from scripts and automation
180
180
181 (use 'hg help -v' to show built-in aliases and global options)
181 (use 'hg help -v' to show built-in aliases and global options)
182
182
183 $ hg -q help
183 $ hg -q help
184 Repository creation:
184 Repository creation:
185
185
186 clone make a copy of an existing repository
186 clone make a copy of an existing repository
187 init create a new repository in the given directory
187 init create a new repository in the given directory
188
188
189 Remote repository management:
189 Remote repository management:
190
190
191 incoming show new changesets found in source
191 incoming show new changesets found in source
192 outgoing show changesets not found in the destination
192 outgoing show changesets not found in the destination
193 paths show aliases for remote repositories
193 paths show aliases for remote repositories
194 pull pull changes from the specified source
194 pull pull changes from the specified source
195 push push changes to the specified destination
195 push push changes to the specified destination
196 serve start stand-alone webserver
196 serve start stand-alone webserver
197
197
198 Change creation:
198 Change creation:
199
199
200 commit commit the specified files or all outstanding changes
200 commit commit the specified files or all outstanding changes
201
201
202 Change manipulation:
202 Change manipulation:
203
203
204 backout reverse effect of earlier changeset
204 backout reverse effect of earlier changeset
205 graft copy changes from other branches onto the current branch
205 graft copy changes from other branches onto the current branch
206 merge merge another revision into working directory
206 merge merge another revision into working directory
207
207
208 Change organization:
208 Change organization:
209
209
210 bookmarks create a new bookmark or list existing bookmarks
210 bookmarks create a new bookmark or list existing bookmarks
211 branch set or show the current branch name
211 branch set or show the current branch name
212 branches list repository named branches
212 branches list repository named branches
213 phase set or show the current phase name
213 phase set or show the current phase name
214 tag add one or more tags for the current or given revision
214 tag add one or more tags for the current or given revision
215 tags list repository tags
215 tags list repository tags
216
216
217 File content management:
217 File content management:
218
218
219 annotate show changeset information by line for each file
219 annotate show changeset information by line for each file
220 cat output the current or given revision of files
220 cat output the current or given revision of files
221 copy mark files as copied for the next commit
221 copy mark files as copied for the next commit
222 diff diff repository (or selected files)
222 diff diff repository (or selected files)
223 grep search for a pattern in specified files
223 grep search for a pattern in specified files
224
224
225 Change navigation:
225 Change navigation:
226
226
227 bisect subdivision search of changesets
227 bisect subdivision search of changesets
228 heads show branch heads
228 heads show branch heads
229 identify identify the working directory or specified revision
229 identify identify the working directory or specified revision
230 log show revision history of entire repository or files
230 log show revision history of entire repository or files
231
231
232 Working directory management:
232 Working directory management:
233
233
234 add add the specified files on the next commit
234 add add the specified files on the next commit
235 addremove add all new files, delete all missing files
235 addremove add all new files, delete all missing files
236 files list tracked files
236 files list tracked files
237 forget forget the specified files on the next commit
237 forget forget the specified files on the next commit
238 remove remove the specified files on the next commit
238 remove remove the specified files on the next commit
239 rename rename files; equivalent of copy + remove
239 rename rename files; equivalent of copy + remove
240 resolve redo merges or set/view the merge status of files
240 resolve redo merges or set/view the merge status of files
241 revert restore files to their checkout state
241 revert restore files to their checkout state
242 root print the root (top) of the current working directory
242 root print the root (top) of the current working directory
243 shelve save and set aside changes from the working directory
243 shelve save and set aside changes from the working directory
244 status show changed files in the working directory
244 status show changed files in the working directory
245 summary summarize working directory state
245 summary summarize working directory state
246 unshelve restore a shelved change to the working directory
246 unshelve restore a shelved change to the working directory
247 update update working directory (or switch revisions)
247 update update working directory (or switch revisions)
248
248
249 Change import/export:
249 Change import/export:
250
250
251 archive create an unversioned archive of a repository revision
251 archive create an unversioned archive of a repository revision
252 bundle create a bundle file
252 bundle create a bundle file
253 export dump the header and diffs for one or more changesets
253 export dump the header and diffs for one or more changesets
254 import import an ordered set of patches
254 import import an ordered set of patches
255 unbundle apply one or more bundle files
255 unbundle apply one or more bundle files
256
256
257 Repository maintenance:
257 Repository maintenance:
258
258
259 manifest output the current or given revision of the project manifest
259 manifest output the current or given revision of the project manifest
260 recover roll back an interrupted transaction
260 recover roll back an interrupted transaction
261 verify verify the integrity of the repository
261 verify verify the integrity of the repository
262
262
263 Help:
263 Help:
264
264
265 config show combined config settings from all hgrc files
265 config show combined config settings from all hgrc files
266 help show help for a given topic or a help overview
266 help show help for a given topic or a help overview
267 version output version and copyright information
267 version output version and copyright information
268
268
269 additional help topics:
269 additional help topics:
270
270
271 Mercurial identifiers:
271 Mercurial identifiers:
272
272
273 filesets Specifying File Sets
273 filesets Specifying File Sets
274 hgignore Syntax for Mercurial Ignore Files
274 hgignore Syntax for Mercurial Ignore Files
275 patterns File Name Patterns
275 patterns File Name Patterns
276 revisions Specifying Revisions
276 revisions Specifying Revisions
277 urls URL Paths
277 urls URL Paths
278
278
279 Mercurial output:
279 Mercurial output:
280
280
281 color Colorizing Outputs
281 color Colorizing Outputs
282 dates Date Formats
282 dates Date Formats
283 diffs Diff Formats
283 diffs Diff Formats
284 templating Template Usage
284 templating Template Usage
285
285
286 Mercurial configuration:
286 Mercurial configuration:
287
287
288 config Configuration Files
288 config Configuration Files
289 environment Environment Variables
289 environment Environment Variables
290 extensions Using Additional Features
290 extensions Using Additional Features
291 flags Command-line flags
291 flags Command-line flags
292 hgweb Configuring hgweb
292 hgweb Configuring hgweb
293 merge-tools Merge Tools
293 merge-tools Merge Tools
294 pager Pager Support
294 pager Pager Support
295
295
296 Concepts:
296 Concepts:
297
297
298 bundlespec Bundle File Formats
298 bundlespec Bundle File Formats
299 glossary Glossary
299 glossary Glossary
300 phases Working with Phases
300 phases Working with Phases
301 subrepos Subrepositories
301 subrepos Subrepositories
302
302
303 Miscellaneous:
303 Miscellaneous:
304
304
305 deprecated Deprecated Features
305 deprecated Deprecated Features
306 internals Technical implementation topics
306 internals Technical implementation topics
307 scripting Using Mercurial from scripts and automation
307 scripting Using Mercurial from scripts and automation
308
308
309 Test extension help:
309 Test extension help:
310 $ hg help extensions --config extensions.rebase= --config extensions.children=
310 $ hg help extensions --config extensions.rebase= --config extensions.children=
311 Using Additional Features
311 Using Additional Features
312 """""""""""""""""""""""""
312 """""""""""""""""""""""""
313
313
314 Mercurial has the ability to add new features through the use of
314 Mercurial has the ability to add new features through the use of
315 extensions. Extensions may add new commands, add options to existing
315 extensions. Extensions may add new commands, add options to existing
316 commands, change the default behavior of commands, or implement hooks.
316 commands, change the default behavior of commands, or implement hooks.
317
317
318 To enable the "foo" extension, either shipped with Mercurial or in the
318 To enable the "foo" extension, either shipped with Mercurial or in the
319 Python search path, create an entry for it in your configuration file,
319 Python search path, create an entry for it in your configuration file,
320 like this:
320 like this:
321
321
322 [extensions]
322 [extensions]
323 foo =
323 foo =
324
324
325 You may also specify the full path to an extension:
325 You may also specify the full path to an extension:
326
326
327 [extensions]
327 [extensions]
328 myfeature = ~/.hgext/myfeature.py
328 myfeature = ~/.hgext/myfeature.py
329
329
330 See 'hg help config' for more information on configuration files.
330 See 'hg help config' for more information on configuration files.
331
331
332 Extensions are not loaded by default for a variety of reasons: they can
332 Extensions are not loaded by default for a variety of reasons: they can
333 increase startup overhead; they may be meant for advanced usage only; they
333 increase startup overhead; they may be meant for advanced usage only; they
334 may provide potentially dangerous abilities (such as letting you destroy
334 may provide potentially dangerous abilities (such as letting you destroy
335 or modify history); they might not be ready for prime time; or they may
335 or modify history); they might not be ready for prime time; or they may
336 alter some usual behaviors of stock Mercurial. It is thus up to the user
336 alter some usual behaviors of stock Mercurial. It is thus up to the user
337 to activate extensions as needed.
337 to activate extensions as needed.
338
338
339 To explicitly disable an extension enabled in a configuration file of
339 To explicitly disable an extension enabled in a configuration file of
340 broader scope, prepend its path with !:
340 broader scope, prepend its path with !:
341
341
342 [extensions]
342 [extensions]
343 # disabling extension bar residing in /path/to/extension/bar.py
343 # disabling extension bar residing in /path/to/extension/bar.py
344 bar = !/path/to/extension/bar.py
344 bar = !/path/to/extension/bar.py
345 # ditto, but no path was supplied for extension baz
345 # ditto, but no path was supplied for extension baz
346 baz = !
346 baz = !
347
347
348 enabled extensions:
348 enabled extensions:
349
349
350 children command to display child changesets (DEPRECATED)
350 children command to display child changesets (DEPRECATED)
351 rebase command to move sets of revisions to a different ancestor
351 rebase command to move sets of revisions to a different ancestor
352
352
353 disabled extensions:
353 disabled extensions:
354
354
355 acl hooks for controlling repository access
355 acl hooks for controlling repository access
356 blackbox log repository events to a blackbox for debugging
356 blackbox log repository events to a blackbox for debugging
357 bugzilla hooks for integrating with the Bugzilla bug tracker
357 bugzilla hooks for integrating with the Bugzilla bug tracker
358 censor erase file content at a given revision
358 censor erase file content at a given revision
359 churn command to display statistics about repository history
359 churn command to display statistics about repository history
360 clonebundles advertise pre-generated bundles to seed clones
360 clonebundles advertise pre-generated bundles to seed clones
361 closehead close arbitrary heads without checking them out first
361 closehead close arbitrary heads without checking them out first
362 convert import revisions from foreign VCS repositories into
362 convert import revisions from foreign VCS repositories into
363 Mercurial
363 Mercurial
364 eol automatically manage newlines in repository files
364 eol automatically manage newlines in repository files
365 extdiff command to allow external programs to compare revisions
365 extdiff command to allow external programs to compare revisions
366 factotum http authentication with factotum
366 factotum http authentication with factotum
367 fastexport export repositories as git fast-import stream
367 fastexport export repositories as git fast-import stream
368 githelp try mapping git commands to Mercurial commands
368 githelp try mapping git commands to Mercurial commands
369 gpg commands to sign and verify changesets
369 gpg commands to sign and verify changesets
370 hgk browse the repository in a graphical way
370 hgk browse the repository in a graphical way
371 highlight syntax highlighting for hgweb (requires Pygments)
371 highlight syntax highlighting for hgweb (requires Pygments)
372 histedit interactive history editing
372 histedit interactive history editing
373 keyword expand keywords in tracked files
373 keyword expand keywords in tracked files
374 largefiles track large binary files
374 largefiles track large binary files
375 mq manage a stack of patches
375 mq manage a stack of patches
376 notify hooks for sending email push notifications
376 notify hooks for sending email push notifications
377 patchbomb command to send changesets as (a series of) patch emails
377 patchbomb command to send changesets as (a series of) patch emails
378 purge command to delete untracked files from the working
378 purge command to delete untracked files from the working
379 directory
379 directory
380 relink recreates hardlinks between repository clones
380 relink recreates hardlinks between repository clones
381 schemes extend schemes with shortcuts to repository swarms
381 schemes extend schemes with shortcuts to repository swarms
382 share share a common history between several working directories
382 share share a common history between several working directories
383 strip strip changesets and their descendants from history
384 transplant command to transplant changesets from another branch
383 transplant command to transplant changesets from another branch
385 win32mbcs allow the use of MBCS paths with problematic encodings
384 win32mbcs allow the use of MBCS paths with problematic encodings
386 zeroconf discover and advertise repositories on the local network
385 zeroconf discover and advertise repositories on the local network
387
386
388 #endif
387 #endif
389
388
390 Verify that deprecated extensions are included if --verbose:
389 Verify that deprecated extensions are included if --verbose:
391
390
392 $ hg -v help extensions | grep children
391 $ hg -v help extensions | grep children
393 children command to display child changesets (DEPRECATED)
392 children command to display child changesets (DEPRECATED)
394
393
395 Verify that extension keywords appear in help templates
394 Verify that extension keywords appear in help templates
396
395
397 $ hg help --config extensions.transplant= templating|grep transplant > /dev/null
396 $ hg help --config extensions.transplant= templating|grep transplant > /dev/null
398
397
399 Test short command list with verbose option
398 Test short command list with verbose option
400
399
401 $ hg -v help shortlist
400 $ hg -v help shortlist
402 Mercurial Distributed SCM
401 Mercurial Distributed SCM
403
402
404 basic commands:
403 basic commands:
405
404
406 abort abort an unfinished operation (EXPERIMENTAL)
405 abort abort an unfinished operation (EXPERIMENTAL)
407 add add the specified files on the next commit
406 add add the specified files on the next commit
408 annotate, blame
407 annotate, blame
409 show changeset information by line for each file
408 show changeset information by line for each file
410 clone make a copy of an existing repository
409 clone make a copy of an existing repository
411 commit, ci commit the specified files or all outstanding changes
410 commit, ci commit the specified files or all outstanding changes
412 continue resumes an interrupted operation (EXPERIMENTAL)
411 continue resumes an interrupted operation (EXPERIMENTAL)
413 diff diff repository (or selected files)
412 diff diff repository (or selected files)
414 export dump the header and diffs for one or more changesets
413 export dump the header and diffs for one or more changesets
415 forget forget the specified files on the next commit
414 forget forget the specified files on the next commit
416 init create a new repository in the given directory
415 init create a new repository in the given directory
417 log, history show revision history of entire repository or files
416 log, history show revision history of entire repository or files
418 merge merge another revision into working directory
417 merge merge another revision into working directory
419 pull pull changes from the specified source
418 pull pull changes from the specified source
420 push push changes to the specified destination
419 push push changes to the specified destination
421 remove, rm remove the specified files on the next commit
420 remove, rm remove the specified files on the next commit
422 serve start stand-alone webserver
421 serve start stand-alone webserver
423 status, st show changed files in the working directory
422 status, st show changed files in the working directory
424 summary, sum summarize working directory state
423 summary, sum summarize working directory state
425 update, up, checkout, co
424 update, up, checkout, co
426 update working directory (or switch revisions)
425 update working directory (or switch revisions)
427
426
428 global options ([+] can be repeated):
427 global options ([+] can be repeated):
429
428
430 -R --repository REPO repository root directory or name of overlay bundle
429 -R --repository REPO repository root directory or name of overlay bundle
431 file
430 file
432 --cwd DIR change working directory
431 --cwd DIR change working directory
433 -y --noninteractive do not prompt, automatically pick the first choice for
432 -y --noninteractive do not prompt, automatically pick the first choice for
434 all prompts
433 all prompts
435 -q --quiet suppress output
434 -q --quiet suppress output
436 -v --verbose enable additional output
435 -v --verbose enable additional output
437 --color TYPE when to colorize (boolean, always, auto, never, or
436 --color TYPE when to colorize (boolean, always, auto, never, or
438 debug)
437 debug)
439 --config CONFIG [+] set/override config option (use 'section.name=value')
438 --config CONFIG [+] set/override config option (use 'section.name=value')
440 --debug enable debugging output
439 --debug enable debugging output
441 --debugger start debugger
440 --debugger start debugger
442 --encoding ENCODE set the charset encoding (default: ascii)
441 --encoding ENCODE set the charset encoding (default: ascii)
443 --encodingmode MODE set the charset encoding mode (default: strict)
442 --encodingmode MODE set the charset encoding mode (default: strict)
444 --traceback always print a traceback on exception
443 --traceback always print a traceback on exception
445 --time time how long the command takes
444 --time time how long the command takes
446 --profile print command execution profile
445 --profile print command execution profile
447 --version output version information and exit
446 --version output version information and exit
448 -h --help display help and exit
447 -h --help display help and exit
449 --hidden consider hidden changesets
448 --hidden consider hidden changesets
450 --pager TYPE when to paginate (boolean, always, auto, or never)
449 --pager TYPE when to paginate (boolean, always, auto, or never)
451 (default: auto)
450 (default: auto)
452
451
453 (use 'hg help' for the full list of commands)
452 (use 'hg help' for the full list of commands)
454
453
455 $ hg add -h
454 $ hg add -h
456 hg add [OPTION]... [FILE]...
455 hg add [OPTION]... [FILE]...
457
456
458 add the specified files on the next commit
457 add the specified files on the next commit
459
458
460 Schedule files to be version controlled and added to the repository.
459 Schedule files to be version controlled and added to the repository.
461
460
462 The files will be added to the repository at the next commit. To undo an
461 The files will be added to the repository at the next commit. To undo an
463 add before that, see 'hg forget'.
462 add before that, see 'hg forget'.
464
463
465 If no names are given, add all files to the repository (except files
464 If no names are given, add all files to the repository (except files
466 matching ".hgignore").
465 matching ".hgignore").
467
466
468 Returns 0 if all files are successfully added.
467 Returns 0 if all files are successfully added.
469
468
470 options ([+] can be repeated):
469 options ([+] can be repeated):
471
470
472 -I --include PATTERN [+] include names matching the given patterns
471 -I --include PATTERN [+] include names matching the given patterns
473 -X --exclude PATTERN [+] exclude names matching the given patterns
472 -X --exclude PATTERN [+] exclude names matching the given patterns
474 -S --subrepos recurse into subrepositories
473 -S --subrepos recurse into subrepositories
475 -n --dry-run do not perform actions, just print output
474 -n --dry-run do not perform actions, just print output
476
475
477 (some details hidden, use --verbose to show complete help)
476 (some details hidden, use --verbose to show complete help)
478
477
479 Verbose help for add
478 Verbose help for add
480
479
481 $ hg add -hv
480 $ hg add -hv
482 hg add [OPTION]... [FILE]...
481 hg add [OPTION]... [FILE]...
483
482
484 add the specified files on the next commit
483 add the specified files on the next commit
485
484
486 Schedule files to be version controlled and added to the repository.
485 Schedule files to be version controlled and added to the repository.
487
486
488 The files will be added to the repository at the next commit. To undo an
487 The files will be added to the repository at the next commit. To undo an
489 add before that, see 'hg forget'.
488 add before that, see 'hg forget'.
490
489
491 If no names are given, add all files to the repository (except files
490 If no names are given, add all files to the repository (except files
492 matching ".hgignore").
491 matching ".hgignore").
493
492
494 Examples:
493 Examples:
495
494
496 - New (unknown) files are added automatically by 'hg add':
495 - New (unknown) files are added automatically by 'hg add':
497
496
498 $ ls
497 $ ls
499 foo.c
498 foo.c
500 $ hg status
499 $ hg status
501 ? foo.c
500 ? foo.c
502 $ hg add
501 $ hg add
503 adding foo.c
502 adding foo.c
504 $ hg status
503 $ hg status
505 A foo.c
504 A foo.c
506
505
507 - Specific files to be added can be specified:
506 - Specific files to be added can be specified:
508
507
509 $ ls
508 $ ls
510 bar.c foo.c
509 bar.c foo.c
511 $ hg status
510 $ hg status
512 ? bar.c
511 ? bar.c
513 ? foo.c
512 ? foo.c
514 $ hg add bar.c
513 $ hg add bar.c
515 $ hg status
514 $ hg status
516 A bar.c
515 A bar.c
517 ? foo.c
516 ? foo.c
518
517
519 Returns 0 if all files are successfully added.
518 Returns 0 if all files are successfully added.
520
519
521 options ([+] can be repeated):
520 options ([+] can be repeated):
522
521
523 -I --include PATTERN [+] include names matching the given patterns
522 -I --include PATTERN [+] include names matching the given patterns
524 -X --exclude PATTERN [+] exclude names matching the given patterns
523 -X --exclude PATTERN [+] exclude names matching the given patterns
525 -S --subrepos recurse into subrepositories
524 -S --subrepos recurse into subrepositories
526 -n --dry-run do not perform actions, just print output
525 -n --dry-run do not perform actions, just print output
527
526
528 global options ([+] can be repeated):
527 global options ([+] can be repeated):
529
528
530 -R --repository REPO repository root directory or name of overlay bundle
529 -R --repository REPO repository root directory or name of overlay bundle
531 file
530 file
532 --cwd DIR change working directory
531 --cwd DIR change working directory
533 -y --noninteractive do not prompt, automatically pick the first choice for
532 -y --noninteractive do not prompt, automatically pick the first choice for
534 all prompts
533 all prompts
535 -q --quiet suppress output
534 -q --quiet suppress output
536 -v --verbose enable additional output
535 -v --verbose enable additional output
537 --color TYPE when to colorize (boolean, always, auto, never, or
536 --color TYPE when to colorize (boolean, always, auto, never, or
538 debug)
537 debug)
539 --config CONFIG [+] set/override config option (use 'section.name=value')
538 --config CONFIG [+] set/override config option (use 'section.name=value')
540 --debug enable debugging output
539 --debug enable debugging output
541 --debugger start debugger
540 --debugger start debugger
542 --encoding ENCODE set the charset encoding (default: ascii)
541 --encoding ENCODE set the charset encoding (default: ascii)
543 --encodingmode MODE set the charset encoding mode (default: strict)
542 --encodingmode MODE set the charset encoding mode (default: strict)
544 --traceback always print a traceback on exception
543 --traceback always print a traceback on exception
545 --time time how long the command takes
544 --time time how long the command takes
546 --profile print command execution profile
545 --profile print command execution profile
547 --version output version information and exit
546 --version output version information and exit
548 -h --help display help and exit
547 -h --help display help and exit
549 --hidden consider hidden changesets
548 --hidden consider hidden changesets
550 --pager TYPE when to paginate (boolean, always, auto, or never)
549 --pager TYPE when to paginate (boolean, always, auto, or never)
551 (default: auto)
550 (default: auto)
552
551
553 Test the textwidth config option
552 Test the textwidth config option
554
553
555 $ hg root -h --config ui.textwidth=50
554 $ hg root -h --config ui.textwidth=50
556 hg root
555 hg root
557
556
558 print the root (top) of the current working
557 print the root (top) of the current working
559 directory
558 directory
560
559
561 Print the root directory of the current
560 Print the root directory of the current
562 repository.
561 repository.
563
562
564 Returns 0 on success.
563 Returns 0 on success.
565
564
566 options:
565 options:
567
566
568 -T --template TEMPLATE display with template
567 -T --template TEMPLATE display with template
569
568
570 (some details hidden, use --verbose to show
569 (some details hidden, use --verbose to show
571 complete help)
570 complete help)
572
571
573 Test help option with version option
572 Test help option with version option
574
573
575 $ hg add -h --version
574 $ hg add -h --version
576 Mercurial Distributed SCM (version *) (glob)
575 Mercurial Distributed SCM (version *) (glob)
577 (see https://mercurial-scm.org for more information)
576 (see https://mercurial-scm.org for more information)
578
577
579 Copyright (C) 2005-* Matt Mackall and others (glob)
578 Copyright (C) 2005-* Matt Mackall and others (glob)
580 This is free software; see the source for copying conditions. There is NO
579 This is free software; see the source for copying conditions. There is NO
581 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
580 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
582
581
583 $ hg add --skjdfks
582 $ hg add --skjdfks
584 hg add: option --skjdfks not recognized
583 hg add: option --skjdfks not recognized
585 hg add [OPTION]... [FILE]...
584 hg add [OPTION]... [FILE]...
586
585
587 add the specified files on the next commit
586 add the specified files on the next commit
588
587
589 options ([+] can be repeated):
588 options ([+] can be repeated):
590
589
591 -I --include PATTERN [+] include names matching the given patterns
590 -I --include PATTERN [+] include names matching the given patterns
592 -X --exclude PATTERN [+] exclude names matching the given patterns
591 -X --exclude PATTERN [+] exclude names matching the given patterns
593 -S --subrepos recurse into subrepositories
592 -S --subrepos recurse into subrepositories
594 -n --dry-run do not perform actions, just print output
593 -n --dry-run do not perform actions, just print output
595
594
596 (use 'hg add -h' to show more help)
595 (use 'hg add -h' to show more help)
597 [255]
596 [255]
598
597
599 Test ambiguous command help
598 Test ambiguous command help
600
599
601 $ hg help ad
600 $ hg help ad
602 list of commands:
601 list of commands:
603
602
604 add add the specified files on the next commit
603 add add the specified files on the next commit
605 addremove add all new files, delete all missing files
604 addremove add all new files, delete all missing files
606
605
607 (use 'hg help -v ad' to show built-in aliases and global options)
606 (use 'hg help -v ad' to show built-in aliases and global options)
608
607
609 Test command without options
608 Test command without options
610
609
611 $ hg help verify
610 $ hg help verify
612 hg verify
611 hg verify
613
612
614 verify the integrity of the repository
613 verify the integrity of the repository
615
614
616 Verify the integrity of the current repository.
615 Verify the integrity of the current repository.
617
616
618 This will perform an extensive check of the repository's integrity,
617 This will perform an extensive check of the repository's integrity,
619 validating the hashes and checksums of each entry in the changelog,
618 validating the hashes and checksums of each entry in the changelog,
620 manifest, and tracked files, as well as the integrity of their crosslinks
619 manifest, and tracked files, as well as the integrity of their crosslinks
621 and indices.
620 and indices.
622
621
623 Please see https://mercurial-scm.org/wiki/RepositoryCorruption for more
622 Please see https://mercurial-scm.org/wiki/RepositoryCorruption for more
624 information about recovery from corruption of the repository.
623 information about recovery from corruption of the repository.
625
624
626 Returns 0 on success, 1 if errors are encountered.
625 Returns 0 on success, 1 if errors are encountered.
627
626
628 options:
627 options:
629
628
630 (some details hidden, use --verbose to show complete help)
629 (some details hidden, use --verbose to show complete help)
631
630
632 $ hg help diff
631 $ hg help diff
633 hg diff [OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...
632 hg diff [OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...
634
633
635 diff repository (or selected files)
634 diff repository (or selected files)
636
635
637 Show differences between revisions for the specified files.
636 Show differences between revisions for the specified files.
638
637
639 Differences between files are shown using the unified diff format.
638 Differences between files are shown using the unified diff format.
640
639
641 Note:
640 Note:
642 'hg diff' may generate unexpected results for merges, as it will
641 'hg diff' may generate unexpected results for merges, as it will
643 default to comparing against the working directory's first parent
642 default to comparing against the working directory's first parent
644 changeset if no revisions are specified.
643 changeset if no revisions are specified.
645
644
646 When two revision arguments are given, then changes are shown between
645 When two revision arguments are given, then changes are shown between
647 those revisions. If only one revision is specified then that revision is
646 those revisions. If only one revision is specified then that revision is
648 compared to the working directory, and, when no revisions are specified,
647 compared to the working directory, and, when no revisions are specified,
649 the working directory files are compared to its first parent.
648 the working directory files are compared to its first parent.
650
649
651 Alternatively you can specify -c/--change with a revision to see the
650 Alternatively you can specify -c/--change with a revision to see the
652 changes in that changeset relative to its first parent.
651 changes in that changeset relative to its first parent.
653
652
654 Without the -a/--text option, diff will avoid generating diffs of files it
653 Without the -a/--text option, diff will avoid generating diffs of files it
655 detects as binary. With -a, diff will generate a diff anyway, probably
654 detects as binary. With -a, diff will generate a diff anyway, probably
656 with undesirable results.
655 with undesirable results.
657
656
658 Use the -g/--git option to generate diffs in the git extended diff format.
657 Use the -g/--git option to generate diffs in the git extended diff format.
659 For more information, read 'hg help diffs'.
658 For more information, read 'hg help diffs'.
660
659
661 Returns 0 on success.
660 Returns 0 on success.
662
661
663 options ([+] can be repeated):
662 options ([+] can be repeated):
664
663
665 -r --rev REV [+] revision
664 -r --rev REV [+] revision
666 -c --change REV change made by revision
665 -c --change REV change made by revision
667 -a --text treat all files as text
666 -a --text treat all files as text
668 -g --git use git extended diff format
667 -g --git use git extended diff format
669 --binary generate binary diffs in git mode (default)
668 --binary generate binary diffs in git mode (default)
670 --nodates omit dates from diff headers
669 --nodates omit dates from diff headers
671 --noprefix omit a/ and b/ prefixes from filenames
670 --noprefix omit a/ and b/ prefixes from filenames
672 -p --show-function show which function each change is in
671 -p --show-function show which function each change is in
673 --reverse produce a diff that undoes the changes
672 --reverse produce a diff that undoes the changes
674 -w --ignore-all-space ignore white space when comparing lines
673 -w --ignore-all-space ignore white space when comparing lines
675 -b --ignore-space-change ignore changes in the amount of white space
674 -b --ignore-space-change ignore changes in the amount of white space
676 -B --ignore-blank-lines ignore changes whose lines are all blank
675 -B --ignore-blank-lines ignore changes whose lines are all blank
677 -Z --ignore-space-at-eol ignore changes in whitespace at EOL
676 -Z --ignore-space-at-eol ignore changes in whitespace at EOL
678 -U --unified NUM number of lines of context to show
677 -U --unified NUM number of lines of context to show
679 --stat output diffstat-style summary of changes
678 --stat output diffstat-style summary of changes
680 --root DIR produce diffs relative to subdirectory
679 --root DIR produce diffs relative to subdirectory
681 -I --include PATTERN [+] include names matching the given patterns
680 -I --include PATTERN [+] include names matching the given patterns
682 -X --exclude PATTERN [+] exclude names matching the given patterns
681 -X --exclude PATTERN [+] exclude names matching the given patterns
683 -S --subrepos recurse into subrepositories
682 -S --subrepos recurse into subrepositories
684
683
685 (some details hidden, use --verbose to show complete help)
684 (some details hidden, use --verbose to show complete help)
686
685
687 $ hg help status
686 $ hg help status
688 hg status [OPTION]... [FILE]...
687 hg status [OPTION]... [FILE]...
689
688
690 aliases: st
689 aliases: st
691
690
692 show changed files in the working directory
691 show changed files in the working directory
693
692
694 Show status of files in the repository. If names are given, only files
693 Show status of files in the repository. If names are given, only files
695 that match are shown. Files that are clean or ignored or the source of a
694 that match are shown. Files that are clean or ignored or the source of a
696 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
695 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
697 -C/--copies or -A/--all are given. Unless options described with "show
696 -C/--copies or -A/--all are given. Unless options described with "show
698 only ..." are given, the options -mardu are used.
697 only ..." are given, the options -mardu are used.
699
698
700 Option -q/--quiet hides untracked (unknown and ignored) files unless
699 Option -q/--quiet hides untracked (unknown and ignored) files unless
701 explicitly requested with -u/--unknown or -i/--ignored.
700 explicitly requested with -u/--unknown or -i/--ignored.
702
701
703 Note:
702 Note:
704 'hg status' may appear to disagree with diff if permissions have
703 'hg status' may appear to disagree with diff if permissions have
705 changed or a merge has occurred. The standard diff format does not
704 changed or a merge has occurred. The standard diff format does not
706 report permission changes and diff only reports changes relative to one
705 report permission changes and diff only reports changes relative to one
707 merge parent.
706 merge parent.
708
707
709 If one revision is given, it is used as the base revision. If two
708 If one revision is given, it is used as the base revision. If two
710 revisions are given, the differences between them are shown. The --change
709 revisions are given, the differences between them are shown. The --change
711 option can also be used as a shortcut to list the changed files of a
710 option can also be used as a shortcut to list the changed files of a
712 revision from its first parent.
711 revision from its first parent.
713
712
714 The codes used to show the status of files are:
713 The codes used to show the status of files are:
715
714
716 M = modified
715 M = modified
717 A = added
716 A = added
718 R = removed
717 R = removed
719 C = clean
718 C = clean
720 ! = missing (deleted by non-hg command, but still tracked)
719 ! = missing (deleted by non-hg command, but still tracked)
721 ? = not tracked
720 ? = not tracked
722 I = ignored
721 I = ignored
723 = origin of the previous file (with --copies)
722 = origin of the previous file (with --copies)
724
723
725 Returns 0 on success.
724 Returns 0 on success.
726
725
727 options ([+] can be repeated):
726 options ([+] can be repeated):
728
727
729 -A --all show status of all files
728 -A --all show status of all files
730 -m --modified show only modified files
729 -m --modified show only modified files
731 -a --added show only added files
730 -a --added show only added files
732 -r --removed show only removed files
731 -r --removed show only removed files
733 -d --deleted show only missing files
732 -d --deleted show only missing files
734 -c --clean show only files without changes
733 -c --clean show only files without changes
735 -u --unknown show only unknown (not tracked) files
734 -u --unknown show only unknown (not tracked) files
736 -i --ignored show only ignored files
735 -i --ignored show only ignored files
737 -n --no-status hide status prefix
736 -n --no-status hide status prefix
738 -C --copies show source of copied files
737 -C --copies show source of copied files
739 -0 --print0 end filenames with NUL, for use with xargs
738 -0 --print0 end filenames with NUL, for use with xargs
740 --rev REV [+] show difference from revision
739 --rev REV [+] show difference from revision
741 --change REV list the changed files of a revision
740 --change REV list the changed files of a revision
742 -I --include PATTERN [+] include names matching the given patterns
741 -I --include PATTERN [+] include names matching the given patterns
743 -X --exclude PATTERN [+] exclude names matching the given patterns
742 -X --exclude PATTERN [+] exclude names matching the given patterns
744 -S --subrepos recurse into subrepositories
743 -S --subrepos recurse into subrepositories
745 -T --template TEMPLATE display with template
744 -T --template TEMPLATE display with template
746
745
747 (some details hidden, use --verbose to show complete help)
746 (some details hidden, use --verbose to show complete help)
748
747
749 $ hg -q help status
748 $ hg -q help status
750 hg status [OPTION]... [FILE]...
749 hg status [OPTION]... [FILE]...
751
750
752 show changed files in the working directory
751 show changed files in the working directory
753
752
754 $ hg help foo
753 $ hg help foo
755 abort: no such help topic: foo
754 abort: no such help topic: foo
756 (try 'hg help --keyword foo')
755 (try 'hg help --keyword foo')
757 [255]
756 [255]
758
757
759 $ hg skjdfks
758 $ hg skjdfks
760 hg: unknown command 'skjdfks'
759 hg: unknown command 'skjdfks'
761 (use 'hg help' for a list of commands)
760 (use 'hg help' for a list of commands)
762 [255]
761 [255]
763
762
764 Typoed command gives suggestion
763 Typoed command gives suggestion
765 $ hg puls
764 $ hg puls
766 hg: unknown command 'puls'
765 hg: unknown command 'puls'
767 (did you mean one of pull, push?)
766 (did you mean one of pull, push?)
768 [255]
767 [255]
769
768
770 Not enabled extension gets suggested
769 Not enabled extension gets suggested
771
770
772 $ hg rebase
771 $ hg rebase
773 hg: unknown command 'rebase'
772 hg: unknown command 'rebase'
774 'rebase' is provided by the following extension:
773 'rebase' is provided by the following extension:
775
774
776 rebase command to move sets of revisions to a different ancestor
775 rebase command to move sets of revisions to a different ancestor
777
776
778 (use 'hg help extensions' for information on enabling extensions)
777 (use 'hg help extensions' for information on enabling extensions)
779 [255]
778 [255]
780
779
781 Disabled extension gets suggested
780 Disabled extension gets suggested
782 $ hg --config extensions.rebase=! rebase
781 $ hg --config extensions.rebase=! rebase
783 hg: unknown command 'rebase'
782 hg: unknown command 'rebase'
784 'rebase' is provided by the following extension:
783 'rebase' is provided by the following extension:
785
784
786 rebase command to move sets of revisions to a different ancestor
785 rebase command to move sets of revisions to a different ancestor
787
786
788 (use 'hg help extensions' for information on enabling extensions)
787 (use 'hg help extensions' for information on enabling extensions)
789 [255]
788 [255]
790
789
791 Checking that help adapts based on the config:
790 Checking that help adapts based on the config:
792
791
793 $ hg help diff --config ui.tweakdefaults=true | egrep -e '^ *(-g|config)'
792 $ hg help diff --config ui.tweakdefaults=true | egrep -e '^ *(-g|config)'
794 -g --[no-]git use git extended diff format (default: on from
793 -g --[no-]git use git extended diff format (default: on from
795 config)
794 config)
796
795
797 Make sure that we don't run afoul of the help system thinking that
796 Make sure that we don't run afoul of the help system thinking that
798 this is a section and erroring out weirdly.
797 this is a section and erroring out weirdly.
799
798
800 $ hg .log
799 $ hg .log
801 hg: unknown command '.log'
800 hg: unknown command '.log'
802 (did you mean log?)
801 (did you mean log?)
803 [255]
802 [255]
804
803
805 $ hg log.
804 $ hg log.
806 hg: unknown command 'log.'
805 hg: unknown command 'log.'
807 (did you mean log?)
806 (did you mean log?)
808 [255]
807 [255]
809 $ hg pu.lh
808 $ hg pu.lh
810 hg: unknown command 'pu.lh'
809 hg: unknown command 'pu.lh'
811 (did you mean one of pull, push?)
810 (did you mean one of pull, push?)
812 [255]
811 [255]
813
812
814 $ cat > helpext.py <<EOF
813 $ cat > helpext.py <<EOF
815 > import os
814 > import os
816 > from mercurial import commands, fancyopts, registrar
815 > from mercurial import commands, fancyopts, registrar
817 >
816 >
818 > def func(arg):
817 > def func(arg):
819 > return '%sfoo' % arg
818 > return '%sfoo' % arg
820 > class customopt(fancyopts.customopt):
819 > class customopt(fancyopts.customopt):
821 > def newstate(self, oldstate, newparam, abort):
820 > def newstate(self, oldstate, newparam, abort):
822 > return '%sbar' % oldstate
821 > return '%sbar' % oldstate
823 > cmdtable = {}
822 > cmdtable = {}
824 > command = registrar.command(cmdtable)
823 > command = registrar.command(cmdtable)
825 >
824 >
826 > @command(b'nohelp',
825 > @command(b'nohelp',
827 > [(b'', b'longdesc', 3, b'x'*67),
826 > [(b'', b'longdesc', 3, b'x'*67),
828 > (b'n', b'', None, b'normal desc'),
827 > (b'n', b'', None, b'normal desc'),
829 > (b'', b'newline', b'', b'line1\nline2'),
828 > (b'', b'newline', b'', b'line1\nline2'),
830 > (b'', b'default-off', False, b'enable X'),
829 > (b'', b'default-off', False, b'enable X'),
831 > (b'', b'default-on', True, b'enable Y'),
830 > (b'', b'default-on', True, b'enable Y'),
832 > (b'', b'callableopt', func, b'adds foo'),
831 > (b'', b'callableopt', func, b'adds foo'),
833 > (b'', b'customopt', customopt(''), b'adds bar'),
832 > (b'', b'customopt', customopt(''), b'adds bar'),
834 > (b'', b'customopt-withdefault', customopt('foo'), b'adds bar')],
833 > (b'', b'customopt-withdefault', customopt('foo'), b'adds bar')],
835 > b'hg nohelp',
834 > b'hg nohelp',
836 > norepo=True)
835 > norepo=True)
837 > @command(b'debugoptADV', [(b'', b'aopt', None, b'option is (ADVANCED)')])
836 > @command(b'debugoptADV', [(b'', b'aopt', None, b'option is (ADVANCED)')])
838 > @command(b'debugoptDEP', [(b'', b'dopt', None, b'option is (DEPRECATED)')])
837 > @command(b'debugoptDEP', [(b'', b'dopt', None, b'option is (DEPRECATED)')])
839 > @command(b'debugoptEXP', [(b'', b'eopt', None, b'option is (EXPERIMENTAL)')])
838 > @command(b'debugoptEXP', [(b'', b'eopt', None, b'option is (EXPERIMENTAL)')])
840 > def nohelp(ui, *args, **kwargs):
839 > def nohelp(ui, *args, **kwargs):
841 > pass
840 > pass
842 >
841 >
843 > @command(b'hashelp', [], b'hg hashelp', norepo=True)
842 > @command(b'hashelp', [], b'hg hashelp', norepo=True)
844 > def hashelp(ui, *args, **kwargs):
843 > def hashelp(ui, *args, **kwargs):
845 > """Extension command's help"""
844 > """Extension command's help"""
846 >
845 >
847 > def uisetup(ui):
846 > def uisetup(ui):
848 > ui.setconfig(b'alias', b'shellalias', b'!echo hi', b'helpext')
847 > ui.setconfig(b'alias', b'shellalias', b'!echo hi', b'helpext')
849 > ui.setconfig(b'alias', b'hgalias', b'summary', b'helpext')
848 > ui.setconfig(b'alias', b'hgalias', b'summary', b'helpext')
850 > ui.setconfig(b'alias', b'hgalias:doc', b'My doc', b'helpext')
849 > ui.setconfig(b'alias', b'hgalias:doc', b'My doc', b'helpext')
851 > ui.setconfig(b'alias', b'hgalias:category', b'navigation', b'helpext')
850 > ui.setconfig(b'alias', b'hgalias:category', b'navigation', b'helpext')
852 > ui.setconfig(b'alias', b'hgaliasnodoc', b'summary', b'helpext')
851 > ui.setconfig(b'alias', b'hgaliasnodoc', b'summary', b'helpext')
853 >
852 >
854 > EOF
853 > EOF
855 $ echo '[extensions]' >> $HGRCPATH
854 $ echo '[extensions]' >> $HGRCPATH
856 $ echo "helpext = `pwd`/helpext.py" >> $HGRCPATH
855 $ echo "helpext = `pwd`/helpext.py" >> $HGRCPATH
857
856
858 Test for aliases
857 Test for aliases
859
858
860 $ hg help | grep hgalias
859 $ hg help | grep hgalias
861 hgalias My doc
860 hgalias My doc
862
861
863 $ hg help hgalias
862 $ hg help hgalias
864 hg hgalias [--remote]
863 hg hgalias [--remote]
865
864
866 alias for: hg summary
865 alias for: hg summary
867
866
868 My doc
867 My doc
869
868
870 defined by: helpext
869 defined by: helpext
871
870
872 options:
871 options:
873
872
874 --remote check for push and pull
873 --remote check for push and pull
875
874
876 (some details hidden, use --verbose to show complete help)
875 (some details hidden, use --verbose to show complete help)
877 $ hg help hgaliasnodoc
876 $ hg help hgaliasnodoc
878 hg hgaliasnodoc [--remote]
877 hg hgaliasnodoc [--remote]
879
878
880 alias for: hg summary
879 alias for: hg summary
881
880
882 summarize working directory state
881 summarize working directory state
883
882
884 This generates a brief summary of the working directory state, including
883 This generates a brief summary of the working directory state, including
885 parents, branch, commit status, phase and available updates.
884 parents, branch, commit status, phase and available updates.
886
885
887 With the --remote option, this will check the default paths for incoming
886 With the --remote option, this will check the default paths for incoming
888 and outgoing changes. This can be time-consuming.
887 and outgoing changes. This can be time-consuming.
889
888
890 Returns 0 on success.
889 Returns 0 on success.
891
890
892 defined by: helpext
891 defined by: helpext
893
892
894 options:
893 options:
895
894
896 --remote check for push and pull
895 --remote check for push and pull
897
896
898 (some details hidden, use --verbose to show complete help)
897 (some details hidden, use --verbose to show complete help)
899
898
900 $ hg help shellalias
899 $ hg help shellalias
901 hg shellalias
900 hg shellalias
902
901
903 shell alias for: echo hi
902 shell alias for: echo hi
904
903
905 (no help text available)
904 (no help text available)
906
905
907 defined by: helpext
906 defined by: helpext
908
907
909 (some details hidden, use --verbose to show complete help)
908 (some details hidden, use --verbose to show complete help)
910
909
911 Test command with no help text
910 Test command with no help text
912
911
913 $ hg help nohelp
912 $ hg help nohelp
914 hg nohelp
913 hg nohelp
915
914
916 (no help text available)
915 (no help text available)
917
916
918 options:
917 options:
919
918
920 --longdesc VALUE
919 --longdesc VALUE
921 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
920 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
922 xxxxxxxxxxxxxxxxxxxxxxx (default: 3)
921 xxxxxxxxxxxxxxxxxxxxxxx (default: 3)
923 -n -- normal desc
922 -n -- normal desc
924 --newline VALUE line1 line2
923 --newline VALUE line1 line2
925 --default-off enable X
924 --default-off enable X
926 --[no-]default-on enable Y (default: on)
925 --[no-]default-on enable Y (default: on)
927 --callableopt VALUE adds foo
926 --callableopt VALUE adds foo
928 --customopt VALUE adds bar
927 --customopt VALUE adds bar
929 --customopt-withdefault VALUE adds bar (default: foo)
928 --customopt-withdefault VALUE adds bar (default: foo)
930
929
931 (some details hidden, use --verbose to show complete help)
930 (some details hidden, use --verbose to show complete help)
932
931
933 Test that default list of commands includes extension commands that have help,
932 Test that default list of commands includes extension commands that have help,
934 but not those that don't, except in verbose mode, when a keyword is passed, or
933 but not those that don't, except in verbose mode, when a keyword is passed, or
935 when help about the extension is requested.
934 when help about the extension is requested.
936
935
937 #if no-extraextensions
936 #if no-extraextensions
938
937
939 $ hg help | grep hashelp
938 $ hg help | grep hashelp
940 hashelp Extension command's help
939 hashelp Extension command's help
941 $ hg help | grep nohelp
940 $ hg help | grep nohelp
942 [1]
941 [1]
943 $ hg help -v | grep nohelp
942 $ hg help -v | grep nohelp
944 nohelp (no help text available)
943 nohelp (no help text available)
945
944
946 $ hg help -k nohelp
945 $ hg help -k nohelp
947 Commands:
946 Commands:
948
947
949 nohelp hg nohelp
948 nohelp hg nohelp
950
949
951 Extension Commands:
950 Extension Commands:
952
951
953 nohelp (no help text available)
952 nohelp (no help text available)
954
953
955 $ hg help helpext
954 $ hg help helpext
956 helpext extension - no help text available
955 helpext extension - no help text available
957
956
958 list of commands:
957 list of commands:
959
958
960 hashelp Extension command's help
959 hashelp Extension command's help
961 nohelp (no help text available)
960 nohelp (no help text available)
962
961
963 (use 'hg help -v helpext' to show built-in aliases and global options)
962 (use 'hg help -v helpext' to show built-in aliases and global options)
964
963
965 #endif
964 #endif
966
965
967 Test list of internal help commands
966 Test list of internal help commands
968
967
969 $ hg help debug
968 $ hg help debug
970 debug commands (internal and unsupported):
969 debug commands (internal and unsupported):
971
970
972 debugancestor
971 debugancestor
973 find the ancestor revision of two revisions in a given index
972 find the ancestor revision of two revisions in a given index
974 debugantivirusrunning
973 debugantivirusrunning
975 attempt to trigger an antivirus scanner to see if one is active
974 attempt to trigger an antivirus scanner to see if one is active
976 debugapplystreamclonebundle
975 debugapplystreamclonebundle
977 apply a stream clone bundle file
976 apply a stream clone bundle file
978 debugbackupbundle
977 debugbackupbundle
979 lists the changesets available in backup bundles
978 lists the changesets available in backup bundles
980 debugbuilddag
979 debugbuilddag
981 builds a repo with a given DAG from scratch in the current
980 builds a repo with a given DAG from scratch in the current
982 empty repo
981 empty repo
983 debugbundle lists the contents of a bundle
982 debugbundle lists the contents of a bundle
984 debugcapabilities
983 debugcapabilities
985 lists the capabilities of a remote peer
984 lists the capabilities of a remote peer
986 debugchangedfiles
985 debugchangedfiles
987 list the stored files changes for a revision
986 list the stored files changes for a revision
988 debugcheckstate
987 debugcheckstate
989 validate the correctness of the current dirstate
988 validate the correctness of the current dirstate
990 debugcolor show available color, effects or style
989 debugcolor show available color, effects or style
991 debugcommands
990 debugcommands
992 list all available commands and options
991 list all available commands and options
993 debugcomplete
992 debugcomplete
994 returns the completion list associated with the given command
993 returns the completion list associated with the given command
995 debugcreatestreamclonebundle
994 debugcreatestreamclonebundle
996 create a stream clone bundle file
995 create a stream clone bundle file
997 debugdag format the changelog or an index DAG as a concise textual
996 debugdag format the changelog or an index DAG as a concise textual
998 description
997 description
999 debugdata dump the contents of a data file revision
998 debugdata dump the contents of a data file revision
1000 debugdate parse and display a date
999 debugdate parse and display a date
1001 debugdeltachain
1000 debugdeltachain
1002 dump information about delta chains in a revlog
1001 dump information about delta chains in a revlog
1003 debugdirstate
1002 debugdirstate
1004 show the contents of the current dirstate
1003 show the contents of the current dirstate
1005 debugdiscovery
1004 debugdiscovery
1006 runs the changeset discovery protocol in isolation
1005 runs the changeset discovery protocol in isolation
1007 debugdownload
1006 debugdownload
1008 download a resource using Mercurial logic and config
1007 download a resource using Mercurial logic and config
1009 debugextensions
1008 debugextensions
1010 show information about active extensions
1009 show information about active extensions
1011 debugfileset parse and apply a fileset specification
1010 debugfileset parse and apply a fileset specification
1012 debugformat display format information about the current repository
1011 debugformat display format information about the current repository
1013 debugfsinfo show information detected about current filesystem
1012 debugfsinfo show information detected about current filesystem
1014 debuggetbundle
1013 debuggetbundle
1015 retrieves a bundle from a repo
1014 retrieves a bundle from a repo
1016 debugignore display the combined ignore pattern and information about
1015 debugignore display the combined ignore pattern and information about
1017 ignored files
1016 ignored files
1018 debugindex dump index data for a storage primitive
1017 debugindex dump index data for a storage primitive
1019 debugindexdot
1018 debugindexdot
1020 dump an index DAG as a graphviz dot file
1019 dump an index DAG as a graphviz dot file
1021 debugindexstats
1020 debugindexstats
1022 show stats related to the changelog index
1021 show stats related to the changelog index
1023 debuginstall test Mercurial installation
1022 debuginstall test Mercurial installation
1024 debugknown test whether node ids are known to a repo
1023 debugknown test whether node ids are known to a repo
1025 debuglocks show or modify state of locks
1024 debuglocks show or modify state of locks
1026 debugmanifestfulltextcache
1025 debugmanifestfulltextcache
1027 show, clear or amend the contents of the manifest fulltext
1026 show, clear or amend the contents of the manifest fulltext
1028 cache
1027 cache
1029 debugmergestate
1028 debugmergestate
1030 print merge state
1029 print merge state
1031 debugnamecomplete
1030 debugnamecomplete
1032 complete "names" - tags, open branch names, bookmark names
1031 complete "names" - tags, open branch names, bookmark names
1033 debugnodemap write and inspect on disk nodemap
1032 debugnodemap write and inspect on disk nodemap
1034 debugobsolete
1033 debugobsolete
1035 create arbitrary obsolete marker
1034 create arbitrary obsolete marker
1036 debugoptADV (no help text available)
1035 debugoptADV (no help text available)
1037 debugoptDEP (no help text available)
1036 debugoptDEP (no help text available)
1038 debugoptEXP (no help text available)
1037 debugoptEXP (no help text available)
1039 debugp1copies
1038 debugp1copies
1040 dump copy information compared to p1
1039 dump copy information compared to p1
1041 debugp2copies
1040 debugp2copies
1042 dump copy information compared to p2
1041 dump copy information compared to p2
1043 debugpathcomplete
1042 debugpathcomplete
1044 complete part or all of a tracked path
1043 complete part or all of a tracked path
1045 debugpathcopies
1044 debugpathcopies
1046 show copies between two revisions
1045 show copies between two revisions
1047 debugpeer establish a connection to a peer repository
1046 debugpeer establish a connection to a peer repository
1048 debugpickmergetool
1047 debugpickmergetool
1049 examine which merge tool is chosen for specified file
1048 examine which merge tool is chosen for specified file
1050 debugpushkey access the pushkey key/value protocol
1049 debugpushkey access the pushkey key/value protocol
1051 debugpvec (no help text available)
1050 debugpvec (no help text available)
1052 debugrebuilddirstate
1051 debugrebuilddirstate
1053 rebuild the dirstate as it would look like for the given
1052 rebuild the dirstate as it would look like for the given
1054 revision
1053 revision
1055 debugrebuildfncache
1054 debugrebuildfncache
1056 rebuild the fncache file
1055 rebuild the fncache file
1057 debugrename dump rename information
1056 debugrename dump rename information
1058 debugrequires
1057 debugrequires
1059 print the current repo requirements
1058 print the current repo requirements
1060 debugrevlog show data and statistics about a revlog
1059 debugrevlog show data and statistics about a revlog
1061 debugrevlogindex
1060 debugrevlogindex
1062 dump the contents of a revlog index
1061 dump the contents of a revlog index
1063 debugrevspec parse and apply a revision specification
1062 debugrevspec parse and apply a revision specification
1064 debugserve run a server with advanced settings
1063 debugserve run a server with advanced settings
1065 debugsetparents
1064 debugsetparents
1066 manually set the parents of the current working directory
1065 manually set the parents of the current working directory
1067 debugsidedata
1066 debugsidedata
1068 dump the side data for a cl/manifest/file revision
1067 dump the side data for a cl/manifest/file revision
1069 debugssl test a secure connection to a server
1068 debugssl test a secure connection to a server
1069 debugstrip strip changesets and all their descendants from the repository
1070 debugsub (no help text available)
1070 debugsub (no help text available)
1071 debugsuccessorssets
1071 debugsuccessorssets
1072 show set of successors for revision
1072 show set of successors for revision
1073 debugtagscache
1073 debugtagscache
1074 display the contents of .hg/cache/hgtagsfnodes1
1074 display the contents of .hg/cache/hgtagsfnodes1
1075 debugtemplate
1075 debugtemplate
1076 parse and apply a template
1076 parse and apply a template
1077 debuguigetpass
1077 debuguigetpass
1078 show prompt to type password
1078 show prompt to type password
1079 debuguiprompt
1079 debuguiprompt
1080 show plain prompt
1080 show plain prompt
1081 debugupdatecaches
1081 debugupdatecaches
1082 warm all known caches in the repository
1082 warm all known caches in the repository
1083 debugupgraderepo
1083 debugupgraderepo
1084 upgrade a repository to use different features
1084 upgrade a repository to use different features
1085 debugwalk show how files match on given patterns
1085 debugwalk show how files match on given patterns
1086 debugwhyunstable
1086 debugwhyunstable
1087 explain instabilities of a changeset
1087 explain instabilities of a changeset
1088 debugwireargs
1088 debugwireargs
1089 (no help text available)
1089 (no help text available)
1090 debugwireproto
1090 debugwireproto
1091 send wire protocol commands to a server
1091 send wire protocol commands to a server
1092
1092
1093 (use 'hg help -v debug' to show built-in aliases and global options)
1093 (use 'hg help -v debug' to show built-in aliases and global options)
1094
1094
1095 internals topic renders index of available sub-topics
1095 internals topic renders index of available sub-topics
1096
1096
1097 $ hg help internals
1097 $ hg help internals
1098 Technical implementation topics
1098 Technical implementation topics
1099 """""""""""""""""""""""""""""""
1099 """""""""""""""""""""""""""""""
1100
1100
1101 To access a subtopic, use "hg help internals.{subtopic-name}"
1101 To access a subtopic, use "hg help internals.{subtopic-name}"
1102
1102
1103 bid-merge Bid Merge Algorithm
1103 bid-merge Bid Merge Algorithm
1104 bundle2 Bundle2
1104 bundle2 Bundle2
1105 bundles Bundles
1105 bundles Bundles
1106 cbor CBOR
1106 cbor CBOR
1107 censor Censor
1107 censor Censor
1108 changegroups Changegroups
1108 changegroups Changegroups
1109 config Config Registrar
1109 config Config Registrar
1110 extensions Extension API
1110 extensions Extension API
1111 mergestate Mergestate
1111 mergestate Mergestate
1112 requirements Repository Requirements
1112 requirements Repository Requirements
1113 revlogs Revision Logs
1113 revlogs Revision Logs
1114 wireprotocol Wire Protocol
1114 wireprotocol Wire Protocol
1115 wireprotocolrpc
1115 wireprotocolrpc
1116 Wire Protocol RPC
1116 Wire Protocol RPC
1117 wireprotocolv2
1117 wireprotocolv2
1118 Wire Protocol Version 2
1118 Wire Protocol Version 2
1119
1119
1120 sub-topics can be accessed
1120 sub-topics can be accessed
1121
1121
1122 $ hg help internals.changegroups
1122 $ hg help internals.changegroups
1123 Changegroups
1123 Changegroups
1124 """"""""""""
1124 """"""""""""
1125
1125
1126 Changegroups are representations of repository revlog data, specifically
1126 Changegroups are representations of repository revlog data, specifically
1127 the changelog data, root/flat manifest data, treemanifest data, and
1127 the changelog data, root/flat manifest data, treemanifest data, and
1128 filelogs.
1128 filelogs.
1129
1129
1130 There are 3 versions of changegroups: "1", "2", and "3". From a high-
1130 There are 3 versions of changegroups: "1", "2", and "3". From a high-
1131 level, versions "1" and "2" are almost exactly the same, with the only
1131 level, versions "1" and "2" are almost exactly the same, with the only
1132 difference being an additional item in the *delta header*. Version "3"
1132 difference being an additional item in the *delta header*. Version "3"
1133 adds support for storage flags in the *delta header* and optionally
1133 adds support for storage flags in the *delta header* and optionally
1134 exchanging treemanifests (enabled by setting an option on the
1134 exchanging treemanifests (enabled by setting an option on the
1135 "changegroup" part in the bundle2).
1135 "changegroup" part in the bundle2).
1136
1136
1137 Changegroups when not exchanging treemanifests consist of 3 logical
1137 Changegroups when not exchanging treemanifests consist of 3 logical
1138 segments:
1138 segments:
1139
1139
1140 +---------------------------------+
1140 +---------------------------------+
1141 | | | |
1141 | | | |
1142 | changeset | manifest | filelogs |
1142 | changeset | manifest | filelogs |
1143 | | | |
1143 | | | |
1144 | | | |
1144 | | | |
1145 +---------------------------------+
1145 +---------------------------------+
1146
1146
1147 When exchanging treemanifests, there are 4 logical segments:
1147 When exchanging treemanifests, there are 4 logical segments:
1148
1148
1149 +-------------------------------------------------+
1149 +-------------------------------------------------+
1150 | | | | |
1150 | | | | |
1151 | changeset | root | treemanifests | filelogs |
1151 | changeset | root | treemanifests | filelogs |
1152 | | manifest | | |
1152 | | manifest | | |
1153 | | | | |
1153 | | | | |
1154 +-------------------------------------------------+
1154 +-------------------------------------------------+
1155
1155
1156 The principle building block of each segment is a *chunk*. A *chunk* is a
1156 The principle building block of each segment is a *chunk*. A *chunk* is a
1157 framed piece of data:
1157 framed piece of data:
1158
1158
1159 +---------------------------------------+
1159 +---------------------------------------+
1160 | | |
1160 | | |
1161 | length | data |
1161 | length | data |
1162 | (4 bytes) | (<length - 4> bytes) |
1162 | (4 bytes) | (<length - 4> bytes) |
1163 | | |
1163 | | |
1164 +---------------------------------------+
1164 +---------------------------------------+
1165
1165
1166 All integers are big-endian signed integers. Each chunk starts with a
1166 All integers are big-endian signed integers. Each chunk starts with a
1167 32-bit integer indicating the length of the entire chunk (including the
1167 32-bit integer indicating the length of the entire chunk (including the
1168 length field itself).
1168 length field itself).
1169
1169
1170 There is a special case chunk that has a value of 0 for the length
1170 There is a special case chunk that has a value of 0 for the length
1171 ("0x00000000"). We call this an *empty chunk*.
1171 ("0x00000000"). We call this an *empty chunk*.
1172
1172
1173 Delta Groups
1173 Delta Groups
1174 ============
1174 ============
1175
1175
1176 A *delta group* expresses the content of a revlog as a series of deltas,
1176 A *delta group* expresses the content of a revlog as a series of deltas,
1177 or patches against previous revisions.
1177 or patches against previous revisions.
1178
1178
1179 Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
1179 Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
1180 to signal the end of the delta group:
1180 to signal the end of the delta group:
1181
1181
1182 +------------------------------------------------------------------------+
1182 +------------------------------------------------------------------------+
1183 | | | | | |
1183 | | | | | |
1184 | chunk0 length | chunk0 data | chunk1 length | chunk1 data | 0x0 |
1184 | chunk0 length | chunk0 data | chunk1 length | chunk1 data | 0x0 |
1185 | (4 bytes) | (various) | (4 bytes) | (various) | (4 bytes) |
1185 | (4 bytes) | (various) | (4 bytes) | (various) | (4 bytes) |
1186 | | | | | |
1186 | | | | | |
1187 +------------------------------------------------------------------------+
1187 +------------------------------------------------------------------------+
1188
1188
1189 Each *chunk*'s data consists of the following:
1189 Each *chunk*'s data consists of the following:
1190
1190
1191 +---------------------------------------+
1191 +---------------------------------------+
1192 | | |
1192 | | |
1193 | delta header | delta data |
1193 | delta header | delta data |
1194 | (various by version) | (various) |
1194 | (various by version) | (various) |
1195 | | |
1195 | | |
1196 +---------------------------------------+
1196 +---------------------------------------+
1197
1197
1198 The *delta data* is a series of *delta*s that describe a diff from an
1198 The *delta data* is a series of *delta*s that describe a diff from an
1199 existing entry (either that the recipient already has, or previously
1199 existing entry (either that the recipient already has, or previously
1200 specified in the bundle/changegroup).
1200 specified in the bundle/changegroup).
1201
1201
1202 The *delta header* is different between versions "1", "2", and "3" of the
1202 The *delta header* is different between versions "1", "2", and "3" of the
1203 changegroup format.
1203 changegroup format.
1204
1204
1205 Version 1 (headerlen=80):
1205 Version 1 (headerlen=80):
1206
1206
1207 +------------------------------------------------------+
1207 +------------------------------------------------------+
1208 | | | | |
1208 | | | | |
1209 | node | p1 node | p2 node | link node |
1209 | node | p1 node | p2 node | link node |
1210 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
1210 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
1211 | | | | |
1211 | | | | |
1212 +------------------------------------------------------+
1212 +------------------------------------------------------+
1213
1213
1214 Version 2 (headerlen=100):
1214 Version 2 (headerlen=100):
1215
1215
1216 +------------------------------------------------------------------+
1216 +------------------------------------------------------------------+
1217 | | | | | |
1217 | | | | | |
1218 | node | p1 node | p2 node | base node | link node |
1218 | node | p1 node | p2 node | base node | link node |
1219 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
1219 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
1220 | | | | | |
1220 | | | | | |
1221 +------------------------------------------------------------------+
1221 +------------------------------------------------------------------+
1222
1222
1223 Version 3 (headerlen=102):
1223 Version 3 (headerlen=102):
1224
1224
1225 +------------------------------------------------------------------------------+
1225 +------------------------------------------------------------------------------+
1226 | | | | | | |
1226 | | | | | | |
1227 | node | p1 node | p2 node | base node | link node | flags |
1227 | node | p1 node | p2 node | base node | link node | flags |
1228 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (2 bytes) |
1228 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (2 bytes) |
1229 | | | | | | |
1229 | | | | | | |
1230 +------------------------------------------------------------------------------+
1230 +------------------------------------------------------------------------------+
1231
1231
1232 The *delta data* consists of "chunklen - 4 - headerlen" bytes, which
1232 The *delta data* consists of "chunklen - 4 - headerlen" bytes, which
1233 contain a series of *delta*s, densely packed (no separators). These deltas
1233 contain a series of *delta*s, densely packed (no separators). These deltas
1234 describe a diff from an existing entry (either that the recipient already
1234 describe a diff from an existing entry (either that the recipient already
1235 has, or previously specified in the bundle/changegroup). The format is
1235 has, or previously specified in the bundle/changegroup). The format is
1236 described more fully in "hg help internals.bdiff", but briefly:
1236 described more fully in "hg help internals.bdiff", but briefly:
1237
1237
1238 +---------------------------------------------------------------+
1238 +---------------------------------------------------------------+
1239 | | | | |
1239 | | | | |
1240 | start offset | end offset | new length | content |
1240 | start offset | end offset | new length | content |
1241 | (4 bytes) | (4 bytes) | (4 bytes) | (<new length> bytes) |
1241 | (4 bytes) | (4 bytes) | (4 bytes) | (<new length> bytes) |
1242 | | | | |
1242 | | | | |
1243 +---------------------------------------------------------------+
1243 +---------------------------------------------------------------+
1244
1244
1245 Please note that the length field in the delta data does *not* include
1245 Please note that the length field in the delta data does *not* include
1246 itself.
1246 itself.
1247
1247
1248 In version 1, the delta is always applied against the previous node from
1248 In version 1, the delta is always applied against the previous node from
1249 the changegroup or the first parent if this is the first entry in the
1249 the changegroup or the first parent if this is the first entry in the
1250 changegroup.
1250 changegroup.
1251
1251
1252 In version 2 and up, the delta base node is encoded in the entry in the
1252 In version 2 and up, the delta base node is encoded in the entry in the
1253 changegroup. This allows the delta to be expressed against any parent,
1253 changegroup. This allows the delta to be expressed against any parent,
1254 which can result in smaller deltas and more efficient encoding of data.
1254 which can result in smaller deltas and more efficient encoding of data.
1255
1255
1256 The *flags* field holds bitwise flags affecting the processing of revision
1256 The *flags* field holds bitwise flags affecting the processing of revision
1257 data. The following flags are defined:
1257 data. The following flags are defined:
1258
1258
1259 32768
1259 32768
1260 Censored revision. The revision's fulltext has been replaced by censor
1260 Censored revision. The revision's fulltext has been replaced by censor
1261 metadata. May only occur on file revisions.
1261 metadata. May only occur on file revisions.
1262
1262
1263 16384
1263 16384
1264 Ellipsis revision. Revision hash does not match data (likely due to
1264 Ellipsis revision. Revision hash does not match data (likely due to
1265 rewritten parents).
1265 rewritten parents).
1266
1266
1267 8192
1267 8192
1268 Externally stored. The revision fulltext contains "key:value" "\n"
1268 Externally stored. The revision fulltext contains "key:value" "\n"
1269 delimited metadata defining an object stored elsewhere. Used by the LFS
1269 delimited metadata defining an object stored elsewhere. Used by the LFS
1270 extension.
1270 extension.
1271
1271
1272 For historical reasons, the integer values are identical to revlog version
1272 For historical reasons, the integer values are identical to revlog version
1273 1 per-revision storage flags and correspond to bits being set in this
1273 1 per-revision storage flags and correspond to bits being set in this
1274 2-byte field. Bits were allocated starting from the most-significant bit,
1274 2-byte field. Bits were allocated starting from the most-significant bit,
1275 hence the reverse ordering and allocation of these flags.
1275 hence the reverse ordering and allocation of these flags.
1276
1276
1277 Changeset Segment
1277 Changeset Segment
1278 =================
1278 =================
1279
1279
1280 The *changeset segment* consists of a single *delta group* holding
1280 The *changeset segment* consists of a single *delta group* holding
1281 changelog data. The *empty chunk* at the end of the *delta group* denotes
1281 changelog data. The *empty chunk* at the end of the *delta group* denotes
1282 the boundary to the *manifest segment*.
1282 the boundary to the *manifest segment*.
1283
1283
1284 Manifest Segment
1284 Manifest Segment
1285 ================
1285 ================
1286
1286
1287 The *manifest segment* consists of a single *delta group* holding manifest
1287 The *manifest segment* consists of a single *delta group* holding manifest
1288 data. If treemanifests are in use, it contains only the manifest for the
1288 data. If treemanifests are in use, it contains only the manifest for the
1289 root directory of the repository. Otherwise, it contains the entire
1289 root directory of the repository. Otherwise, it contains the entire
1290 manifest data. The *empty chunk* at the end of the *delta group* denotes
1290 manifest data. The *empty chunk* at the end of the *delta group* denotes
1291 the boundary to the next segment (either the *treemanifests segment* or
1291 the boundary to the next segment (either the *treemanifests segment* or
1292 the *filelogs segment*, depending on version and the request options).
1292 the *filelogs segment*, depending on version and the request options).
1293
1293
1294 Treemanifests Segment
1294 Treemanifests Segment
1295 ---------------------
1295 ---------------------
1296
1296
1297 The *treemanifests segment* only exists in changegroup version "3", and
1297 The *treemanifests segment* only exists in changegroup version "3", and
1298 only if the 'treemanifest' param is part of the bundle2 changegroup part
1298 only if the 'treemanifest' param is part of the bundle2 changegroup part
1299 (it is not possible to use changegroup version 3 outside of bundle2).
1299 (it is not possible to use changegroup version 3 outside of bundle2).
1300 Aside from the filenames in the *treemanifests segment* containing a
1300 Aside from the filenames in the *treemanifests segment* containing a
1301 trailing "/" character, it behaves identically to the *filelogs segment*
1301 trailing "/" character, it behaves identically to the *filelogs segment*
1302 (see below). The final sub-segment is followed by an *empty chunk*
1302 (see below). The final sub-segment is followed by an *empty chunk*
1303 (logically, a sub-segment with filename size 0). This denotes the boundary
1303 (logically, a sub-segment with filename size 0). This denotes the boundary
1304 to the *filelogs segment*.
1304 to the *filelogs segment*.
1305
1305
1306 Filelogs Segment
1306 Filelogs Segment
1307 ================
1307 ================
1308
1308
1309 The *filelogs segment* consists of multiple sub-segments, each
1309 The *filelogs segment* consists of multiple sub-segments, each
1310 corresponding to an individual file whose data is being described:
1310 corresponding to an individual file whose data is being described:
1311
1311
1312 +--------------------------------------------------+
1312 +--------------------------------------------------+
1313 | | | | | |
1313 | | | | | |
1314 | filelog0 | filelog1 | filelog2 | ... | 0x0 |
1314 | filelog0 | filelog1 | filelog2 | ... | 0x0 |
1315 | | | | | (4 bytes) |
1315 | | | | | (4 bytes) |
1316 | | | | | |
1316 | | | | | |
1317 +--------------------------------------------------+
1317 +--------------------------------------------------+
1318
1318
1319 The final filelog sub-segment is followed by an *empty chunk* (logically,
1319 The final filelog sub-segment is followed by an *empty chunk* (logically,
1320 a sub-segment with filename size 0). This denotes the end of the segment
1320 a sub-segment with filename size 0). This denotes the end of the segment
1321 and of the overall changegroup.
1321 and of the overall changegroup.
1322
1322
1323 Each filelog sub-segment consists of the following:
1323 Each filelog sub-segment consists of the following:
1324
1324
1325 +------------------------------------------------------+
1325 +------------------------------------------------------+
1326 | | | |
1326 | | | |
1327 | filename length | filename | delta group |
1327 | filename length | filename | delta group |
1328 | (4 bytes) | (<length - 4> bytes) | (various) |
1328 | (4 bytes) | (<length - 4> bytes) | (various) |
1329 | | | |
1329 | | | |
1330 +------------------------------------------------------+
1330 +------------------------------------------------------+
1331
1331
1332 That is, a *chunk* consisting of the filename (not terminated or padded)
1332 That is, a *chunk* consisting of the filename (not terminated or padded)
1333 followed by N chunks constituting the *delta group* for this file. The
1333 followed by N chunks constituting the *delta group* for this file. The
1334 *empty chunk* at the end of each *delta group* denotes the boundary to the
1334 *empty chunk* at the end of each *delta group* denotes the boundary to the
1335 next filelog sub-segment.
1335 next filelog sub-segment.
1336
1336
1337 non-existent subtopics print an error
1337 non-existent subtopics print an error
1338
1338
1339 $ hg help internals.foo
1339 $ hg help internals.foo
1340 abort: no such help topic: internals.foo
1340 abort: no such help topic: internals.foo
1341 (try 'hg help --keyword foo')
1341 (try 'hg help --keyword foo')
1342 [255]
1342 [255]
1343
1343
1344 test advanced, deprecated and experimental options are hidden in command help
1344 test advanced, deprecated and experimental options are hidden in command help
1345 $ hg help debugoptADV
1345 $ hg help debugoptADV
1346 hg debugoptADV
1346 hg debugoptADV
1347
1347
1348 (no help text available)
1348 (no help text available)
1349
1349
1350 options:
1350 options:
1351
1351
1352 (some details hidden, use --verbose to show complete help)
1352 (some details hidden, use --verbose to show complete help)
1353 $ hg help debugoptDEP
1353 $ hg help debugoptDEP
1354 hg debugoptDEP
1354 hg debugoptDEP
1355
1355
1356 (no help text available)
1356 (no help text available)
1357
1357
1358 options:
1358 options:
1359
1359
1360 (some details hidden, use --verbose to show complete help)
1360 (some details hidden, use --verbose to show complete help)
1361
1361
1362 $ hg help debugoptEXP
1362 $ hg help debugoptEXP
1363 hg debugoptEXP
1363 hg debugoptEXP
1364
1364
1365 (no help text available)
1365 (no help text available)
1366
1366
1367 options:
1367 options:
1368
1368
1369 (some details hidden, use --verbose to show complete help)
1369 (some details hidden, use --verbose to show complete help)
1370
1370
1371 test advanced, deprecated and experimental options are shown with -v
1371 test advanced, deprecated and experimental options are shown with -v
1372 $ hg help -v debugoptADV | grep aopt
1372 $ hg help -v debugoptADV | grep aopt
1373 --aopt option is (ADVANCED)
1373 --aopt option is (ADVANCED)
1374 $ hg help -v debugoptDEP | grep dopt
1374 $ hg help -v debugoptDEP | grep dopt
1375 --dopt option is (DEPRECATED)
1375 --dopt option is (DEPRECATED)
1376 $ hg help -v debugoptEXP | grep eopt
1376 $ hg help -v debugoptEXP | grep eopt
1377 --eopt option is (EXPERIMENTAL)
1377 --eopt option is (EXPERIMENTAL)
1378
1378
1379 #if gettext
1379 #if gettext
1380 test deprecated option is hidden with translation with untranslated description
1380 test deprecated option is hidden with translation with untranslated description
1381 (use many globy for not failing on changed transaction)
1381 (use many globy for not failing on changed transaction)
1382 $ LANGUAGE=sv hg help debugoptDEP
1382 $ LANGUAGE=sv hg help debugoptDEP
1383 hg debugoptDEP
1383 hg debugoptDEP
1384
1384
1385 (*) (glob)
1385 (*) (glob)
1386
1386
1387 options:
1387 options:
1388
1388
1389 (some details hidden, use --verbose to show complete help)
1389 (some details hidden, use --verbose to show complete help)
1390 #endif
1390 #endif
1391
1391
1392 Test commands that collide with topics (issue4240)
1392 Test commands that collide with topics (issue4240)
1393
1393
1394 $ hg config -hq
1394 $ hg config -hq
1395 hg config [-u] [NAME]...
1395 hg config [-u] [NAME]...
1396
1396
1397 show combined config settings from all hgrc files
1397 show combined config settings from all hgrc files
1398 $ hg showconfig -hq
1398 $ hg showconfig -hq
1399 hg config [-u] [NAME]...
1399 hg config [-u] [NAME]...
1400
1400
1401 show combined config settings from all hgrc files
1401 show combined config settings from all hgrc files
1402
1402
1403 Test a help topic
1403 Test a help topic
1404
1404
1405 $ hg help dates
1405 $ hg help dates
1406 Date Formats
1406 Date Formats
1407 """"""""""""
1407 """"""""""""
1408
1408
1409 Some commands allow the user to specify a date, e.g.:
1409 Some commands allow the user to specify a date, e.g.:
1410
1410
1411 - backout, commit, import, tag: Specify the commit date.
1411 - backout, commit, import, tag: Specify the commit date.
1412 - log, revert, update: Select revision(s) by date.
1412 - log, revert, update: Select revision(s) by date.
1413
1413
1414 Many date formats are valid. Here are some examples:
1414 Many date formats are valid. Here are some examples:
1415
1415
1416 - "Wed Dec 6 13:18:29 2006" (local timezone assumed)
1416 - "Wed Dec 6 13:18:29 2006" (local timezone assumed)
1417 - "Dec 6 13:18 -0600" (year assumed, time offset provided)
1417 - "Dec 6 13:18 -0600" (year assumed, time offset provided)
1418 - "Dec 6 13:18 UTC" (UTC and GMT are aliases for +0000)
1418 - "Dec 6 13:18 UTC" (UTC and GMT are aliases for +0000)
1419 - "Dec 6" (midnight)
1419 - "Dec 6" (midnight)
1420 - "13:18" (today assumed)
1420 - "13:18" (today assumed)
1421 - "3:39" (3:39AM assumed)
1421 - "3:39" (3:39AM assumed)
1422 - "3:39pm" (15:39)
1422 - "3:39pm" (15:39)
1423 - "2006-12-06 13:18:29" (ISO 8601 format)
1423 - "2006-12-06 13:18:29" (ISO 8601 format)
1424 - "2006-12-6 13:18"
1424 - "2006-12-6 13:18"
1425 - "2006-12-6"
1425 - "2006-12-6"
1426 - "12-6"
1426 - "12-6"
1427 - "12/6"
1427 - "12/6"
1428 - "12/6/6" (Dec 6 2006)
1428 - "12/6/6" (Dec 6 2006)
1429 - "today" (midnight)
1429 - "today" (midnight)
1430 - "yesterday" (midnight)
1430 - "yesterday" (midnight)
1431 - "now" - right now
1431 - "now" - right now
1432
1432
1433 Lastly, there is Mercurial's internal format:
1433 Lastly, there is Mercurial's internal format:
1434
1434
1435 - "1165411109 0" (Wed Dec 6 13:18:29 2006 UTC)
1435 - "1165411109 0" (Wed Dec 6 13:18:29 2006 UTC)
1436
1436
1437 This is the internal representation format for dates. The first number is
1437 This is the internal representation format for dates. The first number is
1438 the number of seconds since the epoch (1970-01-01 00:00 UTC). The second
1438 the number of seconds since the epoch (1970-01-01 00:00 UTC). The second
1439 is the offset of the local timezone, in seconds west of UTC (negative if
1439 is the offset of the local timezone, in seconds west of UTC (negative if
1440 the timezone is east of UTC).
1440 the timezone is east of UTC).
1441
1441
1442 The log command also accepts date ranges:
1442 The log command also accepts date ranges:
1443
1443
1444 - "<DATE" - at or before a given date/time
1444 - "<DATE" - at or before a given date/time
1445 - ">DATE" - on or after a given date/time
1445 - ">DATE" - on or after a given date/time
1446 - "DATE to DATE" - a date range, inclusive
1446 - "DATE to DATE" - a date range, inclusive
1447 - "-DAYS" - within a given number of days of today
1447 - "-DAYS" - within a given number of days of today
1448
1448
1449 Test repeated config section name
1449 Test repeated config section name
1450
1450
1451 $ hg help config.host
1451 $ hg help config.host
1452 "http_proxy.host"
1452 "http_proxy.host"
1453 Host name and (optional) port of the proxy server, for example
1453 Host name and (optional) port of the proxy server, for example
1454 "myproxy:8000".
1454 "myproxy:8000".
1455
1455
1456 "smtp.host"
1456 "smtp.host"
1457 Host name of mail server, e.g. "mail.example.com".
1457 Host name of mail server, e.g. "mail.example.com".
1458
1458
1459
1459
1460 Test section name with dot
1460 Test section name with dot
1461
1461
1462 $ hg help config.ui.username
1462 $ hg help config.ui.username
1463 "ui.username"
1463 "ui.username"
1464 The committer of a changeset created when running "commit". Typically
1464 The committer of a changeset created when running "commit". Typically
1465 a person's name and email address, e.g. "Fred Widget
1465 a person's name and email address, e.g. "Fred Widget
1466 <fred@example.com>". Environment variables in the username are
1466 <fred@example.com>". Environment variables in the username are
1467 expanded.
1467 expanded.
1468
1468
1469 (default: "$EMAIL" or "username@hostname". If the username in hgrc is
1469 (default: "$EMAIL" or "username@hostname". If the username in hgrc is
1470 empty, e.g. if the system admin set "username =" in the system hgrc,
1470 empty, e.g. if the system admin set "username =" in the system hgrc,
1471 it has to be specified manually or in a different hgrc file)
1471 it has to be specified manually or in a different hgrc file)
1472
1472
1473
1473
1474 $ hg help config.annotate.git
1474 $ hg help config.annotate.git
1475 abort: help section not found: config.annotate.git
1475 abort: help section not found: config.annotate.git
1476 [255]
1476 [255]
1477
1477
1478 $ hg help config.update.check
1478 $ hg help config.update.check
1479 "commands.update.check"
1479 "commands.update.check"
1480 Determines what level of checking 'hg update' will perform before
1480 Determines what level of checking 'hg update' will perform before
1481 moving to a destination revision. Valid values are "abort", "none",
1481 moving to a destination revision. Valid values are "abort", "none",
1482 "linear", and "noconflict". "abort" always fails if the working
1482 "linear", and "noconflict". "abort" always fails if the working
1483 directory has uncommitted changes. "none" performs no checking, and
1483 directory has uncommitted changes. "none" performs no checking, and
1484 may result in a merge with uncommitted changes. "linear" allows any
1484 may result in a merge with uncommitted changes. "linear" allows any
1485 update as long as it follows a straight line in the revision history,
1485 update as long as it follows a straight line in the revision history,
1486 and may trigger a merge with uncommitted changes. "noconflict" will
1486 and may trigger a merge with uncommitted changes. "noconflict" will
1487 allow any update which would not trigger a merge with uncommitted
1487 allow any update which would not trigger a merge with uncommitted
1488 changes, if any are present. (default: "linear")
1488 changes, if any are present. (default: "linear")
1489
1489
1490
1490
1491 $ hg help config.commands.update.check
1491 $ hg help config.commands.update.check
1492 "commands.update.check"
1492 "commands.update.check"
1493 Determines what level of checking 'hg update' will perform before
1493 Determines what level of checking 'hg update' will perform before
1494 moving to a destination revision. Valid values are "abort", "none",
1494 moving to a destination revision. Valid values are "abort", "none",
1495 "linear", and "noconflict". "abort" always fails if the working
1495 "linear", and "noconflict". "abort" always fails if the working
1496 directory has uncommitted changes. "none" performs no checking, and
1496 directory has uncommitted changes. "none" performs no checking, and
1497 may result in a merge with uncommitted changes. "linear" allows any
1497 may result in a merge with uncommitted changes. "linear" allows any
1498 update as long as it follows a straight line in the revision history,
1498 update as long as it follows a straight line in the revision history,
1499 and may trigger a merge with uncommitted changes. "noconflict" will
1499 and may trigger a merge with uncommitted changes. "noconflict" will
1500 allow any update which would not trigger a merge with uncommitted
1500 allow any update which would not trigger a merge with uncommitted
1501 changes, if any are present. (default: "linear")
1501 changes, if any are present. (default: "linear")
1502
1502
1503
1503
1504 $ hg help config.ommands.update.check
1504 $ hg help config.ommands.update.check
1505 abort: help section not found: config.ommands.update.check
1505 abort: help section not found: config.ommands.update.check
1506 [255]
1506 [255]
1507
1507
1508 Unrelated trailing paragraphs shouldn't be included
1508 Unrelated trailing paragraphs shouldn't be included
1509
1509
1510 $ hg help config.extramsg | grep '^$'
1510 $ hg help config.extramsg | grep '^$'
1511
1511
1512
1512
1513 Test capitalized section name
1513 Test capitalized section name
1514
1514
1515 $ hg help scripting.HGPLAIN > /dev/null
1515 $ hg help scripting.HGPLAIN > /dev/null
1516
1516
1517 Help subsection:
1517 Help subsection:
1518
1518
1519 $ hg help config.charsets |grep "Email example:" > /dev/null
1519 $ hg help config.charsets |grep "Email example:" > /dev/null
1520 [1]
1520 [1]
1521
1521
1522 Show nested definitions
1522 Show nested definitions
1523 ("profiling.type"[break]"ls"[break]"stat"[break])
1523 ("profiling.type"[break]"ls"[break]"stat"[break])
1524
1524
1525 $ hg help config.type | egrep '^$'|wc -l
1525 $ hg help config.type | egrep '^$'|wc -l
1526 \s*3 (re)
1526 \s*3 (re)
1527
1527
1528 $ hg help config.profiling.type.ls
1528 $ hg help config.profiling.type.ls
1529 "profiling.type.ls"
1529 "profiling.type.ls"
1530 Use Python's built-in instrumenting profiler. This profiler works on
1530 Use Python's built-in instrumenting profiler. This profiler works on
1531 all platforms, but each line number it reports is the first line of
1531 all platforms, but each line number it reports is the first line of
1532 a function. This restriction makes it difficult to identify the
1532 a function. This restriction makes it difficult to identify the
1533 expensive parts of a non-trivial function.
1533 expensive parts of a non-trivial function.
1534
1534
1535
1535
1536 Separate sections from subsections
1536 Separate sections from subsections
1537
1537
1538 $ hg help config.format | egrep '^ ("|-)|^\s*$' | uniq
1538 $ hg help config.format | egrep '^ ("|-)|^\s*$' | uniq
1539 "format"
1539 "format"
1540 --------
1540 --------
1541
1541
1542 "usegeneraldelta"
1542 "usegeneraldelta"
1543
1543
1544 "dotencode"
1544 "dotencode"
1545
1545
1546 "usefncache"
1546 "usefncache"
1547
1547
1548 "usestore"
1548 "usestore"
1549
1549
1550 "sparse-revlog"
1550 "sparse-revlog"
1551
1551
1552 "revlog-compression"
1552 "revlog-compression"
1553
1553
1554 "bookmarks-in-store"
1554 "bookmarks-in-store"
1555
1555
1556 "profiling"
1556 "profiling"
1557 -----------
1557 -----------
1558
1558
1559 "format"
1559 "format"
1560
1560
1561 "progress"
1561 "progress"
1562 ----------
1562 ----------
1563
1563
1564 "format"
1564 "format"
1565
1565
1566
1566
1567 Last item in help config.*:
1567 Last item in help config.*:
1568
1568
1569 $ hg help config.`hg help config|grep '^ "'| \
1569 $ hg help config.`hg help config|grep '^ "'| \
1570 > tail -1|sed 's![ "]*!!g'`| \
1570 > tail -1|sed 's![ "]*!!g'`| \
1571 > grep 'hg help -c config' > /dev/null
1571 > grep 'hg help -c config' > /dev/null
1572 [1]
1572 [1]
1573
1573
1574 note to use help -c for general hg help config:
1574 note to use help -c for general hg help config:
1575
1575
1576 $ hg help config |grep 'hg help -c config' > /dev/null
1576 $ hg help config |grep 'hg help -c config' > /dev/null
1577
1577
1578 Test templating help
1578 Test templating help
1579
1579
1580 $ hg help templating | egrep '(desc|diffstat|firstline|nonempty) '
1580 $ hg help templating | egrep '(desc|diffstat|firstline|nonempty) '
1581 desc String. The text of the changeset description.
1581 desc String. The text of the changeset description.
1582 diffstat String. Statistics of changes with the following format:
1582 diffstat String. Statistics of changes with the following format:
1583 firstline Any text. Returns the first line of text.
1583 firstline Any text. Returns the first line of text.
1584 nonempty Any text. Returns '(none)' if the string is empty.
1584 nonempty Any text. Returns '(none)' if the string is empty.
1585
1585
1586 Test deprecated items
1586 Test deprecated items
1587
1587
1588 $ hg help -v templating | grep currentbookmark
1588 $ hg help -v templating | grep currentbookmark
1589 currentbookmark
1589 currentbookmark
1590 $ hg help templating | (grep currentbookmark || true)
1590 $ hg help templating | (grep currentbookmark || true)
1591
1591
1592 Test help hooks
1592 Test help hooks
1593
1593
1594 $ cat > helphook1.py <<EOF
1594 $ cat > helphook1.py <<EOF
1595 > from mercurial import help
1595 > from mercurial import help
1596 >
1596 >
1597 > def rewrite(ui, topic, doc):
1597 > def rewrite(ui, topic, doc):
1598 > return doc + b'\nhelphook1\n'
1598 > return doc + b'\nhelphook1\n'
1599 >
1599 >
1600 > def extsetup(ui):
1600 > def extsetup(ui):
1601 > help.addtopichook(b'revisions', rewrite)
1601 > help.addtopichook(b'revisions', rewrite)
1602 > EOF
1602 > EOF
1603 $ cat > helphook2.py <<EOF
1603 $ cat > helphook2.py <<EOF
1604 > from mercurial import help
1604 > from mercurial import help
1605 >
1605 >
1606 > def rewrite(ui, topic, doc):
1606 > def rewrite(ui, topic, doc):
1607 > return doc + b'\nhelphook2\n'
1607 > return doc + b'\nhelphook2\n'
1608 >
1608 >
1609 > def extsetup(ui):
1609 > def extsetup(ui):
1610 > help.addtopichook(b'revisions', rewrite)
1610 > help.addtopichook(b'revisions', rewrite)
1611 > EOF
1611 > EOF
1612 $ echo '[extensions]' >> $HGRCPATH
1612 $ echo '[extensions]' >> $HGRCPATH
1613 $ echo "helphook1 = `pwd`/helphook1.py" >> $HGRCPATH
1613 $ echo "helphook1 = `pwd`/helphook1.py" >> $HGRCPATH
1614 $ echo "helphook2 = `pwd`/helphook2.py" >> $HGRCPATH
1614 $ echo "helphook2 = `pwd`/helphook2.py" >> $HGRCPATH
1615 $ hg help revsets | grep helphook
1615 $ hg help revsets | grep helphook
1616 helphook1
1616 helphook1
1617 helphook2
1617 helphook2
1618
1618
1619 help -c should only show debug --debug
1619 help -c should only show debug --debug
1620
1620
1621 $ hg help -c --debug|egrep debug|wc -l|egrep '^\s*0\s*$'
1621 $ hg help -c --debug|egrep debug|wc -l|egrep '^\s*0\s*$'
1622 [1]
1622 [1]
1623
1623
1624 help -c should only show deprecated for -v
1624 help -c should only show deprecated for -v
1625
1625
1626 $ hg help -c -v|egrep DEPRECATED|wc -l|egrep '^\s*0\s*$'
1626 $ hg help -c -v|egrep DEPRECATED|wc -l|egrep '^\s*0\s*$'
1627 [1]
1627 [1]
1628
1628
1629 Test -s / --system
1629 Test -s / --system
1630
1630
1631 $ hg help config.files -s windows |grep 'etc/mercurial' | \
1631 $ hg help config.files -s windows |grep 'etc/mercurial' | \
1632 > wc -l | sed -e 's/ //g'
1632 > wc -l | sed -e 's/ //g'
1633 0
1633 0
1634 $ hg help config.files --system unix | grep 'USER' | \
1634 $ hg help config.files --system unix | grep 'USER' | \
1635 > wc -l | sed -e 's/ //g'
1635 > wc -l | sed -e 's/ //g'
1636 0
1636 0
1637
1637
1638 Test -e / -c / -k combinations
1638 Test -e / -c / -k combinations
1639
1639
1640 $ hg help -c|egrep '^[A-Z].*:|^ debug'
1640 $ hg help -c|egrep '^[A-Z].*:|^ debug'
1641 Commands:
1641 Commands:
1642 $ hg help -e|egrep '^[A-Z].*:|^ debug'
1642 $ hg help -e|egrep '^[A-Z].*:|^ debug'
1643 Extensions:
1643 Extensions:
1644 $ hg help -k|egrep '^[A-Z].*:|^ debug'
1644 $ hg help -k|egrep '^[A-Z].*:|^ debug'
1645 Topics:
1645 Topics:
1646 Commands:
1646 Commands:
1647 Extensions:
1647 Extensions:
1648 Extension Commands:
1648 Extension Commands:
1649 $ hg help -c schemes
1649 $ hg help -c schemes
1650 abort: no such help topic: schemes
1650 abort: no such help topic: schemes
1651 (try 'hg help --keyword schemes')
1651 (try 'hg help --keyword schemes')
1652 [255]
1652 [255]
1653 $ hg help -e schemes |head -1
1653 $ hg help -e schemes |head -1
1654 schemes extension - extend schemes with shortcuts to repository swarms
1654 schemes extension - extend schemes with shortcuts to repository swarms
1655 $ hg help -c -k dates |egrep '^(Topics|Extensions|Commands):'
1655 $ hg help -c -k dates |egrep '^(Topics|Extensions|Commands):'
1656 Commands:
1656 Commands:
1657 $ hg help -e -k a |egrep '^(Topics|Extensions|Commands):'
1657 $ hg help -e -k a |egrep '^(Topics|Extensions|Commands):'
1658 Extensions:
1658 Extensions:
1659 $ hg help -e -c -k date |egrep '^(Topics|Extensions|Commands):'
1659 $ hg help -e -c -k date |egrep '^(Topics|Extensions|Commands):'
1660 Extensions:
1660 Extensions:
1661 Commands:
1661 Commands:
1662 $ hg help -c commit > /dev/null
1662 $ hg help -c commit > /dev/null
1663 $ hg help -e -c commit > /dev/null
1663 $ hg help -e -c commit > /dev/null
1664 $ hg help -e commit
1664 $ hg help -e commit
1665 abort: no such help topic: commit
1665 abort: no such help topic: commit
1666 (try 'hg help --keyword commit')
1666 (try 'hg help --keyword commit')
1667 [255]
1667 [255]
1668
1668
1669 Test keyword search help
1669 Test keyword search help
1670
1670
1671 $ cat > prefixedname.py <<EOF
1671 $ cat > prefixedname.py <<EOF
1672 > '''matched against word "clone"
1672 > '''matched against word "clone"
1673 > '''
1673 > '''
1674 > EOF
1674 > EOF
1675 $ echo '[extensions]' >> $HGRCPATH
1675 $ echo '[extensions]' >> $HGRCPATH
1676 $ echo "dot.dot.prefixedname = `pwd`/prefixedname.py" >> $HGRCPATH
1676 $ echo "dot.dot.prefixedname = `pwd`/prefixedname.py" >> $HGRCPATH
1677 $ hg help -k clone
1677 $ hg help -k clone
1678 Topics:
1678 Topics:
1679
1679
1680 config Configuration Files
1680 config Configuration Files
1681 extensions Using Additional Features
1681 extensions Using Additional Features
1682 glossary Glossary
1682 glossary Glossary
1683 phases Working with Phases
1683 phases Working with Phases
1684 subrepos Subrepositories
1684 subrepos Subrepositories
1685 urls URL Paths
1685 urls URL Paths
1686
1686
1687 Commands:
1687 Commands:
1688
1688
1689 bookmarks create a new bookmark or list existing bookmarks
1689 bookmarks create a new bookmark or list existing bookmarks
1690 clone make a copy of an existing repository
1690 clone make a copy of an existing repository
1691 paths show aliases for remote repositories
1691 paths show aliases for remote repositories
1692 pull pull changes from the specified source
1692 pull pull changes from the specified source
1693 update update working directory (or switch revisions)
1693 update update working directory (or switch revisions)
1694
1694
1695 Extensions:
1695 Extensions:
1696
1696
1697 clonebundles advertise pre-generated bundles to seed clones
1697 clonebundles advertise pre-generated bundles to seed clones
1698 narrow create clones which fetch history data for subset of files
1698 narrow create clones which fetch history data for subset of files
1699 (EXPERIMENTAL)
1699 (EXPERIMENTAL)
1700 prefixedname matched against word "clone"
1700 prefixedname matched against word "clone"
1701 relink recreates hardlinks between repository clones
1701 relink recreates hardlinks between repository clones
1702
1702
1703 Extension Commands:
1703 Extension Commands:
1704
1704
1705 qclone clone main and patch repository at same time
1705 qclone clone main and patch repository at same time
1706
1706
1707 Test unfound topic
1707 Test unfound topic
1708
1708
1709 $ hg help nonexistingtopicthatwillneverexisteverever
1709 $ hg help nonexistingtopicthatwillneverexisteverever
1710 abort: no such help topic: nonexistingtopicthatwillneverexisteverever
1710 abort: no such help topic: nonexistingtopicthatwillneverexisteverever
1711 (try 'hg help --keyword nonexistingtopicthatwillneverexisteverever')
1711 (try 'hg help --keyword nonexistingtopicthatwillneverexisteverever')
1712 [255]
1712 [255]
1713
1713
1714 Test unfound keyword
1714 Test unfound keyword
1715
1715
1716 $ hg help --keyword nonexistingwordthatwillneverexisteverever
1716 $ hg help --keyword nonexistingwordthatwillneverexisteverever
1717 abort: no matches
1717 abort: no matches
1718 (try 'hg help' for a list of topics)
1718 (try 'hg help' for a list of topics)
1719 [255]
1719 [255]
1720
1720
1721 Test omit indicating for help
1721 Test omit indicating for help
1722
1722
1723 $ cat > addverboseitems.py <<EOF
1723 $ cat > addverboseitems.py <<EOF
1724 > r'''extension to test omit indicating.
1724 > r'''extension to test omit indicating.
1725 >
1725 >
1726 > This paragraph is never omitted (for extension)
1726 > This paragraph is never omitted (for extension)
1727 >
1727 >
1728 > .. container:: verbose
1728 > .. container:: verbose
1729 >
1729 >
1730 > This paragraph is omitted,
1730 > This paragraph is omitted,
1731 > if :hg:\`help\` is invoked without \`\`-v\`\` (for extension)
1731 > if :hg:\`help\` is invoked without \`\`-v\`\` (for extension)
1732 >
1732 >
1733 > This paragraph is never omitted, too (for extension)
1733 > This paragraph is never omitted, too (for extension)
1734 > '''
1734 > '''
1735 > from __future__ import absolute_import
1735 > from __future__ import absolute_import
1736 > from mercurial import commands, help
1736 > from mercurial import commands, help
1737 > testtopic = br"""This paragraph is never omitted (for topic).
1737 > testtopic = br"""This paragraph is never omitted (for topic).
1738 >
1738 >
1739 > .. container:: verbose
1739 > .. container:: verbose
1740 >
1740 >
1741 > This paragraph is omitted,
1741 > This paragraph is omitted,
1742 > if :hg:\`help\` is invoked without \`\`-v\`\` (for topic)
1742 > if :hg:\`help\` is invoked without \`\`-v\`\` (for topic)
1743 >
1743 >
1744 > This paragraph is never omitted, too (for topic)
1744 > This paragraph is never omitted, too (for topic)
1745 > """
1745 > """
1746 > def extsetup(ui):
1746 > def extsetup(ui):
1747 > help.helptable.append(([b"topic-containing-verbose"],
1747 > help.helptable.append(([b"topic-containing-verbose"],
1748 > b"This is the topic to test omit indicating.",
1748 > b"This is the topic to test omit indicating.",
1749 > lambda ui: testtopic))
1749 > lambda ui: testtopic))
1750 > EOF
1750 > EOF
1751 $ echo '[extensions]' >> $HGRCPATH
1751 $ echo '[extensions]' >> $HGRCPATH
1752 $ echo "addverboseitems = `pwd`/addverboseitems.py" >> $HGRCPATH
1752 $ echo "addverboseitems = `pwd`/addverboseitems.py" >> $HGRCPATH
1753 $ hg help addverboseitems
1753 $ hg help addverboseitems
1754 addverboseitems extension - extension to test omit indicating.
1754 addverboseitems extension - extension to test omit indicating.
1755
1755
1756 This paragraph is never omitted (for extension)
1756 This paragraph is never omitted (for extension)
1757
1757
1758 This paragraph is never omitted, too (for extension)
1758 This paragraph is never omitted, too (for extension)
1759
1759
1760 (some details hidden, use --verbose to show complete help)
1760 (some details hidden, use --verbose to show complete help)
1761
1761
1762 no commands defined
1762 no commands defined
1763 $ hg help -v addverboseitems
1763 $ hg help -v addverboseitems
1764 addverboseitems extension - extension to test omit indicating.
1764 addverboseitems extension - extension to test omit indicating.
1765
1765
1766 This paragraph is never omitted (for extension)
1766 This paragraph is never omitted (for extension)
1767
1767
1768 This paragraph is omitted, if 'hg help' is invoked without "-v" (for
1768 This paragraph is omitted, if 'hg help' is invoked without "-v" (for
1769 extension)
1769 extension)
1770
1770
1771 This paragraph is never omitted, too (for extension)
1771 This paragraph is never omitted, too (for extension)
1772
1772
1773 no commands defined
1773 no commands defined
1774 $ hg help topic-containing-verbose
1774 $ hg help topic-containing-verbose
1775 This is the topic to test omit indicating.
1775 This is the topic to test omit indicating.
1776 """"""""""""""""""""""""""""""""""""""""""
1776 """"""""""""""""""""""""""""""""""""""""""
1777
1777
1778 This paragraph is never omitted (for topic).
1778 This paragraph is never omitted (for topic).
1779
1779
1780 This paragraph is never omitted, too (for topic)
1780 This paragraph is never omitted, too (for topic)
1781
1781
1782 (some details hidden, use --verbose to show complete help)
1782 (some details hidden, use --verbose to show complete help)
1783 $ hg help -v topic-containing-verbose
1783 $ hg help -v topic-containing-verbose
1784 This is the topic to test omit indicating.
1784 This is the topic to test omit indicating.
1785 """"""""""""""""""""""""""""""""""""""""""
1785 """"""""""""""""""""""""""""""""""""""""""
1786
1786
1787 This paragraph is never omitted (for topic).
1787 This paragraph is never omitted (for topic).
1788
1788
1789 This paragraph is omitted, if 'hg help' is invoked without "-v" (for
1789 This paragraph is omitted, if 'hg help' is invoked without "-v" (for
1790 topic)
1790 topic)
1791
1791
1792 This paragraph is never omitted, too (for topic)
1792 This paragraph is never omitted, too (for topic)
1793
1793
1794 Test section lookup
1794 Test section lookup
1795
1795
1796 $ hg help revset.merge
1796 $ hg help revset.merge
1797 "merge()"
1797 "merge()"
1798 Changeset is a merge changeset.
1798 Changeset is a merge changeset.
1799
1799
1800 $ hg help glossary.dag
1800 $ hg help glossary.dag
1801 DAG
1801 DAG
1802 The repository of changesets of a distributed version control system
1802 The repository of changesets of a distributed version control system
1803 (DVCS) can be described as a directed acyclic graph (DAG), consisting
1803 (DVCS) can be described as a directed acyclic graph (DAG), consisting
1804 of nodes and edges, where nodes correspond to changesets and edges
1804 of nodes and edges, where nodes correspond to changesets and edges
1805 imply a parent -> child relation. This graph can be visualized by
1805 imply a parent -> child relation. This graph can be visualized by
1806 graphical tools such as 'hg log --graph'. In Mercurial, the DAG is
1806 graphical tools such as 'hg log --graph'. In Mercurial, the DAG is
1807 limited by the requirement for children to have at most two parents.
1807 limited by the requirement for children to have at most two parents.
1808
1808
1809
1809
1810 $ hg help hgrc.paths
1810 $ hg help hgrc.paths
1811 "paths"
1811 "paths"
1812 -------
1812 -------
1813
1813
1814 Assigns symbolic names and behavior to repositories.
1814 Assigns symbolic names and behavior to repositories.
1815
1815
1816 Options are symbolic names defining the URL or directory that is the
1816 Options are symbolic names defining the URL or directory that is the
1817 location of the repository. Example:
1817 location of the repository. Example:
1818
1818
1819 [paths]
1819 [paths]
1820 my_server = https://example.com/my_repo
1820 my_server = https://example.com/my_repo
1821 local_path = /home/me/repo
1821 local_path = /home/me/repo
1822
1822
1823 These symbolic names can be used from the command line. To pull from
1823 These symbolic names can be used from the command line. To pull from
1824 "my_server": 'hg pull my_server'. To push to "local_path": 'hg push
1824 "my_server": 'hg pull my_server'. To push to "local_path": 'hg push
1825 local_path'.
1825 local_path'.
1826
1826
1827 Options containing colons (":") denote sub-options that can influence
1827 Options containing colons (":") denote sub-options that can influence
1828 behavior for that specific path. Example:
1828 behavior for that specific path. Example:
1829
1829
1830 [paths]
1830 [paths]
1831 my_server = https://example.com/my_path
1831 my_server = https://example.com/my_path
1832 my_server:pushurl = ssh://example.com/my_path
1832 my_server:pushurl = ssh://example.com/my_path
1833
1833
1834 The following sub-options can be defined:
1834 The following sub-options can be defined:
1835
1835
1836 "pushurl"
1836 "pushurl"
1837 The URL to use for push operations. If not defined, the location
1837 The URL to use for push operations. If not defined, the location
1838 defined by the path's main entry is used.
1838 defined by the path's main entry is used.
1839
1839
1840 "pushrev"
1840 "pushrev"
1841 A revset defining which revisions to push by default.
1841 A revset defining which revisions to push by default.
1842
1842
1843 When 'hg push' is executed without a "-r" argument, the revset defined
1843 When 'hg push' is executed without a "-r" argument, the revset defined
1844 by this sub-option is evaluated to determine what to push.
1844 by this sub-option is evaluated to determine what to push.
1845
1845
1846 For example, a value of "." will push the working directory's revision
1846 For example, a value of "." will push the working directory's revision
1847 by default.
1847 by default.
1848
1848
1849 Revsets specifying bookmarks will not result in the bookmark being
1849 Revsets specifying bookmarks will not result in the bookmark being
1850 pushed.
1850 pushed.
1851
1851
1852 The following special named paths exist:
1852 The following special named paths exist:
1853
1853
1854 "default"
1854 "default"
1855 The URL or directory to use when no source or remote is specified.
1855 The URL or directory to use when no source or remote is specified.
1856
1856
1857 'hg clone' will automatically define this path to the location the
1857 'hg clone' will automatically define this path to the location the
1858 repository was cloned from.
1858 repository was cloned from.
1859
1859
1860 "default-push"
1860 "default-push"
1861 (deprecated) The URL or directory for the default 'hg push' location.
1861 (deprecated) The URL or directory for the default 'hg push' location.
1862 "default:pushurl" should be used instead.
1862 "default:pushurl" should be used instead.
1863
1863
1864 $ hg help glossary.mcguffin
1864 $ hg help glossary.mcguffin
1865 abort: help section not found: glossary.mcguffin
1865 abort: help section not found: glossary.mcguffin
1866 [255]
1866 [255]
1867
1867
1868 $ hg help glossary.mc.guffin
1868 $ hg help glossary.mc.guffin
1869 abort: help section not found: glossary.mc.guffin
1869 abort: help section not found: glossary.mc.guffin
1870 [255]
1870 [255]
1871
1871
1872 $ hg help template.files
1872 $ hg help template.files
1873 files List of strings. All files modified, added, or removed by
1873 files List of strings. All files modified, added, or removed by
1874 this changeset.
1874 this changeset.
1875 files(pattern)
1875 files(pattern)
1876 All files of the current changeset matching the pattern. See
1876 All files of the current changeset matching the pattern. See
1877 'hg help patterns'.
1877 'hg help patterns'.
1878
1878
1879 Test section lookup by translated message
1879 Test section lookup by translated message
1880
1880
1881 str.lower() instead of encoding.lower(str) on translated message might
1881 str.lower() instead of encoding.lower(str) on translated message might
1882 make message meaningless, because some encoding uses 0x41(A) - 0x5a(Z)
1882 make message meaningless, because some encoding uses 0x41(A) - 0x5a(Z)
1883 as the second or later byte of multi-byte character.
1883 as the second or later byte of multi-byte character.
1884
1884
1885 For example, "\x8bL\x98^" (translation of "record" in ja_JP.cp932)
1885 For example, "\x8bL\x98^" (translation of "record" in ja_JP.cp932)
1886 contains 0x4c (L). str.lower() replaces 0x4c(L) by 0x6c(l) and this
1886 contains 0x4c (L). str.lower() replaces 0x4c(L) by 0x6c(l) and this
1887 replacement makes message meaningless.
1887 replacement makes message meaningless.
1888
1888
1889 This tests that section lookup by translated string isn't broken by
1889 This tests that section lookup by translated string isn't broken by
1890 such str.lower().
1890 such str.lower().
1891
1891
1892 $ "$PYTHON" <<EOF
1892 $ "$PYTHON" <<EOF
1893 > def escape(s):
1893 > def escape(s):
1894 > return b''.join(b'\\u%x' % ord(uc) for uc in s.decode('cp932'))
1894 > return b''.join(b'\\u%x' % ord(uc) for uc in s.decode('cp932'))
1895 > # translation of "record" in ja_JP.cp932
1895 > # translation of "record" in ja_JP.cp932
1896 > upper = b"\x8bL\x98^"
1896 > upper = b"\x8bL\x98^"
1897 > # str.lower()-ed section name should be treated as different one
1897 > # str.lower()-ed section name should be treated as different one
1898 > lower = b"\x8bl\x98^"
1898 > lower = b"\x8bl\x98^"
1899 > with open('ambiguous.py', 'wb') as fp:
1899 > with open('ambiguous.py', 'wb') as fp:
1900 > fp.write(b"""# ambiguous section names in ja_JP.cp932
1900 > fp.write(b"""# ambiguous section names in ja_JP.cp932
1901 > u'''summary of extension
1901 > u'''summary of extension
1902 >
1902 >
1903 > %s
1903 > %s
1904 > ----
1904 > ----
1905 >
1905 >
1906 > Upper name should show only this message
1906 > Upper name should show only this message
1907 >
1907 >
1908 > %s
1908 > %s
1909 > ----
1909 > ----
1910 >
1910 >
1911 > Lower name should show only this message
1911 > Lower name should show only this message
1912 >
1912 >
1913 > subsequent section
1913 > subsequent section
1914 > ------------------
1914 > ------------------
1915 >
1915 >
1916 > This should be hidden at 'hg help ambiguous' with section name.
1916 > This should be hidden at 'hg help ambiguous' with section name.
1917 > '''
1917 > '''
1918 > """ % (escape(upper), escape(lower)))
1918 > """ % (escape(upper), escape(lower)))
1919 > EOF
1919 > EOF
1920
1920
1921 $ cat >> $HGRCPATH <<EOF
1921 $ cat >> $HGRCPATH <<EOF
1922 > [extensions]
1922 > [extensions]
1923 > ambiguous = ./ambiguous.py
1923 > ambiguous = ./ambiguous.py
1924 > EOF
1924 > EOF
1925
1925
1926 $ "$PYTHON" <<EOF | sh
1926 $ "$PYTHON" <<EOF | sh
1927 > from mercurial.utils import procutil
1927 > from mercurial.utils import procutil
1928 > upper = b"\x8bL\x98^"
1928 > upper = b"\x8bL\x98^"
1929 > procutil.stdout.write(b"hg --encoding cp932 help -e ambiguous.%s\n" % upper)
1929 > procutil.stdout.write(b"hg --encoding cp932 help -e ambiguous.%s\n" % upper)
1930 > EOF
1930 > EOF
1931 \x8bL\x98^ (esc)
1931 \x8bL\x98^ (esc)
1932 ----
1932 ----
1933
1933
1934 Upper name should show only this message
1934 Upper name should show only this message
1935
1935
1936
1936
1937 $ "$PYTHON" <<EOF | sh
1937 $ "$PYTHON" <<EOF | sh
1938 > from mercurial.utils import procutil
1938 > from mercurial.utils import procutil
1939 > lower = b"\x8bl\x98^"
1939 > lower = b"\x8bl\x98^"
1940 > procutil.stdout.write(b"hg --encoding cp932 help -e ambiguous.%s\n" % lower)
1940 > procutil.stdout.write(b"hg --encoding cp932 help -e ambiguous.%s\n" % lower)
1941 > EOF
1941 > EOF
1942 \x8bl\x98^ (esc)
1942 \x8bl\x98^ (esc)
1943 ----
1943 ----
1944
1944
1945 Lower name should show only this message
1945 Lower name should show only this message
1946
1946
1947
1947
1948 $ cat >> $HGRCPATH <<EOF
1948 $ cat >> $HGRCPATH <<EOF
1949 > [extensions]
1949 > [extensions]
1950 > ambiguous = !
1950 > ambiguous = !
1951 > EOF
1951 > EOF
1952
1952
1953 Show help content of disabled extensions
1953 Show help content of disabled extensions
1954
1954
1955 $ cat >> $HGRCPATH <<EOF
1955 $ cat >> $HGRCPATH <<EOF
1956 > [extensions]
1956 > [extensions]
1957 > ambiguous = !./ambiguous.py
1957 > ambiguous = !./ambiguous.py
1958 > EOF
1958 > EOF
1959 $ hg help -e ambiguous
1959 $ hg help -e ambiguous
1960 ambiguous extension - (no help text available)
1960 ambiguous extension - (no help text available)
1961
1961
1962 (use 'hg help extensions' for information on enabling extensions)
1962 (use 'hg help extensions' for information on enabling extensions)
1963
1963
1964 Test dynamic list of merge tools only shows up once
1964 Test dynamic list of merge tools only shows up once
1965 $ hg help merge-tools
1965 $ hg help merge-tools
1966 Merge Tools
1966 Merge Tools
1967 """""""""""
1967 """""""""""
1968
1968
1969 To merge files Mercurial uses merge tools.
1969 To merge files Mercurial uses merge tools.
1970
1970
1971 A merge tool combines two different versions of a file into a merged file.
1971 A merge tool combines two different versions of a file into a merged file.
1972 Merge tools are given the two files and the greatest common ancestor of
1972 Merge tools are given the two files and the greatest common ancestor of
1973 the two file versions, so they can determine the changes made on both
1973 the two file versions, so they can determine the changes made on both
1974 branches.
1974 branches.
1975
1975
1976 Merge tools are used both for 'hg resolve', 'hg merge', 'hg update', 'hg
1976 Merge tools are used both for 'hg resolve', 'hg merge', 'hg update', 'hg
1977 backout' and in several extensions.
1977 backout' and in several extensions.
1978
1978
1979 Usually, the merge tool tries to automatically reconcile the files by
1979 Usually, the merge tool tries to automatically reconcile the files by
1980 combining all non-overlapping changes that occurred separately in the two
1980 combining all non-overlapping changes that occurred separately in the two
1981 different evolutions of the same initial base file. Furthermore, some
1981 different evolutions of the same initial base file. Furthermore, some
1982 interactive merge programs make it easier to manually resolve conflicting
1982 interactive merge programs make it easier to manually resolve conflicting
1983 merges, either in a graphical way, or by inserting some conflict markers.
1983 merges, either in a graphical way, or by inserting some conflict markers.
1984 Mercurial does not include any interactive merge programs but relies on
1984 Mercurial does not include any interactive merge programs but relies on
1985 external tools for that.
1985 external tools for that.
1986
1986
1987 Available merge tools
1987 Available merge tools
1988 =====================
1988 =====================
1989
1989
1990 External merge tools and their properties are configured in the merge-
1990 External merge tools and their properties are configured in the merge-
1991 tools configuration section - see hgrc(5) - but they can often just be
1991 tools configuration section - see hgrc(5) - but they can often just be
1992 named by their executable.
1992 named by their executable.
1993
1993
1994 A merge tool is generally usable if its executable can be found on the
1994 A merge tool is generally usable if its executable can be found on the
1995 system and if it can handle the merge. The executable is found if it is an
1995 system and if it can handle the merge. The executable is found if it is an
1996 absolute or relative executable path or the name of an application in the
1996 absolute or relative executable path or the name of an application in the
1997 executable search path. The tool is assumed to be able to handle the merge
1997 executable search path. The tool is assumed to be able to handle the merge
1998 if it can handle symlinks if the file is a symlink, if it can handle
1998 if it can handle symlinks if the file is a symlink, if it can handle
1999 binary files if the file is binary, and if a GUI is available if the tool
1999 binary files if the file is binary, and if a GUI is available if the tool
2000 requires a GUI.
2000 requires a GUI.
2001
2001
2002 There are some internal merge tools which can be used. The internal merge
2002 There are some internal merge tools which can be used. The internal merge
2003 tools are:
2003 tools are:
2004
2004
2005 ":dump"
2005 ":dump"
2006 Creates three versions of the files to merge, containing the contents of
2006 Creates three versions of the files to merge, containing the contents of
2007 local, other and base. These files can then be used to perform a merge
2007 local, other and base. These files can then be used to perform a merge
2008 manually. If the file to be merged is named "a.txt", these files will
2008 manually. If the file to be merged is named "a.txt", these files will
2009 accordingly be named "a.txt.local", "a.txt.other" and "a.txt.base" and
2009 accordingly be named "a.txt.local", "a.txt.other" and "a.txt.base" and
2010 they will be placed in the same directory as "a.txt".
2010 they will be placed in the same directory as "a.txt".
2011
2011
2012 This implies premerge. Therefore, files aren't dumped, if premerge runs
2012 This implies premerge. Therefore, files aren't dumped, if premerge runs
2013 successfully. Use :forcedump to forcibly write files out.
2013 successfully. Use :forcedump to forcibly write files out.
2014
2014
2015 (actual capabilities: binary, symlink)
2015 (actual capabilities: binary, symlink)
2016
2016
2017 ":fail"
2017 ":fail"
2018 Rather than attempting to merge files that were modified on both
2018 Rather than attempting to merge files that were modified on both
2019 branches, it marks them as unresolved. The resolve command must be used
2019 branches, it marks them as unresolved. The resolve command must be used
2020 to resolve these conflicts.
2020 to resolve these conflicts.
2021
2021
2022 (actual capabilities: binary, symlink)
2022 (actual capabilities: binary, symlink)
2023
2023
2024 ":forcedump"
2024 ":forcedump"
2025 Creates three versions of the files as same as :dump, but omits
2025 Creates three versions of the files as same as :dump, but omits
2026 premerge.
2026 premerge.
2027
2027
2028 (actual capabilities: binary, symlink)
2028 (actual capabilities: binary, symlink)
2029
2029
2030 ":local"
2030 ":local"
2031 Uses the local 'p1()' version of files as the merged version.
2031 Uses the local 'p1()' version of files as the merged version.
2032
2032
2033 (actual capabilities: binary, symlink)
2033 (actual capabilities: binary, symlink)
2034
2034
2035 ":merge"
2035 ":merge"
2036 Uses the internal non-interactive simple merge algorithm for merging
2036 Uses the internal non-interactive simple merge algorithm for merging
2037 files. It will fail if there are any conflicts and leave markers in the
2037 files. It will fail if there are any conflicts and leave markers in the
2038 partially merged file. Markers will have two sections, one for each side
2038 partially merged file. Markers will have two sections, one for each side
2039 of merge.
2039 of merge.
2040
2040
2041 ":merge-local"
2041 ":merge-local"
2042 Like :merge, but resolve all conflicts non-interactively in favor of the
2042 Like :merge, but resolve all conflicts non-interactively in favor of the
2043 local 'p1()' changes.
2043 local 'p1()' changes.
2044
2044
2045 ":merge-other"
2045 ":merge-other"
2046 Like :merge, but resolve all conflicts non-interactively in favor of the
2046 Like :merge, but resolve all conflicts non-interactively in favor of the
2047 other 'p2()' changes.
2047 other 'p2()' changes.
2048
2048
2049 ":merge3"
2049 ":merge3"
2050 Uses the internal non-interactive simple merge algorithm for merging
2050 Uses the internal non-interactive simple merge algorithm for merging
2051 files. It will fail if there are any conflicts and leave markers in the
2051 files. It will fail if there are any conflicts and leave markers in the
2052 partially merged file. Marker will have three sections, one from each
2052 partially merged file. Marker will have three sections, one from each
2053 side of the merge and one for the base content.
2053 side of the merge and one for the base content.
2054
2054
2055 ":other"
2055 ":other"
2056 Uses the other 'p2()' version of files as the merged version.
2056 Uses the other 'p2()' version of files as the merged version.
2057
2057
2058 (actual capabilities: binary, symlink)
2058 (actual capabilities: binary, symlink)
2059
2059
2060 ":prompt"
2060 ":prompt"
2061 Asks the user which of the local 'p1()' or the other 'p2()' version to
2061 Asks the user which of the local 'p1()' or the other 'p2()' version to
2062 keep as the merged version.
2062 keep as the merged version.
2063
2063
2064 (actual capabilities: binary, symlink)
2064 (actual capabilities: binary, symlink)
2065
2065
2066 ":tagmerge"
2066 ":tagmerge"
2067 Uses the internal tag merge algorithm (experimental).
2067 Uses the internal tag merge algorithm (experimental).
2068
2068
2069 ":union"
2069 ":union"
2070 Uses the internal non-interactive simple merge algorithm for merging
2070 Uses the internal non-interactive simple merge algorithm for merging
2071 files. It will use both left and right sides for conflict regions. No
2071 files. It will use both left and right sides for conflict regions. No
2072 markers are inserted.
2072 markers are inserted.
2073
2073
2074 Internal tools are always available and do not require a GUI but will by
2074 Internal tools are always available and do not require a GUI but will by
2075 default not handle symlinks or binary files. See next section for detail
2075 default not handle symlinks or binary files. See next section for detail
2076 about "actual capabilities" described above.
2076 about "actual capabilities" described above.
2077
2077
2078 Choosing a merge tool
2078 Choosing a merge tool
2079 =====================
2079 =====================
2080
2080
2081 Mercurial uses these rules when deciding which merge tool to use:
2081 Mercurial uses these rules when deciding which merge tool to use:
2082
2082
2083 1. If a tool has been specified with the --tool option to merge or
2083 1. If a tool has been specified with the --tool option to merge or
2084 resolve, it is used. If it is the name of a tool in the merge-tools
2084 resolve, it is used. If it is the name of a tool in the merge-tools
2085 configuration, its configuration is used. Otherwise the specified tool
2085 configuration, its configuration is used. Otherwise the specified tool
2086 must be executable by the shell.
2086 must be executable by the shell.
2087 2. If the "HGMERGE" environment variable is present, its value is used and
2087 2. If the "HGMERGE" environment variable is present, its value is used and
2088 must be executable by the shell.
2088 must be executable by the shell.
2089 3. If the filename of the file to be merged matches any of the patterns in
2089 3. If the filename of the file to be merged matches any of the patterns in
2090 the merge-patterns configuration section, the first usable merge tool
2090 the merge-patterns configuration section, the first usable merge tool
2091 corresponding to a matching pattern is used.
2091 corresponding to a matching pattern is used.
2092 4. If ui.merge is set it will be considered next. If the value is not the
2092 4. If ui.merge is set it will be considered next. If the value is not the
2093 name of a configured tool, the specified value is used and must be
2093 name of a configured tool, the specified value is used and must be
2094 executable by the shell. Otherwise the named tool is used if it is
2094 executable by the shell. Otherwise the named tool is used if it is
2095 usable.
2095 usable.
2096 5. If any usable merge tools are present in the merge-tools configuration
2096 5. If any usable merge tools are present in the merge-tools configuration
2097 section, the one with the highest priority is used.
2097 section, the one with the highest priority is used.
2098 6. If a program named "hgmerge" can be found on the system, it is used -
2098 6. If a program named "hgmerge" can be found on the system, it is used -
2099 but it will by default not be used for symlinks and binary files.
2099 but it will by default not be used for symlinks and binary files.
2100 7. If the file to be merged is not binary and is not a symlink, then
2100 7. If the file to be merged is not binary and is not a symlink, then
2101 internal ":merge" is used.
2101 internal ":merge" is used.
2102 8. Otherwise, ":prompt" is used.
2102 8. Otherwise, ":prompt" is used.
2103
2103
2104 For historical reason, Mercurial treats merge tools as below while
2104 For historical reason, Mercurial treats merge tools as below while
2105 examining rules above.
2105 examining rules above.
2106
2106
2107 step specified via binary symlink
2107 step specified via binary symlink
2108 ----------------------------------
2108 ----------------------------------
2109 1. --tool o/o o/o
2109 1. --tool o/o o/o
2110 2. HGMERGE o/o o/o
2110 2. HGMERGE o/o o/o
2111 3. merge-patterns o/o(*) x/?(*)
2111 3. merge-patterns o/o(*) x/?(*)
2112 4. ui.merge x/?(*) x/?(*)
2112 4. ui.merge x/?(*) x/?(*)
2113
2113
2114 Each capability column indicates Mercurial behavior for internal/external
2114 Each capability column indicates Mercurial behavior for internal/external
2115 merge tools at examining each rule.
2115 merge tools at examining each rule.
2116
2116
2117 - "o": "assume that a tool has capability"
2117 - "o": "assume that a tool has capability"
2118 - "x": "assume that a tool does not have capability"
2118 - "x": "assume that a tool does not have capability"
2119 - "?": "check actual capability of a tool"
2119 - "?": "check actual capability of a tool"
2120
2120
2121 If "merge.strict-capability-check" configuration is true, Mercurial checks
2121 If "merge.strict-capability-check" configuration is true, Mercurial checks
2122 capabilities of merge tools strictly in (*) cases above (= each capability
2122 capabilities of merge tools strictly in (*) cases above (= each capability
2123 column becomes "?/?"). It is false by default for backward compatibility.
2123 column becomes "?/?"). It is false by default for backward compatibility.
2124
2124
2125 Note:
2125 Note:
2126 After selecting a merge program, Mercurial will by default attempt to
2126 After selecting a merge program, Mercurial will by default attempt to
2127 merge the files using a simple merge algorithm first. Only if it
2127 merge the files using a simple merge algorithm first. Only if it
2128 doesn't succeed because of conflicting changes will Mercurial actually
2128 doesn't succeed because of conflicting changes will Mercurial actually
2129 execute the merge program. Whether to use the simple merge algorithm
2129 execute the merge program. Whether to use the simple merge algorithm
2130 first can be controlled by the premerge setting of the merge tool.
2130 first can be controlled by the premerge setting of the merge tool.
2131 Premerge is enabled by default unless the file is binary or a symlink.
2131 Premerge is enabled by default unless the file is binary or a symlink.
2132
2132
2133 See the merge-tools and ui sections of hgrc(5) for details on the
2133 See the merge-tools and ui sections of hgrc(5) for details on the
2134 configuration of merge tools.
2134 configuration of merge tools.
2135
2135
2136 Compression engines listed in `hg help bundlespec`
2136 Compression engines listed in `hg help bundlespec`
2137
2137
2138 $ hg help bundlespec | grep gzip
2138 $ hg help bundlespec | grep gzip
2139 "v1" bundles can only use the "gzip", "bzip2", and "none" compression
2139 "v1" bundles can only use the "gzip", "bzip2", and "none" compression
2140 An algorithm that produces smaller bundles than "gzip".
2140 An algorithm that produces smaller bundles than "gzip".
2141 This engine will likely produce smaller bundles than "gzip" but will be
2141 This engine will likely produce smaller bundles than "gzip" but will be
2142 "gzip"
2142 "gzip"
2143 better compression than "gzip". It also frequently yields better (?)
2143 better compression than "gzip". It also frequently yields better (?)
2144
2144
2145 Test usage of section marks in help documents
2145 Test usage of section marks in help documents
2146
2146
2147 $ cd "$TESTDIR"/../doc
2147 $ cd "$TESTDIR"/../doc
2148 $ "$PYTHON" check-seclevel.py
2148 $ "$PYTHON" check-seclevel.py
2149 $ cd $TESTTMP
2149 $ cd $TESTTMP
2150
2150
2151 #if serve
2151 #if serve
2152
2152
2153 Test the help pages in hgweb.
2153 Test the help pages in hgweb.
2154
2154
2155 Dish up an empty repo; serve it cold.
2155 Dish up an empty repo; serve it cold.
2156
2156
2157 $ hg init "$TESTTMP/test"
2157 $ hg init "$TESTTMP/test"
2158 $ hg serve -R "$TESTTMP/test" -n test -p $HGPORT -d --pid-file=hg.pid
2158 $ hg serve -R "$TESTTMP/test" -n test -p $HGPORT -d --pid-file=hg.pid
2159 $ cat hg.pid >> $DAEMON_PIDS
2159 $ cat hg.pid >> $DAEMON_PIDS
2160
2160
2161 $ get-with-headers.py $LOCALIP:$HGPORT "help"
2161 $ get-with-headers.py $LOCALIP:$HGPORT "help"
2162 200 Script output follows
2162 200 Script output follows
2163
2163
2164 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2164 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2165 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2165 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2166 <head>
2166 <head>
2167 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2167 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2168 <meta name="robots" content="index, nofollow" />
2168 <meta name="robots" content="index, nofollow" />
2169 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2169 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2170 <script type="text/javascript" src="/static/mercurial.js"></script>
2170 <script type="text/javascript" src="/static/mercurial.js"></script>
2171
2171
2172 <title>Help: Index</title>
2172 <title>Help: Index</title>
2173 </head>
2173 </head>
2174 <body>
2174 <body>
2175
2175
2176 <div class="container">
2176 <div class="container">
2177 <div class="menu">
2177 <div class="menu">
2178 <div class="logo">
2178 <div class="logo">
2179 <a href="https://mercurial-scm.org/">
2179 <a href="https://mercurial-scm.org/">
2180 <img src="/static/hglogo.png" alt="mercurial" /></a>
2180 <img src="/static/hglogo.png" alt="mercurial" /></a>
2181 </div>
2181 </div>
2182 <ul>
2182 <ul>
2183 <li><a href="/shortlog">log</a></li>
2183 <li><a href="/shortlog">log</a></li>
2184 <li><a href="/graph">graph</a></li>
2184 <li><a href="/graph">graph</a></li>
2185 <li><a href="/tags">tags</a></li>
2185 <li><a href="/tags">tags</a></li>
2186 <li><a href="/bookmarks">bookmarks</a></li>
2186 <li><a href="/bookmarks">bookmarks</a></li>
2187 <li><a href="/branches">branches</a></li>
2187 <li><a href="/branches">branches</a></li>
2188 </ul>
2188 </ul>
2189 <ul>
2189 <ul>
2190 <li class="active">help</li>
2190 <li class="active">help</li>
2191 </ul>
2191 </ul>
2192 </div>
2192 </div>
2193
2193
2194 <div class="main">
2194 <div class="main">
2195 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2195 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2196
2196
2197 <form class="search" action="/log">
2197 <form class="search" action="/log">
2198
2198
2199 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
2199 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
2200 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2200 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2201 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2201 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2202 </form>
2202 </form>
2203 <table class="bigtable">
2203 <table class="bigtable">
2204 <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr>
2204 <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr>
2205
2205
2206 <tr><td>
2206 <tr><td>
2207 <a href="/help/bundlespec">
2207 <a href="/help/bundlespec">
2208 bundlespec
2208 bundlespec
2209 </a>
2209 </a>
2210 </td><td>
2210 </td><td>
2211 Bundle File Formats
2211 Bundle File Formats
2212 </td></tr>
2212 </td></tr>
2213 <tr><td>
2213 <tr><td>
2214 <a href="/help/color">
2214 <a href="/help/color">
2215 color
2215 color
2216 </a>
2216 </a>
2217 </td><td>
2217 </td><td>
2218 Colorizing Outputs
2218 Colorizing Outputs
2219 </td></tr>
2219 </td></tr>
2220 <tr><td>
2220 <tr><td>
2221 <a href="/help/config">
2221 <a href="/help/config">
2222 config
2222 config
2223 </a>
2223 </a>
2224 </td><td>
2224 </td><td>
2225 Configuration Files
2225 Configuration Files
2226 </td></tr>
2226 </td></tr>
2227 <tr><td>
2227 <tr><td>
2228 <a href="/help/dates">
2228 <a href="/help/dates">
2229 dates
2229 dates
2230 </a>
2230 </a>
2231 </td><td>
2231 </td><td>
2232 Date Formats
2232 Date Formats
2233 </td></tr>
2233 </td></tr>
2234 <tr><td>
2234 <tr><td>
2235 <a href="/help/deprecated">
2235 <a href="/help/deprecated">
2236 deprecated
2236 deprecated
2237 </a>
2237 </a>
2238 </td><td>
2238 </td><td>
2239 Deprecated Features
2239 Deprecated Features
2240 </td></tr>
2240 </td></tr>
2241 <tr><td>
2241 <tr><td>
2242 <a href="/help/diffs">
2242 <a href="/help/diffs">
2243 diffs
2243 diffs
2244 </a>
2244 </a>
2245 </td><td>
2245 </td><td>
2246 Diff Formats
2246 Diff Formats
2247 </td></tr>
2247 </td></tr>
2248 <tr><td>
2248 <tr><td>
2249 <a href="/help/environment">
2249 <a href="/help/environment">
2250 environment
2250 environment
2251 </a>
2251 </a>
2252 </td><td>
2252 </td><td>
2253 Environment Variables
2253 Environment Variables
2254 </td></tr>
2254 </td></tr>
2255 <tr><td>
2255 <tr><td>
2256 <a href="/help/extensions">
2256 <a href="/help/extensions">
2257 extensions
2257 extensions
2258 </a>
2258 </a>
2259 </td><td>
2259 </td><td>
2260 Using Additional Features
2260 Using Additional Features
2261 </td></tr>
2261 </td></tr>
2262 <tr><td>
2262 <tr><td>
2263 <a href="/help/filesets">
2263 <a href="/help/filesets">
2264 filesets
2264 filesets
2265 </a>
2265 </a>
2266 </td><td>
2266 </td><td>
2267 Specifying File Sets
2267 Specifying File Sets
2268 </td></tr>
2268 </td></tr>
2269 <tr><td>
2269 <tr><td>
2270 <a href="/help/flags">
2270 <a href="/help/flags">
2271 flags
2271 flags
2272 </a>
2272 </a>
2273 </td><td>
2273 </td><td>
2274 Command-line flags
2274 Command-line flags
2275 </td></tr>
2275 </td></tr>
2276 <tr><td>
2276 <tr><td>
2277 <a href="/help/glossary">
2277 <a href="/help/glossary">
2278 glossary
2278 glossary
2279 </a>
2279 </a>
2280 </td><td>
2280 </td><td>
2281 Glossary
2281 Glossary
2282 </td></tr>
2282 </td></tr>
2283 <tr><td>
2283 <tr><td>
2284 <a href="/help/hgignore">
2284 <a href="/help/hgignore">
2285 hgignore
2285 hgignore
2286 </a>
2286 </a>
2287 </td><td>
2287 </td><td>
2288 Syntax for Mercurial Ignore Files
2288 Syntax for Mercurial Ignore Files
2289 </td></tr>
2289 </td></tr>
2290 <tr><td>
2290 <tr><td>
2291 <a href="/help/hgweb">
2291 <a href="/help/hgweb">
2292 hgweb
2292 hgweb
2293 </a>
2293 </a>
2294 </td><td>
2294 </td><td>
2295 Configuring hgweb
2295 Configuring hgweb
2296 </td></tr>
2296 </td></tr>
2297 <tr><td>
2297 <tr><td>
2298 <a href="/help/internals">
2298 <a href="/help/internals">
2299 internals
2299 internals
2300 </a>
2300 </a>
2301 </td><td>
2301 </td><td>
2302 Technical implementation topics
2302 Technical implementation topics
2303 </td></tr>
2303 </td></tr>
2304 <tr><td>
2304 <tr><td>
2305 <a href="/help/merge-tools">
2305 <a href="/help/merge-tools">
2306 merge-tools
2306 merge-tools
2307 </a>
2307 </a>
2308 </td><td>
2308 </td><td>
2309 Merge Tools
2309 Merge Tools
2310 </td></tr>
2310 </td></tr>
2311 <tr><td>
2311 <tr><td>
2312 <a href="/help/pager">
2312 <a href="/help/pager">
2313 pager
2313 pager
2314 </a>
2314 </a>
2315 </td><td>
2315 </td><td>
2316 Pager Support
2316 Pager Support
2317 </td></tr>
2317 </td></tr>
2318 <tr><td>
2318 <tr><td>
2319 <a href="/help/patterns">
2319 <a href="/help/patterns">
2320 patterns
2320 patterns
2321 </a>
2321 </a>
2322 </td><td>
2322 </td><td>
2323 File Name Patterns
2323 File Name Patterns
2324 </td></tr>
2324 </td></tr>
2325 <tr><td>
2325 <tr><td>
2326 <a href="/help/phases">
2326 <a href="/help/phases">
2327 phases
2327 phases
2328 </a>
2328 </a>
2329 </td><td>
2329 </td><td>
2330 Working with Phases
2330 Working with Phases
2331 </td></tr>
2331 </td></tr>
2332 <tr><td>
2332 <tr><td>
2333 <a href="/help/revisions">
2333 <a href="/help/revisions">
2334 revisions
2334 revisions
2335 </a>
2335 </a>
2336 </td><td>
2336 </td><td>
2337 Specifying Revisions
2337 Specifying Revisions
2338 </td></tr>
2338 </td></tr>
2339 <tr><td>
2339 <tr><td>
2340 <a href="/help/scripting">
2340 <a href="/help/scripting">
2341 scripting
2341 scripting
2342 </a>
2342 </a>
2343 </td><td>
2343 </td><td>
2344 Using Mercurial from scripts and automation
2344 Using Mercurial from scripts and automation
2345 </td></tr>
2345 </td></tr>
2346 <tr><td>
2346 <tr><td>
2347 <a href="/help/subrepos">
2347 <a href="/help/subrepos">
2348 subrepos
2348 subrepos
2349 </a>
2349 </a>
2350 </td><td>
2350 </td><td>
2351 Subrepositories
2351 Subrepositories
2352 </td></tr>
2352 </td></tr>
2353 <tr><td>
2353 <tr><td>
2354 <a href="/help/templating">
2354 <a href="/help/templating">
2355 templating
2355 templating
2356 </a>
2356 </a>
2357 </td><td>
2357 </td><td>
2358 Template Usage
2358 Template Usage
2359 </td></tr>
2359 </td></tr>
2360 <tr><td>
2360 <tr><td>
2361 <a href="/help/urls">
2361 <a href="/help/urls">
2362 urls
2362 urls
2363 </a>
2363 </a>
2364 </td><td>
2364 </td><td>
2365 URL Paths
2365 URL Paths
2366 </td></tr>
2366 </td></tr>
2367 <tr><td>
2367 <tr><td>
2368 <a href="/help/topic-containing-verbose">
2368 <a href="/help/topic-containing-verbose">
2369 topic-containing-verbose
2369 topic-containing-verbose
2370 </a>
2370 </a>
2371 </td><td>
2371 </td><td>
2372 This is the topic to test omit indicating.
2372 This is the topic to test omit indicating.
2373 </td></tr>
2373 </td></tr>
2374
2374
2375
2375
2376 <tr><td colspan="2"><h2><a name="main" href="#main">Main Commands</a></h2></td></tr>
2376 <tr><td colspan="2"><h2><a name="main" href="#main">Main Commands</a></h2></td></tr>
2377
2377
2378 <tr><td>
2378 <tr><td>
2379 <a href="/help/abort">
2379 <a href="/help/abort">
2380 abort
2380 abort
2381 </a>
2381 </a>
2382 </td><td>
2382 </td><td>
2383 abort an unfinished operation (EXPERIMENTAL)
2383 abort an unfinished operation (EXPERIMENTAL)
2384 </td></tr>
2384 </td></tr>
2385 <tr><td>
2385 <tr><td>
2386 <a href="/help/add">
2386 <a href="/help/add">
2387 add
2387 add
2388 </a>
2388 </a>
2389 </td><td>
2389 </td><td>
2390 add the specified files on the next commit
2390 add the specified files on the next commit
2391 </td></tr>
2391 </td></tr>
2392 <tr><td>
2392 <tr><td>
2393 <a href="/help/annotate">
2393 <a href="/help/annotate">
2394 annotate
2394 annotate
2395 </a>
2395 </a>
2396 </td><td>
2396 </td><td>
2397 show changeset information by line for each file
2397 show changeset information by line for each file
2398 </td></tr>
2398 </td></tr>
2399 <tr><td>
2399 <tr><td>
2400 <a href="/help/clone">
2400 <a href="/help/clone">
2401 clone
2401 clone
2402 </a>
2402 </a>
2403 </td><td>
2403 </td><td>
2404 make a copy of an existing repository
2404 make a copy of an existing repository
2405 </td></tr>
2405 </td></tr>
2406 <tr><td>
2406 <tr><td>
2407 <a href="/help/commit">
2407 <a href="/help/commit">
2408 commit
2408 commit
2409 </a>
2409 </a>
2410 </td><td>
2410 </td><td>
2411 commit the specified files or all outstanding changes
2411 commit the specified files or all outstanding changes
2412 </td></tr>
2412 </td></tr>
2413 <tr><td>
2413 <tr><td>
2414 <a href="/help/continue">
2414 <a href="/help/continue">
2415 continue
2415 continue
2416 </a>
2416 </a>
2417 </td><td>
2417 </td><td>
2418 resumes an interrupted operation (EXPERIMENTAL)
2418 resumes an interrupted operation (EXPERIMENTAL)
2419 </td></tr>
2419 </td></tr>
2420 <tr><td>
2420 <tr><td>
2421 <a href="/help/diff">
2421 <a href="/help/diff">
2422 diff
2422 diff
2423 </a>
2423 </a>
2424 </td><td>
2424 </td><td>
2425 diff repository (or selected files)
2425 diff repository (or selected files)
2426 </td></tr>
2426 </td></tr>
2427 <tr><td>
2427 <tr><td>
2428 <a href="/help/export">
2428 <a href="/help/export">
2429 export
2429 export
2430 </a>
2430 </a>
2431 </td><td>
2431 </td><td>
2432 dump the header and diffs for one or more changesets
2432 dump the header and diffs for one or more changesets
2433 </td></tr>
2433 </td></tr>
2434 <tr><td>
2434 <tr><td>
2435 <a href="/help/forget">
2435 <a href="/help/forget">
2436 forget
2436 forget
2437 </a>
2437 </a>
2438 </td><td>
2438 </td><td>
2439 forget the specified files on the next commit
2439 forget the specified files on the next commit
2440 </td></tr>
2440 </td></tr>
2441 <tr><td>
2441 <tr><td>
2442 <a href="/help/init">
2442 <a href="/help/init">
2443 init
2443 init
2444 </a>
2444 </a>
2445 </td><td>
2445 </td><td>
2446 create a new repository in the given directory
2446 create a new repository in the given directory
2447 </td></tr>
2447 </td></tr>
2448 <tr><td>
2448 <tr><td>
2449 <a href="/help/log">
2449 <a href="/help/log">
2450 log
2450 log
2451 </a>
2451 </a>
2452 </td><td>
2452 </td><td>
2453 show revision history of entire repository or files
2453 show revision history of entire repository or files
2454 </td></tr>
2454 </td></tr>
2455 <tr><td>
2455 <tr><td>
2456 <a href="/help/merge">
2456 <a href="/help/merge">
2457 merge
2457 merge
2458 </a>
2458 </a>
2459 </td><td>
2459 </td><td>
2460 merge another revision into working directory
2460 merge another revision into working directory
2461 </td></tr>
2461 </td></tr>
2462 <tr><td>
2462 <tr><td>
2463 <a href="/help/pull">
2463 <a href="/help/pull">
2464 pull
2464 pull
2465 </a>
2465 </a>
2466 </td><td>
2466 </td><td>
2467 pull changes from the specified source
2467 pull changes from the specified source
2468 </td></tr>
2468 </td></tr>
2469 <tr><td>
2469 <tr><td>
2470 <a href="/help/push">
2470 <a href="/help/push">
2471 push
2471 push
2472 </a>
2472 </a>
2473 </td><td>
2473 </td><td>
2474 push changes to the specified destination
2474 push changes to the specified destination
2475 </td></tr>
2475 </td></tr>
2476 <tr><td>
2476 <tr><td>
2477 <a href="/help/remove">
2477 <a href="/help/remove">
2478 remove
2478 remove
2479 </a>
2479 </a>
2480 </td><td>
2480 </td><td>
2481 remove the specified files on the next commit
2481 remove the specified files on the next commit
2482 </td></tr>
2482 </td></tr>
2483 <tr><td>
2483 <tr><td>
2484 <a href="/help/serve">
2484 <a href="/help/serve">
2485 serve
2485 serve
2486 </a>
2486 </a>
2487 </td><td>
2487 </td><td>
2488 start stand-alone webserver
2488 start stand-alone webserver
2489 </td></tr>
2489 </td></tr>
2490 <tr><td>
2490 <tr><td>
2491 <a href="/help/status">
2491 <a href="/help/status">
2492 status
2492 status
2493 </a>
2493 </a>
2494 </td><td>
2494 </td><td>
2495 show changed files in the working directory
2495 show changed files in the working directory
2496 </td></tr>
2496 </td></tr>
2497 <tr><td>
2497 <tr><td>
2498 <a href="/help/summary">
2498 <a href="/help/summary">
2499 summary
2499 summary
2500 </a>
2500 </a>
2501 </td><td>
2501 </td><td>
2502 summarize working directory state
2502 summarize working directory state
2503 </td></tr>
2503 </td></tr>
2504 <tr><td>
2504 <tr><td>
2505 <a href="/help/update">
2505 <a href="/help/update">
2506 update
2506 update
2507 </a>
2507 </a>
2508 </td><td>
2508 </td><td>
2509 update working directory (or switch revisions)
2509 update working directory (or switch revisions)
2510 </td></tr>
2510 </td></tr>
2511
2511
2512
2512
2513
2513
2514 <tr><td colspan="2"><h2><a name="other" href="#other">Other Commands</a></h2></td></tr>
2514 <tr><td colspan="2"><h2><a name="other" href="#other">Other Commands</a></h2></td></tr>
2515
2515
2516 <tr><td>
2516 <tr><td>
2517 <a href="/help/addremove">
2517 <a href="/help/addremove">
2518 addremove
2518 addremove
2519 </a>
2519 </a>
2520 </td><td>
2520 </td><td>
2521 add all new files, delete all missing files
2521 add all new files, delete all missing files
2522 </td></tr>
2522 </td></tr>
2523 <tr><td>
2523 <tr><td>
2524 <a href="/help/archive">
2524 <a href="/help/archive">
2525 archive
2525 archive
2526 </a>
2526 </a>
2527 </td><td>
2527 </td><td>
2528 create an unversioned archive of a repository revision
2528 create an unversioned archive of a repository revision
2529 </td></tr>
2529 </td></tr>
2530 <tr><td>
2530 <tr><td>
2531 <a href="/help/backout">
2531 <a href="/help/backout">
2532 backout
2532 backout
2533 </a>
2533 </a>
2534 </td><td>
2534 </td><td>
2535 reverse effect of earlier changeset
2535 reverse effect of earlier changeset
2536 </td></tr>
2536 </td></tr>
2537 <tr><td>
2537 <tr><td>
2538 <a href="/help/bisect">
2538 <a href="/help/bisect">
2539 bisect
2539 bisect
2540 </a>
2540 </a>
2541 </td><td>
2541 </td><td>
2542 subdivision search of changesets
2542 subdivision search of changesets
2543 </td></tr>
2543 </td></tr>
2544 <tr><td>
2544 <tr><td>
2545 <a href="/help/bookmarks">
2545 <a href="/help/bookmarks">
2546 bookmarks
2546 bookmarks
2547 </a>
2547 </a>
2548 </td><td>
2548 </td><td>
2549 create a new bookmark or list existing bookmarks
2549 create a new bookmark or list existing bookmarks
2550 </td></tr>
2550 </td></tr>
2551 <tr><td>
2551 <tr><td>
2552 <a href="/help/branch">
2552 <a href="/help/branch">
2553 branch
2553 branch
2554 </a>
2554 </a>
2555 </td><td>
2555 </td><td>
2556 set or show the current branch name
2556 set or show the current branch name
2557 </td></tr>
2557 </td></tr>
2558 <tr><td>
2558 <tr><td>
2559 <a href="/help/branches">
2559 <a href="/help/branches">
2560 branches
2560 branches
2561 </a>
2561 </a>
2562 </td><td>
2562 </td><td>
2563 list repository named branches
2563 list repository named branches
2564 </td></tr>
2564 </td></tr>
2565 <tr><td>
2565 <tr><td>
2566 <a href="/help/bundle">
2566 <a href="/help/bundle">
2567 bundle
2567 bundle
2568 </a>
2568 </a>
2569 </td><td>
2569 </td><td>
2570 create a bundle file
2570 create a bundle file
2571 </td></tr>
2571 </td></tr>
2572 <tr><td>
2572 <tr><td>
2573 <a href="/help/cat">
2573 <a href="/help/cat">
2574 cat
2574 cat
2575 </a>
2575 </a>
2576 </td><td>
2576 </td><td>
2577 output the current or given revision of files
2577 output the current or given revision of files
2578 </td></tr>
2578 </td></tr>
2579 <tr><td>
2579 <tr><td>
2580 <a href="/help/config">
2580 <a href="/help/config">
2581 config
2581 config
2582 </a>
2582 </a>
2583 </td><td>
2583 </td><td>
2584 show combined config settings from all hgrc files
2584 show combined config settings from all hgrc files
2585 </td></tr>
2585 </td></tr>
2586 <tr><td>
2586 <tr><td>
2587 <a href="/help/copy">
2587 <a href="/help/copy">
2588 copy
2588 copy
2589 </a>
2589 </a>
2590 </td><td>
2590 </td><td>
2591 mark files as copied for the next commit
2591 mark files as copied for the next commit
2592 </td></tr>
2592 </td></tr>
2593 <tr><td>
2593 <tr><td>
2594 <a href="/help/files">
2594 <a href="/help/files">
2595 files
2595 files
2596 </a>
2596 </a>
2597 </td><td>
2597 </td><td>
2598 list tracked files
2598 list tracked files
2599 </td></tr>
2599 </td></tr>
2600 <tr><td>
2600 <tr><td>
2601 <a href="/help/graft">
2601 <a href="/help/graft">
2602 graft
2602 graft
2603 </a>
2603 </a>
2604 </td><td>
2604 </td><td>
2605 copy changes from other branches onto the current branch
2605 copy changes from other branches onto the current branch
2606 </td></tr>
2606 </td></tr>
2607 <tr><td>
2607 <tr><td>
2608 <a href="/help/grep">
2608 <a href="/help/grep">
2609 grep
2609 grep
2610 </a>
2610 </a>
2611 </td><td>
2611 </td><td>
2612 search for a pattern in specified files
2612 search for a pattern in specified files
2613 </td></tr>
2613 </td></tr>
2614 <tr><td>
2614 <tr><td>
2615 <a href="/help/hashelp">
2615 <a href="/help/hashelp">
2616 hashelp
2616 hashelp
2617 </a>
2617 </a>
2618 </td><td>
2618 </td><td>
2619 Extension command's help
2619 Extension command's help
2620 </td></tr>
2620 </td></tr>
2621 <tr><td>
2621 <tr><td>
2622 <a href="/help/heads">
2622 <a href="/help/heads">
2623 heads
2623 heads
2624 </a>
2624 </a>
2625 </td><td>
2625 </td><td>
2626 show branch heads
2626 show branch heads
2627 </td></tr>
2627 </td></tr>
2628 <tr><td>
2628 <tr><td>
2629 <a href="/help/help">
2629 <a href="/help/help">
2630 help
2630 help
2631 </a>
2631 </a>
2632 </td><td>
2632 </td><td>
2633 show help for a given topic or a help overview
2633 show help for a given topic or a help overview
2634 </td></tr>
2634 </td></tr>
2635 <tr><td>
2635 <tr><td>
2636 <a href="/help/hgalias">
2636 <a href="/help/hgalias">
2637 hgalias
2637 hgalias
2638 </a>
2638 </a>
2639 </td><td>
2639 </td><td>
2640 My doc
2640 My doc
2641 </td></tr>
2641 </td></tr>
2642 <tr><td>
2642 <tr><td>
2643 <a href="/help/hgaliasnodoc">
2643 <a href="/help/hgaliasnodoc">
2644 hgaliasnodoc
2644 hgaliasnodoc
2645 </a>
2645 </a>
2646 </td><td>
2646 </td><td>
2647 summarize working directory state
2647 summarize working directory state
2648 </td></tr>
2648 </td></tr>
2649 <tr><td>
2649 <tr><td>
2650 <a href="/help/identify">
2650 <a href="/help/identify">
2651 identify
2651 identify
2652 </a>
2652 </a>
2653 </td><td>
2653 </td><td>
2654 identify the working directory or specified revision
2654 identify the working directory or specified revision
2655 </td></tr>
2655 </td></tr>
2656 <tr><td>
2656 <tr><td>
2657 <a href="/help/import">
2657 <a href="/help/import">
2658 import
2658 import
2659 </a>
2659 </a>
2660 </td><td>
2660 </td><td>
2661 import an ordered set of patches
2661 import an ordered set of patches
2662 </td></tr>
2662 </td></tr>
2663 <tr><td>
2663 <tr><td>
2664 <a href="/help/incoming">
2664 <a href="/help/incoming">
2665 incoming
2665 incoming
2666 </a>
2666 </a>
2667 </td><td>
2667 </td><td>
2668 show new changesets found in source
2668 show new changesets found in source
2669 </td></tr>
2669 </td></tr>
2670 <tr><td>
2670 <tr><td>
2671 <a href="/help/manifest">
2671 <a href="/help/manifest">
2672 manifest
2672 manifest
2673 </a>
2673 </a>
2674 </td><td>
2674 </td><td>
2675 output the current or given revision of the project manifest
2675 output the current or given revision of the project manifest
2676 </td></tr>
2676 </td></tr>
2677 <tr><td>
2677 <tr><td>
2678 <a href="/help/nohelp">
2678 <a href="/help/nohelp">
2679 nohelp
2679 nohelp
2680 </a>
2680 </a>
2681 </td><td>
2681 </td><td>
2682 (no help text available)
2682 (no help text available)
2683 </td></tr>
2683 </td></tr>
2684 <tr><td>
2684 <tr><td>
2685 <a href="/help/outgoing">
2685 <a href="/help/outgoing">
2686 outgoing
2686 outgoing
2687 </a>
2687 </a>
2688 </td><td>
2688 </td><td>
2689 show changesets not found in the destination
2689 show changesets not found in the destination
2690 </td></tr>
2690 </td></tr>
2691 <tr><td>
2691 <tr><td>
2692 <a href="/help/paths">
2692 <a href="/help/paths">
2693 paths
2693 paths
2694 </a>
2694 </a>
2695 </td><td>
2695 </td><td>
2696 show aliases for remote repositories
2696 show aliases for remote repositories
2697 </td></tr>
2697 </td></tr>
2698 <tr><td>
2698 <tr><td>
2699 <a href="/help/phase">
2699 <a href="/help/phase">
2700 phase
2700 phase
2701 </a>
2701 </a>
2702 </td><td>
2702 </td><td>
2703 set or show the current phase name
2703 set or show the current phase name
2704 </td></tr>
2704 </td></tr>
2705 <tr><td>
2705 <tr><td>
2706 <a href="/help/recover">
2706 <a href="/help/recover">
2707 recover
2707 recover
2708 </a>
2708 </a>
2709 </td><td>
2709 </td><td>
2710 roll back an interrupted transaction
2710 roll back an interrupted transaction
2711 </td></tr>
2711 </td></tr>
2712 <tr><td>
2712 <tr><td>
2713 <a href="/help/rename">
2713 <a href="/help/rename">
2714 rename
2714 rename
2715 </a>
2715 </a>
2716 </td><td>
2716 </td><td>
2717 rename files; equivalent of copy + remove
2717 rename files; equivalent of copy + remove
2718 </td></tr>
2718 </td></tr>
2719 <tr><td>
2719 <tr><td>
2720 <a href="/help/resolve">
2720 <a href="/help/resolve">
2721 resolve
2721 resolve
2722 </a>
2722 </a>
2723 </td><td>
2723 </td><td>
2724 redo merges or set/view the merge status of files
2724 redo merges or set/view the merge status of files
2725 </td></tr>
2725 </td></tr>
2726 <tr><td>
2726 <tr><td>
2727 <a href="/help/revert">
2727 <a href="/help/revert">
2728 revert
2728 revert
2729 </a>
2729 </a>
2730 </td><td>
2730 </td><td>
2731 restore files to their checkout state
2731 restore files to their checkout state
2732 </td></tr>
2732 </td></tr>
2733 <tr><td>
2733 <tr><td>
2734 <a href="/help/root">
2734 <a href="/help/root">
2735 root
2735 root
2736 </a>
2736 </a>
2737 </td><td>
2737 </td><td>
2738 print the root (top) of the current working directory
2738 print the root (top) of the current working directory
2739 </td></tr>
2739 </td></tr>
2740 <tr><td>
2740 <tr><td>
2741 <a href="/help/shellalias">
2741 <a href="/help/shellalias">
2742 shellalias
2742 shellalias
2743 </a>
2743 </a>
2744 </td><td>
2744 </td><td>
2745 (no help text available)
2745 (no help text available)
2746 </td></tr>
2746 </td></tr>
2747 <tr><td>
2747 <tr><td>
2748 <a href="/help/shelve">
2748 <a href="/help/shelve">
2749 shelve
2749 shelve
2750 </a>
2750 </a>
2751 </td><td>
2751 </td><td>
2752 save and set aside changes from the working directory
2752 save and set aside changes from the working directory
2753 </td></tr>
2753 </td></tr>
2754 <tr><td>
2754 <tr><td>
2755 <a href="/help/tag">
2755 <a href="/help/tag">
2756 tag
2756 tag
2757 </a>
2757 </a>
2758 </td><td>
2758 </td><td>
2759 add one or more tags for the current or given revision
2759 add one or more tags for the current or given revision
2760 </td></tr>
2760 </td></tr>
2761 <tr><td>
2761 <tr><td>
2762 <a href="/help/tags">
2762 <a href="/help/tags">
2763 tags
2763 tags
2764 </a>
2764 </a>
2765 </td><td>
2765 </td><td>
2766 list repository tags
2766 list repository tags
2767 </td></tr>
2767 </td></tr>
2768 <tr><td>
2768 <tr><td>
2769 <a href="/help/unbundle">
2769 <a href="/help/unbundle">
2770 unbundle
2770 unbundle
2771 </a>
2771 </a>
2772 </td><td>
2772 </td><td>
2773 apply one or more bundle files
2773 apply one or more bundle files
2774 </td></tr>
2774 </td></tr>
2775 <tr><td>
2775 <tr><td>
2776 <a href="/help/unshelve">
2776 <a href="/help/unshelve">
2777 unshelve
2777 unshelve
2778 </a>
2778 </a>
2779 </td><td>
2779 </td><td>
2780 restore a shelved change to the working directory
2780 restore a shelved change to the working directory
2781 </td></tr>
2781 </td></tr>
2782 <tr><td>
2782 <tr><td>
2783 <a href="/help/verify">
2783 <a href="/help/verify">
2784 verify
2784 verify
2785 </a>
2785 </a>
2786 </td><td>
2786 </td><td>
2787 verify the integrity of the repository
2787 verify the integrity of the repository
2788 </td></tr>
2788 </td></tr>
2789 <tr><td>
2789 <tr><td>
2790 <a href="/help/version">
2790 <a href="/help/version">
2791 version
2791 version
2792 </a>
2792 </a>
2793 </td><td>
2793 </td><td>
2794 output version and copyright information
2794 output version and copyright information
2795 </td></tr>
2795 </td></tr>
2796
2796
2797
2797
2798 </table>
2798 </table>
2799 </div>
2799 </div>
2800 </div>
2800 </div>
2801
2801
2802
2802
2803
2803
2804 </body>
2804 </body>
2805 </html>
2805 </html>
2806
2806
2807
2807
2808 $ get-with-headers.py $LOCALIP:$HGPORT "help/add"
2808 $ get-with-headers.py $LOCALIP:$HGPORT "help/add"
2809 200 Script output follows
2809 200 Script output follows
2810
2810
2811 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2811 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2812 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2812 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2813 <head>
2813 <head>
2814 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2814 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2815 <meta name="robots" content="index, nofollow" />
2815 <meta name="robots" content="index, nofollow" />
2816 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2816 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2817 <script type="text/javascript" src="/static/mercurial.js"></script>
2817 <script type="text/javascript" src="/static/mercurial.js"></script>
2818
2818
2819 <title>Help: add</title>
2819 <title>Help: add</title>
2820 </head>
2820 </head>
2821 <body>
2821 <body>
2822
2822
2823 <div class="container">
2823 <div class="container">
2824 <div class="menu">
2824 <div class="menu">
2825 <div class="logo">
2825 <div class="logo">
2826 <a href="https://mercurial-scm.org/">
2826 <a href="https://mercurial-scm.org/">
2827 <img src="/static/hglogo.png" alt="mercurial" /></a>
2827 <img src="/static/hglogo.png" alt="mercurial" /></a>
2828 </div>
2828 </div>
2829 <ul>
2829 <ul>
2830 <li><a href="/shortlog">log</a></li>
2830 <li><a href="/shortlog">log</a></li>
2831 <li><a href="/graph">graph</a></li>
2831 <li><a href="/graph">graph</a></li>
2832 <li><a href="/tags">tags</a></li>
2832 <li><a href="/tags">tags</a></li>
2833 <li><a href="/bookmarks">bookmarks</a></li>
2833 <li><a href="/bookmarks">bookmarks</a></li>
2834 <li><a href="/branches">branches</a></li>
2834 <li><a href="/branches">branches</a></li>
2835 </ul>
2835 </ul>
2836 <ul>
2836 <ul>
2837 <li class="active"><a href="/help">help</a></li>
2837 <li class="active"><a href="/help">help</a></li>
2838 </ul>
2838 </ul>
2839 </div>
2839 </div>
2840
2840
2841 <div class="main">
2841 <div class="main">
2842 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2842 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2843 <h3>Help: add</h3>
2843 <h3>Help: add</h3>
2844
2844
2845 <form class="search" action="/log">
2845 <form class="search" action="/log">
2846
2846
2847 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
2847 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
2848 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2848 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2849 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2849 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2850 </form>
2850 </form>
2851 <div id="doc">
2851 <div id="doc">
2852 <p>
2852 <p>
2853 hg add [OPTION]... [FILE]...
2853 hg add [OPTION]... [FILE]...
2854 </p>
2854 </p>
2855 <p>
2855 <p>
2856 add the specified files on the next commit
2856 add the specified files on the next commit
2857 </p>
2857 </p>
2858 <p>
2858 <p>
2859 Schedule files to be version controlled and added to the
2859 Schedule files to be version controlled and added to the
2860 repository.
2860 repository.
2861 </p>
2861 </p>
2862 <p>
2862 <p>
2863 The files will be added to the repository at the next commit. To
2863 The files will be added to the repository at the next commit. To
2864 undo an add before that, see 'hg forget'.
2864 undo an add before that, see 'hg forget'.
2865 </p>
2865 </p>
2866 <p>
2866 <p>
2867 If no names are given, add all files to the repository (except
2867 If no names are given, add all files to the repository (except
2868 files matching &quot;.hgignore&quot;).
2868 files matching &quot;.hgignore&quot;).
2869 </p>
2869 </p>
2870 <p>
2870 <p>
2871 Examples:
2871 Examples:
2872 </p>
2872 </p>
2873 <ul>
2873 <ul>
2874 <li> New (unknown) files are added automatically by 'hg add':
2874 <li> New (unknown) files are added automatically by 'hg add':
2875 <pre>
2875 <pre>
2876 \$ ls (re)
2876 \$ ls (re)
2877 foo.c
2877 foo.c
2878 \$ hg status (re)
2878 \$ hg status (re)
2879 ? foo.c
2879 ? foo.c
2880 \$ hg add (re)
2880 \$ hg add (re)
2881 adding foo.c
2881 adding foo.c
2882 \$ hg status (re)
2882 \$ hg status (re)
2883 A foo.c
2883 A foo.c
2884 </pre>
2884 </pre>
2885 <li> Specific files to be added can be specified:
2885 <li> Specific files to be added can be specified:
2886 <pre>
2886 <pre>
2887 \$ ls (re)
2887 \$ ls (re)
2888 bar.c foo.c
2888 bar.c foo.c
2889 \$ hg status (re)
2889 \$ hg status (re)
2890 ? bar.c
2890 ? bar.c
2891 ? foo.c
2891 ? foo.c
2892 \$ hg add bar.c (re)
2892 \$ hg add bar.c (re)
2893 \$ hg status (re)
2893 \$ hg status (re)
2894 A bar.c
2894 A bar.c
2895 ? foo.c
2895 ? foo.c
2896 </pre>
2896 </pre>
2897 </ul>
2897 </ul>
2898 <p>
2898 <p>
2899 Returns 0 if all files are successfully added.
2899 Returns 0 if all files are successfully added.
2900 </p>
2900 </p>
2901 <p>
2901 <p>
2902 options ([+] can be repeated):
2902 options ([+] can be repeated):
2903 </p>
2903 </p>
2904 <table>
2904 <table>
2905 <tr><td>-I</td>
2905 <tr><td>-I</td>
2906 <td>--include PATTERN [+]</td>
2906 <td>--include PATTERN [+]</td>
2907 <td>include names matching the given patterns</td></tr>
2907 <td>include names matching the given patterns</td></tr>
2908 <tr><td>-X</td>
2908 <tr><td>-X</td>
2909 <td>--exclude PATTERN [+]</td>
2909 <td>--exclude PATTERN [+]</td>
2910 <td>exclude names matching the given patterns</td></tr>
2910 <td>exclude names matching the given patterns</td></tr>
2911 <tr><td>-S</td>
2911 <tr><td>-S</td>
2912 <td>--subrepos</td>
2912 <td>--subrepos</td>
2913 <td>recurse into subrepositories</td></tr>
2913 <td>recurse into subrepositories</td></tr>
2914 <tr><td>-n</td>
2914 <tr><td>-n</td>
2915 <td>--dry-run</td>
2915 <td>--dry-run</td>
2916 <td>do not perform actions, just print output</td></tr>
2916 <td>do not perform actions, just print output</td></tr>
2917 </table>
2917 </table>
2918 <p>
2918 <p>
2919 global options ([+] can be repeated):
2919 global options ([+] can be repeated):
2920 </p>
2920 </p>
2921 <table>
2921 <table>
2922 <tr><td>-R</td>
2922 <tr><td>-R</td>
2923 <td>--repository REPO</td>
2923 <td>--repository REPO</td>
2924 <td>repository root directory or name of overlay bundle file</td></tr>
2924 <td>repository root directory or name of overlay bundle file</td></tr>
2925 <tr><td></td>
2925 <tr><td></td>
2926 <td>--cwd DIR</td>
2926 <td>--cwd DIR</td>
2927 <td>change working directory</td></tr>
2927 <td>change working directory</td></tr>
2928 <tr><td>-y</td>
2928 <tr><td>-y</td>
2929 <td>--noninteractive</td>
2929 <td>--noninteractive</td>
2930 <td>do not prompt, automatically pick the first choice for all prompts</td></tr>
2930 <td>do not prompt, automatically pick the first choice for all prompts</td></tr>
2931 <tr><td>-q</td>
2931 <tr><td>-q</td>
2932 <td>--quiet</td>
2932 <td>--quiet</td>
2933 <td>suppress output</td></tr>
2933 <td>suppress output</td></tr>
2934 <tr><td>-v</td>
2934 <tr><td>-v</td>
2935 <td>--verbose</td>
2935 <td>--verbose</td>
2936 <td>enable additional output</td></tr>
2936 <td>enable additional output</td></tr>
2937 <tr><td></td>
2937 <tr><td></td>
2938 <td>--color TYPE</td>
2938 <td>--color TYPE</td>
2939 <td>when to colorize (boolean, always, auto, never, or debug)</td></tr>
2939 <td>when to colorize (boolean, always, auto, never, or debug)</td></tr>
2940 <tr><td></td>
2940 <tr><td></td>
2941 <td>--config CONFIG [+]</td>
2941 <td>--config CONFIG [+]</td>
2942 <td>set/override config option (use 'section.name=value')</td></tr>
2942 <td>set/override config option (use 'section.name=value')</td></tr>
2943 <tr><td></td>
2943 <tr><td></td>
2944 <td>--debug</td>
2944 <td>--debug</td>
2945 <td>enable debugging output</td></tr>
2945 <td>enable debugging output</td></tr>
2946 <tr><td></td>
2946 <tr><td></td>
2947 <td>--debugger</td>
2947 <td>--debugger</td>
2948 <td>start debugger</td></tr>
2948 <td>start debugger</td></tr>
2949 <tr><td></td>
2949 <tr><td></td>
2950 <td>--encoding ENCODE</td>
2950 <td>--encoding ENCODE</td>
2951 <td>set the charset encoding (default: ascii)</td></tr>
2951 <td>set the charset encoding (default: ascii)</td></tr>
2952 <tr><td></td>
2952 <tr><td></td>
2953 <td>--encodingmode MODE</td>
2953 <td>--encodingmode MODE</td>
2954 <td>set the charset encoding mode (default: strict)</td></tr>
2954 <td>set the charset encoding mode (default: strict)</td></tr>
2955 <tr><td></td>
2955 <tr><td></td>
2956 <td>--traceback</td>
2956 <td>--traceback</td>
2957 <td>always print a traceback on exception</td></tr>
2957 <td>always print a traceback on exception</td></tr>
2958 <tr><td></td>
2958 <tr><td></td>
2959 <td>--time</td>
2959 <td>--time</td>
2960 <td>time how long the command takes</td></tr>
2960 <td>time how long the command takes</td></tr>
2961 <tr><td></td>
2961 <tr><td></td>
2962 <td>--profile</td>
2962 <td>--profile</td>
2963 <td>print command execution profile</td></tr>
2963 <td>print command execution profile</td></tr>
2964 <tr><td></td>
2964 <tr><td></td>
2965 <td>--version</td>
2965 <td>--version</td>
2966 <td>output version information and exit</td></tr>
2966 <td>output version information and exit</td></tr>
2967 <tr><td>-h</td>
2967 <tr><td>-h</td>
2968 <td>--help</td>
2968 <td>--help</td>
2969 <td>display help and exit</td></tr>
2969 <td>display help and exit</td></tr>
2970 <tr><td></td>
2970 <tr><td></td>
2971 <td>--hidden</td>
2971 <td>--hidden</td>
2972 <td>consider hidden changesets</td></tr>
2972 <td>consider hidden changesets</td></tr>
2973 <tr><td></td>
2973 <tr><td></td>
2974 <td>--pager TYPE</td>
2974 <td>--pager TYPE</td>
2975 <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
2975 <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
2976 </table>
2976 </table>
2977
2977
2978 </div>
2978 </div>
2979 </div>
2979 </div>
2980 </div>
2980 </div>
2981
2981
2982
2982
2983
2983
2984 </body>
2984 </body>
2985 </html>
2985 </html>
2986
2986
2987
2987
2988 $ get-with-headers.py $LOCALIP:$HGPORT "help/remove"
2988 $ get-with-headers.py $LOCALIP:$HGPORT "help/remove"
2989 200 Script output follows
2989 200 Script output follows
2990
2990
2991 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2991 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2992 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2992 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2993 <head>
2993 <head>
2994 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2994 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2995 <meta name="robots" content="index, nofollow" />
2995 <meta name="robots" content="index, nofollow" />
2996 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2996 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2997 <script type="text/javascript" src="/static/mercurial.js"></script>
2997 <script type="text/javascript" src="/static/mercurial.js"></script>
2998
2998
2999 <title>Help: remove</title>
2999 <title>Help: remove</title>
3000 </head>
3000 </head>
3001 <body>
3001 <body>
3002
3002
3003 <div class="container">
3003 <div class="container">
3004 <div class="menu">
3004 <div class="menu">
3005 <div class="logo">
3005 <div class="logo">
3006 <a href="https://mercurial-scm.org/">
3006 <a href="https://mercurial-scm.org/">
3007 <img src="/static/hglogo.png" alt="mercurial" /></a>
3007 <img src="/static/hglogo.png" alt="mercurial" /></a>
3008 </div>
3008 </div>
3009 <ul>
3009 <ul>
3010 <li><a href="/shortlog">log</a></li>
3010 <li><a href="/shortlog">log</a></li>
3011 <li><a href="/graph">graph</a></li>
3011 <li><a href="/graph">graph</a></li>
3012 <li><a href="/tags">tags</a></li>
3012 <li><a href="/tags">tags</a></li>
3013 <li><a href="/bookmarks">bookmarks</a></li>
3013 <li><a href="/bookmarks">bookmarks</a></li>
3014 <li><a href="/branches">branches</a></li>
3014 <li><a href="/branches">branches</a></li>
3015 </ul>
3015 </ul>
3016 <ul>
3016 <ul>
3017 <li class="active"><a href="/help">help</a></li>
3017 <li class="active"><a href="/help">help</a></li>
3018 </ul>
3018 </ul>
3019 </div>
3019 </div>
3020
3020
3021 <div class="main">
3021 <div class="main">
3022 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3022 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3023 <h3>Help: remove</h3>
3023 <h3>Help: remove</h3>
3024
3024
3025 <form class="search" action="/log">
3025 <form class="search" action="/log">
3026
3026
3027 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3027 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3028 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3028 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3029 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3029 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3030 </form>
3030 </form>
3031 <div id="doc">
3031 <div id="doc">
3032 <p>
3032 <p>
3033 hg remove [OPTION]... FILE...
3033 hg remove [OPTION]... FILE...
3034 </p>
3034 </p>
3035 <p>
3035 <p>
3036 aliases: rm
3036 aliases: rm
3037 </p>
3037 </p>
3038 <p>
3038 <p>
3039 remove the specified files on the next commit
3039 remove the specified files on the next commit
3040 </p>
3040 </p>
3041 <p>
3041 <p>
3042 Schedule the indicated files for removal from the current branch.
3042 Schedule the indicated files for removal from the current branch.
3043 </p>
3043 </p>
3044 <p>
3044 <p>
3045 This command schedules the files to be removed at the next commit.
3045 This command schedules the files to be removed at the next commit.
3046 To undo a remove before that, see 'hg revert'. To undo added
3046 To undo a remove before that, see 'hg revert'. To undo added
3047 files, see 'hg forget'.
3047 files, see 'hg forget'.
3048 </p>
3048 </p>
3049 <p>
3049 <p>
3050 -A/--after can be used to remove only files that have already
3050 -A/--after can be used to remove only files that have already
3051 been deleted, -f/--force can be used to force deletion, and -Af
3051 been deleted, -f/--force can be used to force deletion, and -Af
3052 can be used to remove files from the next revision without
3052 can be used to remove files from the next revision without
3053 deleting them from the working directory.
3053 deleting them from the working directory.
3054 </p>
3054 </p>
3055 <p>
3055 <p>
3056 The following table details the behavior of remove for different
3056 The following table details the behavior of remove for different
3057 file states (columns) and option combinations (rows). The file
3057 file states (columns) and option combinations (rows). The file
3058 states are Added [A], Clean [C], Modified [M] and Missing [!]
3058 states are Added [A], Clean [C], Modified [M] and Missing [!]
3059 (as reported by 'hg status'). The actions are Warn, Remove
3059 (as reported by 'hg status'). The actions are Warn, Remove
3060 (from branch) and Delete (from disk):
3060 (from branch) and Delete (from disk):
3061 </p>
3061 </p>
3062 <table>
3062 <table>
3063 <tr><td>opt/state</td>
3063 <tr><td>opt/state</td>
3064 <td>A</td>
3064 <td>A</td>
3065 <td>C</td>
3065 <td>C</td>
3066 <td>M</td>
3066 <td>M</td>
3067 <td>!</td></tr>
3067 <td>!</td></tr>
3068 <tr><td>none</td>
3068 <tr><td>none</td>
3069 <td>W</td>
3069 <td>W</td>
3070 <td>RD</td>
3070 <td>RD</td>
3071 <td>W</td>
3071 <td>W</td>
3072 <td>R</td></tr>
3072 <td>R</td></tr>
3073 <tr><td>-f</td>
3073 <tr><td>-f</td>
3074 <td>R</td>
3074 <td>R</td>
3075 <td>RD</td>
3075 <td>RD</td>
3076 <td>RD</td>
3076 <td>RD</td>
3077 <td>R</td></tr>
3077 <td>R</td></tr>
3078 <tr><td>-A</td>
3078 <tr><td>-A</td>
3079 <td>W</td>
3079 <td>W</td>
3080 <td>W</td>
3080 <td>W</td>
3081 <td>W</td>
3081 <td>W</td>
3082 <td>R</td></tr>
3082 <td>R</td></tr>
3083 <tr><td>-Af</td>
3083 <tr><td>-Af</td>
3084 <td>R</td>
3084 <td>R</td>
3085 <td>R</td>
3085 <td>R</td>
3086 <td>R</td>
3086 <td>R</td>
3087 <td>R</td></tr>
3087 <td>R</td></tr>
3088 </table>
3088 </table>
3089 <p>
3089 <p>
3090 <b>Note:</b>
3090 <b>Note:</b>
3091 </p>
3091 </p>
3092 <p>
3092 <p>
3093 'hg remove' never deletes files in Added [A] state from the
3093 'hg remove' never deletes files in Added [A] state from the
3094 working directory, not even if &quot;--force&quot; is specified.
3094 working directory, not even if &quot;--force&quot; is specified.
3095 </p>
3095 </p>
3096 <p>
3096 <p>
3097 Returns 0 on success, 1 if any warnings encountered.
3097 Returns 0 on success, 1 if any warnings encountered.
3098 </p>
3098 </p>
3099 <p>
3099 <p>
3100 options ([+] can be repeated):
3100 options ([+] can be repeated):
3101 </p>
3101 </p>
3102 <table>
3102 <table>
3103 <tr><td>-A</td>
3103 <tr><td>-A</td>
3104 <td>--after</td>
3104 <td>--after</td>
3105 <td>record delete for missing files</td></tr>
3105 <td>record delete for missing files</td></tr>
3106 <tr><td>-f</td>
3106 <tr><td>-f</td>
3107 <td>--force</td>
3107 <td>--force</td>
3108 <td>forget added files, delete modified files</td></tr>
3108 <td>forget added files, delete modified files</td></tr>
3109 <tr><td>-S</td>
3109 <tr><td>-S</td>
3110 <td>--subrepos</td>
3110 <td>--subrepos</td>
3111 <td>recurse into subrepositories</td></tr>
3111 <td>recurse into subrepositories</td></tr>
3112 <tr><td>-I</td>
3112 <tr><td>-I</td>
3113 <td>--include PATTERN [+]</td>
3113 <td>--include PATTERN [+]</td>
3114 <td>include names matching the given patterns</td></tr>
3114 <td>include names matching the given patterns</td></tr>
3115 <tr><td>-X</td>
3115 <tr><td>-X</td>
3116 <td>--exclude PATTERN [+]</td>
3116 <td>--exclude PATTERN [+]</td>
3117 <td>exclude names matching the given patterns</td></tr>
3117 <td>exclude names matching the given patterns</td></tr>
3118 <tr><td>-n</td>
3118 <tr><td>-n</td>
3119 <td>--dry-run</td>
3119 <td>--dry-run</td>
3120 <td>do not perform actions, just print output</td></tr>
3120 <td>do not perform actions, just print output</td></tr>
3121 </table>
3121 </table>
3122 <p>
3122 <p>
3123 global options ([+] can be repeated):
3123 global options ([+] can be repeated):
3124 </p>
3124 </p>
3125 <table>
3125 <table>
3126 <tr><td>-R</td>
3126 <tr><td>-R</td>
3127 <td>--repository REPO</td>
3127 <td>--repository REPO</td>
3128 <td>repository root directory or name of overlay bundle file</td></tr>
3128 <td>repository root directory or name of overlay bundle file</td></tr>
3129 <tr><td></td>
3129 <tr><td></td>
3130 <td>--cwd DIR</td>
3130 <td>--cwd DIR</td>
3131 <td>change working directory</td></tr>
3131 <td>change working directory</td></tr>
3132 <tr><td>-y</td>
3132 <tr><td>-y</td>
3133 <td>--noninteractive</td>
3133 <td>--noninteractive</td>
3134 <td>do not prompt, automatically pick the first choice for all prompts</td></tr>
3134 <td>do not prompt, automatically pick the first choice for all prompts</td></tr>
3135 <tr><td>-q</td>
3135 <tr><td>-q</td>
3136 <td>--quiet</td>
3136 <td>--quiet</td>
3137 <td>suppress output</td></tr>
3137 <td>suppress output</td></tr>
3138 <tr><td>-v</td>
3138 <tr><td>-v</td>
3139 <td>--verbose</td>
3139 <td>--verbose</td>
3140 <td>enable additional output</td></tr>
3140 <td>enable additional output</td></tr>
3141 <tr><td></td>
3141 <tr><td></td>
3142 <td>--color TYPE</td>
3142 <td>--color TYPE</td>
3143 <td>when to colorize (boolean, always, auto, never, or debug)</td></tr>
3143 <td>when to colorize (boolean, always, auto, never, or debug)</td></tr>
3144 <tr><td></td>
3144 <tr><td></td>
3145 <td>--config CONFIG [+]</td>
3145 <td>--config CONFIG [+]</td>
3146 <td>set/override config option (use 'section.name=value')</td></tr>
3146 <td>set/override config option (use 'section.name=value')</td></tr>
3147 <tr><td></td>
3147 <tr><td></td>
3148 <td>--debug</td>
3148 <td>--debug</td>
3149 <td>enable debugging output</td></tr>
3149 <td>enable debugging output</td></tr>
3150 <tr><td></td>
3150 <tr><td></td>
3151 <td>--debugger</td>
3151 <td>--debugger</td>
3152 <td>start debugger</td></tr>
3152 <td>start debugger</td></tr>
3153 <tr><td></td>
3153 <tr><td></td>
3154 <td>--encoding ENCODE</td>
3154 <td>--encoding ENCODE</td>
3155 <td>set the charset encoding (default: ascii)</td></tr>
3155 <td>set the charset encoding (default: ascii)</td></tr>
3156 <tr><td></td>
3156 <tr><td></td>
3157 <td>--encodingmode MODE</td>
3157 <td>--encodingmode MODE</td>
3158 <td>set the charset encoding mode (default: strict)</td></tr>
3158 <td>set the charset encoding mode (default: strict)</td></tr>
3159 <tr><td></td>
3159 <tr><td></td>
3160 <td>--traceback</td>
3160 <td>--traceback</td>
3161 <td>always print a traceback on exception</td></tr>
3161 <td>always print a traceback on exception</td></tr>
3162 <tr><td></td>
3162 <tr><td></td>
3163 <td>--time</td>
3163 <td>--time</td>
3164 <td>time how long the command takes</td></tr>
3164 <td>time how long the command takes</td></tr>
3165 <tr><td></td>
3165 <tr><td></td>
3166 <td>--profile</td>
3166 <td>--profile</td>
3167 <td>print command execution profile</td></tr>
3167 <td>print command execution profile</td></tr>
3168 <tr><td></td>
3168 <tr><td></td>
3169 <td>--version</td>
3169 <td>--version</td>
3170 <td>output version information and exit</td></tr>
3170 <td>output version information and exit</td></tr>
3171 <tr><td>-h</td>
3171 <tr><td>-h</td>
3172 <td>--help</td>
3172 <td>--help</td>
3173 <td>display help and exit</td></tr>
3173 <td>display help and exit</td></tr>
3174 <tr><td></td>
3174 <tr><td></td>
3175 <td>--hidden</td>
3175 <td>--hidden</td>
3176 <td>consider hidden changesets</td></tr>
3176 <td>consider hidden changesets</td></tr>
3177 <tr><td></td>
3177 <tr><td></td>
3178 <td>--pager TYPE</td>
3178 <td>--pager TYPE</td>
3179 <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
3179 <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
3180 </table>
3180 </table>
3181
3181
3182 </div>
3182 </div>
3183 </div>
3183 </div>
3184 </div>
3184 </div>
3185
3185
3186
3186
3187
3187
3188 </body>
3188 </body>
3189 </html>
3189 </html>
3190
3190
3191
3191
3192 $ get-with-headers.py $LOCALIP:$HGPORT "help/dates"
3192 $ get-with-headers.py $LOCALIP:$HGPORT "help/dates"
3193 200 Script output follows
3193 200 Script output follows
3194
3194
3195 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3195 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3196 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
3196 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
3197 <head>
3197 <head>
3198 <link rel="icon" href="/static/hgicon.png" type="image/png" />
3198 <link rel="icon" href="/static/hgicon.png" type="image/png" />
3199 <meta name="robots" content="index, nofollow" />
3199 <meta name="robots" content="index, nofollow" />
3200 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
3200 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
3201 <script type="text/javascript" src="/static/mercurial.js"></script>
3201 <script type="text/javascript" src="/static/mercurial.js"></script>
3202
3202
3203 <title>Help: dates</title>
3203 <title>Help: dates</title>
3204 </head>
3204 </head>
3205 <body>
3205 <body>
3206
3206
3207 <div class="container">
3207 <div class="container">
3208 <div class="menu">
3208 <div class="menu">
3209 <div class="logo">
3209 <div class="logo">
3210 <a href="https://mercurial-scm.org/">
3210 <a href="https://mercurial-scm.org/">
3211 <img src="/static/hglogo.png" alt="mercurial" /></a>
3211 <img src="/static/hglogo.png" alt="mercurial" /></a>
3212 </div>
3212 </div>
3213 <ul>
3213 <ul>
3214 <li><a href="/shortlog">log</a></li>
3214 <li><a href="/shortlog">log</a></li>
3215 <li><a href="/graph">graph</a></li>
3215 <li><a href="/graph">graph</a></li>
3216 <li><a href="/tags">tags</a></li>
3216 <li><a href="/tags">tags</a></li>
3217 <li><a href="/bookmarks">bookmarks</a></li>
3217 <li><a href="/bookmarks">bookmarks</a></li>
3218 <li><a href="/branches">branches</a></li>
3218 <li><a href="/branches">branches</a></li>
3219 </ul>
3219 </ul>
3220 <ul>
3220 <ul>
3221 <li class="active"><a href="/help">help</a></li>
3221 <li class="active"><a href="/help">help</a></li>
3222 </ul>
3222 </ul>
3223 </div>
3223 </div>
3224
3224
3225 <div class="main">
3225 <div class="main">
3226 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3226 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3227 <h3>Help: dates</h3>
3227 <h3>Help: dates</h3>
3228
3228
3229 <form class="search" action="/log">
3229 <form class="search" action="/log">
3230
3230
3231 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3231 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3232 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3232 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3233 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3233 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3234 </form>
3234 </form>
3235 <div id="doc">
3235 <div id="doc">
3236 <h1>Date Formats</h1>
3236 <h1>Date Formats</h1>
3237 <p>
3237 <p>
3238 Some commands allow the user to specify a date, e.g.:
3238 Some commands allow the user to specify a date, e.g.:
3239 </p>
3239 </p>
3240 <ul>
3240 <ul>
3241 <li> backout, commit, import, tag: Specify the commit date.
3241 <li> backout, commit, import, tag: Specify the commit date.
3242 <li> log, revert, update: Select revision(s) by date.
3242 <li> log, revert, update: Select revision(s) by date.
3243 </ul>
3243 </ul>
3244 <p>
3244 <p>
3245 Many date formats are valid. Here are some examples:
3245 Many date formats are valid. Here are some examples:
3246 </p>
3246 </p>
3247 <ul>
3247 <ul>
3248 <li> &quot;Wed Dec 6 13:18:29 2006&quot; (local timezone assumed)
3248 <li> &quot;Wed Dec 6 13:18:29 2006&quot; (local timezone assumed)
3249 <li> &quot;Dec 6 13:18 -0600&quot; (year assumed, time offset provided)
3249 <li> &quot;Dec 6 13:18 -0600&quot; (year assumed, time offset provided)
3250 <li> &quot;Dec 6 13:18 UTC&quot; (UTC and GMT are aliases for +0000)
3250 <li> &quot;Dec 6 13:18 UTC&quot; (UTC and GMT are aliases for +0000)
3251 <li> &quot;Dec 6&quot; (midnight)
3251 <li> &quot;Dec 6&quot; (midnight)
3252 <li> &quot;13:18&quot; (today assumed)
3252 <li> &quot;13:18&quot; (today assumed)
3253 <li> &quot;3:39&quot; (3:39AM assumed)
3253 <li> &quot;3:39&quot; (3:39AM assumed)
3254 <li> &quot;3:39pm&quot; (15:39)
3254 <li> &quot;3:39pm&quot; (15:39)
3255 <li> &quot;2006-12-06 13:18:29&quot; (ISO 8601 format)
3255 <li> &quot;2006-12-06 13:18:29&quot; (ISO 8601 format)
3256 <li> &quot;2006-12-6 13:18&quot;
3256 <li> &quot;2006-12-6 13:18&quot;
3257 <li> &quot;2006-12-6&quot;
3257 <li> &quot;2006-12-6&quot;
3258 <li> &quot;12-6&quot;
3258 <li> &quot;12-6&quot;
3259 <li> &quot;12/6&quot;
3259 <li> &quot;12/6&quot;
3260 <li> &quot;12/6/6&quot; (Dec 6 2006)
3260 <li> &quot;12/6/6&quot; (Dec 6 2006)
3261 <li> &quot;today&quot; (midnight)
3261 <li> &quot;today&quot; (midnight)
3262 <li> &quot;yesterday&quot; (midnight)
3262 <li> &quot;yesterday&quot; (midnight)
3263 <li> &quot;now&quot; - right now
3263 <li> &quot;now&quot; - right now
3264 </ul>
3264 </ul>
3265 <p>
3265 <p>
3266 Lastly, there is Mercurial's internal format:
3266 Lastly, there is Mercurial's internal format:
3267 </p>
3267 </p>
3268 <ul>
3268 <ul>
3269 <li> &quot;1165411109 0&quot; (Wed Dec 6 13:18:29 2006 UTC)
3269 <li> &quot;1165411109 0&quot; (Wed Dec 6 13:18:29 2006 UTC)
3270 </ul>
3270 </ul>
3271 <p>
3271 <p>
3272 This is the internal representation format for dates. The first number
3272 This is the internal representation format for dates. The first number
3273 is the number of seconds since the epoch (1970-01-01 00:00 UTC). The
3273 is the number of seconds since the epoch (1970-01-01 00:00 UTC). The
3274 second is the offset of the local timezone, in seconds west of UTC
3274 second is the offset of the local timezone, in seconds west of UTC
3275 (negative if the timezone is east of UTC).
3275 (negative if the timezone is east of UTC).
3276 </p>
3276 </p>
3277 <p>
3277 <p>
3278 The log command also accepts date ranges:
3278 The log command also accepts date ranges:
3279 </p>
3279 </p>
3280 <ul>
3280 <ul>
3281 <li> &quot;&lt;DATE&quot; - at or before a given date/time
3281 <li> &quot;&lt;DATE&quot; - at or before a given date/time
3282 <li> &quot;&gt;DATE&quot; - on or after a given date/time
3282 <li> &quot;&gt;DATE&quot; - on or after a given date/time
3283 <li> &quot;DATE to DATE&quot; - a date range, inclusive
3283 <li> &quot;DATE to DATE&quot; - a date range, inclusive
3284 <li> &quot;-DAYS&quot; - within a given number of days of today
3284 <li> &quot;-DAYS&quot; - within a given number of days of today
3285 </ul>
3285 </ul>
3286
3286
3287 </div>
3287 </div>
3288 </div>
3288 </div>
3289 </div>
3289 </div>
3290
3290
3291
3291
3292
3292
3293 </body>
3293 </body>
3294 </html>
3294 </html>
3295
3295
3296
3296
3297 $ get-with-headers.py $LOCALIP:$HGPORT "help/pager"
3297 $ get-with-headers.py $LOCALIP:$HGPORT "help/pager"
3298 200 Script output follows
3298 200 Script output follows
3299
3299
3300 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3300 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3301 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
3301 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
3302 <head>
3302 <head>
3303 <link rel="icon" href="/static/hgicon.png" type="image/png" />
3303 <link rel="icon" href="/static/hgicon.png" type="image/png" />
3304 <meta name="robots" content="index, nofollow" />
3304 <meta name="robots" content="index, nofollow" />
3305 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
3305 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
3306 <script type="text/javascript" src="/static/mercurial.js"></script>
3306 <script type="text/javascript" src="/static/mercurial.js"></script>
3307
3307
3308 <title>Help: pager</title>
3308 <title>Help: pager</title>
3309 </head>
3309 </head>
3310 <body>
3310 <body>
3311
3311
3312 <div class="container">
3312 <div class="container">
3313 <div class="menu">
3313 <div class="menu">
3314 <div class="logo">
3314 <div class="logo">
3315 <a href="https://mercurial-scm.org/">
3315 <a href="https://mercurial-scm.org/">
3316 <img src="/static/hglogo.png" alt="mercurial" /></a>
3316 <img src="/static/hglogo.png" alt="mercurial" /></a>
3317 </div>
3317 </div>
3318 <ul>
3318 <ul>
3319 <li><a href="/shortlog">log</a></li>
3319 <li><a href="/shortlog">log</a></li>
3320 <li><a href="/graph">graph</a></li>
3320 <li><a href="/graph">graph</a></li>
3321 <li><a href="/tags">tags</a></li>
3321 <li><a href="/tags">tags</a></li>
3322 <li><a href="/bookmarks">bookmarks</a></li>
3322 <li><a href="/bookmarks">bookmarks</a></li>
3323 <li><a href="/branches">branches</a></li>
3323 <li><a href="/branches">branches</a></li>
3324 </ul>
3324 </ul>
3325 <ul>
3325 <ul>
3326 <li class="active"><a href="/help">help</a></li>
3326 <li class="active"><a href="/help">help</a></li>
3327 </ul>
3327 </ul>
3328 </div>
3328 </div>
3329
3329
3330 <div class="main">
3330 <div class="main">
3331 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3331 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3332 <h3>Help: pager</h3>
3332 <h3>Help: pager</h3>
3333
3333
3334 <form class="search" action="/log">
3334 <form class="search" action="/log">
3335
3335
3336 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3336 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3337 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3337 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3338 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3338 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3339 </form>
3339 </form>
3340 <div id="doc">
3340 <div id="doc">
3341 <h1>Pager Support</h1>
3341 <h1>Pager Support</h1>
3342 <p>
3342 <p>
3343 Some Mercurial commands can produce a lot of output, and Mercurial will
3343 Some Mercurial commands can produce a lot of output, and Mercurial will
3344 attempt to use a pager to make those commands more pleasant.
3344 attempt to use a pager to make those commands more pleasant.
3345 </p>
3345 </p>
3346 <p>
3346 <p>
3347 To set the pager that should be used, set the application variable:
3347 To set the pager that should be used, set the application variable:
3348 </p>
3348 </p>
3349 <pre>
3349 <pre>
3350 [pager]
3350 [pager]
3351 pager = less -FRX
3351 pager = less -FRX
3352 </pre>
3352 </pre>
3353 <p>
3353 <p>
3354 If no pager is set in the user or repository configuration, Mercurial uses the
3354 If no pager is set in the user or repository configuration, Mercurial uses the
3355 environment variable $PAGER. If $PAGER is not set, pager.pager from the default
3355 environment variable $PAGER. If $PAGER is not set, pager.pager from the default
3356 or system configuration is used. If none of these are set, a default pager will
3356 or system configuration is used. If none of these are set, a default pager will
3357 be used, typically 'less' on Unix and 'more' on Windows.
3357 be used, typically 'less' on Unix and 'more' on Windows.
3358 </p>
3358 </p>
3359 <p>
3359 <p>
3360 You can disable the pager for certain commands by adding them to the
3360 You can disable the pager for certain commands by adding them to the
3361 pager.ignore list:
3361 pager.ignore list:
3362 </p>
3362 </p>
3363 <pre>
3363 <pre>
3364 [pager]
3364 [pager]
3365 ignore = version, help, update
3365 ignore = version, help, update
3366 </pre>
3366 </pre>
3367 <p>
3367 <p>
3368 To ignore global commands like 'hg version' or 'hg help', you have
3368 To ignore global commands like 'hg version' or 'hg help', you have
3369 to specify them in your user configuration file.
3369 to specify them in your user configuration file.
3370 </p>
3370 </p>
3371 <p>
3371 <p>
3372 To control whether the pager is used at all for an individual command,
3372 To control whether the pager is used at all for an individual command,
3373 you can use --pager=&lt;value&gt;:
3373 you can use --pager=&lt;value&gt;:
3374 </p>
3374 </p>
3375 <ul>
3375 <ul>
3376 <li> use as needed: 'auto'.
3376 <li> use as needed: 'auto'.
3377 <li> require the pager: 'yes' or 'on'.
3377 <li> require the pager: 'yes' or 'on'.
3378 <li> suppress the pager: 'no' or 'off' (any unrecognized value will also work).
3378 <li> suppress the pager: 'no' or 'off' (any unrecognized value will also work).
3379 </ul>
3379 </ul>
3380 <p>
3380 <p>
3381 To globally turn off all attempts to use a pager, set:
3381 To globally turn off all attempts to use a pager, set:
3382 </p>
3382 </p>
3383 <pre>
3383 <pre>
3384 [ui]
3384 [ui]
3385 paginate = never
3385 paginate = never
3386 </pre>
3386 </pre>
3387 <p>
3387 <p>
3388 which will prevent the pager from running.
3388 which will prevent the pager from running.
3389 </p>
3389 </p>
3390
3390
3391 </div>
3391 </div>
3392 </div>
3392 </div>
3393 </div>
3393 </div>
3394
3394
3395
3395
3396
3396
3397 </body>
3397 </body>
3398 </html>
3398 </html>
3399
3399
3400
3400
3401 Sub-topic indexes rendered properly
3401 Sub-topic indexes rendered properly
3402
3402
3403 $ get-with-headers.py $LOCALIP:$HGPORT "help/internals"
3403 $ get-with-headers.py $LOCALIP:$HGPORT "help/internals"
3404 200 Script output follows
3404 200 Script output follows
3405
3405
3406 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3406 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3407 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
3407 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
3408 <head>
3408 <head>
3409 <link rel="icon" href="/static/hgicon.png" type="image/png" />
3409 <link rel="icon" href="/static/hgicon.png" type="image/png" />
3410 <meta name="robots" content="index, nofollow" />
3410 <meta name="robots" content="index, nofollow" />
3411 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
3411 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
3412 <script type="text/javascript" src="/static/mercurial.js"></script>
3412 <script type="text/javascript" src="/static/mercurial.js"></script>
3413
3413
3414 <title>Help: internals</title>
3414 <title>Help: internals</title>
3415 </head>
3415 </head>
3416 <body>
3416 <body>
3417
3417
3418 <div class="container">
3418 <div class="container">
3419 <div class="menu">
3419 <div class="menu">
3420 <div class="logo">
3420 <div class="logo">
3421 <a href="https://mercurial-scm.org/">
3421 <a href="https://mercurial-scm.org/">
3422 <img src="/static/hglogo.png" alt="mercurial" /></a>
3422 <img src="/static/hglogo.png" alt="mercurial" /></a>
3423 </div>
3423 </div>
3424 <ul>
3424 <ul>
3425 <li><a href="/shortlog">log</a></li>
3425 <li><a href="/shortlog">log</a></li>
3426 <li><a href="/graph">graph</a></li>
3426 <li><a href="/graph">graph</a></li>
3427 <li><a href="/tags">tags</a></li>
3427 <li><a href="/tags">tags</a></li>
3428 <li><a href="/bookmarks">bookmarks</a></li>
3428 <li><a href="/bookmarks">bookmarks</a></li>
3429 <li><a href="/branches">branches</a></li>
3429 <li><a href="/branches">branches</a></li>
3430 </ul>
3430 </ul>
3431 <ul>
3431 <ul>
3432 <li><a href="/help">help</a></li>
3432 <li><a href="/help">help</a></li>
3433 </ul>
3433 </ul>
3434 </div>
3434 </div>
3435
3435
3436 <div class="main">
3436 <div class="main">
3437 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3437 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3438
3438
3439 <form class="search" action="/log">
3439 <form class="search" action="/log">
3440
3440
3441 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3441 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3442 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3442 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3443 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3443 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3444 </form>
3444 </form>
3445 <table class="bigtable">
3445 <table class="bigtable">
3446 <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr>
3446 <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr>
3447
3447
3448 <tr><td>
3448 <tr><td>
3449 <a href="/help/internals.bid-merge">
3449 <a href="/help/internals.bid-merge">
3450 bid-merge
3450 bid-merge
3451 </a>
3451 </a>
3452 </td><td>
3452 </td><td>
3453 Bid Merge Algorithm
3453 Bid Merge Algorithm
3454 </td></tr>
3454 </td></tr>
3455 <tr><td>
3455 <tr><td>
3456 <a href="/help/internals.bundle2">
3456 <a href="/help/internals.bundle2">
3457 bundle2
3457 bundle2
3458 </a>
3458 </a>
3459 </td><td>
3459 </td><td>
3460 Bundle2
3460 Bundle2
3461 </td></tr>
3461 </td></tr>
3462 <tr><td>
3462 <tr><td>
3463 <a href="/help/internals.bundles">
3463 <a href="/help/internals.bundles">
3464 bundles
3464 bundles
3465 </a>
3465 </a>
3466 </td><td>
3466 </td><td>
3467 Bundles
3467 Bundles
3468 </td></tr>
3468 </td></tr>
3469 <tr><td>
3469 <tr><td>
3470 <a href="/help/internals.cbor">
3470 <a href="/help/internals.cbor">
3471 cbor
3471 cbor
3472 </a>
3472 </a>
3473 </td><td>
3473 </td><td>
3474 CBOR
3474 CBOR
3475 </td></tr>
3475 </td></tr>
3476 <tr><td>
3476 <tr><td>
3477 <a href="/help/internals.censor">
3477 <a href="/help/internals.censor">
3478 censor
3478 censor
3479 </a>
3479 </a>
3480 </td><td>
3480 </td><td>
3481 Censor
3481 Censor
3482 </td></tr>
3482 </td></tr>
3483 <tr><td>
3483 <tr><td>
3484 <a href="/help/internals.changegroups">
3484 <a href="/help/internals.changegroups">
3485 changegroups
3485 changegroups
3486 </a>
3486 </a>
3487 </td><td>
3487 </td><td>
3488 Changegroups
3488 Changegroups
3489 </td></tr>
3489 </td></tr>
3490 <tr><td>
3490 <tr><td>
3491 <a href="/help/internals.config">
3491 <a href="/help/internals.config">
3492 config
3492 config
3493 </a>
3493 </a>
3494 </td><td>
3494 </td><td>
3495 Config Registrar
3495 Config Registrar
3496 </td></tr>
3496 </td></tr>
3497 <tr><td>
3497 <tr><td>
3498 <a href="/help/internals.extensions">
3498 <a href="/help/internals.extensions">
3499 extensions
3499 extensions
3500 </a>
3500 </a>
3501 </td><td>
3501 </td><td>
3502 Extension API
3502 Extension API
3503 </td></tr>
3503 </td></tr>
3504 <tr><td>
3504 <tr><td>
3505 <a href="/help/internals.mergestate">
3505 <a href="/help/internals.mergestate">
3506 mergestate
3506 mergestate
3507 </a>
3507 </a>
3508 </td><td>
3508 </td><td>
3509 Mergestate
3509 Mergestate
3510 </td></tr>
3510 </td></tr>
3511 <tr><td>
3511 <tr><td>
3512 <a href="/help/internals.requirements">
3512 <a href="/help/internals.requirements">
3513 requirements
3513 requirements
3514 </a>
3514 </a>
3515 </td><td>
3515 </td><td>
3516 Repository Requirements
3516 Repository Requirements
3517 </td></tr>
3517 </td></tr>
3518 <tr><td>
3518 <tr><td>
3519 <a href="/help/internals.revlogs">
3519 <a href="/help/internals.revlogs">
3520 revlogs
3520 revlogs
3521 </a>
3521 </a>
3522 </td><td>
3522 </td><td>
3523 Revision Logs
3523 Revision Logs
3524 </td></tr>
3524 </td></tr>
3525 <tr><td>
3525 <tr><td>
3526 <a href="/help/internals.wireprotocol">
3526 <a href="/help/internals.wireprotocol">
3527 wireprotocol
3527 wireprotocol
3528 </a>
3528 </a>
3529 </td><td>
3529 </td><td>
3530 Wire Protocol
3530 Wire Protocol
3531 </td></tr>
3531 </td></tr>
3532 <tr><td>
3532 <tr><td>
3533 <a href="/help/internals.wireprotocolrpc">
3533 <a href="/help/internals.wireprotocolrpc">
3534 wireprotocolrpc
3534 wireprotocolrpc
3535 </a>
3535 </a>
3536 </td><td>
3536 </td><td>
3537 Wire Protocol RPC
3537 Wire Protocol RPC
3538 </td></tr>
3538 </td></tr>
3539 <tr><td>
3539 <tr><td>
3540 <a href="/help/internals.wireprotocolv2">
3540 <a href="/help/internals.wireprotocolv2">
3541 wireprotocolv2
3541 wireprotocolv2
3542 </a>
3542 </a>
3543 </td><td>
3543 </td><td>
3544 Wire Protocol Version 2
3544 Wire Protocol Version 2
3545 </td></tr>
3545 </td></tr>
3546
3546
3547
3547
3548
3548
3549
3549
3550
3550
3551 </table>
3551 </table>
3552 </div>
3552 </div>
3553 </div>
3553 </div>
3554
3554
3555
3555
3556
3556
3557 </body>
3557 </body>
3558 </html>
3558 </html>
3559
3559
3560
3560
3561 Sub-topic topics rendered properly
3561 Sub-topic topics rendered properly
3562
3562
3563 $ get-with-headers.py $LOCALIP:$HGPORT "help/internals.changegroups"
3563 $ get-with-headers.py $LOCALIP:$HGPORT "help/internals.changegroups"
3564 200 Script output follows
3564 200 Script output follows
3565
3565
3566 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3566 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3567 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
3567 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
3568 <head>
3568 <head>
3569 <link rel="icon" href="/static/hgicon.png" type="image/png" />
3569 <link rel="icon" href="/static/hgicon.png" type="image/png" />
3570 <meta name="robots" content="index, nofollow" />
3570 <meta name="robots" content="index, nofollow" />
3571 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
3571 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
3572 <script type="text/javascript" src="/static/mercurial.js"></script>
3572 <script type="text/javascript" src="/static/mercurial.js"></script>
3573
3573
3574 <title>Help: internals.changegroups</title>
3574 <title>Help: internals.changegroups</title>
3575 </head>
3575 </head>
3576 <body>
3576 <body>
3577
3577
3578 <div class="container">
3578 <div class="container">
3579 <div class="menu">
3579 <div class="menu">
3580 <div class="logo">
3580 <div class="logo">
3581 <a href="https://mercurial-scm.org/">
3581 <a href="https://mercurial-scm.org/">
3582 <img src="/static/hglogo.png" alt="mercurial" /></a>
3582 <img src="/static/hglogo.png" alt="mercurial" /></a>
3583 </div>
3583 </div>
3584 <ul>
3584 <ul>
3585 <li><a href="/shortlog">log</a></li>
3585 <li><a href="/shortlog">log</a></li>
3586 <li><a href="/graph">graph</a></li>
3586 <li><a href="/graph">graph</a></li>
3587 <li><a href="/tags">tags</a></li>
3587 <li><a href="/tags">tags</a></li>
3588 <li><a href="/bookmarks">bookmarks</a></li>
3588 <li><a href="/bookmarks">bookmarks</a></li>
3589 <li><a href="/branches">branches</a></li>
3589 <li><a href="/branches">branches</a></li>
3590 </ul>
3590 </ul>
3591 <ul>
3591 <ul>
3592 <li class="active"><a href="/help">help</a></li>
3592 <li class="active"><a href="/help">help</a></li>
3593 </ul>
3593 </ul>
3594 </div>
3594 </div>
3595
3595
3596 <div class="main">
3596 <div class="main">
3597 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3597 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3598 <h3>Help: internals.changegroups</h3>
3598 <h3>Help: internals.changegroups</h3>
3599
3599
3600 <form class="search" action="/log">
3600 <form class="search" action="/log">
3601
3601
3602 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3602 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3603 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3603 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3604 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3604 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3605 </form>
3605 </form>
3606 <div id="doc">
3606 <div id="doc">
3607 <h1>Changegroups</h1>
3607 <h1>Changegroups</h1>
3608 <p>
3608 <p>
3609 Changegroups are representations of repository revlog data, specifically
3609 Changegroups are representations of repository revlog data, specifically
3610 the changelog data, root/flat manifest data, treemanifest data, and
3610 the changelog data, root/flat manifest data, treemanifest data, and
3611 filelogs.
3611 filelogs.
3612 </p>
3612 </p>
3613 <p>
3613 <p>
3614 There are 3 versions of changegroups: &quot;1&quot;, &quot;2&quot;, and &quot;3&quot;. From a
3614 There are 3 versions of changegroups: &quot;1&quot;, &quot;2&quot;, and &quot;3&quot;. From a
3615 high-level, versions &quot;1&quot; and &quot;2&quot; are almost exactly the same, with the
3615 high-level, versions &quot;1&quot; and &quot;2&quot; are almost exactly the same, with the
3616 only difference being an additional item in the *delta header*. Version
3616 only difference being an additional item in the *delta header*. Version
3617 &quot;3&quot; adds support for storage flags in the *delta header* and optionally
3617 &quot;3&quot; adds support for storage flags in the *delta header* and optionally
3618 exchanging treemanifests (enabled by setting an option on the
3618 exchanging treemanifests (enabled by setting an option on the
3619 &quot;changegroup&quot; part in the bundle2).
3619 &quot;changegroup&quot; part in the bundle2).
3620 </p>
3620 </p>
3621 <p>
3621 <p>
3622 Changegroups when not exchanging treemanifests consist of 3 logical
3622 Changegroups when not exchanging treemanifests consist of 3 logical
3623 segments:
3623 segments:
3624 </p>
3624 </p>
3625 <pre>
3625 <pre>
3626 +---------------------------------+
3626 +---------------------------------+
3627 | | | |
3627 | | | |
3628 | changeset | manifest | filelogs |
3628 | changeset | manifest | filelogs |
3629 | | | |
3629 | | | |
3630 | | | |
3630 | | | |
3631 +---------------------------------+
3631 +---------------------------------+
3632 </pre>
3632 </pre>
3633 <p>
3633 <p>
3634 When exchanging treemanifests, there are 4 logical segments:
3634 When exchanging treemanifests, there are 4 logical segments:
3635 </p>
3635 </p>
3636 <pre>
3636 <pre>
3637 +-------------------------------------------------+
3637 +-------------------------------------------------+
3638 | | | | |
3638 | | | | |
3639 | changeset | root | treemanifests | filelogs |
3639 | changeset | root | treemanifests | filelogs |
3640 | | manifest | | |
3640 | | manifest | | |
3641 | | | | |
3641 | | | | |
3642 +-------------------------------------------------+
3642 +-------------------------------------------------+
3643 </pre>
3643 </pre>
3644 <p>
3644 <p>
3645 The principle building block of each segment is a *chunk*. A *chunk*
3645 The principle building block of each segment is a *chunk*. A *chunk*
3646 is a framed piece of data:
3646 is a framed piece of data:
3647 </p>
3647 </p>
3648 <pre>
3648 <pre>
3649 +---------------------------------------+
3649 +---------------------------------------+
3650 | | |
3650 | | |
3651 | length | data |
3651 | length | data |
3652 | (4 bytes) | (&lt;length - 4&gt; bytes) |
3652 | (4 bytes) | (&lt;length - 4&gt; bytes) |
3653 | | |
3653 | | |
3654 +---------------------------------------+
3654 +---------------------------------------+
3655 </pre>
3655 </pre>
3656 <p>
3656 <p>
3657 All integers are big-endian signed integers. Each chunk starts with a 32-bit
3657 All integers are big-endian signed integers. Each chunk starts with a 32-bit
3658 integer indicating the length of the entire chunk (including the length field
3658 integer indicating the length of the entire chunk (including the length field
3659 itself).
3659 itself).
3660 </p>
3660 </p>
3661 <p>
3661 <p>
3662 There is a special case chunk that has a value of 0 for the length
3662 There is a special case chunk that has a value of 0 for the length
3663 (&quot;0x00000000&quot;). We call this an *empty chunk*.
3663 (&quot;0x00000000&quot;). We call this an *empty chunk*.
3664 </p>
3664 </p>
3665 <h2>Delta Groups</h2>
3665 <h2>Delta Groups</h2>
3666 <p>
3666 <p>
3667 A *delta group* expresses the content of a revlog as a series of deltas,
3667 A *delta group* expresses the content of a revlog as a series of deltas,
3668 or patches against previous revisions.
3668 or patches against previous revisions.
3669 </p>
3669 </p>
3670 <p>
3670 <p>
3671 Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
3671 Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
3672 to signal the end of the delta group:
3672 to signal the end of the delta group:
3673 </p>
3673 </p>
3674 <pre>
3674 <pre>
3675 +------------------------------------------------------------------------+
3675 +------------------------------------------------------------------------+
3676 | | | | | |
3676 | | | | | |
3677 | chunk0 length | chunk0 data | chunk1 length | chunk1 data | 0x0 |
3677 | chunk0 length | chunk0 data | chunk1 length | chunk1 data | 0x0 |
3678 | (4 bytes) | (various) | (4 bytes) | (various) | (4 bytes) |
3678 | (4 bytes) | (various) | (4 bytes) | (various) | (4 bytes) |
3679 | | | | | |
3679 | | | | | |
3680 +------------------------------------------------------------------------+
3680 +------------------------------------------------------------------------+
3681 </pre>
3681 </pre>
3682 <p>
3682 <p>
3683 Each *chunk*'s data consists of the following:
3683 Each *chunk*'s data consists of the following:
3684 </p>
3684 </p>
3685 <pre>
3685 <pre>
3686 +---------------------------------------+
3686 +---------------------------------------+
3687 | | |
3687 | | |
3688 | delta header | delta data |
3688 | delta header | delta data |
3689 | (various by version) | (various) |
3689 | (various by version) | (various) |
3690 | | |
3690 | | |
3691 +---------------------------------------+
3691 +---------------------------------------+
3692 </pre>
3692 </pre>
3693 <p>
3693 <p>
3694 The *delta data* is a series of *delta*s that describe a diff from an existing
3694 The *delta data* is a series of *delta*s that describe a diff from an existing
3695 entry (either that the recipient already has, or previously specified in the
3695 entry (either that the recipient already has, or previously specified in the
3696 bundle/changegroup).
3696 bundle/changegroup).
3697 </p>
3697 </p>
3698 <p>
3698 <p>
3699 The *delta header* is different between versions &quot;1&quot;, &quot;2&quot;, and
3699 The *delta header* is different between versions &quot;1&quot;, &quot;2&quot;, and
3700 &quot;3&quot; of the changegroup format.
3700 &quot;3&quot; of the changegroup format.
3701 </p>
3701 </p>
3702 <p>
3702 <p>
3703 Version 1 (headerlen=80):
3703 Version 1 (headerlen=80):
3704 </p>
3704 </p>
3705 <pre>
3705 <pre>
3706 +------------------------------------------------------+
3706 +------------------------------------------------------+
3707 | | | | |
3707 | | | | |
3708 | node | p1 node | p2 node | link node |
3708 | node | p1 node | p2 node | link node |
3709 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
3709 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
3710 | | | | |
3710 | | | | |
3711 +------------------------------------------------------+
3711 +------------------------------------------------------+
3712 </pre>
3712 </pre>
3713 <p>
3713 <p>
3714 Version 2 (headerlen=100):
3714 Version 2 (headerlen=100):
3715 </p>
3715 </p>
3716 <pre>
3716 <pre>
3717 +------------------------------------------------------------------+
3717 +------------------------------------------------------------------+
3718 | | | | | |
3718 | | | | | |
3719 | node | p1 node | p2 node | base node | link node |
3719 | node | p1 node | p2 node | base node | link node |
3720 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
3720 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
3721 | | | | | |
3721 | | | | | |
3722 +------------------------------------------------------------------+
3722 +------------------------------------------------------------------+
3723 </pre>
3723 </pre>
3724 <p>
3724 <p>
3725 Version 3 (headerlen=102):
3725 Version 3 (headerlen=102):
3726 </p>
3726 </p>
3727 <pre>
3727 <pre>
3728 +------------------------------------------------------------------------------+
3728 +------------------------------------------------------------------------------+
3729 | | | | | | |
3729 | | | | | | |
3730 | node | p1 node | p2 node | base node | link node | flags |
3730 | node | p1 node | p2 node | base node | link node | flags |
3731 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (2 bytes) |
3731 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (2 bytes) |
3732 | | | | | | |
3732 | | | | | | |
3733 +------------------------------------------------------------------------------+
3733 +------------------------------------------------------------------------------+
3734 </pre>
3734 </pre>
3735 <p>
3735 <p>
3736 The *delta data* consists of &quot;chunklen - 4 - headerlen&quot; bytes, which contain a
3736 The *delta data* consists of &quot;chunklen - 4 - headerlen&quot; bytes, which contain a
3737 series of *delta*s, densely packed (no separators). These deltas describe a diff
3737 series of *delta*s, densely packed (no separators). These deltas describe a diff
3738 from an existing entry (either that the recipient already has, or previously
3738 from an existing entry (either that the recipient already has, or previously
3739 specified in the bundle/changegroup). The format is described more fully in
3739 specified in the bundle/changegroup). The format is described more fully in
3740 &quot;hg help internals.bdiff&quot;, but briefly:
3740 &quot;hg help internals.bdiff&quot;, but briefly:
3741 </p>
3741 </p>
3742 <pre>
3742 <pre>
3743 +---------------------------------------------------------------+
3743 +---------------------------------------------------------------+
3744 | | | | |
3744 | | | | |
3745 | start offset | end offset | new length | content |
3745 | start offset | end offset | new length | content |
3746 | (4 bytes) | (4 bytes) | (4 bytes) | (&lt;new length&gt; bytes) |
3746 | (4 bytes) | (4 bytes) | (4 bytes) | (&lt;new length&gt; bytes) |
3747 | | | | |
3747 | | | | |
3748 +---------------------------------------------------------------+
3748 +---------------------------------------------------------------+
3749 </pre>
3749 </pre>
3750 <p>
3750 <p>
3751 Please note that the length field in the delta data does *not* include itself.
3751 Please note that the length field in the delta data does *not* include itself.
3752 </p>
3752 </p>
3753 <p>
3753 <p>
3754 In version 1, the delta is always applied against the previous node from
3754 In version 1, the delta is always applied against the previous node from
3755 the changegroup or the first parent if this is the first entry in the
3755 the changegroup or the first parent if this is the first entry in the
3756 changegroup.
3756 changegroup.
3757 </p>
3757 </p>
3758 <p>
3758 <p>
3759 In version 2 and up, the delta base node is encoded in the entry in the
3759 In version 2 and up, the delta base node is encoded in the entry in the
3760 changegroup. This allows the delta to be expressed against any parent,
3760 changegroup. This allows the delta to be expressed against any parent,
3761 which can result in smaller deltas and more efficient encoding of data.
3761 which can result in smaller deltas and more efficient encoding of data.
3762 </p>
3762 </p>
3763 <p>
3763 <p>
3764 The *flags* field holds bitwise flags affecting the processing of revision
3764 The *flags* field holds bitwise flags affecting the processing of revision
3765 data. The following flags are defined:
3765 data. The following flags are defined:
3766 </p>
3766 </p>
3767 <dl>
3767 <dl>
3768 <dt>32768
3768 <dt>32768
3769 <dd>Censored revision. The revision's fulltext has been replaced by censor metadata. May only occur on file revisions.
3769 <dd>Censored revision. The revision's fulltext has been replaced by censor metadata. May only occur on file revisions.
3770 <dt>16384
3770 <dt>16384
3771 <dd>Ellipsis revision. Revision hash does not match data (likely due to rewritten parents).
3771 <dd>Ellipsis revision. Revision hash does not match data (likely due to rewritten parents).
3772 <dt>8192
3772 <dt>8192
3773 <dd>Externally stored. The revision fulltext contains &quot;key:value&quot; &quot;\n&quot; delimited metadata defining an object stored elsewhere. Used by the LFS extension.
3773 <dd>Externally stored. The revision fulltext contains &quot;key:value&quot; &quot;\n&quot; delimited metadata defining an object stored elsewhere. Used by the LFS extension.
3774 </dl>
3774 </dl>
3775 <p>
3775 <p>
3776 For historical reasons, the integer values are identical to revlog version 1
3776 For historical reasons, the integer values are identical to revlog version 1
3777 per-revision storage flags and correspond to bits being set in this 2-byte
3777 per-revision storage flags and correspond to bits being set in this 2-byte
3778 field. Bits were allocated starting from the most-significant bit, hence the
3778 field. Bits were allocated starting from the most-significant bit, hence the
3779 reverse ordering and allocation of these flags.
3779 reverse ordering and allocation of these flags.
3780 </p>
3780 </p>
3781 <h2>Changeset Segment</h2>
3781 <h2>Changeset Segment</h2>
3782 <p>
3782 <p>
3783 The *changeset segment* consists of a single *delta group* holding
3783 The *changeset segment* consists of a single *delta group* holding
3784 changelog data. The *empty chunk* at the end of the *delta group* denotes
3784 changelog data. The *empty chunk* at the end of the *delta group* denotes
3785 the boundary to the *manifest segment*.
3785 the boundary to the *manifest segment*.
3786 </p>
3786 </p>
3787 <h2>Manifest Segment</h2>
3787 <h2>Manifest Segment</h2>
3788 <p>
3788 <p>
3789 The *manifest segment* consists of a single *delta group* holding manifest
3789 The *manifest segment* consists of a single *delta group* holding manifest
3790 data. If treemanifests are in use, it contains only the manifest for the
3790 data. If treemanifests are in use, it contains only the manifest for the
3791 root directory of the repository. Otherwise, it contains the entire
3791 root directory of the repository. Otherwise, it contains the entire
3792 manifest data. The *empty chunk* at the end of the *delta group* denotes
3792 manifest data. The *empty chunk* at the end of the *delta group* denotes
3793 the boundary to the next segment (either the *treemanifests segment* or the
3793 the boundary to the next segment (either the *treemanifests segment* or the
3794 *filelogs segment*, depending on version and the request options).
3794 *filelogs segment*, depending on version and the request options).
3795 </p>
3795 </p>
3796 <h3>Treemanifests Segment</h3>
3796 <h3>Treemanifests Segment</h3>
3797 <p>
3797 <p>
3798 The *treemanifests segment* only exists in changegroup version &quot;3&quot;, and
3798 The *treemanifests segment* only exists in changegroup version &quot;3&quot;, and
3799 only if the 'treemanifest' param is part of the bundle2 changegroup part
3799 only if the 'treemanifest' param is part of the bundle2 changegroup part
3800 (it is not possible to use changegroup version 3 outside of bundle2).
3800 (it is not possible to use changegroup version 3 outside of bundle2).
3801 Aside from the filenames in the *treemanifests segment* containing a
3801 Aside from the filenames in the *treemanifests segment* containing a
3802 trailing &quot;/&quot; character, it behaves identically to the *filelogs segment*
3802 trailing &quot;/&quot; character, it behaves identically to the *filelogs segment*
3803 (see below). The final sub-segment is followed by an *empty chunk* (logically,
3803 (see below). The final sub-segment is followed by an *empty chunk* (logically,
3804 a sub-segment with filename size 0). This denotes the boundary to the
3804 a sub-segment with filename size 0). This denotes the boundary to the
3805 *filelogs segment*.
3805 *filelogs segment*.
3806 </p>
3806 </p>
3807 <h2>Filelogs Segment</h2>
3807 <h2>Filelogs Segment</h2>
3808 <p>
3808 <p>
3809 The *filelogs segment* consists of multiple sub-segments, each
3809 The *filelogs segment* consists of multiple sub-segments, each
3810 corresponding to an individual file whose data is being described:
3810 corresponding to an individual file whose data is being described:
3811 </p>
3811 </p>
3812 <pre>
3812 <pre>
3813 +--------------------------------------------------+
3813 +--------------------------------------------------+
3814 | | | | | |
3814 | | | | | |
3815 | filelog0 | filelog1 | filelog2 | ... | 0x0 |
3815 | filelog0 | filelog1 | filelog2 | ... | 0x0 |
3816 | | | | | (4 bytes) |
3816 | | | | | (4 bytes) |
3817 | | | | | |
3817 | | | | | |
3818 +--------------------------------------------------+
3818 +--------------------------------------------------+
3819 </pre>
3819 </pre>
3820 <p>
3820 <p>
3821 The final filelog sub-segment is followed by an *empty chunk* (logically,
3821 The final filelog sub-segment is followed by an *empty chunk* (logically,
3822 a sub-segment with filename size 0). This denotes the end of the segment
3822 a sub-segment with filename size 0). This denotes the end of the segment
3823 and of the overall changegroup.
3823 and of the overall changegroup.
3824 </p>
3824 </p>
3825 <p>
3825 <p>
3826 Each filelog sub-segment consists of the following:
3826 Each filelog sub-segment consists of the following:
3827 </p>
3827 </p>
3828 <pre>
3828 <pre>
3829 +------------------------------------------------------+
3829 +------------------------------------------------------+
3830 | | | |
3830 | | | |
3831 | filename length | filename | delta group |
3831 | filename length | filename | delta group |
3832 | (4 bytes) | (&lt;length - 4&gt; bytes) | (various) |
3832 | (4 bytes) | (&lt;length - 4&gt; bytes) | (various) |
3833 | | | |
3833 | | | |
3834 +------------------------------------------------------+
3834 +------------------------------------------------------+
3835 </pre>
3835 </pre>
3836 <p>
3836 <p>
3837 That is, a *chunk* consisting of the filename (not terminated or padded)
3837 That is, a *chunk* consisting of the filename (not terminated or padded)
3838 followed by N chunks constituting the *delta group* for this file. The
3838 followed by N chunks constituting the *delta group* for this file. The
3839 *empty chunk* at the end of each *delta group* denotes the boundary to the
3839 *empty chunk* at the end of each *delta group* denotes the boundary to the
3840 next filelog sub-segment.
3840 next filelog sub-segment.
3841 </p>
3841 </p>
3842
3842
3843 </div>
3843 </div>
3844 </div>
3844 </div>
3845 </div>
3845 </div>
3846
3846
3847
3847
3848
3848
3849 </body>
3849 </body>
3850 </html>
3850 </html>
3851
3851
3852
3852
3853 $ get-with-headers.py 127.0.0.1:$HGPORT "help/unknowntopic"
3853 $ get-with-headers.py 127.0.0.1:$HGPORT "help/unknowntopic"
3854 404 Not Found
3854 404 Not Found
3855
3855
3856 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3856 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3857 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
3857 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
3858 <head>
3858 <head>
3859 <link rel="icon" href="/static/hgicon.png" type="image/png" />
3859 <link rel="icon" href="/static/hgicon.png" type="image/png" />
3860 <meta name="robots" content="index, nofollow" />
3860 <meta name="robots" content="index, nofollow" />
3861 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
3861 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
3862 <script type="text/javascript" src="/static/mercurial.js"></script>
3862 <script type="text/javascript" src="/static/mercurial.js"></script>
3863
3863
3864 <title>test: error</title>
3864 <title>test: error</title>
3865 </head>
3865 </head>
3866 <body>
3866 <body>
3867
3867
3868 <div class="container">
3868 <div class="container">
3869 <div class="menu">
3869 <div class="menu">
3870 <div class="logo">
3870 <div class="logo">
3871 <a href="https://mercurial-scm.org/">
3871 <a href="https://mercurial-scm.org/">
3872 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
3872 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
3873 </div>
3873 </div>
3874 <ul>
3874 <ul>
3875 <li><a href="/shortlog">log</a></li>
3875 <li><a href="/shortlog">log</a></li>
3876 <li><a href="/graph">graph</a></li>
3876 <li><a href="/graph">graph</a></li>
3877 <li><a href="/tags">tags</a></li>
3877 <li><a href="/tags">tags</a></li>
3878 <li><a href="/bookmarks">bookmarks</a></li>
3878 <li><a href="/bookmarks">bookmarks</a></li>
3879 <li><a href="/branches">branches</a></li>
3879 <li><a href="/branches">branches</a></li>
3880 </ul>
3880 </ul>
3881 <ul>
3881 <ul>
3882 <li><a href="/help">help</a></li>
3882 <li><a href="/help">help</a></li>
3883 </ul>
3883 </ul>
3884 </div>
3884 </div>
3885
3885
3886 <div class="main">
3886 <div class="main">
3887
3887
3888 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3888 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3889 <h3>error</h3>
3889 <h3>error</h3>
3890
3890
3891
3891
3892 <form class="search" action="/log">
3892 <form class="search" action="/log">
3893
3893
3894 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3894 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
3895 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3895 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3896 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3896 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3897 </form>
3897 </form>
3898
3898
3899 <div class="description">
3899 <div class="description">
3900 <p>
3900 <p>
3901 An error occurred while processing your request:
3901 An error occurred while processing your request:
3902 </p>
3902 </p>
3903 <p>
3903 <p>
3904 Not Found
3904 Not Found
3905 </p>
3905 </p>
3906 </div>
3906 </div>
3907 </div>
3907 </div>
3908 </div>
3908 </div>
3909
3909
3910
3910
3911
3911
3912 </body>
3912 </body>
3913 </html>
3913 </html>
3914
3914
3915 [1]
3915 [1]
3916
3916
3917 $ killdaemons.py
3917 $ killdaemons.py
3918
3918
3919 #endif
3919 #endif
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now