##// END OF EJS Templates
perf-bundle: accept --type argument
marmoute -
r50308:b380583a default
parent child Browse files
Show More
@@ -1,4044 +1,4071 b''
1 # perf.py - performance test routines
1 # perf.py - performance test routines
2 '''helper extension to measure performance
2 '''helper extension to measure performance
3
3
4 Configurations
4 Configurations
5 ==============
5 ==============
6
6
7 ``perf``
7 ``perf``
8 --------
8 --------
9
9
10 ``all-timing``
10 ``all-timing``
11 When set, additional statistics will be reported for each benchmark: best,
11 When set, additional statistics will be reported for each benchmark: best,
12 worst, median average. If not set only the best timing is reported
12 worst, median average. If not set only the best timing is reported
13 (default: off).
13 (default: off).
14
14
15 ``presleep``
15 ``presleep``
16 number of second to wait before any group of runs (default: 1)
16 number of second to wait before any group of runs (default: 1)
17
17
18 ``pre-run``
18 ``pre-run``
19 number of run to perform before starting measurement.
19 number of run to perform before starting measurement.
20
20
21 ``profile-benchmark``
21 ``profile-benchmark``
22 Enable profiling for the benchmarked section.
22 Enable profiling for the benchmarked section.
23 (The first iteration is benchmarked)
23 (The first iteration is benchmarked)
24
24
25 ``run-limits``
25 ``run-limits``
26 Control the number of runs each benchmark will perform. The option value
26 Control the number of runs each benchmark will perform. The option value
27 should be a list of `<time>-<numberofrun>` pairs. After each run the
27 should be a list of `<time>-<numberofrun>` pairs. After each run the
28 conditions are considered in order with the following logic:
28 conditions are considered in order with the following logic:
29
29
30 If benchmark has been running for <time> seconds, and we have performed
30 If benchmark has been running for <time> seconds, and we have performed
31 <numberofrun> iterations, stop the benchmark,
31 <numberofrun> iterations, stop the benchmark,
32
32
33 The default value is: `3.0-100, 10.0-3`
33 The default value is: `3.0-100, 10.0-3`
34
34
35 ``stub``
35 ``stub``
36 When set, benchmarks will only be run once, useful for testing
36 When set, benchmarks will only be run once, useful for testing
37 (default: off)
37 (default: off)
38 '''
38 '''
39
39
40 # "historical portability" policy of perf.py:
40 # "historical portability" policy of perf.py:
41 #
41 #
42 # We have to do:
42 # We have to do:
43 # - make perf.py "loadable" with as wide Mercurial version as possible
43 # - make perf.py "loadable" with as wide Mercurial version as possible
44 # This doesn't mean that perf commands work correctly with that Mercurial.
44 # This doesn't mean that perf commands work correctly with that Mercurial.
45 # BTW, perf.py itself has been available since 1.1 (or eb240755386d).
45 # BTW, perf.py itself has been available since 1.1 (or eb240755386d).
46 # - make historical perf command work correctly with as wide Mercurial
46 # - make historical perf command work correctly with as wide Mercurial
47 # version as possible
47 # version as possible
48 #
48 #
49 # We have to do, if possible with reasonable cost:
49 # We have to do, if possible with reasonable cost:
50 # - make recent perf command for historical feature work correctly
50 # - make recent perf command for historical feature work correctly
51 # with early Mercurial
51 # with early Mercurial
52 #
52 #
53 # We don't have to do:
53 # We don't have to do:
54 # - make perf command for recent feature work correctly with early
54 # - make perf command for recent feature work correctly with early
55 # Mercurial
55 # Mercurial
56
56
57 import contextlib
57 import contextlib
58 import functools
58 import functools
59 import gc
59 import gc
60 import os
60 import os
61 import random
61 import random
62 import shutil
62 import shutil
63 import struct
63 import struct
64 import sys
64 import sys
65 import tempfile
65 import tempfile
66 import threading
66 import threading
67 import time
67 import time
68
68
69 import mercurial.revlog
69 import mercurial.revlog
70 from mercurial import (
70 from mercurial import (
71 changegroup,
71 changegroup,
72 cmdutil,
72 cmdutil,
73 commands,
73 commands,
74 copies,
74 copies,
75 error,
75 error,
76 extensions,
76 extensions,
77 hg,
77 hg,
78 mdiff,
78 mdiff,
79 merge,
79 merge,
80 util,
80 util,
81 )
81 )
82
82
83 # for "historical portability":
83 # for "historical portability":
84 # try to import modules separately (in dict order), and ignore
84 # try to import modules separately (in dict order), and ignore
85 # failure, because these aren't available with early Mercurial
85 # failure, because these aren't available with early Mercurial
86 try:
86 try:
87 from mercurial import branchmap # since 2.5 (or bcee63733aad)
87 from mercurial import branchmap # since 2.5 (or bcee63733aad)
88 except ImportError:
88 except ImportError:
89 pass
89 pass
90 try:
90 try:
91 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
91 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
92 except ImportError:
92 except ImportError:
93 pass
93 pass
94 try:
94 try:
95 from mercurial import registrar # since 3.7 (or 37d50250b696)
95 from mercurial import registrar # since 3.7 (or 37d50250b696)
96
96
97 dir(registrar) # forcibly load it
97 dir(registrar) # forcibly load it
98 except ImportError:
98 except ImportError:
99 registrar = None
99 registrar = None
100 try:
100 try:
101 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
101 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
102 except ImportError:
102 except ImportError:
103 pass
103 pass
104 try:
104 try:
105 from mercurial.utils import repoviewutil # since 5.0
105 from mercurial.utils import repoviewutil # since 5.0
106 except ImportError:
106 except ImportError:
107 repoviewutil = None
107 repoviewutil = None
108 try:
108 try:
109 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
109 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
110 except ImportError:
110 except ImportError:
111 pass
111 pass
112 try:
112 try:
113 from mercurial import setdiscovery # since 1.9 (or cb98fed52495)
113 from mercurial import setdiscovery # since 1.9 (or cb98fed52495)
114 except ImportError:
114 except ImportError:
115 pass
115 pass
116
116
117 try:
117 try:
118 from mercurial import profiling
118 from mercurial import profiling
119 except ImportError:
119 except ImportError:
120 profiling = None
120 profiling = None
121
121
122 try:
122 try:
123 from mercurial.revlogutils import constants as revlog_constants
123 from mercurial.revlogutils import constants as revlog_constants
124
124
125 perf_rl_kind = (revlog_constants.KIND_OTHER, b'created-by-perf')
125 perf_rl_kind = (revlog_constants.KIND_OTHER, b'created-by-perf')
126
126
127 def revlog(opener, *args, **kwargs):
127 def revlog(opener, *args, **kwargs):
128 return mercurial.revlog.revlog(opener, perf_rl_kind, *args, **kwargs)
128 return mercurial.revlog.revlog(opener, perf_rl_kind, *args, **kwargs)
129
129
130
130
131 except (ImportError, AttributeError):
131 except (ImportError, AttributeError):
132 perf_rl_kind = None
132 perf_rl_kind = None
133
133
134 def revlog(opener, *args, **kwargs):
134 def revlog(opener, *args, **kwargs):
135 return mercurial.revlog.revlog(opener, *args, **kwargs)
135 return mercurial.revlog.revlog(opener, *args, **kwargs)
136
136
137
137
138 def identity(a):
138 def identity(a):
139 return a
139 return a
140
140
141
141
142 try:
142 try:
143 from mercurial import pycompat
143 from mercurial import pycompat
144
144
145 getargspec = pycompat.getargspec # added to module after 4.5
145 getargspec = pycompat.getargspec # added to module after 4.5
146 _byteskwargs = pycompat.byteskwargs # since 4.1 (or fbc3f73dc802)
146 _byteskwargs = pycompat.byteskwargs # since 4.1 (or fbc3f73dc802)
147 _sysstr = pycompat.sysstr # since 4.0 (or 2219f4f82ede)
147 _sysstr = pycompat.sysstr # since 4.0 (or 2219f4f82ede)
148 _bytestr = pycompat.bytestr # since 4.2 (or b70407bd84d5)
148 _bytestr = pycompat.bytestr # since 4.2 (or b70407bd84d5)
149 _xrange = pycompat.xrange # since 4.8 (or 7eba8f83129b)
149 _xrange = pycompat.xrange # since 4.8 (or 7eba8f83129b)
150 fsencode = pycompat.fsencode # since 3.9 (or f4a5e0e86a7e)
150 fsencode = pycompat.fsencode # since 3.9 (or f4a5e0e86a7e)
151 if pycompat.ispy3:
151 if pycompat.ispy3:
152 _maxint = sys.maxsize # per py3 docs for replacing maxint
152 _maxint = sys.maxsize # per py3 docs for replacing maxint
153 else:
153 else:
154 _maxint = sys.maxint
154 _maxint = sys.maxint
155 except (NameError, ImportError, AttributeError):
155 except (NameError, ImportError, AttributeError):
156 import inspect
156 import inspect
157
157
158 getargspec = inspect.getargspec
158 getargspec = inspect.getargspec
159 _byteskwargs = identity
159 _byteskwargs = identity
160 _bytestr = str
160 _bytestr = str
161 fsencode = identity # no py3 support
161 fsencode = identity # no py3 support
162 _maxint = sys.maxint # no py3 support
162 _maxint = sys.maxint # no py3 support
163 _sysstr = lambda x: x # no py3 support
163 _sysstr = lambda x: x # no py3 support
164 _xrange = xrange
164 _xrange = xrange
165
165
166 try:
166 try:
167 # 4.7+
167 # 4.7+
168 queue = pycompat.queue.Queue
168 queue = pycompat.queue.Queue
169 except (NameError, AttributeError, ImportError):
169 except (NameError, AttributeError, ImportError):
170 # <4.7.
170 # <4.7.
171 try:
171 try:
172 queue = pycompat.queue
172 queue = pycompat.queue
173 except (NameError, AttributeError, ImportError):
173 except (NameError, AttributeError, ImportError):
174 import Queue as queue
174 import Queue as queue
175
175
176 try:
176 try:
177 from mercurial import logcmdutil
177 from mercurial import logcmdutil
178
178
179 makelogtemplater = logcmdutil.maketemplater
179 makelogtemplater = logcmdutil.maketemplater
180 except (AttributeError, ImportError):
180 except (AttributeError, ImportError):
181 try:
181 try:
182 makelogtemplater = cmdutil.makelogtemplater
182 makelogtemplater = cmdutil.makelogtemplater
183 except (AttributeError, ImportError):
183 except (AttributeError, ImportError):
184 makelogtemplater = None
184 makelogtemplater = None
185
185
186 # for "historical portability":
186 # for "historical portability":
187 # define util.safehasattr forcibly, because util.safehasattr has been
187 # define util.safehasattr forcibly, because util.safehasattr has been
188 # available since 1.9.3 (or 94b200a11cf7)
188 # available since 1.9.3 (or 94b200a11cf7)
189 _undefined = object()
189 _undefined = object()
190
190
191
191
192 def safehasattr(thing, attr):
192 def safehasattr(thing, attr):
193 return getattr(thing, _sysstr(attr), _undefined) is not _undefined
193 return getattr(thing, _sysstr(attr), _undefined) is not _undefined
194
194
195
195
196 setattr(util, 'safehasattr', safehasattr)
196 setattr(util, 'safehasattr', safehasattr)
197
197
198 # for "historical portability":
198 # for "historical portability":
199 # define util.timer forcibly, because util.timer has been available
199 # define util.timer forcibly, because util.timer has been available
200 # since ae5d60bb70c9
200 # since ae5d60bb70c9
201 if safehasattr(time, 'perf_counter'):
201 if safehasattr(time, 'perf_counter'):
202 util.timer = time.perf_counter
202 util.timer = time.perf_counter
203 elif os.name == b'nt':
203 elif os.name == b'nt':
204 util.timer = time.clock
204 util.timer = time.clock
205 else:
205 else:
206 util.timer = time.time
206 util.timer = time.time
207
207
208 # for "historical portability":
208 # for "historical portability":
209 # use locally defined empty option list, if formatteropts isn't
209 # use locally defined empty option list, if formatteropts isn't
210 # available, because commands.formatteropts has been available since
210 # available, because commands.formatteropts has been available since
211 # 3.2 (or 7a7eed5176a4), even though formatting itself has been
211 # 3.2 (or 7a7eed5176a4), even though formatting itself has been
212 # available since 2.2 (or ae5f92e154d3)
212 # available since 2.2 (or ae5f92e154d3)
213 formatteropts = getattr(
213 formatteropts = getattr(
214 cmdutil, "formatteropts", getattr(commands, "formatteropts", [])
214 cmdutil, "formatteropts", getattr(commands, "formatteropts", [])
215 )
215 )
216
216
217 # for "historical portability":
217 # for "historical portability":
218 # use locally defined option list, if debugrevlogopts isn't available,
218 # use locally defined option list, if debugrevlogopts isn't available,
219 # because commands.debugrevlogopts has been available since 3.7 (or
219 # because commands.debugrevlogopts has been available since 3.7 (or
220 # 5606f7d0d063), even though cmdutil.openrevlog() has been available
220 # 5606f7d0d063), even though cmdutil.openrevlog() has been available
221 # since 1.9 (or a79fea6b3e77).
221 # since 1.9 (or a79fea6b3e77).
222 revlogopts = getattr(
222 revlogopts = getattr(
223 cmdutil,
223 cmdutil,
224 "debugrevlogopts",
224 "debugrevlogopts",
225 getattr(
225 getattr(
226 commands,
226 commands,
227 "debugrevlogopts",
227 "debugrevlogopts",
228 [
228 [
229 (b'c', b'changelog', False, b'open changelog'),
229 (b'c', b'changelog', False, b'open changelog'),
230 (b'm', b'manifest', False, b'open manifest'),
230 (b'm', b'manifest', False, b'open manifest'),
231 (b'', b'dir', False, b'open directory manifest'),
231 (b'', b'dir', False, b'open directory manifest'),
232 ],
232 ],
233 ),
233 ),
234 )
234 )
235
235
236 cmdtable = {}
236 cmdtable = {}
237
237
238 # for "historical portability":
238 # for "historical portability":
239 # define parsealiases locally, because cmdutil.parsealiases has been
239 # define parsealiases locally, because cmdutil.parsealiases has been
240 # available since 1.5 (or 6252852b4332)
240 # available since 1.5 (or 6252852b4332)
241 def parsealiases(cmd):
241 def parsealiases(cmd):
242 return cmd.split(b"|")
242 return cmd.split(b"|")
243
243
244
244
245 if safehasattr(registrar, 'command'):
245 if safehasattr(registrar, 'command'):
246 command = registrar.command(cmdtable)
246 command = registrar.command(cmdtable)
247 elif safehasattr(cmdutil, 'command'):
247 elif safehasattr(cmdutil, 'command'):
248 command = cmdutil.command(cmdtable)
248 command = cmdutil.command(cmdtable)
249 if 'norepo' not in getargspec(command).args:
249 if 'norepo' not in getargspec(command).args:
250 # for "historical portability":
250 # for "historical portability":
251 # wrap original cmdutil.command, because "norepo" option has
251 # wrap original cmdutil.command, because "norepo" option has
252 # been available since 3.1 (or 75a96326cecb)
252 # been available since 3.1 (or 75a96326cecb)
253 _command = command
253 _command = command
254
254
255 def command(name, options=(), synopsis=None, norepo=False):
255 def command(name, options=(), synopsis=None, norepo=False):
256 if norepo:
256 if norepo:
257 commands.norepo += b' %s' % b' '.join(parsealiases(name))
257 commands.norepo += b' %s' % b' '.join(parsealiases(name))
258 return _command(name, list(options), synopsis)
258 return _command(name, list(options), synopsis)
259
259
260
260
261 else:
261 else:
262 # for "historical portability":
262 # for "historical portability":
263 # define "@command" annotation locally, because cmdutil.command
263 # define "@command" annotation locally, because cmdutil.command
264 # has been available since 1.9 (or 2daa5179e73f)
264 # has been available since 1.9 (or 2daa5179e73f)
265 def command(name, options=(), synopsis=None, norepo=False):
265 def command(name, options=(), synopsis=None, norepo=False):
266 def decorator(func):
266 def decorator(func):
267 if synopsis:
267 if synopsis:
268 cmdtable[name] = func, list(options), synopsis
268 cmdtable[name] = func, list(options), synopsis
269 else:
269 else:
270 cmdtable[name] = func, list(options)
270 cmdtable[name] = func, list(options)
271 if norepo:
271 if norepo:
272 commands.norepo += b' %s' % b' '.join(parsealiases(name))
272 commands.norepo += b' %s' % b' '.join(parsealiases(name))
273 return func
273 return func
274
274
275 return decorator
275 return decorator
276
276
277
277
278 try:
278 try:
279 import mercurial.registrar
279 import mercurial.registrar
280 import mercurial.configitems
280 import mercurial.configitems
281
281
282 configtable = {}
282 configtable = {}
283 configitem = mercurial.registrar.configitem(configtable)
283 configitem = mercurial.registrar.configitem(configtable)
284 configitem(
284 configitem(
285 b'perf',
285 b'perf',
286 b'presleep',
286 b'presleep',
287 default=mercurial.configitems.dynamicdefault,
287 default=mercurial.configitems.dynamicdefault,
288 experimental=True,
288 experimental=True,
289 )
289 )
290 configitem(
290 configitem(
291 b'perf',
291 b'perf',
292 b'stub',
292 b'stub',
293 default=mercurial.configitems.dynamicdefault,
293 default=mercurial.configitems.dynamicdefault,
294 experimental=True,
294 experimental=True,
295 )
295 )
296 configitem(
296 configitem(
297 b'perf',
297 b'perf',
298 b'parentscount',
298 b'parentscount',
299 default=mercurial.configitems.dynamicdefault,
299 default=mercurial.configitems.dynamicdefault,
300 experimental=True,
300 experimental=True,
301 )
301 )
302 configitem(
302 configitem(
303 b'perf',
303 b'perf',
304 b'all-timing',
304 b'all-timing',
305 default=mercurial.configitems.dynamicdefault,
305 default=mercurial.configitems.dynamicdefault,
306 experimental=True,
306 experimental=True,
307 )
307 )
308 configitem(
308 configitem(
309 b'perf',
309 b'perf',
310 b'pre-run',
310 b'pre-run',
311 default=mercurial.configitems.dynamicdefault,
311 default=mercurial.configitems.dynamicdefault,
312 )
312 )
313 configitem(
313 configitem(
314 b'perf',
314 b'perf',
315 b'profile-benchmark',
315 b'profile-benchmark',
316 default=mercurial.configitems.dynamicdefault,
316 default=mercurial.configitems.dynamicdefault,
317 )
317 )
318 configitem(
318 configitem(
319 b'perf',
319 b'perf',
320 b'run-limits',
320 b'run-limits',
321 default=mercurial.configitems.dynamicdefault,
321 default=mercurial.configitems.dynamicdefault,
322 experimental=True,
322 experimental=True,
323 )
323 )
324 except (ImportError, AttributeError):
324 except (ImportError, AttributeError):
325 pass
325 pass
326 except TypeError:
326 except TypeError:
327 # compatibility fix for a11fd395e83f
327 # compatibility fix for a11fd395e83f
328 # hg version: 5.2
328 # hg version: 5.2
329 configitem(
329 configitem(
330 b'perf',
330 b'perf',
331 b'presleep',
331 b'presleep',
332 default=mercurial.configitems.dynamicdefault,
332 default=mercurial.configitems.dynamicdefault,
333 )
333 )
334 configitem(
334 configitem(
335 b'perf',
335 b'perf',
336 b'stub',
336 b'stub',
337 default=mercurial.configitems.dynamicdefault,
337 default=mercurial.configitems.dynamicdefault,
338 )
338 )
339 configitem(
339 configitem(
340 b'perf',
340 b'perf',
341 b'parentscount',
341 b'parentscount',
342 default=mercurial.configitems.dynamicdefault,
342 default=mercurial.configitems.dynamicdefault,
343 )
343 )
344 configitem(
344 configitem(
345 b'perf',
345 b'perf',
346 b'all-timing',
346 b'all-timing',
347 default=mercurial.configitems.dynamicdefault,
347 default=mercurial.configitems.dynamicdefault,
348 )
348 )
349 configitem(
349 configitem(
350 b'perf',
350 b'perf',
351 b'pre-run',
351 b'pre-run',
352 default=mercurial.configitems.dynamicdefault,
352 default=mercurial.configitems.dynamicdefault,
353 )
353 )
354 configitem(
354 configitem(
355 b'perf',
355 b'perf',
356 b'profile-benchmark',
356 b'profile-benchmark',
357 default=mercurial.configitems.dynamicdefault,
357 default=mercurial.configitems.dynamicdefault,
358 )
358 )
359 configitem(
359 configitem(
360 b'perf',
360 b'perf',
361 b'run-limits',
361 b'run-limits',
362 default=mercurial.configitems.dynamicdefault,
362 default=mercurial.configitems.dynamicdefault,
363 )
363 )
364
364
365
365
366 def getlen(ui):
366 def getlen(ui):
367 if ui.configbool(b"perf", b"stub", False):
367 if ui.configbool(b"perf", b"stub", False):
368 return lambda x: 1
368 return lambda x: 1
369 return len
369 return len
370
370
371
371
372 class noop:
372 class noop:
373 """dummy context manager"""
373 """dummy context manager"""
374
374
375 def __enter__(self):
375 def __enter__(self):
376 pass
376 pass
377
377
378 def __exit__(self, *args):
378 def __exit__(self, *args):
379 pass
379 pass
380
380
381
381
382 NOOPCTX = noop()
382 NOOPCTX = noop()
383
383
384
384
385 def gettimer(ui, opts=None):
385 def gettimer(ui, opts=None):
386 """return a timer function and formatter: (timer, formatter)
386 """return a timer function and formatter: (timer, formatter)
387
387
388 This function exists to gather the creation of formatter in a single
388 This function exists to gather the creation of formatter in a single
389 place instead of duplicating it in all performance commands."""
389 place instead of duplicating it in all performance commands."""
390
390
391 # enforce an idle period before execution to counteract power management
391 # enforce an idle period before execution to counteract power management
392 # experimental config: perf.presleep
392 # experimental config: perf.presleep
393 time.sleep(getint(ui, b"perf", b"presleep", 1))
393 time.sleep(getint(ui, b"perf", b"presleep", 1))
394
394
395 if opts is None:
395 if opts is None:
396 opts = {}
396 opts = {}
397 # redirect all to stderr unless buffer api is in use
397 # redirect all to stderr unless buffer api is in use
398 if not ui._buffers:
398 if not ui._buffers:
399 ui = ui.copy()
399 ui = ui.copy()
400 uifout = safeattrsetter(ui, b'fout', ignoremissing=True)
400 uifout = safeattrsetter(ui, b'fout', ignoremissing=True)
401 if uifout:
401 if uifout:
402 # for "historical portability":
402 # for "historical portability":
403 # ui.fout/ferr have been available since 1.9 (or 4e1ccd4c2b6d)
403 # ui.fout/ferr have been available since 1.9 (or 4e1ccd4c2b6d)
404 uifout.set(ui.ferr)
404 uifout.set(ui.ferr)
405
405
406 # get a formatter
406 # get a formatter
407 uiformatter = getattr(ui, 'formatter', None)
407 uiformatter = getattr(ui, 'formatter', None)
408 if uiformatter:
408 if uiformatter:
409 fm = uiformatter(b'perf', opts)
409 fm = uiformatter(b'perf', opts)
410 else:
410 else:
411 # for "historical portability":
411 # for "historical portability":
412 # define formatter locally, because ui.formatter has been
412 # define formatter locally, because ui.formatter has been
413 # available since 2.2 (or ae5f92e154d3)
413 # available since 2.2 (or ae5f92e154d3)
414 from mercurial import node
414 from mercurial import node
415
415
416 class defaultformatter:
416 class defaultformatter:
417 """Minimized composition of baseformatter and plainformatter"""
417 """Minimized composition of baseformatter and plainformatter"""
418
418
419 def __init__(self, ui, topic, opts):
419 def __init__(self, ui, topic, opts):
420 self._ui = ui
420 self._ui = ui
421 if ui.debugflag:
421 if ui.debugflag:
422 self.hexfunc = node.hex
422 self.hexfunc = node.hex
423 else:
423 else:
424 self.hexfunc = node.short
424 self.hexfunc = node.short
425
425
426 def __nonzero__(self):
426 def __nonzero__(self):
427 return False
427 return False
428
428
429 __bool__ = __nonzero__
429 __bool__ = __nonzero__
430
430
431 def startitem(self):
431 def startitem(self):
432 pass
432 pass
433
433
434 def data(self, **data):
434 def data(self, **data):
435 pass
435 pass
436
436
437 def write(self, fields, deftext, *fielddata, **opts):
437 def write(self, fields, deftext, *fielddata, **opts):
438 self._ui.write(deftext % fielddata, **opts)
438 self._ui.write(deftext % fielddata, **opts)
439
439
440 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
440 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
441 if cond:
441 if cond:
442 self._ui.write(deftext % fielddata, **opts)
442 self._ui.write(deftext % fielddata, **opts)
443
443
444 def plain(self, text, **opts):
444 def plain(self, text, **opts):
445 self._ui.write(text, **opts)
445 self._ui.write(text, **opts)
446
446
447 def end(self):
447 def end(self):
448 pass
448 pass
449
449
450 fm = defaultformatter(ui, b'perf', opts)
450 fm = defaultformatter(ui, b'perf', opts)
451
451
452 # stub function, runs code only once instead of in a loop
452 # stub function, runs code only once instead of in a loop
453 # experimental config: perf.stub
453 # experimental config: perf.stub
454 if ui.configbool(b"perf", b"stub", False):
454 if ui.configbool(b"perf", b"stub", False):
455 return functools.partial(stub_timer, fm), fm
455 return functools.partial(stub_timer, fm), fm
456
456
457 # experimental config: perf.all-timing
457 # experimental config: perf.all-timing
458 displayall = ui.configbool(b"perf", b"all-timing", False)
458 displayall = ui.configbool(b"perf", b"all-timing", False)
459
459
460 # experimental config: perf.run-limits
460 # experimental config: perf.run-limits
461 limitspec = ui.configlist(b"perf", b"run-limits", [])
461 limitspec = ui.configlist(b"perf", b"run-limits", [])
462 limits = []
462 limits = []
463 for item in limitspec:
463 for item in limitspec:
464 parts = item.split(b'-', 1)
464 parts = item.split(b'-', 1)
465 if len(parts) < 2:
465 if len(parts) < 2:
466 ui.warn((b'malformatted run limit entry, missing "-": %s\n' % item))
466 ui.warn((b'malformatted run limit entry, missing "-": %s\n' % item))
467 continue
467 continue
468 try:
468 try:
469 time_limit = float(_sysstr(parts[0]))
469 time_limit = float(_sysstr(parts[0]))
470 except ValueError as e:
470 except ValueError as e:
471 ui.warn(
471 ui.warn(
472 (
472 (
473 b'malformatted run limit entry, %s: %s\n'
473 b'malformatted run limit entry, %s: %s\n'
474 % (_bytestr(e), item)
474 % (_bytestr(e), item)
475 )
475 )
476 )
476 )
477 continue
477 continue
478 try:
478 try:
479 run_limit = int(_sysstr(parts[1]))
479 run_limit = int(_sysstr(parts[1]))
480 except ValueError as e:
480 except ValueError as e:
481 ui.warn(
481 ui.warn(
482 (
482 (
483 b'malformatted run limit entry, %s: %s\n'
483 b'malformatted run limit entry, %s: %s\n'
484 % (_bytestr(e), item)
484 % (_bytestr(e), item)
485 )
485 )
486 )
486 )
487 continue
487 continue
488 limits.append((time_limit, run_limit))
488 limits.append((time_limit, run_limit))
489 if not limits:
489 if not limits:
490 limits = DEFAULTLIMITS
490 limits = DEFAULTLIMITS
491
491
492 profiler = None
492 profiler = None
493 if profiling is not None:
493 if profiling is not None:
494 if ui.configbool(b"perf", b"profile-benchmark", False):
494 if ui.configbool(b"perf", b"profile-benchmark", False):
495 profiler = profiling.profile(ui)
495 profiler = profiling.profile(ui)
496
496
497 prerun = getint(ui, b"perf", b"pre-run", 0)
497 prerun = getint(ui, b"perf", b"pre-run", 0)
498 t = functools.partial(
498 t = functools.partial(
499 _timer,
499 _timer,
500 fm,
500 fm,
501 displayall=displayall,
501 displayall=displayall,
502 limits=limits,
502 limits=limits,
503 prerun=prerun,
503 prerun=prerun,
504 profiler=profiler,
504 profiler=profiler,
505 )
505 )
506 return t, fm
506 return t, fm
507
507
508
508
509 def stub_timer(fm, func, setup=None, title=None):
509 def stub_timer(fm, func, setup=None, title=None):
510 if setup is not None:
510 if setup is not None:
511 setup()
511 setup()
512 func()
512 func()
513
513
514
514
515 @contextlib.contextmanager
515 @contextlib.contextmanager
516 def timeone():
516 def timeone():
517 r = []
517 r = []
518 ostart = os.times()
518 ostart = os.times()
519 cstart = util.timer()
519 cstart = util.timer()
520 yield r
520 yield r
521 cstop = util.timer()
521 cstop = util.timer()
522 ostop = os.times()
522 ostop = os.times()
523 a, b = ostart, ostop
523 a, b = ostart, ostop
524 r.append((cstop - cstart, b[0] - a[0], b[1] - a[1]))
524 r.append((cstop - cstart, b[0] - a[0], b[1] - a[1]))
525
525
526
526
527 # list of stop condition (elapsed time, minimal run count)
527 # list of stop condition (elapsed time, minimal run count)
528 DEFAULTLIMITS = (
528 DEFAULTLIMITS = (
529 (3.0, 100),
529 (3.0, 100),
530 (10.0, 3),
530 (10.0, 3),
531 )
531 )
532
532
533
533
534 def _timer(
534 def _timer(
535 fm,
535 fm,
536 func,
536 func,
537 setup=None,
537 setup=None,
538 title=None,
538 title=None,
539 displayall=False,
539 displayall=False,
540 limits=DEFAULTLIMITS,
540 limits=DEFAULTLIMITS,
541 prerun=0,
541 prerun=0,
542 profiler=None,
542 profiler=None,
543 ):
543 ):
544 gc.collect()
544 gc.collect()
545 results = []
545 results = []
546 begin = util.timer()
546 begin = util.timer()
547 count = 0
547 count = 0
548 if profiler is None:
548 if profiler is None:
549 profiler = NOOPCTX
549 profiler = NOOPCTX
550 for i in range(prerun):
550 for i in range(prerun):
551 if setup is not None:
551 if setup is not None:
552 setup()
552 setup()
553 func()
553 func()
554 keepgoing = True
554 keepgoing = True
555 while keepgoing:
555 while keepgoing:
556 if setup is not None:
556 if setup is not None:
557 setup()
557 setup()
558 with profiler:
558 with profiler:
559 with timeone() as item:
559 with timeone() as item:
560 r = func()
560 r = func()
561 profiler = NOOPCTX
561 profiler = NOOPCTX
562 count += 1
562 count += 1
563 results.append(item[0])
563 results.append(item[0])
564 cstop = util.timer()
564 cstop = util.timer()
565 # Look for a stop condition.
565 # Look for a stop condition.
566 elapsed = cstop - begin
566 elapsed = cstop - begin
567 for t, mincount in limits:
567 for t, mincount in limits:
568 if elapsed >= t and count >= mincount:
568 if elapsed >= t and count >= mincount:
569 keepgoing = False
569 keepgoing = False
570 break
570 break
571
571
572 formatone(fm, results, title=title, result=r, displayall=displayall)
572 formatone(fm, results, title=title, result=r, displayall=displayall)
573
573
574
574
575 def formatone(fm, timings, title=None, result=None, displayall=False):
575 def formatone(fm, timings, title=None, result=None, displayall=False):
576
576
577 count = len(timings)
577 count = len(timings)
578
578
579 fm.startitem()
579 fm.startitem()
580
580
581 if title:
581 if title:
582 fm.write(b'title', b'! %s\n', title)
582 fm.write(b'title', b'! %s\n', title)
583 if result:
583 if result:
584 fm.write(b'result', b'! result: %s\n', result)
584 fm.write(b'result', b'! result: %s\n', result)
585
585
586 def display(role, entry):
586 def display(role, entry):
587 prefix = b''
587 prefix = b''
588 if role != b'best':
588 if role != b'best':
589 prefix = b'%s.' % role
589 prefix = b'%s.' % role
590 fm.plain(b'!')
590 fm.plain(b'!')
591 fm.write(prefix + b'wall', b' wall %f', entry[0])
591 fm.write(prefix + b'wall', b' wall %f', entry[0])
592 fm.write(prefix + b'comb', b' comb %f', entry[1] + entry[2])
592 fm.write(prefix + b'comb', b' comb %f', entry[1] + entry[2])
593 fm.write(prefix + b'user', b' user %f', entry[1])
593 fm.write(prefix + b'user', b' user %f', entry[1])
594 fm.write(prefix + b'sys', b' sys %f', entry[2])
594 fm.write(prefix + b'sys', b' sys %f', entry[2])
595 fm.write(prefix + b'count', b' (%s of %%d)' % role, count)
595 fm.write(prefix + b'count', b' (%s of %%d)' % role, count)
596 fm.plain(b'\n')
596 fm.plain(b'\n')
597
597
598 timings.sort()
598 timings.sort()
599 min_val = timings[0]
599 min_val = timings[0]
600 display(b'best', min_val)
600 display(b'best', min_val)
601 if displayall:
601 if displayall:
602 max_val = timings[-1]
602 max_val = timings[-1]
603 display(b'max', max_val)
603 display(b'max', max_val)
604 avg = tuple([sum(x) / count for x in zip(*timings)])
604 avg = tuple([sum(x) / count for x in zip(*timings)])
605 display(b'avg', avg)
605 display(b'avg', avg)
606 median = timings[len(timings) // 2]
606 median = timings[len(timings) // 2]
607 display(b'median', median)
607 display(b'median', median)
608
608
609
609
610 # utilities for historical portability
610 # utilities for historical portability
611
611
612
612
613 def getint(ui, section, name, default):
613 def getint(ui, section, name, default):
614 # for "historical portability":
614 # for "historical portability":
615 # ui.configint has been available since 1.9 (or fa2b596db182)
615 # ui.configint has been available since 1.9 (or fa2b596db182)
616 v = ui.config(section, name, None)
616 v = ui.config(section, name, None)
617 if v is None:
617 if v is None:
618 return default
618 return default
619 try:
619 try:
620 return int(v)
620 return int(v)
621 except ValueError:
621 except ValueError:
622 raise error.ConfigError(
622 raise error.ConfigError(
623 b"%s.%s is not an integer ('%s')" % (section, name, v)
623 b"%s.%s is not an integer ('%s')" % (section, name, v)
624 )
624 )
625
625
626
626
627 def safeattrsetter(obj, name, ignoremissing=False):
627 def safeattrsetter(obj, name, ignoremissing=False):
628 """Ensure that 'obj' has 'name' attribute before subsequent setattr
628 """Ensure that 'obj' has 'name' attribute before subsequent setattr
629
629
630 This function is aborted, if 'obj' doesn't have 'name' attribute
630 This function is aborted, if 'obj' doesn't have 'name' attribute
631 at runtime. This avoids overlooking removal of an attribute, which
631 at runtime. This avoids overlooking removal of an attribute, which
632 breaks assumption of performance measurement, in the future.
632 breaks assumption of performance measurement, in the future.
633
633
634 This function returns the object to (1) assign a new value, and
634 This function returns the object to (1) assign a new value, and
635 (2) restore an original value to the attribute.
635 (2) restore an original value to the attribute.
636
636
637 If 'ignoremissing' is true, missing 'name' attribute doesn't cause
637 If 'ignoremissing' is true, missing 'name' attribute doesn't cause
638 abortion, and this function returns None. This is useful to
638 abortion, and this function returns None. This is useful to
639 examine an attribute, which isn't ensured in all Mercurial
639 examine an attribute, which isn't ensured in all Mercurial
640 versions.
640 versions.
641 """
641 """
642 if not util.safehasattr(obj, name):
642 if not util.safehasattr(obj, name):
643 if ignoremissing:
643 if ignoremissing:
644 return None
644 return None
645 raise error.Abort(
645 raise error.Abort(
646 (
646 (
647 b"missing attribute %s of %s might break assumption"
647 b"missing attribute %s of %s might break assumption"
648 b" of performance measurement"
648 b" of performance measurement"
649 )
649 )
650 % (name, obj)
650 % (name, obj)
651 )
651 )
652
652
653 origvalue = getattr(obj, _sysstr(name))
653 origvalue = getattr(obj, _sysstr(name))
654
654
655 class attrutil:
655 class attrutil:
656 def set(self, newvalue):
656 def set(self, newvalue):
657 setattr(obj, _sysstr(name), newvalue)
657 setattr(obj, _sysstr(name), newvalue)
658
658
659 def restore(self):
659 def restore(self):
660 setattr(obj, _sysstr(name), origvalue)
660 setattr(obj, _sysstr(name), origvalue)
661
661
662 return attrutil()
662 return attrutil()
663
663
664
664
665 # utilities to examine each internal API changes
665 # utilities to examine each internal API changes
666
666
667
667
668 def getbranchmapsubsettable():
668 def getbranchmapsubsettable():
669 # for "historical portability":
669 # for "historical portability":
670 # subsettable is defined in:
670 # subsettable is defined in:
671 # - branchmap since 2.9 (or 175c6fd8cacc)
671 # - branchmap since 2.9 (or 175c6fd8cacc)
672 # - repoview since 2.5 (or 59a9f18d4587)
672 # - repoview since 2.5 (or 59a9f18d4587)
673 # - repoviewutil since 5.0
673 # - repoviewutil since 5.0
674 for mod in (branchmap, repoview, repoviewutil):
674 for mod in (branchmap, repoview, repoviewutil):
675 subsettable = getattr(mod, 'subsettable', None)
675 subsettable = getattr(mod, 'subsettable', None)
676 if subsettable:
676 if subsettable:
677 return subsettable
677 return subsettable
678
678
679 # bisecting in bcee63733aad::59a9f18d4587 can reach here (both
679 # bisecting in bcee63733aad::59a9f18d4587 can reach here (both
680 # branchmap and repoview modules exist, but subsettable attribute
680 # branchmap and repoview modules exist, but subsettable attribute
681 # doesn't)
681 # doesn't)
682 raise error.Abort(
682 raise error.Abort(
683 b"perfbranchmap not available with this Mercurial",
683 b"perfbranchmap not available with this Mercurial",
684 hint=b"use 2.5 or later",
684 hint=b"use 2.5 or later",
685 )
685 )
686
686
687
687
688 def getsvfs(repo):
688 def getsvfs(repo):
689 """Return appropriate object to access files under .hg/store"""
689 """Return appropriate object to access files under .hg/store"""
690 # for "historical portability":
690 # for "historical portability":
691 # repo.svfs has been available since 2.3 (or 7034365089bf)
691 # repo.svfs has been available since 2.3 (or 7034365089bf)
692 svfs = getattr(repo, 'svfs', None)
692 svfs = getattr(repo, 'svfs', None)
693 if svfs:
693 if svfs:
694 return svfs
694 return svfs
695 else:
695 else:
696 return getattr(repo, 'sopener')
696 return getattr(repo, 'sopener')
697
697
698
698
699 def getvfs(repo):
699 def getvfs(repo):
700 """Return appropriate object to access files under .hg"""
700 """Return appropriate object to access files under .hg"""
701 # for "historical portability":
701 # for "historical portability":
702 # repo.vfs has been available since 2.3 (or 7034365089bf)
702 # repo.vfs has been available since 2.3 (or 7034365089bf)
703 vfs = getattr(repo, 'vfs', None)
703 vfs = getattr(repo, 'vfs', None)
704 if vfs:
704 if vfs:
705 return vfs
705 return vfs
706 else:
706 else:
707 return getattr(repo, 'opener')
707 return getattr(repo, 'opener')
708
708
709
709
710 def repocleartagscachefunc(repo):
710 def repocleartagscachefunc(repo):
711 """Return the function to clear tags cache according to repo internal API"""
711 """Return the function to clear tags cache according to repo internal API"""
712 if util.safehasattr(repo, b'_tagscache'): # since 2.0 (or 9dca7653b525)
712 if util.safehasattr(repo, b'_tagscache'): # since 2.0 (or 9dca7653b525)
713 # in this case, setattr(repo, '_tagscache', None) or so isn't
713 # in this case, setattr(repo, '_tagscache', None) or so isn't
714 # correct way to clear tags cache, because existing code paths
714 # correct way to clear tags cache, because existing code paths
715 # expect _tagscache to be a structured object.
715 # expect _tagscache to be a structured object.
716 def clearcache():
716 def clearcache():
717 # _tagscache has been filteredpropertycache since 2.5 (or
717 # _tagscache has been filteredpropertycache since 2.5 (or
718 # 98c867ac1330), and delattr() can't work in such case
718 # 98c867ac1330), and delattr() can't work in such case
719 if '_tagscache' in vars(repo):
719 if '_tagscache' in vars(repo):
720 del repo.__dict__['_tagscache']
720 del repo.__dict__['_tagscache']
721
721
722 return clearcache
722 return clearcache
723
723
724 repotags = safeattrsetter(repo, b'_tags', ignoremissing=True)
724 repotags = safeattrsetter(repo, b'_tags', ignoremissing=True)
725 if repotags: # since 1.4 (or 5614a628d173)
725 if repotags: # since 1.4 (or 5614a628d173)
726 return lambda: repotags.set(None)
726 return lambda: repotags.set(None)
727
727
728 repotagscache = safeattrsetter(repo, b'tagscache', ignoremissing=True)
728 repotagscache = safeattrsetter(repo, b'tagscache', ignoremissing=True)
729 if repotagscache: # since 0.6 (or d7df759d0e97)
729 if repotagscache: # since 0.6 (or d7df759d0e97)
730 return lambda: repotagscache.set(None)
730 return lambda: repotagscache.set(None)
731
731
732 # Mercurial earlier than 0.6 (or d7df759d0e97) logically reaches
732 # Mercurial earlier than 0.6 (or d7df759d0e97) logically reaches
733 # this point, but it isn't so problematic, because:
733 # this point, but it isn't so problematic, because:
734 # - repo.tags of such Mercurial isn't "callable", and repo.tags()
734 # - repo.tags of such Mercurial isn't "callable", and repo.tags()
735 # in perftags() causes failure soon
735 # in perftags() causes failure soon
736 # - perf.py itself has been available since 1.1 (or eb240755386d)
736 # - perf.py itself has been available since 1.1 (or eb240755386d)
737 raise error.Abort(b"tags API of this hg command is unknown")
737 raise error.Abort(b"tags API of this hg command is unknown")
738
738
739
739
740 # utilities to clear cache
740 # utilities to clear cache
741
741
742
742
743 def clearfilecache(obj, attrname):
743 def clearfilecache(obj, attrname):
744 unfiltered = getattr(obj, 'unfiltered', None)
744 unfiltered = getattr(obj, 'unfiltered', None)
745 if unfiltered is not None:
745 if unfiltered is not None:
746 obj = obj.unfiltered()
746 obj = obj.unfiltered()
747 if attrname in vars(obj):
747 if attrname in vars(obj):
748 delattr(obj, attrname)
748 delattr(obj, attrname)
749 obj._filecache.pop(attrname, None)
749 obj._filecache.pop(attrname, None)
750
750
751
751
752 def clearchangelog(repo):
752 def clearchangelog(repo):
753 if repo is not repo.unfiltered():
753 if repo is not repo.unfiltered():
754 object.__setattr__(repo, '_clcachekey', None)
754 object.__setattr__(repo, '_clcachekey', None)
755 object.__setattr__(repo, '_clcache', None)
755 object.__setattr__(repo, '_clcache', None)
756 clearfilecache(repo.unfiltered(), 'changelog')
756 clearfilecache(repo.unfiltered(), 'changelog')
757
757
758
758
759 # perf commands
759 # perf commands
760
760
761
761
762 @command(b'perf::walk|perfwalk', formatteropts)
762 @command(b'perf::walk|perfwalk', formatteropts)
763 def perfwalk(ui, repo, *pats, **opts):
763 def perfwalk(ui, repo, *pats, **opts):
764 opts = _byteskwargs(opts)
764 opts = _byteskwargs(opts)
765 timer, fm = gettimer(ui, opts)
765 timer, fm = gettimer(ui, opts)
766 m = scmutil.match(repo[None], pats, {})
766 m = scmutil.match(repo[None], pats, {})
767 timer(
767 timer(
768 lambda: len(
768 lambda: len(
769 list(
769 list(
770 repo.dirstate.walk(m, subrepos=[], unknown=True, ignored=False)
770 repo.dirstate.walk(m, subrepos=[], unknown=True, ignored=False)
771 )
771 )
772 )
772 )
773 )
773 )
774 fm.end()
774 fm.end()
775
775
776
776
777 @command(b'perf::annotate|perfannotate', formatteropts)
777 @command(b'perf::annotate|perfannotate', formatteropts)
778 def perfannotate(ui, repo, f, **opts):
778 def perfannotate(ui, repo, f, **opts):
779 opts = _byteskwargs(opts)
779 opts = _byteskwargs(opts)
780 timer, fm = gettimer(ui, opts)
780 timer, fm = gettimer(ui, opts)
781 fc = repo[b'.'][f]
781 fc = repo[b'.'][f]
782 timer(lambda: len(fc.annotate(True)))
782 timer(lambda: len(fc.annotate(True)))
783 fm.end()
783 fm.end()
784
784
785
785
786 @command(
786 @command(
787 b'perf::status|perfstatus',
787 b'perf::status|perfstatus',
788 [
788 [
789 (b'u', b'unknown', False, b'ask status to look for unknown files'),
789 (b'u', b'unknown', False, b'ask status to look for unknown files'),
790 (b'', b'dirstate', False, b'benchmark the internal dirstate call'),
790 (b'', b'dirstate', False, b'benchmark the internal dirstate call'),
791 ]
791 ]
792 + formatteropts,
792 + formatteropts,
793 )
793 )
794 def perfstatus(ui, repo, **opts):
794 def perfstatus(ui, repo, **opts):
795 """benchmark the performance of a single status call
795 """benchmark the performance of a single status call
796
796
797 The repository data are preserved between each call.
797 The repository data are preserved between each call.
798
798
799 By default, only the status of the tracked file are requested. If
799 By default, only the status of the tracked file are requested. If
800 `--unknown` is passed, the "unknown" files are also tracked.
800 `--unknown` is passed, the "unknown" files are also tracked.
801 """
801 """
802 opts = _byteskwargs(opts)
802 opts = _byteskwargs(opts)
803 # m = match.always(repo.root, repo.getcwd())
803 # m = match.always(repo.root, repo.getcwd())
804 # timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
804 # timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
805 # False))))
805 # False))))
806 timer, fm = gettimer(ui, opts)
806 timer, fm = gettimer(ui, opts)
807 if opts[b'dirstate']:
807 if opts[b'dirstate']:
808 dirstate = repo.dirstate
808 dirstate = repo.dirstate
809 m = scmutil.matchall(repo)
809 m = scmutil.matchall(repo)
810 unknown = opts[b'unknown']
810 unknown = opts[b'unknown']
811
811
812 def status_dirstate():
812 def status_dirstate():
813 s = dirstate.status(
813 s = dirstate.status(
814 m, subrepos=[], ignored=False, clean=False, unknown=unknown
814 m, subrepos=[], ignored=False, clean=False, unknown=unknown
815 )
815 )
816 sum(map(bool, s))
816 sum(map(bool, s))
817
817
818 timer(status_dirstate)
818 timer(status_dirstate)
819 else:
819 else:
820 timer(lambda: sum(map(len, repo.status(unknown=opts[b'unknown']))))
820 timer(lambda: sum(map(len, repo.status(unknown=opts[b'unknown']))))
821 fm.end()
821 fm.end()
822
822
823
823
824 @command(b'perf::addremove|perfaddremove', formatteropts)
824 @command(b'perf::addremove|perfaddremove', formatteropts)
825 def perfaddremove(ui, repo, **opts):
825 def perfaddremove(ui, repo, **opts):
826 opts = _byteskwargs(opts)
826 opts = _byteskwargs(opts)
827 timer, fm = gettimer(ui, opts)
827 timer, fm = gettimer(ui, opts)
828 try:
828 try:
829 oldquiet = repo.ui.quiet
829 oldquiet = repo.ui.quiet
830 repo.ui.quiet = True
830 repo.ui.quiet = True
831 matcher = scmutil.match(repo[None])
831 matcher = scmutil.match(repo[None])
832 opts[b'dry_run'] = True
832 opts[b'dry_run'] = True
833 if 'uipathfn' in getargspec(scmutil.addremove).args:
833 if 'uipathfn' in getargspec(scmutil.addremove).args:
834 uipathfn = scmutil.getuipathfn(repo)
834 uipathfn = scmutil.getuipathfn(repo)
835 timer(lambda: scmutil.addremove(repo, matcher, b"", uipathfn, opts))
835 timer(lambda: scmutil.addremove(repo, matcher, b"", uipathfn, opts))
836 else:
836 else:
837 timer(lambda: scmutil.addremove(repo, matcher, b"", opts))
837 timer(lambda: scmutil.addremove(repo, matcher, b"", opts))
838 finally:
838 finally:
839 repo.ui.quiet = oldquiet
839 repo.ui.quiet = oldquiet
840 fm.end()
840 fm.end()
841
841
842
842
843 def clearcaches(cl):
843 def clearcaches(cl):
844 # behave somewhat consistently across internal API changes
844 # behave somewhat consistently across internal API changes
845 if util.safehasattr(cl, b'clearcaches'):
845 if util.safehasattr(cl, b'clearcaches'):
846 cl.clearcaches()
846 cl.clearcaches()
847 elif util.safehasattr(cl, b'_nodecache'):
847 elif util.safehasattr(cl, b'_nodecache'):
848 # <= hg-5.2
848 # <= hg-5.2
849 from mercurial.node import nullid, nullrev
849 from mercurial.node import nullid, nullrev
850
850
851 cl._nodecache = {nullid: nullrev}
851 cl._nodecache = {nullid: nullrev}
852 cl._nodepos = None
852 cl._nodepos = None
853
853
854
854
855 @command(b'perf::heads|perfheads', formatteropts)
855 @command(b'perf::heads|perfheads', formatteropts)
856 def perfheads(ui, repo, **opts):
856 def perfheads(ui, repo, **opts):
857 """benchmark the computation of a changelog heads"""
857 """benchmark the computation of a changelog heads"""
858 opts = _byteskwargs(opts)
858 opts = _byteskwargs(opts)
859 timer, fm = gettimer(ui, opts)
859 timer, fm = gettimer(ui, opts)
860 cl = repo.changelog
860 cl = repo.changelog
861
861
862 def s():
862 def s():
863 clearcaches(cl)
863 clearcaches(cl)
864
864
865 def d():
865 def d():
866 len(cl.headrevs())
866 len(cl.headrevs())
867
867
868 timer(d, setup=s)
868 timer(d, setup=s)
869 fm.end()
869 fm.end()
870
870
871
871
872 @command(
872 @command(
873 b'perf::tags|perftags',
873 b'perf::tags|perftags',
874 formatteropts
874 formatteropts
875 + [
875 + [
876 (b'', b'clear-revlogs', False, b'refresh changelog and manifest'),
876 (b'', b'clear-revlogs', False, b'refresh changelog and manifest'),
877 ],
877 ],
878 )
878 )
879 def perftags(ui, repo, **opts):
879 def perftags(ui, repo, **opts):
880 opts = _byteskwargs(opts)
880 opts = _byteskwargs(opts)
881 timer, fm = gettimer(ui, opts)
881 timer, fm = gettimer(ui, opts)
882 repocleartagscache = repocleartagscachefunc(repo)
882 repocleartagscache = repocleartagscachefunc(repo)
883 clearrevlogs = opts[b'clear_revlogs']
883 clearrevlogs = opts[b'clear_revlogs']
884
884
885 def s():
885 def s():
886 if clearrevlogs:
886 if clearrevlogs:
887 clearchangelog(repo)
887 clearchangelog(repo)
888 clearfilecache(repo.unfiltered(), 'manifest')
888 clearfilecache(repo.unfiltered(), 'manifest')
889 repocleartagscache()
889 repocleartagscache()
890
890
891 def t():
891 def t():
892 return len(repo.tags())
892 return len(repo.tags())
893
893
894 timer(t, setup=s)
894 timer(t, setup=s)
895 fm.end()
895 fm.end()
896
896
897
897
898 @command(b'perf::ancestors|perfancestors', formatteropts)
898 @command(b'perf::ancestors|perfancestors', formatteropts)
899 def perfancestors(ui, repo, **opts):
899 def perfancestors(ui, repo, **opts):
900 opts = _byteskwargs(opts)
900 opts = _byteskwargs(opts)
901 timer, fm = gettimer(ui, opts)
901 timer, fm = gettimer(ui, opts)
902 heads = repo.changelog.headrevs()
902 heads = repo.changelog.headrevs()
903
903
904 def d():
904 def d():
905 for a in repo.changelog.ancestors(heads):
905 for a in repo.changelog.ancestors(heads):
906 pass
906 pass
907
907
908 timer(d)
908 timer(d)
909 fm.end()
909 fm.end()
910
910
911
911
912 @command(b'perf::ancestorset|perfancestorset', formatteropts)
912 @command(b'perf::ancestorset|perfancestorset', formatteropts)
913 def perfancestorset(ui, repo, revset, **opts):
913 def perfancestorset(ui, repo, revset, **opts):
914 opts = _byteskwargs(opts)
914 opts = _byteskwargs(opts)
915 timer, fm = gettimer(ui, opts)
915 timer, fm = gettimer(ui, opts)
916 revs = repo.revs(revset)
916 revs = repo.revs(revset)
917 heads = repo.changelog.headrevs()
917 heads = repo.changelog.headrevs()
918
918
919 def d():
919 def d():
920 s = repo.changelog.ancestors(heads)
920 s = repo.changelog.ancestors(heads)
921 for rev in revs:
921 for rev in revs:
922 rev in s
922 rev in s
923
923
924 timer(d)
924 timer(d)
925 fm.end()
925 fm.end()
926
926
927
927
928 @command(b'perf::discovery|perfdiscovery', formatteropts, b'PATH')
928 @command(b'perf::discovery|perfdiscovery', formatteropts, b'PATH')
929 def perfdiscovery(ui, repo, path, **opts):
929 def perfdiscovery(ui, repo, path, **opts):
930 """benchmark discovery between local repo and the peer at given path"""
930 """benchmark discovery between local repo and the peer at given path"""
931 repos = [repo, None]
931 repos = [repo, None]
932 timer, fm = gettimer(ui, opts)
932 timer, fm = gettimer(ui, opts)
933
933
934 try:
934 try:
935 from mercurial.utils.urlutil import get_unique_pull_path
935 from mercurial.utils.urlutil import get_unique_pull_path
936
936
937 path = get_unique_pull_path(b'perfdiscovery', repo, ui, path)[0]
937 path = get_unique_pull_path(b'perfdiscovery', repo, ui, path)[0]
938 except ImportError:
938 except ImportError:
939 path = ui.expandpath(path)
939 path = ui.expandpath(path)
940
940
941 def s():
941 def s():
942 repos[1] = hg.peer(ui, opts, path)
942 repos[1] = hg.peer(ui, opts, path)
943
943
944 def d():
944 def d():
945 setdiscovery.findcommonheads(ui, *repos)
945 setdiscovery.findcommonheads(ui, *repos)
946
946
947 timer(d, setup=s)
947 timer(d, setup=s)
948 fm.end()
948 fm.end()
949
949
950
950
951 @command(
951 @command(
952 b'perf::bookmarks|perfbookmarks',
952 b'perf::bookmarks|perfbookmarks',
953 formatteropts
953 formatteropts
954 + [
954 + [
955 (b'', b'clear-revlogs', False, b'refresh changelog and manifest'),
955 (b'', b'clear-revlogs', False, b'refresh changelog and manifest'),
956 ],
956 ],
957 )
957 )
958 def perfbookmarks(ui, repo, **opts):
958 def perfbookmarks(ui, repo, **opts):
959 """benchmark parsing bookmarks from disk to memory"""
959 """benchmark parsing bookmarks from disk to memory"""
960 opts = _byteskwargs(opts)
960 opts = _byteskwargs(opts)
961 timer, fm = gettimer(ui, opts)
961 timer, fm = gettimer(ui, opts)
962
962
963 clearrevlogs = opts[b'clear_revlogs']
963 clearrevlogs = opts[b'clear_revlogs']
964
964
965 def s():
965 def s():
966 if clearrevlogs:
966 if clearrevlogs:
967 clearchangelog(repo)
967 clearchangelog(repo)
968 clearfilecache(repo, b'_bookmarks')
968 clearfilecache(repo, b'_bookmarks')
969
969
970 def d():
970 def d():
971 repo._bookmarks
971 repo._bookmarks
972
972
973 timer(d, setup=s)
973 timer(d, setup=s)
974 fm.end()
974 fm.end()
975
975
976
976
977 @command(
977 @command(
978 b'perf::bundle',
978 b'perf::bundle',
979 [
979 [
980 (
980 (
981 b'r',
981 b'r',
982 b'rev',
982 b'rev',
983 [],
983 [],
984 b'changesets to bundle',
984 b'changesets to bundle',
985 b'REV',
985 b'REV',
986 ),
986 ),
987 (
988 b't',
989 b'type',
990 b'none',
991 b'bundlespec to use (see `hg help bundlespec`)',
992 b'TYPE',
993 ),
987 ]
994 ]
988 + formatteropts,
995 + formatteropts,
989 b'REVS',
996 b'REVS',
990 )
997 )
991 def perfbundle(ui, repo, *revs, **opts):
998 def perfbundle(ui, repo, *revs, **opts):
992 """benchmark the creation of a bundle from a repository
999 """benchmark the creation of a bundle from a repository
993
1000
994 For now, this create a `none-v1` bundle.
1001 For now, this only supports "none" compression.
995 """
1002 """
996 from mercurial import bundlecaches
1003 from mercurial import bundlecaches
997 from mercurial import discovery
1004 from mercurial import discovery
998 from mercurial import bundle2
1005 from mercurial import bundle2
999
1006
1000 opts = _byteskwargs(opts)
1007 opts = _byteskwargs(opts)
1001 timer, fm = gettimer(ui, opts)
1008 timer, fm = gettimer(ui, opts)
1002
1009
1003 cl = repo.changelog
1010 cl = repo.changelog
1004 revs = list(revs)
1011 revs = list(revs)
1005 revs.extend(opts.get(b'rev', ()))
1012 revs.extend(opts.get(b'rev', ()))
1006 revs = scmutil.revrange(repo, revs)
1013 revs = scmutil.revrange(repo, revs)
1007 if not revs:
1014 if not revs:
1008 raise error.Abort(b"not revision specified")
1015 raise error.Abort(b"not revision specified")
1009 # make it a consistent set (ie: without topological gaps)
1016 # make it a consistent set (ie: without topological gaps)
1010 old_len = len(revs)
1017 old_len = len(revs)
1011 revs = list(repo.revs(b"%ld::%ld", revs, revs))
1018 revs = list(repo.revs(b"%ld::%ld", revs, revs))
1012 if old_len != len(revs):
1019 if old_len != len(revs):
1013 new_count = len(revs) - old_len
1020 new_count = len(revs) - old_len
1014 msg = b"add %d new revisions to make it a consistent set\n"
1021 msg = b"add %d new revisions to make it a consistent set\n"
1015 ui.write_err(msg % new_count)
1022 ui.write_err(msg % new_count)
1016
1023
1017 targets = [cl.node(r) for r in repo.revs(b"heads(::%ld)", revs)]
1024 targets = [cl.node(r) for r in repo.revs(b"heads(::%ld)", revs)]
1018 bases = [cl.node(r) for r in repo.revs(b"heads(::%ld - %ld)", revs, revs)]
1025 bases = [cl.node(r) for r in repo.revs(b"heads(::%ld - %ld)", revs, revs)]
1019 outgoing = discovery.outgoing(repo, bases, targets)
1026 outgoing = discovery.outgoing(repo, bases, targets)
1020
1027
1021 bundlespec = bundlecaches.parsebundlespec(
1028 bundle_spec = opts.get(b'type')
1022 repo, b"none", strict=False
1029
1023 )
1030 bundle_spec = bundlecaches.parsebundlespec(repo, bundle_spec, strict=False)
1024
1031
1025 bversion = b'HG10' + bundlespec.wirecompression
1032 cgversion = bundle_spec.params[b"cg.version"]
1033 if cgversion not in changegroup.supportedoutgoingversions(repo):
1034 err = b"repository does not support bundle version %s"
1035 raise error.Abort(err % cgversion)
1036
1037 if cgversion == b'01': # bundle1
1038 bversion = b'HG10' + bundle_spec.wirecompression
1039 bcompression = None
1040 elif cgversion in (b'02', b'03'):
1041 bversion = b'HG20'
1042 bcompression = bundle_spec.wirecompression
1043 else:
1044 err = b'perf::bundle: unexpected changegroup version %s'
1045 raise error.ProgrammingError(err % cgversion)
1046
1047 if bcompression is None:
1048 bcompression = b'UN'
1049
1050 if bcompression != b'UN':
1051 err = b'perf::bundle: compression currently unsupported: %s'
1052 raise error.ProgrammingError(err % bcompression)
1026
1053
1027 def do_bundle():
1054 def do_bundle():
1028 bundle2.writenewbundle(
1055 bundle2.writenewbundle(
1029 ui,
1056 ui,
1030 repo,
1057 repo,
1031 b'perf::bundle',
1058 b'perf::bundle',
1032 os.devnull,
1059 os.devnull,
1033 bversion,
1060 bversion,
1034 outgoing,
1061 outgoing,
1035 {},
1062 bundle_spec.params,
1036 )
1063 )
1037
1064
1038 timer(do_bundle)
1065 timer(do_bundle)
1039 fm.end()
1066 fm.end()
1040
1067
1041
1068
1042 @command(b'perf::bundleread|perfbundleread', formatteropts, b'BUNDLE')
1069 @command(b'perf::bundleread|perfbundleread', formatteropts, b'BUNDLE')
1043 def perfbundleread(ui, repo, bundlepath, **opts):
1070 def perfbundleread(ui, repo, bundlepath, **opts):
1044 """Benchmark reading of bundle files.
1071 """Benchmark reading of bundle files.
1045
1072
1046 This command is meant to isolate the I/O part of bundle reading as
1073 This command is meant to isolate the I/O part of bundle reading as
1047 much as possible.
1074 much as possible.
1048 """
1075 """
1049 from mercurial import (
1076 from mercurial import (
1050 bundle2,
1077 bundle2,
1051 exchange,
1078 exchange,
1052 streamclone,
1079 streamclone,
1053 )
1080 )
1054
1081
1055 opts = _byteskwargs(opts)
1082 opts = _byteskwargs(opts)
1056
1083
1057 def makebench(fn):
1084 def makebench(fn):
1058 def run():
1085 def run():
1059 with open(bundlepath, b'rb') as fh:
1086 with open(bundlepath, b'rb') as fh:
1060 bundle = exchange.readbundle(ui, fh, bundlepath)
1087 bundle = exchange.readbundle(ui, fh, bundlepath)
1061 fn(bundle)
1088 fn(bundle)
1062
1089
1063 return run
1090 return run
1064
1091
1065 def makereadnbytes(size):
1092 def makereadnbytes(size):
1066 def run():
1093 def run():
1067 with open(bundlepath, b'rb') as fh:
1094 with open(bundlepath, b'rb') as fh:
1068 bundle = exchange.readbundle(ui, fh, bundlepath)
1095 bundle = exchange.readbundle(ui, fh, bundlepath)
1069 while bundle.read(size):
1096 while bundle.read(size):
1070 pass
1097 pass
1071
1098
1072 return run
1099 return run
1073
1100
1074 def makestdioread(size):
1101 def makestdioread(size):
1075 def run():
1102 def run():
1076 with open(bundlepath, b'rb') as fh:
1103 with open(bundlepath, b'rb') as fh:
1077 while fh.read(size):
1104 while fh.read(size):
1078 pass
1105 pass
1079
1106
1080 return run
1107 return run
1081
1108
1082 # bundle1
1109 # bundle1
1083
1110
1084 def deltaiter(bundle):
1111 def deltaiter(bundle):
1085 for delta in bundle.deltaiter():
1112 for delta in bundle.deltaiter():
1086 pass
1113 pass
1087
1114
1088 def iterchunks(bundle):
1115 def iterchunks(bundle):
1089 for chunk in bundle.getchunks():
1116 for chunk in bundle.getchunks():
1090 pass
1117 pass
1091
1118
1092 # bundle2
1119 # bundle2
1093
1120
1094 def forwardchunks(bundle):
1121 def forwardchunks(bundle):
1095 for chunk in bundle._forwardchunks():
1122 for chunk in bundle._forwardchunks():
1096 pass
1123 pass
1097
1124
1098 def iterparts(bundle):
1125 def iterparts(bundle):
1099 for part in bundle.iterparts():
1126 for part in bundle.iterparts():
1100 pass
1127 pass
1101
1128
1102 def iterpartsseekable(bundle):
1129 def iterpartsseekable(bundle):
1103 for part in bundle.iterparts(seekable=True):
1130 for part in bundle.iterparts(seekable=True):
1104 pass
1131 pass
1105
1132
1106 def seek(bundle):
1133 def seek(bundle):
1107 for part in bundle.iterparts(seekable=True):
1134 for part in bundle.iterparts(seekable=True):
1108 part.seek(0, os.SEEK_END)
1135 part.seek(0, os.SEEK_END)
1109
1136
1110 def makepartreadnbytes(size):
1137 def makepartreadnbytes(size):
1111 def run():
1138 def run():
1112 with open(bundlepath, b'rb') as fh:
1139 with open(bundlepath, b'rb') as fh:
1113 bundle = exchange.readbundle(ui, fh, bundlepath)
1140 bundle = exchange.readbundle(ui, fh, bundlepath)
1114 for part in bundle.iterparts():
1141 for part in bundle.iterparts():
1115 while part.read(size):
1142 while part.read(size):
1116 pass
1143 pass
1117
1144
1118 return run
1145 return run
1119
1146
1120 benches = [
1147 benches = [
1121 (makestdioread(8192), b'read(8k)'),
1148 (makestdioread(8192), b'read(8k)'),
1122 (makestdioread(16384), b'read(16k)'),
1149 (makestdioread(16384), b'read(16k)'),
1123 (makestdioread(32768), b'read(32k)'),
1150 (makestdioread(32768), b'read(32k)'),
1124 (makestdioread(131072), b'read(128k)'),
1151 (makestdioread(131072), b'read(128k)'),
1125 ]
1152 ]
1126
1153
1127 with open(bundlepath, b'rb') as fh:
1154 with open(bundlepath, b'rb') as fh:
1128 bundle = exchange.readbundle(ui, fh, bundlepath)
1155 bundle = exchange.readbundle(ui, fh, bundlepath)
1129
1156
1130 if isinstance(bundle, changegroup.cg1unpacker):
1157 if isinstance(bundle, changegroup.cg1unpacker):
1131 benches.extend(
1158 benches.extend(
1132 [
1159 [
1133 (makebench(deltaiter), b'cg1 deltaiter()'),
1160 (makebench(deltaiter), b'cg1 deltaiter()'),
1134 (makebench(iterchunks), b'cg1 getchunks()'),
1161 (makebench(iterchunks), b'cg1 getchunks()'),
1135 (makereadnbytes(8192), b'cg1 read(8k)'),
1162 (makereadnbytes(8192), b'cg1 read(8k)'),
1136 (makereadnbytes(16384), b'cg1 read(16k)'),
1163 (makereadnbytes(16384), b'cg1 read(16k)'),
1137 (makereadnbytes(32768), b'cg1 read(32k)'),
1164 (makereadnbytes(32768), b'cg1 read(32k)'),
1138 (makereadnbytes(131072), b'cg1 read(128k)'),
1165 (makereadnbytes(131072), b'cg1 read(128k)'),
1139 ]
1166 ]
1140 )
1167 )
1141 elif isinstance(bundle, bundle2.unbundle20):
1168 elif isinstance(bundle, bundle2.unbundle20):
1142 benches.extend(
1169 benches.extend(
1143 [
1170 [
1144 (makebench(forwardchunks), b'bundle2 forwardchunks()'),
1171 (makebench(forwardchunks), b'bundle2 forwardchunks()'),
1145 (makebench(iterparts), b'bundle2 iterparts()'),
1172 (makebench(iterparts), b'bundle2 iterparts()'),
1146 (
1173 (
1147 makebench(iterpartsseekable),
1174 makebench(iterpartsseekable),
1148 b'bundle2 iterparts() seekable',
1175 b'bundle2 iterparts() seekable',
1149 ),
1176 ),
1150 (makebench(seek), b'bundle2 part seek()'),
1177 (makebench(seek), b'bundle2 part seek()'),
1151 (makepartreadnbytes(8192), b'bundle2 part read(8k)'),
1178 (makepartreadnbytes(8192), b'bundle2 part read(8k)'),
1152 (makepartreadnbytes(16384), b'bundle2 part read(16k)'),
1179 (makepartreadnbytes(16384), b'bundle2 part read(16k)'),
1153 (makepartreadnbytes(32768), b'bundle2 part read(32k)'),
1180 (makepartreadnbytes(32768), b'bundle2 part read(32k)'),
1154 (makepartreadnbytes(131072), b'bundle2 part read(128k)'),
1181 (makepartreadnbytes(131072), b'bundle2 part read(128k)'),
1155 ]
1182 ]
1156 )
1183 )
1157 elif isinstance(bundle, streamclone.streamcloneapplier):
1184 elif isinstance(bundle, streamclone.streamcloneapplier):
1158 raise error.Abort(b'stream clone bundles not supported')
1185 raise error.Abort(b'stream clone bundles not supported')
1159 else:
1186 else:
1160 raise error.Abort(b'unhandled bundle type: %s' % type(bundle))
1187 raise error.Abort(b'unhandled bundle type: %s' % type(bundle))
1161
1188
1162 for fn, title in benches:
1189 for fn, title in benches:
1163 timer, fm = gettimer(ui, opts)
1190 timer, fm = gettimer(ui, opts)
1164 timer(fn, title=title)
1191 timer(fn, title=title)
1165 fm.end()
1192 fm.end()
1166
1193
1167
1194
1168 @command(
1195 @command(
1169 b'perf::changegroupchangelog|perfchangegroupchangelog',
1196 b'perf::changegroupchangelog|perfchangegroupchangelog',
1170 formatteropts
1197 formatteropts
1171 + [
1198 + [
1172 (b'', b'cgversion', b'02', b'changegroup version'),
1199 (b'', b'cgversion', b'02', b'changegroup version'),
1173 (b'r', b'rev', b'', b'revisions to add to changegroup'),
1200 (b'r', b'rev', b'', b'revisions to add to changegroup'),
1174 ],
1201 ],
1175 )
1202 )
1176 def perfchangegroupchangelog(ui, repo, cgversion=b'02', rev=None, **opts):
1203 def perfchangegroupchangelog(ui, repo, cgversion=b'02', rev=None, **opts):
1177 """Benchmark producing a changelog group for a changegroup.
1204 """Benchmark producing a changelog group for a changegroup.
1178
1205
1179 This measures the time spent processing the changelog during a
1206 This measures the time spent processing the changelog during a
1180 bundle operation. This occurs during `hg bundle` and on a server
1207 bundle operation. This occurs during `hg bundle` and on a server
1181 processing a `getbundle` wire protocol request (handles clones
1208 processing a `getbundle` wire protocol request (handles clones
1182 and pull requests).
1209 and pull requests).
1183
1210
1184 By default, all revisions are added to the changegroup.
1211 By default, all revisions are added to the changegroup.
1185 """
1212 """
1186 opts = _byteskwargs(opts)
1213 opts = _byteskwargs(opts)
1187 cl = repo.changelog
1214 cl = repo.changelog
1188 nodes = [cl.lookup(r) for r in repo.revs(rev or b'all()')]
1215 nodes = [cl.lookup(r) for r in repo.revs(rev or b'all()')]
1189 bundler = changegroup.getbundler(cgversion, repo)
1216 bundler = changegroup.getbundler(cgversion, repo)
1190
1217
1191 def d():
1218 def d():
1192 state, chunks = bundler._generatechangelog(cl, nodes)
1219 state, chunks = bundler._generatechangelog(cl, nodes)
1193 for chunk in chunks:
1220 for chunk in chunks:
1194 pass
1221 pass
1195
1222
1196 timer, fm = gettimer(ui, opts)
1223 timer, fm = gettimer(ui, opts)
1197
1224
1198 # Terminal printing can interfere with timing. So disable it.
1225 # Terminal printing can interfere with timing. So disable it.
1199 with ui.configoverride({(b'progress', b'disable'): True}):
1226 with ui.configoverride({(b'progress', b'disable'): True}):
1200 timer(d)
1227 timer(d)
1201
1228
1202 fm.end()
1229 fm.end()
1203
1230
1204
1231
1205 @command(b'perf::dirs|perfdirs', formatteropts)
1232 @command(b'perf::dirs|perfdirs', formatteropts)
1206 def perfdirs(ui, repo, **opts):
1233 def perfdirs(ui, repo, **opts):
1207 opts = _byteskwargs(opts)
1234 opts = _byteskwargs(opts)
1208 timer, fm = gettimer(ui, opts)
1235 timer, fm = gettimer(ui, opts)
1209 dirstate = repo.dirstate
1236 dirstate = repo.dirstate
1210 b'a' in dirstate
1237 b'a' in dirstate
1211
1238
1212 def d():
1239 def d():
1213 dirstate.hasdir(b'a')
1240 dirstate.hasdir(b'a')
1214 try:
1241 try:
1215 del dirstate._map._dirs
1242 del dirstate._map._dirs
1216 except AttributeError:
1243 except AttributeError:
1217 pass
1244 pass
1218
1245
1219 timer(d)
1246 timer(d)
1220 fm.end()
1247 fm.end()
1221
1248
1222
1249
1223 @command(
1250 @command(
1224 b'perf::dirstate|perfdirstate',
1251 b'perf::dirstate|perfdirstate',
1225 [
1252 [
1226 (
1253 (
1227 b'',
1254 b'',
1228 b'iteration',
1255 b'iteration',
1229 None,
1256 None,
1230 b'benchmark a full iteration for the dirstate',
1257 b'benchmark a full iteration for the dirstate',
1231 ),
1258 ),
1232 (
1259 (
1233 b'',
1260 b'',
1234 b'contains',
1261 b'contains',
1235 None,
1262 None,
1236 b'benchmark a large amount of `nf in dirstate` calls',
1263 b'benchmark a large amount of `nf in dirstate` calls',
1237 ),
1264 ),
1238 ]
1265 ]
1239 + formatteropts,
1266 + formatteropts,
1240 )
1267 )
1241 def perfdirstate(ui, repo, **opts):
1268 def perfdirstate(ui, repo, **opts):
1242 """benchmap the time of various distate operations
1269 """benchmap the time of various distate operations
1243
1270
1244 By default benchmark the time necessary to load a dirstate from scratch.
1271 By default benchmark the time necessary to load a dirstate from scratch.
1245 The dirstate is loaded to the point were a "contains" request can be
1272 The dirstate is loaded to the point were a "contains" request can be
1246 answered.
1273 answered.
1247 """
1274 """
1248 opts = _byteskwargs(opts)
1275 opts = _byteskwargs(opts)
1249 timer, fm = gettimer(ui, opts)
1276 timer, fm = gettimer(ui, opts)
1250 b"a" in repo.dirstate
1277 b"a" in repo.dirstate
1251
1278
1252 if opts[b'iteration'] and opts[b'contains']:
1279 if opts[b'iteration'] and opts[b'contains']:
1253 msg = b'only specify one of --iteration or --contains'
1280 msg = b'only specify one of --iteration or --contains'
1254 raise error.Abort(msg)
1281 raise error.Abort(msg)
1255
1282
1256 if opts[b'iteration']:
1283 if opts[b'iteration']:
1257 setup = None
1284 setup = None
1258 dirstate = repo.dirstate
1285 dirstate = repo.dirstate
1259
1286
1260 def d():
1287 def d():
1261 for f in dirstate:
1288 for f in dirstate:
1262 pass
1289 pass
1263
1290
1264 elif opts[b'contains']:
1291 elif opts[b'contains']:
1265 setup = None
1292 setup = None
1266 dirstate = repo.dirstate
1293 dirstate = repo.dirstate
1267 allfiles = list(dirstate)
1294 allfiles = list(dirstate)
1268 # also add file path that will be "missing" from the dirstate
1295 # also add file path that will be "missing" from the dirstate
1269 allfiles.extend([f[::-1] for f in allfiles])
1296 allfiles.extend([f[::-1] for f in allfiles])
1270
1297
1271 def d():
1298 def d():
1272 for f in allfiles:
1299 for f in allfiles:
1273 f in dirstate
1300 f in dirstate
1274
1301
1275 else:
1302 else:
1276
1303
1277 def setup():
1304 def setup():
1278 repo.dirstate.invalidate()
1305 repo.dirstate.invalidate()
1279
1306
1280 def d():
1307 def d():
1281 b"a" in repo.dirstate
1308 b"a" in repo.dirstate
1282
1309
1283 timer(d, setup=setup)
1310 timer(d, setup=setup)
1284 fm.end()
1311 fm.end()
1285
1312
1286
1313
1287 @command(b'perf::dirstatedirs|perfdirstatedirs', formatteropts)
1314 @command(b'perf::dirstatedirs|perfdirstatedirs', formatteropts)
1288 def perfdirstatedirs(ui, repo, **opts):
1315 def perfdirstatedirs(ui, repo, **opts):
1289 """benchmap a 'dirstate.hasdir' call from an empty `dirs` cache"""
1316 """benchmap a 'dirstate.hasdir' call from an empty `dirs` cache"""
1290 opts = _byteskwargs(opts)
1317 opts = _byteskwargs(opts)
1291 timer, fm = gettimer(ui, opts)
1318 timer, fm = gettimer(ui, opts)
1292 repo.dirstate.hasdir(b"a")
1319 repo.dirstate.hasdir(b"a")
1293
1320
1294 def setup():
1321 def setup():
1295 try:
1322 try:
1296 del repo.dirstate._map._dirs
1323 del repo.dirstate._map._dirs
1297 except AttributeError:
1324 except AttributeError:
1298 pass
1325 pass
1299
1326
1300 def d():
1327 def d():
1301 repo.dirstate.hasdir(b"a")
1328 repo.dirstate.hasdir(b"a")
1302
1329
1303 timer(d, setup=setup)
1330 timer(d, setup=setup)
1304 fm.end()
1331 fm.end()
1305
1332
1306
1333
1307 @command(b'perf::dirstatefoldmap|perfdirstatefoldmap', formatteropts)
1334 @command(b'perf::dirstatefoldmap|perfdirstatefoldmap', formatteropts)
1308 def perfdirstatefoldmap(ui, repo, **opts):
1335 def perfdirstatefoldmap(ui, repo, **opts):
1309 """benchmap a `dirstate._map.filefoldmap.get()` request
1336 """benchmap a `dirstate._map.filefoldmap.get()` request
1310
1337
1311 The dirstate filefoldmap cache is dropped between every request.
1338 The dirstate filefoldmap cache is dropped between every request.
1312 """
1339 """
1313 opts = _byteskwargs(opts)
1340 opts = _byteskwargs(opts)
1314 timer, fm = gettimer(ui, opts)
1341 timer, fm = gettimer(ui, opts)
1315 dirstate = repo.dirstate
1342 dirstate = repo.dirstate
1316 dirstate._map.filefoldmap.get(b'a')
1343 dirstate._map.filefoldmap.get(b'a')
1317
1344
1318 def setup():
1345 def setup():
1319 del dirstate._map.filefoldmap
1346 del dirstate._map.filefoldmap
1320
1347
1321 def d():
1348 def d():
1322 dirstate._map.filefoldmap.get(b'a')
1349 dirstate._map.filefoldmap.get(b'a')
1323
1350
1324 timer(d, setup=setup)
1351 timer(d, setup=setup)
1325 fm.end()
1352 fm.end()
1326
1353
1327
1354
1328 @command(b'perf::dirfoldmap|perfdirfoldmap', formatteropts)
1355 @command(b'perf::dirfoldmap|perfdirfoldmap', formatteropts)
1329 def perfdirfoldmap(ui, repo, **opts):
1356 def perfdirfoldmap(ui, repo, **opts):
1330 """benchmap a `dirstate._map.dirfoldmap.get()` request
1357 """benchmap a `dirstate._map.dirfoldmap.get()` request
1331
1358
1332 The dirstate dirfoldmap cache is dropped between every request.
1359 The dirstate dirfoldmap cache is dropped between every request.
1333 """
1360 """
1334 opts = _byteskwargs(opts)
1361 opts = _byteskwargs(opts)
1335 timer, fm = gettimer(ui, opts)
1362 timer, fm = gettimer(ui, opts)
1336 dirstate = repo.dirstate
1363 dirstate = repo.dirstate
1337 dirstate._map.dirfoldmap.get(b'a')
1364 dirstate._map.dirfoldmap.get(b'a')
1338
1365
1339 def setup():
1366 def setup():
1340 del dirstate._map.dirfoldmap
1367 del dirstate._map.dirfoldmap
1341 try:
1368 try:
1342 del dirstate._map._dirs
1369 del dirstate._map._dirs
1343 except AttributeError:
1370 except AttributeError:
1344 pass
1371 pass
1345
1372
1346 def d():
1373 def d():
1347 dirstate._map.dirfoldmap.get(b'a')
1374 dirstate._map.dirfoldmap.get(b'a')
1348
1375
1349 timer(d, setup=setup)
1376 timer(d, setup=setup)
1350 fm.end()
1377 fm.end()
1351
1378
1352
1379
1353 @command(b'perf::dirstatewrite|perfdirstatewrite', formatteropts)
1380 @command(b'perf::dirstatewrite|perfdirstatewrite', formatteropts)
1354 def perfdirstatewrite(ui, repo, **opts):
1381 def perfdirstatewrite(ui, repo, **opts):
1355 """benchmap the time it take to write a dirstate on disk"""
1382 """benchmap the time it take to write a dirstate on disk"""
1356 opts = _byteskwargs(opts)
1383 opts = _byteskwargs(opts)
1357 timer, fm = gettimer(ui, opts)
1384 timer, fm = gettimer(ui, opts)
1358 ds = repo.dirstate
1385 ds = repo.dirstate
1359 b"a" in ds
1386 b"a" in ds
1360
1387
1361 def setup():
1388 def setup():
1362 ds._dirty = True
1389 ds._dirty = True
1363
1390
1364 def d():
1391 def d():
1365 ds.write(repo.currenttransaction())
1392 ds.write(repo.currenttransaction())
1366
1393
1367 timer(d, setup=setup)
1394 timer(d, setup=setup)
1368 fm.end()
1395 fm.end()
1369
1396
1370
1397
1371 def _getmergerevs(repo, opts):
1398 def _getmergerevs(repo, opts):
1372 """parse command argument to return rev involved in merge
1399 """parse command argument to return rev involved in merge
1373
1400
1374 input: options dictionnary with `rev`, `from` and `bse`
1401 input: options dictionnary with `rev`, `from` and `bse`
1375 output: (localctx, otherctx, basectx)
1402 output: (localctx, otherctx, basectx)
1376 """
1403 """
1377 if opts[b'from']:
1404 if opts[b'from']:
1378 fromrev = scmutil.revsingle(repo, opts[b'from'])
1405 fromrev = scmutil.revsingle(repo, opts[b'from'])
1379 wctx = repo[fromrev]
1406 wctx = repo[fromrev]
1380 else:
1407 else:
1381 wctx = repo[None]
1408 wctx = repo[None]
1382 # we don't want working dir files to be stat'd in the benchmark, so
1409 # we don't want working dir files to be stat'd in the benchmark, so
1383 # prime that cache
1410 # prime that cache
1384 wctx.dirty()
1411 wctx.dirty()
1385 rctx = scmutil.revsingle(repo, opts[b'rev'], opts[b'rev'])
1412 rctx = scmutil.revsingle(repo, opts[b'rev'], opts[b'rev'])
1386 if opts[b'base']:
1413 if opts[b'base']:
1387 fromrev = scmutil.revsingle(repo, opts[b'base'])
1414 fromrev = scmutil.revsingle(repo, opts[b'base'])
1388 ancestor = repo[fromrev]
1415 ancestor = repo[fromrev]
1389 else:
1416 else:
1390 ancestor = wctx.ancestor(rctx)
1417 ancestor = wctx.ancestor(rctx)
1391 return (wctx, rctx, ancestor)
1418 return (wctx, rctx, ancestor)
1392
1419
1393
1420
1394 @command(
1421 @command(
1395 b'perf::mergecalculate|perfmergecalculate',
1422 b'perf::mergecalculate|perfmergecalculate',
1396 [
1423 [
1397 (b'r', b'rev', b'.', b'rev to merge against'),
1424 (b'r', b'rev', b'.', b'rev to merge against'),
1398 (b'', b'from', b'', b'rev to merge from'),
1425 (b'', b'from', b'', b'rev to merge from'),
1399 (b'', b'base', b'', b'the revision to use as base'),
1426 (b'', b'base', b'', b'the revision to use as base'),
1400 ]
1427 ]
1401 + formatteropts,
1428 + formatteropts,
1402 )
1429 )
1403 def perfmergecalculate(ui, repo, **opts):
1430 def perfmergecalculate(ui, repo, **opts):
1404 opts = _byteskwargs(opts)
1431 opts = _byteskwargs(opts)
1405 timer, fm = gettimer(ui, opts)
1432 timer, fm = gettimer(ui, opts)
1406
1433
1407 wctx, rctx, ancestor = _getmergerevs(repo, opts)
1434 wctx, rctx, ancestor = _getmergerevs(repo, opts)
1408
1435
1409 def d():
1436 def d():
1410 # acceptremote is True because we don't want prompts in the middle of
1437 # acceptremote is True because we don't want prompts in the middle of
1411 # our benchmark
1438 # our benchmark
1412 merge.calculateupdates(
1439 merge.calculateupdates(
1413 repo,
1440 repo,
1414 wctx,
1441 wctx,
1415 rctx,
1442 rctx,
1416 [ancestor],
1443 [ancestor],
1417 branchmerge=False,
1444 branchmerge=False,
1418 force=False,
1445 force=False,
1419 acceptremote=True,
1446 acceptremote=True,
1420 followcopies=True,
1447 followcopies=True,
1421 )
1448 )
1422
1449
1423 timer(d)
1450 timer(d)
1424 fm.end()
1451 fm.end()
1425
1452
1426
1453
1427 @command(
1454 @command(
1428 b'perf::mergecopies|perfmergecopies',
1455 b'perf::mergecopies|perfmergecopies',
1429 [
1456 [
1430 (b'r', b'rev', b'.', b'rev to merge against'),
1457 (b'r', b'rev', b'.', b'rev to merge against'),
1431 (b'', b'from', b'', b'rev to merge from'),
1458 (b'', b'from', b'', b'rev to merge from'),
1432 (b'', b'base', b'', b'the revision to use as base'),
1459 (b'', b'base', b'', b'the revision to use as base'),
1433 ]
1460 ]
1434 + formatteropts,
1461 + formatteropts,
1435 )
1462 )
1436 def perfmergecopies(ui, repo, **opts):
1463 def perfmergecopies(ui, repo, **opts):
1437 """measure runtime of `copies.mergecopies`"""
1464 """measure runtime of `copies.mergecopies`"""
1438 opts = _byteskwargs(opts)
1465 opts = _byteskwargs(opts)
1439 timer, fm = gettimer(ui, opts)
1466 timer, fm = gettimer(ui, opts)
1440 wctx, rctx, ancestor = _getmergerevs(repo, opts)
1467 wctx, rctx, ancestor = _getmergerevs(repo, opts)
1441
1468
1442 def d():
1469 def d():
1443 # acceptremote is True because we don't want prompts in the middle of
1470 # acceptremote is True because we don't want prompts in the middle of
1444 # our benchmark
1471 # our benchmark
1445 copies.mergecopies(repo, wctx, rctx, ancestor)
1472 copies.mergecopies(repo, wctx, rctx, ancestor)
1446
1473
1447 timer(d)
1474 timer(d)
1448 fm.end()
1475 fm.end()
1449
1476
1450
1477
1451 @command(b'perf::pathcopies|perfpathcopies', [], b"REV REV")
1478 @command(b'perf::pathcopies|perfpathcopies', [], b"REV REV")
1452 def perfpathcopies(ui, repo, rev1, rev2, **opts):
1479 def perfpathcopies(ui, repo, rev1, rev2, **opts):
1453 """benchmark the copy tracing logic"""
1480 """benchmark the copy tracing logic"""
1454 opts = _byteskwargs(opts)
1481 opts = _byteskwargs(opts)
1455 timer, fm = gettimer(ui, opts)
1482 timer, fm = gettimer(ui, opts)
1456 ctx1 = scmutil.revsingle(repo, rev1, rev1)
1483 ctx1 = scmutil.revsingle(repo, rev1, rev1)
1457 ctx2 = scmutil.revsingle(repo, rev2, rev2)
1484 ctx2 = scmutil.revsingle(repo, rev2, rev2)
1458
1485
1459 def d():
1486 def d():
1460 copies.pathcopies(ctx1, ctx2)
1487 copies.pathcopies(ctx1, ctx2)
1461
1488
1462 timer(d)
1489 timer(d)
1463 fm.end()
1490 fm.end()
1464
1491
1465
1492
1466 @command(
1493 @command(
1467 b'perf::phases|perfphases',
1494 b'perf::phases|perfphases',
1468 [
1495 [
1469 (b'', b'full', False, b'include file reading time too'),
1496 (b'', b'full', False, b'include file reading time too'),
1470 ],
1497 ],
1471 b"",
1498 b"",
1472 )
1499 )
1473 def perfphases(ui, repo, **opts):
1500 def perfphases(ui, repo, **opts):
1474 """benchmark phasesets computation"""
1501 """benchmark phasesets computation"""
1475 opts = _byteskwargs(opts)
1502 opts = _byteskwargs(opts)
1476 timer, fm = gettimer(ui, opts)
1503 timer, fm = gettimer(ui, opts)
1477 _phases = repo._phasecache
1504 _phases = repo._phasecache
1478 full = opts.get(b'full')
1505 full = opts.get(b'full')
1479
1506
1480 def d():
1507 def d():
1481 phases = _phases
1508 phases = _phases
1482 if full:
1509 if full:
1483 clearfilecache(repo, b'_phasecache')
1510 clearfilecache(repo, b'_phasecache')
1484 phases = repo._phasecache
1511 phases = repo._phasecache
1485 phases.invalidate()
1512 phases.invalidate()
1486 phases.loadphaserevs(repo)
1513 phases.loadphaserevs(repo)
1487
1514
1488 timer(d)
1515 timer(d)
1489 fm.end()
1516 fm.end()
1490
1517
1491
1518
1492 @command(b'perf::phasesremote|perfphasesremote', [], b"[DEST]")
1519 @command(b'perf::phasesremote|perfphasesremote', [], b"[DEST]")
1493 def perfphasesremote(ui, repo, dest=None, **opts):
1520 def perfphasesremote(ui, repo, dest=None, **opts):
1494 """benchmark time needed to analyse phases of the remote server"""
1521 """benchmark time needed to analyse phases of the remote server"""
1495 from mercurial.node import bin
1522 from mercurial.node import bin
1496 from mercurial import (
1523 from mercurial import (
1497 exchange,
1524 exchange,
1498 hg,
1525 hg,
1499 phases,
1526 phases,
1500 )
1527 )
1501
1528
1502 opts = _byteskwargs(opts)
1529 opts = _byteskwargs(opts)
1503 timer, fm = gettimer(ui, opts)
1530 timer, fm = gettimer(ui, opts)
1504
1531
1505 path = ui.getpath(dest, default=(b'default-push', b'default'))
1532 path = ui.getpath(dest, default=(b'default-push', b'default'))
1506 if not path:
1533 if not path:
1507 raise error.Abort(
1534 raise error.Abort(
1508 b'default repository not configured!',
1535 b'default repository not configured!',
1509 hint=b"see 'hg help config.paths'",
1536 hint=b"see 'hg help config.paths'",
1510 )
1537 )
1511 dest = path.pushloc or path.loc
1538 dest = path.pushloc or path.loc
1512 ui.statusnoi18n(b'analysing phase of %s\n' % util.hidepassword(dest))
1539 ui.statusnoi18n(b'analysing phase of %s\n' % util.hidepassword(dest))
1513 other = hg.peer(repo, opts, dest)
1540 other = hg.peer(repo, opts, dest)
1514
1541
1515 # easier to perform discovery through the operation
1542 # easier to perform discovery through the operation
1516 op = exchange.pushoperation(repo, other)
1543 op = exchange.pushoperation(repo, other)
1517 exchange._pushdiscoverychangeset(op)
1544 exchange._pushdiscoverychangeset(op)
1518
1545
1519 remotesubset = op.fallbackheads
1546 remotesubset = op.fallbackheads
1520
1547
1521 with other.commandexecutor() as e:
1548 with other.commandexecutor() as e:
1522 remotephases = e.callcommand(
1549 remotephases = e.callcommand(
1523 b'listkeys', {b'namespace': b'phases'}
1550 b'listkeys', {b'namespace': b'phases'}
1524 ).result()
1551 ).result()
1525 del other
1552 del other
1526 publishing = remotephases.get(b'publishing', False)
1553 publishing = remotephases.get(b'publishing', False)
1527 if publishing:
1554 if publishing:
1528 ui.statusnoi18n(b'publishing: yes\n')
1555 ui.statusnoi18n(b'publishing: yes\n')
1529 else:
1556 else:
1530 ui.statusnoi18n(b'publishing: no\n')
1557 ui.statusnoi18n(b'publishing: no\n')
1531
1558
1532 has_node = getattr(repo.changelog.index, 'has_node', None)
1559 has_node = getattr(repo.changelog.index, 'has_node', None)
1533 if has_node is None:
1560 if has_node is None:
1534 has_node = repo.changelog.nodemap.__contains__
1561 has_node = repo.changelog.nodemap.__contains__
1535 nonpublishroots = 0
1562 nonpublishroots = 0
1536 for nhex, phase in remotephases.iteritems():
1563 for nhex, phase in remotephases.iteritems():
1537 if nhex == b'publishing': # ignore data related to publish option
1564 if nhex == b'publishing': # ignore data related to publish option
1538 continue
1565 continue
1539 node = bin(nhex)
1566 node = bin(nhex)
1540 if has_node(node) and int(phase):
1567 if has_node(node) and int(phase):
1541 nonpublishroots += 1
1568 nonpublishroots += 1
1542 ui.statusnoi18n(b'number of roots: %d\n' % len(remotephases))
1569 ui.statusnoi18n(b'number of roots: %d\n' % len(remotephases))
1543 ui.statusnoi18n(b'number of known non public roots: %d\n' % nonpublishroots)
1570 ui.statusnoi18n(b'number of known non public roots: %d\n' % nonpublishroots)
1544
1571
1545 def d():
1572 def d():
1546 phases.remotephasessummary(repo, remotesubset, remotephases)
1573 phases.remotephasessummary(repo, remotesubset, remotephases)
1547
1574
1548 timer(d)
1575 timer(d)
1549 fm.end()
1576 fm.end()
1550
1577
1551
1578
1552 @command(
1579 @command(
1553 b'perf::manifest|perfmanifest',
1580 b'perf::manifest|perfmanifest',
1554 [
1581 [
1555 (b'm', b'manifest-rev', False, b'Look up a manifest node revision'),
1582 (b'm', b'manifest-rev', False, b'Look up a manifest node revision'),
1556 (b'', b'clear-disk', False, b'clear on-disk caches too'),
1583 (b'', b'clear-disk', False, b'clear on-disk caches too'),
1557 ]
1584 ]
1558 + formatteropts,
1585 + formatteropts,
1559 b'REV|NODE',
1586 b'REV|NODE',
1560 )
1587 )
1561 def perfmanifest(ui, repo, rev, manifest_rev=False, clear_disk=False, **opts):
1588 def perfmanifest(ui, repo, rev, manifest_rev=False, clear_disk=False, **opts):
1562 """benchmark the time to read a manifest from disk and return a usable
1589 """benchmark the time to read a manifest from disk and return a usable
1563 dict-like object
1590 dict-like object
1564
1591
1565 Manifest caches are cleared before retrieval."""
1592 Manifest caches are cleared before retrieval."""
1566 opts = _byteskwargs(opts)
1593 opts = _byteskwargs(opts)
1567 timer, fm = gettimer(ui, opts)
1594 timer, fm = gettimer(ui, opts)
1568 if not manifest_rev:
1595 if not manifest_rev:
1569 ctx = scmutil.revsingle(repo, rev, rev)
1596 ctx = scmutil.revsingle(repo, rev, rev)
1570 t = ctx.manifestnode()
1597 t = ctx.manifestnode()
1571 else:
1598 else:
1572 from mercurial.node import bin
1599 from mercurial.node import bin
1573
1600
1574 if len(rev) == 40:
1601 if len(rev) == 40:
1575 t = bin(rev)
1602 t = bin(rev)
1576 else:
1603 else:
1577 try:
1604 try:
1578 rev = int(rev)
1605 rev = int(rev)
1579
1606
1580 if util.safehasattr(repo.manifestlog, b'getstorage'):
1607 if util.safehasattr(repo.manifestlog, b'getstorage'):
1581 t = repo.manifestlog.getstorage(b'').node(rev)
1608 t = repo.manifestlog.getstorage(b'').node(rev)
1582 else:
1609 else:
1583 t = repo.manifestlog._revlog.lookup(rev)
1610 t = repo.manifestlog._revlog.lookup(rev)
1584 except ValueError:
1611 except ValueError:
1585 raise error.Abort(
1612 raise error.Abort(
1586 b'manifest revision must be integer or full node'
1613 b'manifest revision must be integer or full node'
1587 )
1614 )
1588
1615
1589 def d():
1616 def d():
1590 repo.manifestlog.clearcaches(clear_persisted_data=clear_disk)
1617 repo.manifestlog.clearcaches(clear_persisted_data=clear_disk)
1591 repo.manifestlog[t].read()
1618 repo.manifestlog[t].read()
1592
1619
1593 timer(d)
1620 timer(d)
1594 fm.end()
1621 fm.end()
1595
1622
1596
1623
1597 @command(b'perf::changeset|perfchangeset', formatteropts)
1624 @command(b'perf::changeset|perfchangeset', formatteropts)
1598 def perfchangeset(ui, repo, rev, **opts):
1625 def perfchangeset(ui, repo, rev, **opts):
1599 opts = _byteskwargs(opts)
1626 opts = _byteskwargs(opts)
1600 timer, fm = gettimer(ui, opts)
1627 timer, fm = gettimer(ui, opts)
1601 n = scmutil.revsingle(repo, rev).node()
1628 n = scmutil.revsingle(repo, rev).node()
1602
1629
1603 def d():
1630 def d():
1604 repo.changelog.read(n)
1631 repo.changelog.read(n)
1605 # repo.changelog._cache = None
1632 # repo.changelog._cache = None
1606
1633
1607 timer(d)
1634 timer(d)
1608 fm.end()
1635 fm.end()
1609
1636
1610
1637
1611 @command(b'perf::ignore|perfignore', formatteropts)
1638 @command(b'perf::ignore|perfignore', formatteropts)
1612 def perfignore(ui, repo, **opts):
1639 def perfignore(ui, repo, **opts):
1613 """benchmark operation related to computing ignore"""
1640 """benchmark operation related to computing ignore"""
1614 opts = _byteskwargs(opts)
1641 opts = _byteskwargs(opts)
1615 timer, fm = gettimer(ui, opts)
1642 timer, fm = gettimer(ui, opts)
1616 dirstate = repo.dirstate
1643 dirstate = repo.dirstate
1617
1644
1618 def setupone():
1645 def setupone():
1619 dirstate.invalidate()
1646 dirstate.invalidate()
1620 clearfilecache(dirstate, b'_ignore')
1647 clearfilecache(dirstate, b'_ignore')
1621
1648
1622 def runone():
1649 def runone():
1623 dirstate._ignore
1650 dirstate._ignore
1624
1651
1625 timer(runone, setup=setupone, title=b"load")
1652 timer(runone, setup=setupone, title=b"load")
1626 fm.end()
1653 fm.end()
1627
1654
1628
1655
1629 @command(
1656 @command(
1630 b'perf::index|perfindex',
1657 b'perf::index|perfindex',
1631 [
1658 [
1632 (b'', b'rev', [], b'revision to be looked up (default tip)'),
1659 (b'', b'rev', [], b'revision to be looked up (default tip)'),
1633 (b'', b'no-lookup', None, b'do not revision lookup post creation'),
1660 (b'', b'no-lookup', None, b'do not revision lookup post creation'),
1634 ]
1661 ]
1635 + formatteropts,
1662 + formatteropts,
1636 )
1663 )
1637 def perfindex(ui, repo, **opts):
1664 def perfindex(ui, repo, **opts):
1638 """benchmark index creation time followed by a lookup
1665 """benchmark index creation time followed by a lookup
1639
1666
1640 The default is to look `tip` up. Depending on the index implementation,
1667 The default is to look `tip` up. Depending on the index implementation,
1641 the revision looked up can matters. For example, an implementation
1668 the revision looked up can matters. For example, an implementation
1642 scanning the index will have a faster lookup time for `--rev tip` than for
1669 scanning the index will have a faster lookup time for `--rev tip` than for
1643 `--rev 0`. The number of looked up revisions and their order can also
1670 `--rev 0`. The number of looked up revisions and their order can also
1644 matters.
1671 matters.
1645
1672
1646 Example of useful set to test:
1673 Example of useful set to test:
1647
1674
1648 * tip
1675 * tip
1649 * 0
1676 * 0
1650 * -10:
1677 * -10:
1651 * :10
1678 * :10
1652 * -10: + :10
1679 * -10: + :10
1653 * :10: + -10:
1680 * :10: + -10:
1654 * -10000:
1681 * -10000:
1655 * -10000: + 0
1682 * -10000: + 0
1656
1683
1657 It is not currently possible to check for lookup of a missing node. For
1684 It is not currently possible to check for lookup of a missing node. For
1658 deeper lookup benchmarking, checkout the `perfnodemap` command."""
1685 deeper lookup benchmarking, checkout the `perfnodemap` command."""
1659 import mercurial.revlog
1686 import mercurial.revlog
1660
1687
1661 opts = _byteskwargs(opts)
1688 opts = _byteskwargs(opts)
1662 timer, fm = gettimer(ui, opts)
1689 timer, fm = gettimer(ui, opts)
1663 mercurial.revlog._prereadsize = 2 ** 24 # disable lazy parser in old hg
1690 mercurial.revlog._prereadsize = 2 ** 24 # disable lazy parser in old hg
1664 if opts[b'no_lookup']:
1691 if opts[b'no_lookup']:
1665 if opts['rev']:
1692 if opts['rev']:
1666 raise error.Abort('--no-lookup and --rev are mutually exclusive')
1693 raise error.Abort('--no-lookup and --rev are mutually exclusive')
1667 nodes = []
1694 nodes = []
1668 elif not opts[b'rev']:
1695 elif not opts[b'rev']:
1669 nodes = [repo[b"tip"].node()]
1696 nodes = [repo[b"tip"].node()]
1670 else:
1697 else:
1671 revs = scmutil.revrange(repo, opts[b'rev'])
1698 revs = scmutil.revrange(repo, opts[b'rev'])
1672 cl = repo.changelog
1699 cl = repo.changelog
1673 nodes = [cl.node(r) for r in revs]
1700 nodes = [cl.node(r) for r in revs]
1674
1701
1675 unfi = repo.unfiltered()
1702 unfi = repo.unfiltered()
1676 # find the filecache func directly
1703 # find the filecache func directly
1677 # This avoid polluting the benchmark with the filecache logic
1704 # This avoid polluting the benchmark with the filecache logic
1678 makecl = unfi.__class__.changelog.func
1705 makecl = unfi.__class__.changelog.func
1679
1706
1680 def setup():
1707 def setup():
1681 # probably not necessary, but for good measure
1708 # probably not necessary, but for good measure
1682 clearchangelog(unfi)
1709 clearchangelog(unfi)
1683
1710
1684 def d():
1711 def d():
1685 cl = makecl(unfi)
1712 cl = makecl(unfi)
1686 for n in nodes:
1713 for n in nodes:
1687 cl.rev(n)
1714 cl.rev(n)
1688
1715
1689 timer(d, setup=setup)
1716 timer(d, setup=setup)
1690 fm.end()
1717 fm.end()
1691
1718
1692
1719
1693 @command(
1720 @command(
1694 b'perf::nodemap|perfnodemap',
1721 b'perf::nodemap|perfnodemap',
1695 [
1722 [
1696 (b'', b'rev', [], b'revision to be looked up (default tip)'),
1723 (b'', b'rev', [], b'revision to be looked up (default tip)'),
1697 (b'', b'clear-caches', True, b'clear revlog cache between calls'),
1724 (b'', b'clear-caches', True, b'clear revlog cache between calls'),
1698 ]
1725 ]
1699 + formatteropts,
1726 + formatteropts,
1700 )
1727 )
1701 def perfnodemap(ui, repo, **opts):
1728 def perfnodemap(ui, repo, **opts):
1702 """benchmark the time necessary to look up revision from a cold nodemap
1729 """benchmark the time necessary to look up revision from a cold nodemap
1703
1730
1704 Depending on the implementation, the amount and order of revision we look
1731 Depending on the implementation, the amount and order of revision we look
1705 up can varies. Example of useful set to test:
1732 up can varies. Example of useful set to test:
1706 * tip
1733 * tip
1707 * 0
1734 * 0
1708 * -10:
1735 * -10:
1709 * :10
1736 * :10
1710 * -10: + :10
1737 * -10: + :10
1711 * :10: + -10:
1738 * :10: + -10:
1712 * -10000:
1739 * -10000:
1713 * -10000: + 0
1740 * -10000: + 0
1714
1741
1715 The command currently focus on valid binary lookup. Benchmarking for
1742 The command currently focus on valid binary lookup. Benchmarking for
1716 hexlookup, prefix lookup and missing lookup would also be valuable.
1743 hexlookup, prefix lookup and missing lookup would also be valuable.
1717 """
1744 """
1718 import mercurial.revlog
1745 import mercurial.revlog
1719
1746
1720 opts = _byteskwargs(opts)
1747 opts = _byteskwargs(opts)
1721 timer, fm = gettimer(ui, opts)
1748 timer, fm = gettimer(ui, opts)
1722 mercurial.revlog._prereadsize = 2 ** 24 # disable lazy parser in old hg
1749 mercurial.revlog._prereadsize = 2 ** 24 # disable lazy parser in old hg
1723
1750
1724 unfi = repo.unfiltered()
1751 unfi = repo.unfiltered()
1725 clearcaches = opts[b'clear_caches']
1752 clearcaches = opts[b'clear_caches']
1726 # find the filecache func directly
1753 # find the filecache func directly
1727 # This avoid polluting the benchmark with the filecache logic
1754 # This avoid polluting the benchmark with the filecache logic
1728 makecl = unfi.__class__.changelog.func
1755 makecl = unfi.__class__.changelog.func
1729 if not opts[b'rev']:
1756 if not opts[b'rev']:
1730 raise error.Abort(b'use --rev to specify revisions to look up')
1757 raise error.Abort(b'use --rev to specify revisions to look up')
1731 revs = scmutil.revrange(repo, opts[b'rev'])
1758 revs = scmutil.revrange(repo, opts[b'rev'])
1732 cl = repo.changelog
1759 cl = repo.changelog
1733 nodes = [cl.node(r) for r in revs]
1760 nodes = [cl.node(r) for r in revs]
1734
1761
1735 # use a list to pass reference to a nodemap from one closure to the next
1762 # use a list to pass reference to a nodemap from one closure to the next
1736 nodeget = [None]
1763 nodeget = [None]
1737
1764
1738 def setnodeget():
1765 def setnodeget():
1739 # probably not necessary, but for good measure
1766 # probably not necessary, but for good measure
1740 clearchangelog(unfi)
1767 clearchangelog(unfi)
1741 cl = makecl(unfi)
1768 cl = makecl(unfi)
1742 if util.safehasattr(cl.index, 'get_rev'):
1769 if util.safehasattr(cl.index, 'get_rev'):
1743 nodeget[0] = cl.index.get_rev
1770 nodeget[0] = cl.index.get_rev
1744 else:
1771 else:
1745 nodeget[0] = cl.nodemap.get
1772 nodeget[0] = cl.nodemap.get
1746
1773
1747 def d():
1774 def d():
1748 get = nodeget[0]
1775 get = nodeget[0]
1749 for n in nodes:
1776 for n in nodes:
1750 get(n)
1777 get(n)
1751
1778
1752 setup = None
1779 setup = None
1753 if clearcaches:
1780 if clearcaches:
1754
1781
1755 def setup():
1782 def setup():
1756 setnodeget()
1783 setnodeget()
1757
1784
1758 else:
1785 else:
1759 setnodeget()
1786 setnodeget()
1760 d() # prewarm the data structure
1787 d() # prewarm the data structure
1761 timer(d, setup=setup)
1788 timer(d, setup=setup)
1762 fm.end()
1789 fm.end()
1763
1790
1764
1791
1765 @command(b'perf::startup|perfstartup', formatteropts)
1792 @command(b'perf::startup|perfstartup', formatteropts)
1766 def perfstartup(ui, repo, **opts):
1793 def perfstartup(ui, repo, **opts):
1767 opts = _byteskwargs(opts)
1794 opts = _byteskwargs(opts)
1768 timer, fm = gettimer(ui, opts)
1795 timer, fm = gettimer(ui, opts)
1769
1796
1770 def d():
1797 def d():
1771 if os.name != 'nt':
1798 if os.name != 'nt':
1772 os.system(
1799 os.system(
1773 b"HGRCPATH= %s version -q > /dev/null" % fsencode(sys.argv[0])
1800 b"HGRCPATH= %s version -q > /dev/null" % fsencode(sys.argv[0])
1774 )
1801 )
1775 else:
1802 else:
1776 os.environ['HGRCPATH'] = r' '
1803 os.environ['HGRCPATH'] = r' '
1777 os.system("%s version -q > NUL" % sys.argv[0])
1804 os.system("%s version -q > NUL" % sys.argv[0])
1778
1805
1779 timer(d)
1806 timer(d)
1780 fm.end()
1807 fm.end()
1781
1808
1782
1809
1783 @command(b'perf::parents|perfparents', formatteropts)
1810 @command(b'perf::parents|perfparents', formatteropts)
1784 def perfparents(ui, repo, **opts):
1811 def perfparents(ui, repo, **opts):
1785 """benchmark the time necessary to fetch one changeset's parents.
1812 """benchmark the time necessary to fetch one changeset's parents.
1786
1813
1787 The fetch is done using the `node identifier`, traversing all object layers
1814 The fetch is done using the `node identifier`, traversing all object layers
1788 from the repository object. The first N revisions will be used for this
1815 from the repository object. The first N revisions will be used for this
1789 benchmark. N is controlled by the ``perf.parentscount`` config option
1816 benchmark. N is controlled by the ``perf.parentscount`` config option
1790 (default: 1000).
1817 (default: 1000).
1791 """
1818 """
1792 opts = _byteskwargs(opts)
1819 opts = _byteskwargs(opts)
1793 timer, fm = gettimer(ui, opts)
1820 timer, fm = gettimer(ui, opts)
1794 # control the number of commits perfparents iterates over
1821 # control the number of commits perfparents iterates over
1795 # experimental config: perf.parentscount
1822 # experimental config: perf.parentscount
1796 count = getint(ui, b"perf", b"parentscount", 1000)
1823 count = getint(ui, b"perf", b"parentscount", 1000)
1797 if len(repo.changelog) < count:
1824 if len(repo.changelog) < count:
1798 raise error.Abort(b"repo needs %d commits for this test" % count)
1825 raise error.Abort(b"repo needs %d commits for this test" % count)
1799 repo = repo.unfiltered()
1826 repo = repo.unfiltered()
1800 nl = [repo.changelog.node(i) for i in _xrange(count)]
1827 nl = [repo.changelog.node(i) for i in _xrange(count)]
1801
1828
1802 def d():
1829 def d():
1803 for n in nl:
1830 for n in nl:
1804 repo.changelog.parents(n)
1831 repo.changelog.parents(n)
1805
1832
1806 timer(d)
1833 timer(d)
1807 fm.end()
1834 fm.end()
1808
1835
1809
1836
1810 @command(b'perf::ctxfiles|perfctxfiles', formatteropts)
1837 @command(b'perf::ctxfiles|perfctxfiles', formatteropts)
1811 def perfctxfiles(ui, repo, x, **opts):
1838 def perfctxfiles(ui, repo, x, **opts):
1812 opts = _byteskwargs(opts)
1839 opts = _byteskwargs(opts)
1813 x = int(x)
1840 x = int(x)
1814 timer, fm = gettimer(ui, opts)
1841 timer, fm = gettimer(ui, opts)
1815
1842
1816 def d():
1843 def d():
1817 len(repo[x].files())
1844 len(repo[x].files())
1818
1845
1819 timer(d)
1846 timer(d)
1820 fm.end()
1847 fm.end()
1821
1848
1822
1849
1823 @command(b'perf::rawfiles|perfrawfiles', formatteropts)
1850 @command(b'perf::rawfiles|perfrawfiles', formatteropts)
1824 def perfrawfiles(ui, repo, x, **opts):
1851 def perfrawfiles(ui, repo, x, **opts):
1825 opts = _byteskwargs(opts)
1852 opts = _byteskwargs(opts)
1826 x = int(x)
1853 x = int(x)
1827 timer, fm = gettimer(ui, opts)
1854 timer, fm = gettimer(ui, opts)
1828 cl = repo.changelog
1855 cl = repo.changelog
1829
1856
1830 def d():
1857 def d():
1831 len(cl.read(x)[3])
1858 len(cl.read(x)[3])
1832
1859
1833 timer(d)
1860 timer(d)
1834 fm.end()
1861 fm.end()
1835
1862
1836
1863
1837 @command(b'perf::lookup|perflookup', formatteropts)
1864 @command(b'perf::lookup|perflookup', formatteropts)
1838 def perflookup(ui, repo, rev, **opts):
1865 def perflookup(ui, repo, rev, **opts):
1839 opts = _byteskwargs(opts)
1866 opts = _byteskwargs(opts)
1840 timer, fm = gettimer(ui, opts)
1867 timer, fm = gettimer(ui, opts)
1841 timer(lambda: len(repo.lookup(rev)))
1868 timer(lambda: len(repo.lookup(rev)))
1842 fm.end()
1869 fm.end()
1843
1870
1844
1871
1845 @command(
1872 @command(
1846 b'perf::linelogedits|perflinelogedits',
1873 b'perf::linelogedits|perflinelogedits',
1847 [
1874 [
1848 (b'n', b'edits', 10000, b'number of edits'),
1875 (b'n', b'edits', 10000, b'number of edits'),
1849 (b'', b'max-hunk-lines', 10, b'max lines in a hunk'),
1876 (b'', b'max-hunk-lines', 10, b'max lines in a hunk'),
1850 ],
1877 ],
1851 norepo=True,
1878 norepo=True,
1852 )
1879 )
1853 def perflinelogedits(ui, **opts):
1880 def perflinelogedits(ui, **opts):
1854 from mercurial import linelog
1881 from mercurial import linelog
1855
1882
1856 opts = _byteskwargs(opts)
1883 opts = _byteskwargs(opts)
1857
1884
1858 edits = opts[b'edits']
1885 edits = opts[b'edits']
1859 maxhunklines = opts[b'max_hunk_lines']
1886 maxhunklines = opts[b'max_hunk_lines']
1860
1887
1861 maxb1 = 100000
1888 maxb1 = 100000
1862 random.seed(0)
1889 random.seed(0)
1863 randint = random.randint
1890 randint = random.randint
1864 currentlines = 0
1891 currentlines = 0
1865 arglist = []
1892 arglist = []
1866 for rev in _xrange(edits):
1893 for rev in _xrange(edits):
1867 a1 = randint(0, currentlines)
1894 a1 = randint(0, currentlines)
1868 a2 = randint(a1, min(currentlines, a1 + maxhunklines))
1895 a2 = randint(a1, min(currentlines, a1 + maxhunklines))
1869 b1 = randint(0, maxb1)
1896 b1 = randint(0, maxb1)
1870 b2 = randint(b1, b1 + maxhunklines)
1897 b2 = randint(b1, b1 + maxhunklines)
1871 currentlines += (b2 - b1) - (a2 - a1)
1898 currentlines += (b2 - b1) - (a2 - a1)
1872 arglist.append((rev, a1, a2, b1, b2))
1899 arglist.append((rev, a1, a2, b1, b2))
1873
1900
1874 def d():
1901 def d():
1875 ll = linelog.linelog()
1902 ll = linelog.linelog()
1876 for args in arglist:
1903 for args in arglist:
1877 ll.replacelines(*args)
1904 ll.replacelines(*args)
1878
1905
1879 timer, fm = gettimer(ui, opts)
1906 timer, fm = gettimer(ui, opts)
1880 timer(d)
1907 timer(d)
1881 fm.end()
1908 fm.end()
1882
1909
1883
1910
1884 @command(b'perf::revrange|perfrevrange', formatteropts)
1911 @command(b'perf::revrange|perfrevrange', formatteropts)
1885 def perfrevrange(ui, repo, *specs, **opts):
1912 def perfrevrange(ui, repo, *specs, **opts):
1886 opts = _byteskwargs(opts)
1913 opts = _byteskwargs(opts)
1887 timer, fm = gettimer(ui, opts)
1914 timer, fm = gettimer(ui, opts)
1888 revrange = scmutil.revrange
1915 revrange = scmutil.revrange
1889 timer(lambda: len(revrange(repo, specs)))
1916 timer(lambda: len(revrange(repo, specs)))
1890 fm.end()
1917 fm.end()
1891
1918
1892
1919
1893 @command(b'perf::nodelookup|perfnodelookup', formatteropts)
1920 @command(b'perf::nodelookup|perfnodelookup', formatteropts)
1894 def perfnodelookup(ui, repo, rev, **opts):
1921 def perfnodelookup(ui, repo, rev, **opts):
1895 opts = _byteskwargs(opts)
1922 opts = _byteskwargs(opts)
1896 timer, fm = gettimer(ui, opts)
1923 timer, fm = gettimer(ui, opts)
1897 import mercurial.revlog
1924 import mercurial.revlog
1898
1925
1899 mercurial.revlog._prereadsize = 2 ** 24 # disable lazy parser in old hg
1926 mercurial.revlog._prereadsize = 2 ** 24 # disable lazy parser in old hg
1900 n = scmutil.revsingle(repo, rev).node()
1927 n = scmutil.revsingle(repo, rev).node()
1901
1928
1902 try:
1929 try:
1903 cl = revlog(getsvfs(repo), radix=b"00changelog")
1930 cl = revlog(getsvfs(repo), radix=b"00changelog")
1904 except TypeError:
1931 except TypeError:
1905 cl = revlog(getsvfs(repo), indexfile=b"00changelog.i")
1932 cl = revlog(getsvfs(repo), indexfile=b"00changelog.i")
1906
1933
1907 def d():
1934 def d():
1908 cl.rev(n)
1935 cl.rev(n)
1909 clearcaches(cl)
1936 clearcaches(cl)
1910
1937
1911 timer(d)
1938 timer(d)
1912 fm.end()
1939 fm.end()
1913
1940
1914
1941
1915 @command(
1942 @command(
1916 b'perf::log|perflog',
1943 b'perf::log|perflog',
1917 [(b'', b'rename', False, b'ask log to follow renames')] + formatteropts,
1944 [(b'', b'rename', False, b'ask log to follow renames')] + formatteropts,
1918 )
1945 )
1919 def perflog(ui, repo, rev=None, **opts):
1946 def perflog(ui, repo, rev=None, **opts):
1920 opts = _byteskwargs(opts)
1947 opts = _byteskwargs(opts)
1921 if rev is None:
1948 if rev is None:
1922 rev = []
1949 rev = []
1923 timer, fm = gettimer(ui, opts)
1950 timer, fm = gettimer(ui, opts)
1924 ui.pushbuffer()
1951 ui.pushbuffer()
1925 timer(
1952 timer(
1926 lambda: commands.log(
1953 lambda: commands.log(
1927 ui, repo, rev=rev, date=b'', user=b'', copies=opts.get(b'rename')
1954 ui, repo, rev=rev, date=b'', user=b'', copies=opts.get(b'rename')
1928 )
1955 )
1929 )
1956 )
1930 ui.popbuffer()
1957 ui.popbuffer()
1931 fm.end()
1958 fm.end()
1932
1959
1933
1960
1934 @command(b'perf::moonwalk|perfmoonwalk', formatteropts)
1961 @command(b'perf::moonwalk|perfmoonwalk', formatteropts)
1935 def perfmoonwalk(ui, repo, **opts):
1962 def perfmoonwalk(ui, repo, **opts):
1936 """benchmark walking the changelog backwards
1963 """benchmark walking the changelog backwards
1937
1964
1938 This also loads the changelog data for each revision in the changelog.
1965 This also loads the changelog data for each revision in the changelog.
1939 """
1966 """
1940 opts = _byteskwargs(opts)
1967 opts = _byteskwargs(opts)
1941 timer, fm = gettimer(ui, opts)
1968 timer, fm = gettimer(ui, opts)
1942
1969
1943 def moonwalk():
1970 def moonwalk():
1944 for i in repo.changelog.revs(start=(len(repo) - 1), stop=-1):
1971 for i in repo.changelog.revs(start=(len(repo) - 1), stop=-1):
1945 ctx = repo[i]
1972 ctx = repo[i]
1946 ctx.branch() # read changelog data (in addition to the index)
1973 ctx.branch() # read changelog data (in addition to the index)
1947
1974
1948 timer(moonwalk)
1975 timer(moonwalk)
1949 fm.end()
1976 fm.end()
1950
1977
1951
1978
1952 @command(
1979 @command(
1953 b'perf::templating|perftemplating',
1980 b'perf::templating|perftemplating',
1954 [
1981 [
1955 (b'r', b'rev', [], b'revisions to run the template on'),
1982 (b'r', b'rev', [], b'revisions to run the template on'),
1956 ]
1983 ]
1957 + formatteropts,
1984 + formatteropts,
1958 )
1985 )
1959 def perftemplating(ui, repo, testedtemplate=None, **opts):
1986 def perftemplating(ui, repo, testedtemplate=None, **opts):
1960 """test the rendering time of a given template"""
1987 """test the rendering time of a given template"""
1961 if makelogtemplater is None:
1988 if makelogtemplater is None:
1962 raise error.Abort(
1989 raise error.Abort(
1963 b"perftemplating not available with this Mercurial",
1990 b"perftemplating not available with this Mercurial",
1964 hint=b"use 4.3 or later",
1991 hint=b"use 4.3 or later",
1965 )
1992 )
1966
1993
1967 opts = _byteskwargs(opts)
1994 opts = _byteskwargs(opts)
1968
1995
1969 nullui = ui.copy()
1996 nullui = ui.copy()
1970 nullui.fout = open(os.devnull, 'wb')
1997 nullui.fout = open(os.devnull, 'wb')
1971 nullui.disablepager()
1998 nullui.disablepager()
1972 revs = opts.get(b'rev')
1999 revs = opts.get(b'rev')
1973 if not revs:
2000 if not revs:
1974 revs = [b'all()']
2001 revs = [b'all()']
1975 revs = list(scmutil.revrange(repo, revs))
2002 revs = list(scmutil.revrange(repo, revs))
1976
2003
1977 defaulttemplate = (
2004 defaulttemplate = (
1978 b'{date|shortdate} [{rev}:{node|short}]'
2005 b'{date|shortdate} [{rev}:{node|short}]'
1979 b' {author|person}: {desc|firstline}\n'
2006 b' {author|person}: {desc|firstline}\n'
1980 )
2007 )
1981 if testedtemplate is None:
2008 if testedtemplate is None:
1982 testedtemplate = defaulttemplate
2009 testedtemplate = defaulttemplate
1983 displayer = makelogtemplater(nullui, repo, testedtemplate)
2010 displayer = makelogtemplater(nullui, repo, testedtemplate)
1984
2011
1985 def format():
2012 def format():
1986 for r in revs:
2013 for r in revs:
1987 ctx = repo[r]
2014 ctx = repo[r]
1988 displayer.show(ctx)
2015 displayer.show(ctx)
1989 displayer.flush(ctx)
2016 displayer.flush(ctx)
1990
2017
1991 timer, fm = gettimer(ui, opts)
2018 timer, fm = gettimer(ui, opts)
1992 timer(format)
2019 timer(format)
1993 fm.end()
2020 fm.end()
1994
2021
1995
2022
1996 def _displaystats(ui, opts, entries, data):
2023 def _displaystats(ui, opts, entries, data):
1997 # use a second formatter because the data are quite different, not sure
2024 # use a second formatter because the data are quite different, not sure
1998 # how it flies with the templater.
2025 # how it flies with the templater.
1999 fm = ui.formatter(b'perf-stats', opts)
2026 fm = ui.formatter(b'perf-stats', opts)
2000 for key, title in entries:
2027 for key, title in entries:
2001 values = data[key]
2028 values = data[key]
2002 nbvalues = len(data)
2029 nbvalues = len(data)
2003 values.sort()
2030 values.sort()
2004 stats = {
2031 stats = {
2005 'key': key,
2032 'key': key,
2006 'title': title,
2033 'title': title,
2007 'nbitems': len(values),
2034 'nbitems': len(values),
2008 'min': values[0][0],
2035 'min': values[0][0],
2009 '10%': values[(nbvalues * 10) // 100][0],
2036 '10%': values[(nbvalues * 10) // 100][0],
2010 '25%': values[(nbvalues * 25) // 100][0],
2037 '25%': values[(nbvalues * 25) // 100][0],
2011 '50%': values[(nbvalues * 50) // 100][0],
2038 '50%': values[(nbvalues * 50) // 100][0],
2012 '75%': values[(nbvalues * 75) // 100][0],
2039 '75%': values[(nbvalues * 75) // 100][0],
2013 '80%': values[(nbvalues * 80) // 100][0],
2040 '80%': values[(nbvalues * 80) // 100][0],
2014 '85%': values[(nbvalues * 85) // 100][0],
2041 '85%': values[(nbvalues * 85) // 100][0],
2015 '90%': values[(nbvalues * 90) // 100][0],
2042 '90%': values[(nbvalues * 90) // 100][0],
2016 '95%': values[(nbvalues * 95) // 100][0],
2043 '95%': values[(nbvalues * 95) // 100][0],
2017 '99%': values[(nbvalues * 99) // 100][0],
2044 '99%': values[(nbvalues * 99) // 100][0],
2018 'max': values[-1][0],
2045 'max': values[-1][0],
2019 }
2046 }
2020 fm.startitem()
2047 fm.startitem()
2021 fm.data(**stats)
2048 fm.data(**stats)
2022 # make node pretty for the human output
2049 # make node pretty for the human output
2023 fm.plain('### %s (%d items)\n' % (title, len(values)))
2050 fm.plain('### %s (%d items)\n' % (title, len(values)))
2024 lines = [
2051 lines = [
2025 'min',
2052 'min',
2026 '10%',
2053 '10%',
2027 '25%',
2054 '25%',
2028 '50%',
2055 '50%',
2029 '75%',
2056 '75%',
2030 '80%',
2057 '80%',
2031 '85%',
2058 '85%',
2032 '90%',
2059 '90%',
2033 '95%',
2060 '95%',
2034 '99%',
2061 '99%',
2035 'max',
2062 'max',
2036 ]
2063 ]
2037 for l in lines:
2064 for l in lines:
2038 fm.plain('%s: %s\n' % (l, stats[l]))
2065 fm.plain('%s: %s\n' % (l, stats[l]))
2039 fm.end()
2066 fm.end()
2040
2067
2041
2068
2042 @command(
2069 @command(
2043 b'perf::helper-mergecopies|perfhelper-mergecopies',
2070 b'perf::helper-mergecopies|perfhelper-mergecopies',
2044 formatteropts
2071 formatteropts
2045 + [
2072 + [
2046 (b'r', b'revs', [], b'restrict search to these revisions'),
2073 (b'r', b'revs', [], b'restrict search to these revisions'),
2047 (b'', b'timing', False, b'provides extra data (costly)'),
2074 (b'', b'timing', False, b'provides extra data (costly)'),
2048 (b'', b'stats', False, b'provides statistic about the measured data'),
2075 (b'', b'stats', False, b'provides statistic about the measured data'),
2049 ],
2076 ],
2050 )
2077 )
2051 def perfhelpermergecopies(ui, repo, revs=[], **opts):
2078 def perfhelpermergecopies(ui, repo, revs=[], **opts):
2052 """find statistics about potential parameters for `perfmergecopies`
2079 """find statistics about potential parameters for `perfmergecopies`
2053
2080
2054 This command find (base, p1, p2) triplet relevant for copytracing
2081 This command find (base, p1, p2) triplet relevant for copytracing
2055 benchmarking in the context of a merge. It reports values for some of the
2082 benchmarking in the context of a merge. It reports values for some of the
2056 parameters that impact merge copy tracing time during merge.
2083 parameters that impact merge copy tracing time during merge.
2057
2084
2058 If `--timing` is set, rename detection is run and the associated timing
2085 If `--timing` is set, rename detection is run and the associated timing
2059 will be reported. The extra details come at the cost of slower command
2086 will be reported. The extra details come at the cost of slower command
2060 execution.
2087 execution.
2061
2088
2062 Since rename detection is only run once, other factors might easily
2089 Since rename detection is only run once, other factors might easily
2063 affect the precision of the timing. However it should give a good
2090 affect the precision of the timing. However it should give a good
2064 approximation of which revision triplets are very costly.
2091 approximation of which revision triplets are very costly.
2065 """
2092 """
2066 opts = _byteskwargs(opts)
2093 opts = _byteskwargs(opts)
2067 fm = ui.formatter(b'perf', opts)
2094 fm = ui.formatter(b'perf', opts)
2068 dotiming = opts[b'timing']
2095 dotiming = opts[b'timing']
2069 dostats = opts[b'stats']
2096 dostats = opts[b'stats']
2070
2097
2071 output_template = [
2098 output_template = [
2072 ("base", "%(base)12s"),
2099 ("base", "%(base)12s"),
2073 ("p1", "%(p1.node)12s"),
2100 ("p1", "%(p1.node)12s"),
2074 ("p2", "%(p2.node)12s"),
2101 ("p2", "%(p2.node)12s"),
2075 ("p1.nb-revs", "%(p1.nbrevs)12d"),
2102 ("p1.nb-revs", "%(p1.nbrevs)12d"),
2076 ("p1.nb-files", "%(p1.nbmissingfiles)12d"),
2103 ("p1.nb-files", "%(p1.nbmissingfiles)12d"),
2077 ("p1.renames", "%(p1.renamedfiles)12d"),
2104 ("p1.renames", "%(p1.renamedfiles)12d"),
2078 ("p1.time", "%(p1.time)12.3f"),
2105 ("p1.time", "%(p1.time)12.3f"),
2079 ("p2.nb-revs", "%(p2.nbrevs)12d"),
2106 ("p2.nb-revs", "%(p2.nbrevs)12d"),
2080 ("p2.nb-files", "%(p2.nbmissingfiles)12d"),
2107 ("p2.nb-files", "%(p2.nbmissingfiles)12d"),
2081 ("p2.renames", "%(p2.renamedfiles)12d"),
2108 ("p2.renames", "%(p2.renamedfiles)12d"),
2082 ("p2.time", "%(p2.time)12.3f"),
2109 ("p2.time", "%(p2.time)12.3f"),
2083 ("renames", "%(nbrenamedfiles)12d"),
2110 ("renames", "%(nbrenamedfiles)12d"),
2084 ("total.time", "%(time)12.3f"),
2111 ("total.time", "%(time)12.3f"),
2085 ]
2112 ]
2086 if not dotiming:
2113 if not dotiming:
2087 output_template = [
2114 output_template = [
2088 i
2115 i
2089 for i in output_template
2116 for i in output_template
2090 if not ('time' in i[0] or 'renames' in i[0])
2117 if not ('time' in i[0] or 'renames' in i[0])
2091 ]
2118 ]
2092 header_names = [h for (h, v) in output_template]
2119 header_names = [h for (h, v) in output_template]
2093 output = ' '.join([v for (h, v) in output_template]) + '\n'
2120 output = ' '.join([v for (h, v) in output_template]) + '\n'
2094 header = ' '.join(['%12s'] * len(header_names)) + '\n'
2121 header = ' '.join(['%12s'] * len(header_names)) + '\n'
2095 fm.plain(header % tuple(header_names))
2122 fm.plain(header % tuple(header_names))
2096
2123
2097 if not revs:
2124 if not revs:
2098 revs = ['all()']
2125 revs = ['all()']
2099 revs = scmutil.revrange(repo, revs)
2126 revs = scmutil.revrange(repo, revs)
2100
2127
2101 if dostats:
2128 if dostats:
2102 alldata = {
2129 alldata = {
2103 'nbrevs': [],
2130 'nbrevs': [],
2104 'nbmissingfiles': [],
2131 'nbmissingfiles': [],
2105 }
2132 }
2106 if dotiming:
2133 if dotiming:
2107 alldata['parentnbrenames'] = []
2134 alldata['parentnbrenames'] = []
2108 alldata['totalnbrenames'] = []
2135 alldata['totalnbrenames'] = []
2109 alldata['parenttime'] = []
2136 alldata['parenttime'] = []
2110 alldata['totaltime'] = []
2137 alldata['totaltime'] = []
2111
2138
2112 roi = repo.revs('merge() and %ld', revs)
2139 roi = repo.revs('merge() and %ld', revs)
2113 for r in roi:
2140 for r in roi:
2114 ctx = repo[r]
2141 ctx = repo[r]
2115 p1 = ctx.p1()
2142 p1 = ctx.p1()
2116 p2 = ctx.p2()
2143 p2 = ctx.p2()
2117 bases = repo.changelog._commonancestorsheads(p1.rev(), p2.rev())
2144 bases = repo.changelog._commonancestorsheads(p1.rev(), p2.rev())
2118 for b in bases:
2145 for b in bases:
2119 b = repo[b]
2146 b = repo[b]
2120 p1missing = copies._computeforwardmissing(b, p1)
2147 p1missing = copies._computeforwardmissing(b, p1)
2121 p2missing = copies._computeforwardmissing(b, p2)
2148 p2missing = copies._computeforwardmissing(b, p2)
2122 data = {
2149 data = {
2123 b'base': b.hex(),
2150 b'base': b.hex(),
2124 b'p1.node': p1.hex(),
2151 b'p1.node': p1.hex(),
2125 b'p1.nbrevs': len(repo.revs('only(%d, %d)', p1.rev(), b.rev())),
2152 b'p1.nbrevs': len(repo.revs('only(%d, %d)', p1.rev(), b.rev())),
2126 b'p1.nbmissingfiles': len(p1missing),
2153 b'p1.nbmissingfiles': len(p1missing),
2127 b'p2.node': p2.hex(),
2154 b'p2.node': p2.hex(),
2128 b'p2.nbrevs': len(repo.revs('only(%d, %d)', p2.rev(), b.rev())),
2155 b'p2.nbrevs': len(repo.revs('only(%d, %d)', p2.rev(), b.rev())),
2129 b'p2.nbmissingfiles': len(p2missing),
2156 b'p2.nbmissingfiles': len(p2missing),
2130 }
2157 }
2131 if dostats:
2158 if dostats:
2132 if p1missing:
2159 if p1missing:
2133 alldata['nbrevs'].append(
2160 alldata['nbrevs'].append(
2134 (data['p1.nbrevs'], b.hex(), p1.hex())
2161 (data['p1.nbrevs'], b.hex(), p1.hex())
2135 )
2162 )
2136 alldata['nbmissingfiles'].append(
2163 alldata['nbmissingfiles'].append(
2137 (data['p1.nbmissingfiles'], b.hex(), p1.hex())
2164 (data['p1.nbmissingfiles'], b.hex(), p1.hex())
2138 )
2165 )
2139 if p2missing:
2166 if p2missing:
2140 alldata['nbrevs'].append(
2167 alldata['nbrevs'].append(
2141 (data['p2.nbrevs'], b.hex(), p2.hex())
2168 (data['p2.nbrevs'], b.hex(), p2.hex())
2142 )
2169 )
2143 alldata['nbmissingfiles'].append(
2170 alldata['nbmissingfiles'].append(
2144 (data['p2.nbmissingfiles'], b.hex(), p2.hex())
2171 (data['p2.nbmissingfiles'], b.hex(), p2.hex())
2145 )
2172 )
2146 if dotiming:
2173 if dotiming:
2147 begin = util.timer()
2174 begin = util.timer()
2148 mergedata = copies.mergecopies(repo, p1, p2, b)
2175 mergedata = copies.mergecopies(repo, p1, p2, b)
2149 end = util.timer()
2176 end = util.timer()
2150 # not very stable timing since we did only one run
2177 # not very stable timing since we did only one run
2151 data['time'] = end - begin
2178 data['time'] = end - begin
2152 # mergedata contains five dicts: "copy", "movewithdir",
2179 # mergedata contains five dicts: "copy", "movewithdir",
2153 # "diverge", "renamedelete" and "dirmove".
2180 # "diverge", "renamedelete" and "dirmove".
2154 # The first 4 are about renamed file so lets count that.
2181 # The first 4 are about renamed file so lets count that.
2155 renames = len(mergedata[0])
2182 renames = len(mergedata[0])
2156 renames += len(mergedata[1])
2183 renames += len(mergedata[1])
2157 renames += len(mergedata[2])
2184 renames += len(mergedata[2])
2158 renames += len(mergedata[3])
2185 renames += len(mergedata[3])
2159 data['nbrenamedfiles'] = renames
2186 data['nbrenamedfiles'] = renames
2160 begin = util.timer()
2187 begin = util.timer()
2161 p1renames = copies.pathcopies(b, p1)
2188 p1renames = copies.pathcopies(b, p1)
2162 end = util.timer()
2189 end = util.timer()
2163 data['p1.time'] = end - begin
2190 data['p1.time'] = end - begin
2164 begin = util.timer()
2191 begin = util.timer()
2165 p2renames = copies.pathcopies(b, p2)
2192 p2renames = copies.pathcopies(b, p2)
2166 end = util.timer()
2193 end = util.timer()
2167 data['p2.time'] = end - begin
2194 data['p2.time'] = end - begin
2168 data['p1.renamedfiles'] = len(p1renames)
2195 data['p1.renamedfiles'] = len(p1renames)
2169 data['p2.renamedfiles'] = len(p2renames)
2196 data['p2.renamedfiles'] = len(p2renames)
2170
2197
2171 if dostats:
2198 if dostats:
2172 if p1missing:
2199 if p1missing:
2173 alldata['parentnbrenames'].append(
2200 alldata['parentnbrenames'].append(
2174 (data['p1.renamedfiles'], b.hex(), p1.hex())
2201 (data['p1.renamedfiles'], b.hex(), p1.hex())
2175 )
2202 )
2176 alldata['parenttime'].append(
2203 alldata['parenttime'].append(
2177 (data['p1.time'], b.hex(), p1.hex())
2204 (data['p1.time'], b.hex(), p1.hex())
2178 )
2205 )
2179 if p2missing:
2206 if p2missing:
2180 alldata['parentnbrenames'].append(
2207 alldata['parentnbrenames'].append(
2181 (data['p2.renamedfiles'], b.hex(), p2.hex())
2208 (data['p2.renamedfiles'], b.hex(), p2.hex())
2182 )
2209 )
2183 alldata['parenttime'].append(
2210 alldata['parenttime'].append(
2184 (data['p2.time'], b.hex(), p2.hex())
2211 (data['p2.time'], b.hex(), p2.hex())
2185 )
2212 )
2186 if p1missing or p2missing:
2213 if p1missing or p2missing:
2187 alldata['totalnbrenames'].append(
2214 alldata['totalnbrenames'].append(
2188 (
2215 (
2189 data['nbrenamedfiles'],
2216 data['nbrenamedfiles'],
2190 b.hex(),
2217 b.hex(),
2191 p1.hex(),
2218 p1.hex(),
2192 p2.hex(),
2219 p2.hex(),
2193 )
2220 )
2194 )
2221 )
2195 alldata['totaltime'].append(
2222 alldata['totaltime'].append(
2196 (data['time'], b.hex(), p1.hex(), p2.hex())
2223 (data['time'], b.hex(), p1.hex(), p2.hex())
2197 )
2224 )
2198 fm.startitem()
2225 fm.startitem()
2199 fm.data(**data)
2226 fm.data(**data)
2200 # make node pretty for the human output
2227 # make node pretty for the human output
2201 out = data.copy()
2228 out = data.copy()
2202 out['base'] = fm.hexfunc(b.node())
2229 out['base'] = fm.hexfunc(b.node())
2203 out['p1.node'] = fm.hexfunc(p1.node())
2230 out['p1.node'] = fm.hexfunc(p1.node())
2204 out['p2.node'] = fm.hexfunc(p2.node())
2231 out['p2.node'] = fm.hexfunc(p2.node())
2205 fm.plain(output % out)
2232 fm.plain(output % out)
2206
2233
2207 fm.end()
2234 fm.end()
2208 if dostats:
2235 if dostats:
2209 # use a second formatter because the data are quite different, not sure
2236 # use a second formatter because the data are quite different, not sure
2210 # how it flies with the templater.
2237 # how it flies with the templater.
2211 entries = [
2238 entries = [
2212 ('nbrevs', 'number of revision covered'),
2239 ('nbrevs', 'number of revision covered'),
2213 ('nbmissingfiles', 'number of missing files at head'),
2240 ('nbmissingfiles', 'number of missing files at head'),
2214 ]
2241 ]
2215 if dotiming:
2242 if dotiming:
2216 entries.append(
2243 entries.append(
2217 ('parentnbrenames', 'rename from one parent to base')
2244 ('parentnbrenames', 'rename from one parent to base')
2218 )
2245 )
2219 entries.append(('totalnbrenames', 'total number of renames'))
2246 entries.append(('totalnbrenames', 'total number of renames'))
2220 entries.append(('parenttime', 'time for one parent'))
2247 entries.append(('parenttime', 'time for one parent'))
2221 entries.append(('totaltime', 'time for both parents'))
2248 entries.append(('totaltime', 'time for both parents'))
2222 _displaystats(ui, opts, entries, alldata)
2249 _displaystats(ui, opts, entries, alldata)
2223
2250
2224
2251
2225 @command(
2252 @command(
2226 b'perf::helper-pathcopies|perfhelper-pathcopies',
2253 b'perf::helper-pathcopies|perfhelper-pathcopies',
2227 formatteropts
2254 formatteropts
2228 + [
2255 + [
2229 (b'r', b'revs', [], b'restrict search to these revisions'),
2256 (b'r', b'revs', [], b'restrict search to these revisions'),
2230 (b'', b'timing', False, b'provides extra data (costly)'),
2257 (b'', b'timing', False, b'provides extra data (costly)'),
2231 (b'', b'stats', False, b'provides statistic about the measured data'),
2258 (b'', b'stats', False, b'provides statistic about the measured data'),
2232 ],
2259 ],
2233 )
2260 )
2234 def perfhelperpathcopies(ui, repo, revs=[], **opts):
2261 def perfhelperpathcopies(ui, repo, revs=[], **opts):
2235 """find statistic about potential parameters for the `perftracecopies`
2262 """find statistic about potential parameters for the `perftracecopies`
2236
2263
2237 This command find source-destination pair relevant for copytracing testing.
2264 This command find source-destination pair relevant for copytracing testing.
2238 It report value for some of the parameters that impact copy tracing time.
2265 It report value for some of the parameters that impact copy tracing time.
2239
2266
2240 If `--timing` is set, rename detection is run and the associated timing
2267 If `--timing` is set, rename detection is run and the associated timing
2241 will be reported. The extra details comes at the cost of a slower command
2268 will be reported. The extra details comes at the cost of a slower command
2242 execution.
2269 execution.
2243
2270
2244 Since the rename detection is only run once, other factors might easily
2271 Since the rename detection is only run once, other factors might easily
2245 affect the precision of the timing. However it should give a good
2272 affect the precision of the timing. However it should give a good
2246 approximation of which revision pairs are very costly.
2273 approximation of which revision pairs are very costly.
2247 """
2274 """
2248 opts = _byteskwargs(opts)
2275 opts = _byteskwargs(opts)
2249 fm = ui.formatter(b'perf', opts)
2276 fm = ui.formatter(b'perf', opts)
2250 dotiming = opts[b'timing']
2277 dotiming = opts[b'timing']
2251 dostats = opts[b'stats']
2278 dostats = opts[b'stats']
2252
2279
2253 if dotiming:
2280 if dotiming:
2254 header = '%12s %12s %12s %12s %12s %12s\n'
2281 header = '%12s %12s %12s %12s %12s %12s\n'
2255 output = (
2282 output = (
2256 "%(source)12s %(destination)12s "
2283 "%(source)12s %(destination)12s "
2257 "%(nbrevs)12d %(nbmissingfiles)12d "
2284 "%(nbrevs)12d %(nbmissingfiles)12d "
2258 "%(nbrenamedfiles)12d %(time)18.5f\n"
2285 "%(nbrenamedfiles)12d %(time)18.5f\n"
2259 )
2286 )
2260 header_names = (
2287 header_names = (
2261 "source",
2288 "source",
2262 "destination",
2289 "destination",
2263 "nb-revs",
2290 "nb-revs",
2264 "nb-files",
2291 "nb-files",
2265 "nb-renames",
2292 "nb-renames",
2266 "time",
2293 "time",
2267 )
2294 )
2268 fm.plain(header % header_names)
2295 fm.plain(header % header_names)
2269 else:
2296 else:
2270 header = '%12s %12s %12s %12s\n'
2297 header = '%12s %12s %12s %12s\n'
2271 output = (
2298 output = (
2272 "%(source)12s %(destination)12s "
2299 "%(source)12s %(destination)12s "
2273 "%(nbrevs)12d %(nbmissingfiles)12d\n"
2300 "%(nbrevs)12d %(nbmissingfiles)12d\n"
2274 )
2301 )
2275 fm.plain(header % ("source", "destination", "nb-revs", "nb-files"))
2302 fm.plain(header % ("source", "destination", "nb-revs", "nb-files"))
2276
2303
2277 if not revs:
2304 if not revs:
2278 revs = ['all()']
2305 revs = ['all()']
2279 revs = scmutil.revrange(repo, revs)
2306 revs = scmutil.revrange(repo, revs)
2280
2307
2281 if dostats:
2308 if dostats:
2282 alldata = {
2309 alldata = {
2283 'nbrevs': [],
2310 'nbrevs': [],
2284 'nbmissingfiles': [],
2311 'nbmissingfiles': [],
2285 }
2312 }
2286 if dotiming:
2313 if dotiming:
2287 alldata['nbrenames'] = []
2314 alldata['nbrenames'] = []
2288 alldata['time'] = []
2315 alldata['time'] = []
2289
2316
2290 roi = repo.revs('merge() and %ld', revs)
2317 roi = repo.revs('merge() and %ld', revs)
2291 for r in roi:
2318 for r in roi:
2292 ctx = repo[r]
2319 ctx = repo[r]
2293 p1 = ctx.p1().rev()
2320 p1 = ctx.p1().rev()
2294 p2 = ctx.p2().rev()
2321 p2 = ctx.p2().rev()
2295 bases = repo.changelog._commonancestorsheads(p1, p2)
2322 bases = repo.changelog._commonancestorsheads(p1, p2)
2296 for p in (p1, p2):
2323 for p in (p1, p2):
2297 for b in bases:
2324 for b in bases:
2298 base = repo[b]
2325 base = repo[b]
2299 parent = repo[p]
2326 parent = repo[p]
2300 missing = copies._computeforwardmissing(base, parent)
2327 missing = copies._computeforwardmissing(base, parent)
2301 if not missing:
2328 if not missing:
2302 continue
2329 continue
2303 data = {
2330 data = {
2304 b'source': base.hex(),
2331 b'source': base.hex(),
2305 b'destination': parent.hex(),
2332 b'destination': parent.hex(),
2306 b'nbrevs': len(repo.revs('only(%d, %d)', p, b)),
2333 b'nbrevs': len(repo.revs('only(%d, %d)', p, b)),
2307 b'nbmissingfiles': len(missing),
2334 b'nbmissingfiles': len(missing),
2308 }
2335 }
2309 if dostats:
2336 if dostats:
2310 alldata['nbrevs'].append(
2337 alldata['nbrevs'].append(
2311 (
2338 (
2312 data['nbrevs'],
2339 data['nbrevs'],
2313 base.hex(),
2340 base.hex(),
2314 parent.hex(),
2341 parent.hex(),
2315 )
2342 )
2316 )
2343 )
2317 alldata['nbmissingfiles'].append(
2344 alldata['nbmissingfiles'].append(
2318 (
2345 (
2319 data['nbmissingfiles'],
2346 data['nbmissingfiles'],
2320 base.hex(),
2347 base.hex(),
2321 parent.hex(),
2348 parent.hex(),
2322 )
2349 )
2323 )
2350 )
2324 if dotiming:
2351 if dotiming:
2325 begin = util.timer()
2352 begin = util.timer()
2326 renames = copies.pathcopies(base, parent)
2353 renames = copies.pathcopies(base, parent)
2327 end = util.timer()
2354 end = util.timer()
2328 # not very stable timing since we did only one run
2355 # not very stable timing since we did only one run
2329 data['time'] = end - begin
2356 data['time'] = end - begin
2330 data['nbrenamedfiles'] = len(renames)
2357 data['nbrenamedfiles'] = len(renames)
2331 if dostats:
2358 if dostats:
2332 alldata['time'].append(
2359 alldata['time'].append(
2333 (
2360 (
2334 data['time'],
2361 data['time'],
2335 base.hex(),
2362 base.hex(),
2336 parent.hex(),
2363 parent.hex(),
2337 )
2364 )
2338 )
2365 )
2339 alldata['nbrenames'].append(
2366 alldata['nbrenames'].append(
2340 (
2367 (
2341 data['nbrenamedfiles'],
2368 data['nbrenamedfiles'],
2342 base.hex(),
2369 base.hex(),
2343 parent.hex(),
2370 parent.hex(),
2344 )
2371 )
2345 )
2372 )
2346 fm.startitem()
2373 fm.startitem()
2347 fm.data(**data)
2374 fm.data(**data)
2348 out = data.copy()
2375 out = data.copy()
2349 out['source'] = fm.hexfunc(base.node())
2376 out['source'] = fm.hexfunc(base.node())
2350 out['destination'] = fm.hexfunc(parent.node())
2377 out['destination'] = fm.hexfunc(parent.node())
2351 fm.plain(output % out)
2378 fm.plain(output % out)
2352
2379
2353 fm.end()
2380 fm.end()
2354 if dostats:
2381 if dostats:
2355 entries = [
2382 entries = [
2356 ('nbrevs', 'number of revision covered'),
2383 ('nbrevs', 'number of revision covered'),
2357 ('nbmissingfiles', 'number of missing files at head'),
2384 ('nbmissingfiles', 'number of missing files at head'),
2358 ]
2385 ]
2359 if dotiming:
2386 if dotiming:
2360 entries.append(('nbrenames', 'renamed files'))
2387 entries.append(('nbrenames', 'renamed files'))
2361 entries.append(('time', 'time'))
2388 entries.append(('time', 'time'))
2362 _displaystats(ui, opts, entries, alldata)
2389 _displaystats(ui, opts, entries, alldata)
2363
2390
2364
2391
2365 @command(b'perf::cca|perfcca', formatteropts)
2392 @command(b'perf::cca|perfcca', formatteropts)
2366 def perfcca(ui, repo, **opts):
2393 def perfcca(ui, repo, **opts):
2367 opts = _byteskwargs(opts)
2394 opts = _byteskwargs(opts)
2368 timer, fm = gettimer(ui, opts)
2395 timer, fm = gettimer(ui, opts)
2369 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
2396 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
2370 fm.end()
2397 fm.end()
2371
2398
2372
2399
2373 @command(b'perf::fncacheload|perffncacheload', formatteropts)
2400 @command(b'perf::fncacheload|perffncacheload', formatteropts)
2374 def perffncacheload(ui, repo, **opts):
2401 def perffncacheload(ui, repo, **opts):
2375 opts = _byteskwargs(opts)
2402 opts = _byteskwargs(opts)
2376 timer, fm = gettimer(ui, opts)
2403 timer, fm = gettimer(ui, opts)
2377 s = repo.store
2404 s = repo.store
2378
2405
2379 def d():
2406 def d():
2380 s.fncache._load()
2407 s.fncache._load()
2381
2408
2382 timer(d)
2409 timer(d)
2383 fm.end()
2410 fm.end()
2384
2411
2385
2412
2386 @command(b'perf::fncachewrite|perffncachewrite', formatteropts)
2413 @command(b'perf::fncachewrite|perffncachewrite', formatteropts)
2387 def perffncachewrite(ui, repo, **opts):
2414 def perffncachewrite(ui, repo, **opts):
2388 opts = _byteskwargs(opts)
2415 opts = _byteskwargs(opts)
2389 timer, fm = gettimer(ui, opts)
2416 timer, fm = gettimer(ui, opts)
2390 s = repo.store
2417 s = repo.store
2391 lock = repo.lock()
2418 lock = repo.lock()
2392 s.fncache._load()
2419 s.fncache._load()
2393 tr = repo.transaction(b'perffncachewrite')
2420 tr = repo.transaction(b'perffncachewrite')
2394 tr.addbackup(b'fncache')
2421 tr.addbackup(b'fncache')
2395
2422
2396 def d():
2423 def d():
2397 s.fncache._dirty = True
2424 s.fncache._dirty = True
2398 s.fncache.write(tr)
2425 s.fncache.write(tr)
2399
2426
2400 timer(d)
2427 timer(d)
2401 tr.close()
2428 tr.close()
2402 lock.release()
2429 lock.release()
2403 fm.end()
2430 fm.end()
2404
2431
2405
2432
2406 @command(b'perf::fncacheencode|perffncacheencode', formatteropts)
2433 @command(b'perf::fncacheencode|perffncacheencode', formatteropts)
2407 def perffncacheencode(ui, repo, **opts):
2434 def perffncacheencode(ui, repo, **opts):
2408 opts = _byteskwargs(opts)
2435 opts = _byteskwargs(opts)
2409 timer, fm = gettimer(ui, opts)
2436 timer, fm = gettimer(ui, opts)
2410 s = repo.store
2437 s = repo.store
2411 s.fncache._load()
2438 s.fncache._load()
2412
2439
2413 def d():
2440 def d():
2414 for p in s.fncache.entries:
2441 for p in s.fncache.entries:
2415 s.encode(p)
2442 s.encode(p)
2416
2443
2417 timer(d)
2444 timer(d)
2418 fm.end()
2445 fm.end()
2419
2446
2420
2447
2421 def _bdiffworker(q, blocks, xdiff, ready, done):
2448 def _bdiffworker(q, blocks, xdiff, ready, done):
2422 while not done.is_set():
2449 while not done.is_set():
2423 pair = q.get()
2450 pair = q.get()
2424 while pair is not None:
2451 while pair is not None:
2425 if xdiff:
2452 if xdiff:
2426 mdiff.bdiff.xdiffblocks(*pair)
2453 mdiff.bdiff.xdiffblocks(*pair)
2427 elif blocks:
2454 elif blocks:
2428 mdiff.bdiff.blocks(*pair)
2455 mdiff.bdiff.blocks(*pair)
2429 else:
2456 else:
2430 mdiff.textdiff(*pair)
2457 mdiff.textdiff(*pair)
2431 q.task_done()
2458 q.task_done()
2432 pair = q.get()
2459 pair = q.get()
2433 q.task_done() # for the None one
2460 q.task_done() # for the None one
2434 with ready:
2461 with ready:
2435 ready.wait()
2462 ready.wait()
2436
2463
2437
2464
2438 def _manifestrevision(repo, mnode):
2465 def _manifestrevision(repo, mnode):
2439 ml = repo.manifestlog
2466 ml = repo.manifestlog
2440
2467
2441 if util.safehasattr(ml, b'getstorage'):
2468 if util.safehasattr(ml, b'getstorage'):
2442 store = ml.getstorage(b'')
2469 store = ml.getstorage(b'')
2443 else:
2470 else:
2444 store = ml._revlog
2471 store = ml._revlog
2445
2472
2446 return store.revision(mnode)
2473 return store.revision(mnode)
2447
2474
2448
2475
2449 @command(
2476 @command(
2450 b'perf::bdiff|perfbdiff',
2477 b'perf::bdiff|perfbdiff',
2451 revlogopts
2478 revlogopts
2452 + formatteropts
2479 + formatteropts
2453 + [
2480 + [
2454 (
2481 (
2455 b'',
2482 b'',
2456 b'count',
2483 b'count',
2457 1,
2484 1,
2458 b'number of revisions to test (when using --startrev)',
2485 b'number of revisions to test (when using --startrev)',
2459 ),
2486 ),
2460 (b'', b'alldata', False, b'test bdiffs for all associated revisions'),
2487 (b'', b'alldata', False, b'test bdiffs for all associated revisions'),
2461 (b'', b'threads', 0, b'number of thread to use (disable with 0)'),
2488 (b'', b'threads', 0, b'number of thread to use (disable with 0)'),
2462 (b'', b'blocks', False, b'test computing diffs into blocks'),
2489 (b'', b'blocks', False, b'test computing diffs into blocks'),
2463 (b'', b'xdiff', False, b'use xdiff algorithm'),
2490 (b'', b'xdiff', False, b'use xdiff algorithm'),
2464 ],
2491 ],
2465 b'-c|-m|FILE REV',
2492 b'-c|-m|FILE REV',
2466 )
2493 )
2467 def perfbdiff(ui, repo, file_, rev=None, count=None, threads=0, **opts):
2494 def perfbdiff(ui, repo, file_, rev=None, count=None, threads=0, **opts):
2468 """benchmark a bdiff between revisions
2495 """benchmark a bdiff between revisions
2469
2496
2470 By default, benchmark a bdiff between its delta parent and itself.
2497 By default, benchmark a bdiff between its delta parent and itself.
2471
2498
2472 With ``--count``, benchmark bdiffs between delta parents and self for N
2499 With ``--count``, benchmark bdiffs between delta parents and self for N
2473 revisions starting at the specified revision.
2500 revisions starting at the specified revision.
2474
2501
2475 With ``--alldata``, assume the requested revision is a changeset and
2502 With ``--alldata``, assume the requested revision is a changeset and
2476 measure bdiffs for all changes related to that changeset (manifest
2503 measure bdiffs for all changes related to that changeset (manifest
2477 and filelogs).
2504 and filelogs).
2478 """
2505 """
2479 opts = _byteskwargs(opts)
2506 opts = _byteskwargs(opts)
2480
2507
2481 if opts[b'xdiff'] and not opts[b'blocks']:
2508 if opts[b'xdiff'] and not opts[b'blocks']:
2482 raise error.CommandError(b'perfbdiff', b'--xdiff requires --blocks')
2509 raise error.CommandError(b'perfbdiff', b'--xdiff requires --blocks')
2483
2510
2484 if opts[b'alldata']:
2511 if opts[b'alldata']:
2485 opts[b'changelog'] = True
2512 opts[b'changelog'] = True
2486
2513
2487 if opts.get(b'changelog') or opts.get(b'manifest'):
2514 if opts.get(b'changelog') or opts.get(b'manifest'):
2488 file_, rev = None, file_
2515 file_, rev = None, file_
2489 elif rev is None:
2516 elif rev is None:
2490 raise error.CommandError(b'perfbdiff', b'invalid arguments')
2517 raise error.CommandError(b'perfbdiff', b'invalid arguments')
2491
2518
2492 blocks = opts[b'blocks']
2519 blocks = opts[b'blocks']
2493 xdiff = opts[b'xdiff']
2520 xdiff = opts[b'xdiff']
2494 textpairs = []
2521 textpairs = []
2495
2522
2496 r = cmdutil.openrevlog(repo, b'perfbdiff', file_, opts)
2523 r = cmdutil.openrevlog(repo, b'perfbdiff', file_, opts)
2497
2524
2498 startrev = r.rev(r.lookup(rev))
2525 startrev = r.rev(r.lookup(rev))
2499 for rev in range(startrev, min(startrev + count, len(r) - 1)):
2526 for rev in range(startrev, min(startrev + count, len(r) - 1)):
2500 if opts[b'alldata']:
2527 if opts[b'alldata']:
2501 # Load revisions associated with changeset.
2528 # Load revisions associated with changeset.
2502 ctx = repo[rev]
2529 ctx = repo[rev]
2503 mtext = _manifestrevision(repo, ctx.manifestnode())
2530 mtext = _manifestrevision(repo, ctx.manifestnode())
2504 for pctx in ctx.parents():
2531 for pctx in ctx.parents():
2505 pman = _manifestrevision(repo, pctx.manifestnode())
2532 pman = _manifestrevision(repo, pctx.manifestnode())
2506 textpairs.append((pman, mtext))
2533 textpairs.append((pman, mtext))
2507
2534
2508 # Load filelog revisions by iterating manifest delta.
2535 # Load filelog revisions by iterating manifest delta.
2509 man = ctx.manifest()
2536 man = ctx.manifest()
2510 pman = ctx.p1().manifest()
2537 pman = ctx.p1().manifest()
2511 for filename, change in pman.diff(man).items():
2538 for filename, change in pman.diff(man).items():
2512 fctx = repo.file(filename)
2539 fctx = repo.file(filename)
2513 f1 = fctx.revision(change[0][0] or -1)
2540 f1 = fctx.revision(change[0][0] or -1)
2514 f2 = fctx.revision(change[1][0] or -1)
2541 f2 = fctx.revision(change[1][0] or -1)
2515 textpairs.append((f1, f2))
2542 textpairs.append((f1, f2))
2516 else:
2543 else:
2517 dp = r.deltaparent(rev)
2544 dp = r.deltaparent(rev)
2518 textpairs.append((r.revision(dp), r.revision(rev)))
2545 textpairs.append((r.revision(dp), r.revision(rev)))
2519
2546
2520 withthreads = threads > 0
2547 withthreads = threads > 0
2521 if not withthreads:
2548 if not withthreads:
2522
2549
2523 def d():
2550 def d():
2524 for pair in textpairs:
2551 for pair in textpairs:
2525 if xdiff:
2552 if xdiff:
2526 mdiff.bdiff.xdiffblocks(*pair)
2553 mdiff.bdiff.xdiffblocks(*pair)
2527 elif blocks:
2554 elif blocks:
2528 mdiff.bdiff.blocks(*pair)
2555 mdiff.bdiff.blocks(*pair)
2529 else:
2556 else:
2530 mdiff.textdiff(*pair)
2557 mdiff.textdiff(*pair)
2531
2558
2532 else:
2559 else:
2533 q = queue()
2560 q = queue()
2534 for i in _xrange(threads):
2561 for i in _xrange(threads):
2535 q.put(None)
2562 q.put(None)
2536 ready = threading.Condition()
2563 ready = threading.Condition()
2537 done = threading.Event()
2564 done = threading.Event()
2538 for i in _xrange(threads):
2565 for i in _xrange(threads):
2539 threading.Thread(
2566 threading.Thread(
2540 target=_bdiffworker, args=(q, blocks, xdiff, ready, done)
2567 target=_bdiffworker, args=(q, blocks, xdiff, ready, done)
2541 ).start()
2568 ).start()
2542 q.join()
2569 q.join()
2543
2570
2544 def d():
2571 def d():
2545 for pair in textpairs:
2572 for pair in textpairs:
2546 q.put(pair)
2573 q.put(pair)
2547 for i in _xrange(threads):
2574 for i in _xrange(threads):
2548 q.put(None)
2575 q.put(None)
2549 with ready:
2576 with ready:
2550 ready.notify_all()
2577 ready.notify_all()
2551 q.join()
2578 q.join()
2552
2579
2553 timer, fm = gettimer(ui, opts)
2580 timer, fm = gettimer(ui, opts)
2554 timer(d)
2581 timer(d)
2555 fm.end()
2582 fm.end()
2556
2583
2557 if withthreads:
2584 if withthreads:
2558 done.set()
2585 done.set()
2559 for i in _xrange(threads):
2586 for i in _xrange(threads):
2560 q.put(None)
2587 q.put(None)
2561 with ready:
2588 with ready:
2562 ready.notify_all()
2589 ready.notify_all()
2563
2590
2564
2591
2565 @command(
2592 @command(
2566 b'perf::unidiff|perfunidiff',
2593 b'perf::unidiff|perfunidiff',
2567 revlogopts
2594 revlogopts
2568 + formatteropts
2595 + formatteropts
2569 + [
2596 + [
2570 (
2597 (
2571 b'',
2598 b'',
2572 b'count',
2599 b'count',
2573 1,
2600 1,
2574 b'number of revisions to test (when using --startrev)',
2601 b'number of revisions to test (when using --startrev)',
2575 ),
2602 ),
2576 (b'', b'alldata', False, b'test unidiffs for all associated revisions'),
2603 (b'', b'alldata', False, b'test unidiffs for all associated revisions'),
2577 ],
2604 ],
2578 b'-c|-m|FILE REV',
2605 b'-c|-m|FILE REV',
2579 )
2606 )
2580 def perfunidiff(ui, repo, file_, rev=None, count=None, **opts):
2607 def perfunidiff(ui, repo, file_, rev=None, count=None, **opts):
2581 """benchmark a unified diff between revisions
2608 """benchmark a unified diff between revisions
2582
2609
2583 This doesn't include any copy tracing - it's just a unified diff
2610 This doesn't include any copy tracing - it's just a unified diff
2584 of the texts.
2611 of the texts.
2585
2612
2586 By default, benchmark a diff between its delta parent and itself.
2613 By default, benchmark a diff between its delta parent and itself.
2587
2614
2588 With ``--count``, benchmark diffs between delta parents and self for N
2615 With ``--count``, benchmark diffs between delta parents and self for N
2589 revisions starting at the specified revision.
2616 revisions starting at the specified revision.
2590
2617
2591 With ``--alldata``, assume the requested revision is a changeset and
2618 With ``--alldata``, assume the requested revision is a changeset and
2592 measure diffs for all changes related to that changeset (manifest
2619 measure diffs for all changes related to that changeset (manifest
2593 and filelogs).
2620 and filelogs).
2594 """
2621 """
2595 opts = _byteskwargs(opts)
2622 opts = _byteskwargs(opts)
2596 if opts[b'alldata']:
2623 if opts[b'alldata']:
2597 opts[b'changelog'] = True
2624 opts[b'changelog'] = True
2598
2625
2599 if opts.get(b'changelog') or opts.get(b'manifest'):
2626 if opts.get(b'changelog') or opts.get(b'manifest'):
2600 file_, rev = None, file_
2627 file_, rev = None, file_
2601 elif rev is None:
2628 elif rev is None:
2602 raise error.CommandError(b'perfunidiff', b'invalid arguments')
2629 raise error.CommandError(b'perfunidiff', b'invalid arguments')
2603
2630
2604 textpairs = []
2631 textpairs = []
2605
2632
2606 r = cmdutil.openrevlog(repo, b'perfunidiff', file_, opts)
2633 r = cmdutil.openrevlog(repo, b'perfunidiff', file_, opts)
2607
2634
2608 startrev = r.rev(r.lookup(rev))
2635 startrev = r.rev(r.lookup(rev))
2609 for rev in range(startrev, min(startrev + count, len(r) - 1)):
2636 for rev in range(startrev, min(startrev + count, len(r) - 1)):
2610 if opts[b'alldata']:
2637 if opts[b'alldata']:
2611 # Load revisions associated with changeset.
2638 # Load revisions associated with changeset.
2612 ctx = repo[rev]
2639 ctx = repo[rev]
2613 mtext = _manifestrevision(repo, ctx.manifestnode())
2640 mtext = _manifestrevision(repo, ctx.manifestnode())
2614 for pctx in ctx.parents():
2641 for pctx in ctx.parents():
2615 pman = _manifestrevision(repo, pctx.manifestnode())
2642 pman = _manifestrevision(repo, pctx.manifestnode())
2616 textpairs.append((pman, mtext))
2643 textpairs.append((pman, mtext))
2617
2644
2618 # Load filelog revisions by iterating manifest delta.
2645 # Load filelog revisions by iterating manifest delta.
2619 man = ctx.manifest()
2646 man = ctx.manifest()
2620 pman = ctx.p1().manifest()
2647 pman = ctx.p1().manifest()
2621 for filename, change in pman.diff(man).items():
2648 for filename, change in pman.diff(man).items():
2622 fctx = repo.file(filename)
2649 fctx = repo.file(filename)
2623 f1 = fctx.revision(change[0][0] or -1)
2650 f1 = fctx.revision(change[0][0] or -1)
2624 f2 = fctx.revision(change[1][0] or -1)
2651 f2 = fctx.revision(change[1][0] or -1)
2625 textpairs.append((f1, f2))
2652 textpairs.append((f1, f2))
2626 else:
2653 else:
2627 dp = r.deltaparent(rev)
2654 dp = r.deltaparent(rev)
2628 textpairs.append((r.revision(dp), r.revision(rev)))
2655 textpairs.append((r.revision(dp), r.revision(rev)))
2629
2656
2630 def d():
2657 def d():
2631 for left, right in textpairs:
2658 for left, right in textpairs:
2632 # The date strings don't matter, so we pass empty strings.
2659 # The date strings don't matter, so we pass empty strings.
2633 headerlines, hunks = mdiff.unidiff(
2660 headerlines, hunks = mdiff.unidiff(
2634 left, b'', right, b'', b'left', b'right', binary=False
2661 left, b'', right, b'', b'left', b'right', binary=False
2635 )
2662 )
2636 # consume iterators in roughly the way patch.py does
2663 # consume iterators in roughly the way patch.py does
2637 b'\n'.join(headerlines)
2664 b'\n'.join(headerlines)
2638 b''.join(sum((list(hlines) for hrange, hlines in hunks), []))
2665 b''.join(sum((list(hlines) for hrange, hlines in hunks), []))
2639
2666
2640 timer, fm = gettimer(ui, opts)
2667 timer, fm = gettimer(ui, opts)
2641 timer(d)
2668 timer(d)
2642 fm.end()
2669 fm.end()
2643
2670
2644
2671
2645 @command(b'perf::diffwd|perfdiffwd', formatteropts)
2672 @command(b'perf::diffwd|perfdiffwd', formatteropts)
2646 def perfdiffwd(ui, repo, **opts):
2673 def perfdiffwd(ui, repo, **opts):
2647 """Profile diff of working directory changes"""
2674 """Profile diff of working directory changes"""
2648 opts = _byteskwargs(opts)
2675 opts = _byteskwargs(opts)
2649 timer, fm = gettimer(ui, opts)
2676 timer, fm = gettimer(ui, opts)
2650 options = {
2677 options = {
2651 'w': 'ignore_all_space',
2678 'w': 'ignore_all_space',
2652 'b': 'ignore_space_change',
2679 'b': 'ignore_space_change',
2653 'B': 'ignore_blank_lines',
2680 'B': 'ignore_blank_lines',
2654 }
2681 }
2655
2682
2656 for diffopt in ('', 'w', 'b', 'B', 'wB'):
2683 for diffopt in ('', 'w', 'b', 'B', 'wB'):
2657 opts = {options[c]: b'1' for c in diffopt}
2684 opts = {options[c]: b'1' for c in diffopt}
2658
2685
2659 def d():
2686 def d():
2660 ui.pushbuffer()
2687 ui.pushbuffer()
2661 commands.diff(ui, repo, **opts)
2688 commands.diff(ui, repo, **opts)
2662 ui.popbuffer()
2689 ui.popbuffer()
2663
2690
2664 diffopt = diffopt.encode('ascii')
2691 diffopt = diffopt.encode('ascii')
2665 title = b'diffopts: %s' % (diffopt and (b'-' + diffopt) or b'none')
2692 title = b'diffopts: %s' % (diffopt and (b'-' + diffopt) or b'none')
2666 timer(d, title=title)
2693 timer(d, title=title)
2667 fm.end()
2694 fm.end()
2668
2695
2669
2696
2670 @command(
2697 @command(
2671 b'perf::revlogindex|perfrevlogindex',
2698 b'perf::revlogindex|perfrevlogindex',
2672 revlogopts + formatteropts,
2699 revlogopts + formatteropts,
2673 b'-c|-m|FILE',
2700 b'-c|-m|FILE',
2674 )
2701 )
2675 def perfrevlogindex(ui, repo, file_=None, **opts):
2702 def perfrevlogindex(ui, repo, file_=None, **opts):
2676 """Benchmark operations against a revlog index.
2703 """Benchmark operations against a revlog index.
2677
2704
2678 This tests constructing a revlog instance, reading index data,
2705 This tests constructing a revlog instance, reading index data,
2679 parsing index data, and performing various operations related to
2706 parsing index data, and performing various operations related to
2680 index data.
2707 index data.
2681 """
2708 """
2682
2709
2683 opts = _byteskwargs(opts)
2710 opts = _byteskwargs(opts)
2684
2711
2685 rl = cmdutil.openrevlog(repo, b'perfrevlogindex', file_, opts)
2712 rl = cmdutil.openrevlog(repo, b'perfrevlogindex', file_, opts)
2686
2713
2687 opener = getattr(rl, 'opener') # trick linter
2714 opener = getattr(rl, 'opener') # trick linter
2688 # compat with hg <= 5.8
2715 # compat with hg <= 5.8
2689 radix = getattr(rl, 'radix', None)
2716 radix = getattr(rl, 'radix', None)
2690 indexfile = getattr(rl, '_indexfile', None)
2717 indexfile = getattr(rl, '_indexfile', None)
2691 if indexfile is None:
2718 if indexfile is None:
2692 # compatibility with <= hg-5.8
2719 # compatibility with <= hg-5.8
2693 indexfile = getattr(rl, 'indexfile')
2720 indexfile = getattr(rl, 'indexfile')
2694 data = opener.read(indexfile)
2721 data = opener.read(indexfile)
2695
2722
2696 header = struct.unpack(b'>I', data[0:4])[0]
2723 header = struct.unpack(b'>I', data[0:4])[0]
2697 version = header & 0xFFFF
2724 version = header & 0xFFFF
2698 if version == 1:
2725 if version == 1:
2699 inline = header & (1 << 16)
2726 inline = header & (1 << 16)
2700 else:
2727 else:
2701 raise error.Abort(b'unsupported revlog version: %d' % version)
2728 raise error.Abort(b'unsupported revlog version: %d' % version)
2702
2729
2703 parse_index_v1 = getattr(mercurial.revlog, 'parse_index_v1', None)
2730 parse_index_v1 = getattr(mercurial.revlog, 'parse_index_v1', None)
2704 if parse_index_v1 is None:
2731 if parse_index_v1 is None:
2705 parse_index_v1 = mercurial.revlog.revlogio().parseindex
2732 parse_index_v1 = mercurial.revlog.revlogio().parseindex
2706
2733
2707 rllen = len(rl)
2734 rllen = len(rl)
2708
2735
2709 node0 = rl.node(0)
2736 node0 = rl.node(0)
2710 node25 = rl.node(rllen // 4)
2737 node25 = rl.node(rllen // 4)
2711 node50 = rl.node(rllen // 2)
2738 node50 = rl.node(rllen // 2)
2712 node75 = rl.node(rllen // 4 * 3)
2739 node75 = rl.node(rllen // 4 * 3)
2713 node100 = rl.node(rllen - 1)
2740 node100 = rl.node(rllen - 1)
2714
2741
2715 allrevs = range(rllen)
2742 allrevs = range(rllen)
2716 allrevsrev = list(reversed(allrevs))
2743 allrevsrev = list(reversed(allrevs))
2717 allnodes = [rl.node(rev) for rev in range(rllen)]
2744 allnodes = [rl.node(rev) for rev in range(rllen)]
2718 allnodesrev = list(reversed(allnodes))
2745 allnodesrev = list(reversed(allnodes))
2719
2746
2720 def constructor():
2747 def constructor():
2721 if radix is not None:
2748 if radix is not None:
2722 revlog(opener, radix=radix)
2749 revlog(opener, radix=radix)
2723 else:
2750 else:
2724 # hg <= 5.8
2751 # hg <= 5.8
2725 revlog(opener, indexfile=indexfile)
2752 revlog(opener, indexfile=indexfile)
2726
2753
2727 def read():
2754 def read():
2728 with opener(indexfile) as fh:
2755 with opener(indexfile) as fh:
2729 fh.read()
2756 fh.read()
2730
2757
2731 def parseindex():
2758 def parseindex():
2732 parse_index_v1(data, inline)
2759 parse_index_v1(data, inline)
2733
2760
2734 def getentry(revornode):
2761 def getentry(revornode):
2735 index = parse_index_v1(data, inline)[0]
2762 index = parse_index_v1(data, inline)[0]
2736 index[revornode]
2763 index[revornode]
2737
2764
2738 def getentries(revs, count=1):
2765 def getentries(revs, count=1):
2739 index = parse_index_v1(data, inline)[0]
2766 index = parse_index_v1(data, inline)[0]
2740
2767
2741 for i in range(count):
2768 for i in range(count):
2742 for rev in revs:
2769 for rev in revs:
2743 index[rev]
2770 index[rev]
2744
2771
2745 def resolvenode(node):
2772 def resolvenode(node):
2746 index = parse_index_v1(data, inline)[0]
2773 index = parse_index_v1(data, inline)[0]
2747 rev = getattr(index, 'rev', None)
2774 rev = getattr(index, 'rev', None)
2748 if rev is None:
2775 if rev is None:
2749 nodemap = getattr(parse_index_v1(data, inline)[0], 'nodemap', None)
2776 nodemap = getattr(parse_index_v1(data, inline)[0], 'nodemap', None)
2750 # This only works for the C code.
2777 # This only works for the C code.
2751 if nodemap is None:
2778 if nodemap is None:
2752 return
2779 return
2753 rev = nodemap.__getitem__
2780 rev = nodemap.__getitem__
2754
2781
2755 try:
2782 try:
2756 rev(node)
2783 rev(node)
2757 except error.RevlogError:
2784 except error.RevlogError:
2758 pass
2785 pass
2759
2786
2760 def resolvenodes(nodes, count=1):
2787 def resolvenodes(nodes, count=1):
2761 index = parse_index_v1(data, inline)[0]
2788 index = parse_index_v1(data, inline)[0]
2762 rev = getattr(index, 'rev', None)
2789 rev = getattr(index, 'rev', None)
2763 if rev is None:
2790 if rev is None:
2764 nodemap = getattr(parse_index_v1(data, inline)[0], 'nodemap', None)
2791 nodemap = getattr(parse_index_v1(data, inline)[0], 'nodemap', None)
2765 # This only works for the C code.
2792 # This only works for the C code.
2766 if nodemap is None:
2793 if nodemap is None:
2767 return
2794 return
2768 rev = nodemap.__getitem__
2795 rev = nodemap.__getitem__
2769
2796
2770 for i in range(count):
2797 for i in range(count):
2771 for node in nodes:
2798 for node in nodes:
2772 try:
2799 try:
2773 rev(node)
2800 rev(node)
2774 except error.RevlogError:
2801 except error.RevlogError:
2775 pass
2802 pass
2776
2803
2777 benches = [
2804 benches = [
2778 (constructor, b'revlog constructor'),
2805 (constructor, b'revlog constructor'),
2779 (read, b'read'),
2806 (read, b'read'),
2780 (parseindex, b'create index object'),
2807 (parseindex, b'create index object'),
2781 (lambda: getentry(0), b'retrieve index entry for rev 0'),
2808 (lambda: getentry(0), b'retrieve index entry for rev 0'),
2782 (lambda: resolvenode(b'a' * 20), b'look up missing node'),
2809 (lambda: resolvenode(b'a' * 20), b'look up missing node'),
2783 (lambda: resolvenode(node0), b'look up node at rev 0'),
2810 (lambda: resolvenode(node0), b'look up node at rev 0'),
2784 (lambda: resolvenode(node25), b'look up node at 1/4 len'),
2811 (lambda: resolvenode(node25), b'look up node at 1/4 len'),
2785 (lambda: resolvenode(node50), b'look up node at 1/2 len'),
2812 (lambda: resolvenode(node50), b'look up node at 1/2 len'),
2786 (lambda: resolvenode(node75), b'look up node at 3/4 len'),
2813 (lambda: resolvenode(node75), b'look up node at 3/4 len'),
2787 (lambda: resolvenode(node100), b'look up node at tip'),
2814 (lambda: resolvenode(node100), b'look up node at tip'),
2788 # 2x variation is to measure caching impact.
2815 # 2x variation is to measure caching impact.
2789 (lambda: resolvenodes(allnodes), b'look up all nodes (forward)'),
2816 (lambda: resolvenodes(allnodes), b'look up all nodes (forward)'),
2790 (lambda: resolvenodes(allnodes, 2), b'look up all nodes 2x (forward)'),
2817 (lambda: resolvenodes(allnodes, 2), b'look up all nodes 2x (forward)'),
2791 (lambda: resolvenodes(allnodesrev), b'look up all nodes (reverse)'),
2818 (lambda: resolvenodes(allnodesrev), b'look up all nodes (reverse)'),
2792 (
2819 (
2793 lambda: resolvenodes(allnodesrev, 2),
2820 lambda: resolvenodes(allnodesrev, 2),
2794 b'look up all nodes 2x (reverse)',
2821 b'look up all nodes 2x (reverse)',
2795 ),
2822 ),
2796 (lambda: getentries(allrevs), b'retrieve all index entries (forward)'),
2823 (lambda: getentries(allrevs), b'retrieve all index entries (forward)'),
2797 (
2824 (
2798 lambda: getentries(allrevs, 2),
2825 lambda: getentries(allrevs, 2),
2799 b'retrieve all index entries 2x (forward)',
2826 b'retrieve all index entries 2x (forward)',
2800 ),
2827 ),
2801 (
2828 (
2802 lambda: getentries(allrevsrev),
2829 lambda: getentries(allrevsrev),
2803 b'retrieve all index entries (reverse)',
2830 b'retrieve all index entries (reverse)',
2804 ),
2831 ),
2805 (
2832 (
2806 lambda: getentries(allrevsrev, 2),
2833 lambda: getentries(allrevsrev, 2),
2807 b'retrieve all index entries 2x (reverse)',
2834 b'retrieve all index entries 2x (reverse)',
2808 ),
2835 ),
2809 ]
2836 ]
2810
2837
2811 for fn, title in benches:
2838 for fn, title in benches:
2812 timer, fm = gettimer(ui, opts)
2839 timer, fm = gettimer(ui, opts)
2813 timer(fn, title=title)
2840 timer(fn, title=title)
2814 fm.end()
2841 fm.end()
2815
2842
2816
2843
2817 @command(
2844 @command(
2818 b'perf::revlogrevisions|perfrevlogrevisions',
2845 b'perf::revlogrevisions|perfrevlogrevisions',
2819 revlogopts
2846 revlogopts
2820 + formatteropts
2847 + formatteropts
2821 + [
2848 + [
2822 (b'd', b'dist', 100, b'distance between the revisions'),
2849 (b'd', b'dist', 100, b'distance between the revisions'),
2823 (b's', b'startrev', 0, b'revision to start reading at'),
2850 (b's', b'startrev', 0, b'revision to start reading at'),
2824 (b'', b'reverse', False, b'read in reverse'),
2851 (b'', b'reverse', False, b'read in reverse'),
2825 ],
2852 ],
2826 b'-c|-m|FILE',
2853 b'-c|-m|FILE',
2827 )
2854 )
2828 def perfrevlogrevisions(
2855 def perfrevlogrevisions(
2829 ui, repo, file_=None, startrev=0, reverse=False, **opts
2856 ui, repo, file_=None, startrev=0, reverse=False, **opts
2830 ):
2857 ):
2831 """Benchmark reading a series of revisions from a revlog.
2858 """Benchmark reading a series of revisions from a revlog.
2832
2859
2833 By default, we read every ``-d/--dist`` revision from 0 to tip of
2860 By default, we read every ``-d/--dist`` revision from 0 to tip of
2834 the specified revlog.
2861 the specified revlog.
2835
2862
2836 The start revision can be defined via ``-s/--startrev``.
2863 The start revision can be defined via ``-s/--startrev``.
2837 """
2864 """
2838 opts = _byteskwargs(opts)
2865 opts = _byteskwargs(opts)
2839
2866
2840 rl = cmdutil.openrevlog(repo, b'perfrevlogrevisions', file_, opts)
2867 rl = cmdutil.openrevlog(repo, b'perfrevlogrevisions', file_, opts)
2841 rllen = getlen(ui)(rl)
2868 rllen = getlen(ui)(rl)
2842
2869
2843 if startrev < 0:
2870 if startrev < 0:
2844 startrev = rllen + startrev
2871 startrev = rllen + startrev
2845
2872
2846 def d():
2873 def d():
2847 rl.clearcaches()
2874 rl.clearcaches()
2848
2875
2849 beginrev = startrev
2876 beginrev = startrev
2850 endrev = rllen
2877 endrev = rllen
2851 dist = opts[b'dist']
2878 dist = opts[b'dist']
2852
2879
2853 if reverse:
2880 if reverse:
2854 beginrev, endrev = endrev - 1, beginrev - 1
2881 beginrev, endrev = endrev - 1, beginrev - 1
2855 dist = -1 * dist
2882 dist = -1 * dist
2856
2883
2857 for x in _xrange(beginrev, endrev, dist):
2884 for x in _xrange(beginrev, endrev, dist):
2858 # Old revisions don't support passing int.
2885 # Old revisions don't support passing int.
2859 n = rl.node(x)
2886 n = rl.node(x)
2860 rl.revision(n)
2887 rl.revision(n)
2861
2888
2862 timer, fm = gettimer(ui, opts)
2889 timer, fm = gettimer(ui, opts)
2863 timer(d)
2890 timer(d)
2864 fm.end()
2891 fm.end()
2865
2892
2866
2893
2867 @command(
2894 @command(
2868 b'perf::revlogwrite|perfrevlogwrite',
2895 b'perf::revlogwrite|perfrevlogwrite',
2869 revlogopts
2896 revlogopts
2870 + formatteropts
2897 + formatteropts
2871 + [
2898 + [
2872 (b's', b'startrev', 1000, b'revision to start writing at'),
2899 (b's', b'startrev', 1000, b'revision to start writing at'),
2873 (b'', b'stoprev', -1, b'last revision to write'),
2900 (b'', b'stoprev', -1, b'last revision to write'),
2874 (b'', b'count', 3, b'number of passes to perform'),
2901 (b'', b'count', 3, b'number of passes to perform'),
2875 (b'', b'details', False, b'print timing for every revisions tested'),
2902 (b'', b'details', False, b'print timing for every revisions tested'),
2876 (b'', b'source', b'full', b'the kind of data feed in the revlog'),
2903 (b'', b'source', b'full', b'the kind of data feed in the revlog'),
2877 (b'', b'lazydeltabase', True, b'try the provided delta first'),
2904 (b'', b'lazydeltabase', True, b'try the provided delta first'),
2878 (b'', b'clear-caches', True, b'clear revlog cache between calls'),
2905 (b'', b'clear-caches', True, b'clear revlog cache between calls'),
2879 ],
2906 ],
2880 b'-c|-m|FILE',
2907 b'-c|-m|FILE',
2881 )
2908 )
2882 def perfrevlogwrite(ui, repo, file_=None, startrev=1000, stoprev=-1, **opts):
2909 def perfrevlogwrite(ui, repo, file_=None, startrev=1000, stoprev=-1, **opts):
2883 """Benchmark writing a series of revisions to a revlog.
2910 """Benchmark writing a series of revisions to a revlog.
2884
2911
2885 Possible source values are:
2912 Possible source values are:
2886 * `full`: add from a full text (default).
2913 * `full`: add from a full text (default).
2887 * `parent-1`: add from a delta to the first parent
2914 * `parent-1`: add from a delta to the first parent
2888 * `parent-2`: add from a delta to the second parent if it exists
2915 * `parent-2`: add from a delta to the second parent if it exists
2889 (use a delta from the first parent otherwise)
2916 (use a delta from the first parent otherwise)
2890 * `parent-smallest`: add from the smallest delta (either p1 or p2)
2917 * `parent-smallest`: add from the smallest delta (either p1 or p2)
2891 * `storage`: add from the existing precomputed deltas
2918 * `storage`: add from the existing precomputed deltas
2892
2919
2893 Note: This performance command measures performance in a custom way. As a
2920 Note: This performance command measures performance in a custom way. As a
2894 result some of the global configuration of the 'perf' command does not
2921 result some of the global configuration of the 'perf' command does not
2895 apply to it:
2922 apply to it:
2896
2923
2897 * ``pre-run``: disabled
2924 * ``pre-run``: disabled
2898
2925
2899 * ``profile-benchmark``: disabled
2926 * ``profile-benchmark``: disabled
2900
2927
2901 * ``run-limits``: disabled use --count instead
2928 * ``run-limits``: disabled use --count instead
2902 """
2929 """
2903 opts = _byteskwargs(opts)
2930 opts = _byteskwargs(opts)
2904
2931
2905 rl = cmdutil.openrevlog(repo, b'perfrevlogwrite', file_, opts)
2932 rl = cmdutil.openrevlog(repo, b'perfrevlogwrite', file_, opts)
2906 rllen = getlen(ui)(rl)
2933 rllen = getlen(ui)(rl)
2907 if startrev < 0:
2934 if startrev < 0:
2908 startrev = rllen + startrev
2935 startrev = rllen + startrev
2909 if stoprev < 0:
2936 if stoprev < 0:
2910 stoprev = rllen + stoprev
2937 stoprev = rllen + stoprev
2911
2938
2912 lazydeltabase = opts['lazydeltabase']
2939 lazydeltabase = opts['lazydeltabase']
2913 source = opts['source']
2940 source = opts['source']
2914 clearcaches = opts['clear_caches']
2941 clearcaches = opts['clear_caches']
2915 validsource = (
2942 validsource = (
2916 b'full',
2943 b'full',
2917 b'parent-1',
2944 b'parent-1',
2918 b'parent-2',
2945 b'parent-2',
2919 b'parent-smallest',
2946 b'parent-smallest',
2920 b'storage',
2947 b'storage',
2921 )
2948 )
2922 if source not in validsource:
2949 if source not in validsource:
2923 raise error.Abort('invalid source type: %s' % source)
2950 raise error.Abort('invalid source type: %s' % source)
2924
2951
2925 ### actually gather results
2952 ### actually gather results
2926 count = opts['count']
2953 count = opts['count']
2927 if count <= 0:
2954 if count <= 0:
2928 raise error.Abort('invalide run count: %d' % count)
2955 raise error.Abort('invalide run count: %d' % count)
2929 allresults = []
2956 allresults = []
2930 for c in range(count):
2957 for c in range(count):
2931 timing = _timeonewrite(
2958 timing = _timeonewrite(
2932 ui,
2959 ui,
2933 rl,
2960 rl,
2934 source,
2961 source,
2935 startrev,
2962 startrev,
2936 stoprev,
2963 stoprev,
2937 c + 1,
2964 c + 1,
2938 lazydeltabase=lazydeltabase,
2965 lazydeltabase=lazydeltabase,
2939 clearcaches=clearcaches,
2966 clearcaches=clearcaches,
2940 )
2967 )
2941 allresults.append(timing)
2968 allresults.append(timing)
2942
2969
2943 ### consolidate the results in a single list
2970 ### consolidate the results in a single list
2944 results = []
2971 results = []
2945 for idx, (rev, t) in enumerate(allresults[0]):
2972 for idx, (rev, t) in enumerate(allresults[0]):
2946 ts = [t]
2973 ts = [t]
2947 for other in allresults[1:]:
2974 for other in allresults[1:]:
2948 orev, ot = other[idx]
2975 orev, ot = other[idx]
2949 assert orev == rev
2976 assert orev == rev
2950 ts.append(ot)
2977 ts.append(ot)
2951 results.append((rev, ts))
2978 results.append((rev, ts))
2952 resultcount = len(results)
2979 resultcount = len(results)
2953
2980
2954 ### Compute and display relevant statistics
2981 ### Compute and display relevant statistics
2955
2982
2956 # get a formatter
2983 # get a formatter
2957 fm = ui.formatter(b'perf', opts)
2984 fm = ui.formatter(b'perf', opts)
2958 displayall = ui.configbool(b"perf", b"all-timing", False)
2985 displayall = ui.configbool(b"perf", b"all-timing", False)
2959
2986
2960 # print individual details if requested
2987 # print individual details if requested
2961 if opts['details']:
2988 if opts['details']:
2962 for idx, item in enumerate(results, 1):
2989 for idx, item in enumerate(results, 1):
2963 rev, data = item
2990 rev, data = item
2964 title = 'revisions #%d of %d, rev %d' % (idx, resultcount, rev)
2991 title = 'revisions #%d of %d, rev %d' % (idx, resultcount, rev)
2965 formatone(fm, data, title=title, displayall=displayall)
2992 formatone(fm, data, title=title, displayall=displayall)
2966
2993
2967 # sorts results by median time
2994 # sorts results by median time
2968 results.sort(key=lambda x: sorted(x[1])[len(x[1]) // 2])
2995 results.sort(key=lambda x: sorted(x[1])[len(x[1]) // 2])
2969 # list of (name, index) to display)
2996 # list of (name, index) to display)
2970 relevants = [
2997 relevants = [
2971 ("min", 0),
2998 ("min", 0),
2972 ("10%", resultcount * 10 // 100),
2999 ("10%", resultcount * 10 // 100),
2973 ("25%", resultcount * 25 // 100),
3000 ("25%", resultcount * 25 // 100),
2974 ("50%", resultcount * 70 // 100),
3001 ("50%", resultcount * 70 // 100),
2975 ("75%", resultcount * 75 // 100),
3002 ("75%", resultcount * 75 // 100),
2976 ("90%", resultcount * 90 // 100),
3003 ("90%", resultcount * 90 // 100),
2977 ("95%", resultcount * 95 // 100),
3004 ("95%", resultcount * 95 // 100),
2978 ("99%", resultcount * 99 // 100),
3005 ("99%", resultcount * 99 // 100),
2979 ("99.9%", resultcount * 999 // 1000),
3006 ("99.9%", resultcount * 999 // 1000),
2980 ("99.99%", resultcount * 9999 // 10000),
3007 ("99.99%", resultcount * 9999 // 10000),
2981 ("99.999%", resultcount * 99999 // 100000),
3008 ("99.999%", resultcount * 99999 // 100000),
2982 ("max", -1),
3009 ("max", -1),
2983 ]
3010 ]
2984 if not ui.quiet:
3011 if not ui.quiet:
2985 for name, idx in relevants:
3012 for name, idx in relevants:
2986 data = results[idx]
3013 data = results[idx]
2987 title = '%s of %d, rev %d' % (name, resultcount, data[0])
3014 title = '%s of %d, rev %d' % (name, resultcount, data[0])
2988 formatone(fm, data[1], title=title, displayall=displayall)
3015 formatone(fm, data[1], title=title, displayall=displayall)
2989
3016
2990 # XXX summing that many float will not be very precise, we ignore this fact
3017 # XXX summing that many float will not be very precise, we ignore this fact
2991 # for now
3018 # for now
2992 totaltime = []
3019 totaltime = []
2993 for item in allresults:
3020 for item in allresults:
2994 totaltime.append(
3021 totaltime.append(
2995 (
3022 (
2996 sum(x[1][0] for x in item),
3023 sum(x[1][0] for x in item),
2997 sum(x[1][1] for x in item),
3024 sum(x[1][1] for x in item),
2998 sum(x[1][2] for x in item),
3025 sum(x[1][2] for x in item),
2999 )
3026 )
3000 )
3027 )
3001 formatone(
3028 formatone(
3002 fm,
3029 fm,
3003 totaltime,
3030 totaltime,
3004 title="total time (%d revs)" % resultcount,
3031 title="total time (%d revs)" % resultcount,
3005 displayall=displayall,
3032 displayall=displayall,
3006 )
3033 )
3007 fm.end()
3034 fm.end()
3008
3035
3009
3036
3010 class _faketr:
3037 class _faketr:
3011 def add(s, x, y, z=None):
3038 def add(s, x, y, z=None):
3012 return None
3039 return None
3013
3040
3014
3041
3015 def _timeonewrite(
3042 def _timeonewrite(
3016 ui,
3043 ui,
3017 orig,
3044 orig,
3018 source,
3045 source,
3019 startrev,
3046 startrev,
3020 stoprev,
3047 stoprev,
3021 runidx=None,
3048 runidx=None,
3022 lazydeltabase=True,
3049 lazydeltabase=True,
3023 clearcaches=True,
3050 clearcaches=True,
3024 ):
3051 ):
3025 timings = []
3052 timings = []
3026 tr = _faketr()
3053 tr = _faketr()
3027 with _temprevlog(ui, orig, startrev) as dest:
3054 with _temprevlog(ui, orig, startrev) as dest:
3028 dest._lazydeltabase = lazydeltabase
3055 dest._lazydeltabase = lazydeltabase
3029 revs = list(orig.revs(startrev, stoprev))
3056 revs = list(orig.revs(startrev, stoprev))
3030 total = len(revs)
3057 total = len(revs)
3031 topic = 'adding'
3058 topic = 'adding'
3032 if runidx is not None:
3059 if runidx is not None:
3033 topic += ' (run #%d)' % runidx
3060 topic += ' (run #%d)' % runidx
3034 # Support both old and new progress API
3061 # Support both old and new progress API
3035 if util.safehasattr(ui, 'makeprogress'):
3062 if util.safehasattr(ui, 'makeprogress'):
3036 progress = ui.makeprogress(topic, unit='revs', total=total)
3063 progress = ui.makeprogress(topic, unit='revs', total=total)
3037
3064
3038 def updateprogress(pos):
3065 def updateprogress(pos):
3039 progress.update(pos)
3066 progress.update(pos)
3040
3067
3041 def completeprogress():
3068 def completeprogress():
3042 progress.complete()
3069 progress.complete()
3043
3070
3044 else:
3071 else:
3045
3072
3046 def updateprogress(pos):
3073 def updateprogress(pos):
3047 ui.progress(topic, pos, unit='revs', total=total)
3074 ui.progress(topic, pos, unit='revs', total=total)
3048
3075
3049 def completeprogress():
3076 def completeprogress():
3050 ui.progress(topic, None, unit='revs', total=total)
3077 ui.progress(topic, None, unit='revs', total=total)
3051
3078
3052 for idx, rev in enumerate(revs):
3079 for idx, rev in enumerate(revs):
3053 updateprogress(idx)
3080 updateprogress(idx)
3054 addargs, addkwargs = _getrevisionseed(orig, rev, tr, source)
3081 addargs, addkwargs = _getrevisionseed(orig, rev, tr, source)
3055 if clearcaches:
3082 if clearcaches:
3056 dest.index.clearcaches()
3083 dest.index.clearcaches()
3057 dest.clearcaches()
3084 dest.clearcaches()
3058 with timeone() as r:
3085 with timeone() as r:
3059 dest.addrawrevision(*addargs, **addkwargs)
3086 dest.addrawrevision(*addargs, **addkwargs)
3060 timings.append((rev, r[0]))
3087 timings.append((rev, r[0]))
3061 updateprogress(total)
3088 updateprogress(total)
3062 completeprogress()
3089 completeprogress()
3063 return timings
3090 return timings
3064
3091
3065
3092
3066 def _getrevisionseed(orig, rev, tr, source):
3093 def _getrevisionseed(orig, rev, tr, source):
3067 from mercurial.node import nullid
3094 from mercurial.node import nullid
3068
3095
3069 linkrev = orig.linkrev(rev)
3096 linkrev = orig.linkrev(rev)
3070 node = orig.node(rev)
3097 node = orig.node(rev)
3071 p1, p2 = orig.parents(node)
3098 p1, p2 = orig.parents(node)
3072 flags = orig.flags(rev)
3099 flags = orig.flags(rev)
3073 cachedelta = None
3100 cachedelta = None
3074 text = None
3101 text = None
3075
3102
3076 if source == b'full':
3103 if source == b'full':
3077 text = orig.revision(rev)
3104 text = orig.revision(rev)
3078 elif source == b'parent-1':
3105 elif source == b'parent-1':
3079 baserev = orig.rev(p1)
3106 baserev = orig.rev(p1)
3080 cachedelta = (baserev, orig.revdiff(p1, rev))
3107 cachedelta = (baserev, orig.revdiff(p1, rev))
3081 elif source == b'parent-2':
3108 elif source == b'parent-2':
3082 parent = p2
3109 parent = p2
3083 if p2 == nullid:
3110 if p2 == nullid:
3084 parent = p1
3111 parent = p1
3085 baserev = orig.rev(parent)
3112 baserev = orig.rev(parent)
3086 cachedelta = (baserev, orig.revdiff(parent, rev))
3113 cachedelta = (baserev, orig.revdiff(parent, rev))
3087 elif source == b'parent-smallest':
3114 elif source == b'parent-smallest':
3088 p1diff = orig.revdiff(p1, rev)
3115 p1diff = orig.revdiff(p1, rev)
3089 parent = p1
3116 parent = p1
3090 diff = p1diff
3117 diff = p1diff
3091 if p2 != nullid:
3118 if p2 != nullid:
3092 p2diff = orig.revdiff(p2, rev)
3119 p2diff = orig.revdiff(p2, rev)
3093 if len(p1diff) > len(p2diff):
3120 if len(p1diff) > len(p2diff):
3094 parent = p2
3121 parent = p2
3095 diff = p2diff
3122 diff = p2diff
3096 baserev = orig.rev(parent)
3123 baserev = orig.rev(parent)
3097 cachedelta = (baserev, diff)
3124 cachedelta = (baserev, diff)
3098 elif source == b'storage':
3125 elif source == b'storage':
3099 baserev = orig.deltaparent(rev)
3126 baserev = orig.deltaparent(rev)
3100 cachedelta = (baserev, orig.revdiff(orig.node(baserev), rev))
3127 cachedelta = (baserev, orig.revdiff(orig.node(baserev), rev))
3101
3128
3102 return (
3129 return (
3103 (text, tr, linkrev, p1, p2),
3130 (text, tr, linkrev, p1, p2),
3104 {'node': node, 'flags': flags, 'cachedelta': cachedelta},
3131 {'node': node, 'flags': flags, 'cachedelta': cachedelta},
3105 )
3132 )
3106
3133
3107
3134
3108 @contextlib.contextmanager
3135 @contextlib.contextmanager
3109 def _temprevlog(ui, orig, truncaterev):
3136 def _temprevlog(ui, orig, truncaterev):
3110 from mercurial import vfs as vfsmod
3137 from mercurial import vfs as vfsmod
3111
3138
3112 if orig._inline:
3139 if orig._inline:
3113 raise error.Abort('not supporting inline revlog (yet)')
3140 raise error.Abort('not supporting inline revlog (yet)')
3114 revlogkwargs = {}
3141 revlogkwargs = {}
3115 k = 'upperboundcomp'
3142 k = 'upperboundcomp'
3116 if util.safehasattr(orig, k):
3143 if util.safehasattr(orig, k):
3117 revlogkwargs[k] = getattr(orig, k)
3144 revlogkwargs[k] = getattr(orig, k)
3118
3145
3119 indexfile = getattr(orig, '_indexfile', None)
3146 indexfile = getattr(orig, '_indexfile', None)
3120 if indexfile is None:
3147 if indexfile is None:
3121 # compatibility with <= hg-5.8
3148 # compatibility with <= hg-5.8
3122 indexfile = getattr(orig, 'indexfile')
3149 indexfile = getattr(orig, 'indexfile')
3123 origindexpath = orig.opener.join(indexfile)
3150 origindexpath = orig.opener.join(indexfile)
3124
3151
3125 datafile = getattr(orig, '_datafile', getattr(orig, 'datafile'))
3152 datafile = getattr(orig, '_datafile', getattr(orig, 'datafile'))
3126 origdatapath = orig.opener.join(datafile)
3153 origdatapath = orig.opener.join(datafile)
3127 radix = b'revlog'
3154 radix = b'revlog'
3128 indexname = b'revlog.i'
3155 indexname = b'revlog.i'
3129 dataname = b'revlog.d'
3156 dataname = b'revlog.d'
3130
3157
3131 tmpdir = tempfile.mkdtemp(prefix='tmp-hgperf-')
3158 tmpdir = tempfile.mkdtemp(prefix='tmp-hgperf-')
3132 try:
3159 try:
3133 # copy the data file in a temporary directory
3160 # copy the data file in a temporary directory
3134 ui.debug('copying data in %s\n' % tmpdir)
3161 ui.debug('copying data in %s\n' % tmpdir)
3135 destindexpath = os.path.join(tmpdir, 'revlog.i')
3162 destindexpath = os.path.join(tmpdir, 'revlog.i')
3136 destdatapath = os.path.join(tmpdir, 'revlog.d')
3163 destdatapath = os.path.join(tmpdir, 'revlog.d')
3137 shutil.copyfile(origindexpath, destindexpath)
3164 shutil.copyfile(origindexpath, destindexpath)
3138 shutil.copyfile(origdatapath, destdatapath)
3165 shutil.copyfile(origdatapath, destdatapath)
3139
3166
3140 # remove the data we want to add again
3167 # remove the data we want to add again
3141 ui.debug('truncating data to be rewritten\n')
3168 ui.debug('truncating data to be rewritten\n')
3142 with open(destindexpath, 'ab') as index:
3169 with open(destindexpath, 'ab') as index:
3143 index.seek(0)
3170 index.seek(0)
3144 index.truncate(truncaterev * orig._io.size)
3171 index.truncate(truncaterev * orig._io.size)
3145 with open(destdatapath, 'ab') as data:
3172 with open(destdatapath, 'ab') as data:
3146 data.seek(0)
3173 data.seek(0)
3147 data.truncate(orig.start(truncaterev))
3174 data.truncate(orig.start(truncaterev))
3148
3175
3149 # instantiate a new revlog from the temporary copy
3176 # instantiate a new revlog from the temporary copy
3150 ui.debug('truncating adding to be rewritten\n')
3177 ui.debug('truncating adding to be rewritten\n')
3151 vfs = vfsmod.vfs(tmpdir)
3178 vfs = vfsmod.vfs(tmpdir)
3152 vfs.options = getattr(orig.opener, 'options', None)
3179 vfs.options = getattr(orig.opener, 'options', None)
3153
3180
3154 try:
3181 try:
3155 dest = revlog(vfs, radix=radix, **revlogkwargs)
3182 dest = revlog(vfs, radix=radix, **revlogkwargs)
3156 except TypeError:
3183 except TypeError:
3157 dest = revlog(
3184 dest = revlog(
3158 vfs, indexfile=indexname, datafile=dataname, **revlogkwargs
3185 vfs, indexfile=indexname, datafile=dataname, **revlogkwargs
3159 )
3186 )
3160 if dest._inline:
3187 if dest._inline:
3161 raise error.Abort('not supporting inline revlog (yet)')
3188 raise error.Abort('not supporting inline revlog (yet)')
3162 # make sure internals are initialized
3189 # make sure internals are initialized
3163 dest.revision(len(dest) - 1)
3190 dest.revision(len(dest) - 1)
3164 yield dest
3191 yield dest
3165 del dest, vfs
3192 del dest, vfs
3166 finally:
3193 finally:
3167 shutil.rmtree(tmpdir, True)
3194 shutil.rmtree(tmpdir, True)
3168
3195
3169
3196
3170 @command(
3197 @command(
3171 b'perf::revlogchunks|perfrevlogchunks',
3198 b'perf::revlogchunks|perfrevlogchunks',
3172 revlogopts
3199 revlogopts
3173 + formatteropts
3200 + formatteropts
3174 + [
3201 + [
3175 (b'e', b'engines', b'', b'compression engines to use'),
3202 (b'e', b'engines', b'', b'compression engines to use'),
3176 (b's', b'startrev', 0, b'revision to start at'),
3203 (b's', b'startrev', 0, b'revision to start at'),
3177 ],
3204 ],
3178 b'-c|-m|FILE',
3205 b'-c|-m|FILE',
3179 )
3206 )
3180 def perfrevlogchunks(ui, repo, file_=None, engines=None, startrev=0, **opts):
3207 def perfrevlogchunks(ui, repo, file_=None, engines=None, startrev=0, **opts):
3181 """Benchmark operations on revlog chunks.
3208 """Benchmark operations on revlog chunks.
3182
3209
3183 Logically, each revlog is a collection of fulltext revisions. However,
3210 Logically, each revlog is a collection of fulltext revisions. However,
3184 stored within each revlog are "chunks" of possibly compressed data. This
3211 stored within each revlog are "chunks" of possibly compressed data. This
3185 data needs to be read and decompressed or compressed and written.
3212 data needs to be read and decompressed or compressed and written.
3186
3213
3187 This command measures the time it takes to read+decompress and recompress
3214 This command measures the time it takes to read+decompress and recompress
3188 chunks in a revlog. It effectively isolates I/O and compression performance.
3215 chunks in a revlog. It effectively isolates I/O and compression performance.
3189 For measurements of higher-level operations like resolving revisions,
3216 For measurements of higher-level operations like resolving revisions,
3190 see ``perfrevlogrevisions`` and ``perfrevlogrevision``.
3217 see ``perfrevlogrevisions`` and ``perfrevlogrevision``.
3191 """
3218 """
3192 opts = _byteskwargs(opts)
3219 opts = _byteskwargs(opts)
3193
3220
3194 rl = cmdutil.openrevlog(repo, b'perfrevlogchunks', file_, opts)
3221 rl = cmdutil.openrevlog(repo, b'perfrevlogchunks', file_, opts)
3195
3222
3196 # _chunkraw was renamed to _getsegmentforrevs.
3223 # _chunkraw was renamed to _getsegmentforrevs.
3197 try:
3224 try:
3198 segmentforrevs = rl._getsegmentforrevs
3225 segmentforrevs = rl._getsegmentforrevs
3199 except AttributeError:
3226 except AttributeError:
3200 segmentforrevs = rl._chunkraw
3227 segmentforrevs = rl._chunkraw
3201
3228
3202 # Verify engines argument.
3229 # Verify engines argument.
3203 if engines:
3230 if engines:
3204 engines = {e.strip() for e in engines.split(b',')}
3231 engines = {e.strip() for e in engines.split(b',')}
3205 for engine in engines:
3232 for engine in engines:
3206 try:
3233 try:
3207 util.compressionengines[engine]
3234 util.compressionengines[engine]
3208 except KeyError:
3235 except KeyError:
3209 raise error.Abort(b'unknown compression engine: %s' % engine)
3236 raise error.Abort(b'unknown compression engine: %s' % engine)
3210 else:
3237 else:
3211 engines = []
3238 engines = []
3212 for e in util.compengines:
3239 for e in util.compengines:
3213 engine = util.compengines[e]
3240 engine = util.compengines[e]
3214 try:
3241 try:
3215 if engine.available():
3242 if engine.available():
3216 engine.revlogcompressor().compress(b'dummy')
3243 engine.revlogcompressor().compress(b'dummy')
3217 engines.append(e)
3244 engines.append(e)
3218 except NotImplementedError:
3245 except NotImplementedError:
3219 pass
3246 pass
3220
3247
3221 revs = list(rl.revs(startrev, len(rl) - 1))
3248 revs = list(rl.revs(startrev, len(rl) - 1))
3222
3249
3223 def rlfh(rl):
3250 def rlfh(rl):
3224 if rl._inline:
3251 if rl._inline:
3225 indexfile = getattr(rl, '_indexfile', None)
3252 indexfile = getattr(rl, '_indexfile', None)
3226 if indexfile is None:
3253 if indexfile is None:
3227 # compatibility with <= hg-5.8
3254 # compatibility with <= hg-5.8
3228 indexfile = getattr(rl, 'indexfile')
3255 indexfile = getattr(rl, 'indexfile')
3229 return getsvfs(repo)(indexfile)
3256 return getsvfs(repo)(indexfile)
3230 else:
3257 else:
3231 datafile = getattr(rl, 'datafile', getattr(rl, 'datafile'))
3258 datafile = getattr(rl, 'datafile', getattr(rl, 'datafile'))
3232 return getsvfs(repo)(datafile)
3259 return getsvfs(repo)(datafile)
3233
3260
3234 def doread():
3261 def doread():
3235 rl.clearcaches()
3262 rl.clearcaches()
3236 for rev in revs:
3263 for rev in revs:
3237 segmentforrevs(rev, rev)
3264 segmentforrevs(rev, rev)
3238
3265
3239 def doreadcachedfh():
3266 def doreadcachedfh():
3240 rl.clearcaches()
3267 rl.clearcaches()
3241 fh = rlfh(rl)
3268 fh = rlfh(rl)
3242 for rev in revs:
3269 for rev in revs:
3243 segmentforrevs(rev, rev, df=fh)
3270 segmentforrevs(rev, rev, df=fh)
3244
3271
3245 def doreadbatch():
3272 def doreadbatch():
3246 rl.clearcaches()
3273 rl.clearcaches()
3247 segmentforrevs(revs[0], revs[-1])
3274 segmentforrevs(revs[0], revs[-1])
3248
3275
3249 def doreadbatchcachedfh():
3276 def doreadbatchcachedfh():
3250 rl.clearcaches()
3277 rl.clearcaches()
3251 fh = rlfh(rl)
3278 fh = rlfh(rl)
3252 segmentforrevs(revs[0], revs[-1], df=fh)
3279 segmentforrevs(revs[0], revs[-1], df=fh)
3253
3280
3254 def dochunk():
3281 def dochunk():
3255 rl.clearcaches()
3282 rl.clearcaches()
3256 fh = rlfh(rl)
3283 fh = rlfh(rl)
3257 for rev in revs:
3284 for rev in revs:
3258 rl._chunk(rev, df=fh)
3285 rl._chunk(rev, df=fh)
3259
3286
3260 chunks = [None]
3287 chunks = [None]
3261
3288
3262 def dochunkbatch():
3289 def dochunkbatch():
3263 rl.clearcaches()
3290 rl.clearcaches()
3264 fh = rlfh(rl)
3291 fh = rlfh(rl)
3265 # Save chunks as a side-effect.
3292 # Save chunks as a side-effect.
3266 chunks[0] = rl._chunks(revs, df=fh)
3293 chunks[0] = rl._chunks(revs, df=fh)
3267
3294
3268 def docompress(compressor):
3295 def docompress(compressor):
3269 rl.clearcaches()
3296 rl.clearcaches()
3270
3297
3271 try:
3298 try:
3272 # Swap in the requested compression engine.
3299 # Swap in the requested compression engine.
3273 oldcompressor = rl._compressor
3300 oldcompressor = rl._compressor
3274 rl._compressor = compressor
3301 rl._compressor = compressor
3275 for chunk in chunks[0]:
3302 for chunk in chunks[0]:
3276 rl.compress(chunk)
3303 rl.compress(chunk)
3277 finally:
3304 finally:
3278 rl._compressor = oldcompressor
3305 rl._compressor = oldcompressor
3279
3306
3280 benches = [
3307 benches = [
3281 (lambda: doread(), b'read'),
3308 (lambda: doread(), b'read'),
3282 (lambda: doreadcachedfh(), b'read w/ reused fd'),
3309 (lambda: doreadcachedfh(), b'read w/ reused fd'),
3283 (lambda: doreadbatch(), b'read batch'),
3310 (lambda: doreadbatch(), b'read batch'),
3284 (lambda: doreadbatchcachedfh(), b'read batch w/ reused fd'),
3311 (lambda: doreadbatchcachedfh(), b'read batch w/ reused fd'),
3285 (lambda: dochunk(), b'chunk'),
3312 (lambda: dochunk(), b'chunk'),
3286 (lambda: dochunkbatch(), b'chunk batch'),
3313 (lambda: dochunkbatch(), b'chunk batch'),
3287 ]
3314 ]
3288
3315
3289 for engine in sorted(engines):
3316 for engine in sorted(engines):
3290 compressor = util.compengines[engine].revlogcompressor()
3317 compressor = util.compengines[engine].revlogcompressor()
3291 benches.append(
3318 benches.append(
3292 (
3319 (
3293 functools.partial(docompress, compressor),
3320 functools.partial(docompress, compressor),
3294 b'compress w/ %s' % engine,
3321 b'compress w/ %s' % engine,
3295 )
3322 )
3296 )
3323 )
3297
3324
3298 for fn, title in benches:
3325 for fn, title in benches:
3299 timer, fm = gettimer(ui, opts)
3326 timer, fm = gettimer(ui, opts)
3300 timer(fn, title=title)
3327 timer(fn, title=title)
3301 fm.end()
3328 fm.end()
3302
3329
3303
3330
3304 @command(
3331 @command(
3305 b'perf::revlogrevision|perfrevlogrevision',
3332 b'perf::revlogrevision|perfrevlogrevision',
3306 revlogopts
3333 revlogopts
3307 + formatteropts
3334 + formatteropts
3308 + [(b'', b'cache', False, b'use caches instead of clearing')],
3335 + [(b'', b'cache', False, b'use caches instead of clearing')],
3309 b'-c|-m|FILE REV',
3336 b'-c|-m|FILE REV',
3310 )
3337 )
3311 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
3338 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
3312 """Benchmark obtaining a revlog revision.
3339 """Benchmark obtaining a revlog revision.
3313
3340
3314 Obtaining a revlog revision consists of roughly the following steps:
3341 Obtaining a revlog revision consists of roughly the following steps:
3315
3342
3316 1. Compute the delta chain
3343 1. Compute the delta chain
3317 2. Slice the delta chain if applicable
3344 2. Slice the delta chain if applicable
3318 3. Obtain the raw chunks for that delta chain
3345 3. Obtain the raw chunks for that delta chain
3319 4. Decompress each raw chunk
3346 4. Decompress each raw chunk
3320 5. Apply binary patches to obtain fulltext
3347 5. Apply binary patches to obtain fulltext
3321 6. Verify hash of fulltext
3348 6. Verify hash of fulltext
3322
3349
3323 This command measures the time spent in each of these phases.
3350 This command measures the time spent in each of these phases.
3324 """
3351 """
3325 opts = _byteskwargs(opts)
3352 opts = _byteskwargs(opts)
3326
3353
3327 if opts.get(b'changelog') or opts.get(b'manifest'):
3354 if opts.get(b'changelog') or opts.get(b'manifest'):
3328 file_, rev = None, file_
3355 file_, rev = None, file_
3329 elif rev is None:
3356 elif rev is None:
3330 raise error.CommandError(b'perfrevlogrevision', b'invalid arguments')
3357 raise error.CommandError(b'perfrevlogrevision', b'invalid arguments')
3331
3358
3332 r = cmdutil.openrevlog(repo, b'perfrevlogrevision', file_, opts)
3359 r = cmdutil.openrevlog(repo, b'perfrevlogrevision', file_, opts)
3333
3360
3334 # _chunkraw was renamed to _getsegmentforrevs.
3361 # _chunkraw was renamed to _getsegmentforrevs.
3335 try:
3362 try:
3336 segmentforrevs = r._getsegmentforrevs
3363 segmentforrevs = r._getsegmentforrevs
3337 except AttributeError:
3364 except AttributeError:
3338 segmentforrevs = r._chunkraw
3365 segmentforrevs = r._chunkraw
3339
3366
3340 node = r.lookup(rev)
3367 node = r.lookup(rev)
3341 rev = r.rev(node)
3368 rev = r.rev(node)
3342
3369
3343 def getrawchunks(data, chain):
3370 def getrawchunks(data, chain):
3344 start = r.start
3371 start = r.start
3345 length = r.length
3372 length = r.length
3346 inline = r._inline
3373 inline = r._inline
3347 try:
3374 try:
3348 iosize = r.index.entry_size
3375 iosize = r.index.entry_size
3349 except AttributeError:
3376 except AttributeError:
3350 iosize = r._io.size
3377 iosize = r._io.size
3351 buffer = util.buffer
3378 buffer = util.buffer
3352
3379
3353 chunks = []
3380 chunks = []
3354 ladd = chunks.append
3381 ladd = chunks.append
3355 for idx, item in enumerate(chain):
3382 for idx, item in enumerate(chain):
3356 offset = start(item[0])
3383 offset = start(item[0])
3357 bits = data[idx]
3384 bits = data[idx]
3358 for rev in item:
3385 for rev in item:
3359 chunkstart = start(rev)
3386 chunkstart = start(rev)
3360 if inline:
3387 if inline:
3361 chunkstart += (rev + 1) * iosize
3388 chunkstart += (rev + 1) * iosize
3362 chunklength = length(rev)
3389 chunklength = length(rev)
3363 ladd(buffer(bits, chunkstart - offset, chunklength))
3390 ladd(buffer(bits, chunkstart - offset, chunklength))
3364
3391
3365 return chunks
3392 return chunks
3366
3393
3367 def dodeltachain(rev):
3394 def dodeltachain(rev):
3368 if not cache:
3395 if not cache:
3369 r.clearcaches()
3396 r.clearcaches()
3370 r._deltachain(rev)
3397 r._deltachain(rev)
3371
3398
3372 def doread(chain):
3399 def doread(chain):
3373 if not cache:
3400 if not cache:
3374 r.clearcaches()
3401 r.clearcaches()
3375 for item in slicedchain:
3402 for item in slicedchain:
3376 segmentforrevs(item[0], item[-1])
3403 segmentforrevs(item[0], item[-1])
3377
3404
3378 def doslice(r, chain, size):
3405 def doslice(r, chain, size):
3379 for s in slicechunk(r, chain, targetsize=size):
3406 for s in slicechunk(r, chain, targetsize=size):
3380 pass
3407 pass
3381
3408
3382 def dorawchunks(data, chain):
3409 def dorawchunks(data, chain):
3383 if not cache:
3410 if not cache:
3384 r.clearcaches()
3411 r.clearcaches()
3385 getrawchunks(data, chain)
3412 getrawchunks(data, chain)
3386
3413
3387 def dodecompress(chunks):
3414 def dodecompress(chunks):
3388 decomp = r.decompress
3415 decomp = r.decompress
3389 for chunk in chunks:
3416 for chunk in chunks:
3390 decomp(chunk)
3417 decomp(chunk)
3391
3418
3392 def dopatch(text, bins):
3419 def dopatch(text, bins):
3393 if not cache:
3420 if not cache:
3394 r.clearcaches()
3421 r.clearcaches()
3395 mdiff.patches(text, bins)
3422 mdiff.patches(text, bins)
3396
3423
3397 def dohash(text):
3424 def dohash(text):
3398 if not cache:
3425 if not cache:
3399 r.clearcaches()
3426 r.clearcaches()
3400 r.checkhash(text, node, rev=rev)
3427 r.checkhash(text, node, rev=rev)
3401
3428
3402 def dorevision():
3429 def dorevision():
3403 if not cache:
3430 if not cache:
3404 r.clearcaches()
3431 r.clearcaches()
3405 r.revision(node)
3432 r.revision(node)
3406
3433
3407 try:
3434 try:
3408 from mercurial.revlogutils.deltas import slicechunk
3435 from mercurial.revlogutils.deltas import slicechunk
3409 except ImportError:
3436 except ImportError:
3410 slicechunk = getattr(revlog, '_slicechunk', None)
3437 slicechunk = getattr(revlog, '_slicechunk', None)
3411
3438
3412 size = r.length(rev)
3439 size = r.length(rev)
3413 chain = r._deltachain(rev)[0]
3440 chain = r._deltachain(rev)[0]
3414 if not getattr(r, '_withsparseread', False):
3441 if not getattr(r, '_withsparseread', False):
3415 slicedchain = (chain,)
3442 slicedchain = (chain,)
3416 else:
3443 else:
3417 slicedchain = tuple(slicechunk(r, chain, targetsize=size))
3444 slicedchain = tuple(slicechunk(r, chain, targetsize=size))
3418 data = [segmentforrevs(seg[0], seg[-1])[1] for seg in slicedchain]
3445 data = [segmentforrevs(seg[0], seg[-1])[1] for seg in slicedchain]
3419 rawchunks = getrawchunks(data, slicedchain)
3446 rawchunks = getrawchunks(data, slicedchain)
3420 bins = r._chunks(chain)
3447 bins = r._chunks(chain)
3421 text = bytes(bins[0])
3448 text = bytes(bins[0])
3422 bins = bins[1:]
3449 bins = bins[1:]
3423 text = mdiff.patches(text, bins)
3450 text = mdiff.patches(text, bins)
3424
3451
3425 benches = [
3452 benches = [
3426 (lambda: dorevision(), b'full'),
3453 (lambda: dorevision(), b'full'),
3427 (lambda: dodeltachain(rev), b'deltachain'),
3454 (lambda: dodeltachain(rev), b'deltachain'),
3428 (lambda: doread(chain), b'read'),
3455 (lambda: doread(chain), b'read'),
3429 ]
3456 ]
3430
3457
3431 if getattr(r, '_withsparseread', False):
3458 if getattr(r, '_withsparseread', False):
3432 slicing = (lambda: doslice(r, chain, size), b'slice-sparse-chain')
3459 slicing = (lambda: doslice(r, chain, size), b'slice-sparse-chain')
3433 benches.append(slicing)
3460 benches.append(slicing)
3434
3461
3435 benches.extend(
3462 benches.extend(
3436 [
3463 [
3437 (lambda: dorawchunks(data, slicedchain), b'rawchunks'),
3464 (lambda: dorawchunks(data, slicedchain), b'rawchunks'),
3438 (lambda: dodecompress(rawchunks), b'decompress'),
3465 (lambda: dodecompress(rawchunks), b'decompress'),
3439 (lambda: dopatch(text, bins), b'patch'),
3466 (lambda: dopatch(text, bins), b'patch'),
3440 (lambda: dohash(text), b'hash'),
3467 (lambda: dohash(text), b'hash'),
3441 ]
3468 ]
3442 )
3469 )
3443
3470
3444 timer, fm = gettimer(ui, opts)
3471 timer, fm = gettimer(ui, opts)
3445 for fn, title in benches:
3472 for fn, title in benches:
3446 timer(fn, title=title)
3473 timer(fn, title=title)
3447 fm.end()
3474 fm.end()
3448
3475
3449
3476
3450 @command(
3477 @command(
3451 b'perf::revset|perfrevset',
3478 b'perf::revset|perfrevset',
3452 [
3479 [
3453 (b'C', b'clear', False, b'clear volatile cache between each call.'),
3480 (b'C', b'clear', False, b'clear volatile cache between each call.'),
3454 (b'', b'contexts', False, b'obtain changectx for each revision'),
3481 (b'', b'contexts', False, b'obtain changectx for each revision'),
3455 ]
3482 ]
3456 + formatteropts,
3483 + formatteropts,
3457 b"REVSET",
3484 b"REVSET",
3458 )
3485 )
3459 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
3486 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
3460 """benchmark the execution time of a revset
3487 """benchmark the execution time of a revset
3461
3488
3462 Use the --clean option if need to evaluate the impact of build volatile
3489 Use the --clean option if need to evaluate the impact of build volatile
3463 revisions set cache on the revset execution. Volatile cache hold filtered
3490 revisions set cache on the revset execution. Volatile cache hold filtered
3464 and obsolete related cache."""
3491 and obsolete related cache."""
3465 opts = _byteskwargs(opts)
3492 opts = _byteskwargs(opts)
3466
3493
3467 timer, fm = gettimer(ui, opts)
3494 timer, fm = gettimer(ui, opts)
3468
3495
3469 def d():
3496 def d():
3470 if clear:
3497 if clear:
3471 repo.invalidatevolatilesets()
3498 repo.invalidatevolatilesets()
3472 if contexts:
3499 if contexts:
3473 for ctx in repo.set(expr):
3500 for ctx in repo.set(expr):
3474 pass
3501 pass
3475 else:
3502 else:
3476 for r in repo.revs(expr):
3503 for r in repo.revs(expr):
3477 pass
3504 pass
3478
3505
3479 timer(d)
3506 timer(d)
3480 fm.end()
3507 fm.end()
3481
3508
3482
3509
3483 @command(
3510 @command(
3484 b'perf::volatilesets|perfvolatilesets',
3511 b'perf::volatilesets|perfvolatilesets',
3485 [
3512 [
3486 (b'', b'clear-obsstore', False, b'drop obsstore between each call.'),
3513 (b'', b'clear-obsstore', False, b'drop obsstore between each call.'),
3487 ]
3514 ]
3488 + formatteropts,
3515 + formatteropts,
3489 )
3516 )
3490 def perfvolatilesets(ui, repo, *names, **opts):
3517 def perfvolatilesets(ui, repo, *names, **opts):
3491 """benchmark the computation of various volatile set
3518 """benchmark the computation of various volatile set
3492
3519
3493 Volatile set computes element related to filtering and obsolescence."""
3520 Volatile set computes element related to filtering and obsolescence."""
3494 opts = _byteskwargs(opts)
3521 opts = _byteskwargs(opts)
3495 timer, fm = gettimer(ui, opts)
3522 timer, fm = gettimer(ui, opts)
3496 repo = repo.unfiltered()
3523 repo = repo.unfiltered()
3497
3524
3498 def getobs(name):
3525 def getobs(name):
3499 def d():
3526 def d():
3500 repo.invalidatevolatilesets()
3527 repo.invalidatevolatilesets()
3501 if opts[b'clear_obsstore']:
3528 if opts[b'clear_obsstore']:
3502 clearfilecache(repo, b'obsstore')
3529 clearfilecache(repo, b'obsstore')
3503 obsolete.getrevs(repo, name)
3530 obsolete.getrevs(repo, name)
3504
3531
3505 return d
3532 return d
3506
3533
3507 allobs = sorted(obsolete.cachefuncs)
3534 allobs = sorted(obsolete.cachefuncs)
3508 if names:
3535 if names:
3509 allobs = [n for n in allobs if n in names]
3536 allobs = [n for n in allobs if n in names]
3510
3537
3511 for name in allobs:
3538 for name in allobs:
3512 timer(getobs(name), title=name)
3539 timer(getobs(name), title=name)
3513
3540
3514 def getfiltered(name):
3541 def getfiltered(name):
3515 def d():
3542 def d():
3516 repo.invalidatevolatilesets()
3543 repo.invalidatevolatilesets()
3517 if opts[b'clear_obsstore']:
3544 if opts[b'clear_obsstore']:
3518 clearfilecache(repo, b'obsstore')
3545 clearfilecache(repo, b'obsstore')
3519 repoview.filterrevs(repo, name)
3546 repoview.filterrevs(repo, name)
3520
3547
3521 return d
3548 return d
3522
3549
3523 allfilter = sorted(repoview.filtertable)
3550 allfilter = sorted(repoview.filtertable)
3524 if names:
3551 if names:
3525 allfilter = [n for n in allfilter if n in names]
3552 allfilter = [n for n in allfilter if n in names]
3526
3553
3527 for name in allfilter:
3554 for name in allfilter:
3528 timer(getfiltered(name), title=name)
3555 timer(getfiltered(name), title=name)
3529 fm.end()
3556 fm.end()
3530
3557
3531
3558
3532 @command(
3559 @command(
3533 b'perf::branchmap|perfbranchmap',
3560 b'perf::branchmap|perfbranchmap',
3534 [
3561 [
3535 (b'f', b'full', False, b'Includes build time of subset'),
3562 (b'f', b'full', False, b'Includes build time of subset'),
3536 (
3563 (
3537 b'',
3564 b'',
3538 b'clear-revbranch',
3565 b'clear-revbranch',
3539 False,
3566 False,
3540 b'purge the revbranch cache between computation',
3567 b'purge the revbranch cache between computation',
3541 ),
3568 ),
3542 ]
3569 ]
3543 + formatteropts,
3570 + formatteropts,
3544 )
3571 )
3545 def perfbranchmap(ui, repo, *filternames, **opts):
3572 def perfbranchmap(ui, repo, *filternames, **opts):
3546 """benchmark the update of a branchmap
3573 """benchmark the update of a branchmap
3547
3574
3548 This benchmarks the full repo.branchmap() call with read and write disabled
3575 This benchmarks the full repo.branchmap() call with read and write disabled
3549 """
3576 """
3550 opts = _byteskwargs(opts)
3577 opts = _byteskwargs(opts)
3551 full = opts.get(b"full", False)
3578 full = opts.get(b"full", False)
3552 clear_revbranch = opts.get(b"clear_revbranch", False)
3579 clear_revbranch = opts.get(b"clear_revbranch", False)
3553 timer, fm = gettimer(ui, opts)
3580 timer, fm = gettimer(ui, opts)
3554
3581
3555 def getbranchmap(filtername):
3582 def getbranchmap(filtername):
3556 """generate a benchmark function for the filtername"""
3583 """generate a benchmark function for the filtername"""
3557 if filtername is None:
3584 if filtername is None:
3558 view = repo
3585 view = repo
3559 else:
3586 else:
3560 view = repo.filtered(filtername)
3587 view = repo.filtered(filtername)
3561 if util.safehasattr(view._branchcaches, '_per_filter'):
3588 if util.safehasattr(view._branchcaches, '_per_filter'):
3562 filtered = view._branchcaches._per_filter
3589 filtered = view._branchcaches._per_filter
3563 else:
3590 else:
3564 # older versions
3591 # older versions
3565 filtered = view._branchcaches
3592 filtered = view._branchcaches
3566
3593
3567 def d():
3594 def d():
3568 if clear_revbranch:
3595 if clear_revbranch:
3569 repo.revbranchcache()._clear()
3596 repo.revbranchcache()._clear()
3570 if full:
3597 if full:
3571 view._branchcaches.clear()
3598 view._branchcaches.clear()
3572 else:
3599 else:
3573 filtered.pop(filtername, None)
3600 filtered.pop(filtername, None)
3574 view.branchmap()
3601 view.branchmap()
3575
3602
3576 return d
3603 return d
3577
3604
3578 # add filter in smaller subset to bigger subset
3605 # add filter in smaller subset to bigger subset
3579 possiblefilters = set(repoview.filtertable)
3606 possiblefilters = set(repoview.filtertable)
3580 if filternames:
3607 if filternames:
3581 possiblefilters &= set(filternames)
3608 possiblefilters &= set(filternames)
3582 subsettable = getbranchmapsubsettable()
3609 subsettable = getbranchmapsubsettable()
3583 allfilters = []
3610 allfilters = []
3584 while possiblefilters:
3611 while possiblefilters:
3585 for name in possiblefilters:
3612 for name in possiblefilters:
3586 subset = subsettable.get(name)
3613 subset = subsettable.get(name)
3587 if subset not in possiblefilters:
3614 if subset not in possiblefilters:
3588 break
3615 break
3589 else:
3616 else:
3590 assert False, b'subset cycle %s!' % possiblefilters
3617 assert False, b'subset cycle %s!' % possiblefilters
3591 allfilters.append(name)
3618 allfilters.append(name)
3592 possiblefilters.remove(name)
3619 possiblefilters.remove(name)
3593
3620
3594 # warm the cache
3621 # warm the cache
3595 if not full:
3622 if not full:
3596 for name in allfilters:
3623 for name in allfilters:
3597 repo.filtered(name).branchmap()
3624 repo.filtered(name).branchmap()
3598 if not filternames or b'unfiltered' in filternames:
3625 if not filternames or b'unfiltered' in filternames:
3599 # add unfiltered
3626 # add unfiltered
3600 allfilters.append(None)
3627 allfilters.append(None)
3601
3628
3602 if util.safehasattr(branchmap.branchcache, 'fromfile'):
3629 if util.safehasattr(branchmap.branchcache, 'fromfile'):
3603 branchcacheread = safeattrsetter(branchmap.branchcache, b'fromfile')
3630 branchcacheread = safeattrsetter(branchmap.branchcache, b'fromfile')
3604 branchcacheread.set(classmethod(lambda *args: None))
3631 branchcacheread.set(classmethod(lambda *args: None))
3605 else:
3632 else:
3606 # older versions
3633 # older versions
3607 branchcacheread = safeattrsetter(branchmap, b'read')
3634 branchcacheread = safeattrsetter(branchmap, b'read')
3608 branchcacheread.set(lambda *args: None)
3635 branchcacheread.set(lambda *args: None)
3609 branchcachewrite = safeattrsetter(branchmap.branchcache, b'write')
3636 branchcachewrite = safeattrsetter(branchmap.branchcache, b'write')
3610 branchcachewrite.set(lambda *args: None)
3637 branchcachewrite.set(lambda *args: None)
3611 try:
3638 try:
3612 for name in allfilters:
3639 for name in allfilters:
3613 printname = name
3640 printname = name
3614 if name is None:
3641 if name is None:
3615 printname = b'unfiltered'
3642 printname = b'unfiltered'
3616 timer(getbranchmap(name), title=printname)
3643 timer(getbranchmap(name), title=printname)
3617 finally:
3644 finally:
3618 branchcacheread.restore()
3645 branchcacheread.restore()
3619 branchcachewrite.restore()
3646 branchcachewrite.restore()
3620 fm.end()
3647 fm.end()
3621
3648
3622
3649
3623 @command(
3650 @command(
3624 b'perf::branchmapupdate|perfbranchmapupdate',
3651 b'perf::branchmapupdate|perfbranchmapupdate',
3625 [
3652 [
3626 (b'', b'base', [], b'subset of revision to start from'),
3653 (b'', b'base', [], b'subset of revision to start from'),
3627 (b'', b'target', [], b'subset of revision to end with'),
3654 (b'', b'target', [], b'subset of revision to end with'),
3628 (b'', b'clear-caches', False, b'clear cache between each runs'),
3655 (b'', b'clear-caches', False, b'clear cache between each runs'),
3629 ]
3656 ]
3630 + formatteropts,
3657 + formatteropts,
3631 )
3658 )
3632 def perfbranchmapupdate(ui, repo, base=(), target=(), **opts):
3659 def perfbranchmapupdate(ui, repo, base=(), target=(), **opts):
3633 """benchmark branchmap update from for <base> revs to <target> revs
3660 """benchmark branchmap update from for <base> revs to <target> revs
3634
3661
3635 If `--clear-caches` is passed, the following items will be reset before
3662 If `--clear-caches` is passed, the following items will be reset before
3636 each update:
3663 each update:
3637 * the changelog instance and associated indexes
3664 * the changelog instance and associated indexes
3638 * the rev-branch-cache instance
3665 * the rev-branch-cache instance
3639
3666
3640 Examples:
3667 Examples:
3641
3668
3642 # update for the one last revision
3669 # update for the one last revision
3643 $ hg perfbranchmapupdate --base 'not tip' --target 'tip'
3670 $ hg perfbranchmapupdate --base 'not tip' --target 'tip'
3644
3671
3645 $ update for change coming with a new branch
3672 $ update for change coming with a new branch
3646 $ hg perfbranchmapupdate --base 'stable' --target 'default'
3673 $ hg perfbranchmapupdate --base 'stable' --target 'default'
3647 """
3674 """
3648 from mercurial import branchmap
3675 from mercurial import branchmap
3649 from mercurial import repoview
3676 from mercurial import repoview
3650
3677
3651 opts = _byteskwargs(opts)
3678 opts = _byteskwargs(opts)
3652 timer, fm = gettimer(ui, opts)
3679 timer, fm = gettimer(ui, opts)
3653 clearcaches = opts[b'clear_caches']
3680 clearcaches = opts[b'clear_caches']
3654 unfi = repo.unfiltered()
3681 unfi = repo.unfiltered()
3655 x = [None] # used to pass data between closure
3682 x = [None] # used to pass data between closure
3656
3683
3657 # we use a `list` here to avoid possible side effect from smartset
3684 # we use a `list` here to avoid possible side effect from smartset
3658 baserevs = list(scmutil.revrange(repo, base))
3685 baserevs = list(scmutil.revrange(repo, base))
3659 targetrevs = list(scmutil.revrange(repo, target))
3686 targetrevs = list(scmutil.revrange(repo, target))
3660 if not baserevs:
3687 if not baserevs:
3661 raise error.Abort(b'no revisions selected for --base')
3688 raise error.Abort(b'no revisions selected for --base')
3662 if not targetrevs:
3689 if not targetrevs:
3663 raise error.Abort(b'no revisions selected for --target')
3690 raise error.Abort(b'no revisions selected for --target')
3664
3691
3665 # make sure the target branchmap also contains the one in the base
3692 # make sure the target branchmap also contains the one in the base
3666 targetrevs = list(set(baserevs) | set(targetrevs))
3693 targetrevs = list(set(baserevs) | set(targetrevs))
3667 targetrevs.sort()
3694 targetrevs.sort()
3668
3695
3669 cl = repo.changelog
3696 cl = repo.changelog
3670 allbaserevs = list(cl.ancestors(baserevs, inclusive=True))
3697 allbaserevs = list(cl.ancestors(baserevs, inclusive=True))
3671 allbaserevs.sort()
3698 allbaserevs.sort()
3672 alltargetrevs = frozenset(cl.ancestors(targetrevs, inclusive=True))
3699 alltargetrevs = frozenset(cl.ancestors(targetrevs, inclusive=True))
3673
3700
3674 newrevs = list(alltargetrevs.difference(allbaserevs))
3701 newrevs = list(alltargetrevs.difference(allbaserevs))
3675 newrevs.sort()
3702 newrevs.sort()
3676
3703
3677 allrevs = frozenset(unfi.changelog.revs())
3704 allrevs = frozenset(unfi.changelog.revs())
3678 basefilterrevs = frozenset(allrevs.difference(allbaserevs))
3705 basefilterrevs = frozenset(allrevs.difference(allbaserevs))
3679 targetfilterrevs = frozenset(allrevs.difference(alltargetrevs))
3706 targetfilterrevs = frozenset(allrevs.difference(alltargetrevs))
3680
3707
3681 def basefilter(repo, visibilityexceptions=None):
3708 def basefilter(repo, visibilityexceptions=None):
3682 return basefilterrevs
3709 return basefilterrevs
3683
3710
3684 def targetfilter(repo, visibilityexceptions=None):
3711 def targetfilter(repo, visibilityexceptions=None):
3685 return targetfilterrevs
3712 return targetfilterrevs
3686
3713
3687 msg = b'benchmark of branchmap with %d revisions with %d new ones\n'
3714 msg = b'benchmark of branchmap with %d revisions with %d new ones\n'
3688 ui.status(msg % (len(allbaserevs), len(newrevs)))
3715 ui.status(msg % (len(allbaserevs), len(newrevs)))
3689 if targetfilterrevs:
3716 if targetfilterrevs:
3690 msg = b'(%d revisions still filtered)\n'
3717 msg = b'(%d revisions still filtered)\n'
3691 ui.status(msg % len(targetfilterrevs))
3718 ui.status(msg % len(targetfilterrevs))
3692
3719
3693 try:
3720 try:
3694 repoview.filtertable[b'__perf_branchmap_update_base'] = basefilter
3721 repoview.filtertable[b'__perf_branchmap_update_base'] = basefilter
3695 repoview.filtertable[b'__perf_branchmap_update_target'] = targetfilter
3722 repoview.filtertable[b'__perf_branchmap_update_target'] = targetfilter
3696
3723
3697 baserepo = repo.filtered(b'__perf_branchmap_update_base')
3724 baserepo = repo.filtered(b'__perf_branchmap_update_base')
3698 targetrepo = repo.filtered(b'__perf_branchmap_update_target')
3725 targetrepo = repo.filtered(b'__perf_branchmap_update_target')
3699
3726
3700 # try to find an existing branchmap to reuse
3727 # try to find an existing branchmap to reuse
3701 subsettable = getbranchmapsubsettable()
3728 subsettable = getbranchmapsubsettable()
3702 candidatefilter = subsettable.get(None)
3729 candidatefilter = subsettable.get(None)
3703 while candidatefilter is not None:
3730 while candidatefilter is not None:
3704 candidatebm = repo.filtered(candidatefilter).branchmap()
3731 candidatebm = repo.filtered(candidatefilter).branchmap()
3705 if candidatebm.validfor(baserepo):
3732 if candidatebm.validfor(baserepo):
3706 filtered = repoview.filterrevs(repo, candidatefilter)
3733 filtered = repoview.filterrevs(repo, candidatefilter)
3707 missing = [r for r in allbaserevs if r in filtered]
3734 missing = [r for r in allbaserevs if r in filtered]
3708 base = candidatebm.copy()
3735 base = candidatebm.copy()
3709 base.update(baserepo, missing)
3736 base.update(baserepo, missing)
3710 break
3737 break
3711 candidatefilter = subsettable.get(candidatefilter)
3738 candidatefilter = subsettable.get(candidatefilter)
3712 else:
3739 else:
3713 # no suitable subset where found
3740 # no suitable subset where found
3714 base = branchmap.branchcache()
3741 base = branchmap.branchcache()
3715 base.update(baserepo, allbaserevs)
3742 base.update(baserepo, allbaserevs)
3716
3743
3717 def setup():
3744 def setup():
3718 x[0] = base.copy()
3745 x[0] = base.copy()
3719 if clearcaches:
3746 if clearcaches:
3720 unfi._revbranchcache = None
3747 unfi._revbranchcache = None
3721 clearchangelog(repo)
3748 clearchangelog(repo)
3722
3749
3723 def bench():
3750 def bench():
3724 x[0].update(targetrepo, newrevs)
3751 x[0].update(targetrepo, newrevs)
3725
3752
3726 timer(bench, setup=setup)
3753 timer(bench, setup=setup)
3727 fm.end()
3754 fm.end()
3728 finally:
3755 finally:
3729 repoview.filtertable.pop(b'__perf_branchmap_update_base', None)
3756 repoview.filtertable.pop(b'__perf_branchmap_update_base', None)
3730 repoview.filtertable.pop(b'__perf_branchmap_update_target', None)
3757 repoview.filtertable.pop(b'__perf_branchmap_update_target', None)
3731
3758
3732
3759
3733 @command(
3760 @command(
3734 b'perf::branchmapload|perfbranchmapload',
3761 b'perf::branchmapload|perfbranchmapload',
3735 [
3762 [
3736 (b'f', b'filter', b'', b'Specify repoview filter'),
3763 (b'f', b'filter', b'', b'Specify repoview filter'),
3737 (b'', b'list', False, b'List brachmap filter caches'),
3764 (b'', b'list', False, b'List brachmap filter caches'),
3738 (b'', b'clear-revlogs', False, b'refresh changelog and manifest'),
3765 (b'', b'clear-revlogs', False, b'refresh changelog and manifest'),
3739 ]
3766 ]
3740 + formatteropts,
3767 + formatteropts,
3741 )
3768 )
3742 def perfbranchmapload(ui, repo, filter=b'', list=False, **opts):
3769 def perfbranchmapload(ui, repo, filter=b'', list=False, **opts):
3743 """benchmark reading the branchmap"""
3770 """benchmark reading the branchmap"""
3744 opts = _byteskwargs(opts)
3771 opts = _byteskwargs(opts)
3745 clearrevlogs = opts[b'clear_revlogs']
3772 clearrevlogs = opts[b'clear_revlogs']
3746
3773
3747 if list:
3774 if list:
3748 for name, kind, st in repo.cachevfs.readdir(stat=True):
3775 for name, kind, st in repo.cachevfs.readdir(stat=True):
3749 if name.startswith(b'branch2'):
3776 if name.startswith(b'branch2'):
3750 filtername = name.partition(b'-')[2] or b'unfiltered'
3777 filtername = name.partition(b'-')[2] or b'unfiltered'
3751 ui.status(
3778 ui.status(
3752 b'%s - %s\n' % (filtername, util.bytecount(st.st_size))
3779 b'%s - %s\n' % (filtername, util.bytecount(st.st_size))
3753 )
3780 )
3754 return
3781 return
3755 if not filter:
3782 if not filter:
3756 filter = None
3783 filter = None
3757 subsettable = getbranchmapsubsettable()
3784 subsettable = getbranchmapsubsettable()
3758 if filter is None:
3785 if filter is None:
3759 repo = repo.unfiltered()
3786 repo = repo.unfiltered()
3760 else:
3787 else:
3761 repo = repoview.repoview(repo, filter)
3788 repo = repoview.repoview(repo, filter)
3762
3789
3763 repo.branchmap() # make sure we have a relevant, up to date branchmap
3790 repo.branchmap() # make sure we have a relevant, up to date branchmap
3764
3791
3765 try:
3792 try:
3766 fromfile = branchmap.branchcache.fromfile
3793 fromfile = branchmap.branchcache.fromfile
3767 except AttributeError:
3794 except AttributeError:
3768 # older versions
3795 # older versions
3769 fromfile = branchmap.read
3796 fromfile = branchmap.read
3770
3797
3771 currentfilter = filter
3798 currentfilter = filter
3772 # try once without timer, the filter may not be cached
3799 # try once without timer, the filter may not be cached
3773 while fromfile(repo) is None:
3800 while fromfile(repo) is None:
3774 currentfilter = subsettable.get(currentfilter)
3801 currentfilter = subsettable.get(currentfilter)
3775 if currentfilter is None:
3802 if currentfilter is None:
3776 raise error.Abort(
3803 raise error.Abort(
3777 b'No branchmap cached for %s repo' % (filter or b'unfiltered')
3804 b'No branchmap cached for %s repo' % (filter or b'unfiltered')
3778 )
3805 )
3779 repo = repo.filtered(currentfilter)
3806 repo = repo.filtered(currentfilter)
3780 timer, fm = gettimer(ui, opts)
3807 timer, fm = gettimer(ui, opts)
3781
3808
3782 def setup():
3809 def setup():
3783 if clearrevlogs:
3810 if clearrevlogs:
3784 clearchangelog(repo)
3811 clearchangelog(repo)
3785
3812
3786 def bench():
3813 def bench():
3787 fromfile(repo)
3814 fromfile(repo)
3788
3815
3789 timer(bench, setup=setup)
3816 timer(bench, setup=setup)
3790 fm.end()
3817 fm.end()
3791
3818
3792
3819
3793 @command(b'perf::loadmarkers|perfloadmarkers')
3820 @command(b'perf::loadmarkers|perfloadmarkers')
3794 def perfloadmarkers(ui, repo):
3821 def perfloadmarkers(ui, repo):
3795 """benchmark the time to parse the on-disk markers for a repo
3822 """benchmark the time to parse the on-disk markers for a repo
3796
3823
3797 Result is the number of markers in the repo."""
3824 Result is the number of markers in the repo."""
3798 timer, fm = gettimer(ui)
3825 timer, fm = gettimer(ui)
3799 svfs = getsvfs(repo)
3826 svfs = getsvfs(repo)
3800 timer(lambda: len(obsolete.obsstore(repo, svfs)))
3827 timer(lambda: len(obsolete.obsstore(repo, svfs)))
3801 fm.end()
3828 fm.end()
3802
3829
3803
3830
3804 @command(
3831 @command(
3805 b'perf::lrucachedict|perflrucachedict',
3832 b'perf::lrucachedict|perflrucachedict',
3806 formatteropts
3833 formatteropts
3807 + [
3834 + [
3808 (b'', b'costlimit', 0, b'maximum total cost of items in cache'),
3835 (b'', b'costlimit', 0, b'maximum total cost of items in cache'),
3809 (b'', b'mincost', 0, b'smallest cost of items in cache'),
3836 (b'', b'mincost', 0, b'smallest cost of items in cache'),
3810 (b'', b'maxcost', 100, b'maximum cost of items in cache'),
3837 (b'', b'maxcost', 100, b'maximum cost of items in cache'),
3811 (b'', b'size', 4, b'size of cache'),
3838 (b'', b'size', 4, b'size of cache'),
3812 (b'', b'gets', 10000, b'number of key lookups'),
3839 (b'', b'gets', 10000, b'number of key lookups'),
3813 (b'', b'sets', 10000, b'number of key sets'),
3840 (b'', b'sets', 10000, b'number of key sets'),
3814 (b'', b'mixed', 10000, b'number of mixed mode operations'),
3841 (b'', b'mixed', 10000, b'number of mixed mode operations'),
3815 (
3842 (
3816 b'',
3843 b'',
3817 b'mixedgetfreq',
3844 b'mixedgetfreq',
3818 50,
3845 50,
3819 b'frequency of get vs set ops in mixed mode',
3846 b'frequency of get vs set ops in mixed mode',
3820 ),
3847 ),
3821 ],
3848 ],
3822 norepo=True,
3849 norepo=True,
3823 )
3850 )
3824 def perflrucache(
3851 def perflrucache(
3825 ui,
3852 ui,
3826 mincost=0,
3853 mincost=0,
3827 maxcost=100,
3854 maxcost=100,
3828 costlimit=0,
3855 costlimit=0,
3829 size=4,
3856 size=4,
3830 gets=10000,
3857 gets=10000,
3831 sets=10000,
3858 sets=10000,
3832 mixed=10000,
3859 mixed=10000,
3833 mixedgetfreq=50,
3860 mixedgetfreq=50,
3834 **opts
3861 **opts
3835 ):
3862 ):
3836 opts = _byteskwargs(opts)
3863 opts = _byteskwargs(opts)
3837
3864
3838 def doinit():
3865 def doinit():
3839 for i in _xrange(10000):
3866 for i in _xrange(10000):
3840 util.lrucachedict(size)
3867 util.lrucachedict(size)
3841
3868
3842 costrange = list(range(mincost, maxcost + 1))
3869 costrange = list(range(mincost, maxcost + 1))
3843
3870
3844 values = []
3871 values = []
3845 for i in _xrange(size):
3872 for i in _xrange(size):
3846 values.append(random.randint(0, _maxint))
3873 values.append(random.randint(0, _maxint))
3847
3874
3848 # Get mode fills the cache and tests raw lookup performance with no
3875 # Get mode fills the cache and tests raw lookup performance with no
3849 # eviction.
3876 # eviction.
3850 getseq = []
3877 getseq = []
3851 for i in _xrange(gets):
3878 for i in _xrange(gets):
3852 getseq.append(random.choice(values))
3879 getseq.append(random.choice(values))
3853
3880
3854 def dogets():
3881 def dogets():
3855 d = util.lrucachedict(size)
3882 d = util.lrucachedict(size)
3856 for v in values:
3883 for v in values:
3857 d[v] = v
3884 d[v] = v
3858 for key in getseq:
3885 for key in getseq:
3859 value = d[key]
3886 value = d[key]
3860 value # silence pyflakes warning
3887 value # silence pyflakes warning
3861
3888
3862 def dogetscost():
3889 def dogetscost():
3863 d = util.lrucachedict(size, maxcost=costlimit)
3890 d = util.lrucachedict(size, maxcost=costlimit)
3864 for i, v in enumerate(values):
3891 for i, v in enumerate(values):
3865 d.insert(v, v, cost=costs[i])
3892 d.insert(v, v, cost=costs[i])
3866 for key in getseq:
3893 for key in getseq:
3867 try:
3894 try:
3868 value = d[key]
3895 value = d[key]
3869 value # silence pyflakes warning
3896 value # silence pyflakes warning
3870 except KeyError:
3897 except KeyError:
3871 pass
3898 pass
3872
3899
3873 # Set mode tests insertion speed with cache eviction.
3900 # Set mode tests insertion speed with cache eviction.
3874 setseq = []
3901 setseq = []
3875 costs = []
3902 costs = []
3876 for i in _xrange(sets):
3903 for i in _xrange(sets):
3877 setseq.append(random.randint(0, _maxint))
3904 setseq.append(random.randint(0, _maxint))
3878 costs.append(random.choice(costrange))
3905 costs.append(random.choice(costrange))
3879
3906
3880 def doinserts():
3907 def doinserts():
3881 d = util.lrucachedict(size)
3908 d = util.lrucachedict(size)
3882 for v in setseq:
3909 for v in setseq:
3883 d.insert(v, v)
3910 d.insert(v, v)
3884
3911
3885 def doinsertscost():
3912 def doinsertscost():
3886 d = util.lrucachedict(size, maxcost=costlimit)
3913 d = util.lrucachedict(size, maxcost=costlimit)
3887 for i, v in enumerate(setseq):
3914 for i, v in enumerate(setseq):
3888 d.insert(v, v, cost=costs[i])
3915 d.insert(v, v, cost=costs[i])
3889
3916
3890 def dosets():
3917 def dosets():
3891 d = util.lrucachedict(size)
3918 d = util.lrucachedict(size)
3892 for v in setseq:
3919 for v in setseq:
3893 d[v] = v
3920 d[v] = v
3894
3921
3895 # Mixed mode randomly performs gets and sets with eviction.
3922 # Mixed mode randomly performs gets and sets with eviction.
3896 mixedops = []
3923 mixedops = []
3897 for i in _xrange(mixed):
3924 for i in _xrange(mixed):
3898 r = random.randint(0, 100)
3925 r = random.randint(0, 100)
3899 if r < mixedgetfreq:
3926 if r < mixedgetfreq:
3900 op = 0
3927 op = 0
3901 else:
3928 else:
3902 op = 1
3929 op = 1
3903
3930
3904 mixedops.append(
3931 mixedops.append(
3905 (op, random.randint(0, size * 2), random.choice(costrange))
3932 (op, random.randint(0, size * 2), random.choice(costrange))
3906 )
3933 )
3907
3934
3908 def domixed():
3935 def domixed():
3909 d = util.lrucachedict(size)
3936 d = util.lrucachedict(size)
3910
3937
3911 for op, v, cost in mixedops:
3938 for op, v, cost in mixedops:
3912 if op == 0:
3939 if op == 0:
3913 try:
3940 try:
3914 d[v]
3941 d[v]
3915 except KeyError:
3942 except KeyError:
3916 pass
3943 pass
3917 else:
3944 else:
3918 d[v] = v
3945 d[v] = v
3919
3946
3920 def domixedcost():
3947 def domixedcost():
3921 d = util.lrucachedict(size, maxcost=costlimit)
3948 d = util.lrucachedict(size, maxcost=costlimit)
3922
3949
3923 for op, v, cost in mixedops:
3950 for op, v, cost in mixedops:
3924 if op == 0:
3951 if op == 0:
3925 try:
3952 try:
3926 d[v]
3953 d[v]
3927 except KeyError:
3954 except KeyError:
3928 pass
3955 pass
3929 else:
3956 else:
3930 d.insert(v, v, cost=cost)
3957 d.insert(v, v, cost=cost)
3931
3958
3932 benches = [
3959 benches = [
3933 (doinit, b'init'),
3960 (doinit, b'init'),
3934 ]
3961 ]
3935
3962
3936 if costlimit:
3963 if costlimit:
3937 benches.extend(
3964 benches.extend(
3938 [
3965 [
3939 (dogetscost, b'gets w/ cost limit'),
3966 (dogetscost, b'gets w/ cost limit'),
3940 (doinsertscost, b'inserts w/ cost limit'),
3967 (doinsertscost, b'inserts w/ cost limit'),
3941 (domixedcost, b'mixed w/ cost limit'),
3968 (domixedcost, b'mixed w/ cost limit'),
3942 ]
3969 ]
3943 )
3970 )
3944 else:
3971 else:
3945 benches.extend(
3972 benches.extend(
3946 [
3973 [
3947 (dogets, b'gets'),
3974 (dogets, b'gets'),
3948 (doinserts, b'inserts'),
3975 (doinserts, b'inserts'),
3949 (dosets, b'sets'),
3976 (dosets, b'sets'),
3950 (domixed, b'mixed'),
3977 (domixed, b'mixed'),
3951 ]
3978 ]
3952 )
3979 )
3953
3980
3954 for fn, title in benches:
3981 for fn, title in benches:
3955 timer, fm = gettimer(ui, opts)
3982 timer, fm = gettimer(ui, opts)
3956 timer(fn, title=title)
3983 timer(fn, title=title)
3957 fm.end()
3984 fm.end()
3958
3985
3959
3986
3960 @command(
3987 @command(
3961 b'perf::write|perfwrite',
3988 b'perf::write|perfwrite',
3962 formatteropts
3989 formatteropts
3963 + [
3990 + [
3964 (b'', b'write-method', b'write', b'ui write method'),
3991 (b'', b'write-method', b'write', b'ui write method'),
3965 (b'', b'nlines', 100, b'number of lines'),
3992 (b'', b'nlines', 100, b'number of lines'),
3966 (b'', b'nitems', 100, b'number of items (per line)'),
3993 (b'', b'nitems', 100, b'number of items (per line)'),
3967 (b'', b'item', b'x', b'item that is written'),
3994 (b'', b'item', b'x', b'item that is written'),
3968 (b'', b'batch-line', None, b'pass whole line to write method at once'),
3995 (b'', b'batch-line', None, b'pass whole line to write method at once'),
3969 (b'', b'flush-line', None, b'flush after each line'),
3996 (b'', b'flush-line', None, b'flush after each line'),
3970 ],
3997 ],
3971 )
3998 )
3972 def perfwrite(ui, repo, **opts):
3999 def perfwrite(ui, repo, **opts):
3973 """microbenchmark ui.write (and others)"""
4000 """microbenchmark ui.write (and others)"""
3974 opts = _byteskwargs(opts)
4001 opts = _byteskwargs(opts)
3975
4002
3976 write = getattr(ui, _sysstr(opts[b'write_method']))
4003 write = getattr(ui, _sysstr(opts[b'write_method']))
3977 nlines = int(opts[b'nlines'])
4004 nlines = int(opts[b'nlines'])
3978 nitems = int(opts[b'nitems'])
4005 nitems = int(opts[b'nitems'])
3979 item = opts[b'item']
4006 item = opts[b'item']
3980 batch_line = opts.get(b'batch_line')
4007 batch_line = opts.get(b'batch_line')
3981 flush_line = opts.get(b'flush_line')
4008 flush_line = opts.get(b'flush_line')
3982
4009
3983 if batch_line:
4010 if batch_line:
3984 line = item * nitems + b'\n'
4011 line = item * nitems + b'\n'
3985
4012
3986 def benchmark():
4013 def benchmark():
3987 for i in pycompat.xrange(nlines):
4014 for i in pycompat.xrange(nlines):
3988 if batch_line:
4015 if batch_line:
3989 write(line)
4016 write(line)
3990 else:
4017 else:
3991 for i in pycompat.xrange(nitems):
4018 for i in pycompat.xrange(nitems):
3992 write(item)
4019 write(item)
3993 write(b'\n')
4020 write(b'\n')
3994 if flush_line:
4021 if flush_line:
3995 ui.flush()
4022 ui.flush()
3996 ui.flush()
4023 ui.flush()
3997
4024
3998 timer, fm = gettimer(ui, opts)
4025 timer, fm = gettimer(ui, opts)
3999 timer(benchmark)
4026 timer(benchmark)
4000 fm.end()
4027 fm.end()
4001
4028
4002
4029
4003 def uisetup(ui):
4030 def uisetup(ui):
4004 if util.safehasattr(cmdutil, b'openrevlog') and not util.safehasattr(
4031 if util.safehasattr(cmdutil, b'openrevlog') and not util.safehasattr(
4005 commands, b'debugrevlogopts'
4032 commands, b'debugrevlogopts'
4006 ):
4033 ):
4007 # for "historical portability":
4034 # for "historical portability":
4008 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
4035 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
4009 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
4036 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
4010 # openrevlog() should cause failure, because it has been
4037 # openrevlog() should cause failure, because it has been
4011 # available since 3.5 (or 49c583ca48c4).
4038 # available since 3.5 (or 49c583ca48c4).
4012 def openrevlog(orig, repo, cmd, file_, opts):
4039 def openrevlog(orig, repo, cmd, file_, opts):
4013 if opts.get(b'dir') and not util.safehasattr(repo, b'dirlog'):
4040 if opts.get(b'dir') and not util.safehasattr(repo, b'dirlog'):
4014 raise error.Abort(
4041 raise error.Abort(
4015 b"This version doesn't support --dir option",
4042 b"This version doesn't support --dir option",
4016 hint=b"use 3.5 or later",
4043 hint=b"use 3.5 or later",
4017 )
4044 )
4018 return orig(repo, cmd, file_, opts)
4045 return orig(repo, cmd, file_, opts)
4019
4046
4020 extensions.wrapfunction(cmdutil, b'openrevlog', openrevlog)
4047 extensions.wrapfunction(cmdutil, b'openrevlog', openrevlog)
4021
4048
4022
4049
4023 @command(
4050 @command(
4024 b'perf::progress|perfprogress',
4051 b'perf::progress|perfprogress',
4025 formatteropts
4052 formatteropts
4026 + [
4053 + [
4027 (b'', b'topic', b'topic', b'topic for progress messages'),
4054 (b'', b'topic', b'topic', b'topic for progress messages'),
4028 (b'c', b'total', 1000000, b'total value we are progressing to'),
4055 (b'c', b'total', 1000000, b'total value we are progressing to'),
4029 ],
4056 ],
4030 norepo=True,
4057 norepo=True,
4031 )
4058 )
4032 def perfprogress(ui, topic=None, total=None, **opts):
4059 def perfprogress(ui, topic=None, total=None, **opts):
4033 """printing of progress bars"""
4060 """printing of progress bars"""
4034 opts = _byteskwargs(opts)
4061 opts = _byteskwargs(opts)
4035
4062
4036 timer, fm = gettimer(ui, opts)
4063 timer, fm = gettimer(ui, opts)
4037
4064
4038 def doprogress():
4065 def doprogress():
4039 with ui.makeprogress(topic, total=total) as progress:
4066 with ui.makeprogress(topic, total=total) as progress:
4040 for i in _xrange(total):
4067 for i in _xrange(total):
4041 progress.increment()
4068 progress.increment()
4042
4069
4043 timer(doprogress)
4070 timer(doprogress)
4044 fm.end()
4071 fm.end()
@@ -1,7975 +1,7974 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
3 # Copyright 2005-2007 Olivia Mackall <olivia@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
8
9 import os
9 import os
10 import re
10 import re
11 import sys
11 import sys
12
12
13 from .i18n import _
13 from .i18n import _
14 from .node import (
14 from .node import (
15 hex,
15 hex,
16 nullrev,
16 nullrev,
17 short,
17 short,
18 wdirrev,
18 wdirrev,
19 )
19 )
20 from .pycompat import open
20 from .pycompat import open
21 from . import (
21 from . import (
22 archival,
22 archival,
23 bookmarks,
23 bookmarks,
24 bundle2,
24 bundle2,
25 bundlecaches,
25 bundlecaches,
26 changegroup,
26 changegroup,
27 cmdutil,
27 cmdutil,
28 copies,
28 copies,
29 debugcommands as debugcommandsmod,
29 debugcommands as debugcommandsmod,
30 destutil,
30 destutil,
31 dirstateguard,
31 dirstateguard,
32 discovery,
32 discovery,
33 encoding,
33 encoding,
34 error,
34 error,
35 exchange,
35 exchange,
36 extensions,
36 extensions,
37 filemerge,
37 filemerge,
38 formatter,
38 formatter,
39 graphmod,
39 graphmod,
40 grep as grepmod,
40 grep as grepmod,
41 hbisect,
41 hbisect,
42 help,
42 help,
43 hg,
43 hg,
44 logcmdutil,
44 logcmdutil,
45 merge as mergemod,
45 merge as mergemod,
46 mergestate as mergestatemod,
46 mergestate as mergestatemod,
47 narrowspec,
47 narrowspec,
48 obsolete,
48 obsolete,
49 obsutil,
49 obsutil,
50 patch,
50 patch,
51 phases,
51 phases,
52 pycompat,
52 pycompat,
53 rcutil,
53 rcutil,
54 registrar,
54 registrar,
55 requirements,
55 requirements,
56 revsetlang,
56 revsetlang,
57 rewriteutil,
57 rewriteutil,
58 scmutil,
58 scmutil,
59 server,
59 server,
60 shelve as shelvemod,
60 shelve as shelvemod,
61 state as statemod,
61 state as statemod,
62 streamclone,
62 streamclone,
63 tags as tagsmod,
63 tags as tagsmod,
64 ui as uimod,
64 ui as uimod,
65 util,
65 util,
66 verify as verifymod,
66 verify as verifymod,
67 vfs as vfsmod,
67 vfs as vfsmod,
68 wireprotoserver,
68 wireprotoserver,
69 )
69 )
70 from .utils import (
70 from .utils import (
71 dateutil,
71 dateutil,
72 stringutil,
72 stringutil,
73 urlutil,
73 urlutil,
74 )
74 )
75
75
76 table = {}
76 table = {}
77 table.update(debugcommandsmod.command._table)
77 table.update(debugcommandsmod.command._table)
78
78
79 command = registrar.command(table)
79 command = registrar.command(table)
80 INTENT_READONLY = registrar.INTENT_READONLY
80 INTENT_READONLY = registrar.INTENT_READONLY
81
81
82 # common command options
82 # common command options
83
83
84 globalopts = [
84 globalopts = [
85 (
85 (
86 b'R',
86 b'R',
87 b'repository',
87 b'repository',
88 b'',
88 b'',
89 _(b'repository root directory or name of overlay bundle file'),
89 _(b'repository root directory or name of overlay bundle file'),
90 _(b'REPO'),
90 _(b'REPO'),
91 ),
91 ),
92 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
92 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
93 (
93 (
94 b'y',
94 b'y',
95 b'noninteractive',
95 b'noninteractive',
96 None,
96 None,
97 _(
97 _(
98 b'do not prompt, automatically pick the first choice for all prompts'
98 b'do not prompt, automatically pick the first choice for all prompts'
99 ),
99 ),
100 ),
100 ),
101 (b'q', b'quiet', None, _(b'suppress output')),
101 (b'q', b'quiet', None, _(b'suppress output')),
102 (b'v', b'verbose', None, _(b'enable additional output')),
102 (b'v', b'verbose', None, _(b'enable additional output')),
103 (
103 (
104 b'',
104 b'',
105 b'color',
105 b'color',
106 b'',
106 b'',
107 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
107 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
108 # and should not be translated
108 # and should not be translated
109 _(b"when to colorize (boolean, always, auto, never, or debug)"),
109 _(b"when to colorize (boolean, always, auto, never, or debug)"),
110 _(b'TYPE'),
110 _(b'TYPE'),
111 ),
111 ),
112 (
112 (
113 b'',
113 b'',
114 b'config',
114 b'config',
115 [],
115 [],
116 _(b'set/override config option (use \'section.name=value\')'),
116 _(b'set/override config option (use \'section.name=value\')'),
117 _(b'CONFIG'),
117 _(b'CONFIG'),
118 ),
118 ),
119 (b'', b'debug', None, _(b'enable debugging output')),
119 (b'', b'debug', None, _(b'enable debugging output')),
120 (b'', b'debugger', None, _(b'start debugger')),
120 (b'', b'debugger', None, _(b'start debugger')),
121 (
121 (
122 b'',
122 b'',
123 b'encoding',
123 b'encoding',
124 encoding.encoding,
124 encoding.encoding,
125 _(b'set the charset encoding'),
125 _(b'set the charset encoding'),
126 _(b'ENCODE'),
126 _(b'ENCODE'),
127 ),
127 ),
128 (
128 (
129 b'',
129 b'',
130 b'encodingmode',
130 b'encodingmode',
131 encoding.encodingmode,
131 encoding.encodingmode,
132 _(b'set the charset encoding mode'),
132 _(b'set the charset encoding mode'),
133 _(b'MODE'),
133 _(b'MODE'),
134 ),
134 ),
135 (b'', b'traceback', None, _(b'always print a traceback on exception')),
135 (b'', b'traceback', None, _(b'always print a traceback on exception')),
136 (b'', b'time', None, _(b'time how long the command takes')),
136 (b'', b'time', None, _(b'time how long the command takes')),
137 (b'', b'profile', None, _(b'print command execution profile')),
137 (b'', b'profile', None, _(b'print command execution profile')),
138 (b'', b'version', None, _(b'output version information and exit')),
138 (b'', b'version', None, _(b'output version information and exit')),
139 (b'h', b'help', None, _(b'display help and exit')),
139 (b'h', b'help', None, _(b'display help and exit')),
140 (b'', b'hidden', False, _(b'consider hidden changesets')),
140 (b'', b'hidden', False, _(b'consider hidden changesets')),
141 (
141 (
142 b'',
142 b'',
143 b'pager',
143 b'pager',
144 b'auto',
144 b'auto',
145 _(b"when to paginate (boolean, always, auto, or never)"),
145 _(b"when to paginate (boolean, always, auto, or never)"),
146 _(b'TYPE'),
146 _(b'TYPE'),
147 ),
147 ),
148 ]
148 ]
149
149
150 dryrunopts = cmdutil.dryrunopts
150 dryrunopts = cmdutil.dryrunopts
151 remoteopts = cmdutil.remoteopts
151 remoteopts = cmdutil.remoteopts
152 walkopts = cmdutil.walkopts
152 walkopts = cmdutil.walkopts
153 commitopts = cmdutil.commitopts
153 commitopts = cmdutil.commitopts
154 commitopts2 = cmdutil.commitopts2
154 commitopts2 = cmdutil.commitopts2
155 commitopts3 = cmdutil.commitopts3
155 commitopts3 = cmdutil.commitopts3
156 formatteropts = cmdutil.formatteropts
156 formatteropts = cmdutil.formatteropts
157 templateopts = cmdutil.templateopts
157 templateopts = cmdutil.templateopts
158 logopts = cmdutil.logopts
158 logopts = cmdutil.logopts
159 diffopts = cmdutil.diffopts
159 diffopts = cmdutil.diffopts
160 diffwsopts = cmdutil.diffwsopts
160 diffwsopts = cmdutil.diffwsopts
161 diffopts2 = cmdutil.diffopts2
161 diffopts2 = cmdutil.diffopts2
162 mergetoolopts = cmdutil.mergetoolopts
162 mergetoolopts = cmdutil.mergetoolopts
163 similarityopts = cmdutil.similarityopts
163 similarityopts = cmdutil.similarityopts
164 subrepoopts = cmdutil.subrepoopts
164 subrepoopts = cmdutil.subrepoopts
165 debugrevlogopts = cmdutil.debugrevlogopts
165 debugrevlogopts = cmdutil.debugrevlogopts
166
166
167 # Commands start here, listed alphabetically
167 # Commands start here, listed alphabetically
168
168
169
169
170 @command(
170 @command(
171 b'abort',
171 b'abort',
172 dryrunopts,
172 dryrunopts,
173 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
173 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
174 helpbasic=True,
174 helpbasic=True,
175 )
175 )
176 def abort(ui, repo, **opts):
176 def abort(ui, repo, **opts):
177 """abort an unfinished operation (EXPERIMENTAL)
177 """abort an unfinished operation (EXPERIMENTAL)
178
178
179 Aborts a multistep operation like graft, histedit, rebase, merge,
179 Aborts a multistep operation like graft, histedit, rebase, merge,
180 and unshelve if they are in an unfinished state.
180 and unshelve if they are in an unfinished state.
181
181
182 use --dry-run/-n to dry run the command.
182 use --dry-run/-n to dry run the command.
183 """
183 """
184 dryrun = opts.get('dry_run')
184 dryrun = opts.get('dry_run')
185 abortstate = cmdutil.getunfinishedstate(repo)
185 abortstate = cmdutil.getunfinishedstate(repo)
186 if not abortstate:
186 if not abortstate:
187 raise error.StateError(_(b'no operation in progress'))
187 raise error.StateError(_(b'no operation in progress'))
188 if not abortstate.abortfunc:
188 if not abortstate.abortfunc:
189 raise error.InputError(
189 raise error.InputError(
190 (
190 (
191 _(b"%s in progress but does not support 'hg abort'")
191 _(b"%s in progress but does not support 'hg abort'")
192 % (abortstate._opname)
192 % (abortstate._opname)
193 ),
193 ),
194 hint=abortstate.hint(),
194 hint=abortstate.hint(),
195 )
195 )
196 if dryrun:
196 if dryrun:
197 ui.status(
197 ui.status(
198 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
198 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
199 )
199 )
200 return
200 return
201 return abortstate.abortfunc(ui, repo)
201 return abortstate.abortfunc(ui, repo)
202
202
203
203
204 @command(
204 @command(
205 b'add',
205 b'add',
206 walkopts + subrepoopts + dryrunopts,
206 walkopts + subrepoopts + dryrunopts,
207 _(b'[OPTION]... [FILE]...'),
207 _(b'[OPTION]... [FILE]...'),
208 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
208 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
209 helpbasic=True,
209 helpbasic=True,
210 inferrepo=True,
210 inferrepo=True,
211 )
211 )
212 def add(ui, repo, *pats, **opts):
212 def add(ui, repo, *pats, **opts):
213 """add the specified files on the next commit
213 """add the specified files on the next commit
214
214
215 Schedule files to be version controlled and added to the
215 Schedule files to be version controlled and added to the
216 repository.
216 repository.
217
217
218 The files will be added to the repository at the next commit. To
218 The files will be added to the repository at the next commit. To
219 undo an add before that, see :hg:`forget`.
219 undo an add before that, see :hg:`forget`.
220
220
221 If no names are given, add all files to the repository (except
221 If no names are given, add all files to the repository (except
222 files matching ``.hgignore``).
222 files matching ``.hgignore``).
223
223
224 .. container:: verbose
224 .. container:: verbose
225
225
226 Examples:
226 Examples:
227
227
228 - New (unknown) files are added
228 - New (unknown) files are added
229 automatically by :hg:`add`::
229 automatically by :hg:`add`::
230
230
231 $ ls
231 $ ls
232 foo.c
232 foo.c
233 $ hg status
233 $ hg status
234 ? foo.c
234 ? foo.c
235 $ hg add
235 $ hg add
236 adding foo.c
236 adding foo.c
237 $ hg status
237 $ hg status
238 A foo.c
238 A foo.c
239
239
240 - Specific files to be added can be specified::
240 - Specific files to be added can be specified::
241
241
242 $ ls
242 $ ls
243 bar.c foo.c
243 bar.c foo.c
244 $ hg status
244 $ hg status
245 ? bar.c
245 ? bar.c
246 ? foo.c
246 ? foo.c
247 $ hg add bar.c
247 $ hg add bar.c
248 $ hg status
248 $ hg status
249 A bar.c
249 A bar.c
250 ? foo.c
250 ? foo.c
251
251
252 Returns 0 if all files are successfully added.
252 Returns 0 if all files are successfully added.
253 """
253 """
254
254
255 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
255 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
256 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
256 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
257 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
257 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
258 return rejected and 1 or 0
258 return rejected and 1 or 0
259
259
260
260
261 @command(
261 @command(
262 b'addremove',
262 b'addremove',
263 similarityopts + subrepoopts + walkopts + dryrunopts,
263 similarityopts + subrepoopts + walkopts + dryrunopts,
264 _(b'[OPTION]... [FILE]...'),
264 _(b'[OPTION]... [FILE]...'),
265 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
265 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
266 inferrepo=True,
266 inferrepo=True,
267 )
267 )
268 def addremove(ui, repo, *pats, **opts):
268 def addremove(ui, repo, *pats, **opts):
269 """add all new files, delete all missing files
269 """add all new files, delete all missing files
270
270
271 Add all new files and remove all missing files from the
271 Add all new files and remove all missing files from the
272 repository.
272 repository.
273
273
274 Unless names are given, new files are ignored if they match any of
274 Unless names are given, new files are ignored if they match any of
275 the patterns in ``.hgignore``. As with add, these changes take
275 the patterns in ``.hgignore``. As with add, these changes take
276 effect at the next commit.
276 effect at the next commit.
277
277
278 Use the -s/--similarity option to detect renamed files. This
278 Use the -s/--similarity option to detect renamed files. This
279 option takes a percentage between 0 (disabled) and 100 (files must
279 option takes a percentage between 0 (disabled) and 100 (files must
280 be identical) as its parameter. With a parameter greater than 0,
280 be identical) as its parameter. With a parameter greater than 0,
281 this compares every removed file with every added file and records
281 this compares every removed file with every added file and records
282 those similar enough as renames. Detecting renamed files this way
282 those similar enough as renames. Detecting renamed files this way
283 can be expensive. After using this option, :hg:`status -C` can be
283 can be expensive. After using this option, :hg:`status -C` can be
284 used to check which files were identified as moved or renamed. If
284 used to check which files were identified as moved or renamed. If
285 not specified, -s/--similarity defaults to 100 and only renames of
285 not specified, -s/--similarity defaults to 100 and only renames of
286 identical files are detected.
286 identical files are detected.
287
287
288 .. container:: verbose
288 .. container:: verbose
289
289
290 Examples:
290 Examples:
291
291
292 - A number of files (bar.c and foo.c) are new,
292 - A number of files (bar.c and foo.c) are new,
293 while foobar.c has been removed (without using :hg:`remove`)
293 while foobar.c has been removed (without using :hg:`remove`)
294 from the repository::
294 from the repository::
295
295
296 $ ls
296 $ ls
297 bar.c foo.c
297 bar.c foo.c
298 $ hg status
298 $ hg status
299 ! foobar.c
299 ! foobar.c
300 ? bar.c
300 ? bar.c
301 ? foo.c
301 ? foo.c
302 $ hg addremove
302 $ hg addremove
303 adding bar.c
303 adding bar.c
304 adding foo.c
304 adding foo.c
305 removing foobar.c
305 removing foobar.c
306 $ hg status
306 $ hg status
307 A bar.c
307 A bar.c
308 A foo.c
308 A foo.c
309 R foobar.c
309 R foobar.c
310
310
311 - A file foobar.c was moved to foo.c without using :hg:`rename`.
311 - A file foobar.c was moved to foo.c without using :hg:`rename`.
312 Afterwards, it was edited slightly::
312 Afterwards, it was edited slightly::
313
313
314 $ ls
314 $ ls
315 foo.c
315 foo.c
316 $ hg status
316 $ hg status
317 ! foobar.c
317 ! foobar.c
318 ? foo.c
318 ? foo.c
319 $ hg addremove --similarity 90
319 $ hg addremove --similarity 90
320 removing foobar.c
320 removing foobar.c
321 adding foo.c
321 adding foo.c
322 recording removal of foobar.c as rename to foo.c (94% similar)
322 recording removal of foobar.c as rename to foo.c (94% similar)
323 $ hg status -C
323 $ hg status -C
324 A foo.c
324 A foo.c
325 foobar.c
325 foobar.c
326 R foobar.c
326 R foobar.c
327
327
328 Returns 0 if all files are successfully added.
328 Returns 0 if all files are successfully added.
329 """
329 """
330 opts = pycompat.byteskwargs(opts)
330 opts = pycompat.byteskwargs(opts)
331 if not opts.get(b'similarity'):
331 if not opts.get(b'similarity'):
332 opts[b'similarity'] = b'100'
332 opts[b'similarity'] = b'100'
333 matcher = scmutil.match(repo[None], pats, opts)
333 matcher = scmutil.match(repo[None], pats, opts)
334 relative = scmutil.anypats(pats, opts)
334 relative = scmutil.anypats(pats, opts)
335 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
335 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
336 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
336 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
337
337
338
338
339 @command(
339 @command(
340 b'annotate|blame',
340 b'annotate|blame',
341 [
341 [
342 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
342 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
343 (
343 (
344 b'',
344 b'',
345 b'follow',
345 b'follow',
346 None,
346 None,
347 _(b'follow copies/renames and list the filename (DEPRECATED)'),
347 _(b'follow copies/renames and list the filename (DEPRECATED)'),
348 ),
348 ),
349 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
349 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
350 (b'a', b'text', None, _(b'treat all files as text')),
350 (b'a', b'text', None, _(b'treat all files as text')),
351 (b'u', b'user', None, _(b'list the author (long with -v)')),
351 (b'u', b'user', None, _(b'list the author (long with -v)')),
352 (b'f', b'file', None, _(b'list the filename')),
352 (b'f', b'file', None, _(b'list the filename')),
353 (b'd', b'date', None, _(b'list the date (short with -q)')),
353 (b'd', b'date', None, _(b'list the date (short with -q)')),
354 (b'n', b'number', None, _(b'list the revision number (default)')),
354 (b'n', b'number', None, _(b'list the revision number (default)')),
355 (b'c', b'changeset', None, _(b'list the changeset')),
355 (b'c', b'changeset', None, _(b'list the changeset')),
356 (
356 (
357 b'l',
357 b'l',
358 b'line-number',
358 b'line-number',
359 None,
359 None,
360 _(b'show line number at the first appearance'),
360 _(b'show line number at the first appearance'),
361 ),
361 ),
362 (
362 (
363 b'',
363 b'',
364 b'skip',
364 b'skip',
365 [],
365 [],
366 _(b'revset to not display (EXPERIMENTAL)'),
366 _(b'revset to not display (EXPERIMENTAL)'),
367 _(b'REV'),
367 _(b'REV'),
368 ),
368 ),
369 ]
369 ]
370 + diffwsopts
370 + diffwsopts
371 + walkopts
371 + walkopts
372 + formatteropts,
372 + formatteropts,
373 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
373 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
374 helpcategory=command.CATEGORY_FILE_CONTENTS,
374 helpcategory=command.CATEGORY_FILE_CONTENTS,
375 helpbasic=True,
375 helpbasic=True,
376 inferrepo=True,
376 inferrepo=True,
377 )
377 )
378 def annotate(ui, repo, *pats, **opts):
378 def annotate(ui, repo, *pats, **opts):
379 """show changeset information by line for each file
379 """show changeset information by line for each file
380
380
381 List changes in files, showing the revision id responsible for
381 List changes in files, showing the revision id responsible for
382 each line.
382 each line.
383
383
384 This command is useful for discovering when a change was made and
384 This command is useful for discovering when a change was made and
385 by whom.
385 by whom.
386
386
387 If you include --file, --user, or --date, the revision number is
387 If you include --file, --user, or --date, the revision number is
388 suppressed unless you also include --number.
388 suppressed unless you also include --number.
389
389
390 Without the -a/--text option, annotate will avoid processing files
390 Without the -a/--text option, annotate will avoid processing files
391 it detects as binary. With -a, annotate will annotate the file
391 it detects as binary. With -a, annotate will annotate the file
392 anyway, although the results will probably be neither useful
392 anyway, although the results will probably be neither useful
393 nor desirable.
393 nor desirable.
394
394
395 .. container:: verbose
395 .. container:: verbose
396
396
397 Template:
397 Template:
398
398
399 The following keywords are supported in addition to the common template
399 The following keywords are supported in addition to the common template
400 keywords and functions. See also :hg:`help templates`.
400 keywords and functions. See also :hg:`help templates`.
401
401
402 :lines: List of lines with annotation data.
402 :lines: List of lines with annotation data.
403 :path: String. Repository-absolute path of the specified file.
403 :path: String. Repository-absolute path of the specified file.
404
404
405 And each entry of ``{lines}`` provides the following sub-keywords in
405 And each entry of ``{lines}`` provides the following sub-keywords in
406 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
406 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
407
407
408 :line: String. Line content.
408 :line: String. Line content.
409 :lineno: Integer. Line number at that revision.
409 :lineno: Integer. Line number at that revision.
410 :path: String. Repository-absolute path of the file at that revision.
410 :path: String. Repository-absolute path of the file at that revision.
411
411
412 See :hg:`help templates.operators` for the list expansion syntax.
412 See :hg:`help templates.operators` for the list expansion syntax.
413
413
414 Returns 0 on success.
414 Returns 0 on success.
415 """
415 """
416 opts = pycompat.byteskwargs(opts)
416 opts = pycompat.byteskwargs(opts)
417 if not pats:
417 if not pats:
418 raise error.InputError(
418 raise error.InputError(
419 _(b'at least one filename or pattern is required')
419 _(b'at least one filename or pattern is required')
420 )
420 )
421
421
422 if opts.get(b'follow'):
422 if opts.get(b'follow'):
423 # --follow is deprecated and now just an alias for -f/--file
423 # --follow is deprecated and now just an alias for -f/--file
424 # to mimic the behavior of Mercurial before version 1.5
424 # to mimic the behavior of Mercurial before version 1.5
425 opts[b'file'] = True
425 opts[b'file'] = True
426
426
427 if (
427 if (
428 not opts.get(b'user')
428 not opts.get(b'user')
429 and not opts.get(b'changeset')
429 and not opts.get(b'changeset')
430 and not opts.get(b'date')
430 and not opts.get(b'date')
431 and not opts.get(b'file')
431 and not opts.get(b'file')
432 ):
432 ):
433 opts[b'number'] = True
433 opts[b'number'] = True
434
434
435 linenumber = opts.get(b'line_number') is not None
435 linenumber = opts.get(b'line_number') is not None
436 if (
436 if (
437 linenumber
437 linenumber
438 and (not opts.get(b'changeset'))
438 and (not opts.get(b'changeset'))
439 and (not opts.get(b'number'))
439 and (not opts.get(b'number'))
440 ):
440 ):
441 raise error.InputError(_(b'at least one of -n/-c is required for -l'))
441 raise error.InputError(_(b'at least one of -n/-c is required for -l'))
442
442
443 rev = opts.get(b'rev')
443 rev = opts.get(b'rev')
444 if rev:
444 if rev:
445 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
445 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
446 ctx = logcmdutil.revsingle(repo, rev)
446 ctx = logcmdutil.revsingle(repo, rev)
447
447
448 ui.pager(b'annotate')
448 ui.pager(b'annotate')
449 rootfm = ui.formatter(b'annotate', opts)
449 rootfm = ui.formatter(b'annotate', opts)
450 if ui.debugflag:
450 if ui.debugflag:
451 shorthex = pycompat.identity
451 shorthex = pycompat.identity
452 else:
452 else:
453
453
454 def shorthex(h):
454 def shorthex(h):
455 return h[:12]
455 return h[:12]
456
456
457 if ui.quiet:
457 if ui.quiet:
458 datefunc = dateutil.shortdate
458 datefunc = dateutil.shortdate
459 else:
459 else:
460 datefunc = dateutil.datestr
460 datefunc = dateutil.datestr
461 if ctx.rev() is None:
461 if ctx.rev() is None:
462 if opts.get(b'changeset'):
462 if opts.get(b'changeset'):
463 # omit "+" suffix which is appended to node hex
463 # omit "+" suffix which is appended to node hex
464 def formatrev(rev):
464 def formatrev(rev):
465 if rev == wdirrev:
465 if rev == wdirrev:
466 return b'%d' % ctx.p1().rev()
466 return b'%d' % ctx.p1().rev()
467 else:
467 else:
468 return b'%d' % rev
468 return b'%d' % rev
469
469
470 else:
470 else:
471
471
472 def formatrev(rev):
472 def formatrev(rev):
473 if rev == wdirrev:
473 if rev == wdirrev:
474 return b'%d+' % ctx.p1().rev()
474 return b'%d+' % ctx.p1().rev()
475 else:
475 else:
476 return b'%d ' % rev
476 return b'%d ' % rev
477
477
478 def formathex(h):
478 def formathex(h):
479 if h == repo.nodeconstants.wdirhex:
479 if h == repo.nodeconstants.wdirhex:
480 return b'%s+' % shorthex(hex(ctx.p1().node()))
480 return b'%s+' % shorthex(hex(ctx.p1().node()))
481 else:
481 else:
482 return b'%s ' % shorthex(h)
482 return b'%s ' % shorthex(h)
483
483
484 else:
484 else:
485 formatrev = b'%d'.__mod__
485 formatrev = b'%d'.__mod__
486 formathex = shorthex
486 formathex = shorthex
487
487
488 opmap = [
488 opmap = [
489 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
489 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
490 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
490 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
491 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
491 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
492 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
492 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
493 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
493 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
494 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
494 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
495 ]
495 ]
496 opnamemap = {
496 opnamemap = {
497 b'rev': b'number',
497 b'rev': b'number',
498 b'node': b'changeset',
498 b'node': b'changeset',
499 b'path': b'file',
499 b'path': b'file',
500 b'lineno': b'line_number',
500 b'lineno': b'line_number',
501 }
501 }
502
502
503 if rootfm.isplain():
503 if rootfm.isplain():
504
504
505 def makefunc(get, fmt):
505 def makefunc(get, fmt):
506 return lambda x: fmt(get(x))
506 return lambda x: fmt(get(x))
507
507
508 else:
508 else:
509
509
510 def makefunc(get, fmt):
510 def makefunc(get, fmt):
511 return get
511 return get
512
512
513 datahint = rootfm.datahint()
513 datahint = rootfm.datahint()
514 funcmap = [
514 funcmap = [
515 (makefunc(get, fmt), sep)
515 (makefunc(get, fmt), sep)
516 for fn, sep, get, fmt in opmap
516 for fn, sep, get, fmt in opmap
517 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
517 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
518 ]
518 ]
519 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
519 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
520 fields = b' '.join(
520 fields = b' '.join(
521 fn
521 fn
522 for fn, sep, get, fmt in opmap
522 for fn, sep, get, fmt in opmap
523 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
523 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
524 )
524 )
525
525
526 def bad(x, y):
526 def bad(x, y):
527 raise error.InputError(b"%s: %s" % (x, y))
527 raise error.InputError(b"%s: %s" % (x, y))
528
528
529 m = scmutil.match(ctx, pats, opts, badfn=bad)
529 m = scmutil.match(ctx, pats, opts, badfn=bad)
530
530
531 follow = not opts.get(b'no_follow')
531 follow = not opts.get(b'no_follow')
532 diffopts = patch.difffeatureopts(
532 diffopts = patch.difffeatureopts(
533 ui, opts, section=b'annotate', whitespace=True
533 ui, opts, section=b'annotate', whitespace=True
534 )
534 )
535 skiprevs = opts.get(b'skip')
535 skiprevs = opts.get(b'skip')
536 if skiprevs:
536 if skiprevs:
537 skiprevs = logcmdutil.revrange(repo, skiprevs)
537 skiprevs = logcmdutil.revrange(repo, skiprevs)
538
538
539 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
539 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
540 for abs in ctx.walk(m):
540 for abs in ctx.walk(m):
541 fctx = ctx[abs]
541 fctx = ctx[abs]
542 rootfm.startitem()
542 rootfm.startitem()
543 rootfm.data(path=abs)
543 rootfm.data(path=abs)
544 if not opts.get(b'text') and fctx.isbinary():
544 if not opts.get(b'text') and fctx.isbinary():
545 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
545 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
546 continue
546 continue
547
547
548 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
548 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
549 lines = fctx.annotate(
549 lines = fctx.annotate(
550 follow=follow, skiprevs=skiprevs, diffopts=diffopts
550 follow=follow, skiprevs=skiprevs, diffopts=diffopts
551 )
551 )
552 if not lines:
552 if not lines:
553 fm.end()
553 fm.end()
554 continue
554 continue
555 formats = []
555 formats = []
556 pieces = []
556 pieces = []
557
557
558 for f, sep in funcmap:
558 for f, sep in funcmap:
559 l = [f(n) for n in lines]
559 l = [f(n) for n in lines]
560 if fm.isplain():
560 if fm.isplain():
561 sizes = [encoding.colwidth(x) for x in l]
561 sizes = [encoding.colwidth(x) for x in l]
562 ml = max(sizes)
562 ml = max(sizes)
563 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
563 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
564 else:
564 else:
565 formats.append([b'%s'] * len(l))
565 formats.append([b'%s'] * len(l))
566 pieces.append(l)
566 pieces.append(l)
567
567
568 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
568 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
569 fm.startitem()
569 fm.startitem()
570 fm.context(fctx=n.fctx)
570 fm.context(fctx=n.fctx)
571 fm.write(fields, b"".join(f), *p)
571 fm.write(fields, b"".join(f), *p)
572 if n.skip:
572 if n.skip:
573 fmt = b"* %s"
573 fmt = b"* %s"
574 else:
574 else:
575 fmt = b": %s"
575 fmt = b": %s"
576 fm.write(b'line', fmt, n.text)
576 fm.write(b'line', fmt, n.text)
577
577
578 if not lines[-1].text.endswith(b'\n'):
578 if not lines[-1].text.endswith(b'\n'):
579 fm.plain(b'\n')
579 fm.plain(b'\n')
580 fm.end()
580 fm.end()
581
581
582 rootfm.end()
582 rootfm.end()
583
583
584
584
585 @command(
585 @command(
586 b'archive',
586 b'archive',
587 [
587 [
588 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
588 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
589 (
589 (
590 b'p',
590 b'p',
591 b'prefix',
591 b'prefix',
592 b'',
592 b'',
593 _(b'directory prefix for files in archive'),
593 _(b'directory prefix for files in archive'),
594 _(b'PREFIX'),
594 _(b'PREFIX'),
595 ),
595 ),
596 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
596 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
597 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
597 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
598 ]
598 ]
599 + subrepoopts
599 + subrepoopts
600 + walkopts,
600 + walkopts,
601 _(b'[OPTION]... DEST'),
601 _(b'[OPTION]... DEST'),
602 helpcategory=command.CATEGORY_IMPORT_EXPORT,
602 helpcategory=command.CATEGORY_IMPORT_EXPORT,
603 )
603 )
604 def archive(ui, repo, dest, **opts):
604 def archive(ui, repo, dest, **opts):
605 """create an unversioned archive of a repository revision
605 """create an unversioned archive of a repository revision
606
606
607 By default, the revision used is the parent of the working
607 By default, the revision used is the parent of the working
608 directory; use -r/--rev to specify a different revision.
608 directory; use -r/--rev to specify a different revision.
609
609
610 The archive type is automatically detected based on file
610 The archive type is automatically detected based on file
611 extension (to override, use -t/--type).
611 extension (to override, use -t/--type).
612
612
613 .. container:: verbose
613 .. container:: verbose
614
614
615 Examples:
615 Examples:
616
616
617 - create a zip file containing the 1.0 release::
617 - create a zip file containing the 1.0 release::
618
618
619 hg archive -r 1.0 project-1.0.zip
619 hg archive -r 1.0 project-1.0.zip
620
620
621 - create a tarball excluding .hg files::
621 - create a tarball excluding .hg files::
622
622
623 hg archive project.tar.gz -X ".hg*"
623 hg archive project.tar.gz -X ".hg*"
624
624
625 Valid types are:
625 Valid types are:
626
626
627 :``files``: a directory full of files (default)
627 :``files``: a directory full of files (default)
628 :``tar``: tar archive, uncompressed
628 :``tar``: tar archive, uncompressed
629 :``tbz2``: tar archive, compressed using bzip2
629 :``tbz2``: tar archive, compressed using bzip2
630 :``tgz``: tar archive, compressed using gzip
630 :``tgz``: tar archive, compressed using gzip
631 :``txz``: tar archive, compressed using lzma (only in Python 3)
631 :``txz``: tar archive, compressed using lzma (only in Python 3)
632 :``uzip``: zip archive, uncompressed
632 :``uzip``: zip archive, uncompressed
633 :``zip``: zip archive, compressed using deflate
633 :``zip``: zip archive, compressed using deflate
634
634
635 The exact name of the destination archive or directory is given
635 The exact name of the destination archive or directory is given
636 using a format string; see :hg:`help export` for details.
636 using a format string; see :hg:`help export` for details.
637
637
638 Each member added to an archive file has a directory prefix
638 Each member added to an archive file has a directory prefix
639 prepended. Use -p/--prefix to specify a format string for the
639 prepended. Use -p/--prefix to specify a format string for the
640 prefix. The default is the basename of the archive, with suffixes
640 prefix. The default is the basename of the archive, with suffixes
641 removed.
641 removed.
642
642
643 Returns 0 on success.
643 Returns 0 on success.
644 """
644 """
645
645
646 opts = pycompat.byteskwargs(opts)
646 opts = pycompat.byteskwargs(opts)
647 rev = opts.get(b'rev')
647 rev = opts.get(b'rev')
648 if rev:
648 if rev:
649 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
649 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
650 ctx = logcmdutil.revsingle(repo, rev)
650 ctx = logcmdutil.revsingle(repo, rev)
651 if not ctx:
651 if not ctx:
652 raise error.InputError(
652 raise error.InputError(
653 _(b'no working directory: please specify a revision')
653 _(b'no working directory: please specify a revision')
654 )
654 )
655 node = ctx.node()
655 node = ctx.node()
656 dest = cmdutil.makefilename(ctx, dest)
656 dest = cmdutil.makefilename(ctx, dest)
657 if os.path.realpath(dest) == repo.root:
657 if os.path.realpath(dest) == repo.root:
658 raise error.InputError(_(b'repository root cannot be destination'))
658 raise error.InputError(_(b'repository root cannot be destination'))
659
659
660 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
660 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
661 prefix = opts.get(b'prefix')
661 prefix = opts.get(b'prefix')
662
662
663 if dest == b'-':
663 if dest == b'-':
664 if kind == b'files':
664 if kind == b'files':
665 raise error.InputError(_(b'cannot archive plain files to stdout'))
665 raise error.InputError(_(b'cannot archive plain files to stdout'))
666 dest = cmdutil.makefileobj(ctx, dest)
666 dest = cmdutil.makefileobj(ctx, dest)
667 if not prefix:
667 if not prefix:
668 prefix = os.path.basename(repo.root) + b'-%h'
668 prefix = os.path.basename(repo.root) + b'-%h'
669
669
670 prefix = cmdutil.makefilename(ctx, prefix)
670 prefix = cmdutil.makefilename(ctx, prefix)
671 match = scmutil.match(ctx, [], opts)
671 match = scmutil.match(ctx, [], opts)
672 archival.archive(
672 archival.archive(
673 repo,
673 repo,
674 dest,
674 dest,
675 node,
675 node,
676 kind,
676 kind,
677 not opts.get(b'no_decode'),
677 not opts.get(b'no_decode'),
678 match,
678 match,
679 prefix,
679 prefix,
680 subrepos=opts.get(b'subrepos'),
680 subrepos=opts.get(b'subrepos'),
681 )
681 )
682
682
683
683
684 @command(
684 @command(
685 b'backout',
685 b'backout',
686 [
686 [
687 (
687 (
688 b'',
688 b'',
689 b'merge',
689 b'merge',
690 None,
690 None,
691 _(b'merge with old dirstate parent after backout'),
691 _(b'merge with old dirstate parent after backout'),
692 ),
692 ),
693 (
693 (
694 b'',
694 b'',
695 b'commit',
695 b'commit',
696 None,
696 None,
697 _(b'commit if no conflicts were encountered (DEPRECATED)'),
697 _(b'commit if no conflicts were encountered (DEPRECATED)'),
698 ),
698 ),
699 (b'', b'no-commit', None, _(b'do not commit')),
699 (b'', b'no-commit', None, _(b'do not commit')),
700 (
700 (
701 b'',
701 b'',
702 b'parent',
702 b'parent',
703 b'',
703 b'',
704 _(b'parent to choose when backing out merge (DEPRECATED)'),
704 _(b'parent to choose when backing out merge (DEPRECATED)'),
705 _(b'REV'),
705 _(b'REV'),
706 ),
706 ),
707 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
707 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
708 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
708 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
709 ]
709 ]
710 + mergetoolopts
710 + mergetoolopts
711 + walkopts
711 + walkopts
712 + commitopts
712 + commitopts
713 + commitopts2,
713 + commitopts2,
714 _(b'[OPTION]... [-r] REV'),
714 _(b'[OPTION]... [-r] REV'),
715 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
715 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
716 )
716 )
717 def backout(ui, repo, node=None, rev=None, **opts):
717 def backout(ui, repo, node=None, rev=None, **opts):
718 """reverse effect of earlier changeset
718 """reverse effect of earlier changeset
719
719
720 Prepare a new changeset with the effect of REV undone in the
720 Prepare a new changeset with the effect of REV undone in the
721 current working directory. If no conflicts were encountered,
721 current working directory. If no conflicts were encountered,
722 it will be committed immediately.
722 it will be committed immediately.
723
723
724 If REV is the parent of the working directory, then this new changeset
724 If REV is the parent of the working directory, then this new changeset
725 is committed automatically (unless --no-commit is specified).
725 is committed automatically (unless --no-commit is specified).
726
726
727 .. note::
727 .. note::
728
728
729 :hg:`backout` cannot be used to fix either an unwanted or
729 :hg:`backout` cannot be used to fix either an unwanted or
730 incorrect merge.
730 incorrect merge.
731
731
732 .. container:: verbose
732 .. container:: verbose
733
733
734 Examples:
734 Examples:
735
735
736 - Reverse the effect of the parent of the working directory.
736 - Reverse the effect of the parent of the working directory.
737 This backout will be committed immediately::
737 This backout will be committed immediately::
738
738
739 hg backout -r .
739 hg backout -r .
740
740
741 - Reverse the effect of previous bad revision 23::
741 - Reverse the effect of previous bad revision 23::
742
742
743 hg backout -r 23
743 hg backout -r 23
744
744
745 - Reverse the effect of previous bad revision 23 and
745 - Reverse the effect of previous bad revision 23 and
746 leave changes uncommitted::
746 leave changes uncommitted::
747
747
748 hg backout -r 23 --no-commit
748 hg backout -r 23 --no-commit
749 hg commit -m "Backout revision 23"
749 hg commit -m "Backout revision 23"
750
750
751 By default, the pending changeset will have one parent,
751 By default, the pending changeset will have one parent,
752 maintaining a linear history. With --merge, the pending
752 maintaining a linear history. With --merge, the pending
753 changeset will instead have two parents: the old parent of the
753 changeset will instead have two parents: the old parent of the
754 working directory and a new child of REV that simply undoes REV.
754 working directory and a new child of REV that simply undoes REV.
755
755
756 Before version 1.7, the behavior without --merge was equivalent
756 Before version 1.7, the behavior without --merge was equivalent
757 to specifying --merge followed by :hg:`update --clean .` to
757 to specifying --merge followed by :hg:`update --clean .` to
758 cancel the merge and leave the child of REV as a head to be
758 cancel the merge and leave the child of REV as a head to be
759 merged separately.
759 merged separately.
760
760
761 See :hg:`help dates` for a list of formats valid for -d/--date.
761 See :hg:`help dates` for a list of formats valid for -d/--date.
762
762
763 See :hg:`help revert` for a way to restore files to the state
763 See :hg:`help revert` for a way to restore files to the state
764 of another revision.
764 of another revision.
765
765
766 Returns 0 on success, 1 if nothing to backout or there are unresolved
766 Returns 0 on success, 1 if nothing to backout or there are unresolved
767 files.
767 files.
768 """
768 """
769 with repo.wlock(), repo.lock():
769 with repo.wlock(), repo.lock():
770 return _dobackout(ui, repo, node, rev, **opts)
770 return _dobackout(ui, repo, node, rev, **opts)
771
771
772
772
773 def _dobackout(ui, repo, node=None, rev=None, **opts):
773 def _dobackout(ui, repo, node=None, rev=None, **opts):
774 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
774 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
775 opts = pycompat.byteskwargs(opts)
775 opts = pycompat.byteskwargs(opts)
776
776
777 if rev and node:
777 if rev and node:
778 raise error.InputError(_(b"please specify just one revision"))
778 raise error.InputError(_(b"please specify just one revision"))
779
779
780 if not rev:
780 if not rev:
781 rev = node
781 rev = node
782
782
783 if not rev:
783 if not rev:
784 raise error.InputError(_(b"please specify a revision to backout"))
784 raise error.InputError(_(b"please specify a revision to backout"))
785
785
786 date = opts.get(b'date')
786 date = opts.get(b'date')
787 if date:
787 if date:
788 opts[b'date'] = dateutil.parsedate(date)
788 opts[b'date'] = dateutil.parsedate(date)
789
789
790 cmdutil.checkunfinished(repo)
790 cmdutil.checkunfinished(repo)
791 cmdutil.bailifchanged(repo)
791 cmdutil.bailifchanged(repo)
792 ctx = logcmdutil.revsingle(repo, rev)
792 ctx = logcmdutil.revsingle(repo, rev)
793 node = ctx.node()
793 node = ctx.node()
794
794
795 op1, op2 = repo.dirstate.parents()
795 op1, op2 = repo.dirstate.parents()
796 if not repo.changelog.isancestor(node, op1):
796 if not repo.changelog.isancestor(node, op1):
797 raise error.InputError(
797 raise error.InputError(
798 _(b'cannot backout change that is not an ancestor')
798 _(b'cannot backout change that is not an ancestor')
799 )
799 )
800
800
801 p1, p2 = repo.changelog.parents(node)
801 p1, p2 = repo.changelog.parents(node)
802 if p1 == repo.nullid:
802 if p1 == repo.nullid:
803 raise error.InputError(_(b'cannot backout a change with no parents'))
803 raise error.InputError(_(b'cannot backout a change with no parents'))
804 if p2 != repo.nullid:
804 if p2 != repo.nullid:
805 if not opts.get(b'parent'):
805 if not opts.get(b'parent'):
806 raise error.InputError(_(b'cannot backout a merge changeset'))
806 raise error.InputError(_(b'cannot backout a merge changeset'))
807 p = repo.lookup(opts[b'parent'])
807 p = repo.lookup(opts[b'parent'])
808 if p not in (p1, p2):
808 if p not in (p1, p2):
809 raise error.InputError(
809 raise error.InputError(
810 _(b'%s is not a parent of %s') % (short(p), short(node))
810 _(b'%s is not a parent of %s') % (short(p), short(node))
811 )
811 )
812 parent = p
812 parent = p
813 else:
813 else:
814 if opts.get(b'parent'):
814 if opts.get(b'parent'):
815 raise error.InputError(
815 raise error.InputError(
816 _(b'cannot use --parent on non-merge changeset')
816 _(b'cannot use --parent on non-merge changeset')
817 )
817 )
818 parent = p1
818 parent = p1
819
819
820 # the backout should appear on the same branch
820 # the backout should appear on the same branch
821 branch = repo.dirstate.branch()
821 branch = repo.dirstate.branch()
822 bheads = repo.branchheads(branch)
822 bheads = repo.branchheads(branch)
823 rctx = scmutil.revsingle(repo, hex(parent))
823 rctx = scmutil.revsingle(repo, hex(parent))
824 if not opts.get(b'merge') and op1 != node:
824 if not opts.get(b'merge') and op1 != node:
825 with dirstateguard.dirstateguard(repo, b'backout'):
825 with dirstateguard.dirstateguard(repo, b'backout'):
826 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
826 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
827 with ui.configoverride(overrides, b'backout'):
827 with ui.configoverride(overrides, b'backout'):
828 stats = mergemod.back_out(ctx, parent=repo[parent])
828 stats = mergemod.back_out(ctx, parent=repo[parent])
829 repo.setparents(op1, op2)
829 repo.setparents(op1, op2)
830 hg._showstats(repo, stats)
830 hg._showstats(repo, stats)
831 if stats.unresolvedcount:
831 if stats.unresolvedcount:
832 repo.ui.status(
832 repo.ui.status(
833 _(b"use 'hg resolve' to retry unresolved file merges\n")
833 _(b"use 'hg resolve' to retry unresolved file merges\n")
834 )
834 )
835 return 1
835 return 1
836 else:
836 else:
837 hg.clean(repo, node, show_stats=False)
837 hg.clean(repo, node, show_stats=False)
838 repo.dirstate.setbranch(branch)
838 repo.dirstate.setbranch(branch)
839 cmdutil.revert(ui, repo, rctx)
839 cmdutil.revert(ui, repo, rctx)
840
840
841 if opts.get(b'no_commit'):
841 if opts.get(b'no_commit'):
842 msg = _(b"changeset %s backed out, don't forget to commit.\n")
842 msg = _(b"changeset %s backed out, don't forget to commit.\n")
843 ui.status(msg % short(node))
843 ui.status(msg % short(node))
844 return 0
844 return 0
845
845
846 def commitfunc(ui, repo, message, match, opts):
846 def commitfunc(ui, repo, message, match, opts):
847 editform = b'backout'
847 editform = b'backout'
848 e = cmdutil.getcommiteditor(
848 e = cmdutil.getcommiteditor(
849 editform=editform, **pycompat.strkwargs(opts)
849 editform=editform, **pycompat.strkwargs(opts)
850 )
850 )
851 if not message:
851 if not message:
852 # we don't translate commit messages
852 # we don't translate commit messages
853 message = b"Backed out changeset %s" % short(node)
853 message = b"Backed out changeset %s" % short(node)
854 e = cmdutil.getcommiteditor(edit=True, editform=editform)
854 e = cmdutil.getcommiteditor(edit=True, editform=editform)
855 return repo.commit(
855 return repo.commit(
856 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
856 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
857 )
857 )
858
858
859 # save to detect changes
859 # save to detect changes
860 tip = repo.changelog.tip()
860 tip = repo.changelog.tip()
861
861
862 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
862 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
863 if not newnode:
863 if not newnode:
864 ui.status(_(b"nothing changed\n"))
864 ui.status(_(b"nothing changed\n"))
865 return 1
865 return 1
866 cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
866 cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
867
867
868 def nice(node):
868 def nice(node):
869 return b'%d:%s' % (repo.changelog.rev(node), short(node))
869 return b'%d:%s' % (repo.changelog.rev(node), short(node))
870
870
871 ui.status(
871 ui.status(
872 _(b'changeset %s backs out changeset %s\n')
872 _(b'changeset %s backs out changeset %s\n')
873 % (nice(newnode), nice(node))
873 % (nice(newnode), nice(node))
874 )
874 )
875 if opts.get(b'merge') and op1 != node:
875 if opts.get(b'merge') and op1 != node:
876 hg.clean(repo, op1, show_stats=False)
876 hg.clean(repo, op1, show_stats=False)
877 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
877 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
878 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
878 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
879 with ui.configoverride(overrides, b'backout'):
879 with ui.configoverride(overrides, b'backout'):
880 return hg.merge(repo[b'tip'])
880 return hg.merge(repo[b'tip'])
881 return 0
881 return 0
882
882
883
883
884 @command(
884 @command(
885 b'bisect',
885 b'bisect',
886 [
886 [
887 (b'r', b'reset', False, _(b'reset bisect state')),
887 (b'r', b'reset', False, _(b'reset bisect state')),
888 (b'g', b'good', False, _(b'mark changeset good')),
888 (b'g', b'good', False, _(b'mark changeset good')),
889 (b'b', b'bad', False, _(b'mark changeset bad')),
889 (b'b', b'bad', False, _(b'mark changeset bad')),
890 (b's', b'skip', False, _(b'skip testing changeset')),
890 (b's', b'skip', False, _(b'skip testing changeset')),
891 (b'e', b'extend', False, _(b'extend the bisect range')),
891 (b'e', b'extend', False, _(b'extend the bisect range')),
892 (
892 (
893 b'c',
893 b'c',
894 b'command',
894 b'command',
895 b'',
895 b'',
896 _(b'use command to check changeset state'),
896 _(b'use command to check changeset state'),
897 _(b'CMD'),
897 _(b'CMD'),
898 ),
898 ),
899 (b'U', b'noupdate', False, _(b'do not update to target')),
899 (b'U', b'noupdate', False, _(b'do not update to target')),
900 ],
900 ],
901 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
901 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
902 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
902 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
903 )
903 )
904 def bisect(
904 def bisect(
905 ui,
905 ui,
906 repo,
906 repo,
907 positional_1=None,
907 positional_1=None,
908 positional_2=None,
908 positional_2=None,
909 command=None,
909 command=None,
910 reset=None,
910 reset=None,
911 good=None,
911 good=None,
912 bad=None,
912 bad=None,
913 skip=None,
913 skip=None,
914 extend=None,
914 extend=None,
915 noupdate=None,
915 noupdate=None,
916 ):
916 ):
917 """subdivision search of changesets
917 """subdivision search of changesets
918
918
919 This command helps to find changesets which introduce problems. To
919 This command helps to find changesets which introduce problems. To
920 use, mark the earliest changeset you know exhibits the problem as
920 use, mark the earliest changeset you know exhibits the problem as
921 bad, then mark the latest changeset which is free from the problem
921 bad, then mark the latest changeset which is free from the problem
922 as good. Bisect will update your working directory to a revision
922 as good. Bisect will update your working directory to a revision
923 for testing (unless the -U/--noupdate option is specified). Once
923 for testing (unless the -U/--noupdate option is specified). Once
924 you have performed tests, mark the working directory as good or
924 you have performed tests, mark the working directory as good or
925 bad, and bisect will either update to another candidate changeset
925 bad, and bisect will either update to another candidate changeset
926 or announce that it has found the bad revision.
926 or announce that it has found the bad revision.
927
927
928 As a shortcut, you can also use the revision argument to mark a
928 As a shortcut, you can also use the revision argument to mark a
929 revision as good or bad without checking it out first.
929 revision as good or bad without checking it out first.
930
930
931 If you supply a command, it will be used for automatic bisection.
931 If you supply a command, it will be used for automatic bisection.
932 The environment variable HG_NODE will contain the ID of the
932 The environment variable HG_NODE will contain the ID of the
933 changeset being tested. The exit status of the command will be
933 changeset being tested. The exit status of the command will be
934 used to mark revisions as good or bad: status 0 means good, 125
934 used to mark revisions as good or bad: status 0 means good, 125
935 means to skip the revision, 127 (command not found) will abort the
935 means to skip the revision, 127 (command not found) will abort the
936 bisection, and any other non-zero exit status means the revision
936 bisection, and any other non-zero exit status means the revision
937 is bad.
937 is bad.
938
938
939 .. container:: verbose
939 .. container:: verbose
940
940
941 Some examples:
941 Some examples:
942
942
943 - start a bisection with known bad revision 34, and good revision 12::
943 - start a bisection with known bad revision 34, and good revision 12::
944
944
945 hg bisect --bad 34
945 hg bisect --bad 34
946 hg bisect --good 12
946 hg bisect --good 12
947
947
948 - advance the current bisection by marking current revision as good or
948 - advance the current bisection by marking current revision as good or
949 bad::
949 bad::
950
950
951 hg bisect --good
951 hg bisect --good
952 hg bisect --bad
952 hg bisect --bad
953
953
954 - mark the current revision, or a known revision, to be skipped (e.g. if
954 - mark the current revision, or a known revision, to be skipped (e.g. if
955 that revision is not usable because of another issue)::
955 that revision is not usable because of another issue)::
956
956
957 hg bisect --skip
957 hg bisect --skip
958 hg bisect --skip 23
958 hg bisect --skip 23
959
959
960 - skip all revisions that do not touch directories ``foo`` or ``bar``::
960 - skip all revisions that do not touch directories ``foo`` or ``bar``::
961
961
962 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
962 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
963
963
964 - forget the current bisection::
964 - forget the current bisection::
965
965
966 hg bisect --reset
966 hg bisect --reset
967
967
968 - use 'make && make tests' to automatically find the first broken
968 - use 'make && make tests' to automatically find the first broken
969 revision::
969 revision::
970
970
971 hg bisect --reset
971 hg bisect --reset
972 hg bisect --bad 34
972 hg bisect --bad 34
973 hg bisect --good 12
973 hg bisect --good 12
974 hg bisect --command "make && make tests"
974 hg bisect --command "make && make tests"
975
975
976 - see all changesets whose states are already known in the current
976 - see all changesets whose states are already known in the current
977 bisection::
977 bisection::
978
978
979 hg log -r "bisect(pruned)"
979 hg log -r "bisect(pruned)"
980
980
981 - see the changeset currently being bisected (especially useful
981 - see the changeset currently being bisected (especially useful
982 if running with -U/--noupdate)::
982 if running with -U/--noupdate)::
983
983
984 hg log -r "bisect(current)"
984 hg log -r "bisect(current)"
985
985
986 - see all changesets that took part in the current bisection::
986 - see all changesets that took part in the current bisection::
987
987
988 hg log -r "bisect(range)"
988 hg log -r "bisect(range)"
989
989
990 - you can even get a nice graph::
990 - you can even get a nice graph::
991
991
992 hg log --graph -r "bisect(range)"
992 hg log --graph -r "bisect(range)"
993
993
994 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
994 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
995
995
996 Returns 0 on success.
996 Returns 0 on success.
997 """
997 """
998 rev = []
998 rev = []
999 # backward compatibility
999 # backward compatibility
1000 if positional_1 in (b"good", b"bad", b"reset", b"init"):
1000 if positional_1 in (b"good", b"bad", b"reset", b"init"):
1001 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1001 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1002 cmd = positional_1
1002 cmd = positional_1
1003 rev.append(positional_2)
1003 rev.append(positional_2)
1004 if cmd == b"good":
1004 if cmd == b"good":
1005 good = True
1005 good = True
1006 elif cmd == b"bad":
1006 elif cmd == b"bad":
1007 bad = True
1007 bad = True
1008 else:
1008 else:
1009 reset = True
1009 reset = True
1010 elif positional_2:
1010 elif positional_2:
1011 raise error.InputError(_(b'incompatible arguments'))
1011 raise error.InputError(_(b'incompatible arguments'))
1012 elif positional_1 is not None:
1012 elif positional_1 is not None:
1013 rev.append(positional_1)
1013 rev.append(positional_1)
1014
1014
1015 incompatibles = {
1015 incompatibles = {
1016 b'--bad': bad,
1016 b'--bad': bad,
1017 b'--command': bool(command),
1017 b'--command': bool(command),
1018 b'--extend': extend,
1018 b'--extend': extend,
1019 b'--good': good,
1019 b'--good': good,
1020 b'--reset': reset,
1020 b'--reset': reset,
1021 b'--skip': skip,
1021 b'--skip': skip,
1022 }
1022 }
1023
1023
1024 enabled = [x for x in incompatibles if incompatibles[x]]
1024 enabled = [x for x in incompatibles if incompatibles[x]]
1025
1025
1026 if len(enabled) > 1:
1026 if len(enabled) > 1:
1027 raise error.InputError(
1027 raise error.InputError(
1028 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1028 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1029 )
1029 )
1030
1030
1031 if reset:
1031 if reset:
1032 hbisect.resetstate(repo)
1032 hbisect.resetstate(repo)
1033 return
1033 return
1034
1034
1035 state = hbisect.load_state(repo)
1035 state = hbisect.load_state(repo)
1036
1036
1037 if rev:
1037 if rev:
1038 nodes = [repo[i].node() for i in logcmdutil.revrange(repo, rev)]
1038 nodes = [repo[i].node() for i in logcmdutil.revrange(repo, rev)]
1039 else:
1039 else:
1040 nodes = [repo.lookup(b'.')]
1040 nodes = [repo.lookup(b'.')]
1041
1041
1042 # update state
1042 # update state
1043 if good or bad or skip:
1043 if good or bad or skip:
1044 if good:
1044 if good:
1045 state[b'good'] += nodes
1045 state[b'good'] += nodes
1046 elif bad:
1046 elif bad:
1047 state[b'bad'] += nodes
1047 state[b'bad'] += nodes
1048 elif skip:
1048 elif skip:
1049 state[b'skip'] += nodes
1049 state[b'skip'] += nodes
1050 hbisect.save_state(repo, state)
1050 hbisect.save_state(repo, state)
1051 if not (state[b'good'] and state[b'bad']):
1051 if not (state[b'good'] and state[b'bad']):
1052 return
1052 return
1053
1053
1054 def mayupdate(repo, node, show_stats=True):
1054 def mayupdate(repo, node, show_stats=True):
1055 """common used update sequence"""
1055 """common used update sequence"""
1056 if noupdate:
1056 if noupdate:
1057 return
1057 return
1058 cmdutil.checkunfinished(repo)
1058 cmdutil.checkunfinished(repo)
1059 cmdutil.bailifchanged(repo)
1059 cmdutil.bailifchanged(repo)
1060 return hg.clean(repo, node, show_stats=show_stats)
1060 return hg.clean(repo, node, show_stats=show_stats)
1061
1061
1062 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1062 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1063
1063
1064 if command:
1064 if command:
1065 changesets = 1
1065 changesets = 1
1066 if noupdate:
1066 if noupdate:
1067 try:
1067 try:
1068 node = state[b'current'][0]
1068 node = state[b'current'][0]
1069 except LookupError:
1069 except LookupError:
1070 raise error.StateError(
1070 raise error.StateError(
1071 _(
1071 _(
1072 b'current bisect revision is unknown - '
1072 b'current bisect revision is unknown - '
1073 b'start a new bisect to fix'
1073 b'start a new bisect to fix'
1074 )
1074 )
1075 )
1075 )
1076 else:
1076 else:
1077 node, p2 = repo.dirstate.parents()
1077 node, p2 = repo.dirstate.parents()
1078 if p2 != repo.nullid:
1078 if p2 != repo.nullid:
1079 raise error.StateError(_(b'current bisect revision is a merge'))
1079 raise error.StateError(_(b'current bisect revision is a merge'))
1080 if rev:
1080 if rev:
1081 if not nodes:
1081 if not nodes:
1082 raise error.InputError(_(b'empty revision set'))
1082 raise error.InputError(_(b'empty revision set'))
1083 node = repo[nodes[-1]].node()
1083 node = repo[nodes[-1]].node()
1084 with hbisect.restore_state(repo, state, node):
1084 with hbisect.restore_state(repo, state, node):
1085 while changesets:
1085 while changesets:
1086 # update state
1086 # update state
1087 state[b'current'] = [node]
1087 state[b'current'] = [node]
1088 hbisect.save_state(repo, state)
1088 hbisect.save_state(repo, state)
1089 status = ui.system(
1089 status = ui.system(
1090 command,
1090 command,
1091 environ={b'HG_NODE': hex(node)},
1091 environ={b'HG_NODE': hex(node)},
1092 blockedtag=b'bisect_check',
1092 blockedtag=b'bisect_check',
1093 )
1093 )
1094 if status == 125:
1094 if status == 125:
1095 transition = b"skip"
1095 transition = b"skip"
1096 elif status == 0:
1096 elif status == 0:
1097 transition = b"good"
1097 transition = b"good"
1098 # status < 0 means process was killed
1098 # status < 0 means process was killed
1099 elif status == 127:
1099 elif status == 127:
1100 raise error.Abort(_(b"failed to execute %s") % command)
1100 raise error.Abort(_(b"failed to execute %s") % command)
1101 elif status < 0:
1101 elif status < 0:
1102 raise error.Abort(_(b"%s killed") % command)
1102 raise error.Abort(_(b"%s killed") % command)
1103 else:
1103 else:
1104 transition = b"bad"
1104 transition = b"bad"
1105 state[transition].append(node)
1105 state[transition].append(node)
1106 ctx = repo[node]
1106 ctx = repo[node]
1107 summary = cmdutil.format_changeset_summary(ui, ctx, b'bisect')
1107 summary = cmdutil.format_changeset_summary(ui, ctx, b'bisect')
1108 ui.status(_(b'changeset %s: %s\n') % (summary, transition))
1108 ui.status(_(b'changeset %s: %s\n') % (summary, transition))
1109 hbisect.checkstate(state)
1109 hbisect.checkstate(state)
1110 # bisect
1110 # bisect
1111 nodes, changesets, bgood = hbisect.bisect(repo, state)
1111 nodes, changesets, bgood = hbisect.bisect(repo, state)
1112 # update to next check
1112 # update to next check
1113 node = nodes[0]
1113 node = nodes[0]
1114 mayupdate(repo, node, show_stats=False)
1114 mayupdate(repo, node, show_stats=False)
1115 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1115 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1116 return
1116 return
1117
1117
1118 hbisect.checkstate(state)
1118 hbisect.checkstate(state)
1119
1119
1120 # actually bisect
1120 # actually bisect
1121 nodes, changesets, good = hbisect.bisect(repo, state)
1121 nodes, changesets, good = hbisect.bisect(repo, state)
1122 if extend:
1122 if extend:
1123 if not changesets:
1123 if not changesets:
1124 extendctx = hbisect.extendrange(repo, state, nodes, good)
1124 extendctx = hbisect.extendrange(repo, state, nodes, good)
1125 if extendctx is not None:
1125 if extendctx is not None:
1126 ui.write(
1126 ui.write(
1127 _(b"Extending search to changeset %s\n")
1127 _(b"Extending search to changeset %s\n")
1128 % cmdutil.format_changeset_summary(ui, extendctx, b'bisect')
1128 % cmdutil.format_changeset_summary(ui, extendctx, b'bisect')
1129 )
1129 )
1130 state[b'current'] = [extendctx.node()]
1130 state[b'current'] = [extendctx.node()]
1131 hbisect.save_state(repo, state)
1131 hbisect.save_state(repo, state)
1132 return mayupdate(repo, extendctx.node())
1132 return mayupdate(repo, extendctx.node())
1133 raise error.StateError(_(b"nothing to extend"))
1133 raise error.StateError(_(b"nothing to extend"))
1134
1134
1135 if changesets == 0:
1135 if changesets == 0:
1136 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1136 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1137 else:
1137 else:
1138 assert len(nodes) == 1 # only a single node can be tested next
1138 assert len(nodes) == 1 # only a single node can be tested next
1139 node = nodes[0]
1139 node = nodes[0]
1140 # compute the approximate number of remaining tests
1140 # compute the approximate number of remaining tests
1141 tests, size = 0, 2
1141 tests, size = 0, 2
1142 while size <= changesets:
1142 while size <= changesets:
1143 tests, size = tests + 1, size * 2
1143 tests, size = tests + 1, size * 2
1144 rev = repo.changelog.rev(node)
1144 rev = repo.changelog.rev(node)
1145 summary = cmdutil.format_changeset_summary(ui, repo[rev], b'bisect')
1145 summary = cmdutil.format_changeset_summary(ui, repo[rev], b'bisect')
1146 ui.write(
1146 ui.write(
1147 _(
1147 _(
1148 b"Testing changeset %s "
1148 b"Testing changeset %s "
1149 b"(%d changesets remaining, ~%d tests)\n"
1149 b"(%d changesets remaining, ~%d tests)\n"
1150 )
1150 )
1151 % (summary, changesets, tests)
1151 % (summary, changesets, tests)
1152 )
1152 )
1153 state[b'current'] = [node]
1153 state[b'current'] = [node]
1154 hbisect.save_state(repo, state)
1154 hbisect.save_state(repo, state)
1155 return mayupdate(repo, node)
1155 return mayupdate(repo, node)
1156
1156
1157
1157
1158 @command(
1158 @command(
1159 b'bookmarks|bookmark',
1159 b'bookmarks|bookmark',
1160 [
1160 [
1161 (b'f', b'force', False, _(b'force')),
1161 (b'f', b'force', False, _(b'force')),
1162 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1162 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1163 (b'd', b'delete', False, _(b'delete a given bookmark')),
1163 (b'd', b'delete', False, _(b'delete a given bookmark')),
1164 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1164 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1165 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1165 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1166 (b'l', b'list', False, _(b'list existing bookmarks')),
1166 (b'l', b'list', False, _(b'list existing bookmarks')),
1167 ]
1167 ]
1168 + formatteropts,
1168 + formatteropts,
1169 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1169 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1170 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1170 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1171 )
1171 )
1172 def bookmark(ui, repo, *names, **opts):
1172 def bookmark(ui, repo, *names, **opts):
1173 """create a new bookmark or list existing bookmarks
1173 """create a new bookmark or list existing bookmarks
1174
1174
1175 Bookmarks are labels on changesets to help track lines of development.
1175 Bookmarks are labels on changesets to help track lines of development.
1176 Bookmarks are unversioned and can be moved, renamed and deleted.
1176 Bookmarks are unversioned and can be moved, renamed and deleted.
1177 Deleting or moving a bookmark has no effect on the associated changesets.
1177 Deleting or moving a bookmark has no effect on the associated changesets.
1178
1178
1179 Creating or updating to a bookmark causes it to be marked as 'active'.
1179 Creating or updating to a bookmark causes it to be marked as 'active'.
1180 The active bookmark is indicated with a '*'.
1180 The active bookmark is indicated with a '*'.
1181 When a commit is made, the active bookmark will advance to the new commit.
1181 When a commit is made, the active bookmark will advance to the new commit.
1182 A plain :hg:`update` will also advance an active bookmark, if possible.
1182 A plain :hg:`update` will also advance an active bookmark, if possible.
1183 Updating away from a bookmark will cause it to be deactivated.
1183 Updating away from a bookmark will cause it to be deactivated.
1184
1184
1185 Bookmarks can be pushed and pulled between repositories (see
1185 Bookmarks can be pushed and pulled between repositories (see
1186 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1186 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1187 diverged, a new 'divergent bookmark' of the form 'name@path' will
1187 diverged, a new 'divergent bookmark' of the form 'name@path' will
1188 be created. Using :hg:`merge` will resolve the divergence.
1188 be created. Using :hg:`merge` will resolve the divergence.
1189
1189
1190 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1190 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1191 the active bookmark's name.
1191 the active bookmark's name.
1192
1192
1193 A bookmark named '@' has the special property that :hg:`clone` will
1193 A bookmark named '@' has the special property that :hg:`clone` will
1194 check it out by default if it exists.
1194 check it out by default if it exists.
1195
1195
1196 .. container:: verbose
1196 .. container:: verbose
1197
1197
1198 Template:
1198 Template:
1199
1199
1200 The following keywords are supported in addition to the common template
1200 The following keywords are supported in addition to the common template
1201 keywords and functions such as ``{bookmark}``. See also
1201 keywords and functions such as ``{bookmark}``. See also
1202 :hg:`help templates`.
1202 :hg:`help templates`.
1203
1203
1204 :active: Boolean. True if the bookmark is active.
1204 :active: Boolean. True if the bookmark is active.
1205
1205
1206 Examples:
1206 Examples:
1207
1207
1208 - create an active bookmark for a new line of development::
1208 - create an active bookmark for a new line of development::
1209
1209
1210 hg book new-feature
1210 hg book new-feature
1211
1211
1212 - create an inactive bookmark as a place marker::
1212 - create an inactive bookmark as a place marker::
1213
1213
1214 hg book -i reviewed
1214 hg book -i reviewed
1215
1215
1216 - create an inactive bookmark on another changeset::
1216 - create an inactive bookmark on another changeset::
1217
1217
1218 hg book -r .^ tested
1218 hg book -r .^ tested
1219
1219
1220 - rename bookmark turkey to dinner::
1220 - rename bookmark turkey to dinner::
1221
1221
1222 hg book -m turkey dinner
1222 hg book -m turkey dinner
1223
1223
1224 - move the '@' bookmark from another branch::
1224 - move the '@' bookmark from another branch::
1225
1225
1226 hg book -f @
1226 hg book -f @
1227
1227
1228 - print only the active bookmark name::
1228 - print only the active bookmark name::
1229
1229
1230 hg book -ql .
1230 hg book -ql .
1231 """
1231 """
1232 opts = pycompat.byteskwargs(opts)
1232 opts = pycompat.byteskwargs(opts)
1233 force = opts.get(b'force')
1233 force = opts.get(b'force')
1234 rev = opts.get(b'rev')
1234 rev = opts.get(b'rev')
1235 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1235 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1236
1236
1237 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1237 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1238 if action:
1238 if action:
1239 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1239 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1240 elif names or rev:
1240 elif names or rev:
1241 action = b'add'
1241 action = b'add'
1242 elif inactive:
1242 elif inactive:
1243 action = b'inactive' # meaning deactivate
1243 action = b'inactive' # meaning deactivate
1244 else:
1244 else:
1245 action = b'list'
1245 action = b'list'
1246
1246
1247 cmdutil.check_incompatible_arguments(
1247 cmdutil.check_incompatible_arguments(
1248 opts, b'inactive', [b'delete', b'list']
1248 opts, b'inactive', [b'delete', b'list']
1249 )
1249 )
1250 if not names and action in {b'add', b'delete'}:
1250 if not names and action in {b'add', b'delete'}:
1251 raise error.InputError(_(b"bookmark name required"))
1251 raise error.InputError(_(b"bookmark name required"))
1252
1252
1253 if action in {b'add', b'delete', b'rename', b'inactive'}:
1253 if action in {b'add', b'delete', b'rename', b'inactive'}:
1254 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1254 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1255 if action == b'delete':
1255 if action == b'delete':
1256 names = pycompat.maplist(repo._bookmarks.expandname, names)
1256 names = pycompat.maplist(repo._bookmarks.expandname, names)
1257 bookmarks.delete(repo, tr, names)
1257 bookmarks.delete(repo, tr, names)
1258 elif action == b'rename':
1258 elif action == b'rename':
1259 if not names:
1259 if not names:
1260 raise error.InputError(_(b"new bookmark name required"))
1260 raise error.InputError(_(b"new bookmark name required"))
1261 elif len(names) > 1:
1261 elif len(names) > 1:
1262 raise error.InputError(
1262 raise error.InputError(
1263 _(b"only one new bookmark name allowed")
1263 _(b"only one new bookmark name allowed")
1264 )
1264 )
1265 oldname = repo._bookmarks.expandname(opts[b'rename'])
1265 oldname = repo._bookmarks.expandname(opts[b'rename'])
1266 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1266 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1267 elif action == b'add':
1267 elif action == b'add':
1268 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1268 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1269 elif action == b'inactive':
1269 elif action == b'inactive':
1270 if len(repo._bookmarks) == 0:
1270 if len(repo._bookmarks) == 0:
1271 ui.status(_(b"no bookmarks set\n"))
1271 ui.status(_(b"no bookmarks set\n"))
1272 elif not repo._activebookmark:
1272 elif not repo._activebookmark:
1273 ui.status(_(b"no active bookmark\n"))
1273 ui.status(_(b"no active bookmark\n"))
1274 else:
1274 else:
1275 bookmarks.deactivate(repo)
1275 bookmarks.deactivate(repo)
1276 elif action == b'list':
1276 elif action == b'list':
1277 names = pycompat.maplist(repo._bookmarks.expandname, names)
1277 names = pycompat.maplist(repo._bookmarks.expandname, names)
1278 with ui.formatter(b'bookmarks', opts) as fm:
1278 with ui.formatter(b'bookmarks', opts) as fm:
1279 bookmarks.printbookmarks(ui, repo, fm, names)
1279 bookmarks.printbookmarks(ui, repo, fm, names)
1280 else:
1280 else:
1281 raise error.ProgrammingError(b'invalid action: %s' % action)
1281 raise error.ProgrammingError(b'invalid action: %s' % action)
1282
1282
1283
1283
1284 @command(
1284 @command(
1285 b'branch',
1285 b'branch',
1286 [
1286 [
1287 (
1287 (
1288 b'f',
1288 b'f',
1289 b'force',
1289 b'force',
1290 None,
1290 None,
1291 _(b'set branch name even if it shadows an existing branch'),
1291 _(b'set branch name even if it shadows an existing branch'),
1292 ),
1292 ),
1293 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1293 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1294 (
1294 (
1295 b'r',
1295 b'r',
1296 b'rev',
1296 b'rev',
1297 [],
1297 [],
1298 _(b'change branches of the given revs (EXPERIMENTAL)'),
1298 _(b'change branches of the given revs (EXPERIMENTAL)'),
1299 ),
1299 ),
1300 ],
1300 ],
1301 _(b'[-fC] [NAME]'),
1301 _(b'[-fC] [NAME]'),
1302 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1302 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1303 )
1303 )
1304 def branch(ui, repo, label=None, **opts):
1304 def branch(ui, repo, label=None, **opts):
1305 """set or show the current branch name
1305 """set or show the current branch name
1306
1306
1307 .. note::
1307 .. note::
1308
1308
1309 Branch names are permanent and global. Use :hg:`bookmark` to create a
1309 Branch names are permanent and global. Use :hg:`bookmark` to create a
1310 light-weight bookmark instead. See :hg:`help glossary` for more
1310 light-weight bookmark instead. See :hg:`help glossary` for more
1311 information about named branches and bookmarks.
1311 information about named branches and bookmarks.
1312
1312
1313 With no argument, show the current branch name. With one argument,
1313 With no argument, show the current branch name. With one argument,
1314 set the working directory branch name (the branch will not exist
1314 set the working directory branch name (the branch will not exist
1315 in the repository until the next commit). Standard practice
1315 in the repository until the next commit). Standard practice
1316 recommends that primary development take place on the 'default'
1316 recommends that primary development take place on the 'default'
1317 branch.
1317 branch.
1318
1318
1319 Unless -f/--force is specified, branch will not let you set a
1319 Unless -f/--force is specified, branch will not let you set a
1320 branch name that already exists.
1320 branch name that already exists.
1321
1321
1322 Use -C/--clean to reset the working directory branch to that of
1322 Use -C/--clean to reset the working directory branch to that of
1323 the parent of the working directory, negating a previous branch
1323 the parent of the working directory, negating a previous branch
1324 change.
1324 change.
1325
1325
1326 Use the command :hg:`update` to switch to an existing branch. Use
1326 Use the command :hg:`update` to switch to an existing branch. Use
1327 :hg:`commit --close-branch` to mark this branch head as closed.
1327 :hg:`commit --close-branch` to mark this branch head as closed.
1328 When all heads of a branch are closed, the branch will be
1328 When all heads of a branch are closed, the branch will be
1329 considered closed.
1329 considered closed.
1330
1330
1331 Returns 0 on success.
1331 Returns 0 on success.
1332 """
1332 """
1333 opts = pycompat.byteskwargs(opts)
1333 opts = pycompat.byteskwargs(opts)
1334 revs = opts.get(b'rev')
1334 revs = opts.get(b'rev')
1335 if label:
1335 if label:
1336 label = label.strip()
1336 label = label.strip()
1337
1337
1338 if not opts.get(b'clean') and not label:
1338 if not opts.get(b'clean') and not label:
1339 if revs:
1339 if revs:
1340 raise error.InputError(
1340 raise error.InputError(
1341 _(b"no branch name specified for the revisions")
1341 _(b"no branch name specified for the revisions")
1342 )
1342 )
1343 ui.write(b"%s\n" % repo.dirstate.branch())
1343 ui.write(b"%s\n" % repo.dirstate.branch())
1344 return
1344 return
1345
1345
1346 with repo.wlock():
1346 with repo.wlock():
1347 if opts.get(b'clean'):
1347 if opts.get(b'clean'):
1348 label = repo[b'.'].branch()
1348 label = repo[b'.'].branch()
1349 repo.dirstate.setbranch(label)
1349 repo.dirstate.setbranch(label)
1350 ui.status(_(b'reset working directory to branch %s\n') % label)
1350 ui.status(_(b'reset working directory to branch %s\n') % label)
1351 elif label:
1351 elif label:
1352
1352
1353 scmutil.checknewlabel(repo, label, b'branch')
1353 scmutil.checknewlabel(repo, label, b'branch')
1354 if revs:
1354 if revs:
1355 return cmdutil.changebranch(ui, repo, revs, label, opts)
1355 return cmdutil.changebranch(ui, repo, revs, label, opts)
1356
1356
1357 if not opts.get(b'force') and label in repo.branchmap():
1357 if not opts.get(b'force') and label in repo.branchmap():
1358 if label not in [p.branch() for p in repo[None].parents()]:
1358 if label not in [p.branch() for p in repo[None].parents()]:
1359 raise error.InputError(
1359 raise error.InputError(
1360 _(b'a branch of the same name already exists'),
1360 _(b'a branch of the same name already exists'),
1361 # i18n: "it" refers to an existing branch
1361 # i18n: "it" refers to an existing branch
1362 hint=_(b"use 'hg update' to switch to it"),
1362 hint=_(b"use 'hg update' to switch to it"),
1363 )
1363 )
1364
1364
1365 repo.dirstate.setbranch(label)
1365 repo.dirstate.setbranch(label)
1366 ui.status(_(b'marked working directory as branch %s\n') % label)
1366 ui.status(_(b'marked working directory as branch %s\n') % label)
1367
1367
1368 # find any open named branches aside from default
1368 # find any open named branches aside from default
1369 for n, h, t, c in repo.branchmap().iterbranches():
1369 for n, h, t, c in repo.branchmap().iterbranches():
1370 if n != b"default" and not c:
1370 if n != b"default" and not c:
1371 return 0
1371 return 0
1372 ui.status(
1372 ui.status(
1373 _(
1373 _(
1374 b'(branches are permanent and global, '
1374 b'(branches are permanent and global, '
1375 b'did you want a bookmark?)\n'
1375 b'did you want a bookmark?)\n'
1376 )
1376 )
1377 )
1377 )
1378
1378
1379
1379
1380 @command(
1380 @command(
1381 b'branches',
1381 b'branches',
1382 [
1382 [
1383 (
1383 (
1384 b'a',
1384 b'a',
1385 b'active',
1385 b'active',
1386 False,
1386 False,
1387 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1387 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1388 ),
1388 ),
1389 (b'c', b'closed', False, _(b'show normal and closed branches')),
1389 (b'c', b'closed', False, _(b'show normal and closed branches')),
1390 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1390 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1391 ]
1391 ]
1392 + formatteropts,
1392 + formatteropts,
1393 _(b'[-c]'),
1393 _(b'[-c]'),
1394 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1394 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1395 intents={INTENT_READONLY},
1395 intents={INTENT_READONLY},
1396 )
1396 )
1397 def branches(ui, repo, active=False, closed=False, **opts):
1397 def branches(ui, repo, active=False, closed=False, **opts):
1398 """list repository named branches
1398 """list repository named branches
1399
1399
1400 List the repository's named branches, indicating which ones are
1400 List the repository's named branches, indicating which ones are
1401 inactive. If -c/--closed is specified, also list branches which have
1401 inactive. If -c/--closed is specified, also list branches which have
1402 been marked closed (see :hg:`commit --close-branch`).
1402 been marked closed (see :hg:`commit --close-branch`).
1403
1403
1404 Use the command :hg:`update` to switch to an existing branch.
1404 Use the command :hg:`update` to switch to an existing branch.
1405
1405
1406 .. container:: verbose
1406 .. container:: verbose
1407
1407
1408 Template:
1408 Template:
1409
1409
1410 The following keywords are supported in addition to the common template
1410 The following keywords are supported in addition to the common template
1411 keywords and functions such as ``{branch}``. See also
1411 keywords and functions such as ``{branch}``. See also
1412 :hg:`help templates`.
1412 :hg:`help templates`.
1413
1413
1414 :active: Boolean. True if the branch is active.
1414 :active: Boolean. True if the branch is active.
1415 :closed: Boolean. True if the branch is closed.
1415 :closed: Boolean. True if the branch is closed.
1416 :current: Boolean. True if it is the current branch.
1416 :current: Boolean. True if it is the current branch.
1417
1417
1418 Returns 0.
1418 Returns 0.
1419 """
1419 """
1420
1420
1421 opts = pycompat.byteskwargs(opts)
1421 opts = pycompat.byteskwargs(opts)
1422 revs = opts.get(b'rev')
1422 revs = opts.get(b'rev')
1423 selectedbranches = None
1423 selectedbranches = None
1424 if revs:
1424 if revs:
1425 revs = logcmdutil.revrange(repo, revs)
1425 revs = logcmdutil.revrange(repo, revs)
1426 getbi = repo.revbranchcache().branchinfo
1426 getbi = repo.revbranchcache().branchinfo
1427 selectedbranches = {getbi(r)[0] for r in revs}
1427 selectedbranches = {getbi(r)[0] for r in revs}
1428
1428
1429 ui.pager(b'branches')
1429 ui.pager(b'branches')
1430 fm = ui.formatter(b'branches', opts)
1430 fm = ui.formatter(b'branches', opts)
1431 hexfunc = fm.hexfunc
1431 hexfunc = fm.hexfunc
1432
1432
1433 allheads = set(repo.heads())
1433 allheads = set(repo.heads())
1434 branches = []
1434 branches = []
1435 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1435 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1436 if selectedbranches is not None and tag not in selectedbranches:
1436 if selectedbranches is not None and tag not in selectedbranches:
1437 continue
1437 continue
1438 isactive = False
1438 isactive = False
1439 if not isclosed:
1439 if not isclosed:
1440 openheads = set(repo.branchmap().iteropen(heads))
1440 openheads = set(repo.branchmap().iteropen(heads))
1441 isactive = bool(openheads & allheads)
1441 isactive = bool(openheads & allheads)
1442 branches.append((tag, repo[tip], isactive, not isclosed))
1442 branches.append((tag, repo[tip], isactive, not isclosed))
1443 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1443 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1444
1444
1445 for tag, ctx, isactive, isopen in branches:
1445 for tag, ctx, isactive, isopen in branches:
1446 if active and not isactive:
1446 if active and not isactive:
1447 continue
1447 continue
1448 if isactive:
1448 if isactive:
1449 label = b'branches.active'
1449 label = b'branches.active'
1450 notice = b''
1450 notice = b''
1451 elif not isopen:
1451 elif not isopen:
1452 if not closed:
1452 if not closed:
1453 continue
1453 continue
1454 label = b'branches.closed'
1454 label = b'branches.closed'
1455 notice = _(b' (closed)')
1455 notice = _(b' (closed)')
1456 else:
1456 else:
1457 label = b'branches.inactive'
1457 label = b'branches.inactive'
1458 notice = _(b' (inactive)')
1458 notice = _(b' (inactive)')
1459 current = tag == repo.dirstate.branch()
1459 current = tag == repo.dirstate.branch()
1460 if current:
1460 if current:
1461 label = b'branches.current'
1461 label = b'branches.current'
1462
1462
1463 fm.startitem()
1463 fm.startitem()
1464 fm.write(b'branch', b'%s', tag, label=label)
1464 fm.write(b'branch', b'%s', tag, label=label)
1465 rev = ctx.rev()
1465 rev = ctx.rev()
1466 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1466 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1467 fmt = b' ' * padsize + b' %d:%s'
1467 fmt = b' ' * padsize + b' %d:%s'
1468 fm.condwrite(
1468 fm.condwrite(
1469 not ui.quiet,
1469 not ui.quiet,
1470 b'rev node',
1470 b'rev node',
1471 fmt,
1471 fmt,
1472 rev,
1472 rev,
1473 hexfunc(ctx.node()),
1473 hexfunc(ctx.node()),
1474 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1474 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1475 )
1475 )
1476 fm.context(ctx=ctx)
1476 fm.context(ctx=ctx)
1477 fm.data(active=isactive, closed=not isopen, current=current)
1477 fm.data(active=isactive, closed=not isopen, current=current)
1478 if not ui.quiet:
1478 if not ui.quiet:
1479 fm.plain(notice)
1479 fm.plain(notice)
1480 fm.plain(b'\n')
1480 fm.plain(b'\n')
1481 fm.end()
1481 fm.end()
1482
1482
1483
1483
1484 @command(
1484 @command(
1485 b'bundle',
1485 b'bundle',
1486 [
1486 [
1487
1488 (
1487 (
1489 b'',
1488 b'',
1490 b'exact',
1489 b'exact',
1491 None,
1490 None,
1492 _(b'compute the base from the revision specified'),
1491 _(b'compute the base from the revision specified'),
1493 ),
1492 ),
1494 (
1493 (
1495 b'f',
1494 b'f',
1496 b'force',
1495 b'force',
1497 None,
1496 None,
1498 _(b'run even when the destination is unrelated'),
1497 _(b'run even when the destination is unrelated'),
1499 ),
1498 ),
1500 (
1499 (
1501 b'r',
1500 b'r',
1502 b'rev',
1501 b'rev',
1503 [],
1502 [],
1504 _(b'a changeset intended to be added to the destination'),
1503 _(b'a changeset intended to be added to the destination'),
1505 _(b'REV'),
1504 _(b'REV'),
1506 ),
1505 ),
1507 (
1506 (
1508 b'b',
1507 b'b',
1509 b'branch',
1508 b'branch',
1510 [],
1509 [],
1511 _(b'a specific branch you would like to bundle'),
1510 _(b'a specific branch you would like to bundle'),
1512 _(b'BRANCH'),
1511 _(b'BRANCH'),
1513 ),
1512 ),
1514 (
1513 (
1515 b'',
1514 b'',
1516 b'base',
1515 b'base',
1517 [],
1516 [],
1518 _(b'a base changeset assumed to be available at the destination'),
1517 _(b'a base changeset assumed to be available at the destination'),
1519 _(b'REV'),
1518 _(b'REV'),
1520 ),
1519 ),
1521 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1520 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1522 (
1521 (
1523 b't',
1522 b't',
1524 b'type',
1523 b'type',
1525 b'bzip2',
1524 b'bzip2',
1526 _(b'bundle compression type to use'),
1525 _(b'bundle compression type to use'),
1527 _(b'TYPE'),
1526 _(b'TYPE'),
1528 ),
1527 ),
1529 ]
1528 ]
1530 + remoteopts,
1529 + remoteopts,
1531 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]...'),
1530 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]...'),
1532 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1531 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1533 )
1532 )
1534 def bundle(ui, repo, fname, *dests, **opts):
1533 def bundle(ui, repo, fname, *dests, **opts):
1535 """create a bundle file
1534 """create a bundle file
1536
1535
1537 Generate a bundle file containing data to be transferred to another
1536 Generate a bundle file containing data to be transferred to another
1538 repository.
1537 repository.
1539
1538
1540 To create a bundle containing all changesets, use -a/--all
1539 To create a bundle containing all changesets, use -a/--all
1541 (or --base null). Otherwise, hg assumes the destination will have
1540 (or --base null). Otherwise, hg assumes the destination will have
1542 all the nodes you specify with --base parameters. Otherwise, hg
1541 all the nodes you specify with --base parameters. Otherwise, hg
1543 will assume the repository has all the nodes in destination, or
1542 will assume the repository has all the nodes in destination, or
1544 default-push/default if no destination is specified, where destination
1543 default-push/default if no destination is specified, where destination
1545 is the repositories you provide through DEST option.
1544 is the repositories you provide through DEST option.
1546
1545
1547 You can change bundle format with the -t/--type option. See
1546 You can change bundle format with the -t/--type option. See
1548 :hg:`help bundlespec` for documentation on this format. By default,
1547 :hg:`help bundlespec` for documentation on this format. By default,
1549 the most appropriate format is used and compression defaults to
1548 the most appropriate format is used and compression defaults to
1550 bzip2.
1549 bzip2.
1551
1550
1552 The bundle file can then be transferred using conventional means
1551 The bundle file can then be transferred using conventional means
1553 and applied to another repository with the unbundle or pull
1552 and applied to another repository with the unbundle or pull
1554 command. This is useful when direct push and pull are not
1553 command. This is useful when direct push and pull are not
1555 available or when exporting an entire repository is undesirable.
1554 available or when exporting an entire repository is undesirable.
1556
1555
1557 Applying bundles preserves all changeset contents including
1556 Applying bundles preserves all changeset contents including
1558 permissions, copy/rename information, and revision history.
1557 permissions, copy/rename information, and revision history.
1559
1558
1560 Returns 0 on success, 1 if no changes found.
1559 Returns 0 on success, 1 if no changes found.
1561 """
1560 """
1562 opts = pycompat.byteskwargs(opts)
1561 opts = pycompat.byteskwargs(opts)
1563
1562
1564 revs = None
1563 revs = None
1565 if b'rev' in opts:
1564 if b'rev' in opts:
1566 revstrings = opts[b'rev']
1565 revstrings = opts[b'rev']
1567 revs = logcmdutil.revrange(repo, revstrings)
1566 revs = logcmdutil.revrange(repo, revstrings)
1568 if revstrings and not revs:
1567 if revstrings and not revs:
1569 raise error.InputError(_(b'no commits to bundle'))
1568 raise error.InputError(_(b'no commits to bundle'))
1570
1569
1571 bundletype = opts.get(b'type', b'bzip2').lower()
1570 bundletype = opts.get(b'type', b'bzip2').lower()
1572 try:
1571 try:
1573 bundlespec = bundlecaches.parsebundlespec(
1572 bundlespec = bundlecaches.parsebundlespec(
1574 repo, bundletype, strict=False
1573 repo, bundletype, strict=False
1575 )
1574 )
1576 except error.UnsupportedBundleSpecification as e:
1575 except error.UnsupportedBundleSpecification as e:
1577 raise error.InputError(
1576 raise error.InputError(
1578 pycompat.bytestr(e),
1577 pycompat.bytestr(e),
1579 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1578 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1580 )
1579 )
1581 cgversion = bundlespec.params[b"cg.version"]
1580 cgversion = bundlespec.params[b"cg.version"]
1582
1581
1583 # Packed bundles are a pseudo bundle format for now.
1582 # Packed bundles are a pseudo bundle format for now.
1584 if cgversion == b's1':
1583 if cgversion == b's1':
1585 raise error.InputError(
1584 raise error.InputError(
1586 _(b'packed bundles cannot be produced by "hg bundle"'),
1585 _(b'packed bundles cannot be produced by "hg bundle"'),
1587 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1586 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1588 )
1587 )
1589
1588
1590 if opts.get(b'all'):
1589 if opts.get(b'all'):
1591 if dests:
1590 if dests:
1592 raise error.InputError(
1591 raise error.InputError(
1593 _(b"--all is incompatible with specifying destinations")
1592 _(b"--all is incompatible with specifying destinations")
1594 )
1593 )
1595 if opts.get(b'base'):
1594 if opts.get(b'base'):
1596 ui.warn(_(b"ignoring --base because --all was specified\n"))
1595 ui.warn(_(b"ignoring --base because --all was specified\n"))
1597 if opts.get(b'exact'):
1596 if opts.get(b'exact'):
1598 ui.warn(_(b"ignoring --exact because --all was specified\n"))
1597 ui.warn(_(b"ignoring --exact because --all was specified\n"))
1599 base = [nullrev]
1598 base = [nullrev]
1600 elif opts.get(b'exact'):
1599 elif opts.get(b'exact'):
1601 if dests:
1600 if dests:
1602 raise error.InputError(
1601 raise error.InputError(
1603 _(b"--exact is incompatible with specifying destinations")
1602 _(b"--exact is incompatible with specifying destinations")
1604 )
1603 )
1605 if opts.get(b'base'):
1604 if opts.get(b'base'):
1606 ui.warn(_(b"ignoring --base because --exact was specified\n"))
1605 ui.warn(_(b"ignoring --base because --exact was specified\n"))
1607 base = repo.revs(b'parents(%ld) - %ld', revs, revs)
1606 base = repo.revs(b'parents(%ld) - %ld', revs, revs)
1608 if not base:
1607 if not base:
1609 base = [nullrev]
1608 base = [nullrev]
1610 else:
1609 else:
1611 base = logcmdutil.revrange(repo, opts.get(b'base'))
1610 base = logcmdutil.revrange(repo, opts.get(b'base'))
1612 if cgversion not in changegroup.supportedoutgoingversions(repo):
1611 if cgversion not in changegroup.supportedoutgoingversions(repo):
1613 raise error.Abort(
1612 raise error.Abort(
1614 _(b"repository does not support bundle version %s") % cgversion
1613 _(b"repository does not support bundle version %s") % cgversion
1615 )
1614 )
1616
1615
1617 if base:
1616 if base:
1618 if dests:
1617 if dests:
1619 raise error.InputError(
1618 raise error.InputError(
1620 _(b"--base is incompatible with specifying destinations")
1619 _(b"--base is incompatible with specifying destinations")
1621 )
1620 )
1622 cl = repo.changelog
1621 cl = repo.changelog
1623 common = [cl.node(rev) for rev in base]
1622 common = [cl.node(rev) for rev in base]
1624 heads = [cl.node(r) for r in revs] if revs else None
1623 heads = [cl.node(r) for r in revs] if revs else None
1625 outgoing = discovery.outgoing(repo, common, heads)
1624 outgoing = discovery.outgoing(repo, common, heads)
1626 missing = outgoing.missing
1625 missing = outgoing.missing
1627 excluded = outgoing.excluded
1626 excluded = outgoing.excluded
1628 else:
1627 else:
1629 missing = set()
1628 missing = set()
1630 excluded = set()
1629 excluded = set()
1631 for path in urlutil.get_push_paths(repo, ui, dests):
1630 for path in urlutil.get_push_paths(repo, ui, dests):
1632 other = hg.peer(repo, opts, path.rawloc)
1631 other = hg.peer(repo, opts, path.rawloc)
1633 if revs is not None:
1632 if revs is not None:
1634 hex_revs = [repo[r].hex() for r in revs]
1633 hex_revs = [repo[r].hex() for r in revs]
1635 else:
1634 else:
1636 hex_revs = None
1635 hex_revs = None
1637 branches = (path.branch, [])
1636 branches = (path.branch, [])
1638 head_revs, checkout = hg.addbranchrevs(
1637 head_revs, checkout = hg.addbranchrevs(
1639 repo, repo, branches, hex_revs
1638 repo, repo, branches, hex_revs
1640 )
1639 )
1641 heads = (
1640 heads = (
1642 head_revs
1641 head_revs
1643 and pycompat.maplist(repo.lookup, head_revs)
1642 and pycompat.maplist(repo.lookup, head_revs)
1644 or head_revs
1643 or head_revs
1645 )
1644 )
1646 outgoing = discovery.findcommonoutgoing(
1645 outgoing = discovery.findcommonoutgoing(
1647 repo,
1646 repo,
1648 other,
1647 other,
1649 onlyheads=heads,
1648 onlyheads=heads,
1650 force=opts.get(b'force'),
1649 force=opts.get(b'force'),
1651 portable=True,
1650 portable=True,
1652 )
1651 )
1653 missing.update(outgoing.missing)
1652 missing.update(outgoing.missing)
1654 excluded.update(outgoing.excluded)
1653 excluded.update(outgoing.excluded)
1655
1654
1656 if not missing:
1655 if not missing:
1657 scmutil.nochangesfound(ui, repo, not base and excluded)
1656 scmutil.nochangesfound(ui, repo, not base and excluded)
1658 return 1
1657 return 1
1659
1658
1660 if heads:
1659 if heads:
1661 outgoing = discovery.outgoing(
1660 outgoing = discovery.outgoing(
1662 repo, missingroots=missing, ancestorsof=heads
1661 repo, missingroots=missing, ancestorsof=heads
1663 )
1662 )
1664 else:
1663 else:
1665 outgoing = discovery.outgoing(repo, missingroots=missing)
1664 outgoing = discovery.outgoing(repo, missingroots=missing)
1666 outgoing.excluded = sorted(excluded)
1665 outgoing.excluded = sorted(excluded)
1667
1666
1668 if cgversion == b'01': # bundle1
1667 if cgversion == b'01': # bundle1
1669 bversion = b'HG10' + bundlespec.wirecompression
1668 bversion = b'HG10' + bundlespec.wirecompression
1670 bcompression = None
1669 bcompression = None
1671 elif cgversion in (b'02', b'03'):
1670 elif cgversion in (b'02', b'03'):
1672 bversion = b'HG20'
1671 bversion = b'HG20'
1673 bcompression = bundlespec.wirecompression
1672 bcompression = bundlespec.wirecompression
1674 else:
1673 else:
1675 raise error.ProgrammingError(
1674 raise error.ProgrammingError(
1676 b'bundle: unexpected changegroup version %s' % cgversion
1675 b'bundle: unexpected changegroup version %s' % cgversion
1677 )
1676 )
1678
1677
1679 # TODO compression options should be derived from bundlespec parsing.
1678 # TODO compression options should be derived from bundlespec parsing.
1680 # This is a temporary hack to allow adjusting bundle compression
1679 # This is a temporary hack to allow adjusting bundle compression
1681 # level without a) formalizing the bundlespec changes to declare it
1680 # level without a) formalizing the bundlespec changes to declare it
1682 # b) introducing a command flag.
1681 # b) introducing a command flag.
1683 compopts = {}
1682 compopts = {}
1684 complevel = ui.configint(
1683 complevel = ui.configint(
1685 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1684 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1686 )
1685 )
1687 if complevel is None:
1686 if complevel is None:
1688 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1687 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1689 if complevel is not None:
1688 if complevel is not None:
1690 compopts[b'level'] = complevel
1689 compopts[b'level'] = complevel
1691
1690
1692 compthreads = ui.configint(
1691 compthreads = ui.configint(
1693 b'experimental', b'bundlecompthreads.' + bundlespec.compression
1692 b'experimental', b'bundlecompthreads.' + bundlespec.compression
1694 )
1693 )
1695 if compthreads is None:
1694 if compthreads is None:
1696 compthreads = ui.configint(b'experimental', b'bundlecompthreads')
1695 compthreads = ui.configint(b'experimental', b'bundlecompthreads')
1697 if compthreads is not None:
1696 if compthreads is not None:
1698 compopts[b'threads'] = compthreads
1697 compopts[b'threads'] = compthreads
1699
1698
1700 # Bundling of obsmarker and phases is optional as not all clients
1699 # Bundling of obsmarker and phases is optional as not all clients
1701 # support the necessary features.
1700 # support the necessary features.
1702 cfg = ui.configbool
1701 cfg = ui.configbool
1703 obsolescence_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker')
1702 obsolescence_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker')
1704 bundlespec.set_param(b'obsolescence', obsolescence_cfg, overwrite=False)
1703 bundlespec.set_param(b'obsolescence', obsolescence_cfg, overwrite=False)
1705 obs_mand_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker:mandatory')
1704 obs_mand_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker:mandatory')
1706 bundlespec.set_param(
1705 bundlespec.set_param(
1707 b'obsolescence-mandatory', obs_mand_cfg, overwrite=False
1706 b'obsolescence-mandatory', obs_mand_cfg, overwrite=False
1708 )
1707 )
1709 phases_cfg = cfg(b'experimental', b'bundle-phases')
1708 phases_cfg = cfg(b'experimental', b'bundle-phases')
1710 bundlespec.set_param(b'phases', phases_cfg, overwrite=False)
1709 bundlespec.set_param(b'phases', phases_cfg, overwrite=False)
1711
1710
1712 bundle2.writenewbundle(
1711 bundle2.writenewbundle(
1713 ui,
1712 ui,
1714 repo,
1713 repo,
1715 b'bundle',
1714 b'bundle',
1716 fname,
1715 fname,
1717 bversion,
1716 bversion,
1718 outgoing,
1717 outgoing,
1719 bundlespec.params,
1718 bundlespec.params,
1720 compression=bcompression,
1719 compression=bcompression,
1721 compopts=compopts,
1720 compopts=compopts,
1722 )
1721 )
1723
1722
1724
1723
1725 @command(
1724 @command(
1726 b'cat',
1725 b'cat',
1727 [
1726 [
1728 (
1727 (
1729 b'o',
1728 b'o',
1730 b'output',
1729 b'output',
1731 b'',
1730 b'',
1732 _(b'print output to file with formatted name'),
1731 _(b'print output to file with formatted name'),
1733 _(b'FORMAT'),
1732 _(b'FORMAT'),
1734 ),
1733 ),
1735 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1734 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1736 (b'', b'decode', None, _(b'apply any matching decode filter')),
1735 (b'', b'decode', None, _(b'apply any matching decode filter')),
1737 ]
1736 ]
1738 + walkopts
1737 + walkopts
1739 + formatteropts,
1738 + formatteropts,
1740 _(b'[OPTION]... FILE...'),
1739 _(b'[OPTION]... FILE...'),
1741 helpcategory=command.CATEGORY_FILE_CONTENTS,
1740 helpcategory=command.CATEGORY_FILE_CONTENTS,
1742 inferrepo=True,
1741 inferrepo=True,
1743 intents={INTENT_READONLY},
1742 intents={INTENT_READONLY},
1744 )
1743 )
1745 def cat(ui, repo, file1, *pats, **opts):
1744 def cat(ui, repo, file1, *pats, **opts):
1746 """output the current or given revision of files
1745 """output the current or given revision of files
1747
1746
1748 Print the specified files as they were at the given revision. If
1747 Print the specified files as they were at the given revision. If
1749 no revision is given, the parent of the working directory is used.
1748 no revision is given, the parent of the working directory is used.
1750
1749
1751 Output may be to a file, in which case the name of the file is
1750 Output may be to a file, in which case the name of the file is
1752 given using a template string. See :hg:`help templates`. In addition
1751 given using a template string. See :hg:`help templates`. In addition
1753 to the common template keywords, the following formatting rules are
1752 to the common template keywords, the following formatting rules are
1754 supported:
1753 supported:
1755
1754
1756 :``%%``: literal "%" character
1755 :``%%``: literal "%" character
1757 :``%s``: basename of file being printed
1756 :``%s``: basename of file being printed
1758 :``%d``: dirname of file being printed, or '.' if in repository root
1757 :``%d``: dirname of file being printed, or '.' if in repository root
1759 :``%p``: root-relative path name of file being printed
1758 :``%p``: root-relative path name of file being printed
1760 :``%H``: changeset hash (40 hexadecimal digits)
1759 :``%H``: changeset hash (40 hexadecimal digits)
1761 :``%R``: changeset revision number
1760 :``%R``: changeset revision number
1762 :``%h``: short-form changeset hash (12 hexadecimal digits)
1761 :``%h``: short-form changeset hash (12 hexadecimal digits)
1763 :``%r``: zero-padded changeset revision number
1762 :``%r``: zero-padded changeset revision number
1764 :``%b``: basename of the exporting repository
1763 :``%b``: basename of the exporting repository
1765 :``\\``: literal "\\" character
1764 :``\\``: literal "\\" character
1766
1765
1767 .. container:: verbose
1766 .. container:: verbose
1768
1767
1769 Template:
1768 Template:
1770
1769
1771 The following keywords are supported in addition to the common template
1770 The following keywords are supported in addition to the common template
1772 keywords and functions. See also :hg:`help templates`.
1771 keywords and functions. See also :hg:`help templates`.
1773
1772
1774 :data: String. File content.
1773 :data: String. File content.
1775 :path: String. Repository-absolute path of the file.
1774 :path: String. Repository-absolute path of the file.
1776
1775
1777 Returns 0 on success.
1776 Returns 0 on success.
1778 """
1777 """
1779 opts = pycompat.byteskwargs(opts)
1778 opts = pycompat.byteskwargs(opts)
1780 rev = opts.get(b'rev')
1779 rev = opts.get(b'rev')
1781 if rev:
1780 if rev:
1782 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1781 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1783 ctx = logcmdutil.revsingle(repo, rev)
1782 ctx = logcmdutil.revsingle(repo, rev)
1784 m = scmutil.match(ctx, (file1,) + pats, opts)
1783 m = scmutil.match(ctx, (file1,) + pats, opts)
1785 fntemplate = opts.pop(b'output', b'')
1784 fntemplate = opts.pop(b'output', b'')
1786 if cmdutil.isstdiofilename(fntemplate):
1785 if cmdutil.isstdiofilename(fntemplate):
1787 fntemplate = b''
1786 fntemplate = b''
1788
1787
1789 if fntemplate:
1788 if fntemplate:
1790 fm = formatter.nullformatter(ui, b'cat', opts)
1789 fm = formatter.nullformatter(ui, b'cat', opts)
1791 else:
1790 else:
1792 ui.pager(b'cat')
1791 ui.pager(b'cat')
1793 fm = ui.formatter(b'cat', opts)
1792 fm = ui.formatter(b'cat', opts)
1794 with fm:
1793 with fm:
1795 return cmdutil.cat(
1794 return cmdutil.cat(
1796 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1795 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1797 )
1796 )
1798
1797
1799
1798
1800 @command(
1799 @command(
1801 b'clone',
1800 b'clone',
1802 [
1801 [
1803 (
1802 (
1804 b'U',
1803 b'U',
1805 b'noupdate',
1804 b'noupdate',
1806 None,
1805 None,
1807 _(
1806 _(
1808 b'the clone will include an empty working '
1807 b'the clone will include an empty working '
1809 b'directory (only a repository)'
1808 b'directory (only a repository)'
1810 ),
1809 ),
1811 ),
1810 ),
1812 (
1811 (
1813 b'u',
1812 b'u',
1814 b'updaterev',
1813 b'updaterev',
1815 b'',
1814 b'',
1816 _(b'revision, tag, or branch to check out'),
1815 _(b'revision, tag, or branch to check out'),
1817 _(b'REV'),
1816 _(b'REV'),
1818 ),
1817 ),
1819 (
1818 (
1820 b'r',
1819 b'r',
1821 b'rev',
1820 b'rev',
1822 [],
1821 [],
1823 _(
1822 _(
1824 b'do not clone everything, but include this changeset'
1823 b'do not clone everything, but include this changeset'
1825 b' and its ancestors'
1824 b' and its ancestors'
1826 ),
1825 ),
1827 _(b'REV'),
1826 _(b'REV'),
1828 ),
1827 ),
1829 (
1828 (
1830 b'b',
1829 b'b',
1831 b'branch',
1830 b'branch',
1832 [],
1831 [],
1833 _(
1832 _(
1834 b'do not clone everything, but include this branch\'s'
1833 b'do not clone everything, but include this branch\'s'
1835 b' changesets and their ancestors'
1834 b' changesets and their ancestors'
1836 ),
1835 ),
1837 _(b'BRANCH'),
1836 _(b'BRANCH'),
1838 ),
1837 ),
1839 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1838 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1840 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1839 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1841 (b'', b'stream', None, _(b'clone with minimal data processing')),
1840 (b'', b'stream', None, _(b'clone with minimal data processing')),
1842 ]
1841 ]
1843 + remoteopts,
1842 + remoteopts,
1844 _(b'[OPTION]... SOURCE [DEST]'),
1843 _(b'[OPTION]... SOURCE [DEST]'),
1845 helpcategory=command.CATEGORY_REPO_CREATION,
1844 helpcategory=command.CATEGORY_REPO_CREATION,
1846 helpbasic=True,
1845 helpbasic=True,
1847 norepo=True,
1846 norepo=True,
1848 )
1847 )
1849 def clone(ui, source, dest=None, **opts):
1848 def clone(ui, source, dest=None, **opts):
1850 """make a copy of an existing repository
1849 """make a copy of an existing repository
1851
1850
1852 Create a copy of an existing repository in a new directory.
1851 Create a copy of an existing repository in a new directory.
1853
1852
1854 If no destination directory name is specified, it defaults to the
1853 If no destination directory name is specified, it defaults to the
1855 basename of the source.
1854 basename of the source.
1856
1855
1857 The location of the source is added to the new repository's
1856 The location of the source is added to the new repository's
1858 ``.hg/hgrc`` file, as the default to be used for future pulls.
1857 ``.hg/hgrc`` file, as the default to be used for future pulls.
1859
1858
1860 Only local paths and ``ssh://`` URLs are supported as
1859 Only local paths and ``ssh://`` URLs are supported as
1861 destinations. For ``ssh://`` destinations, no working directory or
1860 destinations. For ``ssh://`` destinations, no working directory or
1862 ``.hg/hgrc`` will be created on the remote side.
1861 ``.hg/hgrc`` will be created on the remote side.
1863
1862
1864 If the source repository has a bookmark called '@' set, that
1863 If the source repository has a bookmark called '@' set, that
1865 revision will be checked out in the new repository by default.
1864 revision will be checked out in the new repository by default.
1866
1865
1867 To check out a particular version, use -u/--update, or
1866 To check out a particular version, use -u/--update, or
1868 -U/--noupdate to create a clone with no working directory.
1867 -U/--noupdate to create a clone with no working directory.
1869
1868
1870 To pull only a subset of changesets, specify one or more revisions
1869 To pull only a subset of changesets, specify one or more revisions
1871 identifiers with -r/--rev or branches with -b/--branch. The
1870 identifiers with -r/--rev or branches with -b/--branch. The
1872 resulting clone will contain only the specified changesets and
1871 resulting clone will contain only the specified changesets and
1873 their ancestors. These options (or 'clone src#rev dest') imply
1872 their ancestors. These options (or 'clone src#rev dest') imply
1874 --pull, even for local source repositories.
1873 --pull, even for local source repositories.
1875
1874
1876 In normal clone mode, the remote normalizes repository data into a common
1875 In normal clone mode, the remote normalizes repository data into a common
1877 exchange format and the receiving end translates this data into its local
1876 exchange format and the receiving end translates this data into its local
1878 storage format. --stream activates a different clone mode that essentially
1877 storage format. --stream activates a different clone mode that essentially
1879 copies repository files from the remote with minimal data processing. This
1878 copies repository files from the remote with minimal data processing. This
1880 significantly reduces the CPU cost of a clone both remotely and locally.
1879 significantly reduces the CPU cost of a clone both remotely and locally.
1881 However, it often increases the transferred data size by 30-40%. This can
1880 However, it often increases the transferred data size by 30-40%. This can
1882 result in substantially faster clones where I/O throughput is plentiful,
1881 result in substantially faster clones where I/O throughput is plentiful,
1883 especially for larger repositories. A side-effect of --stream clones is
1882 especially for larger repositories. A side-effect of --stream clones is
1884 that storage settings and requirements on the remote are applied locally:
1883 that storage settings and requirements on the remote are applied locally:
1885 a modern client may inherit legacy or inefficient storage used by the
1884 a modern client may inherit legacy or inefficient storage used by the
1886 remote or a legacy Mercurial client may not be able to clone from a
1885 remote or a legacy Mercurial client may not be able to clone from a
1887 modern Mercurial remote.
1886 modern Mercurial remote.
1888
1887
1889 .. note::
1888 .. note::
1890
1889
1891 Specifying a tag will include the tagged changeset but not the
1890 Specifying a tag will include the tagged changeset but not the
1892 changeset containing the tag.
1891 changeset containing the tag.
1893
1892
1894 .. container:: verbose
1893 .. container:: verbose
1895
1894
1896 For efficiency, hardlinks are used for cloning whenever the
1895 For efficiency, hardlinks are used for cloning whenever the
1897 source and destination are on the same filesystem (note this
1896 source and destination are on the same filesystem (note this
1898 applies only to the repository data, not to the working
1897 applies only to the repository data, not to the working
1899 directory). Some filesystems, such as AFS, implement hardlinking
1898 directory). Some filesystems, such as AFS, implement hardlinking
1900 incorrectly, but do not report errors. In these cases, use the
1899 incorrectly, but do not report errors. In these cases, use the
1901 --pull option to avoid hardlinking.
1900 --pull option to avoid hardlinking.
1902
1901
1903 Mercurial will update the working directory to the first applicable
1902 Mercurial will update the working directory to the first applicable
1904 revision from this list:
1903 revision from this list:
1905
1904
1906 a) null if -U or the source repository has no changesets
1905 a) null if -U or the source repository has no changesets
1907 b) if -u . and the source repository is local, the first parent of
1906 b) if -u . and the source repository is local, the first parent of
1908 the source repository's working directory
1907 the source repository's working directory
1909 c) the changeset specified with -u (if a branch name, this means the
1908 c) the changeset specified with -u (if a branch name, this means the
1910 latest head of that branch)
1909 latest head of that branch)
1911 d) the changeset specified with -r
1910 d) the changeset specified with -r
1912 e) the tipmost head specified with -b
1911 e) the tipmost head specified with -b
1913 f) the tipmost head specified with the url#branch source syntax
1912 f) the tipmost head specified with the url#branch source syntax
1914 g) the revision marked with the '@' bookmark, if present
1913 g) the revision marked with the '@' bookmark, if present
1915 h) the tipmost head of the default branch
1914 h) the tipmost head of the default branch
1916 i) tip
1915 i) tip
1917
1916
1918 When cloning from servers that support it, Mercurial may fetch
1917 When cloning from servers that support it, Mercurial may fetch
1919 pre-generated data from a server-advertised URL or inline from the
1918 pre-generated data from a server-advertised URL or inline from the
1920 same stream. When this is done, hooks operating on incoming changesets
1919 same stream. When this is done, hooks operating on incoming changesets
1921 and changegroups may fire more than once, once for each pre-generated
1920 and changegroups may fire more than once, once for each pre-generated
1922 bundle and as well as for any additional remaining data. In addition,
1921 bundle and as well as for any additional remaining data. In addition,
1923 if an error occurs, the repository may be rolled back to a partial
1922 if an error occurs, the repository may be rolled back to a partial
1924 clone. This behavior may change in future releases.
1923 clone. This behavior may change in future releases.
1925 See :hg:`help -e clonebundles` for more.
1924 See :hg:`help -e clonebundles` for more.
1926
1925
1927 Examples:
1926 Examples:
1928
1927
1929 - clone a remote repository to a new directory named hg/::
1928 - clone a remote repository to a new directory named hg/::
1930
1929
1931 hg clone https://www.mercurial-scm.org/repo/hg/
1930 hg clone https://www.mercurial-scm.org/repo/hg/
1932
1931
1933 - create a lightweight local clone::
1932 - create a lightweight local clone::
1934
1933
1935 hg clone project/ project-feature/
1934 hg clone project/ project-feature/
1936
1935
1937 - clone from an absolute path on an ssh server (note double-slash)::
1936 - clone from an absolute path on an ssh server (note double-slash)::
1938
1937
1939 hg clone ssh://user@server//home/projects/alpha/
1938 hg clone ssh://user@server//home/projects/alpha/
1940
1939
1941 - do a streaming clone while checking out a specified version::
1940 - do a streaming clone while checking out a specified version::
1942
1941
1943 hg clone --stream http://server/repo -u 1.5
1942 hg clone --stream http://server/repo -u 1.5
1944
1943
1945 - create a repository without changesets after a particular revision::
1944 - create a repository without changesets after a particular revision::
1946
1945
1947 hg clone -r 04e544 experimental/ good/
1946 hg clone -r 04e544 experimental/ good/
1948
1947
1949 - clone (and track) a particular named branch::
1948 - clone (and track) a particular named branch::
1950
1949
1951 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1950 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1952
1951
1953 See :hg:`help urls` for details on specifying URLs.
1952 See :hg:`help urls` for details on specifying URLs.
1954
1953
1955 Returns 0 on success.
1954 Returns 0 on success.
1956 """
1955 """
1957 opts = pycompat.byteskwargs(opts)
1956 opts = pycompat.byteskwargs(opts)
1958 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1957 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1959
1958
1960 # --include/--exclude can come from narrow or sparse.
1959 # --include/--exclude can come from narrow or sparse.
1961 includepats, excludepats = None, None
1960 includepats, excludepats = None, None
1962
1961
1963 # hg.clone() differentiates between None and an empty set. So make sure
1962 # hg.clone() differentiates between None and an empty set. So make sure
1964 # patterns are sets if narrow is requested without patterns.
1963 # patterns are sets if narrow is requested without patterns.
1965 if opts.get(b'narrow'):
1964 if opts.get(b'narrow'):
1966 includepats = set()
1965 includepats = set()
1967 excludepats = set()
1966 excludepats = set()
1968
1967
1969 if opts.get(b'include'):
1968 if opts.get(b'include'):
1970 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1969 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1971 if opts.get(b'exclude'):
1970 if opts.get(b'exclude'):
1972 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1971 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1973
1972
1974 r = hg.clone(
1973 r = hg.clone(
1975 ui,
1974 ui,
1976 opts,
1975 opts,
1977 source,
1976 source,
1978 dest,
1977 dest,
1979 pull=opts.get(b'pull'),
1978 pull=opts.get(b'pull'),
1980 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1979 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1981 revs=opts.get(b'rev'),
1980 revs=opts.get(b'rev'),
1982 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1981 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1983 branch=opts.get(b'branch'),
1982 branch=opts.get(b'branch'),
1984 shareopts=opts.get(b'shareopts'),
1983 shareopts=opts.get(b'shareopts'),
1985 storeincludepats=includepats,
1984 storeincludepats=includepats,
1986 storeexcludepats=excludepats,
1985 storeexcludepats=excludepats,
1987 depth=opts.get(b'depth') or None,
1986 depth=opts.get(b'depth') or None,
1988 )
1987 )
1989
1988
1990 return r is None
1989 return r is None
1991
1990
1992
1991
1993 @command(
1992 @command(
1994 b'commit|ci',
1993 b'commit|ci',
1995 [
1994 [
1996 (
1995 (
1997 b'A',
1996 b'A',
1998 b'addremove',
1997 b'addremove',
1999 None,
1998 None,
2000 _(b'mark new/missing files as added/removed before committing'),
1999 _(b'mark new/missing files as added/removed before committing'),
2001 ),
2000 ),
2002 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
2001 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
2003 (b'', b'amend', None, _(b'amend the parent of the working directory')),
2002 (b'', b'amend', None, _(b'amend the parent of the working directory')),
2004 (b's', b'secret', None, _(b'use the secret phase for committing')),
2003 (b's', b'secret', None, _(b'use the secret phase for committing')),
2005 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
2004 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
2006 (
2005 (
2007 b'',
2006 b'',
2008 b'force-close-branch',
2007 b'force-close-branch',
2009 None,
2008 None,
2010 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
2009 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
2011 ),
2010 ),
2012 (b'i', b'interactive', None, _(b'use interactive mode')),
2011 (b'i', b'interactive', None, _(b'use interactive mode')),
2013 ]
2012 ]
2014 + walkopts
2013 + walkopts
2015 + commitopts
2014 + commitopts
2016 + commitopts2
2015 + commitopts2
2017 + subrepoopts,
2016 + subrepoopts,
2018 _(b'[OPTION]... [FILE]...'),
2017 _(b'[OPTION]... [FILE]...'),
2019 helpcategory=command.CATEGORY_COMMITTING,
2018 helpcategory=command.CATEGORY_COMMITTING,
2020 helpbasic=True,
2019 helpbasic=True,
2021 inferrepo=True,
2020 inferrepo=True,
2022 )
2021 )
2023 def commit(ui, repo, *pats, **opts):
2022 def commit(ui, repo, *pats, **opts):
2024 """commit the specified files or all outstanding changes
2023 """commit the specified files or all outstanding changes
2025
2024
2026 Commit changes to the given files into the repository. Unlike a
2025 Commit changes to the given files into the repository. Unlike a
2027 centralized SCM, this operation is a local operation. See
2026 centralized SCM, this operation is a local operation. See
2028 :hg:`push` for a way to actively distribute your changes.
2027 :hg:`push` for a way to actively distribute your changes.
2029
2028
2030 If a list of files is omitted, all changes reported by :hg:`status`
2029 If a list of files is omitted, all changes reported by :hg:`status`
2031 will be committed.
2030 will be committed.
2032
2031
2033 If you are committing the result of a merge, do not provide any
2032 If you are committing the result of a merge, do not provide any
2034 filenames or -I/-X filters.
2033 filenames or -I/-X filters.
2035
2034
2036 If no commit message is specified, Mercurial starts your
2035 If no commit message is specified, Mercurial starts your
2037 configured editor where you can enter a message. In case your
2036 configured editor where you can enter a message. In case your
2038 commit fails, you will find a backup of your message in
2037 commit fails, you will find a backup of your message in
2039 ``.hg/last-message.txt``.
2038 ``.hg/last-message.txt``.
2040
2039
2041 The --close-branch flag can be used to mark the current branch
2040 The --close-branch flag can be used to mark the current branch
2042 head closed. When all heads of a branch are closed, the branch
2041 head closed. When all heads of a branch are closed, the branch
2043 will be considered closed and no longer listed.
2042 will be considered closed and no longer listed.
2044
2043
2045 The --amend flag can be used to amend the parent of the
2044 The --amend flag can be used to amend the parent of the
2046 working directory with a new commit that contains the changes
2045 working directory with a new commit that contains the changes
2047 in the parent in addition to those currently reported by :hg:`status`,
2046 in the parent in addition to those currently reported by :hg:`status`,
2048 if there are any. The old commit is stored in a backup bundle in
2047 if there are any. The old commit is stored in a backup bundle in
2049 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2048 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2050 on how to restore it).
2049 on how to restore it).
2051
2050
2052 Message, user and date are taken from the amended commit unless
2051 Message, user and date are taken from the amended commit unless
2053 specified. When a message isn't specified on the command line,
2052 specified. When a message isn't specified on the command line,
2054 the editor will open with the message of the amended commit.
2053 the editor will open with the message of the amended commit.
2055
2054
2056 It is not possible to amend public changesets (see :hg:`help phases`)
2055 It is not possible to amend public changesets (see :hg:`help phases`)
2057 or changesets that have children.
2056 or changesets that have children.
2058
2057
2059 See :hg:`help dates` for a list of formats valid for -d/--date.
2058 See :hg:`help dates` for a list of formats valid for -d/--date.
2060
2059
2061 Returns 0 on success, 1 if nothing changed.
2060 Returns 0 on success, 1 if nothing changed.
2062
2061
2063 .. container:: verbose
2062 .. container:: verbose
2064
2063
2065 Examples:
2064 Examples:
2066
2065
2067 - commit all files ending in .py::
2066 - commit all files ending in .py::
2068
2067
2069 hg commit --include "set:**.py"
2068 hg commit --include "set:**.py"
2070
2069
2071 - commit all non-binary files::
2070 - commit all non-binary files::
2072
2071
2073 hg commit --exclude "set:binary()"
2072 hg commit --exclude "set:binary()"
2074
2073
2075 - amend the current commit and set the date to now::
2074 - amend the current commit and set the date to now::
2076
2075
2077 hg commit --amend --date now
2076 hg commit --amend --date now
2078 """
2077 """
2079 with repo.wlock(), repo.lock():
2078 with repo.wlock(), repo.lock():
2080 return _docommit(ui, repo, *pats, **opts)
2079 return _docommit(ui, repo, *pats, **opts)
2081
2080
2082
2081
2083 def _docommit(ui, repo, *pats, **opts):
2082 def _docommit(ui, repo, *pats, **opts):
2084 if opts.get('interactive'):
2083 if opts.get('interactive'):
2085 opts.pop('interactive')
2084 opts.pop('interactive')
2086 ret = cmdutil.dorecord(
2085 ret = cmdutil.dorecord(
2087 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2086 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2088 )
2087 )
2089 # ret can be 0 (no changes to record) or the value returned by
2088 # ret can be 0 (no changes to record) or the value returned by
2090 # commit(), 1 if nothing changed or None on success.
2089 # commit(), 1 if nothing changed or None on success.
2091 return 1 if ret == 0 else ret
2090 return 1 if ret == 0 else ret
2092
2091
2093 if opts.get('subrepos'):
2092 if opts.get('subrepos'):
2094 cmdutil.check_incompatible_arguments(opts, 'subrepos', ['amend'])
2093 cmdutil.check_incompatible_arguments(opts, 'subrepos', ['amend'])
2095 # Let --subrepos on the command line override config setting.
2094 # Let --subrepos on the command line override config setting.
2096 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2095 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2097
2096
2098 cmdutil.checkunfinished(repo, commit=True)
2097 cmdutil.checkunfinished(repo, commit=True)
2099
2098
2100 branch = repo[None].branch()
2099 branch = repo[None].branch()
2101 bheads = repo.branchheads(branch)
2100 bheads = repo.branchheads(branch)
2102 tip = repo.changelog.tip()
2101 tip = repo.changelog.tip()
2103
2102
2104 extra = {}
2103 extra = {}
2105 if opts.get('close_branch') or opts.get('force_close_branch'):
2104 if opts.get('close_branch') or opts.get('force_close_branch'):
2106 extra[b'close'] = b'1'
2105 extra[b'close'] = b'1'
2107
2106
2108 if repo[b'.'].closesbranch():
2107 if repo[b'.'].closesbranch():
2109 # Not ideal, but let us do an extra status early to prevent early
2108 # Not ideal, but let us do an extra status early to prevent early
2110 # bail out.
2109 # bail out.
2111 matcher = scmutil.match(repo[None], pats, opts)
2110 matcher = scmutil.match(repo[None], pats, opts)
2112 s = repo.status(match=matcher)
2111 s = repo.status(match=matcher)
2113 if s.modified or s.added or s.removed:
2112 if s.modified or s.added or s.removed:
2114 bheads = repo.branchheads(branch, closed=True)
2113 bheads = repo.branchheads(branch, closed=True)
2115 else:
2114 else:
2116 msg = _(b'current revision is already a branch closing head')
2115 msg = _(b'current revision is already a branch closing head')
2117 raise error.InputError(msg)
2116 raise error.InputError(msg)
2118
2117
2119 if not bheads:
2118 if not bheads:
2120 raise error.InputError(
2119 raise error.InputError(
2121 _(b'branch "%s" has no heads to close') % branch
2120 _(b'branch "%s" has no heads to close') % branch
2122 )
2121 )
2123 elif (
2122 elif (
2124 branch == repo[b'.'].branch()
2123 branch == repo[b'.'].branch()
2125 and repo[b'.'].node() not in bheads
2124 and repo[b'.'].node() not in bheads
2126 and not opts.get('force_close_branch')
2125 and not opts.get('force_close_branch')
2127 ):
2126 ):
2128 hint = _(
2127 hint = _(
2129 b'use --force-close-branch to close branch from a non-head'
2128 b'use --force-close-branch to close branch from a non-head'
2130 b' changeset'
2129 b' changeset'
2131 )
2130 )
2132 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2131 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2133 elif opts.get('amend'):
2132 elif opts.get('amend'):
2134 if (
2133 if (
2135 repo[b'.'].p1().branch() != branch
2134 repo[b'.'].p1().branch() != branch
2136 and repo[b'.'].p2().branch() != branch
2135 and repo[b'.'].p2().branch() != branch
2137 ):
2136 ):
2138 raise error.InputError(_(b'can only close branch heads'))
2137 raise error.InputError(_(b'can only close branch heads'))
2139
2138
2140 if opts.get('amend'):
2139 if opts.get('amend'):
2141 if ui.configbool(b'ui', b'commitsubrepos'):
2140 if ui.configbool(b'ui', b'commitsubrepos'):
2142 raise error.InputError(
2141 raise error.InputError(
2143 _(b'cannot amend with ui.commitsubrepos enabled')
2142 _(b'cannot amend with ui.commitsubrepos enabled')
2144 )
2143 )
2145
2144
2146 old = repo[b'.']
2145 old = repo[b'.']
2147 rewriteutil.precheck(repo, [old.rev()], b'amend')
2146 rewriteutil.precheck(repo, [old.rev()], b'amend')
2148
2147
2149 # Currently histedit gets confused if an amend happens while histedit
2148 # Currently histedit gets confused if an amend happens while histedit
2150 # is in progress. Since we have a checkunfinished command, we are
2149 # is in progress. Since we have a checkunfinished command, we are
2151 # temporarily honoring it.
2150 # temporarily honoring it.
2152 #
2151 #
2153 # Note: eventually this guard will be removed. Please do not expect
2152 # Note: eventually this guard will be removed. Please do not expect
2154 # this behavior to remain.
2153 # this behavior to remain.
2155 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2154 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2156 cmdutil.checkunfinished(repo)
2155 cmdutil.checkunfinished(repo)
2157
2156
2158 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2157 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2159 opts = pycompat.byteskwargs(opts)
2158 opts = pycompat.byteskwargs(opts)
2160 if node == old.node():
2159 if node == old.node():
2161 ui.status(_(b"nothing changed\n"))
2160 ui.status(_(b"nothing changed\n"))
2162 return 1
2161 return 1
2163 else:
2162 else:
2164
2163
2165 def commitfunc(ui, repo, message, match, opts):
2164 def commitfunc(ui, repo, message, match, opts):
2166 overrides = {}
2165 overrides = {}
2167 if opts.get(b'secret'):
2166 if opts.get(b'secret'):
2168 overrides[(b'phases', b'new-commit')] = b'secret'
2167 overrides[(b'phases', b'new-commit')] = b'secret'
2169
2168
2170 baseui = repo.baseui
2169 baseui = repo.baseui
2171 with baseui.configoverride(overrides, b'commit'):
2170 with baseui.configoverride(overrides, b'commit'):
2172 with ui.configoverride(overrides, b'commit'):
2171 with ui.configoverride(overrides, b'commit'):
2173 editform = cmdutil.mergeeditform(
2172 editform = cmdutil.mergeeditform(
2174 repo[None], b'commit.normal'
2173 repo[None], b'commit.normal'
2175 )
2174 )
2176 editor = cmdutil.getcommiteditor(
2175 editor = cmdutil.getcommiteditor(
2177 editform=editform, **pycompat.strkwargs(opts)
2176 editform=editform, **pycompat.strkwargs(opts)
2178 )
2177 )
2179 return repo.commit(
2178 return repo.commit(
2180 message,
2179 message,
2181 opts.get(b'user'),
2180 opts.get(b'user'),
2182 opts.get(b'date'),
2181 opts.get(b'date'),
2183 match,
2182 match,
2184 editor=editor,
2183 editor=editor,
2185 extra=extra,
2184 extra=extra,
2186 )
2185 )
2187
2186
2188 opts = pycompat.byteskwargs(opts)
2187 opts = pycompat.byteskwargs(opts)
2189 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2188 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2190
2189
2191 if not node:
2190 if not node:
2192 stat = cmdutil.postcommitstatus(repo, pats, opts)
2191 stat = cmdutil.postcommitstatus(repo, pats, opts)
2193 if stat.deleted:
2192 if stat.deleted:
2194 ui.status(
2193 ui.status(
2195 _(
2194 _(
2196 b"nothing changed (%d missing files, see "
2195 b"nothing changed (%d missing files, see "
2197 b"'hg status')\n"
2196 b"'hg status')\n"
2198 )
2197 )
2199 % len(stat.deleted)
2198 % len(stat.deleted)
2200 )
2199 )
2201 else:
2200 else:
2202 ui.status(_(b"nothing changed\n"))
2201 ui.status(_(b"nothing changed\n"))
2203 return 1
2202 return 1
2204
2203
2205 cmdutil.commitstatus(repo, node, branch, bheads, tip, opts)
2204 cmdutil.commitstatus(repo, node, branch, bheads, tip, opts)
2206
2205
2207 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2206 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2208 status(
2207 status(
2209 ui,
2208 ui,
2210 repo,
2209 repo,
2211 modified=True,
2210 modified=True,
2212 added=True,
2211 added=True,
2213 removed=True,
2212 removed=True,
2214 deleted=True,
2213 deleted=True,
2215 unknown=True,
2214 unknown=True,
2216 subrepos=opts.get(b'subrepos'),
2215 subrepos=opts.get(b'subrepos'),
2217 )
2216 )
2218
2217
2219
2218
2220 @command(
2219 @command(
2221 b'config|showconfig|debugconfig',
2220 b'config|showconfig|debugconfig',
2222 [
2221 [
2223 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2222 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2224 # This is experimental because we need
2223 # This is experimental because we need
2225 # * reasonable behavior around aliases,
2224 # * reasonable behavior around aliases,
2226 # * decide if we display [debug] [experimental] and [devel] section par
2225 # * decide if we display [debug] [experimental] and [devel] section par
2227 # default
2226 # default
2228 # * some way to display "generic" config entry (the one matching
2227 # * some way to display "generic" config entry (the one matching
2229 # regexp,
2228 # regexp,
2230 # * proper display of the different value type
2229 # * proper display of the different value type
2231 # * a better way to handle <DYNAMIC> values (and variable types),
2230 # * a better way to handle <DYNAMIC> values (and variable types),
2232 # * maybe some type information ?
2231 # * maybe some type information ?
2233 (
2232 (
2234 b'',
2233 b'',
2235 b'exp-all-known',
2234 b'exp-all-known',
2236 None,
2235 None,
2237 _(b'show all known config option (EXPERIMENTAL)'),
2236 _(b'show all known config option (EXPERIMENTAL)'),
2238 ),
2237 ),
2239 (b'e', b'edit', None, _(b'edit user config')),
2238 (b'e', b'edit', None, _(b'edit user config')),
2240 (b'l', b'local', None, _(b'edit repository config')),
2239 (b'l', b'local', None, _(b'edit repository config')),
2241 (b'', b'source', None, _(b'show source of configuration value')),
2240 (b'', b'source', None, _(b'show source of configuration value')),
2242 (
2241 (
2243 b'',
2242 b'',
2244 b'shared',
2243 b'shared',
2245 None,
2244 None,
2246 _(b'edit shared source repository config (EXPERIMENTAL)'),
2245 _(b'edit shared source repository config (EXPERIMENTAL)'),
2247 ),
2246 ),
2248 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2247 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2249 (b'g', b'global', None, _(b'edit global config')),
2248 (b'g', b'global', None, _(b'edit global config')),
2250 ]
2249 ]
2251 + formatteropts,
2250 + formatteropts,
2252 _(b'[-u] [NAME]...'),
2251 _(b'[-u] [NAME]...'),
2253 helpcategory=command.CATEGORY_HELP,
2252 helpcategory=command.CATEGORY_HELP,
2254 optionalrepo=True,
2253 optionalrepo=True,
2255 intents={INTENT_READONLY},
2254 intents={INTENT_READONLY},
2256 )
2255 )
2257 def config(ui, repo, *values, **opts):
2256 def config(ui, repo, *values, **opts):
2258 """show combined config settings from all hgrc files
2257 """show combined config settings from all hgrc files
2259
2258
2260 With no arguments, print names and values of all config items.
2259 With no arguments, print names and values of all config items.
2261
2260
2262 With one argument of the form section.name, print just the value
2261 With one argument of the form section.name, print just the value
2263 of that config item.
2262 of that config item.
2264
2263
2265 With multiple arguments, print names and values of all config
2264 With multiple arguments, print names and values of all config
2266 items with matching section names or section.names.
2265 items with matching section names or section.names.
2267
2266
2268 With --edit, start an editor on the user-level config file. With
2267 With --edit, start an editor on the user-level config file. With
2269 --global, edit the system-wide config file. With --local, edit the
2268 --global, edit the system-wide config file. With --local, edit the
2270 repository-level config file.
2269 repository-level config file.
2271
2270
2272 With --source, the source (filename and line number) is printed
2271 With --source, the source (filename and line number) is printed
2273 for each config item.
2272 for each config item.
2274
2273
2275 See :hg:`help config` for more information about config files.
2274 See :hg:`help config` for more information about config files.
2276
2275
2277 .. container:: verbose
2276 .. container:: verbose
2278
2277
2279 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2278 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2280 This file is not shared across shares when in share-safe mode.
2279 This file is not shared across shares when in share-safe mode.
2281
2280
2282 Template:
2281 Template:
2283
2282
2284 The following keywords are supported. See also :hg:`help templates`.
2283 The following keywords are supported. See also :hg:`help templates`.
2285
2284
2286 :name: String. Config name.
2285 :name: String. Config name.
2287 :source: String. Filename and line number where the item is defined.
2286 :source: String. Filename and line number where the item is defined.
2288 :value: String. Config value.
2287 :value: String. Config value.
2289
2288
2290 The --shared flag can be used to edit the config file of shared source
2289 The --shared flag can be used to edit the config file of shared source
2291 repository. It only works when you have shared using the experimental
2290 repository. It only works when you have shared using the experimental
2292 share safe feature.
2291 share safe feature.
2293
2292
2294 Returns 0 on success, 1 if NAME does not exist.
2293 Returns 0 on success, 1 if NAME does not exist.
2295
2294
2296 """
2295 """
2297
2296
2298 opts = pycompat.byteskwargs(opts)
2297 opts = pycompat.byteskwargs(opts)
2299 editopts = (b'edit', b'local', b'global', b'shared', b'non_shared')
2298 editopts = (b'edit', b'local', b'global', b'shared', b'non_shared')
2300 if any(opts.get(o) for o in editopts):
2299 if any(opts.get(o) for o in editopts):
2301 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2300 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2302 if opts.get(b'local'):
2301 if opts.get(b'local'):
2303 if not repo:
2302 if not repo:
2304 raise error.InputError(
2303 raise error.InputError(
2305 _(b"can't use --local outside a repository")
2304 _(b"can't use --local outside a repository")
2306 )
2305 )
2307 paths = [repo.vfs.join(b'hgrc')]
2306 paths = [repo.vfs.join(b'hgrc')]
2308 elif opts.get(b'global'):
2307 elif opts.get(b'global'):
2309 paths = rcutil.systemrcpath()
2308 paths = rcutil.systemrcpath()
2310 elif opts.get(b'shared'):
2309 elif opts.get(b'shared'):
2311 if not repo.shared():
2310 if not repo.shared():
2312 raise error.InputError(
2311 raise error.InputError(
2313 _(b"repository is not shared; can't use --shared")
2312 _(b"repository is not shared; can't use --shared")
2314 )
2313 )
2315 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2314 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2316 raise error.InputError(
2315 raise error.InputError(
2317 _(
2316 _(
2318 b"share safe feature not enabled; "
2317 b"share safe feature not enabled; "
2319 b"unable to edit shared source repository config"
2318 b"unable to edit shared source repository config"
2320 )
2319 )
2321 )
2320 )
2322 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2321 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2323 elif opts.get(b'non_shared'):
2322 elif opts.get(b'non_shared'):
2324 paths = [repo.vfs.join(b'hgrc-not-shared')]
2323 paths = [repo.vfs.join(b'hgrc-not-shared')]
2325 else:
2324 else:
2326 paths = rcutil.userrcpath()
2325 paths = rcutil.userrcpath()
2327
2326
2328 for f in paths:
2327 for f in paths:
2329 if os.path.exists(f):
2328 if os.path.exists(f):
2330 break
2329 break
2331 else:
2330 else:
2332 if opts.get(b'global'):
2331 if opts.get(b'global'):
2333 samplehgrc = uimod.samplehgrcs[b'global']
2332 samplehgrc = uimod.samplehgrcs[b'global']
2334 elif opts.get(b'local'):
2333 elif opts.get(b'local'):
2335 samplehgrc = uimod.samplehgrcs[b'local']
2334 samplehgrc = uimod.samplehgrcs[b'local']
2336 else:
2335 else:
2337 samplehgrc = uimod.samplehgrcs[b'user']
2336 samplehgrc = uimod.samplehgrcs[b'user']
2338
2337
2339 f = paths[0]
2338 f = paths[0]
2340 fp = open(f, b"wb")
2339 fp = open(f, b"wb")
2341 fp.write(util.tonativeeol(samplehgrc))
2340 fp.write(util.tonativeeol(samplehgrc))
2342 fp.close()
2341 fp.close()
2343
2342
2344 editor = ui.geteditor()
2343 editor = ui.geteditor()
2345 ui.system(
2344 ui.system(
2346 b"%s \"%s\"" % (editor, f),
2345 b"%s \"%s\"" % (editor, f),
2347 onerr=error.InputError,
2346 onerr=error.InputError,
2348 errprefix=_(b"edit failed"),
2347 errprefix=_(b"edit failed"),
2349 blockedtag=b'config_edit',
2348 blockedtag=b'config_edit',
2350 )
2349 )
2351 return
2350 return
2352 ui.pager(b'config')
2351 ui.pager(b'config')
2353 fm = ui.formatter(b'config', opts)
2352 fm = ui.formatter(b'config', opts)
2354 for t, f in rcutil.rccomponents():
2353 for t, f in rcutil.rccomponents():
2355 if t == b'path':
2354 if t == b'path':
2356 ui.debug(b'read config from: %s\n' % f)
2355 ui.debug(b'read config from: %s\n' % f)
2357 elif t == b'resource':
2356 elif t == b'resource':
2358 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2357 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2359 elif t == b'items':
2358 elif t == b'items':
2360 # Don't print anything for 'items'.
2359 # Don't print anything for 'items'.
2361 pass
2360 pass
2362 else:
2361 else:
2363 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2362 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2364 untrusted = bool(opts.get(b'untrusted'))
2363 untrusted = bool(opts.get(b'untrusted'))
2365
2364
2366 selsections = selentries = []
2365 selsections = selentries = []
2367 if values:
2366 if values:
2368 selsections = [v for v in values if b'.' not in v]
2367 selsections = [v for v in values if b'.' not in v]
2369 selentries = [v for v in values if b'.' in v]
2368 selentries = [v for v in values if b'.' in v]
2370 uniquesel = len(selentries) == 1 and not selsections
2369 uniquesel = len(selentries) == 1 and not selsections
2371 selsections = set(selsections)
2370 selsections = set(selsections)
2372 selentries = set(selentries)
2371 selentries = set(selentries)
2373
2372
2374 matched = False
2373 matched = False
2375 all_known = opts[b'exp_all_known']
2374 all_known = opts[b'exp_all_known']
2376 show_source = ui.debugflag or opts.get(b'source')
2375 show_source = ui.debugflag or opts.get(b'source')
2377 entries = ui.walkconfig(untrusted=untrusted, all_known=all_known)
2376 entries = ui.walkconfig(untrusted=untrusted, all_known=all_known)
2378 for section, name, value in entries:
2377 for section, name, value in entries:
2379 source = ui.configsource(section, name, untrusted)
2378 source = ui.configsource(section, name, untrusted)
2380 value = pycompat.bytestr(value)
2379 value = pycompat.bytestr(value)
2381 defaultvalue = ui.configdefault(section, name)
2380 defaultvalue = ui.configdefault(section, name)
2382 if fm.isplain():
2381 if fm.isplain():
2383 source = source or b'none'
2382 source = source or b'none'
2384 value = value.replace(b'\n', b'\\n')
2383 value = value.replace(b'\n', b'\\n')
2385 entryname = section + b'.' + name
2384 entryname = section + b'.' + name
2386 if values and not (section in selsections or entryname in selentries):
2385 if values and not (section in selsections or entryname in selentries):
2387 continue
2386 continue
2388 fm.startitem()
2387 fm.startitem()
2389 fm.condwrite(show_source, b'source', b'%s: ', source)
2388 fm.condwrite(show_source, b'source', b'%s: ', source)
2390 if uniquesel:
2389 if uniquesel:
2391 fm.data(name=entryname)
2390 fm.data(name=entryname)
2392 fm.write(b'value', b'%s\n', value)
2391 fm.write(b'value', b'%s\n', value)
2393 else:
2392 else:
2394 fm.write(b'name value', b'%s=%s\n', entryname, value)
2393 fm.write(b'name value', b'%s=%s\n', entryname, value)
2395 if formatter.isprintable(defaultvalue):
2394 if formatter.isprintable(defaultvalue):
2396 fm.data(defaultvalue=defaultvalue)
2395 fm.data(defaultvalue=defaultvalue)
2397 elif isinstance(defaultvalue, list) and all(
2396 elif isinstance(defaultvalue, list) and all(
2398 formatter.isprintable(e) for e in defaultvalue
2397 formatter.isprintable(e) for e in defaultvalue
2399 ):
2398 ):
2400 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2399 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2401 # TODO: no idea how to process unsupported defaultvalue types
2400 # TODO: no idea how to process unsupported defaultvalue types
2402 matched = True
2401 matched = True
2403 fm.end()
2402 fm.end()
2404 if matched:
2403 if matched:
2405 return 0
2404 return 0
2406 return 1
2405 return 1
2407
2406
2408
2407
2409 @command(
2408 @command(
2410 b'continue',
2409 b'continue',
2411 dryrunopts,
2410 dryrunopts,
2412 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2411 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2413 helpbasic=True,
2412 helpbasic=True,
2414 )
2413 )
2415 def continuecmd(ui, repo, **opts):
2414 def continuecmd(ui, repo, **opts):
2416 """resumes an interrupted operation (EXPERIMENTAL)
2415 """resumes an interrupted operation (EXPERIMENTAL)
2417
2416
2418 Finishes a multistep operation like graft, histedit, rebase, merge,
2417 Finishes a multistep operation like graft, histedit, rebase, merge,
2419 and unshelve if they are in an interrupted state.
2418 and unshelve if they are in an interrupted state.
2420
2419
2421 use --dry-run/-n to dry run the command.
2420 use --dry-run/-n to dry run the command.
2422 """
2421 """
2423 dryrun = opts.get('dry_run')
2422 dryrun = opts.get('dry_run')
2424 contstate = cmdutil.getunfinishedstate(repo)
2423 contstate = cmdutil.getunfinishedstate(repo)
2425 if not contstate:
2424 if not contstate:
2426 raise error.StateError(_(b'no operation in progress'))
2425 raise error.StateError(_(b'no operation in progress'))
2427 if not contstate.continuefunc:
2426 if not contstate.continuefunc:
2428 raise error.StateError(
2427 raise error.StateError(
2429 (
2428 (
2430 _(b"%s in progress but does not support 'hg continue'")
2429 _(b"%s in progress but does not support 'hg continue'")
2431 % (contstate._opname)
2430 % (contstate._opname)
2432 ),
2431 ),
2433 hint=contstate.continuemsg(),
2432 hint=contstate.continuemsg(),
2434 )
2433 )
2435 if dryrun:
2434 if dryrun:
2436 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2435 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2437 return
2436 return
2438 return contstate.continuefunc(ui, repo)
2437 return contstate.continuefunc(ui, repo)
2439
2438
2440
2439
2441 @command(
2440 @command(
2442 b'copy|cp',
2441 b'copy|cp',
2443 [
2442 [
2444 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2443 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2445 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2444 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2446 (
2445 (
2447 b'',
2446 b'',
2448 b'at-rev',
2447 b'at-rev',
2449 b'',
2448 b'',
2450 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2449 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2451 _(b'REV'),
2450 _(b'REV'),
2452 ),
2451 ),
2453 (
2452 (
2454 b'f',
2453 b'f',
2455 b'force',
2454 b'force',
2456 None,
2455 None,
2457 _(b'forcibly copy over an existing managed file'),
2456 _(b'forcibly copy over an existing managed file'),
2458 ),
2457 ),
2459 ]
2458 ]
2460 + walkopts
2459 + walkopts
2461 + dryrunopts,
2460 + dryrunopts,
2462 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2461 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2463 helpcategory=command.CATEGORY_FILE_CONTENTS,
2462 helpcategory=command.CATEGORY_FILE_CONTENTS,
2464 )
2463 )
2465 def copy(ui, repo, *pats, **opts):
2464 def copy(ui, repo, *pats, **opts):
2466 """mark files as copied for the next commit
2465 """mark files as copied for the next commit
2467
2466
2468 Mark dest as having copies of source files. If dest is a
2467 Mark dest as having copies of source files. If dest is a
2469 directory, copies are put in that directory. If dest is a file,
2468 directory, copies are put in that directory. If dest is a file,
2470 the source must be a single file.
2469 the source must be a single file.
2471
2470
2472 By default, this command copies the contents of files as they
2471 By default, this command copies the contents of files as they
2473 exist in the working directory. If invoked with -A/--after, the
2472 exist in the working directory. If invoked with -A/--after, the
2474 operation is recorded, but no copying is performed.
2473 operation is recorded, but no copying is performed.
2475
2474
2476 To undo marking a destination file as copied, use --forget. With that
2475 To undo marking a destination file as copied, use --forget. With that
2477 option, all given (positional) arguments are unmarked as copies. The
2476 option, all given (positional) arguments are unmarked as copies. The
2478 destination file(s) will be left in place (still tracked). Note that
2477 destination file(s) will be left in place (still tracked). Note that
2479 :hg:`copy --forget` behaves the same way as :hg:`rename --forget`.
2478 :hg:`copy --forget` behaves the same way as :hg:`rename --forget`.
2480
2479
2481 This command takes effect with the next commit by default.
2480 This command takes effect with the next commit by default.
2482
2481
2483 Returns 0 on success, 1 if errors are encountered.
2482 Returns 0 on success, 1 if errors are encountered.
2484 """
2483 """
2485 opts = pycompat.byteskwargs(opts)
2484 opts = pycompat.byteskwargs(opts)
2486 with repo.wlock():
2485 with repo.wlock():
2487 return cmdutil.copy(ui, repo, pats, opts)
2486 return cmdutil.copy(ui, repo, pats, opts)
2488
2487
2489
2488
2490 @command(
2489 @command(
2491 b'debugcommands',
2490 b'debugcommands',
2492 [],
2491 [],
2493 _(b'[COMMAND]'),
2492 _(b'[COMMAND]'),
2494 helpcategory=command.CATEGORY_HELP,
2493 helpcategory=command.CATEGORY_HELP,
2495 norepo=True,
2494 norepo=True,
2496 )
2495 )
2497 def debugcommands(ui, cmd=b'', *args):
2496 def debugcommands(ui, cmd=b'', *args):
2498 """list all available commands and options"""
2497 """list all available commands and options"""
2499 for cmd, vals in sorted(table.items()):
2498 for cmd, vals in sorted(table.items()):
2500 cmd = cmd.split(b'|')[0]
2499 cmd = cmd.split(b'|')[0]
2501 opts = b', '.join([i[1] for i in vals[1]])
2500 opts = b', '.join([i[1] for i in vals[1]])
2502 ui.write(b'%s: %s\n' % (cmd, opts))
2501 ui.write(b'%s: %s\n' % (cmd, opts))
2503
2502
2504
2503
2505 @command(
2504 @command(
2506 b'debugcomplete',
2505 b'debugcomplete',
2507 [(b'o', b'options', None, _(b'show the command options'))],
2506 [(b'o', b'options', None, _(b'show the command options'))],
2508 _(b'[-o] CMD'),
2507 _(b'[-o] CMD'),
2509 helpcategory=command.CATEGORY_HELP,
2508 helpcategory=command.CATEGORY_HELP,
2510 norepo=True,
2509 norepo=True,
2511 )
2510 )
2512 def debugcomplete(ui, cmd=b'', **opts):
2511 def debugcomplete(ui, cmd=b'', **opts):
2513 """returns the completion list associated with the given command"""
2512 """returns the completion list associated with the given command"""
2514
2513
2515 if opts.get('options'):
2514 if opts.get('options'):
2516 options = []
2515 options = []
2517 otables = [globalopts]
2516 otables = [globalopts]
2518 if cmd:
2517 if cmd:
2519 aliases, entry = cmdutil.findcmd(cmd, table, False)
2518 aliases, entry = cmdutil.findcmd(cmd, table, False)
2520 otables.append(entry[1])
2519 otables.append(entry[1])
2521 for t in otables:
2520 for t in otables:
2522 for o in t:
2521 for o in t:
2523 if b"(DEPRECATED)" in o[3]:
2522 if b"(DEPRECATED)" in o[3]:
2524 continue
2523 continue
2525 if o[0]:
2524 if o[0]:
2526 options.append(b'-%s' % o[0])
2525 options.append(b'-%s' % o[0])
2527 options.append(b'--%s' % o[1])
2526 options.append(b'--%s' % o[1])
2528 ui.write(b"%s\n" % b"\n".join(options))
2527 ui.write(b"%s\n" % b"\n".join(options))
2529 return
2528 return
2530
2529
2531 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2530 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2532 if ui.verbose:
2531 if ui.verbose:
2533 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2532 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2534 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2533 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2535
2534
2536
2535
2537 @command(
2536 @command(
2538 b'diff',
2537 b'diff',
2539 [
2538 [
2540 (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2539 (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2541 (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2540 (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2542 (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2541 (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2543 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2542 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2544 ]
2543 ]
2545 + diffopts
2544 + diffopts
2546 + diffopts2
2545 + diffopts2
2547 + walkopts
2546 + walkopts
2548 + subrepoopts,
2547 + subrepoopts,
2549 _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2548 _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2550 helpcategory=command.CATEGORY_FILE_CONTENTS,
2549 helpcategory=command.CATEGORY_FILE_CONTENTS,
2551 helpbasic=True,
2550 helpbasic=True,
2552 inferrepo=True,
2551 inferrepo=True,
2553 intents={INTENT_READONLY},
2552 intents={INTENT_READONLY},
2554 )
2553 )
2555 def diff(ui, repo, *pats, **opts):
2554 def diff(ui, repo, *pats, **opts):
2556 """diff repository (or selected files)
2555 """diff repository (or selected files)
2557
2556
2558 Show differences between revisions for the specified files.
2557 Show differences between revisions for the specified files.
2559
2558
2560 Differences between files are shown using the unified diff format.
2559 Differences between files are shown using the unified diff format.
2561
2560
2562 .. note::
2561 .. note::
2563
2562
2564 :hg:`diff` may generate unexpected results for merges, as it will
2563 :hg:`diff` may generate unexpected results for merges, as it will
2565 default to comparing against the working directory's first
2564 default to comparing against the working directory's first
2566 parent changeset if no revisions are specified. To diff against the
2565 parent changeset if no revisions are specified. To diff against the
2567 conflict regions, you can use `--config diff.merge=yes`.
2566 conflict regions, you can use `--config diff.merge=yes`.
2568
2567
2569 By default, the working directory files are compared to its first parent. To
2568 By default, the working directory files are compared to its first parent. To
2570 see the differences from another revision, use --from. To see the difference
2569 see the differences from another revision, use --from. To see the difference
2571 to another revision, use --to. For example, :hg:`diff --from .^` will show
2570 to another revision, use --to. For example, :hg:`diff --from .^` will show
2572 the differences from the working copy's grandparent to the working copy,
2571 the differences from the working copy's grandparent to the working copy,
2573 :hg:`diff --to .` will show the diff from the working copy to its parent
2572 :hg:`diff --to .` will show the diff from the working copy to its parent
2574 (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2573 (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2575 show the diff between those two revisions.
2574 show the diff between those two revisions.
2576
2575
2577 Alternatively you can specify -c/--change with a revision to see the changes
2576 Alternatively you can specify -c/--change with a revision to see the changes
2578 in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2577 in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2579 equivalent to :hg:`diff --from 42^ --to 42`)
2578 equivalent to :hg:`diff --from 42^ --to 42`)
2580
2579
2581 Without the -a/--text option, diff will avoid generating diffs of
2580 Without the -a/--text option, diff will avoid generating diffs of
2582 files it detects as binary. With -a, diff will generate a diff
2581 files it detects as binary. With -a, diff will generate a diff
2583 anyway, probably with undesirable results.
2582 anyway, probably with undesirable results.
2584
2583
2585 Use the -g/--git option to generate diffs in the git extended diff
2584 Use the -g/--git option to generate diffs in the git extended diff
2586 format. For more information, read :hg:`help diffs`.
2585 format. For more information, read :hg:`help diffs`.
2587
2586
2588 .. container:: verbose
2587 .. container:: verbose
2589
2588
2590 Examples:
2589 Examples:
2591
2590
2592 - compare a file in the current working directory to its parent::
2591 - compare a file in the current working directory to its parent::
2593
2592
2594 hg diff foo.c
2593 hg diff foo.c
2595
2594
2596 - compare two historical versions of a directory, with rename info::
2595 - compare two historical versions of a directory, with rename info::
2597
2596
2598 hg diff --git --from 1.0 --to 1.2 lib/
2597 hg diff --git --from 1.0 --to 1.2 lib/
2599
2598
2600 - get change stats relative to the last change on some date::
2599 - get change stats relative to the last change on some date::
2601
2600
2602 hg diff --stat --from "date('may 2')"
2601 hg diff --stat --from "date('may 2')"
2603
2602
2604 - diff all newly-added files that contain a keyword::
2603 - diff all newly-added files that contain a keyword::
2605
2604
2606 hg diff "set:added() and grep(GNU)"
2605 hg diff "set:added() and grep(GNU)"
2607
2606
2608 - compare a revision and its parents::
2607 - compare a revision and its parents::
2609
2608
2610 hg diff -c 9353 # compare against first parent
2609 hg diff -c 9353 # compare against first parent
2611 hg diff --from 9353^ --to 9353 # same using revset syntax
2610 hg diff --from 9353^ --to 9353 # same using revset syntax
2612 hg diff --from 9353^2 --to 9353 # compare against the second parent
2611 hg diff --from 9353^2 --to 9353 # compare against the second parent
2613
2612
2614 Returns 0 on success.
2613 Returns 0 on success.
2615 """
2614 """
2616
2615
2617 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2616 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2618 opts = pycompat.byteskwargs(opts)
2617 opts = pycompat.byteskwargs(opts)
2619 revs = opts.get(b'rev')
2618 revs = opts.get(b'rev')
2620 change = opts.get(b'change')
2619 change = opts.get(b'change')
2621 from_rev = opts.get(b'from')
2620 from_rev = opts.get(b'from')
2622 to_rev = opts.get(b'to')
2621 to_rev = opts.get(b'to')
2623 stat = opts.get(b'stat')
2622 stat = opts.get(b'stat')
2624 reverse = opts.get(b'reverse')
2623 reverse = opts.get(b'reverse')
2625
2624
2626 cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2625 cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2627 cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2626 cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2628 if change:
2627 if change:
2629 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2628 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2630 ctx2 = logcmdutil.revsingle(repo, change, None)
2629 ctx2 = logcmdutil.revsingle(repo, change, None)
2631 ctx1 = logcmdutil.diff_parent(ctx2)
2630 ctx1 = logcmdutil.diff_parent(ctx2)
2632 elif from_rev or to_rev:
2631 elif from_rev or to_rev:
2633 repo = scmutil.unhidehashlikerevs(
2632 repo = scmutil.unhidehashlikerevs(
2634 repo, [from_rev] + [to_rev], b'nowarn'
2633 repo, [from_rev] + [to_rev], b'nowarn'
2635 )
2634 )
2636 ctx1 = logcmdutil.revsingle(repo, from_rev, None)
2635 ctx1 = logcmdutil.revsingle(repo, from_rev, None)
2637 ctx2 = logcmdutil.revsingle(repo, to_rev, None)
2636 ctx2 = logcmdutil.revsingle(repo, to_rev, None)
2638 else:
2637 else:
2639 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2638 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2640 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
2639 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
2641
2640
2642 if reverse:
2641 if reverse:
2643 ctxleft = ctx2
2642 ctxleft = ctx2
2644 ctxright = ctx1
2643 ctxright = ctx1
2645 else:
2644 else:
2646 ctxleft = ctx1
2645 ctxleft = ctx1
2647 ctxright = ctx2
2646 ctxright = ctx2
2648
2647
2649 diffopts = patch.diffallopts(ui, opts)
2648 diffopts = patch.diffallopts(ui, opts)
2650 m = scmutil.match(ctx2, pats, opts)
2649 m = scmutil.match(ctx2, pats, opts)
2651 m = repo.narrowmatch(m)
2650 m = repo.narrowmatch(m)
2652 ui.pager(b'diff')
2651 ui.pager(b'diff')
2653 logcmdutil.diffordiffstat(
2652 logcmdutil.diffordiffstat(
2654 ui,
2653 ui,
2655 repo,
2654 repo,
2656 diffopts,
2655 diffopts,
2657 ctxleft,
2656 ctxleft,
2658 ctxright,
2657 ctxright,
2659 m,
2658 m,
2660 stat=stat,
2659 stat=stat,
2661 listsubrepos=opts.get(b'subrepos'),
2660 listsubrepos=opts.get(b'subrepos'),
2662 root=opts.get(b'root'),
2661 root=opts.get(b'root'),
2663 )
2662 )
2664
2663
2665
2664
2666 @command(
2665 @command(
2667 b'export',
2666 b'export',
2668 [
2667 [
2669 (
2668 (
2670 b'B',
2669 b'B',
2671 b'bookmark',
2670 b'bookmark',
2672 b'',
2671 b'',
2673 _(b'export changes only reachable by given bookmark'),
2672 _(b'export changes only reachable by given bookmark'),
2674 _(b'BOOKMARK'),
2673 _(b'BOOKMARK'),
2675 ),
2674 ),
2676 (
2675 (
2677 b'o',
2676 b'o',
2678 b'output',
2677 b'output',
2679 b'',
2678 b'',
2680 _(b'print output to file with formatted name'),
2679 _(b'print output to file with formatted name'),
2681 _(b'FORMAT'),
2680 _(b'FORMAT'),
2682 ),
2681 ),
2683 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2682 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2684 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2683 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2685 ]
2684 ]
2686 + diffopts
2685 + diffopts
2687 + formatteropts,
2686 + formatteropts,
2688 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2687 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2689 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2688 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2690 helpbasic=True,
2689 helpbasic=True,
2691 intents={INTENT_READONLY},
2690 intents={INTENT_READONLY},
2692 )
2691 )
2693 def export(ui, repo, *changesets, **opts):
2692 def export(ui, repo, *changesets, **opts):
2694 """dump the header and diffs for one or more changesets
2693 """dump the header and diffs for one or more changesets
2695
2694
2696 Print the changeset header and diffs for one or more revisions.
2695 Print the changeset header and diffs for one or more revisions.
2697 If no revision is given, the parent of the working directory is used.
2696 If no revision is given, the parent of the working directory is used.
2698
2697
2699 The information shown in the changeset header is: author, date,
2698 The information shown in the changeset header is: author, date,
2700 branch name (if non-default), changeset hash, parent(s) and commit
2699 branch name (if non-default), changeset hash, parent(s) and commit
2701 comment.
2700 comment.
2702
2701
2703 .. note::
2702 .. note::
2704
2703
2705 :hg:`export` may generate unexpected diff output for merge
2704 :hg:`export` may generate unexpected diff output for merge
2706 changesets, as it will compare the merge changeset against its
2705 changesets, as it will compare the merge changeset against its
2707 first parent only.
2706 first parent only.
2708
2707
2709 Output may be to a file, in which case the name of the file is
2708 Output may be to a file, in which case the name of the file is
2710 given using a template string. See :hg:`help templates`. In addition
2709 given using a template string. See :hg:`help templates`. In addition
2711 to the common template keywords, the following formatting rules are
2710 to the common template keywords, the following formatting rules are
2712 supported:
2711 supported:
2713
2712
2714 :``%%``: literal "%" character
2713 :``%%``: literal "%" character
2715 :``%H``: changeset hash (40 hexadecimal digits)
2714 :``%H``: changeset hash (40 hexadecimal digits)
2716 :``%N``: number of patches being generated
2715 :``%N``: number of patches being generated
2717 :``%R``: changeset revision number
2716 :``%R``: changeset revision number
2718 :``%b``: basename of the exporting repository
2717 :``%b``: basename of the exporting repository
2719 :``%h``: short-form changeset hash (12 hexadecimal digits)
2718 :``%h``: short-form changeset hash (12 hexadecimal digits)
2720 :``%m``: first line of the commit message (only alphanumeric characters)
2719 :``%m``: first line of the commit message (only alphanumeric characters)
2721 :``%n``: zero-padded sequence number, starting at 1
2720 :``%n``: zero-padded sequence number, starting at 1
2722 :``%r``: zero-padded changeset revision number
2721 :``%r``: zero-padded changeset revision number
2723 :``\\``: literal "\\" character
2722 :``\\``: literal "\\" character
2724
2723
2725 Without the -a/--text option, export will avoid generating diffs
2724 Without the -a/--text option, export will avoid generating diffs
2726 of files it detects as binary. With -a, export will generate a
2725 of files it detects as binary. With -a, export will generate a
2727 diff anyway, probably with undesirable results.
2726 diff anyway, probably with undesirable results.
2728
2727
2729 With -B/--bookmark changesets reachable by the given bookmark are
2728 With -B/--bookmark changesets reachable by the given bookmark are
2730 selected.
2729 selected.
2731
2730
2732 Use the -g/--git option to generate diffs in the git extended diff
2731 Use the -g/--git option to generate diffs in the git extended diff
2733 format. See :hg:`help diffs` for more information.
2732 format. See :hg:`help diffs` for more information.
2734
2733
2735 With the --switch-parent option, the diff will be against the
2734 With the --switch-parent option, the diff will be against the
2736 second parent. It can be useful to review a merge.
2735 second parent. It can be useful to review a merge.
2737
2736
2738 .. container:: verbose
2737 .. container:: verbose
2739
2738
2740 Template:
2739 Template:
2741
2740
2742 The following keywords are supported in addition to the common template
2741 The following keywords are supported in addition to the common template
2743 keywords and functions. See also :hg:`help templates`.
2742 keywords and functions. See also :hg:`help templates`.
2744
2743
2745 :diff: String. Diff content.
2744 :diff: String. Diff content.
2746 :parents: List of strings. Parent nodes of the changeset.
2745 :parents: List of strings. Parent nodes of the changeset.
2747
2746
2748 Examples:
2747 Examples:
2749
2748
2750 - use export and import to transplant a bugfix to the current
2749 - use export and import to transplant a bugfix to the current
2751 branch::
2750 branch::
2752
2751
2753 hg export -r 9353 | hg import -
2752 hg export -r 9353 | hg import -
2754
2753
2755 - export all the changesets between two revisions to a file with
2754 - export all the changesets between two revisions to a file with
2756 rename information::
2755 rename information::
2757
2756
2758 hg export --git -r 123:150 > changes.txt
2757 hg export --git -r 123:150 > changes.txt
2759
2758
2760 - split outgoing changes into a series of patches with
2759 - split outgoing changes into a series of patches with
2761 descriptive names::
2760 descriptive names::
2762
2761
2763 hg export -r "outgoing()" -o "%n-%m.patch"
2762 hg export -r "outgoing()" -o "%n-%m.patch"
2764
2763
2765 Returns 0 on success.
2764 Returns 0 on success.
2766 """
2765 """
2767 opts = pycompat.byteskwargs(opts)
2766 opts = pycompat.byteskwargs(opts)
2768 bookmark = opts.get(b'bookmark')
2767 bookmark = opts.get(b'bookmark')
2769 changesets += tuple(opts.get(b'rev', []))
2768 changesets += tuple(opts.get(b'rev', []))
2770
2769
2771 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2770 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2772
2771
2773 if bookmark:
2772 if bookmark:
2774 if bookmark not in repo._bookmarks:
2773 if bookmark not in repo._bookmarks:
2775 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2774 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2776
2775
2777 revs = scmutil.bookmarkrevs(repo, bookmark)
2776 revs = scmutil.bookmarkrevs(repo, bookmark)
2778 else:
2777 else:
2779 if not changesets:
2778 if not changesets:
2780 changesets = [b'.']
2779 changesets = [b'.']
2781
2780
2782 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2781 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2783 revs = logcmdutil.revrange(repo, changesets)
2782 revs = logcmdutil.revrange(repo, changesets)
2784
2783
2785 if not revs:
2784 if not revs:
2786 raise error.InputError(_(b"export requires at least one changeset"))
2785 raise error.InputError(_(b"export requires at least one changeset"))
2787 if len(revs) > 1:
2786 if len(revs) > 1:
2788 ui.note(_(b'exporting patches:\n'))
2787 ui.note(_(b'exporting patches:\n'))
2789 else:
2788 else:
2790 ui.note(_(b'exporting patch:\n'))
2789 ui.note(_(b'exporting patch:\n'))
2791
2790
2792 fntemplate = opts.get(b'output')
2791 fntemplate = opts.get(b'output')
2793 if cmdutil.isstdiofilename(fntemplate):
2792 if cmdutil.isstdiofilename(fntemplate):
2794 fntemplate = b''
2793 fntemplate = b''
2795
2794
2796 if fntemplate:
2795 if fntemplate:
2797 fm = formatter.nullformatter(ui, b'export', opts)
2796 fm = formatter.nullformatter(ui, b'export', opts)
2798 else:
2797 else:
2799 ui.pager(b'export')
2798 ui.pager(b'export')
2800 fm = ui.formatter(b'export', opts)
2799 fm = ui.formatter(b'export', opts)
2801 with fm:
2800 with fm:
2802 cmdutil.export(
2801 cmdutil.export(
2803 repo,
2802 repo,
2804 revs,
2803 revs,
2805 fm,
2804 fm,
2806 fntemplate=fntemplate,
2805 fntemplate=fntemplate,
2807 switch_parent=opts.get(b'switch_parent'),
2806 switch_parent=opts.get(b'switch_parent'),
2808 opts=patch.diffallopts(ui, opts),
2807 opts=patch.diffallopts(ui, opts),
2809 )
2808 )
2810
2809
2811
2810
2812 @command(
2811 @command(
2813 b'files',
2812 b'files',
2814 [
2813 [
2815 (
2814 (
2816 b'r',
2815 b'r',
2817 b'rev',
2816 b'rev',
2818 b'',
2817 b'',
2819 _(b'search the repository as it is in REV'),
2818 _(b'search the repository as it is in REV'),
2820 _(b'REV'),
2819 _(b'REV'),
2821 ),
2820 ),
2822 (
2821 (
2823 b'0',
2822 b'0',
2824 b'print0',
2823 b'print0',
2825 None,
2824 None,
2826 _(b'end filenames with NUL, for use with xargs'),
2825 _(b'end filenames with NUL, for use with xargs'),
2827 ),
2826 ),
2828 ]
2827 ]
2829 + walkopts
2828 + walkopts
2830 + formatteropts
2829 + formatteropts
2831 + subrepoopts,
2830 + subrepoopts,
2832 _(b'[OPTION]... [FILE]...'),
2831 _(b'[OPTION]... [FILE]...'),
2833 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2832 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2834 intents={INTENT_READONLY},
2833 intents={INTENT_READONLY},
2835 )
2834 )
2836 def files(ui, repo, *pats, **opts):
2835 def files(ui, repo, *pats, **opts):
2837 """list tracked files
2836 """list tracked files
2838
2837
2839 Print files under Mercurial control in the working directory or
2838 Print files under Mercurial control in the working directory or
2840 specified revision for given files (excluding removed files).
2839 specified revision for given files (excluding removed files).
2841 Files can be specified as filenames or filesets.
2840 Files can be specified as filenames or filesets.
2842
2841
2843 If no files are given to match, this command prints the names
2842 If no files are given to match, this command prints the names
2844 of all files under Mercurial control.
2843 of all files under Mercurial control.
2845
2844
2846 .. container:: verbose
2845 .. container:: verbose
2847
2846
2848 Template:
2847 Template:
2849
2848
2850 The following keywords are supported in addition to the common template
2849 The following keywords are supported in addition to the common template
2851 keywords and functions. See also :hg:`help templates`.
2850 keywords and functions. See also :hg:`help templates`.
2852
2851
2853 :flags: String. Character denoting file's symlink and executable bits.
2852 :flags: String. Character denoting file's symlink and executable bits.
2854 :path: String. Repository-absolute path of the file.
2853 :path: String. Repository-absolute path of the file.
2855 :size: Integer. Size of the file in bytes.
2854 :size: Integer. Size of the file in bytes.
2856
2855
2857 Examples:
2856 Examples:
2858
2857
2859 - list all files under the current directory::
2858 - list all files under the current directory::
2860
2859
2861 hg files .
2860 hg files .
2862
2861
2863 - shows sizes and flags for current revision::
2862 - shows sizes and flags for current revision::
2864
2863
2865 hg files -vr .
2864 hg files -vr .
2866
2865
2867 - list all files named README::
2866 - list all files named README::
2868
2867
2869 hg files -I "**/README"
2868 hg files -I "**/README"
2870
2869
2871 - list all binary files::
2870 - list all binary files::
2872
2871
2873 hg files "set:binary()"
2872 hg files "set:binary()"
2874
2873
2875 - find files containing a regular expression::
2874 - find files containing a regular expression::
2876
2875
2877 hg files "set:grep('bob')"
2876 hg files "set:grep('bob')"
2878
2877
2879 - search tracked file contents with xargs and grep::
2878 - search tracked file contents with xargs and grep::
2880
2879
2881 hg files -0 | xargs -0 grep foo
2880 hg files -0 | xargs -0 grep foo
2882
2881
2883 See :hg:`help patterns` and :hg:`help filesets` for more information
2882 See :hg:`help patterns` and :hg:`help filesets` for more information
2884 on specifying file patterns.
2883 on specifying file patterns.
2885
2884
2886 Returns 0 if a match is found, 1 otherwise.
2885 Returns 0 if a match is found, 1 otherwise.
2887
2886
2888 """
2887 """
2889
2888
2890 opts = pycompat.byteskwargs(opts)
2889 opts = pycompat.byteskwargs(opts)
2891 rev = opts.get(b'rev')
2890 rev = opts.get(b'rev')
2892 if rev:
2891 if rev:
2893 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2892 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2894 ctx = logcmdutil.revsingle(repo, rev, None)
2893 ctx = logcmdutil.revsingle(repo, rev, None)
2895
2894
2896 end = b'\n'
2895 end = b'\n'
2897 if opts.get(b'print0'):
2896 if opts.get(b'print0'):
2898 end = b'\0'
2897 end = b'\0'
2899 fmt = b'%s' + end
2898 fmt = b'%s' + end
2900
2899
2901 m = scmutil.match(ctx, pats, opts)
2900 m = scmutil.match(ctx, pats, opts)
2902 ui.pager(b'files')
2901 ui.pager(b'files')
2903 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2902 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2904 with ui.formatter(b'files', opts) as fm:
2903 with ui.formatter(b'files', opts) as fm:
2905 return cmdutil.files(
2904 return cmdutil.files(
2906 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2905 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2907 )
2906 )
2908
2907
2909
2908
2910 @command(
2909 @command(
2911 b'forget',
2910 b'forget',
2912 [
2911 [
2913 (b'i', b'interactive', None, _(b'use interactive mode')),
2912 (b'i', b'interactive', None, _(b'use interactive mode')),
2914 ]
2913 ]
2915 + walkopts
2914 + walkopts
2916 + dryrunopts,
2915 + dryrunopts,
2917 _(b'[OPTION]... FILE...'),
2916 _(b'[OPTION]... FILE...'),
2918 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2917 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2919 helpbasic=True,
2918 helpbasic=True,
2920 inferrepo=True,
2919 inferrepo=True,
2921 )
2920 )
2922 def forget(ui, repo, *pats, **opts):
2921 def forget(ui, repo, *pats, **opts):
2923 """forget the specified files on the next commit
2922 """forget the specified files on the next commit
2924
2923
2925 Mark the specified files so they will no longer be tracked
2924 Mark the specified files so they will no longer be tracked
2926 after the next commit.
2925 after the next commit.
2927
2926
2928 This only removes files from the current branch, not from the
2927 This only removes files from the current branch, not from the
2929 entire project history, and it does not delete them from the
2928 entire project history, and it does not delete them from the
2930 working directory.
2929 working directory.
2931
2930
2932 To delete the file from the working directory, see :hg:`remove`.
2931 To delete the file from the working directory, see :hg:`remove`.
2933
2932
2934 To undo a forget before the next commit, see :hg:`add`.
2933 To undo a forget before the next commit, see :hg:`add`.
2935
2934
2936 .. container:: verbose
2935 .. container:: verbose
2937
2936
2938 Examples:
2937 Examples:
2939
2938
2940 - forget newly-added binary files::
2939 - forget newly-added binary files::
2941
2940
2942 hg forget "set:added() and binary()"
2941 hg forget "set:added() and binary()"
2943
2942
2944 - forget files that would be excluded by .hgignore::
2943 - forget files that would be excluded by .hgignore::
2945
2944
2946 hg forget "set:hgignore()"
2945 hg forget "set:hgignore()"
2947
2946
2948 Returns 0 on success.
2947 Returns 0 on success.
2949 """
2948 """
2950
2949
2951 opts = pycompat.byteskwargs(opts)
2950 opts = pycompat.byteskwargs(opts)
2952 if not pats:
2951 if not pats:
2953 raise error.InputError(_(b'no files specified'))
2952 raise error.InputError(_(b'no files specified'))
2954
2953
2955 m = scmutil.match(repo[None], pats, opts)
2954 m = scmutil.match(repo[None], pats, opts)
2956 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2955 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2957 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2956 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2958 rejected = cmdutil.forget(
2957 rejected = cmdutil.forget(
2959 ui,
2958 ui,
2960 repo,
2959 repo,
2961 m,
2960 m,
2962 prefix=b"",
2961 prefix=b"",
2963 uipathfn=uipathfn,
2962 uipathfn=uipathfn,
2964 explicitonly=False,
2963 explicitonly=False,
2965 dryrun=dryrun,
2964 dryrun=dryrun,
2966 interactive=interactive,
2965 interactive=interactive,
2967 )[0]
2966 )[0]
2968 return rejected and 1 or 0
2967 return rejected and 1 or 0
2969
2968
2970
2969
2971 @command(
2970 @command(
2972 b'graft',
2971 b'graft',
2973 [
2972 [
2974 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2973 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2975 (
2974 (
2976 b'',
2975 b'',
2977 b'base',
2976 b'base',
2978 b'',
2977 b'',
2979 _(b'base revision when doing the graft merge (ADVANCED)'),
2978 _(b'base revision when doing the graft merge (ADVANCED)'),
2980 _(b'REV'),
2979 _(b'REV'),
2981 ),
2980 ),
2982 (b'c', b'continue', False, _(b'resume interrupted graft')),
2981 (b'c', b'continue', False, _(b'resume interrupted graft')),
2983 (b'', b'stop', False, _(b'stop interrupted graft')),
2982 (b'', b'stop', False, _(b'stop interrupted graft')),
2984 (b'', b'abort', False, _(b'abort interrupted graft')),
2983 (b'', b'abort', False, _(b'abort interrupted graft')),
2985 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2984 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2986 (b'', b'log', None, _(b'append graft info to log message')),
2985 (b'', b'log', None, _(b'append graft info to log message')),
2987 (
2986 (
2988 b'',
2987 b'',
2989 b'no-commit',
2988 b'no-commit',
2990 None,
2989 None,
2991 _(b"don't commit, just apply the changes in working directory"),
2990 _(b"don't commit, just apply the changes in working directory"),
2992 ),
2991 ),
2993 (b'f', b'force', False, _(b'force graft')),
2992 (b'f', b'force', False, _(b'force graft')),
2994 (
2993 (
2995 b'D',
2994 b'D',
2996 b'currentdate',
2995 b'currentdate',
2997 False,
2996 False,
2998 _(b'record the current date as commit date'),
2997 _(b'record the current date as commit date'),
2999 ),
2998 ),
3000 (
2999 (
3001 b'U',
3000 b'U',
3002 b'currentuser',
3001 b'currentuser',
3003 False,
3002 False,
3004 _(b'record the current user as committer'),
3003 _(b'record the current user as committer'),
3005 ),
3004 ),
3006 ]
3005 ]
3007 + commitopts2
3006 + commitopts2
3008 + mergetoolopts
3007 + mergetoolopts
3009 + dryrunopts,
3008 + dryrunopts,
3010 _(b'[OPTION]... [-r REV]... REV...'),
3009 _(b'[OPTION]... [-r REV]... REV...'),
3011 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
3010 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
3012 )
3011 )
3013 def graft(ui, repo, *revs, **opts):
3012 def graft(ui, repo, *revs, **opts):
3014 """copy changes from other branches onto the current branch
3013 """copy changes from other branches onto the current branch
3015
3014
3016 This command uses Mercurial's merge logic to copy individual
3015 This command uses Mercurial's merge logic to copy individual
3017 changes from other branches without merging branches in the
3016 changes from other branches without merging branches in the
3018 history graph. This is sometimes known as 'backporting' or
3017 history graph. This is sometimes known as 'backporting' or
3019 'cherry-picking'. By default, graft will copy user, date, and
3018 'cherry-picking'. By default, graft will copy user, date, and
3020 description from the source changesets.
3019 description from the source changesets.
3021
3020
3022 Changesets that are ancestors of the current revision, that have
3021 Changesets that are ancestors of the current revision, that have
3023 already been grafted, or that are merges will be skipped.
3022 already been grafted, or that are merges will be skipped.
3024
3023
3025 If --log is specified, log messages will have a comment appended
3024 If --log is specified, log messages will have a comment appended
3026 of the form::
3025 of the form::
3027
3026
3028 (grafted from CHANGESETHASH)
3027 (grafted from CHANGESETHASH)
3029
3028
3030 If --force is specified, revisions will be grafted even if they
3029 If --force is specified, revisions will be grafted even if they
3031 are already ancestors of, or have been grafted to, the destination.
3030 are already ancestors of, or have been grafted to, the destination.
3032 This is useful when the revisions have since been backed out.
3031 This is useful when the revisions have since been backed out.
3033
3032
3034 If a graft merge results in conflicts, the graft process is
3033 If a graft merge results in conflicts, the graft process is
3035 interrupted so that the current merge can be manually resolved.
3034 interrupted so that the current merge can be manually resolved.
3036 Once all conflicts are addressed, the graft process can be
3035 Once all conflicts are addressed, the graft process can be
3037 continued with the -c/--continue option.
3036 continued with the -c/--continue option.
3038
3037
3039 The -c/--continue option reapplies all the earlier options.
3038 The -c/--continue option reapplies all the earlier options.
3040
3039
3041 .. container:: verbose
3040 .. container:: verbose
3042
3041
3043 The --base option exposes more of how graft internally uses merge with a
3042 The --base option exposes more of how graft internally uses merge with a
3044 custom base revision. --base can be used to specify another ancestor than
3043 custom base revision. --base can be used to specify another ancestor than
3045 the first and only parent.
3044 the first and only parent.
3046
3045
3047 The command::
3046 The command::
3048
3047
3049 hg graft -r 345 --base 234
3048 hg graft -r 345 --base 234
3050
3049
3051 is thus pretty much the same as::
3050 is thus pretty much the same as::
3052
3051
3053 hg diff --from 234 --to 345 | hg import
3052 hg diff --from 234 --to 345 | hg import
3054
3053
3055 but using merge to resolve conflicts and track moved files.
3054 but using merge to resolve conflicts and track moved files.
3056
3055
3057 The result of a merge can thus be backported as a single commit by
3056 The result of a merge can thus be backported as a single commit by
3058 specifying one of the merge parents as base, and thus effectively
3057 specifying one of the merge parents as base, and thus effectively
3059 grafting the changes from the other side.
3058 grafting the changes from the other side.
3060
3059
3061 It is also possible to collapse multiple changesets and clean up history
3060 It is also possible to collapse multiple changesets and clean up history
3062 by specifying another ancestor as base, much like rebase --collapse
3061 by specifying another ancestor as base, much like rebase --collapse
3063 --keep.
3062 --keep.
3064
3063
3065 The commit message can be tweaked after the fact using commit --amend .
3064 The commit message can be tweaked after the fact using commit --amend .
3066
3065
3067 For using non-ancestors as the base to backout changes, see the backout
3066 For using non-ancestors as the base to backout changes, see the backout
3068 command and the hidden --parent option.
3067 command and the hidden --parent option.
3069
3068
3070 .. container:: verbose
3069 .. container:: verbose
3071
3070
3072 Examples:
3071 Examples:
3073
3072
3074 - copy a single change to the stable branch and edit its description::
3073 - copy a single change to the stable branch and edit its description::
3075
3074
3076 hg update stable
3075 hg update stable
3077 hg graft --edit 9393
3076 hg graft --edit 9393
3078
3077
3079 - graft a range of changesets with one exception, updating dates::
3078 - graft a range of changesets with one exception, updating dates::
3080
3079
3081 hg graft -D "2085::2093 and not 2091"
3080 hg graft -D "2085::2093 and not 2091"
3082
3081
3083 - continue a graft after resolving conflicts::
3082 - continue a graft after resolving conflicts::
3084
3083
3085 hg graft -c
3084 hg graft -c
3086
3085
3087 - show the source of a grafted changeset::
3086 - show the source of a grafted changeset::
3088
3087
3089 hg log --debug -r .
3088 hg log --debug -r .
3090
3089
3091 - show revisions sorted by date::
3090 - show revisions sorted by date::
3092
3091
3093 hg log -r "sort(all(), date)"
3092 hg log -r "sort(all(), date)"
3094
3093
3095 - backport the result of a merge as a single commit::
3094 - backport the result of a merge as a single commit::
3096
3095
3097 hg graft -r 123 --base 123^
3096 hg graft -r 123 --base 123^
3098
3097
3099 - land a feature branch as one changeset::
3098 - land a feature branch as one changeset::
3100
3099
3101 hg up -cr default
3100 hg up -cr default
3102 hg graft -r featureX --base "ancestor('featureX', 'default')"
3101 hg graft -r featureX --base "ancestor('featureX', 'default')"
3103
3102
3104 See :hg:`help revisions` for more about specifying revisions.
3103 See :hg:`help revisions` for more about specifying revisions.
3105
3104
3106 Returns 0 on successful completion, 1 if there are unresolved files.
3105 Returns 0 on successful completion, 1 if there are unresolved files.
3107 """
3106 """
3108 with repo.wlock():
3107 with repo.wlock():
3109 return _dograft(ui, repo, *revs, **opts)
3108 return _dograft(ui, repo, *revs, **opts)
3110
3109
3111
3110
3112 def _dograft(ui, repo, *revs, **opts):
3111 def _dograft(ui, repo, *revs, **opts):
3113 if revs and opts.get('rev'):
3112 if revs and opts.get('rev'):
3114 ui.warn(
3113 ui.warn(
3115 _(
3114 _(
3116 b'warning: inconsistent use of --rev might give unexpected '
3115 b'warning: inconsistent use of --rev might give unexpected '
3117 b'revision ordering!\n'
3116 b'revision ordering!\n'
3118 )
3117 )
3119 )
3118 )
3120
3119
3121 revs = list(revs)
3120 revs = list(revs)
3122 revs.extend(opts.get('rev'))
3121 revs.extend(opts.get('rev'))
3123 # a dict of data to be stored in state file
3122 # a dict of data to be stored in state file
3124 statedata = {}
3123 statedata = {}
3125 # list of new nodes created by ongoing graft
3124 # list of new nodes created by ongoing graft
3126 statedata[b'newnodes'] = []
3125 statedata[b'newnodes'] = []
3127
3126
3128 cmdutil.resolve_commit_options(ui, opts)
3127 cmdutil.resolve_commit_options(ui, opts)
3129
3128
3130 editor = cmdutil.getcommiteditor(editform=b'graft', **opts)
3129 editor = cmdutil.getcommiteditor(editform=b'graft', **opts)
3131
3130
3132 cmdutil.check_at_most_one_arg(opts, 'abort', 'stop', 'continue')
3131 cmdutil.check_at_most_one_arg(opts, 'abort', 'stop', 'continue')
3133
3132
3134 cont = False
3133 cont = False
3135 if opts.get('no_commit'):
3134 if opts.get('no_commit'):
3136 cmdutil.check_incompatible_arguments(
3135 cmdutil.check_incompatible_arguments(
3137 opts,
3136 opts,
3138 'no_commit',
3137 'no_commit',
3139 ['edit', 'currentuser', 'currentdate', 'log'],
3138 ['edit', 'currentuser', 'currentdate', 'log'],
3140 )
3139 )
3141
3140
3142 graftstate = statemod.cmdstate(repo, b'graftstate')
3141 graftstate = statemod.cmdstate(repo, b'graftstate')
3143
3142
3144 if opts.get('stop'):
3143 if opts.get('stop'):
3145 cmdutil.check_incompatible_arguments(
3144 cmdutil.check_incompatible_arguments(
3146 opts,
3145 opts,
3147 'stop',
3146 'stop',
3148 [
3147 [
3149 'edit',
3148 'edit',
3150 'log',
3149 'log',
3151 'user',
3150 'user',
3152 'date',
3151 'date',
3153 'currentdate',
3152 'currentdate',
3154 'currentuser',
3153 'currentuser',
3155 'rev',
3154 'rev',
3156 ],
3155 ],
3157 )
3156 )
3158 return _stopgraft(ui, repo, graftstate)
3157 return _stopgraft(ui, repo, graftstate)
3159 elif opts.get('abort'):
3158 elif opts.get('abort'):
3160 cmdutil.check_incompatible_arguments(
3159 cmdutil.check_incompatible_arguments(
3161 opts,
3160 opts,
3162 'abort',
3161 'abort',
3163 [
3162 [
3164 'edit',
3163 'edit',
3165 'log',
3164 'log',
3166 'user',
3165 'user',
3167 'date',
3166 'date',
3168 'currentdate',
3167 'currentdate',
3169 'currentuser',
3168 'currentuser',
3170 'rev',
3169 'rev',
3171 ],
3170 ],
3172 )
3171 )
3173 return cmdutil.abortgraft(ui, repo, graftstate)
3172 return cmdutil.abortgraft(ui, repo, graftstate)
3174 elif opts.get('continue'):
3173 elif opts.get('continue'):
3175 cont = True
3174 cont = True
3176 if revs:
3175 if revs:
3177 raise error.InputError(_(b"can't specify --continue and revisions"))
3176 raise error.InputError(_(b"can't specify --continue and revisions"))
3178 # read in unfinished revisions
3177 # read in unfinished revisions
3179 if graftstate.exists():
3178 if graftstate.exists():
3180 statedata = cmdutil.readgraftstate(repo, graftstate)
3179 statedata = cmdutil.readgraftstate(repo, graftstate)
3181 if statedata.get(b'date'):
3180 if statedata.get(b'date'):
3182 opts['date'] = statedata[b'date']
3181 opts['date'] = statedata[b'date']
3183 if statedata.get(b'user'):
3182 if statedata.get(b'user'):
3184 opts['user'] = statedata[b'user']
3183 opts['user'] = statedata[b'user']
3185 if statedata.get(b'log'):
3184 if statedata.get(b'log'):
3186 opts['log'] = True
3185 opts['log'] = True
3187 if statedata.get(b'no_commit'):
3186 if statedata.get(b'no_commit'):
3188 opts['no_commit'] = statedata.get(b'no_commit')
3187 opts['no_commit'] = statedata.get(b'no_commit')
3189 if statedata.get(b'base'):
3188 if statedata.get(b'base'):
3190 opts['base'] = statedata.get(b'base')
3189 opts['base'] = statedata.get(b'base')
3191 nodes = statedata[b'nodes']
3190 nodes = statedata[b'nodes']
3192 revs = [repo[node].rev() for node in nodes]
3191 revs = [repo[node].rev() for node in nodes]
3193 else:
3192 else:
3194 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3193 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3195 else:
3194 else:
3196 if not revs:
3195 if not revs:
3197 raise error.InputError(_(b'no revisions specified'))
3196 raise error.InputError(_(b'no revisions specified'))
3198 cmdutil.checkunfinished(repo)
3197 cmdutil.checkunfinished(repo)
3199 cmdutil.bailifchanged(repo)
3198 cmdutil.bailifchanged(repo)
3200 revs = logcmdutil.revrange(repo, revs)
3199 revs = logcmdutil.revrange(repo, revs)
3201
3200
3202 skipped = set()
3201 skipped = set()
3203 basectx = None
3202 basectx = None
3204 if opts.get('base'):
3203 if opts.get('base'):
3205 basectx = logcmdutil.revsingle(repo, opts['base'], None)
3204 basectx = logcmdutil.revsingle(repo, opts['base'], None)
3206 if basectx is None:
3205 if basectx is None:
3207 # check for merges
3206 # check for merges
3208 for rev in repo.revs(b'%ld and merge()', revs):
3207 for rev in repo.revs(b'%ld and merge()', revs):
3209 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3208 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3210 skipped.add(rev)
3209 skipped.add(rev)
3211 revs = [r for r in revs if r not in skipped]
3210 revs = [r for r in revs if r not in skipped]
3212 if not revs:
3211 if not revs:
3213 return -1
3212 return -1
3214 if basectx is not None and len(revs) != 1:
3213 if basectx is not None and len(revs) != 1:
3215 raise error.InputError(_(b'only one revision allowed with --base '))
3214 raise error.InputError(_(b'only one revision allowed with --base '))
3216
3215
3217 # Don't check in the --continue case, in effect retaining --force across
3216 # Don't check in the --continue case, in effect retaining --force across
3218 # --continues. That's because without --force, any revisions we decided to
3217 # --continues. That's because without --force, any revisions we decided to
3219 # skip would have been filtered out here, so they wouldn't have made their
3218 # skip would have been filtered out here, so they wouldn't have made their
3220 # way to the graftstate. With --force, any revisions we would have otherwise
3219 # way to the graftstate. With --force, any revisions we would have otherwise
3221 # skipped would not have been filtered out, and if they hadn't been applied
3220 # skipped would not have been filtered out, and if they hadn't been applied
3222 # already, they'd have been in the graftstate.
3221 # already, they'd have been in the graftstate.
3223 if not (cont or opts.get('force')) and basectx is None:
3222 if not (cont or opts.get('force')) and basectx is None:
3224 # check for ancestors of dest branch
3223 # check for ancestors of dest branch
3225 ancestors = repo.revs(b'%ld & (::.)', revs)
3224 ancestors = repo.revs(b'%ld & (::.)', revs)
3226 for rev in ancestors:
3225 for rev in ancestors:
3227 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3226 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3228
3227
3229 revs = [r for r in revs if r not in ancestors]
3228 revs = [r for r in revs if r not in ancestors]
3230
3229
3231 if not revs:
3230 if not revs:
3232 return -1
3231 return -1
3233
3232
3234 # analyze revs for earlier grafts
3233 # analyze revs for earlier grafts
3235 ids = {}
3234 ids = {}
3236 for ctx in repo.set(b"%ld", revs):
3235 for ctx in repo.set(b"%ld", revs):
3237 ids[ctx.hex()] = ctx.rev()
3236 ids[ctx.hex()] = ctx.rev()
3238 n = ctx.extra().get(b'source')
3237 n = ctx.extra().get(b'source')
3239 if n:
3238 if n:
3240 ids[n] = ctx.rev()
3239 ids[n] = ctx.rev()
3241
3240
3242 # check ancestors for earlier grafts
3241 # check ancestors for earlier grafts
3243 ui.debug(b'scanning for duplicate grafts\n')
3242 ui.debug(b'scanning for duplicate grafts\n')
3244
3243
3245 # The only changesets we can be sure doesn't contain grafts of any
3244 # The only changesets we can be sure doesn't contain grafts of any
3246 # revs, are the ones that are common ancestors of *all* revs:
3245 # revs, are the ones that are common ancestors of *all* revs:
3247 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3246 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3248 ctx = repo[rev]
3247 ctx = repo[rev]
3249 n = ctx.extra().get(b'source')
3248 n = ctx.extra().get(b'source')
3250 if n in ids:
3249 if n in ids:
3251 try:
3250 try:
3252 r = repo[n].rev()
3251 r = repo[n].rev()
3253 except error.RepoLookupError:
3252 except error.RepoLookupError:
3254 r = None
3253 r = None
3255 if r in revs:
3254 if r in revs:
3256 ui.warn(
3255 ui.warn(
3257 _(
3256 _(
3258 b'skipping revision %d:%s '
3257 b'skipping revision %d:%s '
3259 b'(already grafted to %d:%s)\n'
3258 b'(already grafted to %d:%s)\n'
3260 )
3259 )
3261 % (r, repo[r], rev, ctx)
3260 % (r, repo[r], rev, ctx)
3262 )
3261 )
3263 revs.remove(r)
3262 revs.remove(r)
3264 elif ids[n] in revs:
3263 elif ids[n] in revs:
3265 if r is None:
3264 if r is None:
3266 ui.warn(
3265 ui.warn(
3267 _(
3266 _(
3268 b'skipping already grafted revision %d:%s '
3267 b'skipping already grafted revision %d:%s '
3269 b'(%d:%s also has unknown origin %s)\n'
3268 b'(%d:%s also has unknown origin %s)\n'
3270 )
3269 )
3271 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3270 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3272 )
3271 )
3273 else:
3272 else:
3274 ui.warn(
3273 ui.warn(
3275 _(
3274 _(
3276 b'skipping already grafted revision %d:%s '
3275 b'skipping already grafted revision %d:%s '
3277 b'(%d:%s also has origin %d:%s)\n'
3276 b'(%d:%s also has origin %d:%s)\n'
3278 )
3277 )
3279 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3278 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3280 )
3279 )
3281 revs.remove(ids[n])
3280 revs.remove(ids[n])
3282 elif ctx.hex() in ids:
3281 elif ctx.hex() in ids:
3283 r = ids[ctx.hex()]
3282 r = ids[ctx.hex()]
3284 if r in revs:
3283 if r in revs:
3285 ui.warn(
3284 ui.warn(
3286 _(
3285 _(
3287 b'skipping already grafted revision %d:%s '
3286 b'skipping already grafted revision %d:%s '
3288 b'(was grafted from %d:%s)\n'
3287 b'(was grafted from %d:%s)\n'
3289 )
3288 )
3290 % (r, repo[r], rev, ctx)
3289 % (r, repo[r], rev, ctx)
3291 )
3290 )
3292 revs.remove(r)
3291 revs.remove(r)
3293 if not revs:
3292 if not revs:
3294 return -1
3293 return -1
3295
3294
3296 if opts.get('no_commit'):
3295 if opts.get('no_commit'):
3297 statedata[b'no_commit'] = True
3296 statedata[b'no_commit'] = True
3298 if opts.get('base'):
3297 if opts.get('base'):
3299 statedata[b'base'] = opts['base']
3298 statedata[b'base'] = opts['base']
3300 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3299 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3301 desc = b'%d:%s "%s"' % (
3300 desc = b'%d:%s "%s"' % (
3302 ctx.rev(),
3301 ctx.rev(),
3303 ctx,
3302 ctx,
3304 ctx.description().split(b'\n', 1)[0],
3303 ctx.description().split(b'\n', 1)[0],
3305 )
3304 )
3306 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3305 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3307 if names:
3306 if names:
3308 desc += b' (%s)' % b' '.join(names)
3307 desc += b' (%s)' % b' '.join(names)
3309 ui.status(_(b'grafting %s\n') % desc)
3308 ui.status(_(b'grafting %s\n') % desc)
3310 if opts.get('dry_run'):
3309 if opts.get('dry_run'):
3311 continue
3310 continue
3312
3311
3313 source = ctx.extra().get(b'source')
3312 source = ctx.extra().get(b'source')
3314 extra = {}
3313 extra = {}
3315 if source:
3314 if source:
3316 extra[b'source'] = source
3315 extra[b'source'] = source
3317 extra[b'intermediate-source'] = ctx.hex()
3316 extra[b'intermediate-source'] = ctx.hex()
3318 else:
3317 else:
3319 extra[b'source'] = ctx.hex()
3318 extra[b'source'] = ctx.hex()
3320 user = ctx.user()
3319 user = ctx.user()
3321 if opts.get('user'):
3320 if opts.get('user'):
3322 user = opts['user']
3321 user = opts['user']
3323 statedata[b'user'] = user
3322 statedata[b'user'] = user
3324 date = ctx.date()
3323 date = ctx.date()
3325 if opts.get('date'):
3324 if opts.get('date'):
3326 date = opts['date']
3325 date = opts['date']
3327 statedata[b'date'] = date
3326 statedata[b'date'] = date
3328 message = ctx.description()
3327 message = ctx.description()
3329 if opts.get('log'):
3328 if opts.get('log'):
3330 message += b'\n(grafted from %s)' % ctx.hex()
3329 message += b'\n(grafted from %s)' % ctx.hex()
3331 statedata[b'log'] = True
3330 statedata[b'log'] = True
3332
3331
3333 # we don't merge the first commit when continuing
3332 # we don't merge the first commit when continuing
3334 if not cont:
3333 if not cont:
3335 # perform the graft merge with p1(rev) as 'ancestor'
3334 # perform the graft merge with p1(rev) as 'ancestor'
3336 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
3335 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
3337 base = ctx.p1() if basectx is None else basectx
3336 base = ctx.p1() if basectx is None else basectx
3338 with ui.configoverride(overrides, b'graft'):
3337 with ui.configoverride(overrides, b'graft'):
3339 stats = mergemod.graft(
3338 stats = mergemod.graft(
3340 repo, ctx, base, [b'local', b'graft', b'parent of graft']
3339 repo, ctx, base, [b'local', b'graft', b'parent of graft']
3341 )
3340 )
3342 # report any conflicts
3341 # report any conflicts
3343 if stats.unresolvedcount > 0:
3342 if stats.unresolvedcount > 0:
3344 # write out state for --continue
3343 # write out state for --continue
3345 nodes = [repo[rev].hex() for rev in revs[pos:]]
3344 nodes = [repo[rev].hex() for rev in revs[pos:]]
3346 statedata[b'nodes'] = nodes
3345 statedata[b'nodes'] = nodes
3347 stateversion = 1
3346 stateversion = 1
3348 graftstate.save(stateversion, statedata)
3347 graftstate.save(stateversion, statedata)
3349 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3348 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3350 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3349 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3351 return 1
3350 return 1
3352 else:
3351 else:
3353 cont = False
3352 cont = False
3354
3353
3355 # commit if --no-commit is false
3354 # commit if --no-commit is false
3356 if not opts.get('no_commit'):
3355 if not opts.get('no_commit'):
3357 node = repo.commit(
3356 node = repo.commit(
3358 text=message, user=user, date=date, extra=extra, editor=editor
3357 text=message, user=user, date=date, extra=extra, editor=editor
3359 )
3358 )
3360 if node is None:
3359 if node is None:
3361 ui.warn(
3360 ui.warn(
3362 _(b'note: graft of %d:%s created no changes to commit\n')
3361 _(b'note: graft of %d:%s created no changes to commit\n')
3363 % (ctx.rev(), ctx)
3362 % (ctx.rev(), ctx)
3364 )
3363 )
3365 # checking that newnodes exist because old state files won't have it
3364 # checking that newnodes exist because old state files won't have it
3366 elif statedata.get(b'newnodes') is not None:
3365 elif statedata.get(b'newnodes') is not None:
3367 nn = statedata[b'newnodes']
3366 nn = statedata[b'newnodes']
3368 assert isinstance(nn, list) # list of bytes
3367 assert isinstance(nn, list) # list of bytes
3369 nn.append(node)
3368 nn.append(node)
3370
3369
3371 # remove state when we complete successfully
3370 # remove state when we complete successfully
3372 if not opts.get('dry_run'):
3371 if not opts.get('dry_run'):
3373 graftstate.delete()
3372 graftstate.delete()
3374
3373
3375 return 0
3374 return 0
3376
3375
3377
3376
3378 def _stopgraft(ui, repo, graftstate):
3377 def _stopgraft(ui, repo, graftstate):
3379 """stop the interrupted graft"""
3378 """stop the interrupted graft"""
3380 if not graftstate.exists():
3379 if not graftstate.exists():
3381 raise error.StateError(_(b"no interrupted graft found"))
3380 raise error.StateError(_(b"no interrupted graft found"))
3382 pctx = repo[b'.']
3381 pctx = repo[b'.']
3383 mergemod.clean_update(pctx)
3382 mergemod.clean_update(pctx)
3384 graftstate.delete()
3383 graftstate.delete()
3385 ui.status(_(b"stopped the interrupted graft\n"))
3384 ui.status(_(b"stopped the interrupted graft\n"))
3386 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3385 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3387 return 0
3386 return 0
3388
3387
3389
3388
3390 statemod.addunfinished(
3389 statemod.addunfinished(
3391 b'graft',
3390 b'graft',
3392 fname=b'graftstate',
3391 fname=b'graftstate',
3393 clearable=True,
3392 clearable=True,
3394 stopflag=True,
3393 stopflag=True,
3395 continueflag=True,
3394 continueflag=True,
3396 abortfunc=cmdutil.hgabortgraft,
3395 abortfunc=cmdutil.hgabortgraft,
3397 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3396 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3398 )
3397 )
3399
3398
3400
3399
3401 @command(
3400 @command(
3402 b'grep',
3401 b'grep',
3403 [
3402 [
3404 (b'0', b'print0', None, _(b'end fields with NUL')),
3403 (b'0', b'print0', None, _(b'end fields with NUL')),
3405 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3404 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3406 (
3405 (
3407 b'',
3406 b'',
3408 b'diff',
3407 b'diff',
3409 None,
3408 None,
3410 _(
3409 _(
3411 b'search revision differences for when the pattern was added '
3410 b'search revision differences for when the pattern was added '
3412 b'or removed'
3411 b'or removed'
3413 ),
3412 ),
3414 ),
3413 ),
3415 (b'a', b'text', None, _(b'treat all files as text')),
3414 (b'a', b'text', None, _(b'treat all files as text')),
3416 (
3415 (
3417 b'f',
3416 b'f',
3418 b'follow',
3417 b'follow',
3419 None,
3418 None,
3420 _(
3419 _(
3421 b'follow changeset history,'
3420 b'follow changeset history,'
3422 b' or file history across copies and renames'
3421 b' or file history across copies and renames'
3423 ),
3422 ),
3424 ),
3423 ),
3425 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3424 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3426 (
3425 (
3427 b'l',
3426 b'l',
3428 b'files-with-matches',
3427 b'files-with-matches',
3429 None,
3428 None,
3430 _(b'print only filenames and revisions that match'),
3429 _(b'print only filenames and revisions that match'),
3431 ),
3430 ),
3432 (b'n', b'line-number', None, _(b'print matching line numbers')),
3431 (b'n', b'line-number', None, _(b'print matching line numbers')),
3433 (
3432 (
3434 b'r',
3433 b'r',
3435 b'rev',
3434 b'rev',
3436 [],
3435 [],
3437 _(b'search files changed within revision range'),
3436 _(b'search files changed within revision range'),
3438 _(b'REV'),
3437 _(b'REV'),
3439 ),
3438 ),
3440 (
3439 (
3441 b'',
3440 b'',
3442 b'all-files',
3441 b'all-files',
3443 None,
3442 None,
3444 _(
3443 _(
3445 b'include all files in the changeset while grepping (DEPRECATED)'
3444 b'include all files in the changeset while grepping (DEPRECATED)'
3446 ),
3445 ),
3447 ),
3446 ),
3448 (b'u', b'user', None, _(b'list the author (long with -v)')),
3447 (b'u', b'user', None, _(b'list the author (long with -v)')),
3449 (b'd', b'date', None, _(b'list the date (short with -q)')),
3448 (b'd', b'date', None, _(b'list the date (short with -q)')),
3450 ]
3449 ]
3451 + formatteropts
3450 + formatteropts
3452 + walkopts,
3451 + walkopts,
3453 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3452 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3454 helpcategory=command.CATEGORY_FILE_CONTENTS,
3453 helpcategory=command.CATEGORY_FILE_CONTENTS,
3455 inferrepo=True,
3454 inferrepo=True,
3456 intents={INTENT_READONLY},
3455 intents={INTENT_READONLY},
3457 )
3456 )
3458 def grep(ui, repo, pattern, *pats, **opts):
3457 def grep(ui, repo, pattern, *pats, **opts):
3459 """search for a pattern in specified files
3458 """search for a pattern in specified files
3460
3459
3461 Search the working directory or revision history for a regular
3460 Search the working directory or revision history for a regular
3462 expression in the specified files for the entire repository.
3461 expression in the specified files for the entire repository.
3463
3462
3464 By default, grep searches the repository files in the working
3463 By default, grep searches the repository files in the working
3465 directory and prints the files where it finds a match. To specify
3464 directory and prints the files where it finds a match. To specify
3466 historical revisions instead of the working directory, use the
3465 historical revisions instead of the working directory, use the
3467 --rev flag.
3466 --rev flag.
3468
3467
3469 To search instead historical revision differences that contains a
3468 To search instead historical revision differences that contains a
3470 change in match status ("-" for a match that becomes a non-match,
3469 change in match status ("-" for a match that becomes a non-match,
3471 or "+" for a non-match that becomes a match), use the --diff flag.
3470 or "+" for a non-match that becomes a match), use the --diff flag.
3472
3471
3473 PATTERN can be any Python (roughly Perl-compatible) regular
3472 PATTERN can be any Python (roughly Perl-compatible) regular
3474 expression.
3473 expression.
3475
3474
3476 If no FILEs are specified and the --rev flag isn't supplied, all
3475 If no FILEs are specified and the --rev flag isn't supplied, all
3477 files in the working directory are searched. When using the --rev
3476 files in the working directory are searched. When using the --rev
3478 flag and specifying FILEs, use the --follow argument to also
3477 flag and specifying FILEs, use the --follow argument to also
3479 follow the specified FILEs across renames and copies.
3478 follow the specified FILEs across renames and copies.
3480
3479
3481 .. container:: verbose
3480 .. container:: verbose
3482
3481
3483 Template:
3482 Template:
3484
3483
3485 The following keywords are supported in addition to the common template
3484 The following keywords are supported in addition to the common template
3486 keywords and functions. See also :hg:`help templates`.
3485 keywords and functions. See also :hg:`help templates`.
3487
3486
3488 :change: String. Character denoting insertion ``+`` or removal ``-``.
3487 :change: String. Character denoting insertion ``+`` or removal ``-``.
3489 Available if ``--diff`` is specified.
3488 Available if ``--diff`` is specified.
3490 :lineno: Integer. Line number of the match.
3489 :lineno: Integer. Line number of the match.
3491 :path: String. Repository-absolute path of the file.
3490 :path: String. Repository-absolute path of the file.
3492 :texts: List of text chunks.
3491 :texts: List of text chunks.
3493
3492
3494 And each entry of ``{texts}`` provides the following sub-keywords.
3493 And each entry of ``{texts}`` provides the following sub-keywords.
3495
3494
3496 :matched: Boolean. True if the chunk matches the specified pattern.
3495 :matched: Boolean. True if the chunk matches the specified pattern.
3497 :text: String. Chunk content.
3496 :text: String. Chunk content.
3498
3497
3499 See :hg:`help templates.operators` for the list expansion syntax.
3498 See :hg:`help templates.operators` for the list expansion syntax.
3500
3499
3501 Returns 0 if a match is found, 1 otherwise.
3500 Returns 0 if a match is found, 1 otherwise.
3502
3501
3503 """
3502 """
3504 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3503 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3505 opts = pycompat.byteskwargs(opts)
3504 opts = pycompat.byteskwargs(opts)
3506 diff = opts.get(b'all') or opts.get(b'diff')
3505 diff = opts.get(b'all') or opts.get(b'diff')
3507 follow = opts.get(b'follow')
3506 follow = opts.get(b'follow')
3508 if opts.get(b'all_files') is None and not diff:
3507 if opts.get(b'all_files') is None and not diff:
3509 opts[b'all_files'] = True
3508 opts[b'all_files'] = True
3510 plaingrep = (
3509 plaingrep = (
3511 opts.get(b'all_files')
3510 opts.get(b'all_files')
3512 and not opts.get(b'rev')
3511 and not opts.get(b'rev')
3513 and not opts.get(b'follow')
3512 and not opts.get(b'follow')
3514 )
3513 )
3515 all_files = opts.get(b'all_files')
3514 all_files = opts.get(b'all_files')
3516 if plaingrep:
3515 if plaingrep:
3517 opts[b'rev'] = [b'wdir()']
3516 opts[b'rev'] = [b'wdir()']
3518
3517
3519 reflags = re.M
3518 reflags = re.M
3520 if opts.get(b'ignore_case'):
3519 if opts.get(b'ignore_case'):
3521 reflags |= re.I
3520 reflags |= re.I
3522 try:
3521 try:
3523 regexp = util.re.compile(pattern, reflags)
3522 regexp = util.re.compile(pattern, reflags)
3524 except re.error as inst:
3523 except re.error as inst:
3525 ui.warn(
3524 ui.warn(
3526 _(b"grep: invalid match pattern: %s\n")
3525 _(b"grep: invalid match pattern: %s\n")
3527 % stringutil.forcebytestr(inst)
3526 % stringutil.forcebytestr(inst)
3528 )
3527 )
3529 return 1
3528 return 1
3530 sep, eol = b':', b'\n'
3529 sep, eol = b':', b'\n'
3531 if opts.get(b'print0'):
3530 if opts.get(b'print0'):
3532 sep = eol = b'\0'
3531 sep = eol = b'\0'
3533
3532
3534 searcher = grepmod.grepsearcher(
3533 searcher = grepmod.grepsearcher(
3535 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3534 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3536 )
3535 )
3537
3536
3538 getfile = searcher._getfile
3537 getfile = searcher._getfile
3539
3538
3540 uipathfn = scmutil.getuipathfn(repo)
3539 uipathfn = scmutil.getuipathfn(repo)
3541
3540
3542 def display(fm, fn, ctx, pstates, states):
3541 def display(fm, fn, ctx, pstates, states):
3543 rev = scmutil.intrev(ctx)
3542 rev = scmutil.intrev(ctx)
3544 if fm.isplain():
3543 if fm.isplain():
3545 formatuser = ui.shortuser
3544 formatuser = ui.shortuser
3546 else:
3545 else:
3547 formatuser = pycompat.bytestr
3546 formatuser = pycompat.bytestr
3548 if ui.quiet:
3547 if ui.quiet:
3549 datefmt = b'%Y-%m-%d'
3548 datefmt = b'%Y-%m-%d'
3550 else:
3549 else:
3551 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3550 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3552 found = False
3551 found = False
3553
3552
3554 @util.cachefunc
3553 @util.cachefunc
3555 def binary():
3554 def binary():
3556 flog = getfile(fn)
3555 flog = getfile(fn)
3557 try:
3556 try:
3558 return stringutil.binary(flog.read(ctx.filenode(fn)))
3557 return stringutil.binary(flog.read(ctx.filenode(fn)))
3559 except error.WdirUnsupported:
3558 except error.WdirUnsupported:
3560 return ctx[fn].isbinary()
3559 return ctx[fn].isbinary()
3561
3560
3562 fieldnamemap = {b'linenumber': b'lineno'}
3561 fieldnamemap = {b'linenumber': b'lineno'}
3563 if diff:
3562 if diff:
3564 iter = grepmod.difflinestates(pstates, states)
3563 iter = grepmod.difflinestates(pstates, states)
3565 else:
3564 else:
3566 iter = [(b'', l) for l in states]
3565 iter = [(b'', l) for l in states]
3567 for change, l in iter:
3566 for change, l in iter:
3568 fm.startitem()
3567 fm.startitem()
3569 fm.context(ctx=ctx)
3568 fm.context(ctx=ctx)
3570 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3569 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3571 fm.plain(uipathfn(fn), label=b'grep.filename')
3570 fm.plain(uipathfn(fn), label=b'grep.filename')
3572
3571
3573 cols = [
3572 cols = [
3574 (b'rev', b'%d', rev, not plaingrep, b''),
3573 (b'rev', b'%d', rev, not plaingrep, b''),
3575 (
3574 (
3576 b'linenumber',
3575 b'linenumber',
3577 b'%d',
3576 b'%d',
3578 l.linenum,
3577 l.linenum,
3579 opts.get(b'line_number'),
3578 opts.get(b'line_number'),
3580 b'',
3579 b'',
3581 ),
3580 ),
3582 ]
3581 ]
3583 if diff:
3582 if diff:
3584 cols.append(
3583 cols.append(
3585 (
3584 (
3586 b'change',
3585 b'change',
3587 b'%s',
3586 b'%s',
3588 change,
3587 change,
3589 True,
3588 True,
3590 b'grep.inserted '
3589 b'grep.inserted '
3591 if change == b'+'
3590 if change == b'+'
3592 else b'grep.deleted ',
3591 else b'grep.deleted ',
3593 )
3592 )
3594 )
3593 )
3595 cols.extend(
3594 cols.extend(
3596 [
3595 [
3597 (
3596 (
3598 b'user',
3597 b'user',
3599 b'%s',
3598 b'%s',
3600 formatuser(ctx.user()),
3599 formatuser(ctx.user()),
3601 opts.get(b'user'),
3600 opts.get(b'user'),
3602 b'',
3601 b'',
3603 ),
3602 ),
3604 (
3603 (
3605 b'date',
3604 b'date',
3606 b'%s',
3605 b'%s',
3607 fm.formatdate(ctx.date(), datefmt),
3606 fm.formatdate(ctx.date(), datefmt),
3608 opts.get(b'date'),
3607 opts.get(b'date'),
3609 b'',
3608 b'',
3610 ),
3609 ),
3611 ]
3610 ]
3612 )
3611 )
3613 for name, fmt, data, cond, extra_label in cols:
3612 for name, fmt, data, cond, extra_label in cols:
3614 if cond:
3613 if cond:
3615 fm.plain(sep, label=b'grep.sep')
3614 fm.plain(sep, label=b'grep.sep')
3616 field = fieldnamemap.get(name, name)
3615 field = fieldnamemap.get(name, name)
3617 label = extra_label + (b'grep.%s' % name)
3616 label = extra_label + (b'grep.%s' % name)
3618 fm.condwrite(cond, field, fmt, data, label=label)
3617 fm.condwrite(cond, field, fmt, data, label=label)
3619 if not opts.get(b'files_with_matches'):
3618 if not opts.get(b'files_with_matches'):
3620 fm.plain(sep, label=b'grep.sep')
3619 fm.plain(sep, label=b'grep.sep')
3621 if not opts.get(b'text') and binary():
3620 if not opts.get(b'text') and binary():
3622 fm.plain(_(b" Binary file matches"))
3621 fm.plain(_(b" Binary file matches"))
3623 else:
3622 else:
3624 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3623 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3625 fm.plain(eol)
3624 fm.plain(eol)
3626 found = True
3625 found = True
3627 if opts.get(b'files_with_matches'):
3626 if opts.get(b'files_with_matches'):
3628 break
3627 break
3629 return found
3628 return found
3630
3629
3631 def displaymatches(fm, l):
3630 def displaymatches(fm, l):
3632 p = 0
3631 p = 0
3633 for s, e in l.findpos(regexp):
3632 for s, e in l.findpos(regexp):
3634 if p < s:
3633 if p < s:
3635 fm.startitem()
3634 fm.startitem()
3636 fm.write(b'text', b'%s', l.line[p:s])
3635 fm.write(b'text', b'%s', l.line[p:s])
3637 fm.data(matched=False)
3636 fm.data(matched=False)
3638 fm.startitem()
3637 fm.startitem()
3639 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3638 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3640 fm.data(matched=True)
3639 fm.data(matched=True)
3641 p = e
3640 p = e
3642 if p < len(l.line):
3641 if p < len(l.line):
3643 fm.startitem()
3642 fm.startitem()
3644 fm.write(b'text', b'%s', l.line[p:])
3643 fm.write(b'text', b'%s', l.line[p:])
3645 fm.data(matched=False)
3644 fm.data(matched=False)
3646 fm.end()
3645 fm.end()
3647
3646
3648 found = False
3647 found = False
3649
3648
3650 wopts = logcmdutil.walkopts(
3649 wopts = logcmdutil.walkopts(
3651 pats=pats,
3650 pats=pats,
3652 opts=opts,
3651 opts=opts,
3653 revspec=opts[b'rev'],
3652 revspec=opts[b'rev'],
3654 include_pats=opts[b'include'],
3653 include_pats=opts[b'include'],
3655 exclude_pats=opts[b'exclude'],
3654 exclude_pats=opts[b'exclude'],
3656 follow=follow,
3655 follow=follow,
3657 force_changelog_traversal=all_files,
3656 force_changelog_traversal=all_files,
3658 filter_revisions_by_pats=not all_files,
3657 filter_revisions_by_pats=not all_files,
3659 )
3658 )
3660 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3659 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3661
3660
3662 ui.pager(b'grep')
3661 ui.pager(b'grep')
3663 fm = ui.formatter(b'grep', opts)
3662 fm = ui.formatter(b'grep', opts)
3664 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3663 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3665 r = display(fm, fn, ctx, pstates, states)
3664 r = display(fm, fn, ctx, pstates, states)
3666 found = found or r
3665 found = found or r
3667 if r and not diff and not all_files:
3666 if r and not diff and not all_files:
3668 searcher.skipfile(fn, ctx.rev())
3667 searcher.skipfile(fn, ctx.rev())
3669 fm.end()
3668 fm.end()
3670
3669
3671 return not found
3670 return not found
3672
3671
3673
3672
3674 @command(
3673 @command(
3675 b'heads',
3674 b'heads',
3676 [
3675 [
3677 (
3676 (
3678 b'r',
3677 b'r',
3679 b'rev',
3678 b'rev',
3680 b'',
3679 b'',
3681 _(b'show only heads which are descendants of STARTREV'),
3680 _(b'show only heads which are descendants of STARTREV'),
3682 _(b'STARTREV'),
3681 _(b'STARTREV'),
3683 ),
3682 ),
3684 (b't', b'topo', False, _(b'show topological heads only')),
3683 (b't', b'topo', False, _(b'show topological heads only')),
3685 (
3684 (
3686 b'a',
3685 b'a',
3687 b'active',
3686 b'active',
3688 False,
3687 False,
3689 _(b'show active branchheads only (DEPRECATED)'),
3688 _(b'show active branchheads only (DEPRECATED)'),
3690 ),
3689 ),
3691 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3690 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3692 ]
3691 ]
3693 + templateopts,
3692 + templateopts,
3694 _(b'[-ct] [-r STARTREV] [REV]...'),
3693 _(b'[-ct] [-r STARTREV] [REV]...'),
3695 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3694 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3696 intents={INTENT_READONLY},
3695 intents={INTENT_READONLY},
3697 )
3696 )
3698 def heads(ui, repo, *branchrevs, **opts):
3697 def heads(ui, repo, *branchrevs, **opts):
3699 """show branch heads
3698 """show branch heads
3700
3699
3701 With no arguments, show all open branch heads in the repository.
3700 With no arguments, show all open branch heads in the repository.
3702 Branch heads are changesets that have no descendants on the
3701 Branch heads are changesets that have no descendants on the
3703 same branch. They are where development generally takes place and
3702 same branch. They are where development generally takes place and
3704 are the usual targets for update and merge operations.
3703 are the usual targets for update and merge operations.
3705
3704
3706 If one or more REVs are given, only open branch heads on the
3705 If one or more REVs are given, only open branch heads on the
3707 branches associated with the specified changesets are shown. This
3706 branches associated with the specified changesets are shown. This
3708 means that you can use :hg:`heads .` to see the heads on the
3707 means that you can use :hg:`heads .` to see the heads on the
3709 currently checked-out branch.
3708 currently checked-out branch.
3710
3709
3711 If -c/--closed is specified, also show branch heads marked closed
3710 If -c/--closed is specified, also show branch heads marked closed
3712 (see :hg:`commit --close-branch`).
3711 (see :hg:`commit --close-branch`).
3713
3712
3714 If STARTREV is specified, only those heads that are descendants of
3713 If STARTREV is specified, only those heads that are descendants of
3715 STARTREV will be displayed.
3714 STARTREV will be displayed.
3716
3715
3717 If -t/--topo is specified, named branch mechanics will be ignored and only
3716 If -t/--topo is specified, named branch mechanics will be ignored and only
3718 topological heads (changesets with no children) will be shown.
3717 topological heads (changesets with no children) will be shown.
3719
3718
3720 Returns 0 if matching heads are found, 1 if not.
3719 Returns 0 if matching heads are found, 1 if not.
3721 """
3720 """
3722
3721
3723 opts = pycompat.byteskwargs(opts)
3722 opts = pycompat.byteskwargs(opts)
3724 start = None
3723 start = None
3725 rev = opts.get(b'rev')
3724 rev = opts.get(b'rev')
3726 if rev:
3725 if rev:
3727 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3726 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3728 start = logcmdutil.revsingle(repo, rev, None).node()
3727 start = logcmdutil.revsingle(repo, rev, None).node()
3729
3728
3730 if opts.get(b'topo'):
3729 if opts.get(b'topo'):
3731 heads = [repo[h] for h in repo.heads(start)]
3730 heads = [repo[h] for h in repo.heads(start)]
3732 else:
3731 else:
3733 heads = []
3732 heads = []
3734 for branch in repo.branchmap():
3733 for branch in repo.branchmap():
3735 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3734 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3736 heads = [repo[h] for h in heads]
3735 heads = [repo[h] for h in heads]
3737
3736
3738 if branchrevs:
3737 if branchrevs:
3739 branches = {
3738 branches = {
3740 repo[r].branch() for r in logcmdutil.revrange(repo, branchrevs)
3739 repo[r].branch() for r in logcmdutil.revrange(repo, branchrevs)
3741 }
3740 }
3742 heads = [h for h in heads if h.branch() in branches]
3741 heads = [h for h in heads if h.branch() in branches]
3743
3742
3744 if opts.get(b'active') and branchrevs:
3743 if opts.get(b'active') and branchrevs:
3745 dagheads = repo.heads(start)
3744 dagheads = repo.heads(start)
3746 heads = [h for h in heads if h.node() in dagheads]
3745 heads = [h for h in heads if h.node() in dagheads]
3747
3746
3748 if branchrevs:
3747 if branchrevs:
3749 haveheads = {h.branch() for h in heads}
3748 haveheads = {h.branch() for h in heads}
3750 if branches - haveheads:
3749 if branches - haveheads:
3751 headless = b', '.join(b for b in branches - haveheads)
3750 headless = b', '.join(b for b in branches - haveheads)
3752 msg = _(b'no open branch heads found on branches %s')
3751 msg = _(b'no open branch heads found on branches %s')
3753 if opts.get(b'rev'):
3752 if opts.get(b'rev'):
3754 msg += _(b' (started at %s)') % opts[b'rev']
3753 msg += _(b' (started at %s)') % opts[b'rev']
3755 ui.warn((msg + b'\n') % headless)
3754 ui.warn((msg + b'\n') % headless)
3756
3755
3757 if not heads:
3756 if not heads:
3758 return 1
3757 return 1
3759
3758
3760 ui.pager(b'heads')
3759 ui.pager(b'heads')
3761 heads = sorted(heads, key=lambda x: -(x.rev()))
3760 heads = sorted(heads, key=lambda x: -(x.rev()))
3762 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3761 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3763 for ctx in heads:
3762 for ctx in heads:
3764 displayer.show(ctx)
3763 displayer.show(ctx)
3765 displayer.close()
3764 displayer.close()
3766
3765
3767
3766
3768 @command(
3767 @command(
3769 b'help',
3768 b'help',
3770 [
3769 [
3771 (b'e', b'extension', None, _(b'show only help for extensions')),
3770 (b'e', b'extension', None, _(b'show only help for extensions')),
3772 (b'c', b'command', None, _(b'show only help for commands')),
3771 (b'c', b'command', None, _(b'show only help for commands')),
3773 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3772 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3774 (
3773 (
3775 b's',
3774 b's',
3776 b'system',
3775 b'system',
3777 [],
3776 [],
3778 _(b'show help for specific platform(s)'),
3777 _(b'show help for specific platform(s)'),
3779 _(b'PLATFORM'),
3778 _(b'PLATFORM'),
3780 ),
3779 ),
3781 ],
3780 ],
3782 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3781 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3783 helpcategory=command.CATEGORY_HELP,
3782 helpcategory=command.CATEGORY_HELP,
3784 norepo=True,
3783 norepo=True,
3785 intents={INTENT_READONLY},
3784 intents={INTENT_READONLY},
3786 )
3785 )
3787 def help_(ui, name=None, **opts):
3786 def help_(ui, name=None, **opts):
3788 """show help for a given topic or a help overview
3787 """show help for a given topic or a help overview
3789
3788
3790 With no arguments, print a list of commands with short help messages.
3789 With no arguments, print a list of commands with short help messages.
3791
3790
3792 Given a topic, extension, or command name, print help for that
3791 Given a topic, extension, or command name, print help for that
3793 topic.
3792 topic.
3794
3793
3795 Returns 0 if successful.
3794 Returns 0 if successful.
3796 """
3795 """
3797
3796
3798 keep = opts.get('system') or []
3797 keep = opts.get('system') or []
3799 if len(keep) == 0:
3798 if len(keep) == 0:
3800 if pycompat.sysplatform.startswith(b'win'):
3799 if pycompat.sysplatform.startswith(b'win'):
3801 keep.append(b'windows')
3800 keep.append(b'windows')
3802 elif pycompat.sysplatform == b'OpenVMS':
3801 elif pycompat.sysplatform == b'OpenVMS':
3803 keep.append(b'vms')
3802 keep.append(b'vms')
3804 elif pycompat.sysplatform == b'plan9':
3803 elif pycompat.sysplatform == b'plan9':
3805 keep.append(b'plan9')
3804 keep.append(b'plan9')
3806 else:
3805 else:
3807 keep.append(b'unix')
3806 keep.append(b'unix')
3808 keep.append(pycompat.sysplatform.lower())
3807 keep.append(pycompat.sysplatform.lower())
3809 if ui.verbose:
3808 if ui.verbose:
3810 keep.append(b'verbose')
3809 keep.append(b'verbose')
3811
3810
3812 commands = sys.modules[__name__]
3811 commands = sys.modules[__name__]
3813 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3812 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3814 ui.pager(b'help')
3813 ui.pager(b'help')
3815 ui.write(formatted)
3814 ui.write(formatted)
3816
3815
3817
3816
3818 @command(
3817 @command(
3819 b'identify|id',
3818 b'identify|id',
3820 [
3819 [
3821 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3820 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3822 (b'n', b'num', None, _(b'show local revision number')),
3821 (b'n', b'num', None, _(b'show local revision number')),
3823 (b'i', b'id', None, _(b'show global revision id')),
3822 (b'i', b'id', None, _(b'show global revision id')),
3824 (b'b', b'branch', None, _(b'show branch')),
3823 (b'b', b'branch', None, _(b'show branch')),
3825 (b't', b'tags', None, _(b'show tags')),
3824 (b't', b'tags', None, _(b'show tags')),
3826 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3825 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3827 ]
3826 ]
3828 + remoteopts
3827 + remoteopts
3829 + formatteropts,
3828 + formatteropts,
3830 _(b'[-nibtB] [-r REV] [SOURCE]'),
3829 _(b'[-nibtB] [-r REV] [SOURCE]'),
3831 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3830 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3832 optionalrepo=True,
3831 optionalrepo=True,
3833 intents={INTENT_READONLY},
3832 intents={INTENT_READONLY},
3834 )
3833 )
3835 def identify(
3834 def identify(
3836 ui,
3835 ui,
3837 repo,
3836 repo,
3838 source=None,
3837 source=None,
3839 rev=None,
3838 rev=None,
3840 num=None,
3839 num=None,
3841 id=None,
3840 id=None,
3842 branch=None,
3841 branch=None,
3843 tags=None,
3842 tags=None,
3844 bookmarks=None,
3843 bookmarks=None,
3845 **opts
3844 **opts
3846 ):
3845 ):
3847 """identify the working directory or specified revision
3846 """identify the working directory or specified revision
3848
3847
3849 Print a summary identifying the repository state at REV using one or
3848 Print a summary identifying the repository state at REV using one or
3850 two parent hash identifiers, followed by a "+" if the working
3849 two parent hash identifiers, followed by a "+" if the working
3851 directory has uncommitted changes, the branch name (if not default),
3850 directory has uncommitted changes, the branch name (if not default),
3852 a list of tags, and a list of bookmarks.
3851 a list of tags, and a list of bookmarks.
3853
3852
3854 When REV is not given, print a summary of the current state of the
3853 When REV is not given, print a summary of the current state of the
3855 repository including the working directory. Specify -r. to get information
3854 repository including the working directory. Specify -r. to get information
3856 of the working directory parent without scanning uncommitted changes.
3855 of the working directory parent without scanning uncommitted changes.
3857
3856
3858 Specifying a path to a repository root or Mercurial bundle will
3857 Specifying a path to a repository root or Mercurial bundle will
3859 cause lookup to operate on that repository/bundle.
3858 cause lookup to operate on that repository/bundle.
3860
3859
3861 .. container:: verbose
3860 .. container:: verbose
3862
3861
3863 Template:
3862 Template:
3864
3863
3865 The following keywords are supported in addition to the common template
3864 The following keywords are supported in addition to the common template
3866 keywords and functions. See also :hg:`help templates`.
3865 keywords and functions. See also :hg:`help templates`.
3867
3866
3868 :dirty: String. Character ``+`` denoting if the working directory has
3867 :dirty: String. Character ``+`` denoting if the working directory has
3869 uncommitted changes.
3868 uncommitted changes.
3870 :id: String. One or two nodes, optionally followed by ``+``.
3869 :id: String. One or two nodes, optionally followed by ``+``.
3871 :parents: List of strings. Parent nodes of the changeset.
3870 :parents: List of strings. Parent nodes of the changeset.
3872
3871
3873 Examples:
3872 Examples:
3874
3873
3875 - generate a build identifier for the working directory::
3874 - generate a build identifier for the working directory::
3876
3875
3877 hg id --id > build-id.dat
3876 hg id --id > build-id.dat
3878
3877
3879 - find the revision corresponding to a tag::
3878 - find the revision corresponding to a tag::
3880
3879
3881 hg id -n -r 1.3
3880 hg id -n -r 1.3
3882
3881
3883 - check the most recent revision of a remote repository::
3882 - check the most recent revision of a remote repository::
3884
3883
3885 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3884 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3886
3885
3887 See :hg:`log` for generating more information about specific revisions,
3886 See :hg:`log` for generating more information about specific revisions,
3888 including full hash identifiers.
3887 including full hash identifiers.
3889
3888
3890 Returns 0 if successful.
3889 Returns 0 if successful.
3891 """
3890 """
3892
3891
3893 opts = pycompat.byteskwargs(opts)
3892 opts = pycompat.byteskwargs(opts)
3894 if not repo and not source:
3893 if not repo and not source:
3895 raise error.InputError(
3894 raise error.InputError(
3896 _(b"there is no Mercurial repository here (.hg not found)")
3895 _(b"there is no Mercurial repository here (.hg not found)")
3897 )
3896 )
3898
3897
3899 default = not (num or id or branch or tags or bookmarks)
3898 default = not (num or id or branch or tags or bookmarks)
3900 output = []
3899 output = []
3901 revs = []
3900 revs = []
3902
3901
3903 peer = None
3902 peer = None
3904 try:
3903 try:
3905 if source:
3904 if source:
3906 source, branches = urlutil.get_unique_pull_path(
3905 source, branches = urlutil.get_unique_pull_path(
3907 b'identify', repo, ui, source
3906 b'identify', repo, ui, source
3908 )
3907 )
3909 # only pass ui when no repo
3908 # only pass ui when no repo
3910 peer = hg.peer(repo or ui, opts, source)
3909 peer = hg.peer(repo or ui, opts, source)
3911 repo = peer.local()
3910 repo = peer.local()
3912 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3911 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3913
3912
3914 fm = ui.formatter(b'identify', opts)
3913 fm = ui.formatter(b'identify', opts)
3915 fm.startitem()
3914 fm.startitem()
3916
3915
3917 if not repo:
3916 if not repo:
3918 if num or branch or tags:
3917 if num or branch or tags:
3919 raise error.InputError(
3918 raise error.InputError(
3920 _(b"can't query remote revision number, branch, or tags")
3919 _(b"can't query remote revision number, branch, or tags")
3921 )
3920 )
3922 if not rev and revs:
3921 if not rev and revs:
3923 rev = revs[0]
3922 rev = revs[0]
3924 if not rev:
3923 if not rev:
3925 rev = b"tip"
3924 rev = b"tip"
3926
3925
3927 remoterev = peer.lookup(rev)
3926 remoterev = peer.lookup(rev)
3928 hexrev = fm.hexfunc(remoterev)
3927 hexrev = fm.hexfunc(remoterev)
3929 if default or id:
3928 if default or id:
3930 output = [hexrev]
3929 output = [hexrev]
3931 fm.data(id=hexrev)
3930 fm.data(id=hexrev)
3932
3931
3933 @util.cachefunc
3932 @util.cachefunc
3934 def getbms():
3933 def getbms():
3935 bms = []
3934 bms = []
3936
3935
3937 if b'bookmarks' in peer.listkeys(b'namespaces'):
3936 if b'bookmarks' in peer.listkeys(b'namespaces'):
3938 hexremoterev = hex(remoterev)
3937 hexremoterev = hex(remoterev)
3939 bms = [
3938 bms = [
3940 bm
3939 bm
3941 for bm, bmr in peer.listkeys(b'bookmarks').items()
3940 for bm, bmr in peer.listkeys(b'bookmarks').items()
3942 if bmr == hexremoterev
3941 if bmr == hexremoterev
3943 ]
3942 ]
3944
3943
3945 return sorted(bms)
3944 return sorted(bms)
3946
3945
3947 if fm.isplain():
3946 if fm.isplain():
3948 if bookmarks:
3947 if bookmarks:
3949 output.extend(getbms())
3948 output.extend(getbms())
3950 elif default and not ui.quiet:
3949 elif default and not ui.quiet:
3951 # multiple bookmarks for a single parent separated by '/'
3950 # multiple bookmarks for a single parent separated by '/'
3952 bm = b'/'.join(getbms())
3951 bm = b'/'.join(getbms())
3953 if bm:
3952 if bm:
3954 output.append(bm)
3953 output.append(bm)
3955 else:
3954 else:
3956 fm.data(node=hex(remoterev))
3955 fm.data(node=hex(remoterev))
3957 if bookmarks or b'bookmarks' in fm.datahint():
3956 if bookmarks or b'bookmarks' in fm.datahint():
3958 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3957 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3959 else:
3958 else:
3960 if rev:
3959 if rev:
3961 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3960 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3962 ctx = logcmdutil.revsingle(repo, rev, None)
3961 ctx = logcmdutil.revsingle(repo, rev, None)
3963
3962
3964 if ctx.rev() is None:
3963 if ctx.rev() is None:
3965 ctx = repo[None]
3964 ctx = repo[None]
3966 parents = ctx.parents()
3965 parents = ctx.parents()
3967 taglist = []
3966 taglist = []
3968 for p in parents:
3967 for p in parents:
3969 taglist.extend(p.tags())
3968 taglist.extend(p.tags())
3970
3969
3971 dirty = b""
3970 dirty = b""
3972 if ctx.dirty(missing=True, merge=False, branch=False):
3971 if ctx.dirty(missing=True, merge=False, branch=False):
3973 dirty = b'+'
3972 dirty = b'+'
3974 fm.data(dirty=dirty)
3973 fm.data(dirty=dirty)
3975
3974
3976 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3975 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3977 if default or id:
3976 if default or id:
3978 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3977 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3979 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3978 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3980
3979
3981 if num:
3980 if num:
3982 numoutput = [b"%d" % p.rev() for p in parents]
3981 numoutput = [b"%d" % p.rev() for p in parents]
3983 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3982 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3984
3983
3985 fm.data(
3984 fm.data(
3986 parents=fm.formatlist(
3985 parents=fm.formatlist(
3987 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3986 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3988 )
3987 )
3989 )
3988 )
3990 else:
3989 else:
3991 hexoutput = fm.hexfunc(ctx.node())
3990 hexoutput = fm.hexfunc(ctx.node())
3992 if default or id:
3991 if default or id:
3993 output = [hexoutput]
3992 output = [hexoutput]
3994 fm.data(id=hexoutput)
3993 fm.data(id=hexoutput)
3995
3994
3996 if num:
3995 if num:
3997 output.append(pycompat.bytestr(ctx.rev()))
3996 output.append(pycompat.bytestr(ctx.rev()))
3998 taglist = ctx.tags()
3997 taglist = ctx.tags()
3999
3998
4000 if default and not ui.quiet:
3999 if default and not ui.quiet:
4001 b = ctx.branch()
4000 b = ctx.branch()
4002 if b != b'default':
4001 if b != b'default':
4003 output.append(b"(%s)" % b)
4002 output.append(b"(%s)" % b)
4004
4003
4005 # multiple tags for a single parent separated by '/'
4004 # multiple tags for a single parent separated by '/'
4006 t = b'/'.join(taglist)
4005 t = b'/'.join(taglist)
4007 if t:
4006 if t:
4008 output.append(t)
4007 output.append(t)
4009
4008
4010 # multiple bookmarks for a single parent separated by '/'
4009 # multiple bookmarks for a single parent separated by '/'
4011 bm = b'/'.join(ctx.bookmarks())
4010 bm = b'/'.join(ctx.bookmarks())
4012 if bm:
4011 if bm:
4013 output.append(bm)
4012 output.append(bm)
4014 else:
4013 else:
4015 if branch:
4014 if branch:
4016 output.append(ctx.branch())
4015 output.append(ctx.branch())
4017
4016
4018 if tags:
4017 if tags:
4019 output.extend(taglist)
4018 output.extend(taglist)
4020
4019
4021 if bookmarks:
4020 if bookmarks:
4022 output.extend(ctx.bookmarks())
4021 output.extend(ctx.bookmarks())
4023
4022
4024 fm.data(node=ctx.hex())
4023 fm.data(node=ctx.hex())
4025 fm.data(branch=ctx.branch())
4024 fm.data(branch=ctx.branch())
4026 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4025 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4027 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4026 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4028 fm.context(ctx=ctx)
4027 fm.context(ctx=ctx)
4029
4028
4030 fm.plain(b"%s\n" % b' '.join(output))
4029 fm.plain(b"%s\n" % b' '.join(output))
4031 fm.end()
4030 fm.end()
4032 finally:
4031 finally:
4033 if peer:
4032 if peer:
4034 peer.close()
4033 peer.close()
4035
4034
4036
4035
4037 @command(
4036 @command(
4038 b'import|patch',
4037 b'import|patch',
4039 [
4038 [
4040 (
4039 (
4041 b'p',
4040 b'p',
4042 b'strip',
4041 b'strip',
4043 1,
4042 1,
4044 _(
4043 _(
4045 b'directory strip option for patch. This has the same '
4044 b'directory strip option for patch. This has the same '
4046 b'meaning as the corresponding patch option'
4045 b'meaning as the corresponding patch option'
4047 ),
4046 ),
4048 _(b'NUM'),
4047 _(b'NUM'),
4049 ),
4048 ),
4050 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4049 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4051 (b'', b'secret', None, _(b'use the secret phase for committing')),
4050 (b'', b'secret', None, _(b'use the secret phase for committing')),
4052 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4051 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4053 (
4052 (
4054 b'f',
4053 b'f',
4055 b'force',
4054 b'force',
4056 None,
4055 None,
4057 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4056 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4058 ),
4057 ),
4059 (
4058 (
4060 b'',
4059 b'',
4061 b'no-commit',
4060 b'no-commit',
4062 None,
4061 None,
4063 _(b"don't commit, just update the working directory"),
4062 _(b"don't commit, just update the working directory"),
4064 ),
4063 ),
4065 (
4064 (
4066 b'',
4065 b'',
4067 b'bypass',
4066 b'bypass',
4068 None,
4067 None,
4069 _(b"apply patch without touching the working directory"),
4068 _(b"apply patch without touching the working directory"),
4070 ),
4069 ),
4071 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4070 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4072 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4071 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4073 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4072 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4074 (
4073 (
4075 b'',
4074 b'',
4076 b'import-branch',
4075 b'import-branch',
4077 None,
4076 None,
4078 _(b'use any branch information in patch (implied by --exact)'),
4077 _(b'use any branch information in patch (implied by --exact)'),
4079 ),
4078 ),
4080 ]
4079 ]
4081 + commitopts
4080 + commitopts
4082 + commitopts2
4081 + commitopts2
4083 + similarityopts,
4082 + similarityopts,
4084 _(b'[OPTION]... PATCH...'),
4083 _(b'[OPTION]... PATCH...'),
4085 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4084 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4086 )
4085 )
4087 def import_(ui, repo, patch1=None, *patches, **opts):
4086 def import_(ui, repo, patch1=None, *patches, **opts):
4088 """import an ordered set of patches
4087 """import an ordered set of patches
4089
4088
4090 Import a list of patches and commit them individually (unless
4089 Import a list of patches and commit them individually (unless
4091 --no-commit is specified).
4090 --no-commit is specified).
4092
4091
4093 To read a patch from standard input (stdin), use "-" as the patch
4092 To read a patch from standard input (stdin), use "-" as the patch
4094 name. If a URL is specified, the patch will be downloaded from
4093 name. If a URL is specified, the patch will be downloaded from
4095 there.
4094 there.
4096
4095
4097 Import first applies changes to the working directory (unless
4096 Import first applies changes to the working directory (unless
4098 --bypass is specified), import will abort if there are outstanding
4097 --bypass is specified), import will abort if there are outstanding
4099 changes.
4098 changes.
4100
4099
4101 Use --bypass to apply and commit patches directly to the
4100 Use --bypass to apply and commit patches directly to the
4102 repository, without affecting the working directory. Without
4101 repository, without affecting the working directory. Without
4103 --exact, patches will be applied on top of the working directory
4102 --exact, patches will be applied on top of the working directory
4104 parent revision.
4103 parent revision.
4105
4104
4106 You can import a patch straight from a mail message. Even patches
4105 You can import a patch straight from a mail message. Even patches
4107 as attachments work (to use the body part, it must have type
4106 as attachments work (to use the body part, it must have type
4108 text/plain or text/x-patch). From and Subject headers of email
4107 text/plain or text/x-patch). From and Subject headers of email
4109 message are used as default committer and commit message. All
4108 message are used as default committer and commit message. All
4110 text/plain body parts before first diff are added to the commit
4109 text/plain body parts before first diff are added to the commit
4111 message.
4110 message.
4112
4111
4113 If the imported patch was generated by :hg:`export`, user and
4112 If the imported patch was generated by :hg:`export`, user and
4114 description from patch override values from message headers and
4113 description from patch override values from message headers and
4115 body. Values given on command line with -m/--message and -u/--user
4114 body. Values given on command line with -m/--message and -u/--user
4116 override these.
4115 override these.
4117
4116
4118 If --exact is specified, import will set the working directory to
4117 If --exact is specified, import will set the working directory to
4119 the parent of each patch before applying it, and will abort if the
4118 the parent of each patch before applying it, and will abort if the
4120 resulting changeset has a different ID than the one recorded in
4119 resulting changeset has a different ID than the one recorded in
4121 the patch. This will guard against various ways that portable
4120 the patch. This will guard against various ways that portable
4122 patch formats and mail systems might fail to transfer Mercurial
4121 patch formats and mail systems might fail to transfer Mercurial
4123 data or metadata. See :hg:`bundle` for lossless transmission.
4122 data or metadata. See :hg:`bundle` for lossless transmission.
4124
4123
4125 Use --partial to ensure a changeset will be created from the patch
4124 Use --partial to ensure a changeset will be created from the patch
4126 even if some hunks fail to apply. Hunks that fail to apply will be
4125 even if some hunks fail to apply. Hunks that fail to apply will be
4127 written to a <target-file>.rej file. Conflicts can then be resolved
4126 written to a <target-file>.rej file. Conflicts can then be resolved
4128 by hand before :hg:`commit --amend` is run to update the created
4127 by hand before :hg:`commit --amend` is run to update the created
4129 changeset. This flag exists to let people import patches that
4128 changeset. This flag exists to let people import patches that
4130 partially apply without losing the associated metadata (author,
4129 partially apply without losing the associated metadata (author,
4131 date, description, ...).
4130 date, description, ...).
4132
4131
4133 .. note::
4132 .. note::
4134
4133
4135 When no hunks apply cleanly, :hg:`import --partial` will create
4134 When no hunks apply cleanly, :hg:`import --partial` will create
4136 an empty changeset, importing only the patch metadata.
4135 an empty changeset, importing only the patch metadata.
4137
4136
4138 With -s/--similarity, hg will attempt to discover renames and
4137 With -s/--similarity, hg will attempt to discover renames and
4139 copies in the patch in the same way as :hg:`addremove`.
4138 copies in the patch in the same way as :hg:`addremove`.
4140
4139
4141 It is possible to use external patch programs to perform the patch
4140 It is possible to use external patch programs to perform the patch
4142 by setting the ``ui.patch`` configuration option. For the default
4141 by setting the ``ui.patch`` configuration option. For the default
4143 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4142 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4144 See :hg:`help config` for more information about configuration
4143 See :hg:`help config` for more information about configuration
4145 files and how to use these options.
4144 files and how to use these options.
4146
4145
4147 See :hg:`help dates` for a list of formats valid for -d/--date.
4146 See :hg:`help dates` for a list of formats valid for -d/--date.
4148
4147
4149 .. container:: verbose
4148 .. container:: verbose
4150
4149
4151 Examples:
4150 Examples:
4152
4151
4153 - import a traditional patch from a website and detect renames::
4152 - import a traditional patch from a website and detect renames::
4154
4153
4155 hg import -s 80 http://example.com/bugfix.patch
4154 hg import -s 80 http://example.com/bugfix.patch
4156
4155
4157 - import a changeset from an hgweb server::
4156 - import a changeset from an hgweb server::
4158
4157
4159 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4158 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4160
4159
4161 - import all the patches in an Unix-style mbox::
4160 - import all the patches in an Unix-style mbox::
4162
4161
4163 hg import incoming-patches.mbox
4162 hg import incoming-patches.mbox
4164
4163
4165 - import patches from stdin::
4164 - import patches from stdin::
4166
4165
4167 hg import -
4166 hg import -
4168
4167
4169 - attempt to exactly restore an exported changeset (not always
4168 - attempt to exactly restore an exported changeset (not always
4170 possible)::
4169 possible)::
4171
4170
4172 hg import --exact proposed-fix.patch
4171 hg import --exact proposed-fix.patch
4173
4172
4174 - use an external tool to apply a patch which is too fuzzy for
4173 - use an external tool to apply a patch which is too fuzzy for
4175 the default internal tool.
4174 the default internal tool.
4176
4175
4177 hg import --config ui.patch="patch --merge" fuzzy.patch
4176 hg import --config ui.patch="patch --merge" fuzzy.patch
4178
4177
4179 - change the default fuzzing from 2 to a less strict 7
4178 - change the default fuzzing from 2 to a less strict 7
4180
4179
4181 hg import --config ui.fuzz=7 fuzz.patch
4180 hg import --config ui.fuzz=7 fuzz.patch
4182
4181
4183 Returns 0 on success, 1 on partial success (see --partial).
4182 Returns 0 on success, 1 on partial success (see --partial).
4184 """
4183 """
4185
4184
4186 cmdutil.check_incompatible_arguments(
4185 cmdutil.check_incompatible_arguments(
4187 opts, 'no_commit', ['bypass', 'secret']
4186 opts, 'no_commit', ['bypass', 'secret']
4188 )
4187 )
4189 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4188 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4190 opts = pycompat.byteskwargs(opts)
4189 opts = pycompat.byteskwargs(opts)
4191 if not patch1:
4190 if not patch1:
4192 raise error.InputError(_(b'need at least one patch to import'))
4191 raise error.InputError(_(b'need at least one patch to import'))
4193
4192
4194 patches = (patch1,) + patches
4193 patches = (patch1,) + patches
4195
4194
4196 date = opts.get(b'date')
4195 date = opts.get(b'date')
4197 if date:
4196 if date:
4198 opts[b'date'] = dateutil.parsedate(date)
4197 opts[b'date'] = dateutil.parsedate(date)
4199
4198
4200 exact = opts.get(b'exact')
4199 exact = opts.get(b'exact')
4201 update = not opts.get(b'bypass')
4200 update = not opts.get(b'bypass')
4202 try:
4201 try:
4203 sim = float(opts.get(b'similarity') or 0)
4202 sim = float(opts.get(b'similarity') or 0)
4204 except ValueError:
4203 except ValueError:
4205 raise error.InputError(_(b'similarity must be a number'))
4204 raise error.InputError(_(b'similarity must be a number'))
4206 if sim < 0 or sim > 100:
4205 if sim < 0 or sim > 100:
4207 raise error.InputError(_(b'similarity must be between 0 and 100'))
4206 raise error.InputError(_(b'similarity must be between 0 and 100'))
4208 if sim and not update:
4207 if sim and not update:
4209 raise error.InputError(_(b'cannot use --similarity with --bypass'))
4208 raise error.InputError(_(b'cannot use --similarity with --bypass'))
4210
4209
4211 base = opts[b"base"]
4210 base = opts[b"base"]
4212 msgs = []
4211 msgs = []
4213 ret = 0
4212 ret = 0
4214
4213
4215 with repo.wlock():
4214 with repo.wlock():
4216 if update:
4215 if update:
4217 cmdutil.checkunfinished(repo)
4216 cmdutil.checkunfinished(repo)
4218 if exact or not opts.get(b'force'):
4217 if exact or not opts.get(b'force'):
4219 cmdutil.bailifchanged(repo)
4218 cmdutil.bailifchanged(repo)
4220
4219
4221 if not opts.get(b'no_commit'):
4220 if not opts.get(b'no_commit'):
4222 lock = repo.lock
4221 lock = repo.lock
4223 tr = lambda: repo.transaction(b'import')
4222 tr = lambda: repo.transaction(b'import')
4224 dsguard = util.nullcontextmanager
4223 dsguard = util.nullcontextmanager
4225 else:
4224 else:
4226 lock = util.nullcontextmanager
4225 lock = util.nullcontextmanager
4227 tr = util.nullcontextmanager
4226 tr = util.nullcontextmanager
4228 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4227 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4229 with lock(), tr(), dsguard():
4228 with lock(), tr(), dsguard():
4230 parents = repo[None].parents()
4229 parents = repo[None].parents()
4231 for patchurl in patches:
4230 for patchurl in patches:
4232 if patchurl == b'-':
4231 if patchurl == b'-':
4233 ui.status(_(b'applying patch from stdin\n'))
4232 ui.status(_(b'applying patch from stdin\n'))
4234 patchfile = ui.fin
4233 patchfile = ui.fin
4235 patchurl = b'stdin' # for error message
4234 patchurl = b'stdin' # for error message
4236 else:
4235 else:
4237 patchurl = os.path.join(base, patchurl)
4236 patchurl = os.path.join(base, patchurl)
4238 ui.status(_(b'applying %s\n') % patchurl)
4237 ui.status(_(b'applying %s\n') % patchurl)
4239 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4238 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4240
4239
4241 haspatch = False
4240 haspatch = False
4242 for hunk in patch.split(patchfile):
4241 for hunk in patch.split(patchfile):
4243 with patch.extract(ui, hunk) as patchdata:
4242 with patch.extract(ui, hunk) as patchdata:
4244 msg, node, rej = cmdutil.tryimportone(
4243 msg, node, rej = cmdutil.tryimportone(
4245 ui, repo, patchdata, parents, opts, msgs, hg.clean
4244 ui, repo, patchdata, parents, opts, msgs, hg.clean
4246 )
4245 )
4247 if msg:
4246 if msg:
4248 haspatch = True
4247 haspatch = True
4249 ui.note(msg + b'\n')
4248 ui.note(msg + b'\n')
4250 if update or exact:
4249 if update or exact:
4251 parents = repo[None].parents()
4250 parents = repo[None].parents()
4252 else:
4251 else:
4253 parents = [repo[node]]
4252 parents = [repo[node]]
4254 if rej:
4253 if rej:
4255 ui.write_err(_(b"patch applied partially\n"))
4254 ui.write_err(_(b"patch applied partially\n"))
4256 ui.write_err(
4255 ui.write_err(
4257 _(
4256 _(
4258 b"(fix the .rej files and run "
4257 b"(fix the .rej files and run "
4259 b"`hg commit --amend`)\n"
4258 b"`hg commit --amend`)\n"
4260 )
4259 )
4261 )
4260 )
4262 ret = 1
4261 ret = 1
4263 break
4262 break
4264
4263
4265 if not haspatch:
4264 if not haspatch:
4266 raise error.InputError(_(b'%s: no diffs found') % patchurl)
4265 raise error.InputError(_(b'%s: no diffs found') % patchurl)
4267
4266
4268 if msgs:
4267 if msgs:
4269 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4268 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4270 return ret
4269 return ret
4271
4270
4272
4271
4273 @command(
4272 @command(
4274 b'incoming|in',
4273 b'incoming|in',
4275 [
4274 [
4276 (
4275 (
4277 b'f',
4276 b'f',
4278 b'force',
4277 b'force',
4279 None,
4278 None,
4280 _(b'run even if remote repository is unrelated'),
4279 _(b'run even if remote repository is unrelated'),
4281 ),
4280 ),
4282 (b'n', b'newest-first', None, _(b'show newest record first')),
4281 (b'n', b'newest-first', None, _(b'show newest record first')),
4283 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4282 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4284 (
4283 (
4285 b'r',
4284 b'r',
4286 b'rev',
4285 b'rev',
4287 [],
4286 [],
4288 _(b'a remote changeset intended to be added'),
4287 _(b'a remote changeset intended to be added'),
4289 _(b'REV'),
4288 _(b'REV'),
4290 ),
4289 ),
4291 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4290 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4292 (
4291 (
4293 b'b',
4292 b'b',
4294 b'branch',
4293 b'branch',
4295 [],
4294 [],
4296 _(b'a specific branch you would like to pull'),
4295 _(b'a specific branch you would like to pull'),
4297 _(b'BRANCH'),
4296 _(b'BRANCH'),
4298 ),
4297 ),
4299 ]
4298 ]
4300 + logopts
4299 + logopts
4301 + remoteopts
4300 + remoteopts
4302 + subrepoopts,
4301 + subrepoopts,
4303 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4302 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4304 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4303 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4305 )
4304 )
4306 def incoming(ui, repo, source=b"default", **opts):
4305 def incoming(ui, repo, source=b"default", **opts):
4307 """show new changesets found in source
4306 """show new changesets found in source
4308
4307
4309 Show new changesets found in the specified path/URL or the default
4308 Show new changesets found in the specified path/URL or the default
4310 pull location. These are the changesets that would have been pulled
4309 pull location. These are the changesets that would have been pulled
4311 by :hg:`pull` at the time you issued this command.
4310 by :hg:`pull` at the time you issued this command.
4312
4311
4313 See pull for valid source format details.
4312 See pull for valid source format details.
4314
4313
4315 .. container:: verbose
4314 .. container:: verbose
4316
4315
4317 With -B/--bookmarks, the result of bookmark comparison between
4316 With -B/--bookmarks, the result of bookmark comparison between
4318 local and remote repositories is displayed. With -v/--verbose,
4317 local and remote repositories is displayed. With -v/--verbose,
4319 status is also displayed for each bookmark like below::
4318 status is also displayed for each bookmark like below::
4320
4319
4321 BM1 01234567890a added
4320 BM1 01234567890a added
4322 BM2 1234567890ab advanced
4321 BM2 1234567890ab advanced
4323 BM3 234567890abc diverged
4322 BM3 234567890abc diverged
4324 BM4 34567890abcd changed
4323 BM4 34567890abcd changed
4325
4324
4326 The action taken locally when pulling depends on the
4325 The action taken locally when pulling depends on the
4327 status of each bookmark:
4326 status of each bookmark:
4328
4327
4329 :``added``: pull will create it
4328 :``added``: pull will create it
4330 :``advanced``: pull will update it
4329 :``advanced``: pull will update it
4331 :``diverged``: pull will create a divergent bookmark
4330 :``diverged``: pull will create a divergent bookmark
4332 :``changed``: result depends on remote changesets
4331 :``changed``: result depends on remote changesets
4333
4332
4334 From the point of view of pulling behavior, bookmark
4333 From the point of view of pulling behavior, bookmark
4335 existing only in the remote repository are treated as ``added``,
4334 existing only in the remote repository are treated as ``added``,
4336 even if it is in fact locally deleted.
4335 even if it is in fact locally deleted.
4337
4336
4338 .. container:: verbose
4337 .. container:: verbose
4339
4338
4340 For remote repository, using --bundle avoids downloading the
4339 For remote repository, using --bundle avoids downloading the
4341 changesets twice if the incoming is followed by a pull.
4340 changesets twice if the incoming is followed by a pull.
4342
4341
4343 Examples:
4342 Examples:
4344
4343
4345 - show incoming changes with patches and full description::
4344 - show incoming changes with patches and full description::
4346
4345
4347 hg incoming -vp
4346 hg incoming -vp
4348
4347
4349 - show incoming changes excluding merges, store a bundle::
4348 - show incoming changes excluding merges, store a bundle::
4350
4349
4351 hg in -vpM --bundle incoming.hg
4350 hg in -vpM --bundle incoming.hg
4352 hg pull incoming.hg
4351 hg pull incoming.hg
4353
4352
4354 - briefly list changes inside a bundle::
4353 - briefly list changes inside a bundle::
4355
4354
4356 hg in changes.hg -T "{desc|firstline}\\n"
4355 hg in changes.hg -T "{desc|firstline}\\n"
4357
4356
4358 Returns 0 if there are incoming changes, 1 otherwise.
4357 Returns 0 if there are incoming changes, 1 otherwise.
4359 """
4358 """
4360 opts = pycompat.byteskwargs(opts)
4359 opts = pycompat.byteskwargs(opts)
4361 if opts.get(b'graph'):
4360 if opts.get(b'graph'):
4362 logcmdutil.checkunsupportedgraphflags([], opts)
4361 logcmdutil.checkunsupportedgraphflags([], opts)
4363
4362
4364 def display(other, chlist, displayer):
4363 def display(other, chlist, displayer):
4365 revdag = logcmdutil.graphrevs(other, chlist, opts)
4364 revdag = logcmdutil.graphrevs(other, chlist, opts)
4366 logcmdutil.displaygraph(
4365 logcmdutil.displaygraph(
4367 ui, repo, revdag, displayer, graphmod.asciiedges
4366 ui, repo, revdag, displayer, graphmod.asciiedges
4368 )
4367 )
4369
4368
4370 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4369 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4371 return 0
4370 return 0
4372
4371
4373 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4372 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4374
4373
4375 if opts.get(b'bookmarks'):
4374 if opts.get(b'bookmarks'):
4376 srcs = urlutil.get_pull_paths(repo, ui, [source])
4375 srcs = urlutil.get_pull_paths(repo, ui, [source])
4377 for path in srcs:
4376 for path in srcs:
4378 source, branches = urlutil.parseurl(
4377 source, branches = urlutil.parseurl(
4379 path.rawloc, opts.get(b'branch')
4378 path.rawloc, opts.get(b'branch')
4380 )
4379 )
4381 other = hg.peer(repo, opts, source)
4380 other = hg.peer(repo, opts, source)
4382 try:
4381 try:
4383 if b'bookmarks' not in other.listkeys(b'namespaces'):
4382 if b'bookmarks' not in other.listkeys(b'namespaces'):
4384 ui.warn(_(b"remote doesn't support bookmarks\n"))
4383 ui.warn(_(b"remote doesn't support bookmarks\n"))
4385 return 0
4384 return 0
4386 ui.pager(b'incoming')
4385 ui.pager(b'incoming')
4387 ui.status(
4386 ui.status(
4388 _(b'comparing with %s\n') % urlutil.hidepassword(source)
4387 _(b'comparing with %s\n') % urlutil.hidepassword(source)
4389 )
4388 )
4390 return bookmarks.incoming(
4389 return bookmarks.incoming(
4391 ui, repo, other, mode=path.bookmarks_mode
4390 ui, repo, other, mode=path.bookmarks_mode
4392 )
4391 )
4393 finally:
4392 finally:
4394 other.close()
4393 other.close()
4395
4394
4396 return hg.incoming(ui, repo, source, opts)
4395 return hg.incoming(ui, repo, source, opts)
4397
4396
4398
4397
4399 @command(
4398 @command(
4400 b'init',
4399 b'init',
4401 remoteopts,
4400 remoteopts,
4402 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4401 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4403 helpcategory=command.CATEGORY_REPO_CREATION,
4402 helpcategory=command.CATEGORY_REPO_CREATION,
4404 helpbasic=True,
4403 helpbasic=True,
4405 norepo=True,
4404 norepo=True,
4406 )
4405 )
4407 def init(ui, dest=b".", **opts):
4406 def init(ui, dest=b".", **opts):
4408 """create a new repository in the given directory
4407 """create a new repository in the given directory
4409
4408
4410 Initialize a new repository in the given directory. If the given
4409 Initialize a new repository in the given directory. If the given
4411 directory does not exist, it will be created.
4410 directory does not exist, it will be created.
4412
4411
4413 If no directory is given, the current directory is used.
4412 If no directory is given, the current directory is used.
4414
4413
4415 It is possible to specify an ``ssh://`` URL as the destination.
4414 It is possible to specify an ``ssh://`` URL as the destination.
4416 See :hg:`help urls` for more information.
4415 See :hg:`help urls` for more information.
4417
4416
4418 Returns 0 on success.
4417 Returns 0 on success.
4419 """
4418 """
4420 opts = pycompat.byteskwargs(opts)
4419 opts = pycompat.byteskwargs(opts)
4421 path = urlutil.get_clone_path(ui, dest)[1]
4420 path = urlutil.get_clone_path(ui, dest)[1]
4422 peer = hg.peer(ui, opts, path, create=True)
4421 peer = hg.peer(ui, opts, path, create=True)
4423 peer.close()
4422 peer.close()
4424
4423
4425
4424
4426 @command(
4425 @command(
4427 b'locate',
4426 b'locate',
4428 [
4427 [
4429 (
4428 (
4430 b'r',
4429 b'r',
4431 b'rev',
4430 b'rev',
4432 b'',
4431 b'',
4433 _(b'search the repository as it is in REV'),
4432 _(b'search the repository as it is in REV'),
4434 _(b'REV'),
4433 _(b'REV'),
4435 ),
4434 ),
4436 (
4435 (
4437 b'0',
4436 b'0',
4438 b'print0',
4437 b'print0',
4439 None,
4438 None,
4440 _(b'end filenames with NUL, for use with xargs'),
4439 _(b'end filenames with NUL, for use with xargs'),
4441 ),
4440 ),
4442 (
4441 (
4443 b'f',
4442 b'f',
4444 b'fullpath',
4443 b'fullpath',
4445 None,
4444 None,
4446 _(b'print complete paths from the filesystem root'),
4445 _(b'print complete paths from the filesystem root'),
4447 ),
4446 ),
4448 ]
4447 ]
4449 + walkopts,
4448 + walkopts,
4450 _(b'[OPTION]... [PATTERN]...'),
4449 _(b'[OPTION]... [PATTERN]...'),
4451 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4450 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4452 )
4451 )
4453 def locate(ui, repo, *pats, **opts):
4452 def locate(ui, repo, *pats, **opts):
4454 """locate files matching specific patterns (DEPRECATED)
4453 """locate files matching specific patterns (DEPRECATED)
4455
4454
4456 Print files under Mercurial control in the working directory whose
4455 Print files under Mercurial control in the working directory whose
4457 names match the given patterns.
4456 names match the given patterns.
4458
4457
4459 By default, this command searches all directories in the working
4458 By default, this command searches all directories in the working
4460 directory. To search just the current directory and its
4459 directory. To search just the current directory and its
4461 subdirectories, use "--include .".
4460 subdirectories, use "--include .".
4462
4461
4463 If no patterns are given to match, this command prints the names
4462 If no patterns are given to match, this command prints the names
4464 of all files under Mercurial control in the working directory.
4463 of all files under Mercurial control in the working directory.
4465
4464
4466 If you want to feed the output of this command into the "xargs"
4465 If you want to feed the output of this command into the "xargs"
4467 command, use the -0 option to both this command and "xargs". This
4466 command, use the -0 option to both this command and "xargs". This
4468 will avoid the problem of "xargs" treating single filenames that
4467 will avoid the problem of "xargs" treating single filenames that
4469 contain whitespace as multiple filenames.
4468 contain whitespace as multiple filenames.
4470
4469
4471 See :hg:`help files` for a more versatile command.
4470 See :hg:`help files` for a more versatile command.
4472
4471
4473 Returns 0 if a match is found, 1 otherwise.
4472 Returns 0 if a match is found, 1 otherwise.
4474 """
4473 """
4475 opts = pycompat.byteskwargs(opts)
4474 opts = pycompat.byteskwargs(opts)
4476 if opts.get(b'print0'):
4475 if opts.get(b'print0'):
4477 end = b'\0'
4476 end = b'\0'
4478 else:
4477 else:
4479 end = b'\n'
4478 end = b'\n'
4480 ctx = logcmdutil.revsingle(repo, opts.get(b'rev'), None)
4479 ctx = logcmdutil.revsingle(repo, opts.get(b'rev'), None)
4481
4480
4482 ret = 1
4481 ret = 1
4483 m = scmutil.match(
4482 m = scmutil.match(
4484 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4483 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4485 )
4484 )
4486
4485
4487 ui.pager(b'locate')
4486 ui.pager(b'locate')
4488 if ctx.rev() is None:
4487 if ctx.rev() is None:
4489 # When run on the working copy, "locate" includes removed files, so
4488 # When run on the working copy, "locate" includes removed files, so
4490 # we get the list of files from the dirstate.
4489 # we get the list of files from the dirstate.
4491 filesgen = sorted(repo.dirstate.matches(m))
4490 filesgen = sorted(repo.dirstate.matches(m))
4492 else:
4491 else:
4493 filesgen = ctx.matches(m)
4492 filesgen = ctx.matches(m)
4494 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4493 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4495 for abs in filesgen:
4494 for abs in filesgen:
4496 if opts.get(b'fullpath'):
4495 if opts.get(b'fullpath'):
4497 ui.write(repo.wjoin(abs), end)
4496 ui.write(repo.wjoin(abs), end)
4498 else:
4497 else:
4499 ui.write(uipathfn(abs), end)
4498 ui.write(uipathfn(abs), end)
4500 ret = 0
4499 ret = 0
4501
4500
4502 return ret
4501 return ret
4503
4502
4504
4503
4505 @command(
4504 @command(
4506 b'log|history',
4505 b'log|history',
4507 [
4506 [
4508 (
4507 (
4509 b'f',
4508 b'f',
4510 b'follow',
4509 b'follow',
4511 None,
4510 None,
4512 _(
4511 _(
4513 b'follow changeset history, or file history across copies and renames'
4512 b'follow changeset history, or file history across copies and renames'
4514 ),
4513 ),
4515 ),
4514 ),
4516 (
4515 (
4517 b'',
4516 b'',
4518 b'follow-first',
4517 b'follow-first',
4519 None,
4518 None,
4520 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4519 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4521 ),
4520 ),
4522 (
4521 (
4523 b'd',
4522 b'd',
4524 b'date',
4523 b'date',
4525 b'',
4524 b'',
4526 _(b'show revisions matching date spec'),
4525 _(b'show revisions matching date spec'),
4527 _(b'DATE'),
4526 _(b'DATE'),
4528 ),
4527 ),
4529 (b'C', b'copies', None, _(b'show copied files')),
4528 (b'C', b'copies', None, _(b'show copied files')),
4530 (
4529 (
4531 b'k',
4530 b'k',
4532 b'keyword',
4531 b'keyword',
4533 [],
4532 [],
4534 _(b'do case-insensitive search for a given text'),
4533 _(b'do case-insensitive search for a given text'),
4535 _(b'TEXT'),
4534 _(b'TEXT'),
4536 ),
4535 ),
4537 (
4536 (
4538 b'r',
4537 b'r',
4539 b'rev',
4538 b'rev',
4540 [],
4539 [],
4541 _(b'revisions to select or follow from'),
4540 _(b'revisions to select or follow from'),
4542 _(b'REV'),
4541 _(b'REV'),
4543 ),
4542 ),
4544 (
4543 (
4545 b'L',
4544 b'L',
4546 b'line-range',
4545 b'line-range',
4547 [],
4546 [],
4548 _(b'follow line range of specified file (EXPERIMENTAL)'),
4547 _(b'follow line range of specified file (EXPERIMENTAL)'),
4549 _(b'FILE,RANGE'),
4548 _(b'FILE,RANGE'),
4550 ),
4549 ),
4551 (
4550 (
4552 b'',
4551 b'',
4553 b'removed',
4552 b'removed',
4554 None,
4553 None,
4555 _(b'include revisions where files were removed'),
4554 _(b'include revisions where files were removed'),
4556 ),
4555 ),
4557 (
4556 (
4558 b'm',
4557 b'm',
4559 b'only-merges',
4558 b'only-merges',
4560 None,
4559 None,
4561 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4560 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4562 ),
4561 ),
4563 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4562 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4564 (
4563 (
4565 b'',
4564 b'',
4566 b'only-branch',
4565 b'only-branch',
4567 [],
4566 [],
4568 _(
4567 _(
4569 b'show only changesets within the given named branch (DEPRECATED)'
4568 b'show only changesets within the given named branch (DEPRECATED)'
4570 ),
4569 ),
4571 _(b'BRANCH'),
4570 _(b'BRANCH'),
4572 ),
4571 ),
4573 (
4572 (
4574 b'b',
4573 b'b',
4575 b'branch',
4574 b'branch',
4576 [],
4575 [],
4577 _(b'show changesets within the given named branch'),
4576 _(b'show changesets within the given named branch'),
4578 _(b'BRANCH'),
4577 _(b'BRANCH'),
4579 ),
4578 ),
4580 (
4579 (
4581 b'B',
4580 b'B',
4582 b'bookmark',
4581 b'bookmark',
4583 [],
4582 [],
4584 _(b"show changesets within the given bookmark"),
4583 _(b"show changesets within the given bookmark"),
4585 _(b'BOOKMARK'),
4584 _(b'BOOKMARK'),
4586 ),
4585 ),
4587 (
4586 (
4588 b'P',
4587 b'P',
4589 b'prune',
4588 b'prune',
4590 [],
4589 [],
4591 _(b'do not display revision or any of its ancestors'),
4590 _(b'do not display revision or any of its ancestors'),
4592 _(b'REV'),
4591 _(b'REV'),
4593 ),
4592 ),
4594 ]
4593 ]
4595 + logopts
4594 + logopts
4596 + walkopts,
4595 + walkopts,
4597 _(b'[OPTION]... [FILE]'),
4596 _(b'[OPTION]... [FILE]'),
4598 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4597 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4599 helpbasic=True,
4598 helpbasic=True,
4600 inferrepo=True,
4599 inferrepo=True,
4601 intents={INTENT_READONLY},
4600 intents={INTENT_READONLY},
4602 )
4601 )
4603 def log(ui, repo, *pats, **opts):
4602 def log(ui, repo, *pats, **opts):
4604 """show revision history of entire repository or files
4603 """show revision history of entire repository or files
4605
4604
4606 Print the revision history of the specified files or the entire
4605 Print the revision history of the specified files or the entire
4607 project.
4606 project.
4608
4607
4609 If no revision range is specified, the default is ``tip:0`` unless
4608 If no revision range is specified, the default is ``tip:0`` unless
4610 --follow is set.
4609 --follow is set.
4611
4610
4612 File history is shown without following rename or copy history of
4611 File history is shown without following rename or copy history of
4613 files. Use -f/--follow with a filename to follow history across
4612 files. Use -f/--follow with a filename to follow history across
4614 renames and copies. --follow without a filename will only show
4613 renames and copies. --follow without a filename will only show
4615 ancestors of the starting revisions. The starting revisions can be
4614 ancestors of the starting revisions. The starting revisions can be
4616 specified by -r/--rev, which default to the working directory parent.
4615 specified by -r/--rev, which default to the working directory parent.
4617
4616
4618 By default this command prints revision number and changeset id,
4617 By default this command prints revision number and changeset id,
4619 tags, non-trivial parents, user, date and time, and a summary for
4618 tags, non-trivial parents, user, date and time, and a summary for
4620 each commit. When the -v/--verbose switch is used, the list of
4619 each commit. When the -v/--verbose switch is used, the list of
4621 changed files and full commit message are shown.
4620 changed files and full commit message are shown.
4622
4621
4623 With --graph the revisions are shown as an ASCII art DAG with the most
4622 With --graph the revisions are shown as an ASCII art DAG with the most
4624 recent changeset at the top.
4623 recent changeset at the top.
4625 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4624 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4626 involved in an unresolved merge conflict, '_' closes a branch,
4625 involved in an unresolved merge conflict, '_' closes a branch,
4627 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4626 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4628 changeset from the lines below is a parent of the 'o' merge on the same
4627 changeset from the lines below is a parent of the 'o' merge on the same
4629 line.
4628 line.
4630 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4629 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4631 of a '|' indicates one or more revisions in a path are omitted.
4630 of a '|' indicates one or more revisions in a path are omitted.
4632
4631
4633 .. container:: verbose
4632 .. container:: verbose
4634
4633
4635 Use -L/--line-range FILE,M:N options to follow the history of lines
4634 Use -L/--line-range FILE,M:N options to follow the history of lines
4636 from M to N in FILE. With -p/--patch only diff hunks affecting
4635 from M to N in FILE. With -p/--patch only diff hunks affecting
4637 specified line range will be shown. This option requires --follow;
4636 specified line range will be shown. This option requires --follow;
4638 it can be specified multiple times. Currently, this option is not
4637 it can be specified multiple times. Currently, this option is not
4639 compatible with --graph. This option is experimental.
4638 compatible with --graph. This option is experimental.
4640
4639
4641 .. note::
4640 .. note::
4642
4641
4643 :hg:`log --patch` may generate unexpected diff output for merge
4642 :hg:`log --patch` may generate unexpected diff output for merge
4644 changesets, as it will only compare the merge changeset against
4643 changesets, as it will only compare the merge changeset against
4645 its first parent. Also, only files different from BOTH parents
4644 its first parent. Also, only files different from BOTH parents
4646 will appear in files:.
4645 will appear in files:.
4647
4646
4648 .. note::
4647 .. note::
4649
4648
4650 For performance reasons, :hg:`log FILE` may omit duplicate changes
4649 For performance reasons, :hg:`log FILE` may omit duplicate changes
4651 made on branches and will not show removals or mode changes. To
4650 made on branches and will not show removals or mode changes. To
4652 see all such changes, use the --removed switch.
4651 see all such changes, use the --removed switch.
4653
4652
4654 .. container:: verbose
4653 .. container:: verbose
4655
4654
4656 .. note::
4655 .. note::
4657
4656
4658 The history resulting from -L/--line-range options depends on diff
4657 The history resulting from -L/--line-range options depends on diff
4659 options; for instance if white-spaces are ignored, respective changes
4658 options; for instance if white-spaces are ignored, respective changes
4660 with only white-spaces in specified line range will not be listed.
4659 with only white-spaces in specified line range will not be listed.
4661
4660
4662 .. container:: verbose
4661 .. container:: verbose
4663
4662
4664 Some examples:
4663 Some examples:
4665
4664
4666 - changesets with full descriptions and file lists::
4665 - changesets with full descriptions and file lists::
4667
4666
4668 hg log -v
4667 hg log -v
4669
4668
4670 - changesets ancestral to the working directory::
4669 - changesets ancestral to the working directory::
4671
4670
4672 hg log -f
4671 hg log -f
4673
4672
4674 - last 10 commits on the current branch::
4673 - last 10 commits on the current branch::
4675
4674
4676 hg log -l 10 -b .
4675 hg log -l 10 -b .
4677
4676
4678 - changesets showing all modifications of a file, including removals::
4677 - changesets showing all modifications of a file, including removals::
4679
4678
4680 hg log --removed file.c
4679 hg log --removed file.c
4681
4680
4682 - all changesets that touch a directory, with diffs, excluding merges::
4681 - all changesets that touch a directory, with diffs, excluding merges::
4683
4682
4684 hg log -Mp lib/
4683 hg log -Mp lib/
4685
4684
4686 - all revision numbers that match a keyword::
4685 - all revision numbers that match a keyword::
4687
4686
4688 hg log -k bug --template "{rev}\\n"
4687 hg log -k bug --template "{rev}\\n"
4689
4688
4690 - the full hash identifier of the working directory parent::
4689 - the full hash identifier of the working directory parent::
4691
4690
4692 hg log -r . --template "{node}\\n"
4691 hg log -r . --template "{node}\\n"
4693
4692
4694 - list available log templates::
4693 - list available log templates::
4695
4694
4696 hg log -T list
4695 hg log -T list
4697
4696
4698 - check if a given changeset is included in a tagged release::
4697 - check if a given changeset is included in a tagged release::
4699
4698
4700 hg log -r "a21ccf and ancestor(1.9)"
4699 hg log -r "a21ccf and ancestor(1.9)"
4701
4700
4702 - find all changesets by some user in a date range::
4701 - find all changesets by some user in a date range::
4703
4702
4704 hg log -k alice -d "may 2008 to jul 2008"
4703 hg log -k alice -d "may 2008 to jul 2008"
4705
4704
4706 - summary of all changesets after the last tag::
4705 - summary of all changesets after the last tag::
4707
4706
4708 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4707 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4709
4708
4710 - changesets touching lines 13 to 23 for file.c::
4709 - changesets touching lines 13 to 23 for file.c::
4711
4710
4712 hg log -L file.c,13:23
4711 hg log -L file.c,13:23
4713
4712
4714 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4713 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4715 main.c with patch::
4714 main.c with patch::
4716
4715
4717 hg log -L file.c,13:23 -L main.c,2:6 -p
4716 hg log -L file.c,13:23 -L main.c,2:6 -p
4718
4717
4719 See :hg:`help dates` for a list of formats valid for -d/--date.
4718 See :hg:`help dates` for a list of formats valid for -d/--date.
4720
4719
4721 See :hg:`help revisions` for more about specifying and ordering
4720 See :hg:`help revisions` for more about specifying and ordering
4722 revisions.
4721 revisions.
4723
4722
4724 See :hg:`help templates` for more about pre-packaged styles and
4723 See :hg:`help templates` for more about pre-packaged styles and
4725 specifying custom templates. The default template used by the log
4724 specifying custom templates. The default template used by the log
4726 command can be customized via the ``command-templates.log`` configuration
4725 command can be customized via the ``command-templates.log`` configuration
4727 setting.
4726 setting.
4728
4727
4729 Returns 0 on success.
4728 Returns 0 on success.
4730
4729
4731 """
4730 """
4732 opts = pycompat.byteskwargs(opts)
4731 opts = pycompat.byteskwargs(opts)
4733 linerange = opts.get(b'line_range')
4732 linerange = opts.get(b'line_range')
4734
4733
4735 if linerange and not opts.get(b'follow'):
4734 if linerange and not opts.get(b'follow'):
4736 raise error.InputError(_(b'--line-range requires --follow'))
4735 raise error.InputError(_(b'--line-range requires --follow'))
4737
4736
4738 if linerange and pats:
4737 if linerange and pats:
4739 # TODO: take pats as patterns with no line-range filter
4738 # TODO: take pats as patterns with no line-range filter
4740 raise error.InputError(
4739 raise error.InputError(
4741 _(b'FILE arguments are not compatible with --line-range option')
4740 _(b'FILE arguments are not compatible with --line-range option')
4742 )
4741 )
4743
4742
4744 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4743 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4745 walk_opts = logcmdutil.parseopts(ui, pats, opts)
4744 walk_opts = logcmdutil.parseopts(ui, pats, opts)
4746 revs, differ = logcmdutil.getrevs(repo, walk_opts)
4745 revs, differ = logcmdutil.getrevs(repo, walk_opts)
4747 if linerange:
4746 if linerange:
4748 # TODO: should follow file history from logcmdutil._initialrevs(),
4747 # TODO: should follow file history from logcmdutil._initialrevs(),
4749 # then filter the result by logcmdutil._makerevset() and --limit
4748 # then filter the result by logcmdutil._makerevset() and --limit
4750 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4749 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4751
4750
4752 getcopies = None
4751 getcopies = None
4753 if opts.get(b'copies'):
4752 if opts.get(b'copies'):
4754 endrev = None
4753 endrev = None
4755 if revs:
4754 if revs:
4756 endrev = revs.max() + 1
4755 endrev = revs.max() + 1
4757 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4756 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4758
4757
4759 ui.pager(b'log')
4758 ui.pager(b'log')
4760 displayer = logcmdutil.changesetdisplayer(
4759 displayer = logcmdutil.changesetdisplayer(
4761 ui, repo, opts, differ, buffered=True
4760 ui, repo, opts, differ, buffered=True
4762 )
4761 )
4763 if opts.get(b'graph'):
4762 if opts.get(b'graph'):
4764 displayfn = logcmdutil.displaygraphrevs
4763 displayfn = logcmdutil.displaygraphrevs
4765 else:
4764 else:
4766 displayfn = logcmdutil.displayrevs
4765 displayfn = logcmdutil.displayrevs
4767 displayfn(ui, repo, revs, displayer, getcopies)
4766 displayfn(ui, repo, revs, displayer, getcopies)
4768
4767
4769
4768
4770 @command(
4769 @command(
4771 b'manifest',
4770 b'manifest',
4772 [
4771 [
4773 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4772 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4774 (b'', b'all', False, _(b"list files from all revisions")),
4773 (b'', b'all', False, _(b"list files from all revisions")),
4775 ]
4774 ]
4776 + formatteropts,
4775 + formatteropts,
4777 _(b'[-r REV]'),
4776 _(b'[-r REV]'),
4778 helpcategory=command.CATEGORY_MAINTENANCE,
4777 helpcategory=command.CATEGORY_MAINTENANCE,
4779 intents={INTENT_READONLY},
4778 intents={INTENT_READONLY},
4780 )
4779 )
4781 def manifest(ui, repo, node=None, rev=None, **opts):
4780 def manifest(ui, repo, node=None, rev=None, **opts):
4782 """output the current or given revision of the project manifest
4781 """output the current or given revision of the project manifest
4783
4782
4784 Print a list of version controlled files for the given revision.
4783 Print a list of version controlled files for the given revision.
4785 If no revision is given, the first parent of the working directory
4784 If no revision is given, the first parent of the working directory
4786 is used, or the null revision if no revision is checked out.
4785 is used, or the null revision if no revision is checked out.
4787
4786
4788 With -v, print file permissions, symlink and executable bits.
4787 With -v, print file permissions, symlink and executable bits.
4789 With --debug, print file revision hashes.
4788 With --debug, print file revision hashes.
4790
4789
4791 If option --all is specified, the list of all files from all revisions
4790 If option --all is specified, the list of all files from all revisions
4792 is printed. This includes deleted and renamed files.
4791 is printed. This includes deleted and renamed files.
4793
4792
4794 Returns 0 on success.
4793 Returns 0 on success.
4795 """
4794 """
4796 opts = pycompat.byteskwargs(opts)
4795 opts = pycompat.byteskwargs(opts)
4797 fm = ui.formatter(b'manifest', opts)
4796 fm = ui.formatter(b'manifest', opts)
4798
4797
4799 if opts.get(b'all'):
4798 if opts.get(b'all'):
4800 if rev or node:
4799 if rev or node:
4801 raise error.InputError(_(b"can't specify a revision with --all"))
4800 raise error.InputError(_(b"can't specify a revision with --all"))
4802
4801
4803 res = set()
4802 res = set()
4804 for rev in repo:
4803 for rev in repo:
4805 ctx = repo[rev]
4804 ctx = repo[rev]
4806 res |= set(ctx.files())
4805 res |= set(ctx.files())
4807
4806
4808 ui.pager(b'manifest')
4807 ui.pager(b'manifest')
4809 for f in sorted(res):
4808 for f in sorted(res):
4810 fm.startitem()
4809 fm.startitem()
4811 fm.write(b"path", b'%s\n', f)
4810 fm.write(b"path", b'%s\n', f)
4812 fm.end()
4811 fm.end()
4813 return
4812 return
4814
4813
4815 if rev and node:
4814 if rev and node:
4816 raise error.InputError(_(b"please specify just one revision"))
4815 raise error.InputError(_(b"please specify just one revision"))
4817
4816
4818 if not node:
4817 if not node:
4819 node = rev
4818 node = rev
4820
4819
4821 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4820 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4822 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4821 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4823 if node:
4822 if node:
4824 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4823 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4825 ctx = logcmdutil.revsingle(repo, node)
4824 ctx = logcmdutil.revsingle(repo, node)
4826 mf = ctx.manifest()
4825 mf = ctx.manifest()
4827 ui.pager(b'manifest')
4826 ui.pager(b'manifest')
4828 for f in ctx:
4827 for f in ctx:
4829 fm.startitem()
4828 fm.startitem()
4830 fm.context(ctx=ctx)
4829 fm.context(ctx=ctx)
4831 fl = ctx[f].flags()
4830 fl = ctx[f].flags()
4832 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4831 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4833 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4832 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4834 fm.write(b'path', b'%s\n', f)
4833 fm.write(b'path', b'%s\n', f)
4835 fm.end()
4834 fm.end()
4836
4835
4837
4836
4838 @command(
4837 @command(
4839 b'merge',
4838 b'merge',
4840 [
4839 [
4841 (
4840 (
4842 b'f',
4841 b'f',
4843 b'force',
4842 b'force',
4844 None,
4843 None,
4845 _(b'force a merge including outstanding changes (DEPRECATED)'),
4844 _(b'force a merge including outstanding changes (DEPRECATED)'),
4846 ),
4845 ),
4847 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4846 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4848 (
4847 (
4849 b'P',
4848 b'P',
4850 b'preview',
4849 b'preview',
4851 None,
4850 None,
4852 _(b'review revisions to merge (no merge is performed)'),
4851 _(b'review revisions to merge (no merge is performed)'),
4853 ),
4852 ),
4854 (b'', b'abort', None, _(b'abort the ongoing merge')),
4853 (b'', b'abort', None, _(b'abort the ongoing merge')),
4855 ]
4854 ]
4856 + mergetoolopts,
4855 + mergetoolopts,
4857 _(b'[-P] [[-r] REV]'),
4856 _(b'[-P] [[-r] REV]'),
4858 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4857 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4859 helpbasic=True,
4858 helpbasic=True,
4860 )
4859 )
4861 def merge(ui, repo, node=None, **opts):
4860 def merge(ui, repo, node=None, **opts):
4862 """merge another revision into working directory
4861 """merge another revision into working directory
4863
4862
4864 The current working directory is updated with all changes made in
4863 The current working directory is updated with all changes made in
4865 the requested revision since the last common predecessor revision.
4864 the requested revision since the last common predecessor revision.
4866
4865
4867 Files that changed between either parent are marked as changed for
4866 Files that changed between either parent are marked as changed for
4868 the next commit and a commit must be performed before any further
4867 the next commit and a commit must be performed before any further
4869 updates to the repository are allowed. The next commit will have
4868 updates to the repository are allowed. The next commit will have
4870 two parents.
4869 two parents.
4871
4870
4872 ``--tool`` can be used to specify the merge tool used for file
4871 ``--tool`` can be used to specify the merge tool used for file
4873 merges. It overrides the HGMERGE environment variable and your
4872 merges. It overrides the HGMERGE environment variable and your
4874 configuration files. See :hg:`help merge-tools` for options.
4873 configuration files. See :hg:`help merge-tools` for options.
4875
4874
4876 If no revision is specified, the working directory's parent is a
4875 If no revision is specified, the working directory's parent is a
4877 head revision, and the current branch contains exactly one other
4876 head revision, and the current branch contains exactly one other
4878 head, the other head is merged with by default. Otherwise, an
4877 head, the other head is merged with by default. Otherwise, an
4879 explicit revision with which to merge must be provided.
4878 explicit revision with which to merge must be provided.
4880
4879
4881 See :hg:`help resolve` for information on handling file conflicts.
4880 See :hg:`help resolve` for information on handling file conflicts.
4882
4881
4883 To undo an uncommitted merge, use :hg:`merge --abort` which
4882 To undo an uncommitted merge, use :hg:`merge --abort` which
4884 will check out a clean copy of the original merge parent, losing
4883 will check out a clean copy of the original merge parent, losing
4885 all changes.
4884 all changes.
4886
4885
4887 Returns 0 on success, 1 if there are unresolved files.
4886 Returns 0 on success, 1 if there are unresolved files.
4888 """
4887 """
4889
4888
4890 opts = pycompat.byteskwargs(opts)
4889 opts = pycompat.byteskwargs(opts)
4891 abort = opts.get(b'abort')
4890 abort = opts.get(b'abort')
4892 if abort and repo.dirstate.p2() == repo.nullid:
4891 if abort and repo.dirstate.p2() == repo.nullid:
4893 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4892 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4894 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4893 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4895 if abort:
4894 if abort:
4896 state = cmdutil.getunfinishedstate(repo)
4895 state = cmdutil.getunfinishedstate(repo)
4897 if state and state._opname != b'merge':
4896 if state and state._opname != b'merge':
4898 raise error.StateError(
4897 raise error.StateError(
4899 _(b'cannot abort merge with %s in progress') % (state._opname),
4898 _(b'cannot abort merge with %s in progress') % (state._opname),
4900 hint=state.hint(),
4899 hint=state.hint(),
4901 )
4900 )
4902 if node:
4901 if node:
4903 raise error.InputError(_(b"cannot specify a node with --abort"))
4902 raise error.InputError(_(b"cannot specify a node with --abort"))
4904 return hg.abortmerge(repo.ui, repo)
4903 return hg.abortmerge(repo.ui, repo)
4905
4904
4906 if opts.get(b'rev') and node:
4905 if opts.get(b'rev') and node:
4907 raise error.InputError(_(b"please specify just one revision"))
4906 raise error.InputError(_(b"please specify just one revision"))
4908 if not node:
4907 if not node:
4909 node = opts.get(b'rev')
4908 node = opts.get(b'rev')
4910
4909
4911 if node:
4910 if node:
4912 ctx = logcmdutil.revsingle(repo, node)
4911 ctx = logcmdutil.revsingle(repo, node)
4913 else:
4912 else:
4914 if ui.configbool(b'commands', b'merge.require-rev'):
4913 if ui.configbool(b'commands', b'merge.require-rev'):
4915 raise error.InputError(
4914 raise error.InputError(
4916 _(
4915 _(
4917 b'configuration requires specifying revision to merge '
4916 b'configuration requires specifying revision to merge '
4918 b'with'
4917 b'with'
4919 )
4918 )
4920 )
4919 )
4921 ctx = repo[destutil.destmerge(repo)]
4920 ctx = repo[destutil.destmerge(repo)]
4922
4921
4923 if ctx.node() is None:
4922 if ctx.node() is None:
4924 raise error.InputError(
4923 raise error.InputError(
4925 _(b'merging with the working copy has no effect')
4924 _(b'merging with the working copy has no effect')
4926 )
4925 )
4927
4926
4928 if opts.get(b'preview'):
4927 if opts.get(b'preview'):
4929 # find nodes that are ancestors of p2 but not of p1
4928 # find nodes that are ancestors of p2 but not of p1
4930 p1 = repo[b'.'].node()
4929 p1 = repo[b'.'].node()
4931 p2 = ctx.node()
4930 p2 = ctx.node()
4932 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4931 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4933
4932
4934 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4933 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4935 for node in nodes:
4934 for node in nodes:
4936 displayer.show(repo[node])
4935 displayer.show(repo[node])
4937 displayer.close()
4936 displayer.close()
4938 return 0
4937 return 0
4939
4938
4940 # ui.forcemerge is an internal variable, do not document
4939 # ui.forcemerge is an internal variable, do not document
4941 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4940 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4942 with ui.configoverride(overrides, b'merge'):
4941 with ui.configoverride(overrides, b'merge'):
4943 force = opts.get(b'force')
4942 force = opts.get(b'force')
4944 labels = [b'working copy', b'merge rev', b'common ancestor']
4943 labels = [b'working copy', b'merge rev', b'common ancestor']
4945 return hg.merge(ctx, force=force, labels=labels)
4944 return hg.merge(ctx, force=force, labels=labels)
4946
4945
4947
4946
4948 statemod.addunfinished(
4947 statemod.addunfinished(
4949 b'merge',
4948 b'merge',
4950 fname=None,
4949 fname=None,
4951 clearable=True,
4950 clearable=True,
4952 allowcommit=True,
4951 allowcommit=True,
4953 cmdmsg=_(b'outstanding uncommitted merge'),
4952 cmdmsg=_(b'outstanding uncommitted merge'),
4954 abortfunc=hg.abortmerge,
4953 abortfunc=hg.abortmerge,
4955 statushint=_(
4954 statushint=_(
4956 b'To continue: hg commit\nTo abort: hg merge --abort'
4955 b'To continue: hg commit\nTo abort: hg merge --abort'
4957 ),
4956 ),
4958 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4957 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4959 )
4958 )
4960
4959
4961
4960
4962 @command(
4961 @command(
4963 b'outgoing|out',
4962 b'outgoing|out',
4964 [
4963 [
4965 (
4964 (
4966 b'f',
4965 b'f',
4967 b'force',
4966 b'force',
4968 None,
4967 None,
4969 _(b'run even when the destination is unrelated'),
4968 _(b'run even when the destination is unrelated'),
4970 ),
4969 ),
4971 (
4970 (
4972 b'r',
4971 b'r',
4973 b'rev',
4972 b'rev',
4974 [],
4973 [],
4975 _(b'a changeset intended to be included in the destination'),
4974 _(b'a changeset intended to be included in the destination'),
4976 _(b'REV'),
4975 _(b'REV'),
4977 ),
4976 ),
4978 (b'n', b'newest-first', None, _(b'show newest record first')),
4977 (b'n', b'newest-first', None, _(b'show newest record first')),
4979 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4978 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4980 (
4979 (
4981 b'b',
4980 b'b',
4982 b'branch',
4981 b'branch',
4983 [],
4982 [],
4984 _(b'a specific branch you would like to push'),
4983 _(b'a specific branch you would like to push'),
4985 _(b'BRANCH'),
4984 _(b'BRANCH'),
4986 ),
4985 ),
4987 ]
4986 ]
4988 + logopts
4987 + logopts
4989 + remoteopts
4988 + remoteopts
4990 + subrepoopts,
4989 + subrepoopts,
4991 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]...'),
4990 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]...'),
4992 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4991 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4993 )
4992 )
4994 def outgoing(ui, repo, *dests, **opts):
4993 def outgoing(ui, repo, *dests, **opts):
4995 """show changesets not found in the destination
4994 """show changesets not found in the destination
4996
4995
4997 Show changesets not found in the specified destination repository
4996 Show changesets not found in the specified destination repository
4998 or the default push location. These are the changesets that would
4997 or the default push location. These are the changesets that would
4999 be pushed if a push was requested.
4998 be pushed if a push was requested.
5000
4999
5001 See pull for details of valid destination formats.
5000 See pull for details of valid destination formats.
5002
5001
5003 .. container:: verbose
5002 .. container:: verbose
5004
5003
5005 With -B/--bookmarks, the result of bookmark comparison between
5004 With -B/--bookmarks, the result of bookmark comparison between
5006 local and remote repositories is displayed. With -v/--verbose,
5005 local and remote repositories is displayed. With -v/--verbose,
5007 status is also displayed for each bookmark like below::
5006 status is also displayed for each bookmark like below::
5008
5007
5009 BM1 01234567890a added
5008 BM1 01234567890a added
5010 BM2 deleted
5009 BM2 deleted
5011 BM3 234567890abc advanced
5010 BM3 234567890abc advanced
5012 BM4 34567890abcd diverged
5011 BM4 34567890abcd diverged
5013 BM5 4567890abcde changed
5012 BM5 4567890abcde changed
5014
5013
5015 The action taken when pushing depends on the
5014 The action taken when pushing depends on the
5016 status of each bookmark:
5015 status of each bookmark:
5017
5016
5018 :``added``: push with ``-B`` will create it
5017 :``added``: push with ``-B`` will create it
5019 :``deleted``: push with ``-B`` will delete it
5018 :``deleted``: push with ``-B`` will delete it
5020 :``advanced``: push will update it
5019 :``advanced``: push will update it
5021 :``diverged``: push with ``-B`` will update it
5020 :``diverged``: push with ``-B`` will update it
5022 :``changed``: push with ``-B`` will update it
5021 :``changed``: push with ``-B`` will update it
5023
5022
5024 From the point of view of pushing behavior, bookmarks
5023 From the point of view of pushing behavior, bookmarks
5025 existing only in the remote repository are treated as
5024 existing only in the remote repository are treated as
5026 ``deleted``, even if it is in fact added remotely.
5025 ``deleted``, even if it is in fact added remotely.
5027
5026
5028 Returns 0 if there are outgoing changes, 1 otherwise.
5027 Returns 0 if there are outgoing changes, 1 otherwise.
5029 """
5028 """
5030 opts = pycompat.byteskwargs(opts)
5029 opts = pycompat.byteskwargs(opts)
5031 if opts.get(b'bookmarks'):
5030 if opts.get(b'bookmarks'):
5032 for path in urlutil.get_push_paths(repo, ui, dests):
5031 for path in urlutil.get_push_paths(repo, ui, dests):
5033 dest = path.pushloc or path.loc
5032 dest = path.pushloc or path.loc
5034 other = hg.peer(repo, opts, dest)
5033 other = hg.peer(repo, opts, dest)
5035 try:
5034 try:
5036 if b'bookmarks' not in other.listkeys(b'namespaces'):
5035 if b'bookmarks' not in other.listkeys(b'namespaces'):
5037 ui.warn(_(b"remote doesn't support bookmarks\n"))
5036 ui.warn(_(b"remote doesn't support bookmarks\n"))
5038 return 0
5037 return 0
5039 ui.status(
5038 ui.status(
5040 _(b'comparing with %s\n') % urlutil.hidepassword(dest)
5039 _(b'comparing with %s\n') % urlutil.hidepassword(dest)
5041 )
5040 )
5042 ui.pager(b'outgoing')
5041 ui.pager(b'outgoing')
5043 return bookmarks.outgoing(ui, repo, other)
5042 return bookmarks.outgoing(ui, repo, other)
5044 finally:
5043 finally:
5045 other.close()
5044 other.close()
5046
5045
5047 return hg.outgoing(ui, repo, dests, opts)
5046 return hg.outgoing(ui, repo, dests, opts)
5048
5047
5049
5048
5050 @command(
5049 @command(
5051 b'parents',
5050 b'parents',
5052 [
5051 [
5053 (
5052 (
5054 b'r',
5053 b'r',
5055 b'rev',
5054 b'rev',
5056 b'',
5055 b'',
5057 _(b'show parents of the specified revision'),
5056 _(b'show parents of the specified revision'),
5058 _(b'REV'),
5057 _(b'REV'),
5059 ),
5058 ),
5060 ]
5059 ]
5061 + templateopts,
5060 + templateopts,
5062 _(b'[-r REV] [FILE]'),
5061 _(b'[-r REV] [FILE]'),
5063 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5062 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5064 inferrepo=True,
5063 inferrepo=True,
5065 )
5064 )
5066 def parents(ui, repo, file_=None, **opts):
5065 def parents(ui, repo, file_=None, **opts):
5067 """show the parents of the working directory or revision (DEPRECATED)
5066 """show the parents of the working directory or revision (DEPRECATED)
5068
5067
5069 Print the working directory's parent revisions. If a revision is
5068 Print the working directory's parent revisions. If a revision is
5070 given via -r/--rev, the parent of that revision will be printed.
5069 given via -r/--rev, the parent of that revision will be printed.
5071 If a file argument is given, the revision in which the file was
5070 If a file argument is given, the revision in which the file was
5072 last changed (before the working directory revision or the
5071 last changed (before the working directory revision or the
5073 argument to --rev if given) is printed.
5072 argument to --rev if given) is printed.
5074
5073
5075 This command is equivalent to::
5074 This command is equivalent to::
5076
5075
5077 hg log -r "p1()+p2()" or
5076 hg log -r "p1()+p2()" or
5078 hg log -r "p1(REV)+p2(REV)" or
5077 hg log -r "p1(REV)+p2(REV)" or
5079 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5078 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5080 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5079 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5081
5080
5082 See :hg:`summary` and :hg:`help revsets` for related information.
5081 See :hg:`summary` and :hg:`help revsets` for related information.
5083
5082
5084 Returns 0 on success.
5083 Returns 0 on success.
5085 """
5084 """
5086
5085
5087 opts = pycompat.byteskwargs(opts)
5086 opts = pycompat.byteskwargs(opts)
5088 rev = opts.get(b'rev')
5087 rev = opts.get(b'rev')
5089 if rev:
5088 if rev:
5090 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5089 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5091 ctx = logcmdutil.revsingle(repo, rev, None)
5090 ctx = logcmdutil.revsingle(repo, rev, None)
5092
5091
5093 if file_:
5092 if file_:
5094 m = scmutil.match(ctx, (file_,), opts)
5093 m = scmutil.match(ctx, (file_,), opts)
5095 if m.anypats() or len(m.files()) != 1:
5094 if m.anypats() or len(m.files()) != 1:
5096 raise error.InputError(_(b'can only specify an explicit filename'))
5095 raise error.InputError(_(b'can only specify an explicit filename'))
5097 file_ = m.files()[0]
5096 file_ = m.files()[0]
5098 filenodes = []
5097 filenodes = []
5099 for cp in ctx.parents():
5098 for cp in ctx.parents():
5100 if not cp:
5099 if not cp:
5101 continue
5100 continue
5102 try:
5101 try:
5103 filenodes.append(cp.filenode(file_))
5102 filenodes.append(cp.filenode(file_))
5104 except error.LookupError:
5103 except error.LookupError:
5105 pass
5104 pass
5106 if not filenodes:
5105 if not filenodes:
5107 raise error.InputError(_(b"'%s' not found in manifest") % file_)
5106 raise error.InputError(_(b"'%s' not found in manifest") % file_)
5108 p = []
5107 p = []
5109 for fn in filenodes:
5108 for fn in filenodes:
5110 fctx = repo.filectx(file_, fileid=fn)
5109 fctx = repo.filectx(file_, fileid=fn)
5111 p.append(fctx.node())
5110 p.append(fctx.node())
5112 else:
5111 else:
5113 p = [cp.node() for cp in ctx.parents()]
5112 p = [cp.node() for cp in ctx.parents()]
5114
5113
5115 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5114 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5116 for n in p:
5115 for n in p:
5117 if n != repo.nullid:
5116 if n != repo.nullid:
5118 displayer.show(repo[n])
5117 displayer.show(repo[n])
5119 displayer.close()
5118 displayer.close()
5120
5119
5121
5120
5122 @command(
5121 @command(
5123 b'paths',
5122 b'paths',
5124 formatteropts,
5123 formatteropts,
5125 _(b'[NAME]'),
5124 _(b'[NAME]'),
5126 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5125 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5127 optionalrepo=True,
5126 optionalrepo=True,
5128 intents={INTENT_READONLY},
5127 intents={INTENT_READONLY},
5129 )
5128 )
5130 def paths(ui, repo, search=None, **opts):
5129 def paths(ui, repo, search=None, **opts):
5131 """show aliases for remote repositories
5130 """show aliases for remote repositories
5132
5131
5133 Show definition of symbolic path name NAME. If no name is given,
5132 Show definition of symbolic path name NAME. If no name is given,
5134 show definition of all available names.
5133 show definition of all available names.
5135
5134
5136 Option -q/--quiet suppresses all output when searching for NAME
5135 Option -q/--quiet suppresses all output when searching for NAME
5137 and shows only the path names when listing all definitions.
5136 and shows only the path names when listing all definitions.
5138
5137
5139 Path names are defined in the [paths] section of your
5138 Path names are defined in the [paths] section of your
5140 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5139 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5141 repository, ``.hg/hgrc`` is used, too.
5140 repository, ``.hg/hgrc`` is used, too.
5142
5141
5143 The path names ``default`` and ``default-push`` have a special
5142 The path names ``default`` and ``default-push`` have a special
5144 meaning. When performing a push or pull operation, they are used
5143 meaning. When performing a push or pull operation, they are used
5145 as fallbacks if no location is specified on the command-line.
5144 as fallbacks if no location is specified on the command-line.
5146 When ``default-push`` is set, it will be used for push and
5145 When ``default-push`` is set, it will be used for push and
5147 ``default`` will be used for pull; otherwise ``default`` is used
5146 ``default`` will be used for pull; otherwise ``default`` is used
5148 as the fallback for both. When cloning a repository, the clone
5147 as the fallback for both. When cloning a repository, the clone
5149 source is written as ``default`` in ``.hg/hgrc``.
5148 source is written as ``default`` in ``.hg/hgrc``.
5150
5149
5151 .. note::
5150 .. note::
5152
5151
5153 ``default`` and ``default-push`` apply to all inbound (e.g.
5152 ``default`` and ``default-push`` apply to all inbound (e.g.
5154 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5153 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5155 and :hg:`bundle`) operations.
5154 and :hg:`bundle`) operations.
5156
5155
5157 See :hg:`help urls` for more information.
5156 See :hg:`help urls` for more information.
5158
5157
5159 .. container:: verbose
5158 .. container:: verbose
5160
5159
5161 Template:
5160 Template:
5162
5161
5163 The following keywords are supported. See also :hg:`help templates`.
5162 The following keywords are supported. See also :hg:`help templates`.
5164
5163
5165 :name: String. Symbolic name of the path alias.
5164 :name: String. Symbolic name of the path alias.
5166 :pushurl: String. URL for push operations.
5165 :pushurl: String. URL for push operations.
5167 :url: String. URL or directory path for the other operations.
5166 :url: String. URL or directory path for the other operations.
5168
5167
5169 Returns 0 on success.
5168 Returns 0 on success.
5170 """
5169 """
5171
5170
5172 opts = pycompat.byteskwargs(opts)
5171 opts = pycompat.byteskwargs(opts)
5173
5172
5174 pathitems = urlutil.list_paths(ui, search)
5173 pathitems = urlutil.list_paths(ui, search)
5175 ui.pager(b'paths')
5174 ui.pager(b'paths')
5176
5175
5177 fm = ui.formatter(b'paths', opts)
5176 fm = ui.formatter(b'paths', opts)
5178 if fm.isplain():
5177 if fm.isplain():
5179 hidepassword = urlutil.hidepassword
5178 hidepassword = urlutil.hidepassword
5180 else:
5179 else:
5181 hidepassword = bytes
5180 hidepassword = bytes
5182 if ui.quiet:
5181 if ui.quiet:
5183 namefmt = b'%s\n'
5182 namefmt = b'%s\n'
5184 else:
5183 else:
5185 namefmt = b'%s = '
5184 namefmt = b'%s = '
5186 showsubopts = not search and not ui.quiet
5185 showsubopts = not search and not ui.quiet
5187
5186
5188 for name, path in pathitems:
5187 for name, path in pathitems:
5189 fm.startitem()
5188 fm.startitem()
5190 fm.condwrite(not search, b'name', namefmt, name)
5189 fm.condwrite(not search, b'name', namefmt, name)
5191 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5190 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5192 for subopt, value in sorted(path.suboptions.items()):
5191 for subopt, value in sorted(path.suboptions.items()):
5193 assert subopt not in (b'name', b'url')
5192 assert subopt not in (b'name', b'url')
5194 if showsubopts:
5193 if showsubopts:
5195 fm.plain(b'%s:%s = ' % (name, subopt))
5194 fm.plain(b'%s:%s = ' % (name, subopt))
5196 if isinstance(value, bool):
5195 if isinstance(value, bool):
5197 if value:
5196 if value:
5198 value = b'yes'
5197 value = b'yes'
5199 else:
5198 else:
5200 value = b'no'
5199 value = b'no'
5201 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5200 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5202
5201
5203 fm.end()
5202 fm.end()
5204
5203
5205 if search and not pathitems:
5204 if search and not pathitems:
5206 if not ui.quiet:
5205 if not ui.quiet:
5207 ui.warn(_(b"not found!\n"))
5206 ui.warn(_(b"not found!\n"))
5208 return 1
5207 return 1
5209 else:
5208 else:
5210 return 0
5209 return 0
5211
5210
5212
5211
5213 @command(
5212 @command(
5214 b'phase',
5213 b'phase',
5215 [
5214 [
5216 (b'p', b'public', False, _(b'set changeset phase to public')),
5215 (b'p', b'public', False, _(b'set changeset phase to public')),
5217 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5216 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5218 (b's', b'secret', False, _(b'set changeset phase to secret')),
5217 (b's', b'secret', False, _(b'set changeset phase to secret')),
5219 (b'f', b'force', False, _(b'allow to move boundary backward')),
5218 (b'f', b'force', False, _(b'allow to move boundary backward')),
5220 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5219 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5221 ],
5220 ],
5222 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5221 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5223 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5222 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5224 )
5223 )
5225 def phase(ui, repo, *revs, **opts):
5224 def phase(ui, repo, *revs, **opts):
5226 """set or show the current phase name
5225 """set or show the current phase name
5227
5226
5228 With no argument, show the phase name of the current revision(s).
5227 With no argument, show the phase name of the current revision(s).
5229
5228
5230 With one of -p/--public, -d/--draft or -s/--secret, change the
5229 With one of -p/--public, -d/--draft or -s/--secret, change the
5231 phase value of the specified revisions.
5230 phase value of the specified revisions.
5232
5231
5233 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5232 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5234 lower phase to a higher phase. Phases are ordered as follows::
5233 lower phase to a higher phase. Phases are ordered as follows::
5235
5234
5236 public < draft < secret
5235 public < draft < secret
5237
5236
5238 Returns 0 on success, 1 if some phases could not be changed.
5237 Returns 0 on success, 1 if some phases could not be changed.
5239
5238
5240 (For more information about the phases concept, see :hg:`help phases`.)
5239 (For more information about the phases concept, see :hg:`help phases`.)
5241 """
5240 """
5242 opts = pycompat.byteskwargs(opts)
5241 opts = pycompat.byteskwargs(opts)
5243 # search for a unique phase argument
5242 # search for a unique phase argument
5244 targetphase = None
5243 targetphase = None
5245 for idx, name in enumerate(phases.cmdphasenames):
5244 for idx, name in enumerate(phases.cmdphasenames):
5246 if opts[name]:
5245 if opts[name]:
5247 if targetphase is not None:
5246 if targetphase is not None:
5248 raise error.InputError(_(b'only one phase can be specified'))
5247 raise error.InputError(_(b'only one phase can be specified'))
5249 targetphase = idx
5248 targetphase = idx
5250
5249
5251 # look for specified revision
5250 # look for specified revision
5252 revs = list(revs)
5251 revs = list(revs)
5253 revs.extend(opts[b'rev'])
5252 revs.extend(opts[b'rev'])
5254 if revs:
5253 if revs:
5255 revs = logcmdutil.revrange(repo, revs)
5254 revs = logcmdutil.revrange(repo, revs)
5256 else:
5255 else:
5257 # display both parents as the second parent phase can influence
5256 # display both parents as the second parent phase can influence
5258 # the phase of a merge commit
5257 # the phase of a merge commit
5259 revs = [c.rev() for c in repo[None].parents()]
5258 revs = [c.rev() for c in repo[None].parents()]
5260
5259
5261 ret = 0
5260 ret = 0
5262 if targetphase is None:
5261 if targetphase is None:
5263 # display
5262 # display
5264 for r in revs:
5263 for r in revs:
5265 ctx = repo[r]
5264 ctx = repo[r]
5266 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5265 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5267 else:
5266 else:
5268 with repo.lock(), repo.transaction(b"phase") as tr:
5267 with repo.lock(), repo.transaction(b"phase") as tr:
5269 # set phase
5268 # set phase
5270 if not revs:
5269 if not revs:
5271 raise error.InputError(_(b'empty revision set'))
5270 raise error.InputError(_(b'empty revision set'))
5272 nodes = [repo[r].node() for r in revs]
5271 nodes = [repo[r].node() for r in revs]
5273 # moving revision from public to draft may hide them
5272 # moving revision from public to draft may hide them
5274 # We have to check result on an unfiltered repository
5273 # We have to check result on an unfiltered repository
5275 unfi = repo.unfiltered()
5274 unfi = repo.unfiltered()
5276 getphase = unfi._phasecache.phase
5275 getphase = unfi._phasecache.phase
5277 olddata = [getphase(unfi, r) for r in unfi]
5276 olddata = [getphase(unfi, r) for r in unfi]
5278 phases.advanceboundary(repo, tr, targetphase, nodes)
5277 phases.advanceboundary(repo, tr, targetphase, nodes)
5279 if opts[b'force']:
5278 if opts[b'force']:
5280 phases.retractboundary(repo, tr, targetphase, nodes)
5279 phases.retractboundary(repo, tr, targetphase, nodes)
5281 getphase = unfi._phasecache.phase
5280 getphase = unfi._phasecache.phase
5282 newdata = [getphase(unfi, r) for r in unfi]
5281 newdata = [getphase(unfi, r) for r in unfi]
5283 changes = sum(newdata[r] != olddata[r] for r in unfi)
5282 changes = sum(newdata[r] != olddata[r] for r in unfi)
5284 cl = unfi.changelog
5283 cl = unfi.changelog
5285 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5284 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5286 if rejected:
5285 if rejected:
5287 ui.warn(
5286 ui.warn(
5288 _(
5287 _(
5289 b'cannot move %i changesets to a higher '
5288 b'cannot move %i changesets to a higher '
5290 b'phase, use --force\n'
5289 b'phase, use --force\n'
5291 )
5290 )
5292 % len(rejected)
5291 % len(rejected)
5293 )
5292 )
5294 ret = 1
5293 ret = 1
5295 if changes:
5294 if changes:
5296 msg = _(b'phase changed for %i changesets\n') % changes
5295 msg = _(b'phase changed for %i changesets\n') % changes
5297 if ret:
5296 if ret:
5298 ui.status(msg)
5297 ui.status(msg)
5299 else:
5298 else:
5300 ui.note(msg)
5299 ui.note(msg)
5301 else:
5300 else:
5302 ui.warn(_(b'no phases changed\n'))
5301 ui.warn(_(b'no phases changed\n'))
5303 return ret
5302 return ret
5304
5303
5305
5304
5306 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5305 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5307 """Run after a changegroup has been added via pull/unbundle
5306 """Run after a changegroup has been added via pull/unbundle
5308
5307
5309 This takes arguments below:
5308 This takes arguments below:
5310
5309
5311 :modheads: change of heads by pull/unbundle
5310 :modheads: change of heads by pull/unbundle
5312 :optupdate: updating working directory is needed or not
5311 :optupdate: updating working directory is needed or not
5313 :checkout: update destination revision (or None to default destination)
5312 :checkout: update destination revision (or None to default destination)
5314 :brev: a name, which might be a bookmark to be activated after updating
5313 :brev: a name, which might be a bookmark to be activated after updating
5315
5314
5316 return True if update raise any conflict, False otherwise.
5315 return True if update raise any conflict, False otherwise.
5317 """
5316 """
5318 if modheads == 0:
5317 if modheads == 0:
5319 return False
5318 return False
5320 if optupdate:
5319 if optupdate:
5321 try:
5320 try:
5322 return hg.updatetotally(ui, repo, checkout, brev)
5321 return hg.updatetotally(ui, repo, checkout, brev)
5323 except error.UpdateAbort as inst:
5322 except error.UpdateAbort as inst:
5324 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5323 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5325 hint = inst.hint
5324 hint = inst.hint
5326 raise error.UpdateAbort(msg, hint=hint)
5325 raise error.UpdateAbort(msg, hint=hint)
5327 if modheads is not None and modheads > 1:
5326 if modheads is not None and modheads > 1:
5328 currentbranchheads = len(repo.branchheads())
5327 currentbranchheads = len(repo.branchheads())
5329 if currentbranchheads == modheads:
5328 if currentbranchheads == modheads:
5330 ui.status(
5329 ui.status(
5331 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5330 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5332 )
5331 )
5333 elif currentbranchheads > 1:
5332 elif currentbranchheads > 1:
5334 ui.status(
5333 ui.status(
5335 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5334 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5336 )
5335 )
5337 else:
5336 else:
5338 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5337 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5339 elif not ui.configbool(b'commands', b'update.requiredest'):
5338 elif not ui.configbool(b'commands', b'update.requiredest'):
5340 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5339 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5341 return False
5340 return False
5342
5341
5343
5342
5344 @command(
5343 @command(
5345 b'pull',
5344 b'pull',
5346 [
5345 [
5347 (
5346 (
5348 b'u',
5347 b'u',
5349 b'update',
5348 b'update',
5350 None,
5349 None,
5351 _(b'update to new branch head if new descendants were pulled'),
5350 _(b'update to new branch head if new descendants were pulled'),
5352 ),
5351 ),
5353 (
5352 (
5354 b'f',
5353 b'f',
5355 b'force',
5354 b'force',
5356 None,
5355 None,
5357 _(b'run even when remote repository is unrelated'),
5356 _(b'run even when remote repository is unrelated'),
5358 ),
5357 ),
5359 (
5358 (
5360 b'',
5359 b'',
5361 b'confirm',
5360 b'confirm',
5362 None,
5361 None,
5363 _(b'confirm pull before applying changes'),
5362 _(b'confirm pull before applying changes'),
5364 ),
5363 ),
5365 (
5364 (
5366 b'r',
5365 b'r',
5367 b'rev',
5366 b'rev',
5368 [],
5367 [],
5369 _(b'a remote changeset intended to be added'),
5368 _(b'a remote changeset intended to be added'),
5370 _(b'REV'),
5369 _(b'REV'),
5371 ),
5370 ),
5372 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5371 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5373 (
5372 (
5374 b'b',
5373 b'b',
5375 b'branch',
5374 b'branch',
5376 [],
5375 [],
5377 _(b'a specific branch you would like to pull'),
5376 _(b'a specific branch you would like to pull'),
5378 _(b'BRANCH'),
5377 _(b'BRANCH'),
5379 ),
5378 ),
5380 ]
5379 ]
5381 + remoteopts,
5380 + remoteopts,
5382 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]...'),
5381 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]...'),
5383 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5382 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5384 helpbasic=True,
5383 helpbasic=True,
5385 )
5384 )
5386 def pull(ui, repo, *sources, **opts):
5385 def pull(ui, repo, *sources, **opts):
5387 """pull changes from the specified source
5386 """pull changes from the specified source
5388
5387
5389 Pull changes from a remote repository to a local one.
5388 Pull changes from a remote repository to a local one.
5390
5389
5391 This finds all changes from the repository at the specified path
5390 This finds all changes from the repository at the specified path
5392 or URL and adds them to a local repository (the current one unless
5391 or URL and adds them to a local repository (the current one unless
5393 -R is specified). By default, this does not update the copy of the
5392 -R is specified). By default, this does not update the copy of the
5394 project in the working directory.
5393 project in the working directory.
5395
5394
5396 When cloning from servers that support it, Mercurial may fetch
5395 When cloning from servers that support it, Mercurial may fetch
5397 pre-generated data. When this is done, hooks operating on incoming
5396 pre-generated data. When this is done, hooks operating on incoming
5398 changesets and changegroups may fire more than once, once for each
5397 changesets and changegroups may fire more than once, once for each
5399 pre-generated bundle and as well as for any additional remaining
5398 pre-generated bundle and as well as for any additional remaining
5400 data. See :hg:`help -e clonebundles` for more.
5399 data. See :hg:`help -e clonebundles` for more.
5401
5400
5402 Use :hg:`incoming` if you want to see what would have been added
5401 Use :hg:`incoming` if you want to see what would have been added
5403 by a pull at the time you issued this command. If you then decide
5402 by a pull at the time you issued this command. If you then decide
5404 to add those changes to the repository, you should use :hg:`pull
5403 to add those changes to the repository, you should use :hg:`pull
5405 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5404 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5406
5405
5407 If SOURCE is omitted, the 'default' path will be used.
5406 If SOURCE is omitted, the 'default' path will be used.
5408 See :hg:`help urls` for more information.
5407 See :hg:`help urls` for more information.
5409
5408
5410 If multiple sources are specified, they will be pulled sequentially as if
5409 If multiple sources are specified, they will be pulled sequentially as if
5411 the command was run multiple time. If --update is specify and the command
5410 the command was run multiple time. If --update is specify and the command
5412 will stop at the first failed --update.
5411 will stop at the first failed --update.
5413
5412
5414 Specifying bookmark as ``.`` is equivalent to specifying the active
5413 Specifying bookmark as ``.`` is equivalent to specifying the active
5415 bookmark's name.
5414 bookmark's name.
5416
5415
5417 Returns 0 on success, 1 if an update had unresolved files.
5416 Returns 0 on success, 1 if an update had unresolved files.
5418 """
5417 """
5419
5418
5420 opts = pycompat.byteskwargs(opts)
5419 opts = pycompat.byteskwargs(opts)
5421 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5420 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5422 b'update'
5421 b'update'
5423 ):
5422 ):
5424 msg = _(b'update destination required by configuration')
5423 msg = _(b'update destination required by configuration')
5425 hint = _(b'use hg pull followed by hg update DEST')
5424 hint = _(b'use hg pull followed by hg update DEST')
5426 raise error.InputError(msg, hint=hint)
5425 raise error.InputError(msg, hint=hint)
5427
5426
5428 for path in urlutil.get_pull_paths(repo, ui, sources):
5427 for path in urlutil.get_pull_paths(repo, ui, sources):
5429 source, branches = urlutil.parseurl(path.rawloc, opts.get(b'branch'))
5428 source, branches = urlutil.parseurl(path.rawloc, opts.get(b'branch'))
5430 ui.status(_(b'pulling from %s\n') % urlutil.hidepassword(source))
5429 ui.status(_(b'pulling from %s\n') % urlutil.hidepassword(source))
5431 ui.flush()
5430 ui.flush()
5432 other = hg.peer(repo, opts, source)
5431 other = hg.peer(repo, opts, source)
5433 update_conflict = None
5432 update_conflict = None
5434 try:
5433 try:
5435 revs, checkout = hg.addbranchrevs(
5434 revs, checkout = hg.addbranchrevs(
5436 repo, other, branches, opts.get(b'rev')
5435 repo, other, branches, opts.get(b'rev')
5437 )
5436 )
5438
5437
5439 pullopargs = {}
5438 pullopargs = {}
5440
5439
5441 nodes = None
5440 nodes = None
5442 if opts.get(b'bookmark') or revs:
5441 if opts.get(b'bookmark') or revs:
5443 # The list of bookmark used here is the same used to actually update
5442 # The list of bookmark used here is the same used to actually update
5444 # the bookmark names, to avoid the race from issue 4689 and we do
5443 # the bookmark names, to avoid the race from issue 4689 and we do
5445 # all lookup and bookmark queries in one go so they see the same
5444 # all lookup and bookmark queries in one go so they see the same
5446 # version of the server state (issue 4700).
5445 # version of the server state (issue 4700).
5447 nodes = []
5446 nodes = []
5448 fnodes = []
5447 fnodes = []
5449 revs = revs or []
5448 revs = revs or []
5450 if revs and not other.capable(b'lookup'):
5449 if revs and not other.capable(b'lookup'):
5451 err = _(
5450 err = _(
5452 b"other repository doesn't support revision lookup, "
5451 b"other repository doesn't support revision lookup, "
5453 b"so a rev cannot be specified."
5452 b"so a rev cannot be specified."
5454 )
5453 )
5455 raise error.Abort(err)
5454 raise error.Abort(err)
5456 with other.commandexecutor() as e:
5455 with other.commandexecutor() as e:
5457 fremotebookmarks = e.callcommand(
5456 fremotebookmarks = e.callcommand(
5458 b'listkeys', {b'namespace': b'bookmarks'}
5457 b'listkeys', {b'namespace': b'bookmarks'}
5459 )
5458 )
5460 for r in revs:
5459 for r in revs:
5461 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5460 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5462 remotebookmarks = fremotebookmarks.result()
5461 remotebookmarks = fremotebookmarks.result()
5463 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5462 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5464 pullopargs[b'remotebookmarks'] = remotebookmarks
5463 pullopargs[b'remotebookmarks'] = remotebookmarks
5465 for b in opts.get(b'bookmark', []):
5464 for b in opts.get(b'bookmark', []):
5466 b = repo._bookmarks.expandname(b)
5465 b = repo._bookmarks.expandname(b)
5467 if b not in remotebookmarks:
5466 if b not in remotebookmarks:
5468 raise error.InputError(
5467 raise error.InputError(
5469 _(b'remote bookmark %s not found!') % b
5468 _(b'remote bookmark %s not found!') % b
5470 )
5469 )
5471 nodes.append(remotebookmarks[b])
5470 nodes.append(remotebookmarks[b])
5472 for i, rev in enumerate(revs):
5471 for i, rev in enumerate(revs):
5473 node = fnodes[i].result()
5472 node = fnodes[i].result()
5474 nodes.append(node)
5473 nodes.append(node)
5475 if rev == checkout:
5474 if rev == checkout:
5476 checkout = node
5475 checkout = node
5477
5476
5478 wlock = util.nullcontextmanager()
5477 wlock = util.nullcontextmanager()
5479 if opts.get(b'update'):
5478 if opts.get(b'update'):
5480 wlock = repo.wlock()
5479 wlock = repo.wlock()
5481 with wlock:
5480 with wlock:
5482 pullopargs.update(opts.get(b'opargs', {}))
5481 pullopargs.update(opts.get(b'opargs', {}))
5483 modheads = exchange.pull(
5482 modheads = exchange.pull(
5484 repo,
5483 repo,
5485 other,
5484 other,
5486 path=path,
5485 path=path,
5487 heads=nodes,
5486 heads=nodes,
5488 force=opts.get(b'force'),
5487 force=opts.get(b'force'),
5489 bookmarks=opts.get(b'bookmark', ()),
5488 bookmarks=opts.get(b'bookmark', ()),
5490 opargs=pullopargs,
5489 opargs=pullopargs,
5491 confirm=opts.get(b'confirm'),
5490 confirm=opts.get(b'confirm'),
5492 ).cgresult
5491 ).cgresult
5493
5492
5494 # brev is a name, which might be a bookmark to be activated at
5493 # brev is a name, which might be a bookmark to be activated at
5495 # the end of the update. In other words, it is an explicit
5494 # the end of the update. In other words, it is an explicit
5496 # destination of the update
5495 # destination of the update
5497 brev = None
5496 brev = None
5498
5497
5499 if checkout:
5498 if checkout:
5500 checkout = repo.unfiltered().changelog.rev(checkout)
5499 checkout = repo.unfiltered().changelog.rev(checkout)
5501
5500
5502 # order below depends on implementation of
5501 # order below depends on implementation of
5503 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5502 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5504 # because 'checkout' is determined without it.
5503 # because 'checkout' is determined without it.
5505 if opts.get(b'rev'):
5504 if opts.get(b'rev'):
5506 brev = opts[b'rev'][0]
5505 brev = opts[b'rev'][0]
5507 elif opts.get(b'branch'):
5506 elif opts.get(b'branch'):
5508 brev = opts[b'branch'][0]
5507 brev = opts[b'branch'][0]
5509 else:
5508 else:
5510 brev = branches[0]
5509 brev = branches[0]
5511 repo._subtoppath = source
5510 repo._subtoppath = source
5512 try:
5511 try:
5513 update_conflict = postincoming(
5512 update_conflict = postincoming(
5514 ui, repo, modheads, opts.get(b'update'), checkout, brev
5513 ui, repo, modheads, opts.get(b'update'), checkout, brev
5515 )
5514 )
5516 except error.FilteredRepoLookupError as exc:
5515 except error.FilteredRepoLookupError as exc:
5517 msg = _(b'cannot update to target: %s') % exc.args[0]
5516 msg = _(b'cannot update to target: %s') % exc.args[0]
5518 exc.args = (msg,) + exc.args[1:]
5517 exc.args = (msg,) + exc.args[1:]
5519 raise
5518 raise
5520 finally:
5519 finally:
5521 del repo._subtoppath
5520 del repo._subtoppath
5522
5521
5523 finally:
5522 finally:
5524 other.close()
5523 other.close()
5525 # skip the remaining pull source if they are some conflict.
5524 # skip the remaining pull source if they are some conflict.
5526 if update_conflict:
5525 if update_conflict:
5527 break
5526 break
5528 if update_conflict:
5527 if update_conflict:
5529 return 1
5528 return 1
5530 else:
5529 else:
5531 return 0
5530 return 0
5532
5531
5533
5532
5534 @command(
5533 @command(
5535 b'purge|clean',
5534 b'purge|clean',
5536 [
5535 [
5537 (b'a', b'abort-on-err', None, _(b'abort if an error occurs')),
5536 (b'a', b'abort-on-err', None, _(b'abort if an error occurs')),
5538 (b'', b'all', None, _(b'purge ignored files too')),
5537 (b'', b'all', None, _(b'purge ignored files too')),
5539 (b'i', b'ignored', None, _(b'purge only ignored files')),
5538 (b'i', b'ignored', None, _(b'purge only ignored files')),
5540 (b'', b'dirs', None, _(b'purge empty directories')),
5539 (b'', b'dirs', None, _(b'purge empty directories')),
5541 (b'', b'files', None, _(b'purge files')),
5540 (b'', b'files', None, _(b'purge files')),
5542 (b'p', b'print', None, _(b'print filenames instead of deleting them')),
5541 (b'p', b'print', None, _(b'print filenames instead of deleting them')),
5543 (
5542 (
5544 b'0',
5543 b'0',
5545 b'print0',
5544 b'print0',
5546 None,
5545 None,
5547 _(
5546 _(
5548 b'end filenames with NUL, for use with xargs'
5547 b'end filenames with NUL, for use with xargs'
5549 b' (implies -p/--print)'
5548 b' (implies -p/--print)'
5550 ),
5549 ),
5551 ),
5550 ),
5552 (b'', b'confirm', None, _(b'ask before permanently deleting files')),
5551 (b'', b'confirm', None, _(b'ask before permanently deleting files')),
5553 ]
5552 ]
5554 + cmdutil.walkopts,
5553 + cmdutil.walkopts,
5555 _(b'hg purge [OPTION]... [DIR]...'),
5554 _(b'hg purge [OPTION]... [DIR]...'),
5556 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5555 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5557 )
5556 )
5558 def purge(ui, repo, *dirs, **opts):
5557 def purge(ui, repo, *dirs, **opts):
5559 """removes files not tracked by Mercurial
5558 """removes files not tracked by Mercurial
5560
5559
5561 Delete files not known to Mercurial. This is useful to test local
5560 Delete files not known to Mercurial. This is useful to test local
5562 and uncommitted changes in an otherwise-clean source tree.
5561 and uncommitted changes in an otherwise-clean source tree.
5563
5562
5564 This means that purge will delete the following by default:
5563 This means that purge will delete the following by default:
5565
5564
5566 - Unknown files: files marked with "?" by :hg:`status`
5565 - Unknown files: files marked with "?" by :hg:`status`
5567 - Empty directories: in fact Mercurial ignores directories unless
5566 - Empty directories: in fact Mercurial ignores directories unless
5568 they contain files under source control management
5567 they contain files under source control management
5569
5568
5570 But it will leave untouched:
5569 But it will leave untouched:
5571
5570
5572 - Modified and unmodified tracked files
5571 - Modified and unmodified tracked files
5573 - Ignored files (unless -i or --all is specified)
5572 - Ignored files (unless -i or --all is specified)
5574 - New files added to the repository (with :hg:`add`)
5573 - New files added to the repository (with :hg:`add`)
5575
5574
5576 The --files and --dirs options can be used to direct purge to delete
5575 The --files and --dirs options can be used to direct purge to delete
5577 only files, only directories, or both. If neither option is given,
5576 only files, only directories, or both. If neither option is given,
5578 both will be deleted.
5577 both will be deleted.
5579
5578
5580 If directories are given on the command line, only files in these
5579 If directories are given on the command line, only files in these
5581 directories are considered.
5580 directories are considered.
5582
5581
5583 Be careful with purge, as you could irreversibly delete some files
5582 Be careful with purge, as you could irreversibly delete some files
5584 you forgot to add to the repository. If you only want to print the
5583 you forgot to add to the repository. If you only want to print the
5585 list of files that this program would delete, use the --print
5584 list of files that this program would delete, use the --print
5586 option.
5585 option.
5587 """
5586 """
5588 opts = pycompat.byteskwargs(opts)
5587 opts = pycompat.byteskwargs(opts)
5589 cmdutil.check_at_most_one_arg(opts, b'all', b'ignored')
5588 cmdutil.check_at_most_one_arg(opts, b'all', b'ignored')
5590
5589
5591 act = not opts.get(b'print')
5590 act = not opts.get(b'print')
5592 eol = b'\n'
5591 eol = b'\n'
5593 if opts.get(b'print0'):
5592 if opts.get(b'print0'):
5594 eol = b'\0'
5593 eol = b'\0'
5595 act = False # --print0 implies --print
5594 act = False # --print0 implies --print
5596 if opts.get(b'all', False):
5595 if opts.get(b'all', False):
5597 ignored = True
5596 ignored = True
5598 unknown = True
5597 unknown = True
5599 else:
5598 else:
5600 ignored = opts.get(b'ignored', False)
5599 ignored = opts.get(b'ignored', False)
5601 unknown = not ignored
5600 unknown = not ignored
5602
5601
5603 removefiles = opts.get(b'files')
5602 removefiles = opts.get(b'files')
5604 removedirs = opts.get(b'dirs')
5603 removedirs = opts.get(b'dirs')
5605 confirm = opts.get(b'confirm')
5604 confirm = opts.get(b'confirm')
5606 if confirm is None:
5605 if confirm is None:
5607 try:
5606 try:
5608 extensions.find(b'purge')
5607 extensions.find(b'purge')
5609 confirm = False
5608 confirm = False
5610 except KeyError:
5609 except KeyError:
5611 confirm = True
5610 confirm = True
5612
5611
5613 if not removefiles and not removedirs:
5612 if not removefiles and not removedirs:
5614 removefiles = True
5613 removefiles = True
5615 removedirs = True
5614 removedirs = True
5616
5615
5617 match = scmutil.match(repo[None], dirs, opts)
5616 match = scmutil.match(repo[None], dirs, opts)
5618
5617
5619 paths = mergemod.purge(
5618 paths = mergemod.purge(
5620 repo,
5619 repo,
5621 match,
5620 match,
5622 unknown=unknown,
5621 unknown=unknown,
5623 ignored=ignored,
5622 ignored=ignored,
5624 removeemptydirs=removedirs,
5623 removeemptydirs=removedirs,
5625 removefiles=removefiles,
5624 removefiles=removefiles,
5626 abortonerror=opts.get(b'abort_on_err'),
5625 abortonerror=opts.get(b'abort_on_err'),
5627 noop=not act,
5626 noop=not act,
5628 confirm=confirm,
5627 confirm=confirm,
5629 )
5628 )
5630
5629
5631 for path in paths:
5630 for path in paths:
5632 if not act:
5631 if not act:
5633 ui.write(b'%s%s' % (path, eol))
5632 ui.write(b'%s%s' % (path, eol))
5634
5633
5635
5634
5636 @command(
5635 @command(
5637 b'push',
5636 b'push',
5638 [
5637 [
5639 (b'f', b'force', None, _(b'force push')),
5638 (b'f', b'force', None, _(b'force push')),
5640 (
5639 (
5641 b'r',
5640 b'r',
5642 b'rev',
5641 b'rev',
5643 [],
5642 [],
5644 _(b'a changeset intended to be included in the destination'),
5643 _(b'a changeset intended to be included in the destination'),
5645 _(b'REV'),
5644 _(b'REV'),
5646 ),
5645 ),
5647 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5646 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5648 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5647 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5649 (
5648 (
5650 b'b',
5649 b'b',
5651 b'branch',
5650 b'branch',
5652 [],
5651 [],
5653 _(b'a specific branch you would like to push'),
5652 _(b'a specific branch you would like to push'),
5654 _(b'BRANCH'),
5653 _(b'BRANCH'),
5655 ),
5654 ),
5656 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5655 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5657 (
5656 (
5658 b'',
5657 b'',
5659 b'pushvars',
5658 b'pushvars',
5660 [],
5659 [],
5661 _(b'variables that can be sent to server (ADVANCED)'),
5660 _(b'variables that can be sent to server (ADVANCED)'),
5662 ),
5661 ),
5663 (
5662 (
5664 b'',
5663 b'',
5665 b'publish',
5664 b'publish',
5666 False,
5665 False,
5667 _(b'push the changeset as public (EXPERIMENTAL)'),
5666 _(b'push the changeset as public (EXPERIMENTAL)'),
5668 ),
5667 ),
5669 ]
5668 ]
5670 + remoteopts,
5669 + remoteopts,
5671 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]...'),
5670 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]...'),
5672 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5671 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5673 helpbasic=True,
5672 helpbasic=True,
5674 )
5673 )
5675 def push(ui, repo, *dests, **opts):
5674 def push(ui, repo, *dests, **opts):
5676 """push changes to the specified destination
5675 """push changes to the specified destination
5677
5676
5678 Push changesets from the local repository to the specified
5677 Push changesets from the local repository to the specified
5679 destination.
5678 destination.
5680
5679
5681 This operation is symmetrical to pull: it is identical to a pull
5680 This operation is symmetrical to pull: it is identical to a pull
5682 in the destination repository from the current one.
5681 in the destination repository from the current one.
5683
5682
5684 By default, push will not allow creation of new heads at the
5683 By default, push will not allow creation of new heads at the
5685 destination, since multiple heads would make it unclear which head
5684 destination, since multiple heads would make it unclear which head
5686 to use. In this situation, it is recommended to pull and merge
5685 to use. In this situation, it is recommended to pull and merge
5687 before pushing.
5686 before pushing.
5688
5687
5689 Use --new-branch if you want to allow push to create a new named
5688 Use --new-branch if you want to allow push to create a new named
5690 branch that is not present at the destination. This allows you to
5689 branch that is not present at the destination. This allows you to
5691 only create a new branch without forcing other changes.
5690 only create a new branch without forcing other changes.
5692
5691
5693 .. note::
5692 .. note::
5694
5693
5695 Extra care should be taken with the -f/--force option,
5694 Extra care should be taken with the -f/--force option,
5696 which will push all new heads on all branches, an action which will
5695 which will push all new heads on all branches, an action which will
5697 almost always cause confusion for collaborators.
5696 almost always cause confusion for collaborators.
5698
5697
5699 If -r/--rev is used, the specified revision and all its ancestors
5698 If -r/--rev is used, the specified revision and all its ancestors
5700 will be pushed to the remote repository.
5699 will be pushed to the remote repository.
5701
5700
5702 If -B/--bookmark is used, the specified bookmarked revision, its
5701 If -B/--bookmark is used, the specified bookmarked revision, its
5703 ancestors, and the bookmark will be pushed to the remote
5702 ancestors, and the bookmark will be pushed to the remote
5704 repository. Specifying ``.`` is equivalent to specifying the active
5703 repository. Specifying ``.`` is equivalent to specifying the active
5705 bookmark's name. Use the --all-bookmarks option for pushing all
5704 bookmark's name. Use the --all-bookmarks option for pushing all
5706 current bookmarks.
5705 current bookmarks.
5707
5706
5708 Please see :hg:`help urls` for important details about ``ssh://``
5707 Please see :hg:`help urls` for important details about ``ssh://``
5709 URLs. If DESTINATION is omitted, a default path will be used.
5708 URLs. If DESTINATION is omitted, a default path will be used.
5710
5709
5711 When passed multiple destinations, push will process them one after the
5710 When passed multiple destinations, push will process them one after the
5712 other, but stop should an error occur.
5711 other, but stop should an error occur.
5713
5712
5714 .. container:: verbose
5713 .. container:: verbose
5715
5714
5716 The --pushvars option sends strings to the server that become
5715 The --pushvars option sends strings to the server that become
5717 environment variables prepended with ``HG_USERVAR_``. For example,
5716 environment variables prepended with ``HG_USERVAR_``. For example,
5718 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5717 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5719 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5718 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5720
5719
5721 pushvars can provide for user-overridable hooks as well as set debug
5720 pushvars can provide for user-overridable hooks as well as set debug
5722 levels. One example is having a hook that blocks commits containing
5721 levels. One example is having a hook that blocks commits containing
5723 conflict markers, but enables the user to override the hook if the file
5722 conflict markers, but enables the user to override the hook if the file
5724 is using conflict markers for testing purposes or the file format has
5723 is using conflict markers for testing purposes or the file format has
5725 strings that look like conflict markers.
5724 strings that look like conflict markers.
5726
5725
5727 By default, servers will ignore `--pushvars`. To enable it add the
5726 By default, servers will ignore `--pushvars`. To enable it add the
5728 following to your configuration file::
5727 following to your configuration file::
5729
5728
5730 [push]
5729 [push]
5731 pushvars.server = true
5730 pushvars.server = true
5732
5731
5733 Returns 0 if push was successful, 1 if nothing to push.
5732 Returns 0 if push was successful, 1 if nothing to push.
5734 """
5733 """
5735
5734
5736 opts = pycompat.byteskwargs(opts)
5735 opts = pycompat.byteskwargs(opts)
5737
5736
5738 if opts.get(b'all_bookmarks'):
5737 if opts.get(b'all_bookmarks'):
5739 cmdutil.check_incompatible_arguments(
5738 cmdutil.check_incompatible_arguments(
5740 opts,
5739 opts,
5741 b'all_bookmarks',
5740 b'all_bookmarks',
5742 [b'bookmark', b'rev'],
5741 [b'bookmark', b'rev'],
5743 )
5742 )
5744 opts[b'bookmark'] = list(repo._bookmarks)
5743 opts[b'bookmark'] = list(repo._bookmarks)
5745
5744
5746 if opts.get(b'bookmark'):
5745 if opts.get(b'bookmark'):
5747 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5746 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5748 for b in opts[b'bookmark']:
5747 for b in opts[b'bookmark']:
5749 # translate -B options to -r so changesets get pushed
5748 # translate -B options to -r so changesets get pushed
5750 b = repo._bookmarks.expandname(b)
5749 b = repo._bookmarks.expandname(b)
5751 if b in repo._bookmarks:
5750 if b in repo._bookmarks:
5752 opts.setdefault(b'rev', []).append(b)
5751 opts.setdefault(b'rev', []).append(b)
5753 else:
5752 else:
5754 # if we try to push a deleted bookmark, translate it to null
5753 # if we try to push a deleted bookmark, translate it to null
5755 # this lets simultaneous -r, -b options continue working
5754 # this lets simultaneous -r, -b options continue working
5756 opts.setdefault(b'rev', []).append(b"null")
5755 opts.setdefault(b'rev', []).append(b"null")
5757
5756
5758 some_pushed = False
5757 some_pushed = False
5759 result = 0
5758 result = 0
5760 for path in urlutil.get_push_paths(repo, ui, dests):
5759 for path in urlutil.get_push_paths(repo, ui, dests):
5761 dest = path.pushloc or path.loc
5760 dest = path.pushloc or path.loc
5762 branches = (path.branch, opts.get(b'branch') or [])
5761 branches = (path.branch, opts.get(b'branch') or [])
5763 ui.status(_(b'pushing to %s\n') % urlutil.hidepassword(dest))
5762 ui.status(_(b'pushing to %s\n') % urlutil.hidepassword(dest))
5764 revs, checkout = hg.addbranchrevs(
5763 revs, checkout = hg.addbranchrevs(
5765 repo, repo, branches, opts.get(b'rev')
5764 repo, repo, branches, opts.get(b'rev')
5766 )
5765 )
5767 other = hg.peer(repo, opts, dest)
5766 other = hg.peer(repo, opts, dest)
5768
5767
5769 try:
5768 try:
5770 if revs:
5769 if revs:
5771 revs = [repo[r].node() for r in logcmdutil.revrange(repo, revs)]
5770 revs = [repo[r].node() for r in logcmdutil.revrange(repo, revs)]
5772 if not revs:
5771 if not revs:
5773 raise error.InputError(
5772 raise error.InputError(
5774 _(b"specified revisions evaluate to an empty set"),
5773 _(b"specified revisions evaluate to an empty set"),
5775 hint=_(b"use different revision arguments"),
5774 hint=_(b"use different revision arguments"),
5776 )
5775 )
5777 elif path.pushrev:
5776 elif path.pushrev:
5778 # It doesn't make any sense to specify ancestor revisions. So limit
5777 # It doesn't make any sense to specify ancestor revisions. So limit
5779 # to DAG heads to make discovery simpler.
5778 # to DAG heads to make discovery simpler.
5780 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5779 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5781 revs = scmutil.revrange(repo, [expr])
5780 revs = scmutil.revrange(repo, [expr])
5782 revs = [repo[rev].node() for rev in revs]
5781 revs = [repo[rev].node() for rev in revs]
5783 if not revs:
5782 if not revs:
5784 raise error.InputError(
5783 raise error.InputError(
5785 _(
5784 _(
5786 b'default push revset for path evaluates to an empty set'
5785 b'default push revset for path evaluates to an empty set'
5787 )
5786 )
5788 )
5787 )
5789 elif ui.configbool(b'commands', b'push.require-revs'):
5788 elif ui.configbool(b'commands', b'push.require-revs'):
5790 raise error.InputError(
5789 raise error.InputError(
5791 _(b'no revisions specified to push'),
5790 _(b'no revisions specified to push'),
5792 hint=_(b'did you mean "hg push -r ."?'),
5791 hint=_(b'did you mean "hg push -r ."?'),
5793 )
5792 )
5794
5793
5795 repo._subtoppath = dest
5794 repo._subtoppath = dest
5796 try:
5795 try:
5797 # push subrepos depth-first for coherent ordering
5796 # push subrepos depth-first for coherent ordering
5798 c = repo[b'.']
5797 c = repo[b'.']
5799 subs = c.substate # only repos that are committed
5798 subs = c.substate # only repos that are committed
5800 for s in sorted(subs):
5799 for s in sorted(subs):
5801 sub_result = c.sub(s).push(opts)
5800 sub_result = c.sub(s).push(opts)
5802 if sub_result == 0:
5801 if sub_result == 0:
5803 return 1
5802 return 1
5804 finally:
5803 finally:
5805 del repo._subtoppath
5804 del repo._subtoppath
5806
5805
5807 opargs = dict(
5806 opargs = dict(
5808 opts.get(b'opargs', {})
5807 opts.get(b'opargs', {})
5809 ) # copy opargs since we may mutate it
5808 ) # copy opargs since we may mutate it
5810 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5809 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5811
5810
5812 pushop = exchange.push(
5811 pushop = exchange.push(
5813 repo,
5812 repo,
5814 other,
5813 other,
5815 opts.get(b'force'),
5814 opts.get(b'force'),
5816 revs=revs,
5815 revs=revs,
5817 newbranch=opts.get(b'new_branch'),
5816 newbranch=opts.get(b'new_branch'),
5818 bookmarks=opts.get(b'bookmark', ()),
5817 bookmarks=opts.get(b'bookmark', ()),
5819 publish=opts.get(b'publish'),
5818 publish=opts.get(b'publish'),
5820 opargs=opargs,
5819 opargs=opargs,
5821 )
5820 )
5822
5821
5823 if pushop.cgresult == 0:
5822 if pushop.cgresult == 0:
5824 result = 1
5823 result = 1
5825 elif pushop.cgresult is not None:
5824 elif pushop.cgresult is not None:
5826 some_pushed = True
5825 some_pushed = True
5827
5826
5828 if pushop.bkresult is not None:
5827 if pushop.bkresult is not None:
5829 if pushop.bkresult == 2:
5828 if pushop.bkresult == 2:
5830 result = 2
5829 result = 2
5831 elif not result and pushop.bkresult:
5830 elif not result and pushop.bkresult:
5832 result = 2
5831 result = 2
5833
5832
5834 if result:
5833 if result:
5835 break
5834 break
5836
5835
5837 finally:
5836 finally:
5838 other.close()
5837 other.close()
5839 if result == 0 and not some_pushed:
5838 if result == 0 and not some_pushed:
5840 result = 1
5839 result = 1
5841 return result
5840 return result
5842
5841
5843
5842
5844 @command(
5843 @command(
5845 b'recover',
5844 b'recover',
5846 [
5845 [
5847 (b'', b'verify', False, b"run `hg verify` after successful recover"),
5846 (b'', b'verify', False, b"run `hg verify` after successful recover"),
5848 ],
5847 ],
5849 helpcategory=command.CATEGORY_MAINTENANCE,
5848 helpcategory=command.CATEGORY_MAINTENANCE,
5850 )
5849 )
5851 def recover(ui, repo, **opts):
5850 def recover(ui, repo, **opts):
5852 """roll back an interrupted transaction
5851 """roll back an interrupted transaction
5853
5852
5854 Recover from an interrupted commit or pull.
5853 Recover from an interrupted commit or pull.
5855
5854
5856 This command tries to fix the repository status after an
5855 This command tries to fix the repository status after an
5857 interrupted operation. It should only be necessary when Mercurial
5856 interrupted operation. It should only be necessary when Mercurial
5858 suggests it.
5857 suggests it.
5859
5858
5860 Returns 0 if successful, 1 if nothing to recover or verify fails.
5859 Returns 0 if successful, 1 if nothing to recover or verify fails.
5861 """
5860 """
5862 ret = repo.recover()
5861 ret = repo.recover()
5863 if ret:
5862 if ret:
5864 if opts['verify']:
5863 if opts['verify']:
5865 return hg.verify(repo)
5864 return hg.verify(repo)
5866 else:
5865 else:
5867 msg = _(
5866 msg = _(
5868 b"(verify step skipped, run `hg verify` to check your "
5867 b"(verify step skipped, run `hg verify` to check your "
5869 b"repository content)\n"
5868 b"repository content)\n"
5870 )
5869 )
5871 ui.warn(msg)
5870 ui.warn(msg)
5872 return 0
5871 return 0
5873 return 1
5872 return 1
5874
5873
5875
5874
5876 @command(
5875 @command(
5877 b'remove|rm',
5876 b'remove|rm',
5878 [
5877 [
5879 (b'A', b'after', None, _(b'record delete for missing files')),
5878 (b'A', b'after', None, _(b'record delete for missing files')),
5880 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5879 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5881 ]
5880 ]
5882 + subrepoopts
5881 + subrepoopts
5883 + walkopts
5882 + walkopts
5884 + dryrunopts,
5883 + dryrunopts,
5885 _(b'[OPTION]... FILE...'),
5884 _(b'[OPTION]... FILE...'),
5886 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5885 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5887 helpbasic=True,
5886 helpbasic=True,
5888 inferrepo=True,
5887 inferrepo=True,
5889 )
5888 )
5890 def remove(ui, repo, *pats, **opts):
5889 def remove(ui, repo, *pats, **opts):
5891 """remove the specified files on the next commit
5890 """remove the specified files on the next commit
5892
5891
5893 Schedule the indicated files for removal from the current branch.
5892 Schedule the indicated files for removal from the current branch.
5894
5893
5895 This command schedules the files to be removed at the next commit.
5894 This command schedules the files to be removed at the next commit.
5896 To undo a remove before that, see :hg:`revert`. To undo added
5895 To undo a remove before that, see :hg:`revert`. To undo added
5897 files, see :hg:`forget`.
5896 files, see :hg:`forget`.
5898
5897
5899 .. container:: verbose
5898 .. container:: verbose
5900
5899
5901 -A/--after can be used to remove only files that have already
5900 -A/--after can be used to remove only files that have already
5902 been deleted, -f/--force can be used to force deletion, and -Af
5901 been deleted, -f/--force can be used to force deletion, and -Af
5903 can be used to remove files from the next revision without
5902 can be used to remove files from the next revision without
5904 deleting them from the working directory.
5903 deleting them from the working directory.
5905
5904
5906 The following table details the behavior of remove for different
5905 The following table details the behavior of remove for different
5907 file states (columns) and option combinations (rows). The file
5906 file states (columns) and option combinations (rows). The file
5908 states are Added [A], Clean [C], Modified [M] and Missing [!]
5907 states are Added [A], Clean [C], Modified [M] and Missing [!]
5909 (as reported by :hg:`status`). The actions are Warn, Remove
5908 (as reported by :hg:`status`). The actions are Warn, Remove
5910 (from branch) and Delete (from disk):
5909 (from branch) and Delete (from disk):
5911
5910
5912 ========= == == == ==
5911 ========= == == == ==
5913 opt/state A C M !
5912 opt/state A C M !
5914 ========= == == == ==
5913 ========= == == == ==
5915 none W RD W R
5914 none W RD W R
5916 -f R RD RD R
5915 -f R RD RD R
5917 -A W W W R
5916 -A W W W R
5918 -Af R R R R
5917 -Af R R R R
5919 ========= == == == ==
5918 ========= == == == ==
5920
5919
5921 .. note::
5920 .. note::
5922
5921
5923 :hg:`remove` never deletes files in Added [A] state from the
5922 :hg:`remove` never deletes files in Added [A] state from the
5924 working directory, not even if ``--force`` is specified.
5923 working directory, not even if ``--force`` is specified.
5925
5924
5926 Returns 0 on success, 1 if any warnings encountered.
5925 Returns 0 on success, 1 if any warnings encountered.
5927 """
5926 """
5928
5927
5929 opts = pycompat.byteskwargs(opts)
5928 opts = pycompat.byteskwargs(opts)
5930 after, force = opts.get(b'after'), opts.get(b'force')
5929 after, force = opts.get(b'after'), opts.get(b'force')
5931 dryrun = opts.get(b'dry_run')
5930 dryrun = opts.get(b'dry_run')
5932 if not pats and not after:
5931 if not pats and not after:
5933 raise error.InputError(_(b'no files specified'))
5932 raise error.InputError(_(b'no files specified'))
5934
5933
5935 m = scmutil.match(repo[None], pats, opts)
5934 m = scmutil.match(repo[None], pats, opts)
5936 subrepos = opts.get(b'subrepos')
5935 subrepos = opts.get(b'subrepos')
5937 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5936 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5938 return cmdutil.remove(
5937 return cmdutil.remove(
5939 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5938 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5940 )
5939 )
5941
5940
5942
5941
5943 @command(
5942 @command(
5944 b'rename|move|mv',
5943 b'rename|move|mv',
5945 [
5944 [
5946 (b'', b'forget', None, _(b'unmark a destination file as renamed')),
5945 (b'', b'forget', None, _(b'unmark a destination file as renamed')),
5947 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5946 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5948 (
5947 (
5949 b'',
5948 b'',
5950 b'at-rev',
5949 b'at-rev',
5951 b'',
5950 b'',
5952 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5951 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5953 _(b'REV'),
5952 _(b'REV'),
5954 ),
5953 ),
5955 (
5954 (
5956 b'f',
5955 b'f',
5957 b'force',
5956 b'force',
5958 None,
5957 None,
5959 _(b'forcibly move over an existing managed file'),
5958 _(b'forcibly move over an existing managed file'),
5960 ),
5959 ),
5961 ]
5960 ]
5962 + walkopts
5961 + walkopts
5963 + dryrunopts,
5962 + dryrunopts,
5964 _(b'[OPTION]... SOURCE... DEST'),
5963 _(b'[OPTION]... SOURCE... DEST'),
5965 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5964 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5966 )
5965 )
5967 def rename(ui, repo, *pats, **opts):
5966 def rename(ui, repo, *pats, **opts):
5968 """rename files; equivalent of copy + remove
5967 """rename files; equivalent of copy + remove
5969
5968
5970 Mark dest as copies of sources; mark sources for deletion. If dest
5969 Mark dest as copies of sources; mark sources for deletion. If dest
5971 is a directory, copies are put in that directory. If dest is a
5970 is a directory, copies are put in that directory. If dest is a
5972 file, there can only be one source.
5971 file, there can only be one source.
5973
5972
5974 By default, this command copies the contents of files as they
5973 By default, this command copies the contents of files as they
5975 exist in the working directory. If invoked with -A/--after, the
5974 exist in the working directory. If invoked with -A/--after, the
5976 operation is recorded, but no copying is performed.
5975 operation is recorded, but no copying is performed.
5977
5976
5978 To undo marking a destination file as renamed, use --forget. With that
5977 To undo marking a destination file as renamed, use --forget. With that
5979 option, all given (positional) arguments are unmarked as renames. The
5978 option, all given (positional) arguments are unmarked as renames. The
5980 destination file(s) will be left in place (still tracked). The source
5979 destination file(s) will be left in place (still tracked). The source
5981 file(s) will not be restored. Note that :hg:`rename --forget` behaves
5980 file(s) will not be restored. Note that :hg:`rename --forget` behaves
5982 the same way as :hg:`copy --forget`.
5981 the same way as :hg:`copy --forget`.
5983
5982
5984 This command takes effect with the next commit by default.
5983 This command takes effect with the next commit by default.
5985
5984
5986 Returns 0 on success, 1 if errors are encountered.
5985 Returns 0 on success, 1 if errors are encountered.
5987 """
5986 """
5988 opts = pycompat.byteskwargs(opts)
5987 opts = pycompat.byteskwargs(opts)
5989 with repo.wlock():
5988 with repo.wlock():
5990 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5989 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5991
5990
5992
5991
5993 @command(
5992 @command(
5994 b'resolve',
5993 b'resolve',
5995 [
5994 [
5996 (b'a', b'all', None, _(b'select all unresolved files')),
5995 (b'a', b'all', None, _(b'select all unresolved files')),
5997 (b'l', b'list', None, _(b'list state of files needing merge')),
5996 (b'l', b'list', None, _(b'list state of files needing merge')),
5998 (b'm', b'mark', None, _(b'mark files as resolved')),
5997 (b'm', b'mark', None, _(b'mark files as resolved')),
5999 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5998 (b'u', b'unmark', None, _(b'mark files as unresolved')),
6000 (b'n', b'no-status', None, _(b'hide status prefix')),
5999 (b'n', b'no-status', None, _(b'hide status prefix')),
6001 (b'', b're-merge', None, _(b're-merge files')),
6000 (b'', b're-merge', None, _(b're-merge files')),
6002 ]
6001 ]
6003 + mergetoolopts
6002 + mergetoolopts
6004 + walkopts
6003 + walkopts
6005 + formatteropts,
6004 + formatteropts,
6006 _(b'[OPTION]... [FILE]...'),
6005 _(b'[OPTION]... [FILE]...'),
6007 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6006 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6008 inferrepo=True,
6007 inferrepo=True,
6009 )
6008 )
6010 def resolve(ui, repo, *pats, **opts):
6009 def resolve(ui, repo, *pats, **opts):
6011 """redo merges or set/view the merge status of files
6010 """redo merges or set/view the merge status of files
6012
6011
6013 Merges with unresolved conflicts are often the result of
6012 Merges with unresolved conflicts are often the result of
6014 non-interactive merging using the ``internal:merge`` configuration
6013 non-interactive merging using the ``internal:merge`` configuration
6015 setting, or a command-line merge tool like ``diff3``. The resolve
6014 setting, or a command-line merge tool like ``diff3``. The resolve
6016 command is used to manage the files involved in a merge, after
6015 command is used to manage the files involved in a merge, after
6017 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
6016 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
6018 working directory must have two parents). See :hg:`help
6017 working directory must have two parents). See :hg:`help
6019 merge-tools` for information on configuring merge tools.
6018 merge-tools` for information on configuring merge tools.
6020
6019
6021 The resolve command can be used in the following ways:
6020 The resolve command can be used in the following ways:
6022
6021
6023 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
6022 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
6024 the specified files, discarding any previous merge attempts. Re-merging
6023 the specified files, discarding any previous merge attempts. Re-merging
6025 is not performed for files already marked as resolved. Use ``--all/-a``
6024 is not performed for files already marked as resolved. Use ``--all/-a``
6026 to select all unresolved files. ``--tool`` can be used to specify
6025 to select all unresolved files. ``--tool`` can be used to specify
6027 the merge tool used for the given files. It overrides the HGMERGE
6026 the merge tool used for the given files. It overrides the HGMERGE
6028 environment variable and your configuration files. Previous file
6027 environment variable and your configuration files. Previous file
6029 contents are saved with a ``.orig`` suffix.
6028 contents are saved with a ``.orig`` suffix.
6030
6029
6031 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
6030 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
6032 (e.g. after having manually fixed-up the files). The default is
6031 (e.g. after having manually fixed-up the files). The default is
6033 to mark all unresolved files.
6032 to mark all unresolved files.
6034
6033
6035 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
6034 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
6036 default is to mark all resolved files.
6035 default is to mark all resolved files.
6037
6036
6038 - :hg:`resolve -l`: list files which had or still have conflicts.
6037 - :hg:`resolve -l`: list files which had or still have conflicts.
6039 In the printed list, ``U`` = unresolved and ``R`` = resolved.
6038 In the printed list, ``U`` = unresolved and ``R`` = resolved.
6040 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
6039 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
6041 the list. See :hg:`help filesets` for details.
6040 the list. See :hg:`help filesets` for details.
6042
6041
6043 .. note::
6042 .. note::
6044
6043
6045 Mercurial will not let you commit files with unresolved merge
6044 Mercurial will not let you commit files with unresolved merge
6046 conflicts. You must use :hg:`resolve -m ...` before you can
6045 conflicts. You must use :hg:`resolve -m ...` before you can
6047 commit after a conflicting merge.
6046 commit after a conflicting merge.
6048
6047
6049 .. container:: verbose
6048 .. container:: verbose
6050
6049
6051 Template:
6050 Template:
6052
6051
6053 The following keywords are supported in addition to the common template
6052 The following keywords are supported in addition to the common template
6054 keywords and functions. See also :hg:`help templates`.
6053 keywords and functions. See also :hg:`help templates`.
6055
6054
6056 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
6055 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
6057 :path: String. Repository-absolute path of the file.
6056 :path: String. Repository-absolute path of the file.
6058
6057
6059 Returns 0 on success, 1 if any files fail a resolve attempt.
6058 Returns 0 on success, 1 if any files fail a resolve attempt.
6060 """
6059 """
6061
6060
6062 opts = pycompat.byteskwargs(opts)
6061 opts = pycompat.byteskwargs(opts)
6063 confirm = ui.configbool(b'commands', b'resolve.confirm')
6062 confirm = ui.configbool(b'commands', b'resolve.confirm')
6064 flaglist = b'all mark unmark list no_status re_merge'.split()
6063 flaglist = b'all mark unmark list no_status re_merge'.split()
6065 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
6064 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
6066
6065
6067 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
6066 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
6068 if actioncount > 1:
6067 if actioncount > 1:
6069 raise error.InputError(_(b"too many actions specified"))
6068 raise error.InputError(_(b"too many actions specified"))
6070 elif actioncount == 0 and ui.configbool(
6069 elif actioncount == 0 and ui.configbool(
6071 b'commands', b'resolve.explicit-re-merge'
6070 b'commands', b'resolve.explicit-re-merge'
6072 ):
6071 ):
6073 hint = _(b'use --mark, --unmark, --list or --re-merge')
6072 hint = _(b'use --mark, --unmark, --list or --re-merge')
6074 raise error.InputError(_(b'no action specified'), hint=hint)
6073 raise error.InputError(_(b'no action specified'), hint=hint)
6075 if pats and all:
6074 if pats and all:
6076 raise error.InputError(_(b"can't specify --all and patterns"))
6075 raise error.InputError(_(b"can't specify --all and patterns"))
6077 if not (all or pats or show or mark or unmark):
6076 if not (all or pats or show or mark or unmark):
6078 raise error.InputError(
6077 raise error.InputError(
6079 _(b'no files or directories specified'),
6078 _(b'no files or directories specified'),
6080 hint=b'use --all to re-merge all unresolved files',
6079 hint=b'use --all to re-merge all unresolved files',
6081 )
6080 )
6082
6081
6083 if confirm:
6082 if confirm:
6084 if all:
6083 if all:
6085 if ui.promptchoice(
6084 if ui.promptchoice(
6086 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
6085 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
6087 ):
6086 ):
6088 raise error.CanceledError(_(b'user quit'))
6087 raise error.CanceledError(_(b'user quit'))
6089 if mark and not pats:
6088 if mark and not pats:
6090 if ui.promptchoice(
6089 if ui.promptchoice(
6091 _(
6090 _(
6092 b'mark all unresolved files as resolved (yn)?'
6091 b'mark all unresolved files as resolved (yn)?'
6093 b'$$ &Yes $$ &No'
6092 b'$$ &Yes $$ &No'
6094 )
6093 )
6095 ):
6094 ):
6096 raise error.CanceledError(_(b'user quit'))
6095 raise error.CanceledError(_(b'user quit'))
6097 if unmark and not pats:
6096 if unmark and not pats:
6098 if ui.promptchoice(
6097 if ui.promptchoice(
6099 _(
6098 _(
6100 b'mark all resolved files as unresolved (yn)?'
6099 b'mark all resolved files as unresolved (yn)?'
6101 b'$$ &Yes $$ &No'
6100 b'$$ &Yes $$ &No'
6102 )
6101 )
6103 ):
6102 ):
6104 raise error.CanceledError(_(b'user quit'))
6103 raise error.CanceledError(_(b'user quit'))
6105
6104
6106 uipathfn = scmutil.getuipathfn(repo)
6105 uipathfn = scmutil.getuipathfn(repo)
6107
6106
6108 if show:
6107 if show:
6109 ui.pager(b'resolve')
6108 ui.pager(b'resolve')
6110 fm = ui.formatter(b'resolve', opts)
6109 fm = ui.formatter(b'resolve', opts)
6111 ms = mergestatemod.mergestate.read(repo)
6110 ms = mergestatemod.mergestate.read(repo)
6112 wctx = repo[None]
6111 wctx = repo[None]
6113 m = scmutil.match(wctx, pats, opts)
6112 m = scmutil.match(wctx, pats, opts)
6114
6113
6115 # Labels and keys based on merge state. Unresolved path conflicts show
6114 # Labels and keys based on merge state. Unresolved path conflicts show
6116 # as 'P'. Resolved path conflicts show as 'R', the same as normal
6115 # as 'P'. Resolved path conflicts show as 'R', the same as normal
6117 # resolved conflicts.
6116 # resolved conflicts.
6118 mergestateinfo = {
6117 mergestateinfo = {
6119 mergestatemod.MERGE_RECORD_UNRESOLVED: (
6118 mergestatemod.MERGE_RECORD_UNRESOLVED: (
6120 b'resolve.unresolved',
6119 b'resolve.unresolved',
6121 b'U',
6120 b'U',
6122 ),
6121 ),
6123 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
6122 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
6124 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
6123 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
6125 b'resolve.unresolved',
6124 b'resolve.unresolved',
6126 b'P',
6125 b'P',
6127 ),
6126 ),
6128 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
6127 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
6129 b'resolve.resolved',
6128 b'resolve.resolved',
6130 b'R',
6129 b'R',
6131 ),
6130 ),
6132 }
6131 }
6133
6132
6134 for f in ms:
6133 for f in ms:
6135 if not m(f):
6134 if not m(f):
6136 continue
6135 continue
6137
6136
6138 label, key = mergestateinfo[ms[f]]
6137 label, key = mergestateinfo[ms[f]]
6139 fm.startitem()
6138 fm.startitem()
6140 fm.context(ctx=wctx)
6139 fm.context(ctx=wctx)
6141 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
6140 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
6142 fm.data(path=f)
6141 fm.data(path=f)
6143 fm.plain(b'%s\n' % uipathfn(f), label=label)
6142 fm.plain(b'%s\n' % uipathfn(f), label=label)
6144 fm.end()
6143 fm.end()
6145 return 0
6144 return 0
6146
6145
6147 with repo.wlock():
6146 with repo.wlock():
6148 ms = mergestatemod.mergestate.read(repo)
6147 ms = mergestatemod.mergestate.read(repo)
6149
6148
6150 if not (ms.active() or repo.dirstate.p2() != repo.nullid):
6149 if not (ms.active() or repo.dirstate.p2() != repo.nullid):
6151 raise error.StateError(
6150 raise error.StateError(
6152 _(b'resolve command not applicable when not merging')
6151 _(b'resolve command not applicable when not merging')
6153 )
6152 )
6154
6153
6155 wctx = repo[None]
6154 wctx = repo[None]
6156 m = scmutil.match(wctx, pats, opts)
6155 m = scmutil.match(wctx, pats, opts)
6157 ret = 0
6156 ret = 0
6158 didwork = False
6157 didwork = False
6159
6158
6160 hasconflictmarkers = []
6159 hasconflictmarkers = []
6161 if mark:
6160 if mark:
6162 markcheck = ui.config(b'commands', b'resolve.mark-check')
6161 markcheck = ui.config(b'commands', b'resolve.mark-check')
6163 if markcheck not in [b'warn', b'abort']:
6162 if markcheck not in [b'warn', b'abort']:
6164 # Treat all invalid / unrecognized values as 'none'.
6163 # Treat all invalid / unrecognized values as 'none'.
6165 markcheck = False
6164 markcheck = False
6166 for f in ms:
6165 for f in ms:
6167 if not m(f):
6166 if not m(f):
6168 continue
6167 continue
6169
6168
6170 didwork = True
6169 didwork = True
6171
6170
6172 # path conflicts must be resolved manually
6171 # path conflicts must be resolved manually
6173 if ms[f] in (
6172 if ms[f] in (
6174 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
6173 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
6175 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
6174 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
6176 ):
6175 ):
6177 if mark:
6176 if mark:
6178 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
6177 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
6179 elif unmark:
6178 elif unmark:
6180 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
6179 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
6181 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
6180 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
6182 ui.warn(
6181 ui.warn(
6183 _(b'%s: path conflict must be resolved manually\n')
6182 _(b'%s: path conflict must be resolved manually\n')
6184 % uipathfn(f)
6183 % uipathfn(f)
6185 )
6184 )
6186 continue
6185 continue
6187
6186
6188 if mark:
6187 if mark:
6189 if markcheck:
6188 if markcheck:
6190 fdata = repo.wvfs.tryread(f)
6189 fdata = repo.wvfs.tryread(f)
6191 if (
6190 if (
6192 filemerge.hasconflictmarkers(fdata)
6191 filemerge.hasconflictmarkers(fdata)
6193 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6192 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6194 ):
6193 ):
6195 hasconflictmarkers.append(f)
6194 hasconflictmarkers.append(f)
6196 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6195 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6197 elif unmark:
6196 elif unmark:
6198 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6197 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6199 else:
6198 else:
6200 # backup pre-resolve (merge uses .orig for its own purposes)
6199 # backup pre-resolve (merge uses .orig for its own purposes)
6201 a = repo.wjoin(f)
6200 a = repo.wjoin(f)
6202 try:
6201 try:
6203 util.copyfile(a, a + b".resolve")
6202 util.copyfile(a, a + b".resolve")
6204 except FileNotFoundError:
6203 except FileNotFoundError:
6205 pass
6204 pass
6206
6205
6207 try:
6206 try:
6208 # preresolve file
6207 # preresolve file
6209 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6208 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6210 with ui.configoverride(overrides, b'resolve'):
6209 with ui.configoverride(overrides, b'resolve'):
6211 r = ms.resolve(f, wctx)
6210 r = ms.resolve(f, wctx)
6212 if r:
6211 if r:
6213 ret = 1
6212 ret = 1
6214 finally:
6213 finally:
6215 ms.commit()
6214 ms.commit()
6216
6215
6217 # replace filemerge's .orig file with our resolve file
6216 # replace filemerge's .orig file with our resolve file
6218 try:
6217 try:
6219 util.rename(
6218 util.rename(
6220 a + b".resolve", scmutil.backuppath(ui, repo, f)
6219 a + b".resolve", scmutil.backuppath(ui, repo, f)
6221 )
6220 )
6222 except FileNotFoundError:
6221 except FileNotFoundError:
6223 pass
6222 pass
6224
6223
6225 if hasconflictmarkers:
6224 if hasconflictmarkers:
6226 ui.warn(
6225 ui.warn(
6227 _(
6226 _(
6228 b'warning: the following files still have conflict '
6227 b'warning: the following files still have conflict '
6229 b'markers:\n'
6228 b'markers:\n'
6230 )
6229 )
6231 + b''.join(
6230 + b''.join(
6232 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6231 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6233 )
6232 )
6234 )
6233 )
6235 if markcheck == b'abort' and not all and not pats:
6234 if markcheck == b'abort' and not all and not pats:
6236 raise error.StateError(
6235 raise error.StateError(
6237 _(b'conflict markers detected'),
6236 _(b'conflict markers detected'),
6238 hint=_(b'use --all to mark anyway'),
6237 hint=_(b'use --all to mark anyway'),
6239 )
6238 )
6240
6239
6241 ms.commit()
6240 ms.commit()
6242 branchmerge = repo.dirstate.p2() != repo.nullid
6241 branchmerge = repo.dirstate.p2() != repo.nullid
6243 # resolve is not doing a parent change here, however, `record updates`
6242 # resolve is not doing a parent change here, however, `record updates`
6244 # will call some dirstate API that at intended for parent changes call.
6243 # will call some dirstate API that at intended for parent changes call.
6245 # Ideally we would not need this and could implement a lighter version
6244 # Ideally we would not need this and could implement a lighter version
6246 # of the recordupdateslogic that will not have to deal with the part
6245 # of the recordupdateslogic that will not have to deal with the part
6247 # related to parent changes. However this would requires that:
6246 # related to parent changes. However this would requires that:
6248 # - we are sure we passed around enough information at update/merge
6247 # - we are sure we passed around enough information at update/merge
6249 # time to no longer needs it at `hg resolve time`
6248 # time to no longer needs it at `hg resolve time`
6250 # - we are sure we store that information well enough to be able to reuse it
6249 # - we are sure we store that information well enough to be able to reuse it
6251 # - we are the necessary logic to reuse it right.
6250 # - we are the necessary logic to reuse it right.
6252 #
6251 #
6253 # All this should eventually happens, but in the mean time, we use this
6252 # All this should eventually happens, but in the mean time, we use this
6254 # context manager slightly out of the context it should be.
6253 # context manager slightly out of the context it should be.
6255 with repo.dirstate.parentchange():
6254 with repo.dirstate.parentchange():
6256 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6255 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6257
6256
6258 if not didwork and pats:
6257 if not didwork and pats:
6259 hint = None
6258 hint = None
6260 if not any([p for p in pats if p.find(b':') >= 0]):
6259 if not any([p for p in pats if p.find(b':') >= 0]):
6261 pats = [b'path:%s' % p for p in pats]
6260 pats = [b'path:%s' % p for p in pats]
6262 m = scmutil.match(wctx, pats, opts)
6261 m = scmutil.match(wctx, pats, opts)
6263 for f in ms:
6262 for f in ms:
6264 if not m(f):
6263 if not m(f):
6265 continue
6264 continue
6266
6265
6267 def flag(o):
6266 def flag(o):
6268 if o == b're_merge':
6267 if o == b're_merge':
6269 return b'--re-merge '
6268 return b'--re-merge '
6270 return b'-%s ' % o[0:1]
6269 return b'-%s ' % o[0:1]
6271
6270
6272 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6271 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6273 hint = _(b"(try: hg resolve %s%s)\n") % (
6272 hint = _(b"(try: hg resolve %s%s)\n") % (
6274 flags,
6273 flags,
6275 b' '.join(pats),
6274 b' '.join(pats),
6276 )
6275 )
6277 break
6276 break
6278 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6277 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6279 if hint:
6278 if hint:
6280 ui.warn(hint)
6279 ui.warn(hint)
6281
6280
6282 unresolvedf = ms.unresolvedcount()
6281 unresolvedf = ms.unresolvedcount()
6283 if not unresolvedf:
6282 if not unresolvedf:
6284 ui.status(_(b'(no more unresolved files)\n'))
6283 ui.status(_(b'(no more unresolved files)\n'))
6285 cmdutil.checkafterresolved(repo)
6284 cmdutil.checkafterresolved(repo)
6286
6285
6287 return ret
6286 return ret
6288
6287
6289
6288
6290 @command(
6289 @command(
6291 b'revert',
6290 b'revert',
6292 [
6291 [
6293 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6292 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6294 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6293 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6295 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6294 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6296 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6295 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6297 (b'i', b'interactive', None, _(b'interactively select the changes')),
6296 (b'i', b'interactive', None, _(b'interactively select the changes')),
6298 ]
6297 ]
6299 + walkopts
6298 + walkopts
6300 + dryrunopts,
6299 + dryrunopts,
6301 _(b'[OPTION]... [-r REV] [NAME]...'),
6300 _(b'[OPTION]... [-r REV] [NAME]...'),
6302 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6301 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6303 )
6302 )
6304 def revert(ui, repo, *pats, **opts):
6303 def revert(ui, repo, *pats, **opts):
6305 """restore files to their checkout state
6304 """restore files to their checkout state
6306
6305
6307 .. note::
6306 .. note::
6308
6307
6309 To check out earlier revisions, you should use :hg:`update REV`.
6308 To check out earlier revisions, you should use :hg:`update REV`.
6310 To cancel an uncommitted merge (and lose your changes),
6309 To cancel an uncommitted merge (and lose your changes),
6311 use :hg:`merge --abort`.
6310 use :hg:`merge --abort`.
6312
6311
6313 With no revision specified, revert the specified files or directories
6312 With no revision specified, revert the specified files or directories
6314 to the contents they had in the parent of the working directory.
6313 to the contents they had in the parent of the working directory.
6315 This restores the contents of files to an unmodified
6314 This restores the contents of files to an unmodified
6316 state and unschedules adds, removes, copies, and renames. If the
6315 state and unschedules adds, removes, copies, and renames. If the
6317 working directory has two parents, you must explicitly specify a
6316 working directory has two parents, you must explicitly specify a
6318 revision.
6317 revision.
6319
6318
6320 Using the -r/--rev or -d/--date options, revert the given files or
6319 Using the -r/--rev or -d/--date options, revert the given files or
6321 directories to their states as of a specific revision. Because
6320 directories to their states as of a specific revision. Because
6322 revert does not change the working directory parents, this will
6321 revert does not change the working directory parents, this will
6323 cause these files to appear modified. This can be helpful to "back
6322 cause these files to appear modified. This can be helpful to "back
6324 out" some or all of an earlier change. See :hg:`backout` for a
6323 out" some or all of an earlier change. See :hg:`backout` for a
6325 related method.
6324 related method.
6326
6325
6327 Modified files are saved with a .orig suffix before reverting.
6326 Modified files are saved with a .orig suffix before reverting.
6328 To disable these backups, use --no-backup. It is possible to store
6327 To disable these backups, use --no-backup. It is possible to store
6329 the backup files in a custom directory relative to the root of the
6328 the backup files in a custom directory relative to the root of the
6330 repository by setting the ``ui.origbackuppath`` configuration
6329 repository by setting the ``ui.origbackuppath`` configuration
6331 option.
6330 option.
6332
6331
6333 See :hg:`help dates` for a list of formats valid for -d/--date.
6332 See :hg:`help dates` for a list of formats valid for -d/--date.
6334
6333
6335 See :hg:`help backout` for a way to reverse the effect of an
6334 See :hg:`help backout` for a way to reverse the effect of an
6336 earlier changeset.
6335 earlier changeset.
6337
6336
6338 Returns 0 on success.
6337 Returns 0 on success.
6339 """
6338 """
6340
6339
6341 opts = pycompat.byteskwargs(opts)
6340 opts = pycompat.byteskwargs(opts)
6342 if opts.get(b"date"):
6341 if opts.get(b"date"):
6343 cmdutil.check_incompatible_arguments(opts, b'date', [b'rev'])
6342 cmdutil.check_incompatible_arguments(opts, b'date', [b'rev'])
6344 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6343 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6345
6344
6346 parent, p2 = repo.dirstate.parents()
6345 parent, p2 = repo.dirstate.parents()
6347 if not opts.get(b'rev') and p2 != repo.nullid:
6346 if not opts.get(b'rev') and p2 != repo.nullid:
6348 # revert after merge is a trap for new users (issue2915)
6347 # revert after merge is a trap for new users (issue2915)
6349 raise error.InputError(
6348 raise error.InputError(
6350 _(b'uncommitted merge with no revision specified'),
6349 _(b'uncommitted merge with no revision specified'),
6351 hint=_(b"use 'hg update' or see 'hg help revert'"),
6350 hint=_(b"use 'hg update' or see 'hg help revert'"),
6352 )
6351 )
6353
6352
6354 rev = opts.get(b'rev')
6353 rev = opts.get(b'rev')
6355 if rev:
6354 if rev:
6356 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6355 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6357 ctx = logcmdutil.revsingle(repo, rev)
6356 ctx = logcmdutil.revsingle(repo, rev)
6358
6357
6359 if not (
6358 if not (
6360 pats
6359 pats
6361 or opts.get(b'include')
6360 or opts.get(b'include')
6362 or opts.get(b'exclude')
6361 or opts.get(b'exclude')
6363 or opts.get(b'all')
6362 or opts.get(b'all')
6364 or opts.get(b'interactive')
6363 or opts.get(b'interactive')
6365 ):
6364 ):
6366 msg = _(b"no files or directories specified")
6365 msg = _(b"no files or directories specified")
6367 if p2 != repo.nullid:
6366 if p2 != repo.nullid:
6368 hint = _(
6367 hint = _(
6369 b"uncommitted merge, use --all to discard all changes,"
6368 b"uncommitted merge, use --all to discard all changes,"
6370 b" or 'hg update -C .' to abort the merge"
6369 b" or 'hg update -C .' to abort the merge"
6371 )
6370 )
6372 raise error.InputError(msg, hint=hint)
6371 raise error.InputError(msg, hint=hint)
6373 dirty = any(repo.status())
6372 dirty = any(repo.status())
6374 node = ctx.node()
6373 node = ctx.node()
6375 if node != parent:
6374 if node != parent:
6376 if dirty:
6375 if dirty:
6377 hint = (
6376 hint = (
6378 _(
6377 _(
6379 b"uncommitted changes, use --all to discard all"
6378 b"uncommitted changes, use --all to discard all"
6380 b" changes, or 'hg update %d' to update"
6379 b" changes, or 'hg update %d' to update"
6381 )
6380 )
6382 % ctx.rev()
6381 % ctx.rev()
6383 )
6382 )
6384 else:
6383 else:
6385 hint = (
6384 hint = (
6386 _(
6385 _(
6387 b"use --all to revert all files,"
6386 b"use --all to revert all files,"
6388 b" or 'hg update %d' to update"
6387 b" or 'hg update %d' to update"
6389 )
6388 )
6390 % ctx.rev()
6389 % ctx.rev()
6391 )
6390 )
6392 elif dirty:
6391 elif dirty:
6393 hint = _(b"uncommitted changes, use --all to discard all changes")
6392 hint = _(b"uncommitted changes, use --all to discard all changes")
6394 else:
6393 else:
6395 hint = _(b"use --all to revert all files")
6394 hint = _(b"use --all to revert all files")
6396 raise error.InputError(msg, hint=hint)
6395 raise error.InputError(msg, hint=hint)
6397
6396
6398 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6397 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6399
6398
6400
6399
6401 @command(
6400 @command(
6402 b'rollback',
6401 b'rollback',
6403 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6402 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6404 helpcategory=command.CATEGORY_MAINTENANCE,
6403 helpcategory=command.CATEGORY_MAINTENANCE,
6405 )
6404 )
6406 def rollback(ui, repo, **opts):
6405 def rollback(ui, repo, **opts):
6407 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6406 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6408
6407
6409 Please use :hg:`commit --amend` instead of rollback to correct
6408 Please use :hg:`commit --amend` instead of rollback to correct
6410 mistakes in the last commit.
6409 mistakes in the last commit.
6411
6410
6412 This command should be used with care. There is only one level of
6411 This command should be used with care. There is only one level of
6413 rollback, and there is no way to undo a rollback. It will also
6412 rollback, and there is no way to undo a rollback. It will also
6414 restore the dirstate at the time of the last transaction, losing
6413 restore the dirstate at the time of the last transaction, losing
6415 any dirstate changes since that time. This command does not alter
6414 any dirstate changes since that time. This command does not alter
6416 the working directory.
6415 the working directory.
6417
6416
6418 Transactions are used to encapsulate the effects of all commands
6417 Transactions are used to encapsulate the effects of all commands
6419 that create new changesets or propagate existing changesets into a
6418 that create new changesets or propagate existing changesets into a
6420 repository.
6419 repository.
6421
6420
6422 .. container:: verbose
6421 .. container:: verbose
6423
6422
6424 For example, the following commands are transactional, and their
6423 For example, the following commands are transactional, and their
6425 effects can be rolled back:
6424 effects can be rolled back:
6426
6425
6427 - commit
6426 - commit
6428 - import
6427 - import
6429 - pull
6428 - pull
6430 - push (with this repository as the destination)
6429 - push (with this repository as the destination)
6431 - unbundle
6430 - unbundle
6432
6431
6433 To avoid permanent data loss, rollback will refuse to rollback a
6432 To avoid permanent data loss, rollback will refuse to rollback a
6434 commit transaction if it isn't checked out. Use --force to
6433 commit transaction if it isn't checked out. Use --force to
6435 override this protection.
6434 override this protection.
6436
6435
6437 The rollback command can be entirely disabled by setting the
6436 The rollback command can be entirely disabled by setting the
6438 ``ui.rollback`` configuration setting to false. If you're here
6437 ``ui.rollback`` configuration setting to false. If you're here
6439 because you want to use rollback and it's disabled, you can
6438 because you want to use rollback and it's disabled, you can
6440 re-enable the command by setting ``ui.rollback`` to true.
6439 re-enable the command by setting ``ui.rollback`` to true.
6441
6440
6442 This command is not intended for use on public repositories. Once
6441 This command is not intended for use on public repositories. Once
6443 changes are visible for pull by other users, rolling a transaction
6442 changes are visible for pull by other users, rolling a transaction
6444 back locally is ineffective (someone else may already have pulled
6443 back locally is ineffective (someone else may already have pulled
6445 the changes). Furthermore, a race is possible with readers of the
6444 the changes). Furthermore, a race is possible with readers of the
6446 repository; for example an in-progress pull from the repository
6445 repository; for example an in-progress pull from the repository
6447 may fail if a rollback is performed.
6446 may fail if a rollback is performed.
6448
6447
6449 Returns 0 on success, 1 if no rollback data is available.
6448 Returns 0 on success, 1 if no rollback data is available.
6450 """
6449 """
6451 if not ui.configbool(b'ui', b'rollback'):
6450 if not ui.configbool(b'ui', b'rollback'):
6452 raise error.Abort(
6451 raise error.Abort(
6453 _(b'rollback is disabled because it is unsafe'),
6452 _(b'rollback is disabled because it is unsafe'),
6454 hint=b'see `hg help -v rollback` for information',
6453 hint=b'see `hg help -v rollback` for information',
6455 )
6454 )
6456 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6455 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6457
6456
6458
6457
6459 @command(
6458 @command(
6460 b'root',
6459 b'root',
6461 [] + formatteropts,
6460 [] + formatteropts,
6462 intents={INTENT_READONLY},
6461 intents={INTENT_READONLY},
6463 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6462 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6464 )
6463 )
6465 def root(ui, repo, **opts):
6464 def root(ui, repo, **opts):
6466 """print the root (top) of the current working directory
6465 """print the root (top) of the current working directory
6467
6466
6468 Print the root directory of the current repository.
6467 Print the root directory of the current repository.
6469
6468
6470 .. container:: verbose
6469 .. container:: verbose
6471
6470
6472 Template:
6471 Template:
6473
6472
6474 The following keywords are supported in addition to the common template
6473 The following keywords are supported in addition to the common template
6475 keywords and functions. See also :hg:`help templates`.
6474 keywords and functions. See also :hg:`help templates`.
6476
6475
6477 :hgpath: String. Path to the .hg directory.
6476 :hgpath: String. Path to the .hg directory.
6478 :storepath: String. Path to the directory holding versioned data.
6477 :storepath: String. Path to the directory holding versioned data.
6479
6478
6480 Returns 0 on success.
6479 Returns 0 on success.
6481 """
6480 """
6482 opts = pycompat.byteskwargs(opts)
6481 opts = pycompat.byteskwargs(opts)
6483 with ui.formatter(b'root', opts) as fm:
6482 with ui.formatter(b'root', opts) as fm:
6484 fm.startitem()
6483 fm.startitem()
6485 fm.write(b'reporoot', b'%s\n', repo.root)
6484 fm.write(b'reporoot', b'%s\n', repo.root)
6486 fm.data(hgpath=repo.path, storepath=repo.spath)
6485 fm.data(hgpath=repo.path, storepath=repo.spath)
6487
6486
6488
6487
6489 @command(
6488 @command(
6490 b'serve',
6489 b'serve',
6491 [
6490 [
6492 (
6491 (
6493 b'A',
6492 b'A',
6494 b'accesslog',
6493 b'accesslog',
6495 b'',
6494 b'',
6496 _(b'name of access log file to write to'),
6495 _(b'name of access log file to write to'),
6497 _(b'FILE'),
6496 _(b'FILE'),
6498 ),
6497 ),
6499 (b'd', b'daemon', None, _(b'run server in background')),
6498 (b'd', b'daemon', None, _(b'run server in background')),
6500 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6499 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6501 (
6500 (
6502 b'E',
6501 b'E',
6503 b'errorlog',
6502 b'errorlog',
6504 b'',
6503 b'',
6505 _(b'name of error log file to write to'),
6504 _(b'name of error log file to write to'),
6506 _(b'FILE'),
6505 _(b'FILE'),
6507 ),
6506 ),
6508 # use string type, then we can check if something was passed
6507 # use string type, then we can check if something was passed
6509 (
6508 (
6510 b'p',
6509 b'p',
6511 b'port',
6510 b'port',
6512 b'',
6511 b'',
6513 _(b'port to listen on (default: 8000)'),
6512 _(b'port to listen on (default: 8000)'),
6514 _(b'PORT'),
6513 _(b'PORT'),
6515 ),
6514 ),
6516 (
6515 (
6517 b'a',
6516 b'a',
6518 b'address',
6517 b'address',
6519 b'',
6518 b'',
6520 _(b'address to listen on (default: all interfaces)'),
6519 _(b'address to listen on (default: all interfaces)'),
6521 _(b'ADDR'),
6520 _(b'ADDR'),
6522 ),
6521 ),
6523 (
6522 (
6524 b'',
6523 b'',
6525 b'prefix',
6524 b'prefix',
6526 b'',
6525 b'',
6527 _(b'prefix path to serve from (default: server root)'),
6526 _(b'prefix path to serve from (default: server root)'),
6528 _(b'PREFIX'),
6527 _(b'PREFIX'),
6529 ),
6528 ),
6530 (
6529 (
6531 b'n',
6530 b'n',
6532 b'name',
6531 b'name',
6533 b'',
6532 b'',
6534 _(b'name to show in web pages (default: working directory)'),
6533 _(b'name to show in web pages (default: working directory)'),
6535 _(b'NAME'),
6534 _(b'NAME'),
6536 ),
6535 ),
6537 (
6536 (
6538 b'',
6537 b'',
6539 b'web-conf',
6538 b'web-conf',
6540 b'',
6539 b'',
6541 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6540 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6542 _(b'FILE'),
6541 _(b'FILE'),
6543 ),
6542 ),
6544 (
6543 (
6545 b'',
6544 b'',
6546 b'webdir-conf',
6545 b'webdir-conf',
6547 b'',
6546 b'',
6548 _(b'name of the hgweb config file (DEPRECATED)'),
6547 _(b'name of the hgweb config file (DEPRECATED)'),
6549 _(b'FILE'),
6548 _(b'FILE'),
6550 ),
6549 ),
6551 (
6550 (
6552 b'',
6551 b'',
6553 b'pid-file',
6552 b'pid-file',
6554 b'',
6553 b'',
6555 _(b'name of file to write process ID to'),
6554 _(b'name of file to write process ID to'),
6556 _(b'FILE'),
6555 _(b'FILE'),
6557 ),
6556 ),
6558 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6557 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6559 (
6558 (
6560 b'',
6559 b'',
6561 b'cmdserver',
6560 b'cmdserver',
6562 b'',
6561 b'',
6563 _(b'for remote clients (ADVANCED)'),
6562 _(b'for remote clients (ADVANCED)'),
6564 _(b'MODE'),
6563 _(b'MODE'),
6565 ),
6564 ),
6566 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6565 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6567 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6566 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6568 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6567 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6569 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6568 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6570 (b'', b'print-url', None, _(b'start and print only the URL')),
6569 (b'', b'print-url', None, _(b'start and print only the URL')),
6571 ]
6570 ]
6572 + subrepoopts,
6571 + subrepoopts,
6573 _(b'[OPTION]...'),
6572 _(b'[OPTION]...'),
6574 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6573 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6575 helpbasic=True,
6574 helpbasic=True,
6576 optionalrepo=True,
6575 optionalrepo=True,
6577 )
6576 )
6578 def serve(ui, repo, **opts):
6577 def serve(ui, repo, **opts):
6579 """start stand-alone webserver
6578 """start stand-alone webserver
6580
6579
6581 Start a local HTTP repository browser and pull server. You can use
6580 Start a local HTTP repository browser and pull server. You can use
6582 this for ad-hoc sharing and browsing of repositories. It is
6581 this for ad-hoc sharing and browsing of repositories. It is
6583 recommended to use a real web server to serve a repository for
6582 recommended to use a real web server to serve a repository for
6584 longer periods of time.
6583 longer periods of time.
6585
6584
6586 Please note that the server does not implement access control.
6585 Please note that the server does not implement access control.
6587 This means that, by default, anybody can read from the server and
6586 This means that, by default, anybody can read from the server and
6588 nobody can write to it by default. Set the ``web.allow-push``
6587 nobody can write to it by default. Set the ``web.allow-push``
6589 option to ``*`` to allow everybody to push to the server. You
6588 option to ``*`` to allow everybody to push to the server. You
6590 should use a real web server if you need to authenticate users.
6589 should use a real web server if you need to authenticate users.
6591
6590
6592 By default, the server logs accesses to stdout and errors to
6591 By default, the server logs accesses to stdout and errors to
6593 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6592 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6594 files.
6593 files.
6595
6594
6596 To have the server choose a free port number to listen on, specify
6595 To have the server choose a free port number to listen on, specify
6597 a port number of 0; in this case, the server will print the port
6596 a port number of 0; in this case, the server will print the port
6598 number it uses.
6597 number it uses.
6599
6598
6600 Returns 0 on success.
6599 Returns 0 on success.
6601 """
6600 """
6602
6601
6603 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6602 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6604 opts = pycompat.byteskwargs(opts)
6603 opts = pycompat.byteskwargs(opts)
6605 if opts[b"print_url"] and ui.verbose:
6604 if opts[b"print_url"] and ui.verbose:
6606 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6605 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6607
6606
6608 if opts[b"stdio"]:
6607 if opts[b"stdio"]:
6609 if repo is None:
6608 if repo is None:
6610 raise error.RepoError(
6609 raise error.RepoError(
6611 _(b"there is no Mercurial repository here (.hg not found)")
6610 _(b"there is no Mercurial repository here (.hg not found)")
6612 )
6611 )
6613 s = wireprotoserver.sshserver(ui, repo)
6612 s = wireprotoserver.sshserver(ui, repo)
6614 s.serve_forever()
6613 s.serve_forever()
6615 return
6614 return
6616
6615
6617 service = server.createservice(ui, repo, opts)
6616 service = server.createservice(ui, repo, opts)
6618 return server.runservice(opts, initfn=service.init, runfn=service.run)
6617 return server.runservice(opts, initfn=service.init, runfn=service.run)
6619
6618
6620
6619
6621 @command(
6620 @command(
6622 b'shelve',
6621 b'shelve',
6623 [
6622 [
6624 (
6623 (
6625 b'A',
6624 b'A',
6626 b'addremove',
6625 b'addremove',
6627 None,
6626 None,
6628 _(b'mark new/missing files as added/removed before shelving'),
6627 _(b'mark new/missing files as added/removed before shelving'),
6629 ),
6628 ),
6630 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6629 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6631 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6630 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6632 (
6631 (
6633 b'',
6632 b'',
6634 b'date',
6633 b'date',
6635 b'',
6634 b'',
6636 _(b'shelve with the specified commit date'),
6635 _(b'shelve with the specified commit date'),
6637 _(b'DATE'),
6636 _(b'DATE'),
6638 ),
6637 ),
6639 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6638 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6640 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6639 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6641 (
6640 (
6642 b'k',
6641 b'k',
6643 b'keep',
6642 b'keep',
6644 False,
6643 False,
6645 _(b'shelve, but keep changes in the working directory'),
6644 _(b'shelve, but keep changes in the working directory'),
6646 ),
6645 ),
6647 (b'l', b'list', None, _(b'list current shelves')),
6646 (b'l', b'list', None, _(b'list current shelves')),
6648 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6647 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6649 (
6648 (
6650 b'n',
6649 b'n',
6651 b'name',
6650 b'name',
6652 b'',
6651 b'',
6653 _(b'use the given name for the shelved commit'),
6652 _(b'use the given name for the shelved commit'),
6654 _(b'NAME'),
6653 _(b'NAME'),
6655 ),
6654 ),
6656 (
6655 (
6657 b'p',
6656 b'p',
6658 b'patch',
6657 b'patch',
6659 None,
6658 None,
6660 _(
6659 _(
6661 b'output patches for changes (provide the names of the shelved '
6660 b'output patches for changes (provide the names of the shelved '
6662 b'changes as positional arguments)'
6661 b'changes as positional arguments)'
6663 ),
6662 ),
6664 ),
6663 ),
6665 (b'i', b'interactive', None, _(b'interactive mode')),
6664 (b'i', b'interactive', None, _(b'interactive mode')),
6666 (
6665 (
6667 b'',
6666 b'',
6668 b'stat',
6667 b'stat',
6669 None,
6668 None,
6670 _(
6669 _(
6671 b'output diffstat-style summary of changes (provide the names of '
6670 b'output diffstat-style summary of changes (provide the names of '
6672 b'the shelved changes as positional arguments)'
6671 b'the shelved changes as positional arguments)'
6673 ),
6672 ),
6674 ),
6673 ),
6675 ]
6674 ]
6676 + cmdutil.walkopts,
6675 + cmdutil.walkopts,
6677 _(b'hg shelve [OPTION]... [FILE]...'),
6676 _(b'hg shelve [OPTION]... [FILE]...'),
6678 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6677 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6679 )
6678 )
6680 def shelve(ui, repo, *pats, **opts):
6679 def shelve(ui, repo, *pats, **opts):
6681 """save and set aside changes from the working directory
6680 """save and set aside changes from the working directory
6682
6681
6683 Shelving takes files that "hg status" reports as not clean, saves
6682 Shelving takes files that "hg status" reports as not clean, saves
6684 the modifications to a bundle (a shelved change), and reverts the
6683 the modifications to a bundle (a shelved change), and reverts the
6685 files so that their state in the working directory becomes clean.
6684 files so that their state in the working directory becomes clean.
6686
6685
6687 To restore these changes to the working directory, using "hg
6686 To restore these changes to the working directory, using "hg
6688 unshelve"; this will work even if you switch to a different
6687 unshelve"; this will work even if you switch to a different
6689 commit.
6688 commit.
6690
6689
6691 When no files are specified, "hg shelve" saves all not-clean
6690 When no files are specified, "hg shelve" saves all not-clean
6692 files. If specific files or directories are named, only changes to
6691 files. If specific files or directories are named, only changes to
6693 those files are shelved.
6692 those files are shelved.
6694
6693
6695 In bare shelve (when no files are specified, without interactive,
6694 In bare shelve (when no files are specified, without interactive,
6696 include and exclude option), shelving remembers information if the
6695 include and exclude option), shelving remembers information if the
6697 working directory was on newly created branch, in other words working
6696 working directory was on newly created branch, in other words working
6698 directory was on different branch than its first parent. In this
6697 directory was on different branch than its first parent. In this
6699 situation unshelving restores branch information to the working directory.
6698 situation unshelving restores branch information to the working directory.
6700
6699
6701 Each shelved change has a name that makes it easier to find later.
6700 Each shelved change has a name that makes it easier to find later.
6702 The name of a shelved change defaults to being based on the active
6701 The name of a shelved change defaults to being based on the active
6703 bookmark, or if there is no active bookmark, the current named
6702 bookmark, or if there is no active bookmark, the current named
6704 branch. To specify a different name, use ``--name``.
6703 branch. To specify a different name, use ``--name``.
6705
6704
6706 To see a list of existing shelved changes, use the ``--list``
6705 To see a list of existing shelved changes, use the ``--list``
6707 option. For each shelved change, this will print its name, age,
6706 option. For each shelved change, this will print its name, age,
6708 and description; use ``--patch`` or ``--stat`` for more details.
6707 and description; use ``--patch`` or ``--stat`` for more details.
6709
6708
6710 To delete specific shelved changes, use ``--delete``. To delete
6709 To delete specific shelved changes, use ``--delete``. To delete
6711 all shelved changes, use ``--cleanup``.
6710 all shelved changes, use ``--cleanup``.
6712 """
6711 """
6713 opts = pycompat.byteskwargs(opts)
6712 opts = pycompat.byteskwargs(opts)
6714 allowables = [
6713 allowables = [
6715 (b'addremove', {b'create'}), # 'create' is pseudo action
6714 (b'addremove', {b'create'}), # 'create' is pseudo action
6716 (b'unknown', {b'create'}),
6715 (b'unknown', {b'create'}),
6717 (b'cleanup', {b'cleanup'}),
6716 (b'cleanup', {b'cleanup'}),
6718 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6717 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6719 (b'delete', {b'delete'}),
6718 (b'delete', {b'delete'}),
6720 (b'edit', {b'create'}),
6719 (b'edit', {b'create'}),
6721 (b'keep', {b'create'}),
6720 (b'keep', {b'create'}),
6722 (b'list', {b'list'}),
6721 (b'list', {b'list'}),
6723 (b'message', {b'create'}),
6722 (b'message', {b'create'}),
6724 (b'name', {b'create'}),
6723 (b'name', {b'create'}),
6725 (b'patch', {b'patch', b'list'}),
6724 (b'patch', {b'patch', b'list'}),
6726 (b'stat', {b'stat', b'list'}),
6725 (b'stat', {b'stat', b'list'}),
6727 ]
6726 ]
6728
6727
6729 def checkopt(opt):
6728 def checkopt(opt):
6730 if opts.get(opt):
6729 if opts.get(opt):
6731 for i, allowable in allowables:
6730 for i, allowable in allowables:
6732 if opts[i] and opt not in allowable:
6731 if opts[i] and opt not in allowable:
6733 raise error.InputError(
6732 raise error.InputError(
6734 _(
6733 _(
6735 b"options '--%s' and '--%s' may not be "
6734 b"options '--%s' and '--%s' may not be "
6736 b"used together"
6735 b"used together"
6737 )
6736 )
6738 % (opt, i)
6737 % (opt, i)
6739 )
6738 )
6740 return True
6739 return True
6741
6740
6742 if checkopt(b'cleanup'):
6741 if checkopt(b'cleanup'):
6743 if pats:
6742 if pats:
6744 raise error.InputError(
6743 raise error.InputError(
6745 _(b"cannot specify names when using '--cleanup'")
6744 _(b"cannot specify names when using '--cleanup'")
6746 )
6745 )
6747 return shelvemod.cleanupcmd(ui, repo)
6746 return shelvemod.cleanupcmd(ui, repo)
6748 elif checkopt(b'delete'):
6747 elif checkopt(b'delete'):
6749 return shelvemod.deletecmd(ui, repo, pats)
6748 return shelvemod.deletecmd(ui, repo, pats)
6750 elif checkopt(b'list'):
6749 elif checkopt(b'list'):
6751 return shelvemod.listcmd(ui, repo, pats, opts)
6750 return shelvemod.listcmd(ui, repo, pats, opts)
6752 elif checkopt(b'patch') or checkopt(b'stat'):
6751 elif checkopt(b'patch') or checkopt(b'stat'):
6753 return shelvemod.patchcmds(ui, repo, pats, opts)
6752 return shelvemod.patchcmds(ui, repo, pats, opts)
6754 else:
6753 else:
6755 return shelvemod.createcmd(ui, repo, pats, opts)
6754 return shelvemod.createcmd(ui, repo, pats, opts)
6756
6755
6757
6756
6758 _NOTTERSE = b'nothing'
6757 _NOTTERSE = b'nothing'
6759
6758
6760
6759
6761 @command(
6760 @command(
6762 b'status|st',
6761 b'status|st',
6763 [
6762 [
6764 (b'A', b'all', None, _(b'show status of all files')),
6763 (b'A', b'all', None, _(b'show status of all files')),
6765 (b'm', b'modified', None, _(b'show only modified files')),
6764 (b'm', b'modified', None, _(b'show only modified files')),
6766 (b'a', b'added', None, _(b'show only added files')),
6765 (b'a', b'added', None, _(b'show only added files')),
6767 (b'r', b'removed', None, _(b'show only removed files')),
6766 (b'r', b'removed', None, _(b'show only removed files')),
6768 (b'd', b'deleted', None, _(b'show only missing files')),
6767 (b'd', b'deleted', None, _(b'show only missing files')),
6769 (b'c', b'clean', None, _(b'show only files without changes')),
6768 (b'c', b'clean', None, _(b'show only files without changes')),
6770 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6769 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6771 (b'i', b'ignored', None, _(b'show only ignored files')),
6770 (b'i', b'ignored', None, _(b'show only ignored files')),
6772 (b'n', b'no-status', None, _(b'hide status prefix')),
6771 (b'n', b'no-status', None, _(b'hide status prefix')),
6773 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6772 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6774 (
6773 (
6775 b'C',
6774 b'C',
6776 b'copies',
6775 b'copies',
6777 None,
6776 None,
6778 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6777 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6779 ),
6778 ),
6780 (
6779 (
6781 b'0',
6780 b'0',
6782 b'print0',
6781 b'print0',
6783 None,
6782 None,
6784 _(b'end filenames with NUL, for use with xargs'),
6783 _(b'end filenames with NUL, for use with xargs'),
6785 ),
6784 ),
6786 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6785 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6787 (
6786 (
6788 b'',
6787 b'',
6789 b'change',
6788 b'change',
6790 b'',
6789 b'',
6791 _(b'list the changed files of a revision'),
6790 _(b'list the changed files of a revision'),
6792 _(b'REV'),
6791 _(b'REV'),
6793 ),
6792 ),
6794 ]
6793 ]
6795 + walkopts
6794 + walkopts
6796 + subrepoopts
6795 + subrepoopts
6797 + formatteropts,
6796 + formatteropts,
6798 _(b'[OPTION]... [FILE]...'),
6797 _(b'[OPTION]... [FILE]...'),
6799 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6798 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6800 helpbasic=True,
6799 helpbasic=True,
6801 inferrepo=True,
6800 inferrepo=True,
6802 intents={INTENT_READONLY},
6801 intents={INTENT_READONLY},
6803 )
6802 )
6804 def status(ui, repo, *pats, **opts):
6803 def status(ui, repo, *pats, **opts):
6805 """show changed files in the working directory
6804 """show changed files in the working directory
6806
6805
6807 Show status of files in the repository. If names are given, only
6806 Show status of files in the repository. If names are given, only
6808 files that match are shown. Files that are clean or ignored or
6807 files that match are shown. Files that are clean or ignored or
6809 the source of a copy/move operation, are not listed unless
6808 the source of a copy/move operation, are not listed unless
6810 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6809 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6811 Unless options described with "show only ..." are given, the
6810 Unless options described with "show only ..." are given, the
6812 options -mardu are used.
6811 options -mardu are used.
6813
6812
6814 Option -q/--quiet hides untracked (unknown and ignored) files
6813 Option -q/--quiet hides untracked (unknown and ignored) files
6815 unless explicitly requested with -u/--unknown or -i/--ignored.
6814 unless explicitly requested with -u/--unknown or -i/--ignored.
6816
6815
6817 .. note::
6816 .. note::
6818
6817
6819 :hg:`status` may appear to disagree with diff if permissions have
6818 :hg:`status` may appear to disagree with diff if permissions have
6820 changed or a merge has occurred. The standard diff format does
6819 changed or a merge has occurred. The standard diff format does
6821 not report permission changes and diff only reports changes
6820 not report permission changes and diff only reports changes
6822 relative to one merge parent.
6821 relative to one merge parent.
6823
6822
6824 If one revision is given, it is used as the base revision.
6823 If one revision is given, it is used as the base revision.
6825 If two revisions are given, the differences between them are
6824 If two revisions are given, the differences between them are
6826 shown. The --change option can also be used as a shortcut to list
6825 shown. The --change option can also be used as a shortcut to list
6827 the changed files of a revision from its first parent.
6826 the changed files of a revision from its first parent.
6828
6827
6829 The codes used to show the status of files are::
6828 The codes used to show the status of files are::
6830
6829
6831 M = modified
6830 M = modified
6832 A = added
6831 A = added
6833 R = removed
6832 R = removed
6834 C = clean
6833 C = clean
6835 ! = missing (deleted by non-hg command, but still tracked)
6834 ! = missing (deleted by non-hg command, but still tracked)
6836 ? = not tracked
6835 ? = not tracked
6837 I = ignored
6836 I = ignored
6838 = origin of the previous file (with --copies)
6837 = origin of the previous file (with --copies)
6839
6838
6840 .. container:: verbose
6839 .. container:: verbose
6841
6840
6842 The -t/--terse option abbreviates the output by showing only the directory
6841 The -t/--terse option abbreviates the output by showing only the directory
6843 name if all the files in it share the same status. The option takes an
6842 name if all the files in it share the same status. The option takes an
6844 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6843 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6845 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6844 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6846 for 'ignored' and 'c' for clean.
6845 for 'ignored' and 'c' for clean.
6847
6846
6848 It abbreviates only those statuses which are passed. Note that clean and
6847 It abbreviates only those statuses which are passed. Note that clean and
6849 ignored files are not displayed with '--terse ic' unless the -c/--clean
6848 ignored files are not displayed with '--terse ic' unless the -c/--clean
6850 and -i/--ignored options are also used.
6849 and -i/--ignored options are also used.
6851
6850
6852 The -v/--verbose option shows information when the repository is in an
6851 The -v/--verbose option shows information when the repository is in an
6853 unfinished merge, shelve, rebase state etc. You can have this behavior
6852 unfinished merge, shelve, rebase state etc. You can have this behavior
6854 turned on by default by enabling the ``commands.status.verbose`` option.
6853 turned on by default by enabling the ``commands.status.verbose`` option.
6855
6854
6856 You can skip displaying some of these states by setting
6855 You can skip displaying some of these states by setting
6857 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6856 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6858 'histedit', 'merge', 'rebase', or 'unshelve'.
6857 'histedit', 'merge', 'rebase', or 'unshelve'.
6859
6858
6860 Template:
6859 Template:
6861
6860
6862 The following keywords are supported in addition to the common template
6861 The following keywords are supported in addition to the common template
6863 keywords and functions. See also :hg:`help templates`.
6862 keywords and functions. See also :hg:`help templates`.
6864
6863
6865 :path: String. Repository-absolute path of the file.
6864 :path: String. Repository-absolute path of the file.
6866 :source: String. Repository-absolute path of the file originated from.
6865 :source: String. Repository-absolute path of the file originated from.
6867 Available if ``--copies`` is specified.
6866 Available if ``--copies`` is specified.
6868 :status: String. Character denoting file's status.
6867 :status: String. Character denoting file's status.
6869
6868
6870 Examples:
6869 Examples:
6871
6870
6872 - show changes in the working directory relative to a
6871 - show changes in the working directory relative to a
6873 changeset::
6872 changeset::
6874
6873
6875 hg status --rev 9353
6874 hg status --rev 9353
6876
6875
6877 - show changes in the working directory relative to the
6876 - show changes in the working directory relative to the
6878 current directory (see :hg:`help patterns` for more information)::
6877 current directory (see :hg:`help patterns` for more information)::
6879
6878
6880 hg status re:
6879 hg status re:
6881
6880
6882 - show all changes including copies in an existing changeset::
6881 - show all changes including copies in an existing changeset::
6883
6882
6884 hg status --copies --change 9353
6883 hg status --copies --change 9353
6885
6884
6886 - get a NUL separated list of added files, suitable for xargs::
6885 - get a NUL separated list of added files, suitable for xargs::
6887
6886
6888 hg status -an0
6887 hg status -an0
6889
6888
6890 - show more information about the repository status, abbreviating
6889 - show more information about the repository status, abbreviating
6891 added, removed, modified, deleted, and untracked paths::
6890 added, removed, modified, deleted, and untracked paths::
6892
6891
6893 hg status -v -t mardu
6892 hg status -v -t mardu
6894
6893
6895 Returns 0 on success.
6894 Returns 0 on success.
6896
6895
6897 """
6896 """
6898
6897
6899 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6898 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6900 opts = pycompat.byteskwargs(opts)
6899 opts = pycompat.byteskwargs(opts)
6901 revs = opts.get(b'rev', [])
6900 revs = opts.get(b'rev', [])
6902 change = opts.get(b'change', b'')
6901 change = opts.get(b'change', b'')
6903 terse = opts.get(b'terse', _NOTTERSE)
6902 terse = opts.get(b'terse', _NOTTERSE)
6904 if terse is _NOTTERSE:
6903 if terse is _NOTTERSE:
6905 if revs:
6904 if revs:
6906 terse = b''
6905 terse = b''
6907 else:
6906 else:
6908 terse = ui.config(b'commands', b'status.terse')
6907 terse = ui.config(b'commands', b'status.terse')
6909
6908
6910 if revs and terse:
6909 if revs and terse:
6911 msg = _(b'cannot use --terse with --rev')
6910 msg = _(b'cannot use --terse with --rev')
6912 raise error.InputError(msg)
6911 raise error.InputError(msg)
6913 elif change:
6912 elif change:
6914 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6913 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6915 ctx2 = logcmdutil.revsingle(repo, change, None)
6914 ctx2 = logcmdutil.revsingle(repo, change, None)
6916 ctx1 = ctx2.p1()
6915 ctx1 = ctx2.p1()
6917 else:
6916 else:
6918 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6917 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6919 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
6918 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
6920
6919
6921 forcerelativevalue = None
6920 forcerelativevalue = None
6922 if ui.hasconfig(b'commands', b'status.relative'):
6921 if ui.hasconfig(b'commands', b'status.relative'):
6923 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6922 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6924 uipathfn = scmutil.getuipathfn(
6923 uipathfn = scmutil.getuipathfn(
6925 repo,
6924 repo,
6926 legacyrelativevalue=bool(pats),
6925 legacyrelativevalue=bool(pats),
6927 forcerelativevalue=forcerelativevalue,
6926 forcerelativevalue=forcerelativevalue,
6928 )
6927 )
6929
6928
6930 if opts.get(b'print0'):
6929 if opts.get(b'print0'):
6931 end = b'\0'
6930 end = b'\0'
6932 else:
6931 else:
6933 end = b'\n'
6932 end = b'\n'
6934 states = b'modified added removed deleted unknown ignored clean'.split()
6933 states = b'modified added removed deleted unknown ignored clean'.split()
6935 show = [k for k in states if opts.get(k)]
6934 show = [k for k in states if opts.get(k)]
6936 if opts.get(b'all'):
6935 if opts.get(b'all'):
6937 show += ui.quiet and (states[:4] + [b'clean']) or states
6936 show += ui.quiet and (states[:4] + [b'clean']) or states
6938
6937
6939 if not show:
6938 if not show:
6940 if ui.quiet:
6939 if ui.quiet:
6941 show = states[:4]
6940 show = states[:4]
6942 else:
6941 else:
6943 show = states[:5]
6942 show = states[:5]
6944
6943
6945 m = scmutil.match(ctx2, pats, opts)
6944 m = scmutil.match(ctx2, pats, opts)
6946 if terse:
6945 if terse:
6947 # we need to compute clean and unknown to terse
6946 # we need to compute clean and unknown to terse
6948 stat = repo.status(
6947 stat = repo.status(
6949 ctx1.node(),
6948 ctx1.node(),
6950 ctx2.node(),
6949 ctx2.node(),
6951 m,
6950 m,
6952 b'ignored' in show or b'i' in terse,
6951 b'ignored' in show or b'i' in terse,
6953 clean=True,
6952 clean=True,
6954 unknown=True,
6953 unknown=True,
6955 listsubrepos=opts.get(b'subrepos'),
6954 listsubrepos=opts.get(b'subrepos'),
6956 )
6955 )
6957
6956
6958 stat = cmdutil.tersedir(stat, terse)
6957 stat = cmdutil.tersedir(stat, terse)
6959 else:
6958 else:
6960 stat = repo.status(
6959 stat = repo.status(
6961 ctx1.node(),
6960 ctx1.node(),
6962 ctx2.node(),
6961 ctx2.node(),
6963 m,
6962 m,
6964 b'ignored' in show,
6963 b'ignored' in show,
6965 b'clean' in show,
6964 b'clean' in show,
6966 b'unknown' in show,
6965 b'unknown' in show,
6967 opts.get(b'subrepos'),
6966 opts.get(b'subrepos'),
6968 )
6967 )
6969
6968
6970 changestates = zip(
6969 changestates = zip(
6971 states,
6970 states,
6972 pycompat.iterbytestr(b'MAR!?IC'),
6971 pycompat.iterbytestr(b'MAR!?IC'),
6973 [getattr(stat, s.decode('utf8')) for s in states],
6972 [getattr(stat, s.decode('utf8')) for s in states],
6974 )
6973 )
6975
6974
6976 copy = {}
6975 copy = {}
6977 if (
6976 if (
6978 opts.get(b'all')
6977 opts.get(b'all')
6979 or opts.get(b'copies')
6978 or opts.get(b'copies')
6980 or ui.configbool(b'ui', b'statuscopies')
6979 or ui.configbool(b'ui', b'statuscopies')
6981 ) and not opts.get(b'no_status'):
6980 ) and not opts.get(b'no_status'):
6982 copy = copies.pathcopies(ctx1, ctx2, m)
6981 copy = copies.pathcopies(ctx1, ctx2, m)
6983
6982
6984 morestatus = None
6983 morestatus = None
6985 if (
6984 if (
6986 (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
6985 (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
6987 and not ui.plain()
6986 and not ui.plain()
6988 and not opts.get(b'print0')
6987 and not opts.get(b'print0')
6989 ):
6988 ):
6990 morestatus = cmdutil.readmorestatus(repo)
6989 morestatus = cmdutil.readmorestatus(repo)
6991
6990
6992 ui.pager(b'status')
6991 ui.pager(b'status')
6993 fm = ui.formatter(b'status', opts)
6992 fm = ui.formatter(b'status', opts)
6994 fmt = b'%s' + end
6993 fmt = b'%s' + end
6995 showchar = not opts.get(b'no_status')
6994 showchar = not opts.get(b'no_status')
6996
6995
6997 for state, char, files in changestates:
6996 for state, char, files in changestates:
6998 if state in show:
6997 if state in show:
6999 label = b'status.' + state
6998 label = b'status.' + state
7000 for f in files:
6999 for f in files:
7001 fm.startitem()
7000 fm.startitem()
7002 fm.context(ctx=ctx2)
7001 fm.context(ctx=ctx2)
7003 fm.data(itemtype=b'file', path=f)
7002 fm.data(itemtype=b'file', path=f)
7004 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
7003 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
7005 fm.plain(fmt % uipathfn(f), label=label)
7004 fm.plain(fmt % uipathfn(f), label=label)
7006 if f in copy:
7005 if f in copy:
7007 fm.data(source=copy[f])
7006 fm.data(source=copy[f])
7008 fm.plain(
7007 fm.plain(
7009 (b' %s' + end) % uipathfn(copy[f]),
7008 (b' %s' + end) % uipathfn(copy[f]),
7010 label=b'status.copied',
7009 label=b'status.copied',
7011 )
7010 )
7012 if morestatus:
7011 if morestatus:
7013 morestatus.formatfile(f, fm)
7012 morestatus.formatfile(f, fm)
7014
7013
7015 if morestatus:
7014 if morestatus:
7016 morestatus.formatfooter(fm)
7015 morestatus.formatfooter(fm)
7017 fm.end()
7016 fm.end()
7018
7017
7019
7018
7020 @command(
7019 @command(
7021 b'summary|sum',
7020 b'summary|sum',
7022 [(b'', b'remote', None, _(b'check for push and pull'))],
7021 [(b'', b'remote', None, _(b'check for push and pull'))],
7023 b'[--remote]',
7022 b'[--remote]',
7024 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7023 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7025 helpbasic=True,
7024 helpbasic=True,
7026 intents={INTENT_READONLY},
7025 intents={INTENT_READONLY},
7027 )
7026 )
7028 def summary(ui, repo, **opts):
7027 def summary(ui, repo, **opts):
7029 """summarize working directory state
7028 """summarize working directory state
7030
7029
7031 This generates a brief summary of the working directory state,
7030 This generates a brief summary of the working directory state,
7032 including parents, branch, commit status, phase and available updates.
7031 including parents, branch, commit status, phase and available updates.
7033
7032
7034 With the --remote option, this will check the default paths for
7033 With the --remote option, this will check the default paths for
7035 incoming and outgoing changes. This can be time-consuming.
7034 incoming and outgoing changes. This can be time-consuming.
7036
7035
7037 Returns 0 on success.
7036 Returns 0 on success.
7038 """
7037 """
7039
7038
7040 opts = pycompat.byteskwargs(opts)
7039 opts = pycompat.byteskwargs(opts)
7041 ui.pager(b'summary')
7040 ui.pager(b'summary')
7042 ctx = repo[None]
7041 ctx = repo[None]
7043 parents = ctx.parents()
7042 parents = ctx.parents()
7044 pnode = parents[0].node()
7043 pnode = parents[0].node()
7045 marks = []
7044 marks = []
7046
7045
7047 try:
7046 try:
7048 ms = mergestatemod.mergestate.read(repo)
7047 ms = mergestatemod.mergestate.read(repo)
7049 except error.UnsupportedMergeRecords as e:
7048 except error.UnsupportedMergeRecords as e:
7050 s = b' '.join(e.recordtypes)
7049 s = b' '.join(e.recordtypes)
7051 ui.warn(
7050 ui.warn(
7052 _(b'warning: merge state has unsupported record types: %s\n') % s
7051 _(b'warning: merge state has unsupported record types: %s\n') % s
7053 )
7052 )
7054 unresolved = []
7053 unresolved = []
7055 else:
7054 else:
7056 unresolved = list(ms.unresolved())
7055 unresolved = list(ms.unresolved())
7057
7056
7058 for p in parents:
7057 for p in parents:
7059 # label with log.changeset (instead of log.parent) since this
7058 # label with log.changeset (instead of log.parent) since this
7060 # shows a working directory parent *changeset*:
7059 # shows a working directory parent *changeset*:
7061 # i18n: column positioning for "hg summary"
7060 # i18n: column positioning for "hg summary"
7062 ui.write(
7061 ui.write(
7063 _(b'parent: %d:%s ') % (p.rev(), p),
7062 _(b'parent: %d:%s ') % (p.rev(), p),
7064 label=logcmdutil.changesetlabels(p),
7063 label=logcmdutil.changesetlabels(p),
7065 )
7064 )
7066 ui.write(b' '.join(p.tags()), label=b'log.tag')
7065 ui.write(b' '.join(p.tags()), label=b'log.tag')
7067 if p.bookmarks():
7066 if p.bookmarks():
7068 marks.extend(p.bookmarks())
7067 marks.extend(p.bookmarks())
7069 if p.rev() == -1:
7068 if p.rev() == -1:
7070 if not len(repo):
7069 if not len(repo):
7071 ui.write(_(b' (empty repository)'))
7070 ui.write(_(b' (empty repository)'))
7072 else:
7071 else:
7073 ui.write(_(b' (no revision checked out)'))
7072 ui.write(_(b' (no revision checked out)'))
7074 if p.obsolete():
7073 if p.obsolete():
7075 ui.write(_(b' (obsolete)'))
7074 ui.write(_(b' (obsolete)'))
7076 if p.isunstable():
7075 if p.isunstable():
7077 instabilities = (
7076 instabilities = (
7078 ui.label(instability, b'trouble.%s' % instability)
7077 ui.label(instability, b'trouble.%s' % instability)
7079 for instability in p.instabilities()
7078 for instability in p.instabilities()
7080 )
7079 )
7081 ui.write(b' (' + b', '.join(instabilities) + b')')
7080 ui.write(b' (' + b', '.join(instabilities) + b')')
7082 ui.write(b'\n')
7081 ui.write(b'\n')
7083 if p.description():
7082 if p.description():
7084 ui.status(
7083 ui.status(
7085 b' ' + p.description().splitlines()[0].strip() + b'\n',
7084 b' ' + p.description().splitlines()[0].strip() + b'\n',
7086 label=b'log.summary',
7085 label=b'log.summary',
7087 )
7086 )
7088
7087
7089 branch = ctx.branch()
7088 branch = ctx.branch()
7090 bheads = repo.branchheads(branch)
7089 bheads = repo.branchheads(branch)
7091 # i18n: column positioning for "hg summary"
7090 # i18n: column positioning for "hg summary"
7092 m = _(b'branch: %s\n') % branch
7091 m = _(b'branch: %s\n') % branch
7093 if branch != b'default':
7092 if branch != b'default':
7094 ui.write(m, label=b'log.branch')
7093 ui.write(m, label=b'log.branch')
7095 else:
7094 else:
7096 ui.status(m, label=b'log.branch')
7095 ui.status(m, label=b'log.branch')
7097
7096
7098 if marks:
7097 if marks:
7099 active = repo._activebookmark
7098 active = repo._activebookmark
7100 # i18n: column positioning for "hg summary"
7099 # i18n: column positioning for "hg summary"
7101 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
7100 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
7102 if active is not None:
7101 if active is not None:
7103 if active in marks:
7102 if active in marks:
7104 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
7103 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
7105 marks.remove(active)
7104 marks.remove(active)
7106 else:
7105 else:
7107 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
7106 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
7108 for m in marks:
7107 for m in marks:
7109 ui.write(b' ' + m, label=b'log.bookmark')
7108 ui.write(b' ' + m, label=b'log.bookmark')
7110 ui.write(b'\n', label=b'log.bookmark')
7109 ui.write(b'\n', label=b'log.bookmark')
7111
7110
7112 status = repo.status(unknown=True)
7111 status = repo.status(unknown=True)
7113
7112
7114 c = repo.dirstate.copies()
7113 c = repo.dirstate.copies()
7115 copied, renamed = [], []
7114 copied, renamed = [], []
7116 for d, s in c.items():
7115 for d, s in c.items():
7117 if s in status.removed:
7116 if s in status.removed:
7118 status.removed.remove(s)
7117 status.removed.remove(s)
7119 renamed.append(d)
7118 renamed.append(d)
7120 else:
7119 else:
7121 copied.append(d)
7120 copied.append(d)
7122 if d in status.added:
7121 if d in status.added:
7123 status.added.remove(d)
7122 status.added.remove(d)
7124
7123
7125 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7124 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7126
7125
7127 labels = [
7126 labels = [
7128 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7127 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7129 (ui.label(_(b'%d added'), b'status.added'), status.added),
7128 (ui.label(_(b'%d added'), b'status.added'), status.added),
7130 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7129 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7131 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7130 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7132 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7131 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7133 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7132 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7134 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7133 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7135 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7134 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7136 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7135 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7137 ]
7136 ]
7138 t = []
7137 t = []
7139 for l, s in labels:
7138 for l, s in labels:
7140 if s:
7139 if s:
7141 t.append(l % len(s))
7140 t.append(l % len(s))
7142
7141
7143 t = b', '.join(t)
7142 t = b', '.join(t)
7144 cleanworkdir = False
7143 cleanworkdir = False
7145
7144
7146 if repo.vfs.exists(b'graftstate'):
7145 if repo.vfs.exists(b'graftstate'):
7147 t += _(b' (graft in progress)')
7146 t += _(b' (graft in progress)')
7148 if repo.vfs.exists(b'updatestate'):
7147 if repo.vfs.exists(b'updatestate'):
7149 t += _(b' (interrupted update)')
7148 t += _(b' (interrupted update)')
7150 elif len(parents) > 1:
7149 elif len(parents) > 1:
7151 t += _(b' (merge)')
7150 t += _(b' (merge)')
7152 elif branch != parents[0].branch():
7151 elif branch != parents[0].branch():
7153 t += _(b' (new branch)')
7152 t += _(b' (new branch)')
7154 elif parents[0].closesbranch() and pnode in repo.branchheads(
7153 elif parents[0].closesbranch() and pnode in repo.branchheads(
7155 branch, closed=True
7154 branch, closed=True
7156 ):
7155 ):
7157 t += _(b' (head closed)')
7156 t += _(b' (head closed)')
7158 elif not (
7157 elif not (
7159 status.modified
7158 status.modified
7160 or status.added
7159 or status.added
7161 or status.removed
7160 or status.removed
7162 or renamed
7161 or renamed
7163 or copied
7162 or copied
7164 or subs
7163 or subs
7165 ):
7164 ):
7166 t += _(b' (clean)')
7165 t += _(b' (clean)')
7167 cleanworkdir = True
7166 cleanworkdir = True
7168 elif pnode not in bheads:
7167 elif pnode not in bheads:
7169 t += _(b' (new branch head)')
7168 t += _(b' (new branch head)')
7170
7169
7171 if parents:
7170 if parents:
7172 pendingphase = max(p.phase() for p in parents)
7171 pendingphase = max(p.phase() for p in parents)
7173 else:
7172 else:
7174 pendingphase = phases.public
7173 pendingphase = phases.public
7175
7174
7176 if pendingphase > phases.newcommitphase(ui):
7175 if pendingphase > phases.newcommitphase(ui):
7177 t += b' (%s)' % phases.phasenames[pendingphase]
7176 t += b' (%s)' % phases.phasenames[pendingphase]
7178
7177
7179 if cleanworkdir:
7178 if cleanworkdir:
7180 # i18n: column positioning for "hg summary"
7179 # i18n: column positioning for "hg summary"
7181 ui.status(_(b'commit: %s\n') % t.strip())
7180 ui.status(_(b'commit: %s\n') % t.strip())
7182 else:
7181 else:
7183 # i18n: column positioning for "hg summary"
7182 # i18n: column positioning for "hg summary"
7184 ui.write(_(b'commit: %s\n') % t.strip())
7183 ui.write(_(b'commit: %s\n') % t.strip())
7185
7184
7186 # all ancestors of branch heads - all ancestors of parent = new csets
7185 # all ancestors of branch heads - all ancestors of parent = new csets
7187 new = len(
7186 new = len(
7188 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7187 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7189 )
7188 )
7190
7189
7191 if new == 0:
7190 if new == 0:
7192 # i18n: column positioning for "hg summary"
7191 # i18n: column positioning for "hg summary"
7193 ui.status(_(b'update: (current)\n'))
7192 ui.status(_(b'update: (current)\n'))
7194 elif pnode not in bheads:
7193 elif pnode not in bheads:
7195 # i18n: column positioning for "hg summary"
7194 # i18n: column positioning for "hg summary"
7196 ui.write(_(b'update: %d new changesets (update)\n') % new)
7195 ui.write(_(b'update: %d new changesets (update)\n') % new)
7197 else:
7196 else:
7198 # i18n: column positioning for "hg summary"
7197 # i18n: column positioning for "hg summary"
7199 ui.write(
7198 ui.write(
7200 _(b'update: %d new changesets, %d branch heads (merge)\n')
7199 _(b'update: %d new changesets, %d branch heads (merge)\n')
7201 % (new, len(bheads))
7200 % (new, len(bheads))
7202 )
7201 )
7203
7202
7204 t = []
7203 t = []
7205 draft = len(repo.revs(b'draft()'))
7204 draft = len(repo.revs(b'draft()'))
7206 if draft:
7205 if draft:
7207 t.append(_(b'%d draft') % draft)
7206 t.append(_(b'%d draft') % draft)
7208 secret = len(repo.revs(b'secret()'))
7207 secret = len(repo.revs(b'secret()'))
7209 if secret:
7208 if secret:
7210 t.append(_(b'%d secret') % secret)
7209 t.append(_(b'%d secret') % secret)
7211
7210
7212 if draft or secret:
7211 if draft or secret:
7213 ui.status(_(b'phases: %s\n') % b', '.join(t))
7212 ui.status(_(b'phases: %s\n') % b', '.join(t))
7214
7213
7215 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7214 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7216 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7215 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7217 numtrouble = len(repo.revs(trouble + b"()"))
7216 numtrouble = len(repo.revs(trouble + b"()"))
7218 # We write all the possibilities to ease translation
7217 # We write all the possibilities to ease translation
7219 troublemsg = {
7218 troublemsg = {
7220 b"orphan": _(b"orphan: %d changesets"),
7219 b"orphan": _(b"orphan: %d changesets"),
7221 b"contentdivergent": _(b"content-divergent: %d changesets"),
7220 b"contentdivergent": _(b"content-divergent: %d changesets"),
7222 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7221 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7223 }
7222 }
7224 if numtrouble > 0:
7223 if numtrouble > 0:
7225 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7224 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7226
7225
7227 cmdutil.summaryhooks(ui, repo)
7226 cmdutil.summaryhooks(ui, repo)
7228
7227
7229 if opts.get(b'remote'):
7228 if opts.get(b'remote'):
7230 needsincoming, needsoutgoing = True, True
7229 needsincoming, needsoutgoing = True, True
7231 else:
7230 else:
7232 needsincoming, needsoutgoing = False, False
7231 needsincoming, needsoutgoing = False, False
7233 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7232 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7234 if i:
7233 if i:
7235 needsincoming = True
7234 needsincoming = True
7236 if o:
7235 if o:
7237 needsoutgoing = True
7236 needsoutgoing = True
7238 if not needsincoming and not needsoutgoing:
7237 if not needsincoming and not needsoutgoing:
7239 return
7238 return
7240
7239
7241 def getincoming():
7240 def getincoming():
7242 # XXX We should actually skip this if no default is specified, instead
7241 # XXX We should actually skip this if no default is specified, instead
7243 # of passing "default" which will resolve as "./default/" if no default
7242 # of passing "default" which will resolve as "./default/" if no default
7244 # path is defined.
7243 # path is defined.
7245 source, branches = urlutil.get_unique_pull_path(
7244 source, branches = urlutil.get_unique_pull_path(
7246 b'summary', repo, ui, b'default'
7245 b'summary', repo, ui, b'default'
7247 )
7246 )
7248 sbranch = branches[0]
7247 sbranch = branches[0]
7249 try:
7248 try:
7250 other = hg.peer(repo, {}, source)
7249 other = hg.peer(repo, {}, source)
7251 except error.RepoError:
7250 except error.RepoError:
7252 if opts.get(b'remote'):
7251 if opts.get(b'remote'):
7253 raise
7252 raise
7254 return source, sbranch, None, None, None
7253 return source, sbranch, None, None, None
7255 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7254 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7256 if revs:
7255 if revs:
7257 revs = [other.lookup(rev) for rev in revs]
7256 revs = [other.lookup(rev) for rev in revs]
7258 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(source))
7257 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(source))
7259 with repo.ui.silent():
7258 with repo.ui.silent():
7260 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7259 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7261 return source, sbranch, other, commoninc, commoninc[1]
7260 return source, sbranch, other, commoninc, commoninc[1]
7262
7261
7263 if needsincoming:
7262 if needsincoming:
7264 source, sbranch, sother, commoninc, incoming = getincoming()
7263 source, sbranch, sother, commoninc, incoming = getincoming()
7265 else:
7264 else:
7266 source = sbranch = sother = commoninc = incoming = None
7265 source = sbranch = sother = commoninc = incoming = None
7267
7266
7268 def getoutgoing():
7267 def getoutgoing():
7269 # XXX We should actually skip this if no default is specified, instead
7268 # XXX We should actually skip this if no default is specified, instead
7270 # of passing "default" which will resolve as "./default/" if no default
7269 # of passing "default" which will resolve as "./default/" if no default
7271 # path is defined.
7270 # path is defined.
7272 d = None
7271 d = None
7273 if b'default-push' in ui.paths:
7272 if b'default-push' in ui.paths:
7274 d = b'default-push'
7273 d = b'default-push'
7275 elif b'default' in ui.paths:
7274 elif b'default' in ui.paths:
7276 d = b'default'
7275 d = b'default'
7277 if d is not None:
7276 if d is not None:
7278 path = urlutil.get_unique_push_path(b'summary', repo, ui, d)
7277 path = urlutil.get_unique_push_path(b'summary', repo, ui, d)
7279 dest = path.pushloc or path.loc
7278 dest = path.pushloc or path.loc
7280 dbranch = path.branch
7279 dbranch = path.branch
7281 else:
7280 else:
7282 dest = b'default'
7281 dest = b'default'
7283 dbranch = None
7282 dbranch = None
7284 revs, checkout = hg.addbranchrevs(repo, repo, (dbranch, []), None)
7283 revs, checkout = hg.addbranchrevs(repo, repo, (dbranch, []), None)
7285 if source != dest:
7284 if source != dest:
7286 try:
7285 try:
7287 dother = hg.peer(repo, {}, dest)
7286 dother = hg.peer(repo, {}, dest)
7288 except error.RepoError:
7287 except error.RepoError:
7289 if opts.get(b'remote'):
7288 if opts.get(b'remote'):
7290 raise
7289 raise
7291 return dest, dbranch, None, None
7290 return dest, dbranch, None, None
7292 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(dest))
7291 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(dest))
7293 elif sother is None:
7292 elif sother is None:
7294 # there is no explicit destination peer, but source one is invalid
7293 # there is no explicit destination peer, but source one is invalid
7295 return dest, dbranch, None, None
7294 return dest, dbranch, None, None
7296 else:
7295 else:
7297 dother = sother
7296 dother = sother
7298 if source != dest or (sbranch is not None and sbranch != dbranch):
7297 if source != dest or (sbranch is not None and sbranch != dbranch):
7299 common = None
7298 common = None
7300 else:
7299 else:
7301 common = commoninc
7300 common = commoninc
7302 if revs:
7301 if revs:
7303 revs = [repo.lookup(rev) for rev in revs]
7302 revs = [repo.lookup(rev) for rev in revs]
7304 with repo.ui.silent():
7303 with repo.ui.silent():
7305 outgoing = discovery.findcommonoutgoing(
7304 outgoing = discovery.findcommonoutgoing(
7306 repo, dother, onlyheads=revs, commoninc=common
7305 repo, dother, onlyheads=revs, commoninc=common
7307 )
7306 )
7308 return dest, dbranch, dother, outgoing
7307 return dest, dbranch, dother, outgoing
7309
7308
7310 if needsoutgoing:
7309 if needsoutgoing:
7311 dest, dbranch, dother, outgoing = getoutgoing()
7310 dest, dbranch, dother, outgoing = getoutgoing()
7312 else:
7311 else:
7313 dest = dbranch = dother = outgoing = None
7312 dest = dbranch = dother = outgoing = None
7314
7313
7315 if opts.get(b'remote'):
7314 if opts.get(b'remote'):
7316 # Help pytype. --remote sets both `needsincoming` and `needsoutgoing`.
7315 # Help pytype. --remote sets both `needsincoming` and `needsoutgoing`.
7317 # The former always sets `sother` (or raises an exception if it can't);
7316 # The former always sets `sother` (or raises an exception if it can't);
7318 # the latter always sets `outgoing`.
7317 # the latter always sets `outgoing`.
7319 assert sother is not None
7318 assert sother is not None
7320 assert outgoing is not None
7319 assert outgoing is not None
7321
7320
7322 t = []
7321 t = []
7323 if incoming:
7322 if incoming:
7324 t.append(_(b'1 or more incoming'))
7323 t.append(_(b'1 or more incoming'))
7325 o = outgoing.missing
7324 o = outgoing.missing
7326 if o:
7325 if o:
7327 t.append(_(b'%d outgoing') % len(o))
7326 t.append(_(b'%d outgoing') % len(o))
7328 other = dother or sother
7327 other = dother or sother
7329 if b'bookmarks' in other.listkeys(b'namespaces'):
7328 if b'bookmarks' in other.listkeys(b'namespaces'):
7330 counts = bookmarks.summary(repo, other)
7329 counts = bookmarks.summary(repo, other)
7331 if counts[0] > 0:
7330 if counts[0] > 0:
7332 t.append(_(b'%d incoming bookmarks') % counts[0])
7331 t.append(_(b'%d incoming bookmarks') % counts[0])
7333 if counts[1] > 0:
7332 if counts[1] > 0:
7334 t.append(_(b'%d outgoing bookmarks') % counts[1])
7333 t.append(_(b'%d outgoing bookmarks') % counts[1])
7335
7334
7336 if t:
7335 if t:
7337 # i18n: column positioning for "hg summary"
7336 # i18n: column positioning for "hg summary"
7338 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7337 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7339 else:
7338 else:
7340 # i18n: column positioning for "hg summary"
7339 # i18n: column positioning for "hg summary"
7341 ui.status(_(b'remote: (synced)\n'))
7340 ui.status(_(b'remote: (synced)\n'))
7342
7341
7343 cmdutil.summaryremotehooks(
7342 cmdutil.summaryremotehooks(
7344 ui,
7343 ui,
7345 repo,
7344 repo,
7346 opts,
7345 opts,
7347 (
7346 (
7348 (source, sbranch, sother, commoninc),
7347 (source, sbranch, sother, commoninc),
7349 (dest, dbranch, dother, outgoing),
7348 (dest, dbranch, dother, outgoing),
7350 ),
7349 ),
7351 )
7350 )
7352
7351
7353
7352
7354 @command(
7353 @command(
7355 b'tag',
7354 b'tag',
7356 [
7355 [
7357 (b'f', b'force', None, _(b'force tag')),
7356 (b'f', b'force', None, _(b'force tag')),
7358 (b'l', b'local', None, _(b'make the tag local')),
7357 (b'l', b'local', None, _(b'make the tag local')),
7359 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7358 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7360 (b'', b'remove', None, _(b'remove a tag')),
7359 (b'', b'remove', None, _(b'remove a tag')),
7361 # -l/--local is already there, commitopts cannot be used
7360 # -l/--local is already there, commitopts cannot be used
7362 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7361 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7363 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7362 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7364 ]
7363 ]
7365 + commitopts2,
7364 + commitopts2,
7366 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7365 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7367 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7366 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7368 )
7367 )
7369 def tag(ui, repo, name1, *names, **opts):
7368 def tag(ui, repo, name1, *names, **opts):
7370 """add one or more tags for the current or given revision
7369 """add one or more tags for the current or given revision
7371
7370
7372 Name a particular revision using <name>.
7371 Name a particular revision using <name>.
7373
7372
7374 Tags are used to name particular revisions of the repository and are
7373 Tags are used to name particular revisions of the repository and are
7375 very useful to compare different revisions, to go back to significant
7374 very useful to compare different revisions, to go back to significant
7376 earlier versions or to mark branch points as releases, etc. Changing
7375 earlier versions or to mark branch points as releases, etc. Changing
7377 an existing tag is normally disallowed; use -f/--force to override.
7376 an existing tag is normally disallowed; use -f/--force to override.
7378
7377
7379 If no revision is given, the parent of the working directory is
7378 If no revision is given, the parent of the working directory is
7380 used.
7379 used.
7381
7380
7382 To facilitate version control, distribution, and merging of tags,
7381 To facilitate version control, distribution, and merging of tags,
7383 they are stored as a file named ".hgtags" which is managed similarly
7382 they are stored as a file named ".hgtags" which is managed similarly
7384 to other project files and can be hand-edited if necessary. This
7383 to other project files and can be hand-edited if necessary. This
7385 also means that tagging creates a new commit. The file
7384 also means that tagging creates a new commit. The file
7386 ".hg/localtags" is used for local tags (not shared among
7385 ".hg/localtags" is used for local tags (not shared among
7387 repositories).
7386 repositories).
7388
7387
7389 Tag commits are usually made at the head of a branch. If the parent
7388 Tag commits are usually made at the head of a branch. If the parent
7390 of the working directory is not a branch head, :hg:`tag` aborts; use
7389 of the working directory is not a branch head, :hg:`tag` aborts; use
7391 -f/--force to force the tag commit to be based on a non-head
7390 -f/--force to force the tag commit to be based on a non-head
7392 changeset.
7391 changeset.
7393
7392
7394 See :hg:`help dates` for a list of formats valid for -d/--date.
7393 See :hg:`help dates` for a list of formats valid for -d/--date.
7395
7394
7396 Since tag names have priority over branch names during revision
7395 Since tag names have priority over branch names during revision
7397 lookup, using an existing branch name as a tag name is discouraged.
7396 lookup, using an existing branch name as a tag name is discouraged.
7398
7397
7399 Returns 0 on success.
7398 Returns 0 on success.
7400 """
7399 """
7401 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7400 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7402 opts = pycompat.byteskwargs(opts)
7401 opts = pycompat.byteskwargs(opts)
7403 with repo.wlock(), repo.lock():
7402 with repo.wlock(), repo.lock():
7404 rev_ = b"."
7403 rev_ = b"."
7405 names = [t.strip() for t in (name1,) + names]
7404 names = [t.strip() for t in (name1,) + names]
7406 if len(names) != len(set(names)):
7405 if len(names) != len(set(names)):
7407 raise error.InputError(_(b'tag names must be unique'))
7406 raise error.InputError(_(b'tag names must be unique'))
7408 for n in names:
7407 for n in names:
7409 scmutil.checknewlabel(repo, n, b'tag')
7408 scmutil.checknewlabel(repo, n, b'tag')
7410 if not n:
7409 if not n:
7411 raise error.InputError(
7410 raise error.InputError(
7412 _(b'tag names cannot consist entirely of whitespace')
7411 _(b'tag names cannot consist entirely of whitespace')
7413 )
7412 )
7414 if opts.get(b'rev'):
7413 if opts.get(b'rev'):
7415 rev_ = opts[b'rev']
7414 rev_ = opts[b'rev']
7416 message = opts.get(b'message')
7415 message = opts.get(b'message')
7417 if opts.get(b'remove'):
7416 if opts.get(b'remove'):
7418 if opts.get(b'local'):
7417 if opts.get(b'local'):
7419 expectedtype = b'local'
7418 expectedtype = b'local'
7420 else:
7419 else:
7421 expectedtype = b'global'
7420 expectedtype = b'global'
7422
7421
7423 for n in names:
7422 for n in names:
7424 if repo.tagtype(n) == b'global':
7423 if repo.tagtype(n) == b'global':
7425 alltags = tagsmod.findglobaltags(ui, repo)
7424 alltags = tagsmod.findglobaltags(ui, repo)
7426 if alltags[n][0] == repo.nullid:
7425 if alltags[n][0] == repo.nullid:
7427 raise error.InputError(
7426 raise error.InputError(
7428 _(b"tag '%s' is already removed") % n
7427 _(b"tag '%s' is already removed") % n
7429 )
7428 )
7430 if not repo.tagtype(n):
7429 if not repo.tagtype(n):
7431 raise error.InputError(_(b"tag '%s' does not exist") % n)
7430 raise error.InputError(_(b"tag '%s' does not exist") % n)
7432 if repo.tagtype(n) != expectedtype:
7431 if repo.tagtype(n) != expectedtype:
7433 if expectedtype == b'global':
7432 if expectedtype == b'global':
7434 raise error.InputError(
7433 raise error.InputError(
7435 _(b"tag '%s' is not a global tag") % n
7434 _(b"tag '%s' is not a global tag") % n
7436 )
7435 )
7437 else:
7436 else:
7438 raise error.InputError(
7437 raise error.InputError(
7439 _(b"tag '%s' is not a local tag") % n
7438 _(b"tag '%s' is not a local tag") % n
7440 )
7439 )
7441 rev_ = b'null'
7440 rev_ = b'null'
7442 if not message:
7441 if not message:
7443 # we don't translate commit messages
7442 # we don't translate commit messages
7444 message = b'Removed tag %s' % b', '.join(names)
7443 message = b'Removed tag %s' % b', '.join(names)
7445 elif not opts.get(b'force'):
7444 elif not opts.get(b'force'):
7446 for n in names:
7445 for n in names:
7447 if n in repo.tags():
7446 if n in repo.tags():
7448 raise error.InputError(
7447 raise error.InputError(
7449 _(b"tag '%s' already exists (use -f to force)") % n
7448 _(b"tag '%s' already exists (use -f to force)") % n
7450 )
7449 )
7451 if not opts.get(b'local'):
7450 if not opts.get(b'local'):
7452 p1, p2 = repo.dirstate.parents()
7451 p1, p2 = repo.dirstate.parents()
7453 if p2 != repo.nullid:
7452 if p2 != repo.nullid:
7454 raise error.StateError(_(b'uncommitted merge'))
7453 raise error.StateError(_(b'uncommitted merge'))
7455 bheads = repo.branchheads()
7454 bheads = repo.branchheads()
7456 if not opts.get(b'force') and bheads and p1 not in bheads:
7455 if not opts.get(b'force') and bheads and p1 not in bheads:
7457 raise error.InputError(
7456 raise error.InputError(
7458 _(
7457 _(
7459 b'working directory is not at a branch head '
7458 b'working directory is not at a branch head '
7460 b'(use -f to force)'
7459 b'(use -f to force)'
7461 )
7460 )
7462 )
7461 )
7463 node = logcmdutil.revsingle(repo, rev_).node()
7462 node = logcmdutil.revsingle(repo, rev_).node()
7464
7463
7465 if not message:
7464 if not message:
7466 # we don't translate commit messages
7465 # we don't translate commit messages
7467 message = b'Added tag %s for changeset %s' % (
7466 message = b'Added tag %s for changeset %s' % (
7468 b', '.join(names),
7467 b', '.join(names),
7469 short(node),
7468 short(node),
7470 )
7469 )
7471
7470
7472 date = opts.get(b'date')
7471 date = opts.get(b'date')
7473 if date:
7472 if date:
7474 date = dateutil.parsedate(date)
7473 date = dateutil.parsedate(date)
7475
7474
7476 if opts.get(b'remove'):
7475 if opts.get(b'remove'):
7477 editform = b'tag.remove'
7476 editform = b'tag.remove'
7478 else:
7477 else:
7479 editform = b'tag.add'
7478 editform = b'tag.add'
7480 editor = cmdutil.getcommiteditor(
7479 editor = cmdutil.getcommiteditor(
7481 editform=editform, **pycompat.strkwargs(opts)
7480 editform=editform, **pycompat.strkwargs(opts)
7482 )
7481 )
7483
7482
7484 # don't allow tagging the null rev
7483 # don't allow tagging the null rev
7485 if (
7484 if (
7486 not opts.get(b'remove')
7485 not opts.get(b'remove')
7487 and logcmdutil.revsingle(repo, rev_).rev() == nullrev
7486 and logcmdutil.revsingle(repo, rev_).rev() == nullrev
7488 ):
7487 ):
7489 raise error.InputError(_(b"cannot tag null revision"))
7488 raise error.InputError(_(b"cannot tag null revision"))
7490
7489
7491 tagsmod.tag(
7490 tagsmod.tag(
7492 repo,
7491 repo,
7493 names,
7492 names,
7494 node,
7493 node,
7495 message,
7494 message,
7496 opts.get(b'local'),
7495 opts.get(b'local'),
7497 opts.get(b'user'),
7496 opts.get(b'user'),
7498 date,
7497 date,
7499 editor=editor,
7498 editor=editor,
7500 )
7499 )
7501
7500
7502
7501
7503 @command(
7502 @command(
7504 b'tags',
7503 b'tags',
7505 formatteropts,
7504 formatteropts,
7506 b'',
7505 b'',
7507 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7506 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7508 intents={INTENT_READONLY},
7507 intents={INTENT_READONLY},
7509 )
7508 )
7510 def tags(ui, repo, **opts):
7509 def tags(ui, repo, **opts):
7511 """list repository tags
7510 """list repository tags
7512
7511
7513 This lists both regular and local tags. When the -v/--verbose
7512 This lists both regular and local tags. When the -v/--verbose
7514 switch is used, a third column "local" is printed for local tags.
7513 switch is used, a third column "local" is printed for local tags.
7515 When the -q/--quiet switch is used, only the tag name is printed.
7514 When the -q/--quiet switch is used, only the tag name is printed.
7516
7515
7517 .. container:: verbose
7516 .. container:: verbose
7518
7517
7519 Template:
7518 Template:
7520
7519
7521 The following keywords are supported in addition to the common template
7520 The following keywords are supported in addition to the common template
7522 keywords and functions such as ``{tag}``. See also
7521 keywords and functions such as ``{tag}``. See also
7523 :hg:`help templates`.
7522 :hg:`help templates`.
7524
7523
7525 :type: String. ``local`` for local tags.
7524 :type: String. ``local`` for local tags.
7526
7525
7527 Returns 0 on success.
7526 Returns 0 on success.
7528 """
7527 """
7529
7528
7530 opts = pycompat.byteskwargs(opts)
7529 opts = pycompat.byteskwargs(opts)
7531 ui.pager(b'tags')
7530 ui.pager(b'tags')
7532 fm = ui.formatter(b'tags', opts)
7531 fm = ui.formatter(b'tags', opts)
7533 hexfunc = fm.hexfunc
7532 hexfunc = fm.hexfunc
7534
7533
7535 for t, n in reversed(repo.tagslist()):
7534 for t, n in reversed(repo.tagslist()):
7536 hn = hexfunc(n)
7535 hn = hexfunc(n)
7537 label = b'tags.normal'
7536 label = b'tags.normal'
7538 tagtype = repo.tagtype(t)
7537 tagtype = repo.tagtype(t)
7539 if not tagtype or tagtype == b'global':
7538 if not tagtype or tagtype == b'global':
7540 tagtype = b''
7539 tagtype = b''
7541 else:
7540 else:
7542 label = b'tags.' + tagtype
7541 label = b'tags.' + tagtype
7543
7542
7544 fm.startitem()
7543 fm.startitem()
7545 fm.context(repo=repo)
7544 fm.context(repo=repo)
7546 fm.write(b'tag', b'%s', t, label=label)
7545 fm.write(b'tag', b'%s', t, label=label)
7547 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7546 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7548 fm.condwrite(
7547 fm.condwrite(
7549 not ui.quiet,
7548 not ui.quiet,
7550 b'rev node',
7549 b'rev node',
7551 fmt,
7550 fmt,
7552 repo.changelog.rev(n),
7551 repo.changelog.rev(n),
7553 hn,
7552 hn,
7554 label=label,
7553 label=label,
7555 )
7554 )
7556 fm.condwrite(
7555 fm.condwrite(
7557 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7556 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7558 )
7557 )
7559 fm.plain(b'\n')
7558 fm.plain(b'\n')
7560 fm.end()
7559 fm.end()
7561
7560
7562
7561
7563 @command(
7562 @command(
7564 b'tip',
7563 b'tip',
7565 [
7564 [
7566 (b'p', b'patch', None, _(b'show patch')),
7565 (b'p', b'patch', None, _(b'show patch')),
7567 (b'g', b'git', None, _(b'use git extended diff format')),
7566 (b'g', b'git', None, _(b'use git extended diff format')),
7568 ]
7567 ]
7569 + templateopts,
7568 + templateopts,
7570 _(b'[-p] [-g]'),
7569 _(b'[-p] [-g]'),
7571 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7570 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7572 )
7571 )
7573 def tip(ui, repo, **opts):
7572 def tip(ui, repo, **opts):
7574 """show the tip revision (DEPRECATED)
7573 """show the tip revision (DEPRECATED)
7575
7574
7576 The tip revision (usually just called the tip) is the changeset
7575 The tip revision (usually just called the tip) is the changeset
7577 most recently added to the repository (and therefore the most
7576 most recently added to the repository (and therefore the most
7578 recently changed head).
7577 recently changed head).
7579
7578
7580 If you have just made a commit, that commit will be the tip. If
7579 If you have just made a commit, that commit will be the tip. If
7581 you have just pulled changes from another repository, the tip of
7580 you have just pulled changes from another repository, the tip of
7582 that repository becomes the current tip. The "tip" tag is special
7581 that repository becomes the current tip. The "tip" tag is special
7583 and cannot be renamed or assigned to a different changeset.
7582 and cannot be renamed or assigned to a different changeset.
7584
7583
7585 This command is deprecated, please use :hg:`heads` instead.
7584 This command is deprecated, please use :hg:`heads` instead.
7586
7585
7587 Returns 0 on success.
7586 Returns 0 on success.
7588 """
7587 """
7589 opts = pycompat.byteskwargs(opts)
7588 opts = pycompat.byteskwargs(opts)
7590 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7589 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7591 displayer.show(repo[b'tip'])
7590 displayer.show(repo[b'tip'])
7592 displayer.close()
7591 displayer.close()
7593
7592
7594
7593
7595 @command(
7594 @command(
7596 b'unbundle',
7595 b'unbundle',
7597 [
7596 [
7598 (
7597 (
7599 b'u',
7598 b'u',
7600 b'update',
7599 b'update',
7601 None,
7600 None,
7602 _(b'update to new branch head if changesets were unbundled'),
7601 _(b'update to new branch head if changesets were unbundled'),
7603 )
7602 )
7604 ],
7603 ],
7605 _(b'[-u] FILE...'),
7604 _(b'[-u] FILE...'),
7606 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7605 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7607 )
7606 )
7608 def unbundle(ui, repo, fname1, *fnames, **opts):
7607 def unbundle(ui, repo, fname1, *fnames, **opts):
7609 """apply one or more bundle files
7608 """apply one or more bundle files
7610
7609
7611 Apply one or more bundle files generated by :hg:`bundle`.
7610 Apply one or more bundle files generated by :hg:`bundle`.
7612
7611
7613 Returns 0 on success, 1 if an update has unresolved files.
7612 Returns 0 on success, 1 if an update has unresolved files.
7614 """
7613 """
7615 fnames = (fname1,) + fnames
7614 fnames = (fname1,) + fnames
7616
7615
7617 with repo.lock():
7616 with repo.lock():
7618 for fname in fnames:
7617 for fname in fnames:
7619 f = hg.openpath(ui, fname)
7618 f = hg.openpath(ui, fname)
7620 gen = exchange.readbundle(ui, f, fname)
7619 gen = exchange.readbundle(ui, f, fname)
7621 if isinstance(gen, streamclone.streamcloneapplier):
7620 if isinstance(gen, streamclone.streamcloneapplier):
7622 raise error.InputError(
7621 raise error.InputError(
7623 _(
7622 _(
7624 b'packed bundles cannot be applied with '
7623 b'packed bundles cannot be applied with '
7625 b'"hg unbundle"'
7624 b'"hg unbundle"'
7626 ),
7625 ),
7627 hint=_(b'use "hg debugapplystreamclonebundle"'),
7626 hint=_(b'use "hg debugapplystreamclonebundle"'),
7628 )
7627 )
7629 url = b'bundle:' + fname
7628 url = b'bundle:' + fname
7630 try:
7629 try:
7631 txnname = b'unbundle'
7630 txnname = b'unbundle'
7632 if not isinstance(gen, bundle2.unbundle20):
7631 if not isinstance(gen, bundle2.unbundle20):
7633 txnname = b'unbundle\n%s' % urlutil.hidepassword(url)
7632 txnname = b'unbundle\n%s' % urlutil.hidepassword(url)
7634 with repo.transaction(txnname) as tr:
7633 with repo.transaction(txnname) as tr:
7635 op = bundle2.applybundle(
7634 op = bundle2.applybundle(
7636 repo, gen, tr, source=b'unbundle', url=url
7635 repo, gen, tr, source=b'unbundle', url=url
7637 )
7636 )
7638 except error.BundleUnknownFeatureError as exc:
7637 except error.BundleUnknownFeatureError as exc:
7639 raise error.Abort(
7638 raise error.Abort(
7640 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7639 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7641 hint=_(
7640 hint=_(
7642 b"see https://mercurial-scm.org/"
7641 b"see https://mercurial-scm.org/"
7643 b"wiki/BundleFeature for more "
7642 b"wiki/BundleFeature for more "
7644 b"information"
7643 b"information"
7645 ),
7644 ),
7646 )
7645 )
7647 modheads = bundle2.combinechangegroupresults(op)
7646 modheads = bundle2.combinechangegroupresults(op)
7648
7647
7649 if postincoming(ui, repo, modheads, opts.get('update'), None, None):
7648 if postincoming(ui, repo, modheads, opts.get('update'), None, None):
7650 return 1
7649 return 1
7651 else:
7650 else:
7652 return 0
7651 return 0
7653
7652
7654
7653
7655 @command(
7654 @command(
7656 b'unshelve',
7655 b'unshelve',
7657 [
7656 [
7658 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7657 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7659 (
7658 (
7660 b'c',
7659 b'c',
7661 b'continue',
7660 b'continue',
7662 None,
7661 None,
7663 _(b'continue an incomplete unshelve operation'),
7662 _(b'continue an incomplete unshelve operation'),
7664 ),
7663 ),
7665 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7664 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7666 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7665 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7667 (
7666 (
7668 b'n',
7667 b'n',
7669 b'name',
7668 b'name',
7670 b'',
7669 b'',
7671 _(b'restore shelved change with given name'),
7670 _(b'restore shelved change with given name'),
7672 _(b'NAME'),
7671 _(b'NAME'),
7673 ),
7672 ),
7674 (b't', b'tool', b'', _(b'specify merge tool')),
7673 (b't', b'tool', b'', _(b'specify merge tool')),
7675 (
7674 (
7676 b'',
7675 b'',
7677 b'date',
7676 b'date',
7678 b'',
7677 b'',
7679 _(b'set date for temporary commits (DEPRECATED)'),
7678 _(b'set date for temporary commits (DEPRECATED)'),
7680 _(b'DATE'),
7679 _(b'DATE'),
7681 ),
7680 ),
7682 ],
7681 ],
7683 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7682 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7684 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7683 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7685 )
7684 )
7686 def unshelve(ui, repo, *shelved, **opts):
7685 def unshelve(ui, repo, *shelved, **opts):
7687 """restore a shelved change to the working directory
7686 """restore a shelved change to the working directory
7688
7687
7689 This command accepts an optional name of a shelved change to
7688 This command accepts an optional name of a shelved change to
7690 restore. If none is given, the most recent shelved change is used.
7689 restore. If none is given, the most recent shelved change is used.
7691
7690
7692 If a shelved change is applied successfully, the bundle that
7691 If a shelved change is applied successfully, the bundle that
7693 contains the shelved changes is moved to a backup location
7692 contains the shelved changes is moved to a backup location
7694 (.hg/shelve-backup).
7693 (.hg/shelve-backup).
7695
7694
7696 Since you can restore a shelved change on top of an arbitrary
7695 Since you can restore a shelved change on top of an arbitrary
7697 commit, it is possible that unshelving will result in a conflict
7696 commit, it is possible that unshelving will result in a conflict
7698 between your changes and the commits you are unshelving onto. If
7697 between your changes and the commits you are unshelving onto. If
7699 this occurs, you must resolve the conflict, then use
7698 this occurs, you must resolve the conflict, then use
7700 ``--continue`` to complete the unshelve operation. (The bundle
7699 ``--continue`` to complete the unshelve operation. (The bundle
7701 will not be moved until you successfully complete the unshelve.)
7700 will not be moved until you successfully complete the unshelve.)
7702
7701
7703 (Alternatively, you can use ``--abort`` to abandon an unshelve
7702 (Alternatively, you can use ``--abort`` to abandon an unshelve
7704 that causes a conflict. This reverts the unshelved changes, and
7703 that causes a conflict. This reverts the unshelved changes, and
7705 leaves the bundle in place.)
7704 leaves the bundle in place.)
7706
7705
7707 If bare shelved change (without interactive, include and exclude
7706 If bare shelved change (without interactive, include and exclude
7708 option) was done on newly created branch it would restore branch
7707 option) was done on newly created branch it would restore branch
7709 information to the working directory.
7708 information to the working directory.
7710
7709
7711 After a successful unshelve, the shelved changes are stored in a
7710 After a successful unshelve, the shelved changes are stored in a
7712 backup directory. Only the N most recent backups are kept. N
7711 backup directory. Only the N most recent backups are kept. N
7713 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7712 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7714 configuration option.
7713 configuration option.
7715
7714
7716 .. container:: verbose
7715 .. container:: verbose
7717
7716
7718 Timestamp in seconds is used to decide order of backups. More
7717 Timestamp in seconds is used to decide order of backups. More
7719 than ``maxbackups`` backups are kept, if same timestamp
7718 than ``maxbackups`` backups are kept, if same timestamp
7720 prevents from deciding exact order of them, for safety.
7719 prevents from deciding exact order of them, for safety.
7721
7720
7722 Selected changes can be unshelved with ``--interactive`` flag.
7721 Selected changes can be unshelved with ``--interactive`` flag.
7723 The working directory is updated with the selected changes, and
7722 The working directory is updated with the selected changes, and
7724 only the unselected changes remain shelved.
7723 only the unselected changes remain shelved.
7725 Note: The whole shelve is applied to working directory first before
7724 Note: The whole shelve is applied to working directory first before
7726 running interactively. So, this will bring up all the conflicts between
7725 running interactively. So, this will bring up all the conflicts between
7727 working directory and the shelve, irrespective of which changes will be
7726 working directory and the shelve, irrespective of which changes will be
7728 unshelved.
7727 unshelved.
7729 """
7728 """
7730 with repo.wlock():
7729 with repo.wlock():
7731 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7730 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7732
7731
7733
7732
7734 statemod.addunfinished(
7733 statemod.addunfinished(
7735 b'unshelve',
7734 b'unshelve',
7736 fname=b'shelvedstate',
7735 fname=b'shelvedstate',
7737 continueflag=True,
7736 continueflag=True,
7738 abortfunc=shelvemod.hgabortunshelve,
7737 abortfunc=shelvemod.hgabortunshelve,
7739 continuefunc=shelvemod.hgcontinueunshelve,
7738 continuefunc=shelvemod.hgcontinueunshelve,
7740 cmdmsg=_(b'unshelve already in progress'),
7739 cmdmsg=_(b'unshelve already in progress'),
7741 )
7740 )
7742
7741
7743
7742
7744 @command(
7743 @command(
7745 b'update|up|checkout|co',
7744 b'update|up|checkout|co',
7746 [
7745 [
7747 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7746 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7748 (b'c', b'check', None, _(b'require clean working directory')),
7747 (b'c', b'check', None, _(b'require clean working directory')),
7749 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7748 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7750 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7749 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7751 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7750 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7752 ]
7751 ]
7753 + mergetoolopts,
7752 + mergetoolopts,
7754 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7753 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7755 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7754 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7756 helpbasic=True,
7755 helpbasic=True,
7757 )
7756 )
7758 def update(ui, repo, node=None, **opts):
7757 def update(ui, repo, node=None, **opts):
7759 """update working directory (or switch revisions)
7758 """update working directory (or switch revisions)
7760
7759
7761 Update the repository's working directory to the specified
7760 Update the repository's working directory to the specified
7762 changeset. If no changeset is specified, update to the tip of the
7761 changeset. If no changeset is specified, update to the tip of the
7763 current named branch and move the active bookmark (see :hg:`help
7762 current named branch and move the active bookmark (see :hg:`help
7764 bookmarks`).
7763 bookmarks`).
7765
7764
7766 Update sets the working directory's parent revision to the specified
7765 Update sets the working directory's parent revision to the specified
7767 changeset (see :hg:`help parents`).
7766 changeset (see :hg:`help parents`).
7768
7767
7769 If the changeset is not a descendant or ancestor of the working
7768 If the changeset is not a descendant or ancestor of the working
7770 directory's parent and there are uncommitted changes, the update is
7769 directory's parent and there are uncommitted changes, the update is
7771 aborted. With the -c/--check option, the working directory is checked
7770 aborted. With the -c/--check option, the working directory is checked
7772 for uncommitted changes; if none are found, the working directory is
7771 for uncommitted changes; if none are found, the working directory is
7773 updated to the specified changeset.
7772 updated to the specified changeset.
7774
7773
7775 .. container:: verbose
7774 .. container:: verbose
7776
7775
7777 The -C/--clean, -c/--check, and -m/--merge options control what
7776 The -C/--clean, -c/--check, and -m/--merge options control what
7778 happens if the working directory contains uncommitted changes.
7777 happens if the working directory contains uncommitted changes.
7779 At most of one of them can be specified.
7778 At most of one of them can be specified.
7780
7779
7781 1. If no option is specified, and if
7780 1. If no option is specified, and if
7782 the requested changeset is an ancestor or descendant of
7781 the requested changeset is an ancestor or descendant of
7783 the working directory's parent, the uncommitted changes
7782 the working directory's parent, the uncommitted changes
7784 are merged into the requested changeset and the merged
7783 are merged into the requested changeset and the merged
7785 result is left uncommitted. If the requested changeset is
7784 result is left uncommitted. If the requested changeset is
7786 not an ancestor or descendant (that is, it is on another
7785 not an ancestor or descendant (that is, it is on another
7787 branch), the update is aborted and the uncommitted changes
7786 branch), the update is aborted and the uncommitted changes
7788 are preserved.
7787 are preserved.
7789
7788
7790 2. With the -m/--merge option, the update is allowed even if the
7789 2. With the -m/--merge option, the update is allowed even if the
7791 requested changeset is not an ancestor or descendant of
7790 requested changeset is not an ancestor or descendant of
7792 the working directory's parent.
7791 the working directory's parent.
7793
7792
7794 3. With the -c/--check option, the update is aborted and the
7793 3. With the -c/--check option, the update is aborted and the
7795 uncommitted changes are preserved.
7794 uncommitted changes are preserved.
7796
7795
7797 4. With the -C/--clean option, uncommitted changes are discarded and
7796 4. With the -C/--clean option, uncommitted changes are discarded and
7798 the working directory is updated to the requested changeset.
7797 the working directory is updated to the requested changeset.
7799
7798
7800 To cancel an uncommitted merge (and lose your changes), use
7799 To cancel an uncommitted merge (and lose your changes), use
7801 :hg:`merge --abort`.
7800 :hg:`merge --abort`.
7802
7801
7803 Use null as the changeset to remove the working directory (like
7802 Use null as the changeset to remove the working directory (like
7804 :hg:`clone -U`).
7803 :hg:`clone -U`).
7805
7804
7806 If you want to revert just one file to an older revision, use
7805 If you want to revert just one file to an older revision, use
7807 :hg:`revert [-r REV] NAME`.
7806 :hg:`revert [-r REV] NAME`.
7808
7807
7809 See :hg:`help dates` for a list of formats valid for -d/--date.
7808 See :hg:`help dates` for a list of formats valid for -d/--date.
7810
7809
7811 Returns 0 on success, 1 if there are unresolved files.
7810 Returns 0 on success, 1 if there are unresolved files.
7812 """
7811 """
7813 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7812 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7814 rev = opts.get('rev')
7813 rev = opts.get('rev')
7815 date = opts.get('date')
7814 date = opts.get('date')
7816 clean = opts.get('clean')
7815 clean = opts.get('clean')
7817 check = opts.get('check')
7816 check = opts.get('check')
7818 merge = opts.get('merge')
7817 merge = opts.get('merge')
7819 if rev and node:
7818 if rev and node:
7820 raise error.InputError(_(b"please specify just one revision"))
7819 raise error.InputError(_(b"please specify just one revision"))
7821
7820
7822 if ui.configbool(b'commands', b'update.requiredest'):
7821 if ui.configbool(b'commands', b'update.requiredest'):
7823 if not node and not rev and not date:
7822 if not node and not rev and not date:
7824 raise error.InputError(
7823 raise error.InputError(
7825 _(b'you must specify a destination'),
7824 _(b'you must specify a destination'),
7826 hint=_(b'for example: hg update ".::"'),
7825 hint=_(b'for example: hg update ".::"'),
7827 )
7826 )
7828
7827
7829 if rev is None or rev == b'':
7828 if rev is None or rev == b'':
7830 rev = node
7829 rev = node
7831
7830
7832 if date and rev is not None:
7831 if date and rev is not None:
7833 raise error.InputError(_(b"you can't specify a revision and a date"))
7832 raise error.InputError(_(b"you can't specify a revision and a date"))
7834
7833
7835 updatecheck = None
7834 updatecheck = None
7836 if check or merge is not None and not merge:
7835 if check or merge is not None and not merge:
7837 updatecheck = b'abort'
7836 updatecheck = b'abort'
7838 elif merge or check is not None and not check:
7837 elif merge or check is not None and not check:
7839 updatecheck = b'none'
7838 updatecheck = b'none'
7840
7839
7841 with repo.wlock():
7840 with repo.wlock():
7842 cmdutil.clearunfinished(repo)
7841 cmdutil.clearunfinished(repo)
7843 if date:
7842 if date:
7844 rev = cmdutil.finddate(ui, repo, date)
7843 rev = cmdutil.finddate(ui, repo, date)
7845
7844
7846 # if we defined a bookmark, we have to remember the original name
7845 # if we defined a bookmark, we have to remember the original name
7847 brev = rev
7846 brev = rev
7848 if rev:
7847 if rev:
7849 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7848 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7850 ctx = logcmdutil.revsingle(repo, rev, default=None)
7849 ctx = logcmdutil.revsingle(repo, rev, default=None)
7851 rev = ctx.rev()
7850 rev = ctx.rev()
7852 hidden = ctx.hidden()
7851 hidden = ctx.hidden()
7853 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7852 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7854 with ui.configoverride(overrides, b'update'):
7853 with ui.configoverride(overrides, b'update'):
7855 ret = hg.updatetotally(
7854 ret = hg.updatetotally(
7856 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7855 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7857 )
7856 )
7858 if hidden:
7857 if hidden:
7859 ctxstr = ctx.hex()[:12]
7858 ctxstr = ctx.hex()[:12]
7860 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7859 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7861
7860
7862 if ctx.obsolete():
7861 if ctx.obsolete():
7863 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7862 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7864 ui.warn(b"(%s)\n" % obsfatemsg)
7863 ui.warn(b"(%s)\n" % obsfatemsg)
7865 return ret
7864 return ret
7866
7865
7867
7866
7868 @command(
7867 @command(
7869 b'verify',
7868 b'verify',
7870 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7869 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7871 helpcategory=command.CATEGORY_MAINTENANCE,
7870 helpcategory=command.CATEGORY_MAINTENANCE,
7872 )
7871 )
7873 def verify(ui, repo, **opts):
7872 def verify(ui, repo, **opts):
7874 """verify the integrity of the repository
7873 """verify the integrity of the repository
7875
7874
7876 Verify the integrity of the current repository.
7875 Verify the integrity of the current repository.
7877
7876
7878 This will perform an extensive check of the repository's
7877 This will perform an extensive check of the repository's
7879 integrity, validating the hashes and checksums of each entry in
7878 integrity, validating the hashes and checksums of each entry in
7880 the changelog, manifest, and tracked files, as well as the
7879 the changelog, manifest, and tracked files, as well as the
7881 integrity of their crosslinks and indices.
7880 integrity of their crosslinks and indices.
7882
7881
7883 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7882 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7884 for more information about recovery from corruption of the
7883 for more information about recovery from corruption of the
7885 repository.
7884 repository.
7886
7885
7887 Returns 0 on success, 1 if errors are encountered.
7886 Returns 0 on success, 1 if errors are encountered.
7888 """
7887 """
7889 opts = pycompat.byteskwargs(opts)
7888 opts = pycompat.byteskwargs(opts)
7890
7889
7891 level = None
7890 level = None
7892 if opts[b'full']:
7891 if opts[b'full']:
7893 level = verifymod.VERIFY_FULL
7892 level = verifymod.VERIFY_FULL
7894 return hg.verify(repo, level)
7893 return hg.verify(repo, level)
7895
7894
7896
7895
7897 @command(
7896 @command(
7898 b'version',
7897 b'version',
7899 [] + formatteropts,
7898 [] + formatteropts,
7900 helpcategory=command.CATEGORY_HELP,
7899 helpcategory=command.CATEGORY_HELP,
7901 norepo=True,
7900 norepo=True,
7902 intents={INTENT_READONLY},
7901 intents={INTENT_READONLY},
7903 )
7902 )
7904 def version_(ui, **opts):
7903 def version_(ui, **opts):
7905 """output version and copyright information
7904 """output version and copyright information
7906
7905
7907 .. container:: verbose
7906 .. container:: verbose
7908
7907
7909 Template:
7908 Template:
7910
7909
7911 The following keywords are supported. See also :hg:`help templates`.
7910 The following keywords are supported. See also :hg:`help templates`.
7912
7911
7913 :extensions: List of extensions.
7912 :extensions: List of extensions.
7914 :ver: String. Version number.
7913 :ver: String. Version number.
7915
7914
7916 And each entry of ``{extensions}`` provides the following sub-keywords
7915 And each entry of ``{extensions}`` provides the following sub-keywords
7917 in addition to ``{ver}``.
7916 in addition to ``{ver}``.
7918
7917
7919 :bundled: Boolean. True if included in the release.
7918 :bundled: Boolean. True if included in the release.
7920 :name: String. Extension name.
7919 :name: String. Extension name.
7921 """
7920 """
7922 opts = pycompat.byteskwargs(opts)
7921 opts = pycompat.byteskwargs(opts)
7923 if ui.verbose:
7922 if ui.verbose:
7924 ui.pager(b'version')
7923 ui.pager(b'version')
7925 fm = ui.formatter(b"version", opts)
7924 fm = ui.formatter(b"version", opts)
7926 fm.startitem()
7925 fm.startitem()
7927 fm.write(
7926 fm.write(
7928 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7927 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7929 )
7928 )
7930 license = _(
7929 license = _(
7931 b"(see https://mercurial-scm.org for more information)\n"
7930 b"(see https://mercurial-scm.org for more information)\n"
7932 b"\nCopyright (C) 2005-2022 Olivia Mackall and others\n"
7931 b"\nCopyright (C) 2005-2022 Olivia Mackall and others\n"
7933 b"This is free software; see the source for copying conditions. "
7932 b"This is free software; see the source for copying conditions. "
7934 b"There is NO\nwarranty; "
7933 b"There is NO\nwarranty; "
7935 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7934 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7936 )
7935 )
7937 if not ui.quiet:
7936 if not ui.quiet:
7938 fm.plain(license)
7937 fm.plain(license)
7939
7938
7940 if ui.verbose:
7939 if ui.verbose:
7941 fm.plain(_(b"\nEnabled extensions:\n\n"))
7940 fm.plain(_(b"\nEnabled extensions:\n\n"))
7942 # format names and versions into columns
7941 # format names and versions into columns
7943 names = []
7942 names = []
7944 vers = []
7943 vers = []
7945 isinternals = []
7944 isinternals = []
7946 for name, module in sorted(extensions.extensions()):
7945 for name, module in sorted(extensions.extensions()):
7947 names.append(name)
7946 names.append(name)
7948 vers.append(extensions.moduleversion(module) or None)
7947 vers.append(extensions.moduleversion(module) or None)
7949 isinternals.append(extensions.ismoduleinternal(module))
7948 isinternals.append(extensions.ismoduleinternal(module))
7950 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7949 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7951 if names:
7950 if names:
7952 namefmt = b" %%-%ds " % max(len(n) for n in names)
7951 namefmt = b" %%-%ds " % max(len(n) for n in names)
7953 places = [_(b"external"), _(b"internal")]
7952 places = [_(b"external"), _(b"internal")]
7954 for n, v, p in zip(names, vers, isinternals):
7953 for n, v, p in zip(names, vers, isinternals):
7955 fn.startitem()
7954 fn.startitem()
7956 fn.condwrite(ui.verbose, b"name", namefmt, n)
7955 fn.condwrite(ui.verbose, b"name", namefmt, n)
7957 if ui.verbose:
7956 if ui.verbose:
7958 fn.plain(b"%s " % places[p])
7957 fn.plain(b"%s " % places[p])
7959 fn.data(bundled=p)
7958 fn.data(bundled=p)
7960 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7959 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7961 if ui.verbose:
7960 if ui.verbose:
7962 fn.plain(b"\n")
7961 fn.plain(b"\n")
7963 fn.end()
7962 fn.end()
7964 fm.end()
7963 fm.end()
7965
7964
7966
7965
7967 def loadcmdtable(ui, name, cmdtable):
7966 def loadcmdtable(ui, name, cmdtable):
7968 """Load command functions from specified cmdtable"""
7967 """Load command functions from specified cmdtable"""
7969 overrides = [cmd for cmd in cmdtable if cmd in table]
7968 overrides = [cmd for cmd in cmdtable if cmd in table]
7970 if overrides:
7969 if overrides:
7971 ui.warn(
7970 ui.warn(
7972 _(b"extension '%s' overrides commands: %s\n")
7971 _(b"extension '%s' overrides commands: %s\n")
7973 % (name, b" ".join(overrides))
7972 % (name, b" ".join(overrides))
7974 )
7973 )
7975 table.update(cmdtable)
7974 table.update(cmdtable)
General Comments 0
You need to be logged in to leave comments. Login now