##// END OF EJS Templates
bundlespec: merge the contentopts and params dictionnary...
marmoute -
r50220:bf66f7a1 default
parent child Browse files
Show More
@@ -1,443 +1,462 b''
1 # bundlecaches.py - utility to deal with pre-computed bundle for servers
1 # bundlecaches.py - utility to deal with pre-computed bundle for servers
2 #
2 #
3 # This software may be used and distributed according to the terms of the
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
4 # GNU General Public License version 2 or any later version.
5
5
6 import collections
7
6 from .i18n import _
8 from .i18n import _
7
9
8 from .thirdparty import attr
10 from .thirdparty import attr
9
11
10 from . import (
12 from . import (
11 error,
13 error,
12 requirements as requirementsmod,
14 requirements as requirementsmod,
13 sslutil,
15 sslutil,
14 util,
16 util,
15 )
17 )
16 from .utils import stringutil
18 from .utils import stringutil
17
19
18 urlreq = util.urlreq
20 urlreq = util.urlreq
19
21
20 CB_MANIFEST_FILE = b'clonebundles.manifest'
22 CB_MANIFEST_FILE = b'clonebundles.manifest'
21
23
22
24
23 @attr.s
25 @attr.s
24 class bundlespec:
26 class bundlespec:
25 compression = attr.ib()
27 compression = attr.ib()
26 wirecompression = attr.ib()
28 wirecompression = attr.ib()
27 version = attr.ib()
29 version = attr.ib()
28 wireversion = attr.ib()
30 wireversion = attr.ib()
29 params = attr.ib()
31 # parameters explicitly overwritten by the config or the specification
30 contentopts = attr.ib()
32 _explicit_params = attr.ib()
33 # default parameter for the version
34 #
35 # Keeping it separated is useful to check what was actually overwritten.
36 _default_opts = attr.ib()
37
38 @property
39 def params(self):
40 return collections.ChainMap(self._explicit_params, self._default_opts)
41
42 @property
43 def contentopts(self):
44 # kept for Backward Compatibility concerns.
45 return self.params
46
47 def set_param(self, key, value):
48 """overwrite a parameter value"""
49 self._explicit_params[key] = value
31
50
32
51
33 # Maps bundle version human names to changegroup versions.
52 # Maps bundle version human names to changegroup versions.
34 _bundlespeccgversions = {
53 _bundlespeccgversions = {
35 b'v1': b'01',
54 b'v1': b'01',
36 b'v2': b'02',
55 b'v2': b'02',
37 b'packed1': b's1',
56 b'packed1': b's1',
38 b'bundle2': b'02', # legacy
57 b'bundle2': b'02', # legacy
39 }
58 }
40
59
41 # Maps bundle version with content opts to choose which part to bundle
60 # Maps bundle version with content opts to choose which part to bundle
42 _bundlespeccontentopts = {
61 _bundlespeccontentopts = {
43 b'v1': {
62 b'v1': {
44 b'changegroup': True,
63 b'changegroup': True,
45 b'cg.version': b'01',
64 b'cg.version': b'01',
46 b'obsolescence': False,
65 b'obsolescence': False,
47 b'phases': False,
66 b'phases': False,
48 b'tagsfnodescache': False,
67 b'tagsfnodescache': False,
49 b'revbranchcache': False,
68 b'revbranchcache': False,
50 },
69 },
51 b'v2': {
70 b'v2': {
52 b'changegroup': True,
71 b'changegroup': True,
53 b'cg.version': b'02',
72 b'cg.version': b'02',
54 b'obsolescence': False,
73 b'obsolescence': False,
55 b'phases': False,
74 b'phases': False,
56 b'tagsfnodescache': True,
75 b'tagsfnodescache': True,
57 b'revbranchcache': True,
76 b'revbranchcache': True,
58 },
77 },
59 b'streamv2': {
78 b'streamv2': {
60 b'changegroup': False,
79 b'changegroup': False,
61 b'cg.version': b'02',
80 b'cg.version': b'02',
62 b'obsolescence': False,
81 b'obsolescence': False,
63 b'phases': False,
82 b'phases': False,
64 b"streamv2": True,
83 b"streamv2": True,
65 b'tagsfnodescache': False,
84 b'tagsfnodescache': False,
66 b'revbranchcache': False,
85 b'revbranchcache': False,
67 },
86 },
68 b'packed1': {
87 b'packed1': {
69 b'cg.version': b's1',
88 b'cg.version': b's1',
70 },
89 },
71 b'bundle2': { # legacy
90 b'bundle2': { # legacy
72 b'cg.version': b'02',
91 b'cg.version': b'02',
73 },
92 },
74 }
93 }
75 _bundlespeccontentopts[b'bundle2'] = _bundlespeccontentopts[b'v2']
94 _bundlespeccontentopts[b'bundle2'] = _bundlespeccontentopts[b'v2']
76
95
77 _bundlespecvariants = {b"streamv2": {}}
96 _bundlespecvariants = {b"streamv2": {}}
78
97
79 # Compression engines allowed in version 1. THIS SHOULD NEVER CHANGE.
98 # Compression engines allowed in version 1. THIS SHOULD NEVER CHANGE.
80 _bundlespecv1compengines = {b'gzip', b'bzip2', b'none'}
99 _bundlespecv1compengines = {b'gzip', b'bzip2', b'none'}
81
100
82
101
83 def parsebundlespec(repo, spec, strict=True):
102 def parsebundlespec(repo, spec, strict=True):
84 """Parse a bundle string specification into parts.
103 """Parse a bundle string specification into parts.
85
104
86 Bundle specifications denote a well-defined bundle/exchange format.
105 Bundle specifications denote a well-defined bundle/exchange format.
87 The content of a given specification should not change over time in
106 The content of a given specification should not change over time in
88 order to ensure that bundles produced by a newer version of Mercurial are
107 order to ensure that bundles produced by a newer version of Mercurial are
89 readable from an older version.
108 readable from an older version.
90
109
91 The string currently has the form:
110 The string currently has the form:
92
111
93 <compression>-<type>[;<parameter0>[;<parameter1>]]
112 <compression>-<type>[;<parameter0>[;<parameter1>]]
94
113
95 Where <compression> is one of the supported compression formats
114 Where <compression> is one of the supported compression formats
96 and <type> is (currently) a version string. A ";" can follow the type and
115 and <type> is (currently) a version string. A ";" can follow the type and
97 all text afterwards is interpreted as URI encoded, ";" delimited key=value
116 all text afterwards is interpreted as URI encoded, ";" delimited key=value
98 pairs.
117 pairs.
99
118
100 If ``strict`` is True (the default) <compression> is required. Otherwise,
119 If ``strict`` is True (the default) <compression> is required. Otherwise,
101 it is optional.
120 it is optional.
102
121
103 Returns a bundlespec object of (compression, version, parameters).
122 Returns a bundlespec object of (compression, version, parameters).
104 Compression will be ``None`` if not in strict mode and a compression isn't
123 Compression will be ``None`` if not in strict mode and a compression isn't
105 defined.
124 defined.
106
125
107 An ``InvalidBundleSpecification`` is raised when the specification is
126 An ``InvalidBundleSpecification`` is raised when the specification is
108 not syntactically well formed.
127 not syntactically well formed.
109
128
110 An ``UnsupportedBundleSpecification`` is raised when the compression or
129 An ``UnsupportedBundleSpecification`` is raised when the compression or
111 bundle type/version is not recognized.
130 bundle type/version is not recognized.
112
131
113 Note: this function will likely eventually return a more complex data
132 Note: this function will likely eventually return a more complex data
114 structure, including bundle2 part information.
133 structure, including bundle2 part information.
115 """
134 """
116
135
117 def parseparams(s):
136 def parseparams(s):
118 if b';' not in s:
137 if b';' not in s:
119 return s, {}
138 return s, {}
120
139
121 params = {}
140 params = {}
122 version, paramstr = s.split(b';', 1)
141 version, paramstr = s.split(b';', 1)
123
142
124 for p in paramstr.split(b';'):
143 for p in paramstr.split(b';'):
125 if b'=' not in p:
144 if b'=' not in p:
126 raise error.InvalidBundleSpecification(
145 raise error.InvalidBundleSpecification(
127 _(
146 _(
128 b'invalid bundle specification: '
147 b'invalid bundle specification: '
129 b'missing "=" in parameter: %s'
148 b'missing "=" in parameter: %s'
130 )
149 )
131 % p
150 % p
132 )
151 )
133
152
134 key, value = p.split(b'=', 1)
153 key, value = p.split(b'=', 1)
135 key = urlreq.unquote(key)
154 key = urlreq.unquote(key)
136 value = urlreq.unquote(value)
155 value = urlreq.unquote(value)
137 params[key] = value
156 params[key] = value
138
157
139 return version, params
158 return version, params
140
159
141 if strict and b'-' not in spec:
160 if strict and b'-' not in spec:
142 raise error.InvalidBundleSpecification(
161 raise error.InvalidBundleSpecification(
143 _(
162 _(
144 b'invalid bundle specification; '
163 b'invalid bundle specification; '
145 b'must be prefixed with compression: %s'
164 b'must be prefixed with compression: %s'
146 )
165 )
147 % spec
166 % spec
148 )
167 )
149
168
150 if b'-' in spec:
169 if b'-' in spec:
151 compression, version = spec.split(b'-', 1)
170 compression, version = spec.split(b'-', 1)
152
171
153 if compression not in util.compengines.supportedbundlenames:
172 if compression not in util.compengines.supportedbundlenames:
154 raise error.UnsupportedBundleSpecification(
173 raise error.UnsupportedBundleSpecification(
155 _(b'%s compression is not supported') % compression
174 _(b'%s compression is not supported') % compression
156 )
175 )
157
176
158 version, params = parseparams(version)
177 version, params = parseparams(version)
159
178
160 if version not in _bundlespeccontentopts:
179 if version not in _bundlespeccontentopts:
161 raise error.UnsupportedBundleSpecification(
180 raise error.UnsupportedBundleSpecification(
162 _(b'%s is not a recognized bundle version') % version
181 _(b'%s is not a recognized bundle version') % version
163 )
182 )
164 else:
183 else:
165 # Value could be just the compression or just the version, in which
184 # Value could be just the compression or just the version, in which
166 # case some defaults are assumed (but only when not in strict mode).
185 # case some defaults are assumed (but only when not in strict mode).
167 assert not strict
186 assert not strict
168
187
169 spec, params = parseparams(spec)
188 spec, params = parseparams(spec)
170
189
171 if spec in util.compengines.supportedbundlenames:
190 if spec in util.compengines.supportedbundlenames:
172 compression = spec
191 compression = spec
173 version = b'v1'
192 version = b'v1'
174 # Generaldelta repos require v2.
193 # Generaldelta repos require v2.
175 if requirementsmod.GENERALDELTA_REQUIREMENT in repo.requirements:
194 if requirementsmod.GENERALDELTA_REQUIREMENT in repo.requirements:
176 version = b'v2'
195 version = b'v2'
177 elif requirementsmod.REVLOGV2_REQUIREMENT in repo.requirements:
196 elif requirementsmod.REVLOGV2_REQUIREMENT in repo.requirements:
178 version = b'v2'
197 version = b'v2'
179 # Modern compression engines require v2.
198 # Modern compression engines require v2.
180 if compression not in _bundlespecv1compengines:
199 if compression not in _bundlespecv1compengines:
181 version = b'v2'
200 version = b'v2'
182 elif spec in _bundlespeccontentopts:
201 elif spec in _bundlespeccontentopts:
183 if spec == b'packed1':
202 if spec == b'packed1':
184 compression = b'none'
203 compression = b'none'
185 else:
204 else:
186 compression = b'bzip2'
205 compression = b'bzip2'
187 version = spec
206 version = spec
188 else:
207 else:
189 raise error.UnsupportedBundleSpecification(
208 raise error.UnsupportedBundleSpecification(
190 _(b'%s is not a recognized bundle specification') % spec
209 _(b'%s is not a recognized bundle specification') % spec
191 )
210 )
192
211
193 # Bundle version 1 only supports a known set of compression engines.
212 # Bundle version 1 only supports a known set of compression engines.
194 if version == b'v1' and compression not in _bundlespecv1compengines:
213 if version == b'v1' and compression not in _bundlespecv1compengines:
195 raise error.UnsupportedBundleSpecification(
214 raise error.UnsupportedBundleSpecification(
196 _(b'compression engine %s is not supported on v1 bundles')
215 _(b'compression engine %s is not supported on v1 bundles')
197 % compression
216 % compression
198 )
217 )
199
218
200 # The specification for packed1 can optionally declare the data formats
219 # The specification for packed1 can optionally declare the data formats
201 # required to apply it. If we see this metadata, compare against what the
220 # required to apply it. If we see this metadata, compare against what the
202 # repo supports and error if the bundle isn't compatible.
221 # repo supports and error if the bundle isn't compatible.
203 if version == b'packed1' and b'requirements' in params:
222 if version == b'packed1' and b'requirements' in params:
204 requirements = set(params[b'requirements'].split(b','))
223 requirements = set(params[b'requirements'].split(b','))
205 missingreqs = requirements - requirementsmod.STREAM_FIXED_REQUIREMENTS
224 missingreqs = requirements - requirementsmod.STREAM_FIXED_REQUIREMENTS
206 if missingreqs:
225 if missingreqs:
207 raise error.UnsupportedBundleSpecification(
226 raise error.UnsupportedBundleSpecification(
208 _(b'missing support for repository features: %s')
227 _(b'missing support for repository features: %s')
209 % b', '.join(sorted(missingreqs))
228 % b', '.join(sorted(missingreqs))
210 )
229 )
211
230
212 # Compute contentopts based on the version
231 # Compute contentopts based on the version
213 if b"stream" in params and params[b"stream"] == b"v2":
232 if b"stream" in params and params[b"stream"] == b"v2":
214 # That case is fishy as this mostly derails the version selection
233 # That case is fishy as this mostly derails the version selection
215 # mechanism. `stream` bundles are quite specific and used differently
234 # mechanism. `stream` bundles are quite specific and used differently
216 # as "normal" bundles.
235 # as "normal" bundles.
217 #
236 #
218 # So we are pinning this to "v2", as this will likely be
237 # So we are pinning this to "v2", as this will likely be
219 # compatible forever. (see the next conditional).
238 # compatible forever. (see the next conditional).
220 #
239 #
221 # (we should probably define a cleaner way to do this and raise a
240 # (we should probably define a cleaner way to do this and raise a
222 # warning when the old way is encounter)
241 # warning when the old way is encounter)
223 version = b"streamv2"
242 version = b"streamv2"
224 contentopts = _bundlespeccontentopts.get(version, {}).copy()
243 contentopts = _bundlespeccontentopts.get(version, {}).copy()
225 if version == b"streamv2":
244 if version == b"streamv2":
226 # streamv2 have been reported as "v2" for a while.
245 # streamv2 have been reported as "v2" for a while.
227 version = b"v2"
246 version = b"v2"
228
247
229 engine = util.compengines.forbundlename(compression)
248 engine = util.compengines.forbundlename(compression)
230 compression, wirecompression = engine.bundletype()
249 compression, wirecompression = engine.bundletype()
231 wireversion = _bundlespeccontentopts[version][b'cg.version']
250 wireversion = _bundlespeccontentopts[version][b'cg.version']
232
251
233 return bundlespec(
252 return bundlespec(
234 compression, wirecompression, version, wireversion, params, contentopts
253 compression, wirecompression, version, wireversion, params, contentopts
235 )
254 )
236
255
237
256
238 def parseclonebundlesmanifest(repo, s):
257 def parseclonebundlesmanifest(repo, s):
239 """Parses the raw text of a clone bundles manifest.
258 """Parses the raw text of a clone bundles manifest.
240
259
241 Returns a list of dicts. The dicts have a ``URL`` key corresponding
260 Returns a list of dicts. The dicts have a ``URL`` key corresponding
242 to the URL and other keys are the attributes for the entry.
261 to the URL and other keys are the attributes for the entry.
243 """
262 """
244 m = []
263 m = []
245 for line in s.splitlines():
264 for line in s.splitlines():
246 fields = line.split()
265 fields = line.split()
247 if not fields:
266 if not fields:
248 continue
267 continue
249 attrs = {b'URL': fields[0]}
268 attrs = {b'URL': fields[0]}
250 for rawattr in fields[1:]:
269 for rawattr in fields[1:]:
251 key, value = rawattr.split(b'=', 1)
270 key, value = rawattr.split(b'=', 1)
252 key = util.urlreq.unquote(key)
271 key = util.urlreq.unquote(key)
253 value = util.urlreq.unquote(value)
272 value = util.urlreq.unquote(value)
254 attrs[key] = value
273 attrs[key] = value
255
274
256 # Parse BUNDLESPEC into components. This makes client-side
275 # Parse BUNDLESPEC into components. This makes client-side
257 # preferences easier to specify since you can prefer a single
276 # preferences easier to specify since you can prefer a single
258 # component of the BUNDLESPEC.
277 # component of the BUNDLESPEC.
259 if key == b'BUNDLESPEC':
278 if key == b'BUNDLESPEC':
260 try:
279 try:
261 bundlespec = parsebundlespec(repo, value)
280 bundlespec = parsebundlespec(repo, value)
262 attrs[b'COMPRESSION'] = bundlespec.compression
281 attrs[b'COMPRESSION'] = bundlespec.compression
263 attrs[b'VERSION'] = bundlespec.version
282 attrs[b'VERSION'] = bundlespec.version
264 except error.InvalidBundleSpecification:
283 except error.InvalidBundleSpecification:
265 pass
284 pass
266 except error.UnsupportedBundleSpecification:
285 except error.UnsupportedBundleSpecification:
267 pass
286 pass
268
287
269 m.append(attrs)
288 m.append(attrs)
270
289
271 return m
290 return m
272
291
273
292
274 def isstreamclonespec(bundlespec):
293 def isstreamclonespec(bundlespec):
275 # Stream clone v1
294 # Stream clone v1
276 if bundlespec.wirecompression == b'UN' and bundlespec.wireversion == b's1':
295 if bundlespec.wirecompression == b'UN' and bundlespec.wireversion == b's1':
277 return True
296 return True
278
297
279 # Stream clone v2
298 # Stream clone v2
280 if (
299 if (
281 bundlespec.wirecompression == b'UN'
300 bundlespec.wirecompression == b'UN'
282 and bundlespec.wireversion == b'02'
301 and bundlespec.wireversion == b'02'
283 and bundlespec.contentopts.get(b'streamv2')
302 and bundlespec.contentopts.get(b'streamv2')
284 ):
303 ):
285 return True
304 return True
286
305
287 return False
306 return False
288
307
289
308
290 def filterclonebundleentries(repo, entries, streamclonerequested=False):
309 def filterclonebundleentries(repo, entries, streamclonerequested=False):
291 """Remove incompatible clone bundle manifest entries.
310 """Remove incompatible clone bundle manifest entries.
292
311
293 Accepts a list of entries parsed with ``parseclonebundlesmanifest``
312 Accepts a list of entries parsed with ``parseclonebundlesmanifest``
294 and returns a new list consisting of only the entries that this client
313 and returns a new list consisting of only the entries that this client
295 should be able to apply.
314 should be able to apply.
296
315
297 There is no guarantee we'll be able to apply all returned entries because
316 There is no guarantee we'll be able to apply all returned entries because
298 the metadata we use to filter on may be missing or wrong.
317 the metadata we use to filter on may be missing or wrong.
299 """
318 """
300 newentries = []
319 newentries = []
301 for entry in entries:
320 for entry in entries:
302 spec = entry.get(b'BUNDLESPEC')
321 spec = entry.get(b'BUNDLESPEC')
303 if spec:
322 if spec:
304 try:
323 try:
305 bundlespec = parsebundlespec(repo, spec, strict=True)
324 bundlespec = parsebundlespec(repo, spec, strict=True)
306
325
307 # If a stream clone was requested, filter out non-streamclone
326 # If a stream clone was requested, filter out non-streamclone
308 # entries.
327 # entries.
309 if streamclonerequested and not isstreamclonespec(bundlespec):
328 if streamclonerequested and not isstreamclonespec(bundlespec):
310 repo.ui.debug(
329 repo.ui.debug(
311 b'filtering %s because not a stream clone\n'
330 b'filtering %s because not a stream clone\n'
312 % entry[b'URL']
331 % entry[b'URL']
313 )
332 )
314 continue
333 continue
315
334
316 except error.InvalidBundleSpecification as e:
335 except error.InvalidBundleSpecification as e:
317 repo.ui.debug(stringutil.forcebytestr(e) + b'\n')
336 repo.ui.debug(stringutil.forcebytestr(e) + b'\n')
318 continue
337 continue
319 except error.UnsupportedBundleSpecification as e:
338 except error.UnsupportedBundleSpecification as e:
320 repo.ui.debug(
339 repo.ui.debug(
321 b'filtering %s because unsupported bundle '
340 b'filtering %s because unsupported bundle '
322 b'spec: %s\n' % (entry[b'URL'], stringutil.forcebytestr(e))
341 b'spec: %s\n' % (entry[b'URL'], stringutil.forcebytestr(e))
323 )
342 )
324 continue
343 continue
325 # If we don't have a spec and requested a stream clone, we don't know
344 # If we don't have a spec and requested a stream clone, we don't know
326 # what the entry is so don't attempt to apply it.
345 # what the entry is so don't attempt to apply it.
327 elif streamclonerequested:
346 elif streamclonerequested:
328 repo.ui.debug(
347 repo.ui.debug(
329 b'filtering %s because cannot determine if a stream '
348 b'filtering %s because cannot determine if a stream '
330 b'clone bundle\n' % entry[b'URL']
349 b'clone bundle\n' % entry[b'URL']
331 )
350 )
332 continue
351 continue
333
352
334 if b'REQUIRESNI' in entry and not sslutil.hassni:
353 if b'REQUIRESNI' in entry and not sslutil.hassni:
335 repo.ui.debug(
354 repo.ui.debug(
336 b'filtering %s because SNI not supported\n' % entry[b'URL']
355 b'filtering %s because SNI not supported\n' % entry[b'URL']
337 )
356 )
338 continue
357 continue
339
358
340 if b'REQUIREDRAM' in entry:
359 if b'REQUIREDRAM' in entry:
341 try:
360 try:
342 requiredram = util.sizetoint(entry[b'REQUIREDRAM'])
361 requiredram = util.sizetoint(entry[b'REQUIREDRAM'])
343 except error.ParseError:
362 except error.ParseError:
344 repo.ui.debug(
363 repo.ui.debug(
345 b'filtering %s due to a bad REQUIREDRAM attribute\n'
364 b'filtering %s due to a bad REQUIREDRAM attribute\n'
346 % entry[b'URL']
365 % entry[b'URL']
347 )
366 )
348 continue
367 continue
349 actualram = repo.ui.estimatememory()
368 actualram = repo.ui.estimatememory()
350 if actualram is not None and actualram * 0.66 < requiredram:
369 if actualram is not None and actualram * 0.66 < requiredram:
351 repo.ui.debug(
370 repo.ui.debug(
352 b'filtering %s as it needs more than 2/3 of system memory\n'
371 b'filtering %s as it needs more than 2/3 of system memory\n'
353 % entry[b'URL']
372 % entry[b'URL']
354 )
373 )
355 continue
374 continue
356
375
357 newentries.append(entry)
376 newentries.append(entry)
358
377
359 return newentries
378 return newentries
360
379
361
380
362 class clonebundleentry:
381 class clonebundleentry:
363 """Represents an item in a clone bundles manifest.
382 """Represents an item in a clone bundles manifest.
364
383
365 This rich class is needed to support sorting since sorted() in Python 3
384 This rich class is needed to support sorting since sorted() in Python 3
366 doesn't support ``cmp`` and our comparison is complex enough that ``key=``
385 doesn't support ``cmp`` and our comparison is complex enough that ``key=``
367 won't work.
386 won't work.
368 """
387 """
369
388
370 def __init__(self, value, prefers):
389 def __init__(self, value, prefers):
371 self.value = value
390 self.value = value
372 self.prefers = prefers
391 self.prefers = prefers
373
392
374 def _cmp(self, other):
393 def _cmp(self, other):
375 for prefkey, prefvalue in self.prefers:
394 for prefkey, prefvalue in self.prefers:
376 avalue = self.value.get(prefkey)
395 avalue = self.value.get(prefkey)
377 bvalue = other.value.get(prefkey)
396 bvalue = other.value.get(prefkey)
378
397
379 # Special case for b missing attribute and a matches exactly.
398 # Special case for b missing attribute and a matches exactly.
380 if avalue is not None and bvalue is None and avalue == prefvalue:
399 if avalue is not None and bvalue is None and avalue == prefvalue:
381 return -1
400 return -1
382
401
383 # Special case for a missing attribute and b matches exactly.
402 # Special case for a missing attribute and b matches exactly.
384 if bvalue is not None and avalue is None and bvalue == prefvalue:
403 if bvalue is not None and avalue is None and bvalue == prefvalue:
385 return 1
404 return 1
386
405
387 # We can't compare unless attribute present on both.
406 # We can't compare unless attribute present on both.
388 if avalue is None or bvalue is None:
407 if avalue is None or bvalue is None:
389 continue
408 continue
390
409
391 # Same values should fall back to next attribute.
410 # Same values should fall back to next attribute.
392 if avalue == bvalue:
411 if avalue == bvalue:
393 continue
412 continue
394
413
395 # Exact matches come first.
414 # Exact matches come first.
396 if avalue == prefvalue:
415 if avalue == prefvalue:
397 return -1
416 return -1
398 if bvalue == prefvalue:
417 if bvalue == prefvalue:
399 return 1
418 return 1
400
419
401 # Fall back to next attribute.
420 # Fall back to next attribute.
402 continue
421 continue
403
422
404 # If we got here we couldn't sort by attributes and prefers. Fall
423 # If we got here we couldn't sort by attributes and prefers. Fall
405 # back to index order.
424 # back to index order.
406 return 0
425 return 0
407
426
408 def __lt__(self, other):
427 def __lt__(self, other):
409 return self._cmp(other) < 0
428 return self._cmp(other) < 0
410
429
411 def __gt__(self, other):
430 def __gt__(self, other):
412 return self._cmp(other) > 0
431 return self._cmp(other) > 0
413
432
414 def __eq__(self, other):
433 def __eq__(self, other):
415 return self._cmp(other) == 0
434 return self._cmp(other) == 0
416
435
417 def __le__(self, other):
436 def __le__(self, other):
418 return self._cmp(other) <= 0
437 return self._cmp(other) <= 0
419
438
420 def __ge__(self, other):
439 def __ge__(self, other):
421 return self._cmp(other) >= 0
440 return self._cmp(other) >= 0
422
441
423 def __ne__(self, other):
442 def __ne__(self, other):
424 return self._cmp(other) != 0
443 return self._cmp(other) != 0
425
444
426
445
427 def sortclonebundleentries(ui, entries):
446 def sortclonebundleentries(ui, entries):
428 prefers = ui.configlist(b'ui', b'clonebundleprefers')
447 prefers = ui.configlist(b'ui', b'clonebundleprefers')
429 if not prefers:
448 if not prefers:
430 return list(entries)
449 return list(entries)
431
450
432 def _split(p):
451 def _split(p):
433 if b'=' not in p:
452 if b'=' not in p:
434 hint = _(b"each comma separated item should be key=value pairs")
453 hint = _(b"each comma separated item should be key=value pairs")
435 raise error.Abort(
454 raise error.Abort(
436 _(b"invalid ui.clonebundleprefers item: %s") % p, hint=hint
455 _(b"invalid ui.clonebundleprefers item: %s") % p, hint=hint
437 )
456 )
438 return p.split(b'=', 1)
457 return p.split(b'=', 1)
439
458
440 prefers = [_split(p) for p in prefers]
459 prefers = [_split(p) for p in prefers]
441
460
442 items = sorted(clonebundleentry(v, prefers) for v in entries)
461 items = sorted(clonebundleentry(v, prefers) for v in entries)
443 return [i.value for i in items]
462 return [i.value for i in items]
@@ -1,7948 +1,7946 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 (
1487 (
1488 b'f',
1488 b'f',
1489 b'force',
1489 b'force',
1490 None,
1490 None,
1491 _(b'run even when the destination is unrelated'),
1491 _(b'run even when the destination is unrelated'),
1492 ),
1492 ),
1493 (
1493 (
1494 b'r',
1494 b'r',
1495 b'rev',
1495 b'rev',
1496 [],
1496 [],
1497 _(b'a changeset intended to be added to the destination'),
1497 _(b'a changeset intended to be added to the destination'),
1498 _(b'REV'),
1498 _(b'REV'),
1499 ),
1499 ),
1500 (
1500 (
1501 b'b',
1501 b'b',
1502 b'branch',
1502 b'branch',
1503 [],
1503 [],
1504 _(b'a specific branch you would like to bundle'),
1504 _(b'a specific branch you would like to bundle'),
1505 _(b'BRANCH'),
1505 _(b'BRANCH'),
1506 ),
1506 ),
1507 (
1507 (
1508 b'',
1508 b'',
1509 b'base',
1509 b'base',
1510 [],
1510 [],
1511 _(b'a base changeset assumed to be available at the destination'),
1511 _(b'a base changeset assumed to be available at the destination'),
1512 _(b'REV'),
1512 _(b'REV'),
1513 ),
1513 ),
1514 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1514 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1515 (
1515 (
1516 b't',
1516 b't',
1517 b'type',
1517 b'type',
1518 b'bzip2',
1518 b'bzip2',
1519 _(b'bundle compression type to use'),
1519 _(b'bundle compression type to use'),
1520 _(b'TYPE'),
1520 _(b'TYPE'),
1521 ),
1521 ),
1522 ]
1522 ]
1523 + remoteopts,
1523 + remoteopts,
1524 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]...'),
1524 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]...'),
1525 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1525 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1526 )
1526 )
1527 def bundle(ui, repo, fname, *dests, **opts):
1527 def bundle(ui, repo, fname, *dests, **opts):
1528 """create a bundle file
1528 """create a bundle file
1529
1529
1530 Generate a bundle file containing data to be transferred to another
1530 Generate a bundle file containing data to be transferred to another
1531 repository.
1531 repository.
1532
1532
1533 To create a bundle containing all changesets, use -a/--all
1533 To create a bundle containing all changesets, use -a/--all
1534 (or --base null). Otherwise, hg assumes the destination will have
1534 (or --base null). Otherwise, hg assumes the destination will have
1535 all the nodes you specify with --base parameters. Otherwise, hg
1535 all the nodes you specify with --base parameters. Otherwise, hg
1536 will assume the repository has all the nodes in destination, or
1536 will assume the repository has all the nodes in destination, or
1537 default-push/default if no destination is specified, where destination
1537 default-push/default if no destination is specified, where destination
1538 is the repositories you provide through DEST option.
1538 is the repositories you provide through DEST option.
1539
1539
1540 You can change bundle format with the -t/--type option. See
1540 You can change bundle format with the -t/--type option. See
1541 :hg:`help bundlespec` for documentation on this format. By default,
1541 :hg:`help bundlespec` for documentation on this format. By default,
1542 the most appropriate format is used and compression defaults to
1542 the most appropriate format is used and compression defaults to
1543 bzip2.
1543 bzip2.
1544
1544
1545 The bundle file can then be transferred using conventional means
1545 The bundle file can then be transferred using conventional means
1546 and applied to another repository with the unbundle or pull
1546 and applied to another repository with the unbundle or pull
1547 command. This is useful when direct push and pull are not
1547 command. This is useful when direct push and pull are not
1548 available or when exporting an entire repository is undesirable.
1548 available or when exporting an entire repository is undesirable.
1549
1549
1550 Applying bundles preserves all changeset contents including
1550 Applying bundles preserves all changeset contents including
1551 permissions, copy/rename information, and revision history.
1551 permissions, copy/rename information, and revision history.
1552
1552
1553 Returns 0 on success, 1 if no changes found.
1553 Returns 0 on success, 1 if no changes found.
1554 """
1554 """
1555 opts = pycompat.byteskwargs(opts)
1555 opts = pycompat.byteskwargs(opts)
1556 revs = None
1556 revs = None
1557 if b'rev' in opts:
1557 if b'rev' in opts:
1558 revstrings = opts[b'rev']
1558 revstrings = opts[b'rev']
1559 revs = logcmdutil.revrange(repo, revstrings)
1559 revs = logcmdutil.revrange(repo, revstrings)
1560 if revstrings and not revs:
1560 if revstrings and not revs:
1561 raise error.InputError(_(b'no commits to bundle'))
1561 raise error.InputError(_(b'no commits to bundle'))
1562
1562
1563 bundletype = opts.get(b'type', b'bzip2').lower()
1563 bundletype = opts.get(b'type', b'bzip2').lower()
1564 try:
1564 try:
1565 bundlespec = bundlecaches.parsebundlespec(
1565 bundlespec = bundlecaches.parsebundlespec(
1566 repo, bundletype, strict=False
1566 repo, bundletype, strict=False
1567 )
1567 )
1568 except error.UnsupportedBundleSpecification as e:
1568 except error.UnsupportedBundleSpecification as e:
1569 raise error.InputError(
1569 raise error.InputError(
1570 pycompat.bytestr(e),
1570 pycompat.bytestr(e),
1571 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1571 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1572 )
1572 )
1573 cgversion = bundlespec.contentopts[b"cg.version"]
1573 cgversion = bundlespec.params[b"cg.version"]
1574
1574
1575 # Packed bundles are a pseudo bundle format for now.
1575 # Packed bundles are a pseudo bundle format for now.
1576 if cgversion == b's1':
1576 if cgversion == b's1':
1577 raise error.InputError(
1577 raise error.InputError(
1578 _(b'packed bundles cannot be produced by "hg bundle"'),
1578 _(b'packed bundles cannot be produced by "hg bundle"'),
1579 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1579 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1580 )
1580 )
1581
1581
1582 if opts.get(b'all'):
1582 if opts.get(b'all'):
1583 if dests:
1583 if dests:
1584 raise error.InputError(
1584 raise error.InputError(
1585 _(b"--all is incompatible with specifying destinations")
1585 _(b"--all is incompatible with specifying destinations")
1586 )
1586 )
1587 if opts.get(b'base'):
1587 if opts.get(b'base'):
1588 ui.warn(_(b"ignoring --base because --all was specified\n"))
1588 ui.warn(_(b"ignoring --base because --all was specified\n"))
1589 base = [nullrev]
1589 base = [nullrev]
1590 else:
1590 else:
1591 base = logcmdutil.revrange(repo, opts.get(b'base'))
1591 base = logcmdutil.revrange(repo, opts.get(b'base'))
1592 if cgversion not in changegroup.supportedoutgoingversions(repo):
1592 if cgversion not in changegroup.supportedoutgoingversions(repo):
1593 raise error.Abort(
1593 raise error.Abort(
1594 _(b"repository does not support bundle version %s") % cgversion
1594 _(b"repository does not support bundle version %s") % cgversion
1595 )
1595 )
1596
1596
1597 if base:
1597 if base:
1598 if dests:
1598 if dests:
1599 raise error.InputError(
1599 raise error.InputError(
1600 _(b"--base is incompatible with specifying destinations")
1600 _(b"--base is incompatible with specifying destinations")
1601 )
1601 )
1602 cl = repo.changelog
1602 cl = repo.changelog
1603 common = [cl.node(rev) for rev in base]
1603 common = [cl.node(rev) for rev in base]
1604 heads = [cl.node(r) for r in revs] if revs else None
1604 heads = [cl.node(r) for r in revs] if revs else None
1605 outgoing = discovery.outgoing(repo, common, heads)
1605 outgoing = discovery.outgoing(repo, common, heads)
1606 missing = outgoing.missing
1606 missing = outgoing.missing
1607 excluded = outgoing.excluded
1607 excluded = outgoing.excluded
1608 else:
1608 else:
1609 missing = set()
1609 missing = set()
1610 excluded = set()
1610 excluded = set()
1611 for path in urlutil.get_push_paths(repo, ui, dests):
1611 for path in urlutil.get_push_paths(repo, ui, dests):
1612 other = hg.peer(repo, opts, path.rawloc)
1612 other = hg.peer(repo, opts, path.rawloc)
1613 if revs is not None:
1613 if revs is not None:
1614 hex_revs = [repo[r].hex() for r in revs]
1614 hex_revs = [repo[r].hex() for r in revs]
1615 else:
1615 else:
1616 hex_revs = None
1616 hex_revs = None
1617 branches = (path.branch, [])
1617 branches = (path.branch, [])
1618 head_revs, checkout = hg.addbranchrevs(
1618 head_revs, checkout = hg.addbranchrevs(
1619 repo, repo, branches, hex_revs
1619 repo, repo, branches, hex_revs
1620 )
1620 )
1621 heads = (
1621 heads = (
1622 head_revs
1622 head_revs
1623 and pycompat.maplist(repo.lookup, head_revs)
1623 and pycompat.maplist(repo.lookup, head_revs)
1624 or head_revs
1624 or head_revs
1625 )
1625 )
1626 outgoing = discovery.findcommonoutgoing(
1626 outgoing = discovery.findcommonoutgoing(
1627 repo,
1627 repo,
1628 other,
1628 other,
1629 onlyheads=heads,
1629 onlyheads=heads,
1630 force=opts.get(b'force'),
1630 force=opts.get(b'force'),
1631 portable=True,
1631 portable=True,
1632 )
1632 )
1633 missing.update(outgoing.missing)
1633 missing.update(outgoing.missing)
1634 excluded.update(outgoing.excluded)
1634 excluded.update(outgoing.excluded)
1635
1635
1636 if not missing:
1636 if not missing:
1637 scmutil.nochangesfound(ui, repo, not base and excluded)
1637 scmutil.nochangesfound(ui, repo, not base and excluded)
1638 return 1
1638 return 1
1639
1639
1640 if heads:
1640 if heads:
1641 outgoing = discovery.outgoing(
1641 outgoing = discovery.outgoing(
1642 repo, missingroots=missing, ancestorsof=heads
1642 repo, missingroots=missing, ancestorsof=heads
1643 )
1643 )
1644 else:
1644 else:
1645 outgoing = discovery.outgoing(repo, missingroots=missing)
1645 outgoing = discovery.outgoing(repo, missingroots=missing)
1646 outgoing.excluded = sorted(excluded)
1646 outgoing.excluded = sorted(excluded)
1647
1647
1648 if cgversion == b'01': # bundle1
1648 if cgversion == b'01': # bundle1
1649 bversion = b'HG10' + bundlespec.wirecompression
1649 bversion = b'HG10' + bundlespec.wirecompression
1650 bcompression = None
1650 bcompression = None
1651 elif cgversion in (b'02', b'03'):
1651 elif cgversion in (b'02', b'03'):
1652 bversion = b'HG20'
1652 bversion = b'HG20'
1653 bcompression = bundlespec.wirecompression
1653 bcompression = bundlespec.wirecompression
1654 else:
1654 else:
1655 raise error.ProgrammingError(
1655 raise error.ProgrammingError(
1656 b'bundle: unexpected changegroup version %s' % cgversion
1656 b'bundle: unexpected changegroup version %s' % cgversion
1657 )
1657 )
1658
1658
1659 # TODO compression options should be derived from bundlespec parsing.
1659 # TODO compression options should be derived from bundlespec parsing.
1660 # This is a temporary hack to allow adjusting bundle compression
1660 # This is a temporary hack to allow adjusting bundle compression
1661 # level without a) formalizing the bundlespec changes to declare it
1661 # level without a) formalizing the bundlespec changes to declare it
1662 # b) introducing a command flag.
1662 # b) introducing a command flag.
1663 compopts = {}
1663 compopts = {}
1664 complevel = ui.configint(
1664 complevel = ui.configint(
1665 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1665 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1666 )
1666 )
1667 if complevel is None:
1667 if complevel is None:
1668 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1668 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1669 if complevel is not None:
1669 if complevel is not None:
1670 compopts[b'level'] = complevel
1670 compopts[b'level'] = complevel
1671
1671
1672 compthreads = ui.configint(
1672 compthreads = ui.configint(
1673 b'experimental', b'bundlecompthreads.' + bundlespec.compression
1673 b'experimental', b'bundlecompthreads.' + bundlespec.compression
1674 )
1674 )
1675 if compthreads is None:
1675 if compthreads is None:
1676 compthreads = ui.configint(b'experimental', b'bundlecompthreads')
1676 compthreads = ui.configint(b'experimental', b'bundlecompthreads')
1677 if compthreads is not None:
1677 if compthreads is not None:
1678 compopts[b'threads'] = compthreads
1678 compopts[b'threads'] = compthreads
1679
1679
1680 # Bundling of obsmarker and phases is optional as not all clients
1680 # Bundling of obsmarker and phases is optional as not all clients
1681 # support the necessary features.
1681 # support the necessary features.
1682 cfg = ui.configbool
1682 cfg = ui.configbool
1683 contentopts = {
1683 obsolescence_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker')
1684 b'obsolescence': cfg(b'experimental', b'evolution.bundle-obsmarker'),
1684 bundlespec.set_param(b'obsolescence', obsolescence_cfg)
1685 b'obsolescence-mandatory': cfg(
1685 obs_mand_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker:mandatory')
1686 b'experimental', b'evolution.bundle-obsmarker:mandatory'
1686 bundlespec.set_param(b'obsolescence-mandatory', obs_mand_cfg)
1687 ),
1687 phases_cfg = cfg(b'experimental', b'bundle-phases')
1688 b'phases': cfg(b'experimental', b'bundle-phases'),
1688 bundlespec.set_param(b'phases', phases_cfg)
1689 }
1690 bundlespec.contentopts.update(contentopts)
1691
1689
1692 bundle2.writenewbundle(
1690 bundle2.writenewbundle(
1693 ui,
1691 ui,
1694 repo,
1692 repo,
1695 b'bundle',
1693 b'bundle',
1696 fname,
1694 fname,
1697 bversion,
1695 bversion,
1698 outgoing,
1696 outgoing,
1699 bundlespec.contentopts,
1697 bundlespec.params,
1700 compression=bcompression,
1698 compression=bcompression,
1701 compopts=compopts,
1699 compopts=compopts,
1702 )
1700 )
1703
1701
1704
1702
1705 @command(
1703 @command(
1706 b'cat',
1704 b'cat',
1707 [
1705 [
1708 (
1706 (
1709 b'o',
1707 b'o',
1710 b'output',
1708 b'output',
1711 b'',
1709 b'',
1712 _(b'print output to file with formatted name'),
1710 _(b'print output to file with formatted name'),
1713 _(b'FORMAT'),
1711 _(b'FORMAT'),
1714 ),
1712 ),
1715 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1713 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1716 (b'', b'decode', None, _(b'apply any matching decode filter')),
1714 (b'', b'decode', None, _(b'apply any matching decode filter')),
1717 ]
1715 ]
1718 + walkopts
1716 + walkopts
1719 + formatteropts,
1717 + formatteropts,
1720 _(b'[OPTION]... FILE...'),
1718 _(b'[OPTION]... FILE...'),
1721 helpcategory=command.CATEGORY_FILE_CONTENTS,
1719 helpcategory=command.CATEGORY_FILE_CONTENTS,
1722 inferrepo=True,
1720 inferrepo=True,
1723 intents={INTENT_READONLY},
1721 intents={INTENT_READONLY},
1724 )
1722 )
1725 def cat(ui, repo, file1, *pats, **opts):
1723 def cat(ui, repo, file1, *pats, **opts):
1726 """output the current or given revision of files
1724 """output the current or given revision of files
1727
1725
1728 Print the specified files as they were at the given revision. If
1726 Print the specified files as they were at the given revision. If
1729 no revision is given, the parent of the working directory is used.
1727 no revision is given, the parent of the working directory is used.
1730
1728
1731 Output may be to a file, in which case the name of the file is
1729 Output may be to a file, in which case the name of the file is
1732 given using a template string. See :hg:`help templates`. In addition
1730 given using a template string. See :hg:`help templates`. In addition
1733 to the common template keywords, the following formatting rules are
1731 to the common template keywords, the following formatting rules are
1734 supported:
1732 supported:
1735
1733
1736 :``%%``: literal "%" character
1734 :``%%``: literal "%" character
1737 :``%s``: basename of file being printed
1735 :``%s``: basename of file being printed
1738 :``%d``: dirname of file being printed, or '.' if in repository root
1736 :``%d``: dirname of file being printed, or '.' if in repository root
1739 :``%p``: root-relative path name of file being printed
1737 :``%p``: root-relative path name of file being printed
1740 :``%H``: changeset hash (40 hexadecimal digits)
1738 :``%H``: changeset hash (40 hexadecimal digits)
1741 :``%R``: changeset revision number
1739 :``%R``: changeset revision number
1742 :``%h``: short-form changeset hash (12 hexadecimal digits)
1740 :``%h``: short-form changeset hash (12 hexadecimal digits)
1743 :``%r``: zero-padded changeset revision number
1741 :``%r``: zero-padded changeset revision number
1744 :``%b``: basename of the exporting repository
1742 :``%b``: basename of the exporting repository
1745 :``\\``: literal "\\" character
1743 :``\\``: literal "\\" character
1746
1744
1747 .. container:: verbose
1745 .. container:: verbose
1748
1746
1749 Template:
1747 Template:
1750
1748
1751 The following keywords are supported in addition to the common template
1749 The following keywords are supported in addition to the common template
1752 keywords and functions. See also :hg:`help templates`.
1750 keywords and functions. See also :hg:`help templates`.
1753
1751
1754 :data: String. File content.
1752 :data: String. File content.
1755 :path: String. Repository-absolute path of the file.
1753 :path: String. Repository-absolute path of the file.
1756
1754
1757 Returns 0 on success.
1755 Returns 0 on success.
1758 """
1756 """
1759 opts = pycompat.byteskwargs(opts)
1757 opts = pycompat.byteskwargs(opts)
1760 rev = opts.get(b'rev')
1758 rev = opts.get(b'rev')
1761 if rev:
1759 if rev:
1762 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1760 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1763 ctx = logcmdutil.revsingle(repo, rev)
1761 ctx = logcmdutil.revsingle(repo, rev)
1764 m = scmutil.match(ctx, (file1,) + pats, opts)
1762 m = scmutil.match(ctx, (file1,) + pats, opts)
1765 fntemplate = opts.pop(b'output', b'')
1763 fntemplate = opts.pop(b'output', b'')
1766 if cmdutil.isstdiofilename(fntemplate):
1764 if cmdutil.isstdiofilename(fntemplate):
1767 fntemplate = b''
1765 fntemplate = b''
1768
1766
1769 if fntemplate:
1767 if fntemplate:
1770 fm = formatter.nullformatter(ui, b'cat', opts)
1768 fm = formatter.nullformatter(ui, b'cat', opts)
1771 else:
1769 else:
1772 ui.pager(b'cat')
1770 ui.pager(b'cat')
1773 fm = ui.formatter(b'cat', opts)
1771 fm = ui.formatter(b'cat', opts)
1774 with fm:
1772 with fm:
1775 return cmdutil.cat(
1773 return cmdutil.cat(
1776 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1774 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1777 )
1775 )
1778
1776
1779
1777
1780 @command(
1778 @command(
1781 b'clone',
1779 b'clone',
1782 [
1780 [
1783 (
1781 (
1784 b'U',
1782 b'U',
1785 b'noupdate',
1783 b'noupdate',
1786 None,
1784 None,
1787 _(
1785 _(
1788 b'the clone will include an empty working '
1786 b'the clone will include an empty working '
1789 b'directory (only a repository)'
1787 b'directory (only a repository)'
1790 ),
1788 ),
1791 ),
1789 ),
1792 (
1790 (
1793 b'u',
1791 b'u',
1794 b'updaterev',
1792 b'updaterev',
1795 b'',
1793 b'',
1796 _(b'revision, tag, or branch to check out'),
1794 _(b'revision, tag, or branch to check out'),
1797 _(b'REV'),
1795 _(b'REV'),
1798 ),
1796 ),
1799 (
1797 (
1800 b'r',
1798 b'r',
1801 b'rev',
1799 b'rev',
1802 [],
1800 [],
1803 _(
1801 _(
1804 b'do not clone everything, but include this changeset'
1802 b'do not clone everything, but include this changeset'
1805 b' and its ancestors'
1803 b' and its ancestors'
1806 ),
1804 ),
1807 _(b'REV'),
1805 _(b'REV'),
1808 ),
1806 ),
1809 (
1807 (
1810 b'b',
1808 b'b',
1811 b'branch',
1809 b'branch',
1812 [],
1810 [],
1813 _(
1811 _(
1814 b'do not clone everything, but include this branch\'s'
1812 b'do not clone everything, but include this branch\'s'
1815 b' changesets and their ancestors'
1813 b' changesets and their ancestors'
1816 ),
1814 ),
1817 _(b'BRANCH'),
1815 _(b'BRANCH'),
1818 ),
1816 ),
1819 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1817 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1820 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1818 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1821 (b'', b'stream', None, _(b'clone with minimal data processing')),
1819 (b'', b'stream', None, _(b'clone with minimal data processing')),
1822 ]
1820 ]
1823 + remoteopts,
1821 + remoteopts,
1824 _(b'[OPTION]... SOURCE [DEST]'),
1822 _(b'[OPTION]... SOURCE [DEST]'),
1825 helpcategory=command.CATEGORY_REPO_CREATION,
1823 helpcategory=command.CATEGORY_REPO_CREATION,
1826 helpbasic=True,
1824 helpbasic=True,
1827 norepo=True,
1825 norepo=True,
1828 )
1826 )
1829 def clone(ui, source, dest=None, **opts):
1827 def clone(ui, source, dest=None, **opts):
1830 """make a copy of an existing repository
1828 """make a copy of an existing repository
1831
1829
1832 Create a copy of an existing repository in a new directory.
1830 Create a copy of an existing repository in a new directory.
1833
1831
1834 If no destination directory name is specified, it defaults to the
1832 If no destination directory name is specified, it defaults to the
1835 basename of the source.
1833 basename of the source.
1836
1834
1837 The location of the source is added to the new repository's
1835 The location of the source is added to the new repository's
1838 ``.hg/hgrc`` file, as the default to be used for future pulls.
1836 ``.hg/hgrc`` file, as the default to be used for future pulls.
1839
1837
1840 Only local paths and ``ssh://`` URLs are supported as
1838 Only local paths and ``ssh://`` URLs are supported as
1841 destinations. For ``ssh://`` destinations, no working directory or
1839 destinations. For ``ssh://`` destinations, no working directory or
1842 ``.hg/hgrc`` will be created on the remote side.
1840 ``.hg/hgrc`` will be created on the remote side.
1843
1841
1844 If the source repository has a bookmark called '@' set, that
1842 If the source repository has a bookmark called '@' set, that
1845 revision will be checked out in the new repository by default.
1843 revision will be checked out in the new repository by default.
1846
1844
1847 To check out a particular version, use -u/--update, or
1845 To check out a particular version, use -u/--update, or
1848 -U/--noupdate to create a clone with no working directory.
1846 -U/--noupdate to create a clone with no working directory.
1849
1847
1850 To pull only a subset of changesets, specify one or more revisions
1848 To pull only a subset of changesets, specify one or more revisions
1851 identifiers with -r/--rev or branches with -b/--branch. The
1849 identifiers with -r/--rev or branches with -b/--branch. The
1852 resulting clone will contain only the specified changesets and
1850 resulting clone will contain only the specified changesets and
1853 their ancestors. These options (or 'clone src#rev dest') imply
1851 their ancestors. These options (or 'clone src#rev dest') imply
1854 --pull, even for local source repositories.
1852 --pull, even for local source repositories.
1855
1853
1856 In normal clone mode, the remote normalizes repository data into a common
1854 In normal clone mode, the remote normalizes repository data into a common
1857 exchange format and the receiving end translates this data into its local
1855 exchange format and the receiving end translates this data into its local
1858 storage format. --stream activates a different clone mode that essentially
1856 storage format. --stream activates a different clone mode that essentially
1859 copies repository files from the remote with minimal data processing. This
1857 copies repository files from the remote with minimal data processing. This
1860 significantly reduces the CPU cost of a clone both remotely and locally.
1858 significantly reduces the CPU cost of a clone both remotely and locally.
1861 However, it often increases the transferred data size by 30-40%. This can
1859 However, it often increases the transferred data size by 30-40%. This can
1862 result in substantially faster clones where I/O throughput is plentiful,
1860 result in substantially faster clones where I/O throughput is plentiful,
1863 especially for larger repositories. A side-effect of --stream clones is
1861 especially for larger repositories. A side-effect of --stream clones is
1864 that storage settings and requirements on the remote are applied locally:
1862 that storage settings and requirements on the remote are applied locally:
1865 a modern client may inherit legacy or inefficient storage used by the
1863 a modern client may inherit legacy or inefficient storage used by the
1866 remote or a legacy Mercurial client may not be able to clone from a
1864 remote or a legacy Mercurial client may not be able to clone from a
1867 modern Mercurial remote.
1865 modern Mercurial remote.
1868
1866
1869 .. note::
1867 .. note::
1870
1868
1871 Specifying a tag will include the tagged changeset but not the
1869 Specifying a tag will include the tagged changeset but not the
1872 changeset containing the tag.
1870 changeset containing the tag.
1873
1871
1874 .. container:: verbose
1872 .. container:: verbose
1875
1873
1876 For efficiency, hardlinks are used for cloning whenever the
1874 For efficiency, hardlinks are used for cloning whenever the
1877 source and destination are on the same filesystem (note this
1875 source and destination are on the same filesystem (note this
1878 applies only to the repository data, not to the working
1876 applies only to the repository data, not to the working
1879 directory). Some filesystems, such as AFS, implement hardlinking
1877 directory). Some filesystems, such as AFS, implement hardlinking
1880 incorrectly, but do not report errors. In these cases, use the
1878 incorrectly, but do not report errors. In these cases, use the
1881 --pull option to avoid hardlinking.
1879 --pull option to avoid hardlinking.
1882
1880
1883 Mercurial will update the working directory to the first applicable
1881 Mercurial will update the working directory to the first applicable
1884 revision from this list:
1882 revision from this list:
1885
1883
1886 a) null if -U or the source repository has no changesets
1884 a) null if -U or the source repository has no changesets
1887 b) if -u . and the source repository is local, the first parent of
1885 b) if -u . and the source repository is local, the first parent of
1888 the source repository's working directory
1886 the source repository's working directory
1889 c) the changeset specified with -u (if a branch name, this means the
1887 c) the changeset specified with -u (if a branch name, this means the
1890 latest head of that branch)
1888 latest head of that branch)
1891 d) the changeset specified with -r
1889 d) the changeset specified with -r
1892 e) the tipmost head specified with -b
1890 e) the tipmost head specified with -b
1893 f) the tipmost head specified with the url#branch source syntax
1891 f) the tipmost head specified with the url#branch source syntax
1894 g) the revision marked with the '@' bookmark, if present
1892 g) the revision marked with the '@' bookmark, if present
1895 h) the tipmost head of the default branch
1893 h) the tipmost head of the default branch
1896 i) tip
1894 i) tip
1897
1895
1898 When cloning from servers that support it, Mercurial may fetch
1896 When cloning from servers that support it, Mercurial may fetch
1899 pre-generated data from a server-advertised URL or inline from the
1897 pre-generated data from a server-advertised URL or inline from the
1900 same stream. When this is done, hooks operating on incoming changesets
1898 same stream. When this is done, hooks operating on incoming changesets
1901 and changegroups may fire more than once, once for each pre-generated
1899 and changegroups may fire more than once, once for each pre-generated
1902 bundle and as well as for any additional remaining data. In addition,
1900 bundle and as well as for any additional remaining data. In addition,
1903 if an error occurs, the repository may be rolled back to a partial
1901 if an error occurs, the repository may be rolled back to a partial
1904 clone. This behavior may change in future releases.
1902 clone. This behavior may change in future releases.
1905 See :hg:`help -e clonebundles` for more.
1903 See :hg:`help -e clonebundles` for more.
1906
1904
1907 Examples:
1905 Examples:
1908
1906
1909 - clone a remote repository to a new directory named hg/::
1907 - clone a remote repository to a new directory named hg/::
1910
1908
1911 hg clone https://www.mercurial-scm.org/repo/hg/
1909 hg clone https://www.mercurial-scm.org/repo/hg/
1912
1910
1913 - create a lightweight local clone::
1911 - create a lightweight local clone::
1914
1912
1915 hg clone project/ project-feature/
1913 hg clone project/ project-feature/
1916
1914
1917 - clone from an absolute path on an ssh server (note double-slash)::
1915 - clone from an absolute path on an ssh server (note double-slash)::
1918
1916
1919 hg clone ssh://user@server//home/projects/alpha/
1917 hg clone ssh://user@server//home/projects/alpha/
1920
1918
1921 - do a streaming clone while checking out a specified version::
1919 - do a streaming clone while checking out a specified version::
1922
1920
1923 hg clone --stream http://server/repo -u 1.5
1921 hg clone --stream http://server/repo -u 1.5
1924
1922
1925 - create a repository without changesets after a particular revision::
1923 - create a repository without changesets after a particular revision::
1926
1924
1927 hg clone -r 04e544 experimental/ good/
1925 hg clone -r 04e544 experimental/ good/
1928
1926
1929 - clone (and track) a particular named branch::
1927 - clone (and track) a particular named branch::
1930
1928
1931 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1929 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1932
1930
1933 See :hg:`help urls` for details on specifying URLs.
1931 See :hg:`help urls` for details on specifying URLs.
1934
1932
1935 Returns 0 on success.
1933 Returns 0 on success.
1936 """
1934 """
1937 opts = pycompat.byteskwargs(opts)
1935 opts = pycompat.byteskwargs(opts)
1938 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1936 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1939
1937
1940 # --include/--exclude can come from narrow or sparse.
1938 # --include/--exclude can come from narrow or sparse.
1941 includepats, excludepats = None, None
1939 includepats, excludepats = None, None
1942
1940
1943 # hg.clone() differentiates between None and an empty set. So make sure
1941 # hg.clone() differentiates between None and an empty set. So make sure
1944 # patterns are sets if narrow is requested without patterns.
1942 # patterns are sets if narrow is requested without patterns.
1945 if opts.get(b'narrow'):
1943 if opts.get(b'narrow'):
1946 includepats = set()
1944 includepats = set()
1947 excludepats = set()
1945 excludepats = set()
1948
1946
1949 if opts.get(b'include'):
1947 if opts.get(b'include'):
1950 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1948 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1951 if opts.get(b'exclude'):
1949 if opts.get(b'exclude'):
1952 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1950 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1953
1951
1954 r = hg.clone(
1952 r = hg.clone(
1955 ui,
1953 ui,
1956 opts,
1954 opts,
1957 source,
1955 source,
1958 dest,
1956 dest,
1959 pull=opts.get(b'pull'),
1957 pull=opts.get(b'pull'),
1960 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1958 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1961 revs=opts.get(b'rev'),
1959 revs=opts.get(b'rev'),
1962 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1960 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1963 branch=opts.get(b'branch'),
1961 branch=opts.get(b'branch'),
1964 shareopts=opts.get(b'shareopts'),
1962 shareopts=opts.get(b'shareopts'),
1965 storeincludepats=includepats,
1963 storeincludepats=includepats,
1966 storeexcludepats=excludepats,
1964 storeexcludepats=excludepats,
1967 depth=opts.get(b'depth') or None,
1965 depth=opts.get(b'depth') or None,
1968 )
1966 )
1969
1967
1970 return r is None
1968 return r is None
1971
1969
1972
1970
1973 @command(
1971 @command(
1974 b'commit|ci',
1972 b'commit|ci',
1975 [
1973 [
1976 (
1974 (
1977 b'A',
1975 b'A',
1978 b'addremove',
1976 b'addremove',
1979 None,
1977 None,
1980 _(b'mark new/missing files as added/removed before committing'),
1978 _(b'mark new/missing files as added/removed before committing'),
1981 ),
1979 ),
1982 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1980 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1983 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1981 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1984 (b's', b'secret', None, _(b'use the secret phase for committing')),
1982 (b's', b'secret', None, _(b'use the secret phase for committing')),
1985 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1983 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1986 (
1984 (
1987 b'',
1985 b'',
1988 b'force-close-branch',
1986 b'force-close-branch',
1989 None,
1987 None,
1990 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1988 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1991 ),
1989 ),
1992 (b'i', b'interactive', None, _(b'use interactive mode')),
1990 (b'i', b'interactive', None, _(b'use interactive mode')),
1993 ]
1991 ]
1994 + walkopts
1992 + walkopts
1995 + commitopts
1993 + commitopts
1996 + commitopts2
1994 + commitopts2
1997 + subrepoopts,
1995 + subrepoopts,
1998 _(b'[OPTION]... [FILE]...'),
1996 _(b'[OPTION]... [FILE]...'),
1999 helpcategory=command.CATEGORY_COMMITTING,
1997 helpcategory=command.CATEGORY_COMMITTING,
2000 helpbasic=True,
1998 helpbasic=True,
2001 inferrepo=True,
1999 inferrepo=True,
2002 )
2000 )
2003 def commit(ui, repo, *pats, **opts):
2001 def commit(ui, repo, *pats, **opts):
2004 """commit the specified files or all outstanding changes
2002 """commit the specified files or all outstanding changes
2005
2003
2006 Commit changes to the given files into the repository. Unlike a
2004 Commit changes to the given files into the repository. Unlike a
2007 centralized SCM, this operation is a local operation. See
2005 centralized SCM, this operation is a local operation. See
2008 :hg:`push` for a way to actively distribute your changes.
2006 :hg:`push` for a way to actively distribute your changes.
2009
2007
2010 If a list of files is omitted, all changes reported by :hg:`status`
2008 If a list of files is omitted, all changes reported by :hg:`status`
2011 will be committed.
2009 will be committed.
2012
2010
2013 If you are committing the result of a merge, do not provide any
2011 If you are committing the result of a merge, do not provide any
2014 filenames or -I/-X filters.
2012 filenames or -I/-X filters.
2015
2013
2016 If no commit message is specified, Mercurial starts your
2014 If no commit message is specified, Mercurial starts your
2017 configured editor where you can enter a message. In case your
2015 configured editor where you can enter a message. In case your
2018 commit fails, you will find a backup of your message in
2016 commit fails, you will find a backup of your message in
2019 ``.hg/last-message.txt``.
2017 ``.hg/last-message.txt``.
2020
2018
2021 The --close-branch flag can be used to mark the current branch
2019 The --close-branch flag can be used to mark the current branch
2022 head closed. When all heads of a branch are closed, the branch
2020 head closed. When all heads of a branch are closed, the branch
2023 will be considered closed and no longer listed.
2021 will be considered closed and no longer listed.
2024
2022
2025 The --amend flag can be used to amend the parent of the
2023 The --amend flag can be used to amend the parent of the
2026 working directory with a new commit that contains the changes
2024 working directory with a new commit that contains the changes
2027 in the parent in addition to those currently reported by :hg:`status`,
2025 in the parent in addition to those currently reported by :hg:`status`,
2028 if there are any. The old commit is stored in a backup bundle in
2026 if there are any. The old commit is stored in a backup bundle in
2029 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2027 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2030 on how to restore it).
2028 on how to restore it).
2031
2029
2032 Message, user and date are taken from the amended commit unless
2030 Message, user and date are taken from the amended commit unless
2033 specified. When a message isn't specified on the command line,
2031 specified. When a message isn't specified on the command line,
2034 the editor will open with the message of the amended commit.
2032 the editor will open with the message of the amended commit.
2035
2033
2036 It is not possible to amend public changesets (see :hg:`help phases`)
2034 It is not possible to amend public changesets (see :hg:`help phases`)
2037 or changesets that have children.
2035 or changesets that have children.
2038
2036
2039 See :hg:`help dates` for a list of formats valid for -d/--date.
2037 See :hg:`help dates` for a list of formats valid for -d/--date.
2040
2038
2041 Returns 0 on success, 1 if nothing changed.
2039 Returns 0 on success, 1 if nothing changed.
2042
2040
2043 .. container:: verbose
2041 .. container:: verbose
2044
2042
2045 Examples:
2043 Examples:
2046
2044
2047 - commit all files ending in .py::
2045 - commit all files ending in .py::
2048
2046
2049 hg commit --include "set:**.py"
2047 hg commit --include "set:**.py"
2050
2048
2051 - commit all non-binary files::
2049 - commit all non-binary files::
2052
2050
2053 hg commit --exclude "set:binary()"
2051 hg commit --exclude "set:binary()"
2054
2052
2055 - amend the current commit and set the date to now::
2053 - amend the current commit and set the date to now::
2056
2054
2057 hg commit --amend --date now
2055 hg commit --amend --date now
2058 """
2056 """
2059 with repo.wlock(), repo.lock():
2057 with repo.wlock(), repo.lock():
2060 return _docommit(ui, repo, *pats, **opts)
2058 return _docommit(ui, repo, *pats, **opts)
2061
2059
2062
2060
2063 def _docommit(ui, repo, *pats, **opts):
2061 def _docommit(ui, repo, *pats, **opts):
2064 if opts.get('interactive'):
2062 if opts.get('interactive'):
2065 opts.pop('interactive')
2063 opts.pop('interactive')
2066 ret = cmdutil.dorecord(
2064 ret = cmdutil.dorecord(
2067 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2065 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2068 )
2066 )
2069 # ret can be 0 (no changes to record) or the value returned by
2067 # ret can be 0 (no changes to record) or the value returned by
2070 # commit(), 1 if nothing changed or None on success.
2068 # commit(), 1 if nothing changed or None on success.
2071 return 1 if ret == 0 else ret
2069 return 1 if ret == 0 else ret
2072
2070
2073 if opts.get('subrepos'):
2071 if opts.get('subrepos'):
2074 cmdutil.check_incompatible_arguments(opts, 'subrepos', ['amend'])
2072 cmdutil.check_incompatible_arguments(opts, 'subrepos', ['amend'])
2075 # Let --subrepos on the command line override config setting.
2073 # Let --subrepos on the command line override config setting.
2076 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2074 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2077
2075
2078 cmdutil.checkunfinished(repo, commit=True)
2076 cmdutil.checkunfinished(repo, commit=True)
2079
2077
2080 branch = repo[None].branch()
2078 branch = repo[None].branch()
2081 bheads = repo.branchheads(branch)
2079 bheads = repo.branchheads(branch)
2082 tip = repo.changelog.tip()
2080 tip = repo.changelog.tip()
2083
2081
2084 extra = {}
2082 extra = {}
2085 if opts.get('close_branch') or opts.get('force_close_branch'):
2083 if opts.get('close_branch') or opts.get('force_close_branch'):
2086 extra[b'close'] = b'1'
2084 extra[b'close'] = b'1'
2087
2085
2088 if repo[b'.'].closesbranch():
2086 if repo[b'.'].closesbranch():
2089 raise error.InputError(
2087 raise error.InputError(
2090 _(b'current revision is already a branch closing head')
2088 _(b'current revision is already a branch closing head')
2091 )
2089 )
2092 elif not bheads:
2090 elif not bheads:
2093 raise error.InputError(
2091 raise error.InputError(
2094 _(b'branch "%s" has no heads to close') % branch
2092 _(b'branch "%s" has no heads to close') % branch
2095 )
2093 )
2096 elif (
2094 elif (
2097 branch == repo[b'.'].branch()
2095 branch == repo[b'.'].branch()
2098 and repo[b'.'].node() not in bheads
2096 and repo[b'.'].node() not in bheads
2099 and not opts.get('force_close_branch')
2097 and not opts.get('force_close_branch')
2100 ):
2098 ):
2101 hint = _(
2099 hint = _(
2102 b'use --force-close-branch to close branch from a non-head'
2100 b'use --force-close-branch to close branch from a non-head'
2103 b' changeset'
2101 b' changeset'
2104 )
2102 )
2105 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2103 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2106 elif opts.get('amend'):
2104 elif opts.get('amend'):
2107 if (
2105 if (
2108 repo[b'.'].p1().branch() != branch
2106 repo[b'.'].p1().branch() != branch
2109 and repo[b'.'].p2().branch() != branch
2107 and repo[b'.'].p2().branch() != branch
2110 ):
2108 ):
2111 raise error.InputError(_(b'can only close branch heads'))
2109 raise error.InputError(_(b'can only close branch heads'))
2112
2110
2113 if opts.get('amend'):
2111 if opts.get('amend'):
2114 if ui.configbool(b'ui', b'commitsubrepos'):
2112 if ui.configbool(b'ui', b'commitsubrepos'):
2115 raise error.InputError(
2113 raise error.InputError(
2116 _(b'cannot amend with ui.commitsubrepos enabled')
2114 _(b'cannot amend with ui.commitsubrepos enabled')
2117 )
2115 )
2118
2116
2119 old = repo[b'.']
2117 old = repo[b'.']
2120 rewriteutil.precheck(repo, [old.rev()], b'amend')
2118 rewriteutil.precheck(repo, [old.rev()], b'amend')
2121
2119
2122 # Currently histedit gets confused if an amend happens while histedit
2120 # Currently histedit gets confused if an amend happens while histedit
2123 # is in progress. Since we have a checkunfinished command, we are
2121 # is in progress. Since we have a checkunfinished command, we are
2124 # temporarily honoring it.
2122 # temporarily honoring it.
2125 #
2123 #
2126 # Note: eventually this guard will be removed. Please do not expect
2124 # Note: eventually this guard will be removed. Please do not expect
2127 # this behavior to remain.
2125 # this behavior to remain.
2128 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2126 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2129 cmdutil.checkunfinished(repo)
2127 cmdutil.checkunfinished(repo)
2130
2128
2131 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2129 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2132 opts = pycompat.byteskwargs(opts)
2130 opts = pycompat.byteskwargs(opts)
2133 if node == old.node():
2131 if node == old.node():
2134 ui.status(_(b"nothing changed\n"))
2132 ui.status(_(b"nothing changed\n"))
2135 return 1
2133 return 1
2136 else:
2134 else:
2137
2135
2138 def commitfunc(ui, repo, message, match, opts):
2136 def commitfunc(ui, repo, message, match, opts):
2139 overrides = {}
2137 overrides = {}
2140 if opts.get(b'secret'):
2138 if opts.get(b'secret'):
2141 overrides[(b'phases', b'new-commit')] = b'secret'
2139 overrides[(b'phases', b'new-commit')] = b'secret'
2142
2140
2143 baseui = repo.baseui
2141 baseui = repo.baseui
2144 with baseui.configoverride(overrides, b'commit'):
2142 with baseui.configoverride(overrides, b'commit'):
2145 with ui.configoverride(overrides, b'commit'):
2143 with ui.configoverride(overrides, b'commit'):
2146 editform = cmdutil.mergeeditform(
2144 editform = cmdutil.mergeeditform(
2147 repo[None], b'commit.normal'
2145 repo[None], b'commit.normal'
2148 )
2146 )
2149 editor = cmdutil.getcommiteditor(
2147 editor = cmdutil.getcommiteditor(
2150 editform=editform, **pycompat.strkwargs(opts)
2148 editform=editform, **pycompat.strkwargs(opts)
2151 )
2149 )
2152 return repo.commit(
2150 return repo.commit(
2153 message,
2151 message,
2154 opts.get(b'user'),
2152 opts.get(b'user'),
2155 opts.get(b'date'),
2153 opts.get(b'date'),
2156 match,
2154 match,
2157 editor=editor,
2155 editor=editor,
2158 extra=extra,
2156 extra=extra,
2159 )
2157 )
2160
2158
2161 opts = pycompat.byteskwargs(opts)
2159 opts = pycompat.byteskwargs(opts)
2162 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2160 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2163
2161
2164 if not node:
2162 if not node:
2165 stat = cmdutil.postcommitstatus(repo, pats, opts)
2163 stat = cmdutil.postcommitstatus(repo, pats, opts)
2166 if stat.deleted:
2164 if stat.deleted:
2167 ui.status(
2165 ui.status(
2168 _(
2166 _(
2169 b"nothing changed (%d missing files, see "
2167 b"nothing changed (%d missing files, see "
2170 b"'hg status')\n"
2168 b"'hg status')\n"
2171 )
2169 )
2172 % len(stat.deleted)
2170 % len(stat.deleted)
2173 )
2171 )
2174 else:
2172 else:
2175 ui.status(_(b"nothing changed\n"))
2173 ui.status(_(b"nothing changed\n"))
2176 return 1
2174 return 1
2177
2175
2178 cmdutil.commitstatus(repo, node, branch, bheads, tip, opts)
2176 cmdutil.commitstatus(repo, node, branch, bheads, tip, opts)
2179
2177
2180 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2178 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2181 status(
2179 status(
2182 ui,
2180 ui,
2183 repo,
2181 repo,
2184 modified=True,
2182 modified=True,
2185 added=True,
2183 added=True,
2186 removed=True,
2184 removed=True,
2187 deleted=True,
2185 deleted=True,
2188 unknown=True,
2186 unknown=True,
2189 subrepos=opts.get(b'subrepos'),
2187 subrepos=opts.get(b'subrepos'),
2190 )
2188 )
2191
2189
2192
2190
2193 @command(
2191 @command(
2194 b'config|showconfig|debugconfig',
2192 b'config|showconfig|debugconfig',
2195 [
2193 [
2196 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2194 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2197 # This is experimental because we need
2195 # This is experimental because we need
2198 # * reasonable behavior around aliases,
2196 # * reasonable behavior around aliases,
2199 # * decide if we display [debug] [experimental] and [devel] section par
2197 # * decide if we display [debug] [experimental] and [devel] section par
2200 # default
2198 # default
2201 # * some way to display "generic" config entry (the one matching
2199 # * some way to display "generic" config entry (the one matching
2202 # regexp,
2200 # regexp,
2203 # * proper display of the different value type
2201 # * proper display of the different value type
2204 # * a better way to handle <DYNAMIC> values (and variable types),
2202 # * a better way to handle <DYNAMIC> values (and variable types),
2205 # * maybe some type information ?
2203 # * maybe some type information ?
2206 (
2204 (
2207 b'',
2205 b'',
2208 b'exp-all-known',
2206 b'exp-all-known',
2209 None,
2207 None,
2210 _(b'show all known config option (EXPERIMENTAL)'),
2208 _(b'show all known config option (EXPERIMENTAL)'),
2211 ),
2209 ),
2212 (b'e', b'edit', None, _(b'edit user config')),
2210 (b'e', b'edit', None, _(b'edit user config')),
2213 (b'l', b'local', None, _(b'edit repository config')),
2211 (b'l', b'local', None, _(b'edit repository config')),
2214 (b'', b'source', None, _(b'show source of configuration value')),
2212 (b'', b'source', None, _(b'show source of configuration value')),
2215 (
2213 (
2216 b'',
2214 b'',
2217 b'shared',
2215 b'shared',
2218 None,
2216 None,
2219 _(b'edit shared source repository config (EXPERIMENTAL)'),
2217 _(b'edit shared source repository config (EXPERIMENTAL)'),
2220 ),
2218 ),
2221 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2219 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2222 (b'g', b'global', None, _(b'edit global config')),
2220 (b'g', b'global', None, _(b'edit global config')),
2223 ]
2221 ]
2224 + formatteropts,
2222 + formatteropts,
2225 _(b'[-u] [NAME]...'),
2223 _(b'[-u] [NAME]...'),
2226 helpcategory=command.CATEGORY_HELP,
2224 helpcategory=command.CATEGORY_HELP,
2227 optionalrepo=True,
2225 optionalrepo=True,
2228 intents={INTENT_READONLY},
2226 intents={INTENT_READONLY},
2229 )
2227 )
2230 def config(ui, repo, *values, **opts):
2228 def config(ui, repo, *values, **opts):
2231 """show combined config settings from all hgrc files
2229 """show combined config settings from all hgrc files
2232
2230
2233 With no arguments, print names and values of all config items.
2231 With no arguments, print names and values of all config items.
2234
2232
2235 With one argument of the form section.name, print just the value
2233 With one argument of the form section.name, print just the value
2236 of that config item.
2234 of that config item.
2237
2235
2238 With multiple arguments, print names and values of all config
2236 With multiple arguments, print names and values of all config
2239 items with matching section names or section.names.
2237 items with matching section names or section.names.
2240
2238
2241 With --edit, start an editor on the user-level config file. With
2239 With --edit, start an editor on the user-level config file. With
2242 --global, edit the system-wide config file. With --local, edit the
2240 --global, edit the system-wide config file. With --local, edit the
2243 repository-level config file.
2241 repository-level config file.
2244
2242
2245 With --source, the source (filename and line number) is printed
2243 With --source, the source (filename and line number) is printed
2246 for each config item.
2244 for each config item.
2247
2245
2248 See :hg:`help config` for more information about config files.
2246 See :hg:`help config` for more information about config files.
2249
2247
2250 .. container:: verbose
2248 .. container:: verbose
2251
2249
2252 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2250 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2253 This file is not shared across shares when in share-safe mode.
2251 This file is not shared across shares when in share-safe mode.
2254
2252
2255 Template:
2253 Template:
2256
2254
2257 The following keywords are supported. See also :hg:`help templates`.
2255 The following keywords are supported. See also :hg:`help templates`.
2258
2256
2259 :name: String. Config name.
2257 :name: String. Config name.
2260 :source: String. Filename and line number where the item is defined.
2258 :source: String. Filename and line number where the item is defined.
2261 :value: String. Config value.
2259 :value: String. Config value.
2262
2260
2263 The --shared flag can be used to edit the config file of shared source
2261 The --shared flag can be used to edit the config file of shared source
2264 repository. It only works when you have shared using the experimental
2262 repository. It only works when you have shared using the experimental
2265 share safe feature.
2263 share safe feature.
2266
2264
2267 Returns 0 on success, 1 if NAME does not exist.
2265 Returns 0 on success, 1 if NAME does not exist.
2268
2266
2269 """
2267 """
2270
2268
2271 opts = pycompat.byteskwargs(opts)
2269 opts = pycompat.byteskwargs(opts)
2272 editopts = (b'edit', b'local', b'global', b'shared', b'non_shared')
2270 editopts = (b'edit', b'local', b'global', b'shared', b'non_shared')
2273 if any(opts.get(o) for o in editopts):
2271 if any(opts.get(o) for o in editopts):
2274 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2272 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2275 if opts.get(b'local'):
2273 if opts.get(b'local'):
2276 if not repo:
2274 if not repo:
2277 raise error.InputError(
2275 raise error.InputError(
2278 _(b"can't use --local outside a repository")
2276 _(b"can't use --local outside a repository")
2279 )
2277 )
2280 paths = [repo.vfs.join(b'hgrc')]
2278 paths = [repo.vfs.join(b'hgrc')]
2281 elif opts.get(b'global'):
2279 elif opts.get(b'global'):
2282 paths = rcutil.systemrcpath()
2280 paths = rcutil.systemrcpath()
2283 elif opts.get(b'shared'):
2281 elif opts.get(b'shared'):
2284 if not repo.shared():
2282 if not repo.shared():
2285 raise error.InputError(
2283 raise error.InputError(
2286 _(b"repository is not shared; can't use --shared")
2284 _(b"repository is not shared; can't use --shared")
2287 )
2285 )
2288 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2286 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2289 raise error.InputError(
2287 raise error.InputError(
2290 _(
2288 _(
2291 b"share safe feature not enabled; "
2289 b"share safe feature not enabled; "
2292 b"unable to edit shared source repository config"
2290 b"unable to edit shared source repository config"
2293 )
2291 )
2294 )
2292 )
2295 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2293 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2296 elif opts.get(b'non_shared'):
2294 elif opts.get(b'non_shared'):
2297 paths = [repo.vfs.join(b'hgrc-not-shared')]
2295 paths = [repo.vfs.join(b'hgrc-not-shared')]
2298 else:
2296 else:
2299 paths = rcutil.userrcpath()
2297 paths = rcutil.userrcpath()
2300
2298
2301 for f in paths:
2299 for f in paths:
2302 if os.path.exists(f):
2300 if os.path.exists(f):
2303 break
2301 break
2304 else:
2302 else:
2305 if opts.get(b'global'):
2303 if opts.get(b'global'):
2306 samplehgrc = uimod.samplehgrcs[b'global']
2304 samplehgrc = uimod.samplehgrcs[b'global']
2307 elif opts.get(b'local'):
2305 elif opts.get(b'local'):
2308 samplehgrc = uimod.samplehgrcs[b'local']
2306 samplehgrc = uimod.samplehgrcs[b'local']
2309 else:
2307 else:
2310 samplehgrc = uimod.samplehgrcs[b'user']
2308 samplehgrc = uimod.samplehgrcs[b'user']
2311
2309
2312 f = paths[0]
2310 f = paths[0]
2313 fp = open(f, b"wb")
2311 fp = open(f, b"wb")
2314 fp.write(util.tonativeeol(samplehgrc))
2312 fp.write(util.tonativeeol(samplehgrc))
2315 fp.close()
2313 fp.close()
2316
2314
2317 editor = ui.geteditor()
2315 editor = ui.geteditor()
2318 ui.system(
2316 ui.system(
2319 b"%s \"%s\"" % (editor, f),
2317 b"%s \"%s\"" % (editor, f),
2320 onerr=error.InputError,
2318 onerr=error.InputError,
2321 errprefix=_(b"edit failed"),
2319 errprefix=_(b"edit failed"),
2322 blockedtag=b'config_edit',
2320 blockedtag=b'config_edit',
2323 )
2321 )
2324 return
2322 return
2325 ui.pager(b'config')
2323 ui.pager(b'config')
2326 fm = ui.formatter(b'config', opts)
2324 fm = ui.formatter(b'config', opts)
2327 for t, f in rcutil.rccomponents():
2325 for t, f in rcutil.rccomponents():
2328 if t == b'path':
2326 if t == b'path':
2329 ui.debug(b'read config from: %s\n' % f)
2327 ui.debug(b'read config from: %s\n' % f)
2330 elif t == b'resource':
2328 elif t == b'resource':
2331 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2329 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2332 elif t == b'items':
2330 elif t == b'items':
2333 # Don't print anything for 'items'.
2331 # Don't print anything for 'items'.
2334 pass
2332 pass
2335 else:
2333 else:
2336 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2334 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2337 untrusted = bool(opts.get(b'untrusted'))
2335 untrusted = bool(opts.get(b'untrusted'))
2338
2336
2339 selsections = selentries = []
2337 selsections = selentries = []
2340 if values:
2338 if values:
2341 selsections = [v for v in values if b'.' not in v]
2339 selsections = [v for v in values if b'.' not in v]
2342 selentries = [v for v in values if b'.' in v]
2340 selentries = [v for v in values if b'.' in v]
2343 uniquesel = len(selentries) == 1 and not selsections
2341 uniquesel = len(selentries) == 1 and not selsections
2344 selsections = set(selsections)
2342 selsections = set(selsections)
2345 selentries = set(selentries)
2343 selentries = set(selentries)
2346
2344
2347 matched = False
2345 matched = False
2348 all_known = opts[b'exp_all_known']
2346 all_known = opts[b'exp_all_known']
2349 show_source = ui.debugflag or opts.get(b'source')
2347 show_source = ui.debugflag or opts.get(b'source')
2350 entries = ui.walkconfig(untrusted=untrusted, all_known=all_known)
2348 entries = ui.walkconfig(untrusted=untrusted, all_known=all_known)
2351 for section, name, value in entries:
2349 for section, name, value in entries:
2352 source = ui.configsource(section, name, untrusted)
2350 source = ui.configsource(section, name, untrusted)
2353 value = pycompat.bytestr(value)
2351 value = pycompat.bytestr(value)
2354 defaultvalue = ui.configdefault(section, name)
2352 defaultvalue = ui.configdefault(section, name)
2355 if fm.isplain():
2353 if fm.isplain():
2356 source = source or b'none'
2354 source = source or b'none'
2357 value = value.replace(b'\n', b'\\n')
2355 value = value.replace(b'\n', b'\\n')
2358 entryname = section + b'.' + name
2356 entryname = section + b'.' + name
2359 if values and not (section in selsections or entryname in selentries):
2357 if values and not (section in selsections or entryname in selentries):
2360 continue
2358 continue
2361 fm.startitem()
2359 fm.startitem()
2362 fm.condwrite(show_source, b'source', b'%s: ', source)
2360 fm.condwrite(show_source, b'source', b'%s: ', source)
2363 if uniquesel:
2361 if uniquesel:
2364 fm.data(name=entryname)
2362 fm.data(name=entryname)
2365 fm.write(b'value', b'%s\n', value)
2363 fm.write(b'value', b'%s\n', value)
2366 else:
2364 else:
2367 fm.write(b'name value', b'%s=%s\n', entryname, value)
2365 fm.write(b'name value', b'%s=%s\n', entryname, value)
2368 if formatter.isprintable(defaultvalue):
2366 if formatter.isprintable(defaultvalue):
2369 fm.data(defaultvalue=defaultvalue)
2367 fm.data(defaultvalue=defaultvalue)
2370 elif isinstance(defaultvalue, list) and all(
2368 elif isinstance(defaultvalue, list) and all(
2371 formatter.isprintable(e) for e in defaultvalue
2369 formatter.isprintable(e) for e in defaultvalue
2372 ):
2370 ):
2373 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2371 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2374 # TODO: no idea how to process unsupported defaultvalue types
2372 # TODO: no idea how to process unsupported defaultvalue types
2375 matched = True
2373 matched = True
2376 fm.end()
2374 fm.end()
2377 if matched:
2375 if matched:
2378 return 0
2376 return 0
2379 return 1
2377 return 1
2380
2378
2381
2379
2382 @command(
2380 @command(
2383 b'continue',
2381 b'continue',
2384 dryrunopts,
2382 dryrunopts,
2385 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2383 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2386 helpbasic=True,
2384 helpbasic=True,
2387 )
2385 )
2388 def continuecmd(ui, repo, **opts):
2386 def continuecmd(ui, repo, **opts):
2389 """resumes an interrupted operation (EXPERIMENTAL)
2387 """resumes an interrupted operation (EXPERIMENTAL)
2390
2388
2391 Finishes a multistep operation like graft, histedit, rebase, merge,
2389 Finishes a multistep operation like graft, histedit, rebase, merge,
2392 and unshelve if they are in an interrupted state.
2390 and unshelve if they are in an interrupted state.
2393
2391
2394 use --dry-run/-n to dry run the command.
2392 use --dry-run/-n to dry run the command.
2395 """
2393 """
2396 dryrun = opts.get('dry_run')
2394 dryrun = opts.get('dry_run')
2397 contstate = cmdutil.getunfinishedstate(repo)
2395 contstate = cmdutil.getunfinishedstate(repo)
2398 if not contstate:
2396 if not contstate:
2399 raise error.StateError(_(b'no operation in progress'))
2397 raise error.StateError(_(b'no operation in progress'))
2400 if not contstate.continuefunc:
2398 if not contstate.continuefunc:
2401 raise error.StateError(
2399 raise error.StateError(
2402 (
2400 (
2403 _(b"%s in progress but does not support 'hg continue'")
2401 _(b"%s in progress but does not support 'hg continue'")
2404 % (contstate._opname)
2402 % (contstate._opname)
2405 ),
2403 ),
2406 hint=contstate.continuemsg(),
2404 hint=contstate.continuemsg(),
2407 )
2405 )
2408 if dryrun:
2406 if dryrun:
2409 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2407 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2410 return
2408 return
2411 return contstate.continuefunc(ui, repo)
2409 return contstate.continuefunc(ui, repo)
2412
2410
2413
2411
2414 @command(
2412 @command(
2415 b'copy|cp',
2413 b'copy|cp',
2416 [
2414 [
2417 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2415 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2418 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2416 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2419 (
2417 (
2420 b'',
2418 b'',
2421 b'at-rev',
2419 b'at-rev',
2422 b'',
2420 b'',
2423 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2421 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2424 _(b'REV'),
2422 _(b'REV'),
2425 ),
2423 ),
2426 (
2424 (
2427 b'f',
2425 b'f',
2428 b'force',
2426 b'force',
2429 None,
2427 None,
2430 _(b'forcibly copy over an existing managed file'),
2428 _(b'forcibly copy over an existing managed file'),
2431 ),
2429 ),
2432 ]
2430 ]
2433 + walkopts
2431 + walkopts
2434 + dryrunopts,
2432 + dryrunopts,
2435 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2433 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2436 helpcategory=command.CATEGORY_FILE_CONTENTS,
2434 helpcategory=command.CATEGORY_FILE_CONTENTS,
2437 )
2435 )
2438 def copy(ui, repo, *pats, **opts):
2436 def copy(ui, repo, *pats, **opts):
2439 """mark files as copied for the next commit
2437 """mark files as copied for the next commit
2440
2438
2441 Mark dest as having copies of source files. If dest is a
2439 Mark dest as having copies of source files. If dest is a
2442 directory, copies are put in that directory. If dest is a file,
2440 directory, copies are put in that directory. If dest is a file,
2443 the source must be a single file.
2441 the source must be a single file.
2444
2442
2445 By default, this command copies the contents of files as they
2443 By default, this command copies the contents of files as they
2446 exist in the working directory. If invoked with -A/--after, the
2444 exist in the working directory. If invoked with -A/--after, the
2447 operation is recorded, but no copying is performed.
2445 operation is recorded, but no copying is performed.
2448
2446
2449 To undo marking a destination file as copied, use --forget. With that
2447 To undo marking a destination file as copied, use --forget. With that
2450 option, all given (positional) arguments are unmarked as copies. The
2448 option, all given (positional) arguments are unmarked as copies. The
2451 destination file(s) will be left in place (still tracked). Note that
2449 destination file(s) will be left in place (still tracked). Note that
2452 :hg:`copy --forget` behaves the same way as :hg:`rename --forget`.
2450 :hg:`copy --forget` behaves the same way as :hg:`rename --forget`.
2453
2451
2454 This command takes effect with the next commit by default.
2452 This command takes effect with the next commit by default.
2455
2453
2456 Returns 0 on success, 1 if errors are encountered.
2454 Returns 0 on success, 1 if errors are encountered.
2457 """
2455 """
2458 opts = pycompat.byteskwargs(opts)
2456 opts = pycompat.byteskwargs(opts)
2459 with repo.wlock():
2457 with repo.wlock():
2460 return cmdutil.copy(ui, repo, pats, opts)
2458 return cmdutil.copy(ui, repo, pats, opts)
2461
2459
2462
2460
2463 @command(
2461 @command(
2464 b'debugcommands',
2462 b'debugcommands',
2465 [],
2463 [],
2466 _(b'[COMMAND]'),
2464 _(b'[COMMAND]'),
2467 helpcategory=command.CATEGORY_HELP,
2465 helpcategory=command.CATEGORY_HELP,
2468 norepo=True,
2466 norepo=True,
2469 )
2467 )
2470 def debugcommands(ui, cmd=b'', *args):
2468 def debugcommands(ui, cmd=b'', *args):
2471 """list all available commands and options"""
2469 """list all available commands and options"""
2472 for cmd, vals in sorted(table.items()):
2470 for cmd, vals in sorted(table.items()):
2473 cmd = cmd.split(b'|')[0]
2471 cmd = cmd.split(b'|')[0]
2474 opts = b', '.join([i[1] for i in vals[1]])
2472 opts = b', '.join([i[1] for i in vals[1]])
2475 ui.write(b'%s: %s\n' % (cmd, opts))
2473 ui.write(b'%s: %s\n' % (cmd, opts))
2476
2474
2477
2475
2478 @command(
2476 @command(
2479 b'debugcomplete',
2477 b'debugcomplete',
2480 [(b'o', b'options', None, _(b'show the command options'))],
2478 [(b'o', b'options', None, _(b'show the command options'))],
2481 _(b'[-o] CMD'),
2479 _(b'[-o] CMD'),
2482 helpcategory=command.CATEGORY_HELP,
2480 helpcategory=command.CATEGORY_HELP,
2483 norepo=True,
2481 norepo=True,
2484 )
2482 )
2485 def debugcomplete(ui, cmd=b'', **opts):
2483 def debugcomplete(ui, cmd=b'', **opts):
2486 """returns the completion list associated with the given command"""
2484 """returns the completion list associated with the given command"""
2487
2485
2488 if opts.get('options'):
2486 if opts.get('options'):
2489 options = []
2487 options = []
2490 otables = [globalopts]
2488 otables = [globalopts]
2491 if cmd:
2489 if cmd:
2492 aliases, entry = cmdutil.findcmd(cmd, table, False)
2490 aliases, entry = cmdutil.findcmd(cmd, table, False)
2493 otables.append(entry[1])
2491 otables.append(entry[1])
2494 for t in otables:
2492 for t in otables:
2495 for o in t:
2493 for o in t:
2496 if b"(DEPRECATED)" in o[3]:
2494 if b"(DEPRECATED)" in o[3]:
2497 continue
2495 continue
2498 if o[0]:
2496 if o[0]:
2499 options.append(b'-%s' % o[0])
2497 options.append(b'-%s' % o[0])
2500 options.append(b'--%s' % o[1])
2498 options.append(b'--%s' % o[1])
2501 ui.write(b"%s\n" % b"\n".join(options))
2499 ui.write(b"%s\n" % b"\n".join(options))
2502 return
2500 return
2503
2501
2504 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2502 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2505 if ui.verbose:
2503 if ui.verbose:
2506 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2504 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2507 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2505 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2508
2506
2509
2507
2510 @command(
2508 @command(
2511 b'diff',
2509 b'diff',
2512 [
2510 [
2513 (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2511 (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2514 (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2512 (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2515 (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2513 (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2516 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2514 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2517 ]
2515 ]
2518 + diffopts
2516 + diffopts
2519 + diffopts2
2517 + diffopts2
2520 + walkopts
2518 + walkopts
2521 + subrepoopts,
2519 + subrepoopts,
2522 _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2520 _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2523 helpcategory=command.CATEGORY_FILE_CONTENTS,
2521 helpcategory=command.CATEGORY_FILE_CONTENTS,
2524 helpbasic=True,
2522 helpbasic=True,
2525 inferrepo=True,
2523 inferrepo=True,
2526 intents={INTENT_READONLY},
2524 intents={INTENT_READONLY},
2527 )
2525 )
2528 def diff(ui, repo, *pats, **opts):
2526 def diff(ui, repo, *pats, **opts):
2529 """diff repository (or selected files)
2527 """diff repository (or selected files)
2530
2528
2531 Show differences between revisions for the specified files.
2529 Show differences between revisions for the specified files.
2532
2530
2533 Differences between files are shown using the unified diff format.
2531 Differences between files are shown using the unified diff format.
2534
2532
2535 .. note::
2533 .. note::
2536
2534
2537 :hg:`diff` may generate unexpected results for merges, as it will
2535 :hg:`diff` may generate unexpected results for merges, as it will
2538 default to comparing against the working directory's first
2536 default to comparing against the working directory's first
2539 parent changeset if no revisions are specified. To diff against the
2537 parent changeset if no revisions are specified. To diff against the
2540 conflict regions, you can use `--config diff.merge=yes`.
2538 conflict regions, you can use `--config diff.merge=yes`.
2541
2539
2542 By default, the working directory files are compared to its first parent. To
2540 By default, the working directory files are compared to its first parent. To
2543 see the differences from another revision, use --from. To see the difference
2541 see the differences from another revision, use --from. To see the difference
2544 to another revision, use --to. For example, :hg:`diff --from .^` will show
2542 to another revision, use --to. For example, :hg:`diff --from .^` will show
2545 the differences from the working copy's grandparent to the working copy,
2543 the differences from the working copy's grandparent to the working copy,
2546 :hg:`diff --to .` will show the diff from the working copy to its parent
2544 :hg:`diff --to .` will show the diff from the working copy to its parent
2547 (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2545 (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2548 show the diff between those two revisions.
2546 show the diff between those two revisions.
2549
2547
2550 Alternatively you can specify -c/--change with a revision to see the changes
2548 Alternatively you can specify -c/--change with a revision to see the changes
2551 in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2549 in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2552 equivalent to :hg:`diff --from 42^ --to 42`)
2550 equivalent to :hg:`diff --from 42^ --to 42`)
2553
2551
2554 Without the -a/--text option, diff will avoid generating diffs of
2552 Without the -a/--text option, diff will avoid generating diffs of
2555 files it detects as binary. With -a, diff will generate a diff
2553 files it detects as binary. With -a, diff will generate a diff
2556 anyway, probably with undesirable results.
2554 anyway, probably with undesirable results.
2557
2555
2558 Use the -g/--git option to generate diffs in the git extended diff
2556 Use the -g/--git option to generate diffs in the git extended diff
2559 format. For more information, read :hg:`help diffs`.
2557 format. For more information, read :hg:`help diffs`.
2560
2558
2561 .. container:: verbose
2559 .. container:: verbose
2562
2560
2563 Examples:
2561 Examples:
2564
2562
2565 - compare a file in the current working directory to its parent::
2563 - compare a file in the current working directory to its parent::
2566
2564
2567 hg diff foo.c
2565 hg diff foo.c
2568
2566
2569 - compare two historical versions of a directory, with rename info::
2567 - compare two historical versions of a directory, with rename info::
2570
2568
2571 hg diff --git --from 1.0 --to 1.2 lib/
2569 hg diff --git --from 1.0 --to 1.2 lib/
2572
2570
2573 - get change stats relative to the last change on some date::
2571 - get change stats relative to the last change on some date::
2574
2572
2575 hg diff --stat --from "date('may 2')"
2573 hg diff --stat --from "date('may 2')"
2576
2574
2577 - diff all newly-added files that contain a keyword::
2575 - diff all newly-added files that contain a keyword::
2578
2576
2579 hg diff "set:added() and grep(GNU)"
2577 hg diff "set:added() and grep(GNU)"
2580
2578
2581 - compare a revision and its parents::
2579 - compare a revision and its parents::
2582
2580
2583 hg diff -c 9353 # compare against first parent
2581 hg diff -c 9353 # compare against first parent
2584 hg diff --from 9353^ --to 9353 # same using revset syntax
2582 hg diff --from 9353^ --to 9353 # same using revset syntax
2585 hg diff --from 9353^2 --to 9353 # compare against the second parent
2583 hg diff --from 9353^2 --to 9353 # compare against the second parent
2586
2584
2587 Returns 0 on success.
2585 Returns 0 on success.
2588 """
2586 """
2589
2587
2590 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2588 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2591 opts = pycompat.byteskwargs(opts)
2589 opts = pycompat.byteskwargs(opts)
2592 revs = opts.get(b'rev')
2590 revs = opts.get(b'rev')
2593 change = opts.get(b'change')
2591 change = opts.get(b'change')
2594 from_rev = opts.get(b'from')
2592 from_rev = opts.get(b'from')
2595 to_rev = opts.get(b'to')
2593 to_rev = opts.get(b'to')
2596 stat = opts.get(b'stat')
2594 stat = opts.get(b'stat')
2597 reverse = opts.get(b'reverse')
2595 reverse = opts.get(b'reverse')
2598
2596
2599 cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2597 cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2600 cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2598 cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2601 if change:
2599 if change:
2602 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2600 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2603 ctx2 = logcmdutil.revsingle(repo, change, None)
2601 ctx2 = logcmdutil.revsingle(repo, change, None)
2604 ctx1 = logcmdutil.diff_parent(ctx2)
2602 ctx1 = logcmdutil.diff_parent(ctx2)
2605 elif from_rev or to_rev:
2603 elif from_rev or to_rev:
2606 repo = scmutil.unhidehashlikerevs(
2604 repo = scmutil.unhidehashlikerevs(
2607 repo, [from_rev] + [to_rev], b'nowarn'
2605 repo, [from_rev] + [to_rev], b'nowarn'
2608 )
2606 )
2609 ctx1 = logcmdutil.revsingle(repo, from_rev, None)
2607 ctx1 = logcmdutil.revsingle(repo, from_rev, None)
2610 ctx2 = logcmdutil.revsingle(repo, to_rev, None)
2608 ctx2 = logcmdutil.revsingle(repo, to_rev, None)
2611 else:
2609 else:
2612 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2610 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2613 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
2611 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
2614
2612
2615 if reverse:
2613 if reverse:
2616 ctxleft = ctx2
2614 ctxleft = ctx2
2617 ctxright = ctx1
2615 ctxright = ctx1
2618 else:
2616 else:
2619 ctxleft = ctx1
2617 ctxleft = ctx1
2620 ctxright = ctx2
2618 ctxright = ctx2
2621
2619
2622 diffopts = patch.diffallopts(ui, opts)
2620 diffopts = patch.diffallopts(ui, opts)
2623 m = scmutil.match(ctx2, pats, opts)
2621 m = scmutil.match(ctx2, pats, opts)
2624 m = repo.narrowmatch(m)
2622 m = repo.narrowmatch(m)
2625 ui.pager(b'diff')
2623 ui.pager(b'diff')
2626 logcmdutil.diffordiffstat(
2624 logcmdutil.diffordiffstat(
2627 ui,
2625 ui,
2628 repo,
2626 repo,
2629 diffopts,
2627 diffopts,
2630 ctxleft,
2628 ctxleft,
2631 ctxright,
2629 ctxright,
2632 m,
2630 m,
2633 stat=stat,
2631 stat=stat,
2634 listsubrepos=opts.get(b'subrepos'),
2632 listsubrepos=opts.get(b'subrepos'),
2635 root=opts.get(b'root'),
2633 root=opts.get(b'root'),
2636 )
2634 )
2637
2635
2638
2636
2639 @command(
2637 @command(
2640 b'export',
2638 b'export',
2641 [
2639 [
2642 (
2640 (
2643 b'B',
2641 b'B',
2644 b'bookmark',
2642 b'bookmark',
2645 b'',
2643 b'',
2646 _(b'export changes only reachable by given bookmark'),
2644 _(b'export changes only reachable by given bookmark'),
2647 _(b'BOOKMARK'),
2645 _(b'BOOKMARK'),
2648 ),
2646 ),
2649 (
2647 (
2650 b'o',
2648 b'o',
2651 b'output',
2649 b'output',
2652 b'',
2650 b'',
2653 _(b'print output to file with formatted name'),
2651 _(b'print output to file with formatted name'),
2654 _(b'FORMAT'),
2652 _(b'FORMAT'),
2655 ),
2653 ),
2656 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2654 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2657 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2655 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2658 ]
2656 ]
2659 + diffopts
2657 + diffopts
2660 + formatteropts,
2658 + formatteropts,
2661 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2659 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2662 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2660 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2663 helpbasic=True,
2661 helpbasic=True,
2664 intents={INTENT_READONLY},
2662 intents={INTENT_READONLY},
2665 )
2663 )
2666 def export(ui, repo, *changesets, **opts):
2664 def export(ui, repo, *changesets, **opts):
2667 """dump the header and diffs for one or more changesets
2665 """dump the header and diffs for one or more changesets
2668
2666
2669 Print the changeset header and diffs for one or more revisions.
2667 Print the changeset header and diffs for one or more revisions.
2670 If no revision is given, the parent of the working directory is used.
2668 If no revision is given, the parent of the working directory is used.
2671
2669
2672 The information shown in the changeset header is: author, date,
2670 The information shown in the changeset header is: author, date,
2673 branch name (if non-default), changeset hash, parent(s) and commit
2671 branch name (if non-default), changeset hash, parent(s) and commit
2674 comment.
2672 comment.
2675
2673
2676 .. note::
2674 .. note::
2677
2675
2678 :hg:`export` may generate unexpected diff output for merge
2676 :hg:`export` may generate unexpected diff output for merge
2679 changesets, as it will compare the merge changeset against its
2677 changesets, as it will compare the merge changeset against its
2680 first parent only.
2678 first parent only.
2681
2679
2682 Output may be to a file, in which case the name of the file is
2680 Output may be to a file, in which case the name of the file is
2683 given using a template string. See :hg:`help templates`. In addition
2681 given using a template string. See :hg:`help templates`. In addition
2684 to the common template keywords, the following formatting rules are
2682 to the common template keywords, the following formatting rules are
2685 supported:
2683 supported:
2686
2684
2687 :``%%``: literal "%" character
2685 :``%%``: literal "%" character
2688 :``%H``: changeset hash (40 hexadecimal digits)
2686 :``%H``: changeset hash (40 hexadecimal digits)
2689 :``%N``: number of patches being generated
2687 :``%N``: number of patches being generated
2690 :``%R``: changeset revision number
2688 :``%R``: changeset revision number
2691 :``%b``: basename of the exporting repository
2689 :``%b``: basename of the exporting repository
2692 :``%h``: short-form changeset hash (12 hexadecimal digits)
2690 :``%h``: short-form changeset hash (12 hexadecimal digits)
2693 :``%m``: first line of the commit message (only alphanumeric characters)
2691 :``%m``: first line of the commit message (only alphanumeric characters)
2694 :``%n``: zero-padded sequence number, starting at 1
2692 :``%n``: zero-padded sequence number, starting at 1
2695 :``%r``: zero-padded changeset revision number
2693 :``%r``: zero-padded changeset revision number
2696 :``\\``: literal "\\" character
2694 :``\\``: literal "\\" character
2697
2695
2698 Without the -a/--text option, export will avoid generating diffs
2696 Without the -a/--text option, export will avoid generating diffs
2699 of files it detects as binary. With -a, export will generate a
2697 of files it detects as binary. With -a, export will generate a
2700 diff anyway, probably with undesirable results.
2698 diff anyway, probably with undesirable results.
2701
2699
2702 With -B/--bookmark changesets reachable by the given bookmark are
2700 With -B/--bookmark changesets reachable by the given bookmark are
2703 selected.
2701 selected.
2704
2702
2705 Use the -g/--git option to generate diffs in the git extended diff
2703 Use the -g/--git option to generate diffs in the git extended diff
2706 format. See :hg:`help diffs` for more information.
2704 format. See :hg:`help diffs` for more information.
2707
2705
2708 With the --switch-parent option, the diff will be against the
2706 With the --switch-parent option, the diff will be against the
2709 second parent. It can be useful to review a merge.
2707 second parent. It can be useful to review a merge.
2710
2708
2711 .. container:: verbose
2709 .. container:: verbose
2712
2710
2713 Template:
2711 Template:
2714
2712
2715 The following keywords are supported in addition to the common template
2713 The following keywords are supported in addition to the common template
2716 keywords and functions. See also :hg:`help templates`.
2714 keywords and functions. See also :hg:`help templates`.
2717
2715
2718 :diff: String. Diff content.
2716 :diff: String. Diff content.
2719 :parents: List of strings. Parent nodes of the changeset.
2717 :parents: List of strings. Parent nodes of the changeset.
2720
2718
2721 Examples:
2719 Examples:
2722
2720
2723 - use export and import to transplant a bugfix to the current
2721 - use export and import to transplant a bugfix to the current
2724 branch::
2722 branch::
2725
2723
2726 hg export -r 9353 | hg import -
2724 hg export -r 9353 | hg import -
2727
2725
2728 - export all the changesets between two revisions to a file with
2726 - export all the changesets between two revisions to a file with
2729 rename information::
2727 rename information::
2730
2728
2731 hg export --git -r 123:150 > changes.txt
2729 hg export --git -r 123:150 > changes.txt
2732
2730
2733 - split outgoing changes into a series of patches with
2731 - split outgoing changes into a series of patches with
2734 descriptive names::
2732 descriptive names::
2735
2733
2736 hg export -r "outgoing()" -o "%n-%m.patch"
2734 hg export -r "outgoing()" -o "%n-%m.patch"
2737
2735
2738 Returns 0 on success.
2736 Returns 0 on success.
2739 """
2737 """
2740 opts = pycompat.byteskwargs(opts)
2738 opts = pycompat.byteskwargs(opts)
2741 bookmark = opts.get(b'bookmark')
2739 bookmark = opts.get(b'bookmark')
2742 changesets += tuple(opts.get(b'rev', []))
2740 changesets += tuple(opts.get(b'rev', []))
2743
2741
2744 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2742 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2745
2743
2746 if bookmark:
2744 if bookmark:
2747 if bookmark not in repo._bookmarks:
2745 if bookmark not in repo._bookmarks:
2748 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2746 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2749
2747
2750 revs = scmutil.bookmarkrevs(repo, bookmark)
2748 revs = scmutil.bookmarkrevs(repo, bookmark)
2751 else:
2749 else:
2752 if not changesets:
2750 if not changesets:
2753 changesets = [b'.']
2751 changesets = [b'.']
2754
2752
2755 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2753 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2756 revs = logcmdutil.revrange(repo, changesets)
2754 revs = logcmdutil.revrange(repo, changesets)
2757
2755
2758 if not revs:
2756 if not revs:
2759 raise error.InputError(_(b"export requires at least one changeset"))
2757 raise error.InputError(_(b"export requires at least one changeset"))
2760 if len(revs) > 1:
2758 if len(revs) > 1:
2761 ui.note(_(b'exporting patches:\n'))
2759 ui.note(_(b'exporting patches:\n'))
2762 else:
2760 else:
2763 ui.note(_(b'exporting patch:\n'))
2761 ui.note(_(b'exporting patch:\n'))
2764
2762
2765 fntemplate = opts.get(b'output')
2763 fntemplate = opts.get(b'output')
2766 if cmdutil.isstdiofilename(fntemplate):
2764 if cmdutil.isstdiofilename(fntemplate):
2767 fntemplate = b''
2765 fntemplate = b''
2768
2766
2769 if fntemplate:
2767 if fntemplate:
2770 fm = formatter.nullformatter(ui, b'export', opts)
2768 fm = formatter.nullformatter(ui, b'export', opts)
2771 else:
2769 else:
2772 ui.pager(b'export')
2770 ui.pager(b'export')
2773 fm = ui.formatter(b'export', opts)
2771 fm = ui.formatter(b'export', opts)
2774 with fm:
2772 with fm:
2775 cmdutil.export(
2773 cmdutil.export(
2776 repo,
2774 repo,
2777 revs,
2775 revs,
2778 fm,
2776 fm,
2779 fntemplate=fntemplate,
2777 fntemplate=fntemplate,
2780 switch_parent=opts.get(b'switch_parent'),
2778 switch_parent=opts.get(b'switch_parent'),
2781 opts=patch.diffallopts(ui, opts),
2779 opts=patch.diffallopts(ui, opts),
2782 )
2780 )
2783
2781
2784
2782
2785 @command(
2783 @command(
2786 b'files',
2784 b'files',
2787 [
2785 [
2788 (
2786 (
2789 b'r',
2787 b'r',
2790 b'rev',
2788 b'rev',
2791 b'',
2789 b'',
2792 _(b'search the repository as it is in REV'),
2790 _(b'search the repository as it is in REV'),
2793 _(b'REV'),
2791 _(b'REV'),
2794 ),
2792 ),
2795 (
2793 (
2796 b'0',
2794 b'0',
2797 b'print0',
2795 b'print0',
2798 None,
2796 None,
2799 _(b'end filenames with NUL, for use with xargs'),
2797 _(b'end filenames with NUL, for use with xargs'),
2800 ),
2798 ),
2801 ]
2799 ]
2802 + walkopts
2800 + walkopts
2803 + formatteropts
2801 + formatteropts
2804 + subrepoopts,
2802 + subrepoopts,
2805 _(b'[OPTION]... [FILE]...'),
2803 _(b'[OPTION]... [FILE]...'),
2806 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2804 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2807 intents={INTENT_READONLY},
2805 intents={INTENT_READONLY},
2808 )
2806 )
2809 def files(ui, repo, *pats, **opts):
2807 def files(ui, repo, *pats, **opts):
2810 """list tracked files
2808 """list tracked files
2811
2809
2812 Print files under Mercurial control in the working directory or
2810 Print files under Mercurial control in the working directory or
2813 specified revision for given files (excluding removed files).
2811 specified revision for given files (excluding removed files).
2814 Files can be specified as filenames or filesets.
2812 Files can be specified as filenames or filesets.
2815
2813
2816 If no files are given to match, this command prints the names
2814 If no files are given to match, this command prints the names
2817 of all files under Mercurial control.
2815 of all files under Mercurial control.
2818
2816
2819 .. container:: verbose
2817 .. container:: verbose
2820
2818
2821 Template:
2819 Template:
2822
2820
2823 The following keywords are supported in addition to the common template
2821 The following keywords are supported in addition to the common template
2824 keywords and functions. See also :hg:`help templates`.
2822 keywords and functions. See also :hg:`help templates`.
2825
2823
2826 :flags: String. Character denoting file's symlink and executable bits.
2824 :flags: String. Character denoting file's symlink and executable bits.
2827 :path: String. Repository-absolute path of the file.
2825 :path: String. Repository-absolute path of the file.
2828 :size: Integer. Size of the file in bytes.
2826 :size: Integer. Size of the file in bytes.
2829
2827
2830 Examples:
2828 Examples:
2831
2829
2832 - list all files under the current directory::
2830 - list all files under the current directory::
2833
2831
2834 hg files .
2832 hg files .
2835
2833
2836 - shows sizes and flags for current revision::
2834 - shows sizes and flags for current revision::
2837
2835
2838 hg files -vr .
2836 hg files -vr .
2839
2837
2840 - list all files named README::
2838 - list all files named README::
2841
2839
2842 hg files -I "**/README"
2840 hg files -I "**/README"
2843
2841
2844 - list all binary files::
2842 - list all binary files::
2845
2843
2846 hg files "set:binary()"
2844 hg files "set:binary()"
2847
2845
2848 - find files containing a regular expression::
2846 - find files containing a regular expression::
2849
2847
2850 hg files "set:grep('bob')"
2848 hg files "set:grep('bob')"
2851
2849
2852 - search tracked file contents with xargs and grep::
2850 - search tracked file contents with xargs and grep::
2853
2851
2854 hg files -0 | xargs -0 grep foo
2852 hg files -0 | xargs -0 grep foo
2855
2853
2856 See :hg:`help patterns` and :hg:`help filesets` for more information
2854 See :hg:`help patterns` and :hg:`help filesets` for more information
2857 on specifying file patterns.
2855 on specifying file patterns.
2858
2856
2859 Returns 0 if a match is found, 1 otherwise.
2857 Returns 0 if a match is found, 1 otherwise.
2860
2858
2861 """
2859 """
2862
2860
2863 opts = pycompat.byteskwargs(opts)
2861 opts = pycompat.byteskwargs(opts)
2864 rev = opts.get(b'rev')
2862 rev = opts.get(b'rev')
2865 if rev:
2863 if rev:
2866 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2864 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2867 ctx = logcmdutil.revsingle(repo, rev, None)
2865 ctx = logcmdutil.revsingle(repo, rev, None)
2868
2866
2869 end = b'\n'
2867 end = b'\n'
2870 if opts.get(b'print0'):
2868 if opts.get(b'print0'):
2871 end = b'\0'
2869 end = b'\0'
2872 fmt = b'%s' + end
2870 fmt = b'%s' + end
2873
2871
2874 m = scmutil.match(ctx, pats, opts)
2872 m = scmutil.match(ctx, pats, opts)
2875 ui.pager(b'files')
2873 ui.pager(b'files')
2876 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2874 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2877 with ui.formatter(b'files', opts) as fm:
2875 with ui.formatter(b'files', opts) as fm:
2878 return cmdutil.files(
2876 return cmdutil.files(
2879 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2877 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2880 )
2878 )
2881
2879
2882
2880
2883 @command(
2881 @command(
2884 b'forget',
2882 b'forget',
2885 [
2883 [
2886 (b'i', b'interactive', None, _(b'use interactive mode')),
2884 (b'i', b'interactive', None, _(b'use interactive mode')),
2887 ]
2885 ]
2888 + walkopts
2886 + walkopts
2889 + dryrunopts,
2887 + dryrunopts,
2890 _(b'[OPTION]... FILE...'),
2888 _(b'[OPTION]... FILE...'),
2891 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2889 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2892 helpbasic=True,
2890 helpbasic=True,
2893 inferrepo=True,
2891 inferrepo=True,
2894 )
2892 )
2895 def forget(ui, repo, *pats, **opts):
2893 def forget(ui, repo, *pats, **opts):
2896 """forget the specified files on the next commit
2894 """forget the specified files on the next commit
2897
2895
2898 Mark the specified files so they will no longer be tracked
2896 Mark the specified files so they will no longer be tracked
2899 after the next commit.
2897 after the next commit.
2900
2898
2901 This only removes files from the current branch, not from the
2899 This only removes files from the current branch, not from the
2902 entire project history, and it does not delete them from the
2900 entire project history, and it does not delete them from the
2903 working directory.
2901 working directory.
2904
2902
2905 To delete the file from the working directory, see :hg:`remove`.
2903 To delete the file from the working directory, see :hg:`remove`.
2906
2904
2907 To undo a forget before the next commit, see :hg:`add`.
2905 To undo a forget before the next commit, see :hg:`add`.
2908
2906
2909 .. container:: verbose
2907 .. container:: verbose
2910
2908
2911 Examples:
2909 Examples:
2912
2910
2913 - forget newly-added binary files::
2911 - forget newly-added binary files::
2914
2912
2915 hg forget "set:added() and binary()"
2913 hg forget "set:added() and binary()"
2916
2914
2917 - forget files that would be excluded by .hgignore::
2915 - forget files that would be excluded by .hgignore::
2918
2916
2919 hg forget "set:hgignore()"
2917 hg forget "set:hgignore()"
2920
2918
2921 Returns 0 on success.
2919 Returns 0 on success.
2922 """
2920 """
2923
2921
2924 opts = pycompat.byteskwargs(opts)
2922 opts = pycompat.byteskwargs(opts)
2925 if not pats:
2923 if not pats:
2926 raise error.InputError(_(b'no files specified'))
2924 raise error.InputError(_(b'no files specified'))
2927
2925
2928 m = scmutil.match(repo[None], pats, opts)
2926 m = scmutil.match(repo[None], pats, opts)
2929 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2927 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2930 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2928 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2931 rejected = cmdutil.forget(
2929 rejected = cmdutil.forget(
2932 ui,
2930 ui,
2933 repo,
2931 repo,
2934 m,
2932 m,
2935 prefix=b"",
2933 prefix=b"",
2936 uipathfn=uipathfn,
2934 uipathfn=uipathfn,
2937 explicitonly=False,
2935 explicitonly=False,
2938 dryrun=dryrun,
2936 dryrun=dryrun,
2939 interactive=interactive,
2937 interactive=interactive,
2940 )[0]
2938 )[0]
2941 return rejected and 1 or 0
2939 return rejected and 1 or 0
2942
2940
2943
2941
2944 @command(
2942 @command(
2945 b'graft',
2943 b'graft',
2946 [
2944 [
2947 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2945 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2948 (
2946 (
2949 b'',
2947 b'',
2950 b'base',
2948 b'base',
2951 b'',
2949 b'',
2952 _(b'base revision when doing the graft merge (ADVANCED)'),
2950 _(b'base revision when doing the graft merge (ADVANCED)'),
2953 _(b'REV'),
2951 _(b'REV'),
2954 ),
2952 ),
2955 (b'c', b'continue', False, _(b'resume interrupted graft')),
2953 (b'c', b'continue', False, _(b'resume interrupted graft')),
2956 (b'', b'stop', False, _(b'stop interrupted graft')),
2954 (b'', b'stop', False, _(b'stop interrupted graft')),
2957 (b'', b'abort', False, _(b'abort interrupted graft')),
2955 (b'', b'abort', False, _(b'abort interrupted graft')),
2958 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2956 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2959 (b'', b'log', None, _(b'append graft info to log message')),
2957 (b'', b'log', None, _(b'append graft info to log message')),
2960 (
2958 (
2961 b'',
2959 b'',
2962 b'no-commit',
2960 b'no-commit',
2963 None,
2961 None,
2964 _(b"don't commit, just apply the changes in working directory"),
2962 _(b"don't commit, just apply the changes in working directory"),
2965 ),
2963 ),
2966 (b'f', b'force', False, _(b'force graft')),
2964 (b'f', b'force', False, _(b'force graft')),
2967 (
2965 (
2968 b'D',
2966 b'D',
2969 b'currentdate',
2967 b'currentdate',
2970 False,
2968 False,
2971 _(b'record the current date as commit date'),
2969 _(b'record the current date as commit date'),
2972 ),
2970 ),
2973 (
2971 (
2974 b'U',
2972 b'U',
2975 b'currentuser',
2973 b'currentuser',
2976 False,
2974 False,
2977 _(b'record the current user as committer'),
2975 _(b'record the current user as committer'),
2978 ),
2976 ),
2979 ]
2977 ]
2980 + commitopts2
2978 + commitopts2
2981 + mergetoolopts
2979 + mergetoolopts
2982 + dryrunopts,
2980 + dryrunopts,
2983 _(b'[OPTION]... [-r REV]... REV...'),
2981 _(b'[OPTION]... [-r REV]... REV...'),
2984 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2982 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2985 )
2983 )
2986 def graft(ui, repo, *revs, **opts):
2984 def graft(ui, repo, *revs, **opts):
2987 """copy changes from other branches onto the current branch
2985 """copy changes from other branches onto the current branch
2988
2986
2989 This command uses Mercurial's merge logic to copy individual
2987 This command uses Mercurial's merge logic to copy individual
2990 changes from other branches without merging branches in the
2988 changes from other branches without merging branches in the
2991 history graph. This is sometimes known as 'backporting' or
2989 history graph. This is sometimes known as 'backporting' or
2992 'cherry-picking'. By default, graft will copy user, date, and
2990 'cherry-picking'. By default, graft will copy user, date, and
2993 description from the source changesets.
2991 description from the source changesets.
2994
2992
2995 Changesets that are ancestors of the current revision, that have
2993 Changesets that are ancestors of the current revision, that have
2996 already been grafted, or that are merges will be skipped.
2994 already been grafted, or that are merges will be skipped.
2997
2995
2998 If --log is specified, log messages will have a comment appended
2996 If --log is specified, log messages will have a comment appended
2999 of the form::
2997 of the form::
3000
2998
3001 (grafted from CHANGESETHASH)
2999 (grafted from CHANGESETHASH)
3002
3000
3003 If --force is specified, revisions will be grafted even if they
3001 If --force is specified, revisions will be grafted even if they
3004 are already ancestors of, or have been grafted to, the destination.
3002 are already ancestors of, or have been grafted to, the destination.
3005 This is useful when the revisions have since been backed out.
3003 This is useful when the revisions have since been backed out.
3006
3004
3007 If a graft merge results in conflicts, the graft process is
3005 If a graft merge results in conflicts, the graft process is
3008 interrupted so that the current merge can be manually resolved.
3006 interrupted so that the current merge can be manually resolved.
3009 Once all conflicts are addressed, the graft process can be
3007 Once all conflicts are addressed, the graft process can be
3010 continued with the -c/--continue option.
3008 continued with the -c/--continue option.
3011
3009
3012 The -c/--continue option reapplies all the earlier options.
3010 The -c/--continue option reapplies all the earlier options.
3013
3011
3014 .. container:: verbose
3012 .. container:: verbose
3015
3013
3016 The --base option exposes more of how graft internally uses merge with a
3014 The --base option exposes more of how graft internally uses merge with a
3017 custom base revision. --base can be used to specify another ancestor than
3015 custom base revision. --base can be used to specify another ancestor than
3018 the first and only parent.
3016 the first and only parent.
3019
3017
3020 The command::
3018 The command::
3021
3019
3022 hg graft -r 345 --base 234
3020 hg graft -r 345 --base 234
3023
3021
3024 is thus pretty much the same as::
3022 is thus pretty much the same as::
3025
3023
3026 hg diff --from 234 --to 345 | hg import
3024 hg diff --from 234 --to 345 | hg import
3027
3025
3028 but using merge to resolve conflicts and track moved files.
3026 but using merge to resolve conflicts and track moved files.
3029
3027
3030 The result of a merge can thus be backported as a single commit by
3028 The result of a merge can thus be backported as a single commit by
3031 specifying one of the merge parents as base, and thus effectively
3029 specifying one of the merge parents as base, and thus effectively
3032 grafting the changes from the other side.
3030 grafting the changes from the other side.
3033
3031
3034 It is also possible to collapse multiple changesets and clean up history
3032 It is also possible to collapse multiple changesets and clean up history
3035 by specifying another ancestor as base, much like rebase --collapse
3033 by specifying another ancestor as base, much like rebase --collapse
3036 --keep.
3034 --keep.
3037
3035
3038 The commit message can be tweaked after the fact using commit --amend .
3036 The commit message can be tweaked after the fact using commit --amend .
3039
3037
3040 For using non-ancestors as the base to backout changes, see the backout
3038 For using non-ancestors as the base to backout changes, see the backout
3041 command and the hidden --parent option.
3039 command and the hidden --parent option.
3042
3040
3043 .. container:: verbose
3041 .. container:: verbose
3044
3042
3045 Examples:
3043 Examples:
3046
3044
3047 - copy a single change to the stable branch and edit its description::
3045 - copy a single change to the stable branch and edit its description::
3048
3046
3049 hg update stable
3047 hg update stable
3050 hg graft --edit 9393
3048 hg graft --edit 9393
3051
3049
3052 - graft a range of changesets with one exception, updating dates::
3050 - graft a range of changesets with one exception, updating dates::
3053
3051
3054 hg graft -D "2085::2093 and not 2091"
3052 hg graft -D "2085::2093 and not 2091"
3055
3053
3056 - continue a graft after resolving conflicts::
3054 - continue a graft after resolving conflicts::
3057
3055
3058 hg graft -c
3056 hg graft -c
3059
3057
3060 - show the source of a grafted changeset::
3058 - show the source of a grafted changeset::
3061
3059
3062 hg log --debug -r .
3060 hg log --debug -r .
3063
3061
3064 - show revisions sorted by date::
3062 - show revisions sorted by date::
3065
3063
3066 hg log -r "sort(all(), date)"
3064 hg log -r "sort(all(), date)"
3067
3065
3068 - backport the result of a merge as a single commit::
3066 - backport the result of a merge as a single commit::
3069
3067
3070 hg graft -r 123 --base 123^
3068 hg graft -r 123 --base 123^
3071
3069
3072 - land a feature branch as one changeset::
3070 - land a feature branch as one changeset::
3073
3071
3074 hg up -cr default
3072 hg up -cr default
3075 hg graft -r featureX --base "ancestor('featureX', 'default')"
3073 hg graft -r featureX --base "ancestor('featureX', 'default')"
3076
3074
3077 See :hg:`help revisions` for more about specifying revisions.
3075 See :hg:`help revisions` for more about specifying revisions.
3078
3076
3079 Returns 0 on successful completion, 1 if there are unresolved files.
3077 Returns 0 on successful completion, 1 if there are unresolved files.
3080 """
3078 """
3081 with repo.wlock():
3079 with repo.wlock():
3082 return _dograft(ui, repo, *revs, **opts)
3080 return _dograft(ui, repo, *revs, **opts)
3083
3081
3084
3082
3085 def _dograft(ui, repo, *revs, **opts):
3083 def _dograft(ui, repo, *revs, **opts):
3086 if revs and opts.get('rev'):
3084 if revs and opts.get('rev'):
3087 ui.warn(
3085 ui.warn(
3088 _(
3086 _(
3089 b'warning: inconsistent use of --rev might give unexpected '
3087 b'warning: inconsistent use of --rev might give unexpected '
3090 b'revision ordering!\n'
3088 b'revision ordering!\n'
3091 )
3089 )
3092 )
3090 )
3093
3091
3094 revs = list(revs)
3092 revs = list(revs)
3095 revs.extend(opts.get('rev'))
3093 revs.extend(opts.get('rev'))
3096 # a dict of data to be stored in state file
3094 # a dict of data to be stored in state file
3097 statedata = {}
3095 statedata = {}
3098 # list of new nodes created by ongoing graft
3096 # list of new nodes created by ongoing graft
3099 statedata[b'newnodes'] = []
3097 statedata[b'newnodes'] = []
3100
3098
3101 cmdutil.resolve_commit_options(ui, opts)
3099 cmdutil.resolve_commit_options(ui, opts)
3102
3100
3103 editor = cmdutil.getcommiteditor(editform=b'graft', **opts)
3101 editor = cmdutil.getcommiteditor(editform=b'graft', **opts)
3104
3102
3105 cmdutil.check_at_most_one_arg(opts, 'abort', 'stop', 'continue')
3103 cmdutil.check_at_most_one_arg(opts, 'abort', 'stop', 'continue')
3106
3104
3107 cont = False
3105 cont = False
3108 if opts.get('no_commit'):
3106 if opts.get('no_commit'):
3109 cmdutil.check_incompatible_arguments(
3107 cmdutil.check_incompatible_arguments(
3110 opts,
3108 opts,
3111 'no_commit',
3109 'no_commit',
3112 ['edit', 'currentuser', 'currentdate', 'log'],
3110 ['edit', 'currentuser', 'currentdate', 'log'],
3113 )
3111 )
3114
3112
3115 graftstate = statemod.cmdstate(repo, b'graftstate')
3113 graftstate = statemod.cmdstate(repo, b'graftstate')
3116
3114
3117 if opts.get('stop'):
3115 if opts.get('stop'):
3118 cmdutil.check_incompatible_arguments(
3116 cmdutil.check_incompatible_arguments(
3119 opts,
3117 opts,
3120 'stop',
3118 'stop',
3121 [
3119 [
3122 'edit',
3120 'edit',
3123 'log',
3121 'log',
3124 'user',
3122 'user',
3125 'date',
3123 'date',
3126 'currentdate',
3124 'currentdate',
3127 'currentuser',
3125 'currentuser',
3128 'rev',
3126 'rev',
3129 ],
3127 ],
3130 )
3128 )
3131 return _stopgraft(ui, repo, graftstate)
3129 return _stopgraft(ui, repo, graftstate)
3132 elif opts.get('abort'):
3130 elif opts.get('abort'):
3133 cmdutil.check_incompatible_arguments(
3131 cmdutil.check_incompatible_arguments(
3134 opts,
3132 opts,
3135 'abort',
3133 'abort',
3136 [
3134 [
3137 'edit',
3135 'edit',
3138 'log',
3136 'log',
3139 'user',
3137 'user',
3140 'date',
3138 'date',
3141 'currentdate',
3139 'currentdate',
3142 'currentuser',
3140 'currentuser',
3143 'rev',
3141 'rev',
3144 ],
3142 ],
3145 )
3143 )
3146 return cmdutil.abortgraft(ui, repo, graftstate)
3144 return cmdutil.abortgraft(ui, repo, graftstate)
3147 elif opts.get('continue'):
3145 elif opts.get('continue'):
3148 cont = True
3146 cont = True
3149 if revs:
3147 if revs:
3150 raise error.InputError(_(b"can't specify --continue and revisions"))
3148 raise error.InputError(_(b"can't specify --continue and revisions"))
3151 # read in unfinished revisions
3149 # read in unfinished revisions
3152 if graftstate.exists():
3150 if graftstate.exists():
3153 statedata = cmdutil.readgraftstate(repo, graftstate)
3151 statedata = cmdutil.readgraftstate(repo, graftstate)
3154 if statedata.get(b'date'):
3152 if statedata.get(b'date'):
3155 opts['date'] = statedata[b'date']
3153 opts['date'] = statedata[b'date']
3156 if statedata.get(b'user'):
3154 if statedata.get(b'user'):
3157 opts['user'] = statedata[b'user']
3155 opts['user'] = statedata[b'user']
3158 if statedata.get(b'log'):
3156 if statedata.get(b'log'):
3159 opts['log'] = True
3157 opts['log'] = True
3160 if statedata.get(b'no_commit'):
3158 if statedata.get(b'no_commit'):
3161 opts['no_commit'] = statedata.get(b'no_commit')
3159 opts['no_commit'] = statedata.get(b'no_commit')
3162 if statedata.get(b'base'):
3160 if statedata.get(b'base'):
3163 opts['base'] = statedata.get(b'base')
3161 opts['base'] = statedata.get(b'base')
3164 nodes = statedata[b'nodes']
3162 nodes = statedata[b'nodes']
3165 revs = [repo[node].rev() for node in nodes]
3163 revs = [repo[node].rev() for node in nodes]
3166 else:
3164 else:
3167 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3165 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3168 else:
3166 else:
3169 if not revs:
3167 if not revs:
3170 raise error.InputError(_(b'no revisions specified'))
3168 raise error.InputError(_(b'no revisions specified'))
3171 cmdutil.checkunfinished(repo)
3169 cmdutil.checkunfinished(repo)
3172 cmdutil.bailifchanged(repo)
3170 cmdutil.bailifchanged(repo)
3173 revs = logcmdutil.revrange(repo, revs)
3171 revs = logcmdutil.revrange(repo, revs)
3174
3172
3175 skipped = set()
3173 skipped = set()
3176 basectx = None
3174 basectx = None
3177 if opts.get('base'):
3175 if opts.get('base'):
3178 basectx = logcmdutil.revsingle(repo, opts['base'], None)
3176 basectx = logcmdutil.revsingle(repo, opts['base'], None)
3179 if basectx is None:
3177 if basectx is None:
3180 # check for merges
3178 # check for merges
3181 for rev in repo.revs(b'%ld and merge()', revs):
3179 for rev in repo.revs(b'%ld and merge()', revs):
3182 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3180 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3183 skipped.add(rev)
3181 skipped.add(rev)
3184 revs = [r for r in revs if r not in skipped]
3182 revs = [r for r in revs if r not in skipped]
3185 if not revs:
3183 if not revs:
3186 return -1
3184 return -1
3187 if basectx is not None and len(revs) != 1:
3185 if basectx is not None and len(revs) != 1:
3188 raise error.InputError(_(b'only one revision allowed with --base '))
3186 raise error.InputError(_(b'only one revision allowed with --base '))
3189
3187
3190 # Don't check in the --continue case, in effect retaining --force across
3188 # Don't check in the --continue case, in effect retaining --force across
3191 # --continues. That's because without --force, any revisions we decided to
3189 # --continues. That's because without --force, any revisions we decided to
3192 # skip would have been filtered out here, so they wouldn't have made their
3190 # skip would have been filtered out here, so they wouldn't have made their
3193 # way to the graftstate. With --force, any revisions we would have otherwise
3191 # way to the graftstate. With --force, any revisions we would have otherwise
3194 # skipped would not have been filtered out, and if they hadn't been applied
3192 # skipped would not have been filtered out, and if they hadn't been applied
3195 # already, they'd have been in the graftstate.
3193 # already, they'd have been in the graftstate.
3196 if not (cont or opts.get('force')) and basectx is None:
3194 if not (cont or opts.get('force')) and basectx is None:
3197 # check for ancestors of dest branch
3195 # check for ancestors of dest branch
3198 ancestors = repo.revs(b'%ld & (::.)', revs)
3196 ancestors = repo.revs(b'%ld & (::.)', revs)
3199 for rev in ancestors:
3197 for rev in ancestors:
3200 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3198 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3201
3199
3202 revs = [r for r in revs if r not in ancestors]
3200 revs = [r for r in revs if r not in ancestors]
3203
3201
3204 if not revs:
3202 if not revs:
3205 return -1
3203 return -1
3206
3204
3207 # analyze revs for earlier grafts
3205 # analyze revs for earlier grafts
3208 ids = {}
3206 ids = {}
3209 for ctx in repo.set(b"%ld", revs):
3207 for ctx in repo.set(b"%ld", revs):
3210 ids[ctx.hex()] = ctx.rev()
3208 ids[ctx.hex()] = ctx.rev()
3211 n = ctx.extra().get(b'source')
3209 n = ctx.extra().get(b'source')
3212 if n:
3210 if n:
3213 ids[n] = ctx.rev()
3211 ids[n] = ctx.rev()
3214
3212
3215 # check ancestors for earlier grafts
3213 # check ancestors for earlier grafts
3216 ui.debug(b'scanning for duplicate grafts\n')
3214 ui.debug(b'scanning for duplicate grafts\n')
3217
3215
3218 # The only changesets we can be sure doesn't contain grafts of any
3216 # The only changesets we can be sure doesn't contain grafts of any
3219 # revs, are the ones that are common ancestors of *all* revs:
3217 # revs, are the ones that are common ancestors of *all* revs:
3220 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3218 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3221 ctx = repo[rev]
3219 ctx = repo[rev]
3222 n = ctx.extra().get(b'source')
3220 n = ctx.extra().get(b'source')
3223 if n in ids:
3221 if n in ids:
3224 try:
3222 try:
3225 r = repo[n].rev()
3223 r = repo[n].rev()
3226 except error.RepoLookupError:
3224 except error.RepoLookupError:
3227 r = None
3225 r = None
3228 if r in revs:
3226 if r in revs:
3229 ui.warn(
3227 ui.warn(
3230 _(
3228 _(
3231 b'skipping revision %d:%s '
3229 b'skipping revision %d:%s '
3232 b'(already grafted to %d:%s)\n'
3230 b'(already grafted to %d:%s)\n'
3233 )
3231 )
3234 % (r, repo[r], rev, ctx)
3232 % (r, repo[r], rev, ctx)
3235 )
3233 )
3236 revs.remove(r)
3234 revs.remove(r)
3237 elif ids[n] in revs:
3235 elif ids[n] in revs:
3238 if r is None:
3236 if r is None:
3239 ui.warn(
3237 ui.warn(
3240 _(
3238 _(
3241 b'skipping already grafted revision %d:%s '
3239 b'skipping already grafted revision %d:%s '
3242 b'(%d:%s also has unknown origin %s)\n'
3240 b'(%d:%s also has unknown origin %s)\n'
3243 )
3241 )
3244 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3242 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3245 )
3243 )
3246 else:
3244 else:
3247 ui.warn(
3245 ui.warn(
3248 _(
3246 _(
3249 b'skipping already grafted revision %d:%s '
3247 b'skipping already grafted revision %d:%s '
3250 b'(%d:%s also has origin %d:%s)\n'
3248 b'(%d:%s also has origin %d:%s)\n'
3251 )
3249 )
3252 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3250 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3253 )
3251 )
3254 revs.remove(ids[n])
3252 revs.remove(ids[n])
3255 elif ctx.hex() in ids:
3253 elif ctx.hex() in ids:
3256 r = ids[ctx.hex()]
3254 r = ids[ctx.hex()]
3257 if r in revs:
3255 if r in revs:
3258 ui.warn(
3256 ui.warn(
3259 _(
3257 _(
3260 b'skipping already grafted revision %d:%s '
3258 b'skipping already grafted revision %d:%s '
3261 b'(was grafted from %d:%s)\n'
3259 b'(was grafted from %d:%s)\n'
3262 )
3260 )
3263 % (r, repo[r], rev, ctx)
3261 % (r, repo[r], rev, ctx)
3264 )
3262 )
3265 revs.remove(r)
3263 revs.remove(r)
3266 if not revs:
3264 if not revs:
3267 return -1
3265 return -1
3268
3266
3269 if opts.get('no_commit'):
3267 if opts.get('no_commit'):
3270 statedata[b'no_commit'] = True
3268 statedata[b'no_commit'] = True
3271 if opts.get('base'):
3269 if opts.get('base'):
3272 statedata[b'base'] = opts['base']
3270 statedata[b'base'] = opts['base']
3273 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3271 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3274 desc = b'%d:%s "%s"' % (
3272 desc = b'%d:%s "%s"' % (
3275 ctx.rev(),
3273 ctx.rev(),
3276 ctx,
3274 ctx,
3277 ctx.description().split(b'\n', 1)[0],
3275 ctx.description().split(b'\n', 1)[0],
3278 )
3276 )
3279 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3277 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3280 if names:
3278 if names:
3281 desc += b' (%s)' % b' '.join(names)
3279 desc += b' (%s)' % b' '.join(names)
3282 ui.status(_(b'grafting %s\n') % desc)
3280 ui.status(_(b'grafting %s\n') % desc)
3283 if opts.get('dry_run'):
3281 if opts.get('dry_run'):
3284 continue
3282 continue
3285
3283
3286 source = ctx.extra().get(b'source')
3284 source = ctx.extra().get(b'source')
3287 extra = {}
3285 extra = {}
3288 if source:
3286 if source:
3289 extra[b'source'] = source
3287 extra[b'source'] = source
3290 extra[b'intermediate-source'] = ctx.hex()
3288 extra[b'intermediate-source'] = ctx.hex()
3291 else:
3289 else:
3292 extra[b'source'] = ctx.hex()
3290 extra[b'source'] = ctx.hex()
3293 user = ctx.user()
3291 user = ctx.user()
3294 if opts.get('user'):
3292 if opts.get('user'):
3295 user = opts['user']
3293 user = opts['user']
3296 statedata[b'user'] = user
3294 statedata[b'user'] = user
3297 date = ctx.date()
3295 date = ctx.date()
3298 if opts.get('date'):
3296 if opts.get('date'):
3299 date = opts['date']
3297 date = opts['date']
3300 statedata[b'date'] = date
3298 statedata[b'date'] = date
3301 message = ctx.description()
3299 message = ctx.description()
3302 if opts.get('log'):
3300 if opts.get('log'):
3303 message += b'\n(grafted from %s)' % ctx.hex()
3301 message += b'\n(grafted from %s)' % ctx.hex()
3304 statedata[b'log'] = True
3302 statedata[b'log'] = True
3305
3303
3306 # we don't merge the first commit when continuing
3304 # we don't merge the first commit when continuing
3307 if not cont:
3305 if not cont:
3308 # perform the graft merge with p1(rev) as 'ancestor'
3306 # perform the graft merge with p1(rev) as 'ancestor'
3309 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
3307 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
3310 base = ctx.p1() if basectx is None else basectx
3308 base = ctx.p1() if basectx is None else basectx
3311 with ui.configoverride(overrides, b'graft'):
3309 with ui.configoverride(overrides, b'graft'):
3312 stats = mergemod.graft(
3310 stats = mergemod.graft(
3313 repo, ctx, base, [b'local', b'graft', b'parent of graft']
3311 repo, ctx, base, [b'local', b'graft', b'parent of graft']
3314 )
3312 )
3315 # report any conflicts
3313 # report any conflicts
3316 if stats.unresolvedcount > 0:
3314 if stats.unresolvedcount > 0:
3317 # write out state for --continue
3315 # write out state for --continue
3318 nodes = [repo[rev].hex() for rev in revs[pos:]]
3316 nodes = [repo[rev].hex() for rev in revs[pos:]]
3319 statedata[b'nodes'] = nodes
3317 statedata[b'nodes'] = nodes
3320 stateversion = 1
3318 stateversion = 1
3321 graftstate.save(stateversion, statedata)
3319 graftstate.save(stateversion, statedata)
3322 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3320 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3323 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3321 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3324 return 1
3322 return 1
3325 else:
3323 else:
3326 cont = False
3324 cont = False
3327
3325
3328 # commit if --no-commit is false
3326 # commit if --no-commit is false
3329 if not opts.get('no_commit'):
3327 if not opts.get('no_commit'):
3330 node = repo.commit(
3328 node = repo.commit(
3331 text=message, user=user, date=date, extra=extra, editor=editor
3329 text=message, user=user, date=date, extra=extra, editor=editor
3332 )
3330 )
3333 if node is None:
3331 if node is None:
3334 ui.warn(
3332 ui.warn(
3335 _(b'note: graft of %d:%s created no changes to commit\n')
3333 _(b'note: graft of %d:%s created no changes to commit\n')
3336 % (ctx.rev(), ctx)
3334 % (ctx.rev(), ctx)
3337 )
3335 )
3338 # checking that newnodes exist because old state files won't have it
3336 # checking that newnodes exist because old state files won't have it
3339 elif statedata.get(b'newnodes') is not None:
3337 elif statedata.get(b'newnodes') is not None:
3340 nn = statedata[b'newnodes']
3338 nn = statedata[b'newnodes']
3341 assert isinstance(nn, list) # list of bytes
3339 assert isinstance(nn, list) # list of bytes
3342 nn.append(node)
3340 nn.append(node)
3343
3341
3344 # remove state when we complete successfully
3342 # remove state when we complete successfully
3345 if not opts.get('dry_run'):
3343 if not opts.get('dry_run'):
3346 graftstate.delete()
3344 graftstate.delete()
3347
3345
3348 return 0
3346 return 0
3349
3347
3350
3348
3351 def _stopgraft(ui, repo, graftstate):
3349 def _stopgraft(ui, repo, graftstate):
3352 """stop the interrupted graft"""
3350 """stop the interrupted graft"""
3353 if not graftstate.exists():
3351 if not graftstate.exists():
3354 raise error.StateError(_(b"no interrupted graft found"))
3352 raise error.StateError(_(b"no interrupted graft found"))
3355 pctx = repo[b'.']
3353 pctx = repo[b'.']
3356 mergemod.clean_update(pctx)
3354 mergemod.clean_update(pctx)
3357 graftstate.delete()
3355 graftstate.delete()
3358 ui.status(_(b"stopped the interrupted graft\n"))
3356 ui.status(_(b"stopped the interrupted graft\n"))
3359 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3357 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3360 return 0
3358 return 0
3361
3359
3362
3360
3363 statemod.addunfinished(
3361 statemod.addunfinished(
3364 b'graft',
3362 b'graft',
3365 fname=b'graftstate',
3363 fname=b'graftstate',
3366 clearable=True,
3364 clearable=True,
3367 stopflag=True,
3365 stopflag=True,
3368 continueflag=True,
3366 continueflag=True,
3369 abortfunc=cmdutil.hgabortgraft,
3367 abortfunc=cmdutil.hgabortgraft,
3370 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3368 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3371 )
3369 )
3372
3370
3373
3371
3374 @command(
3372 @command(
3375 b'grep',
3373 b'grep',
3376 [
3374 [
3377 (b'0', b'print0', None, _(b'end fields with NUL')),
3375 (b'0', b'print0', None, _(b'end fields with NUL')),
3378 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3376 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3379 (
3377 (
3380 b'',
3378 b'',
3381 b'diff',
3379 b'diff',
3382 None,
3380 None,
3383 _(
3381 _(
3384 b'search revision differences for when the pattern was added '
3382 b'search revision differences for when the pattern was added '
3385 b'or removed'
3383 b'or removed'
3386 ),
3384 ),
3387 ),
3385 ),
3388 (b'a', b'text', None, _(b'treat all files as text')),
3386 (b'a', b'text', None, _(b'treat all files as text')),
3389 (
3387 (
3390 b'f',
3388 b'f',
3391 b'follow',
3389 b'follow',
3392 None,
3390 None,
3393 _(
3391 _(
3394 b'follow changeset history,'
3392 b'follow changeset history,'
3395 b' or file history across copies and renames'
3393 b' or file history across copies and renames'
3396 ),
3394 ),
3397 ),
3395 ),
3398 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3396 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3399 (
3397 (
3400 b'l',
3398 b'l',
3401 b'files-with-matches',
3399 b'files-with-matches',
3402 None,
3400 None,
3403 _(b'print only filenames and revisions that match'),
3401 _(b'print only filenames and revisions that match'),
3404 ),
3402 ),
3405 (b'n', b'line-number', None, _(b'print matching line numbers')),
3403 (b'n', b'line-number', None, _(b'print matching line numbers')),
3406 (
3404 (
3407 b'r',
3405 b'r',
3408 b'rev',
3406 b'rev',
3409 [],
3407 [],
3410 _(b'search files changed within revision range'),
3408 _(b'search files changed within revision range'),
3411 _(b'REV'),
3409 _(b'REV'),
3412 ),
3410 ),
3413 (
3411 (
3414 b'',
3412 b'',
3415 b'all-files',
3413 b'all-files',
3416 None,
3414 None,
3417 _(
3415 _(
3418 b'include all files in the changeset while grepping (DEPRECATED)'
3416 b'include all files in the changeset while grepping (DEPRECATED)'
3419 ),
3417 ),
3420 ),
3418 ),
3421 (b'u', b'user', None, _(b'list the author (long with -v)')),
3419 (b'u', b'user', None, _(b'list the author (long with -v)')),
3422 (b'd', b'date', None, _(b'list the date (short with -q)')),
3420 (b'd', b'date', None, _(b'list the date (short with -q)')),
3423 ]
3421 ]
3424 + formatteropts
3422 + formatteropts
3425 + walkopts,
3423 + walkopts,
3426 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3424 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3427 helpcategory=command.CATEGORY_FILE_CONTENTS,
3425 helpcategory=command.CATEGORY_FILE_CONTENTS,
3428 inferrepo=True,
3426 inferrepo=True,
3429 intents={INTENT_READONLY},
3427 intents={INTENT_READONLY},
3430 )
3428 )
3431 def grep(ui, repo, pattern, *pats, **opts):
3429 def grep(ui, repo, pattern, *pats, **opts):
3432 """search for a pattern in specified files
3430 """search for a pattern in specified files
3433
3431
3434 Search the working directory or revision history for a regular
3432 Search the working directory or revision history for a regular
3435 expression in the specified files for the entire repository.
3433 expression in the specified files for the entire repository.
3436
3434
3437 By default, grep searches the repository files in the working
3435 By default, grep searches the repository files in the working
3438 directory and prints the files where it finds a match. To specify
3436 directory and prints the files where it finds a match. To specify
3439 historical revisions instead of the working directory, use the
3437 historical revisions instead of the working directory, use the
3440 --rev flag.
3438 --rev flag.
3441
3439
3442 To search instead historical revision differences that contains a
3440 To search instead historical revision differences that contains a
3443 change in match status ("-" for a match that becomes a non-match,
3441 change in match status ("-" for a match that becomes a non-match,
3444 or "+" for a non-match that becomes a match), use the --diff flag.
3442 or "+" for a non-match that becomes a match), use the --diff flag.
3445
3443
3446 PATTERN can be any Python (roughly Perl-compatible) regular
3444 PATTERN can be any Python (roughly Perl-compatible) regular
3447 expression.
3445 expression.
3448
3446
3449 If no FILEs are specified and the --rev flag isn't supplied, all
3447 If no FILEs are specified and the --rev flag isn't supplied, all
3450 files in the working directory are searched. When using the --rev
3448 files in the working directory are searched. When using the --rev
3451 flag and specifying FILEs, use the --follow argument to also
3449 flag and specifying FILEs, use the --follow argument to also
3452 follow the specified FILEs across renames and copies.
3450 follow the specified FILEs across renames and copies.
3453
3451
3454 .. container:: verbose
3452 .. container:: verbose
3455
3453
3456 Template:
3454 Template:
3457
3455
3458 The following keywords are supported in addition to the common template
3456 The following keywords are supported in addition to the common template
3459 keywords and functions. See also :hg:`help templates`.
3457 keywords and functions. See also :hg:`help templates`.
3460
3458
3461 :change: String. Character denoting insertion ``+`` or removal ``-``.
3459 :change: String. Character denoting insertion ``+`` or removal ``-``.
3462 Available if ``--diff`` is specified.
3460 Available if ``--diff`` is specified.
3463 :lineno: Integer. Line number of the match.
3461 :lineno: Integer. Line number of the match.
3464 :path: String. Repository-absolute path of the file.
3462 :path: String. Repository-absolute path of the file.
3465 :texts: List of text chunks.
3463 :texts: List of text chunks.
3466
3464
3467 And each entry of ``{texts}`` provides the following sub-keywords.
3465 And each entry of ``{texts}`` provides the following sub-keywords.
3468
3466
3469 :matched: Boolean. True if the chunk matches the specified pattern.
3467 :matched: Boolean. True if the chunk matches the specified pattern.
3470 :text: String. Chunk content.
3468 :text: String. Chunk content.
3471
3469
3472 See :hg:`help templates.operators` for the list expansion syntax.
3470 See :hg:`help templates.operators` for the list expansion syntax.
3473
3471
3474 Returns 0 if a match is found, 1 otherwise.
3472 Returns 0 if a match is found, 1 otherwise.
3475
3473
3476 """
3474 """
3477 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3475 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3478 opts = pycompat.byteskwargs(opts)
3476 opts = pycompat.byteskwargs(opts)
3479 diff = opts.get(b'all') or opts.get(b'diff')
3477 diff = opts.get(b'all') or opts.get(b'diff')
3480 follow = opts.get(b'follow')
3478 follow = opts.get(b'follow')
3481 if opts.get(b'all_files') is None and not diff:
3479 if opts.get(b'all_files') is None and not diff:
3482 opts[b'all_files'] = True
3480 opts[b'all_files'] = True
3483 plaingrep = (
3481 plaingrep = (
3484 opts.get(b'all_files')
3482 opts.get(b'all_files')
3485 and not opts.get(b'rev')
3483 and not opts.get(b'rev')
3486 and not opts.get(b'follow')
3484 and not opts.get(b'follow')
3487 )
3485 )
3488 all_files = opts.get(b'all_files')
3486 all_files = opts.get(b'all_files')
3489 if plaingrep:
3487 if plaingrep:
3490 opts[b'rev'] = [b'wdir()']
3488 opts[b'rev'] = [b'wdir()']
3491
3489
3492 reflags = re.M
3490 reflags = re.M
3493 if opts.get(b'ignore_case'):
3491 if opts.get(b'ignore_case'):
3494 reflags |= re.I
3492 reflags |= re.I
3495 try:
3493 try:
3496 regexp = util.re.compile(pattern, reflags)
3494 regexp = util.re.compile(pattern, reflags)
3497 except re.error as inst:
3495 except re.error as inst:
3498 ui.warn(
3496 ui.warn(
3499 _(b"grep: invalid match pattern: %s\n")
3497 _(b"grep: invalid match pattern: %s\n")
3500 % stringutil.forcebytestr(inst)
3498 % stringutil.forcebytestr(inst)
3501 )
3499 )
3502 return 1
3500 return 1
3503 sep, eol = b':', b'\n'
3501 sep, eol = b':', b'\n'
3504 if opts.get(b'print0'):
3502 if opts.get(b'print0'):
3505 sep = eol = b'\0'
3503 sep = eol = b'\0'
3506
3504
3507 searcher = grepmod.grepsearcher(
3505 searcher = grepmod.grepsearcher(
3508 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3506 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3509 )
3507 )
3510
3508
3511 getfile = searcher._getfile
3509 getfile = searcher._getfile
3512
3510
3513 uipathfn = scmutil.getuipathfn(repo)
3511 uipathfn = scmutil.getuipathfn(repo)
3514
3512
3515 def display(fm, fn, ctx, pstates, states):
3513 def display(fm, fn, ctx, pstates, states):
3516 rev = scmutil.intrev(ctx)
3514 rev = scmutil.intrev(ctx)
3517 if fm.isplain():
3515 if fm.isplain():
3518 formatuser = ui.shortuser
3516 formatuser = ui.shortuser
3519 else:
3517 else:
3520 formatuser = pycompat.bytestr
3518 formatuser = pycompat.bytestr
3521 if ui.quiet:
3519 if ui.quiet:
3522 datefmt = b'%Y-%m-%d'
3520 datefmt = b'%Y-%m-%d'
3523 else:
3521 else:
3524 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3522 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3525 found = False
3523 found = False
3526
3524
3527 @util.cachefunc
3525 @util.cachefunc
3528 def binary():
3526 def binary():
3529 flog = getfile(fn)
3527 flog = getfile(fn)
3530 try:
3528 try:
3531 return stringutil.binary(flog.read(ctx.filenode(fn)))
3529 return stringutil.binary(flog.read(ctx.filenode(fn)))
3532 except error.WdirUnsupported:
3530 except error.WdirUnsupported:
3533 return ctx[fn].isbinary()
3531 return ctx[fn].isbinary()
3534
3532
3535 fieldnamemap = {b'linenumber': b'lineno'}
3533 fieldnamemap = {b'linenumber': b'lineno'}
3536 if diff:
3534 if diff:
3537 iter = grepmod.difflinestates(pstates, states)
3535 iter = grepmod.difflinestates(pstates, states)
3538 else:
3536 else:
3539 iter = [(b'', l) for l in states]
3537 iter = [(b'', l) for l in states]
3540 for change, l in iter:
3538 for change, l in iter:
3541 fm.startitem()
3539 fm.startitem()
3542 fm.context(ctx=ctx)
3540 fm.context(ctx=ctx)
3543 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3541 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3544 fm.plain(uipathfn(fn), label=b'grep.filename')
3542 fm.plain(uipathfn(fn), label=b'grep.filename')
3545
3543
3546 cols = [
3544 cols = [
3547 (b'rev', b'%d', rev, not plaingrep, b''),
3545 (b'rev', b'%d', rev, not plaingrep, b''),
3548 (
3546 (
3549 b'linenumber',
3547 b'linenumber',
3550 b'%d',
3548 b'%d',
3551 l.linenum,
3549 l.linenum,
3552 opts.get(b'line_number'),
3550 opts.get(b'line_number'),
3553 b'',
3551 b'',
3554 ),
3552 ),
3555 ]
3553 ]
3556 if diff:
3554 if diff:
3557 cols.append(
3555 cols.append(
3558 (
3556 (
3559 b'change',
3557 b'change',
3560 b'%s',
3558 b'%s',
3561 change,
3559 change,
3562 True,
3560 True,
3563 b'grep.inserted '
3561 b'grep.inserted '
3564 if change == b'+'
3562 if change == b'+'
3565 else b'grep.deleted ',
3563 else b'grep.deleted ',
3566 )
3564 )
3567 )
3565 )
3568 cols.extend(
3566 cols.extend(
3569 [
3567 [
3570 (
3568 (
3571 b'user',
3569 b'user',
3572 b'%s',
3570 b'%s',
3573 formatuser(ctx.user()),
3571 formatuser(ctx.user()),
3574 opts.get(b'user'),
3572 opts.get(b'user'),
3575 b'',
3573 b'',
3576 ),
3574 ),
3577 (
3575 (
3578 b'date',
3576 b'date',
3579 b'%s',
3577 b'%s',
3580 fm.formatdate(ctx.date(), datefmt),
3578 fm.formatdate(ctx.date(), datefmt),
3581 opts.get(b'date'),
3579 opts.get(b'date'),
3582 b'',
3580 b'',
3583 ),
3581 ),
3584 ]
3582 ]
3585 )
3583 )
3586 for name, fmt, data, cond, extra_label in cols:
3584 for name, fmt, data, cond, extra_label in cols:
3587 if cond:
3585 if cond:
3588 fm.plain(sep, label=b'grep.sep')
3586 fm.plain(sep, label=b'grep.sep')
3589 field = fieldnamemap.get(name, name)
3587 field = fieldnamemap.get(name, name)
3590 label = extra_label + (b'grep.%s' % name)
3588 label = extra_label + (b'grep.%s' % name)
3591 fm.condwrite(cond, field, fmt, data, label=label)
3589 fm.condwrite(cond, field, fmt, data, label=label)
3592 if not opts.get(b'files_with_matches'):
3590 if not opts.get(b'files_with_matches'):
3593 fm.plain(sep, label=b'grep.sep')
3591 fm.plain(sep, label=b'grep.sep')
3594 if not opts.get(b'text') and binary():
3592 if not opts.get(b'text') and binary():
3595 fm.plain(_(b" Binary file matches"))
3593 fm.plain(_(b" Binary file matches"))
3596 else:
3594 else:
3597 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3595 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3598 fm.plain(eol)
3596 fm.plain(eol)
3599 found = True
3597 found = True
3600 if opts.get(b'files_with_matches'):
3598 if opts.get(b'files_with_matches'):
3601 break
3599 break
3602 return found
3600 return found
3603
3601
3604 def displaymatches(fm, l):
3602 def displaymatches(fm, l):
3605 p = 0
3603 p = 0
3606 for s, e in l.findpos(regexp):
3604 for s, e in l.findpos(regexp):
3607 if p < s:
3605 if p < s:
3608 fm.startitem()
3606 fm.startitem()
3609 fm.write(b'text', b'%s', l.line[p:s])
3607 fm.write(b'text', b'%s', l.line[p:s])
3610 fm.data(matched=False)
3608 fm.data(matched=False)
3611 fm.startitem()
3609 fm.startitem()
3612 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3610 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3613 fm.data(matched=True)
3611 fm.data(matched=True)
3614 p = e
3612 p = e
3615 if p < len(l.line):
3613 if p < len(l.line):
3616 fm.startitem()
3614 fm.startitem()
3617 fm.write(b'text', b'%s', l.line[p:])
3615 fm.write(b'text', b'%s', l.line[p:])
3618 fm.data(matched=False)
3616 fm.data(matched=False)
3619 fm.end()
3617 fm.end()
3620
3618
3621 found = False
3619 found = False
3622
3620
3623 wopts = logcmdutil.walkopts(
3621 wopts = logcmdutil.walkopts(
3624 pats=pats,
3622 pats=pats,
3625 opts=opts,
3623 opts=opts,
3626 revspec=opts[b'rev'],
3624 revspec=opts[b'rev'],
3627 include_pats=opts[b'include'],
3625 include_pats=opts[b'include'],
3628 exclude_pats=opts[b'exclude'],
3626 exclude_pats=opts[b'exclude'],
3629 follow=follow,
3627 follow=follow,
3630 force_changelog_traversal=all_files,
3628 force_changelog_traversal=all_files,
3631 filter_revisions_by_pats=not all_files,
3629 filter_revisions_by_pats=not all_files,
3632 )
3630 )
3633 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3631 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3634
3632
3635 ui.pager(b'grep')
3633 ui.pager(b'grep')
3636 fm = ui.formatter(b'grep', opts)
3634 fm = ui.formatter(b'grep', opts)
3637 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3635 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3638 r = display(fm, fn, ctx, pstates, states)
3636 r = display(fm, fn, ctx, pstates, states)
3639 found = found or r
3637 found = found or r
3640 if r and not diff and not all_files:
3638 if r and not diff and not all_files:
3641 searcher.skipfile(fn, ctx.rev())
3639 searcher.skipfile(fn, ctx.rev())
3642 fm.end()
3640 fm.end()
3643
3641
3644 return not found
3642 return not found
3645
3643
3646
3644
3647 @command(
3645 @command(
3648 b'heads',
3646 b'heads',
3649 [
3647 [
3650 (
3648 (
3651 b'r',
3649 b'r',
3652 b'rev',
3650 b'rev',
3653 b'',
3651 b'',
3654 _(b'show only heads which are descendants of STARTREV'),
3652 _(b'show only heads which are descendants of STARTREV'),
3655 _(b'STARTREV'),
3653 _(b'STARTREV'),
3656 ),
3654 ),
3657 (b't', b'topo', False, _(b'show topological heads only')),
3655 (b't', b'topo', False, _(b'show topological heads only')),
3658 (
3656 (
3659 b'a',
3657 b'a',
3660 b'active',
3658 b'active',
3661 False,
3659 False,
3662 _(b'show active branchheads only (DEPRECATED)'),
3660 _(b'show active branchheads only (DEPRECATED)'),
3663 ),
3661 ),
3664 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3662 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3665 ]
3663 ]
3666 + templateopts,
3664 + templateopts,
3667 _(b'[-ct] [-r STARTREV] [REV]...'),
3665 _(b'[-ct] [-r STARTREV] [REV]...'),
3668 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3666 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3669 intents={INTENT_READONLY},
3667 intents={INTENT_READONLY},
3670 )
3668 )
3671 def heads(ui, repo, *branchrevs, **opts):
3669 def heads(ui, repo, *branchrevs, **opts):
3672 """show branch heads
3670 """show branch heads
3673
3671
3674 With no arguments, show all open branch heads in the repository.
3672 With no arguments, show all open branch heads in the repository.
3675 Branch heads are changesets that have no descendants on the
3673 Branch heads are changesets that have no descendants on the
3676 same branch. They are where development generally takes place and
3674 same branch. They are where development generally takes place and
3677 are the usual targets for update and merge operations.
3675 are the usual targets for update and merge operations.
3678
3676
3679 If one or more REVs are given, only open branch heads on the
3677 If one or more REVs are given, only open branch heads on the
3680 branches associated with the specified changesets are shown. This
3678 branches associated with the specified changesets are shown. This
3681 means that you can use :hg:`heads .` to see the heads on the
3679 means that you can use :hg:`heads .` to see the heads on the
3682 currently checked-out branch.
3680 currently checked-out branch.
3683
3681
3684 If -c/--closed is specified, also show branch heads marked closed
3682 If -c/--closed is specified, also show branch heads marked closed
3685 (see :hg:`commit --close-branch`).
3683 (see :hg:`commit --close-branch`).
3686
3684
3687 If STARTREV is specified, only those heads that are descendants of
3685 If STARTREV is specified, only those heads that are descendants of
3688 STARTREV will be displayed.
3686 STARTREV will be displayed.
3689
3687
3690 If -t/--topo is specified, named branch mechanics will be ignored and only
3688 If -t/--topo is specified, named branch mechanics will be ignored and only
3691 topological heads (changesets with no children) will be shown.
3689 topological heads (changesets with no children) will be shown.
3692
3690
3693 Returns 0 if matching heads are found, 1 if not.
3691 Returns 0 if matching heads are found, 1 if not.
3694 """
3692 """
3695
3693
3696 opts = pycompat.byteskwargs(opts)
3694 opts = pycompat.byteskwargs(opts)
3697 start = None
3695 start = None
3698 rev = opts.get(b'rev')
3696 rev = opts.get(b'rev')
3699 if rev:
3697 if rev:
3700 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3698 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3701 start = logcmdutil.revsingle(repo, rev, None).node()
3699 start = logcmdutil.revsingle(repo, rev, None).node()
3702
3700
3703 if opts.get(b'topo'):
3701 if opts.get(b'topo'):
3704 heads = [repo[h] for h in repo.heads(start)]
3702 heads = [repo[h] for h in repo.heads(start)]
3705 else:
3703 else:
3706 heads = []
3704 heads = []
3707 for branch in repo.branchmap():
3705 for branch in repo.branchmap():
3708 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3706 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3709 heads = [repo[h] for h in heads]
3707 heads = [repo[h] for h in heads]
3710
3708
3711 if branchrevs:
3709 if branchrevs:
3712 branches = {
3710 branches = {
3713 repo[r].branch() for r in logcmdutil.revrange(repo, branchrevs)
3711 repo[r].branch() for r in logcmdutil.revrange(repo, branchrevs)
3714 }
3712 }
3715 heads = [h for h in heads if h.branch() in branches]
3713 heads = [h for h in heads if h.branch() in branches]
3716
3714
3717 if opts.get(b'active') and branchrevs:
3715 if opts.get(b'active') and branchrevs:
3718 dagheads = repo.heads(start)
3716 dagheads = repo.heads(start)
3719 heads = [h for h in heads if h.node() in dagheads]
3717 heads = [h for h in heads if h.node() in dagheads]
3720
3718
3721 if branchrevs:
3719 if branchrevs:
3722 haveheads = {h.branch() for h in heads}
3720 haveheads = {h.branch() for h in heads}
3723 if branches - haveheads:
3721 if branches - haveheads:
3724 headless = b', '.join(b for b in branches - haveheads)
3722 headless = b', '.join(b for b in branches - haveheads)
3725 msg = _(b'no open branch heads found on branches %s')
3723 msg = _(b'no open branch heads found on branches %s')
3726 if opts.get(b'rev'):
3724 if opts.get(b'rev'):
3727 msg += _(b' (started at %s)') % opts[b'rev']
3725 msg += _(b' (started at %s)') % opts[b'rev']
3728 ui.warn((msg + b'\n') % headless)
3726 ui.warn((msg + b'\n') % headless)
3729
3727
3730 if not heads:
3728 if not heads:
3731 return 1
3729 return 1
3732
3730
3733 ui.pager(b'heads')
3731 ui.pager(b'heads')
3734 heads = sorted(heads, key=lambda x: -(x.rev()))
3732 heads = sorted(heads, key=lambda x: -(x.rev()))
3735 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3733 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3736 for ctx in heads:
3734 for ctx in heads:
3737 displayer.show(ctx)
3735 displayer.show(ctx)
3738 displayer.close()
3736 displayer.close()
3739
3737
3740
3738
3741 @command(
3739 @command(
3742 b'help',
3740 b'help',
3743 [
3741 [
3744 (b'e', b'extension', None, _(b'show only help for extensions')),
3742 (b'e', b'extension', None, _(b'show only help for extensions')),
3745 (b'c', b'command', None, _(b'show only help for commands')),
3743 (b'c', b'command', None, _(b'show only help for commands')),
3746 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3744 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3747 (
3745 (
3748 b's',
3746 b's',
3749 b'system',
3747 b'system',
3750 [],
3748 [],
3751 _(b'show help for specific platform(s)'),
3749 _(b'show help for specific platform(s)'),
3752 _(b'PLATFORM'),
3750 _(b'PLATFORM'),
3753 ),
3751 ),
3754 ],
3752 ],
3755 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3753 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3756 helpcategory=command.CATEGORY_HELP,
3754 helpcategory=command.CATEGORY_HELP,
3757 norepo=True,
3755 norepo=True,
3758 intents={INTENT_READONLY},
3756 intents={INTENT_READONLY},
3759 )
3757 )
3760 def help_(ui, name=None, **opts):
3758 def help_(ui, name=None, **opts):
3761 """show help for a given topic or a help overview
3759 """show help for a given topic or a help overview
3762
3760
3763 With no arguments, print a list of commands with short help messages.
3761 With no arguments, print a list of commands with short help messages.
3764
3762
3765 Given a topic, extension, or command name, print help for that
3763 Given a topic, extension, or command name, print help for that
3766 topic.
3764 topic.
3767
3765
3768 Returns 0 if successful.
3766 Returns 0 if successful.
3769 """
3767 """
3770
3768
3771 keep = opts.get('system') or []
3769 keep = opts.get('system') or []
3772 if len(keep) == 0:
3770 if len(keep) == 0:
3773 if pycompat.sysplatform.startswith(b'win'):
3771 if pycompat.sysplatform.startswith(b'win'):
3774 keep.append(b'windows')
3772 keep.append(b'windows')
3775 elif pycompat.sysplatform == b'OpenVMS':
3773 elif pycompat.sysplatform == b'OpenVMS':
3776 keep.append(b'vms')
3774 keep.append(b'vms')
3777 elif pycompat.sysplatform == b'plan9':
3775 elif pycompat.sysplatform == b'plan9':
3778 keep.append(b'plan9')
3776 keep.append(b'plan9')
3779 else:
3777 else:
3780 keep.append(b'unix')
3778 keep.append(b'unix')
3781 keep.append(pycompat.sysplatform.lower())
3779 keep.append(pycompat.sysplatform.lower())
3782 if ui.verbose:
3780 if ui.verbose:
3783 keep.append(b'verbose')
3781 keep.append(b'verbose')
3784
3782
3785 commands = sys.modules[__name__]
3783 commands = sys.modules[__name__]
3786 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3784 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3787 ui.pager(b'help')
3785 ui.pager(b'help')
3788 ui.write(formatted)
3786 ui.write(formatted)
3789
3787
3790
3788
3791 @command(
3789 @command(
3792 b'identify|id',
3790 b'identify|id',
3793 [
3791 [
3794 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3792 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3795 (b'n', b'num', None, _(b'show local revision number')),
3793 (b'n', b'num', None, _(b'show local revision number')),
3796 (b'i', b'id', None, _(b'show global revision id')),
3794 (b'i', b'id', None, _(b'show global revision id')),
3797 (b'b', b'branch', None, _(b'show branch')),
3795 (b'b', b'branch', None, _(b'show branch')),
3798 (b't', b'tags', None, _(b'show tags')),
3796 (b't', b'tags', None, _(b'show tags')),
3799 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3797 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3800 ]
3798 ]
3801 + remoteopts
3799 + remoteopts
3802 + formatteropts,
3800 + formatteropts,
3803 _(b'[-nibtB] [-r REV] [SOURCE]'),
3801 _(b'[-nibtB] [-r REV] [SOURCE]'),
3804 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3802 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3805 optionalrepo=True,
3803 optionalrepo=True,
3806 intents={INTENT_READONLY},
3804 intents={INTENT_READONLY},
3807 )
3805 )
3808 def identify(
3806 def identify(
3809 ui,
3807 ui,
3810 repo,
3808 repo,
3811 source=None,
3809 source=None,
3812 rev=None,
3810 rev=None,
3813 num=None,
3811 num=None,
3814 id=None,
3812 id=None,
3815 branch=None,
3813 branch=None,
3816 tags=None,
3814 tags=None,
3817 bookmarks=None,
3815 bookmarks=None,
3818 **opts
3816 **opts
3819 ):
3817 ):
3820 """identify the working directory or specified revision
3818 """identify the working directory or specified revision
3821
3819
3822 Print a summary identifying the repository state at REV using one or
3820 Print a summary identifying the repository state at REV using one or
3823 two parent hash identifiers, followed by a "+" if the working
3821 two parent hash identifiers, followed by a "+" if the working
3824 directory has uncommitted changes, the branch name (if not default),
3822 directory has uncommitted changes, the branch name (if not default),
3825 a list of tags, and a list of bookmarks.
3823 a list of tags, and a list of bookmarks.
3826
3824
3827 When REV is not given, print a summary of the current state of the
3825 When REV is not given, print a summary of the current state of the
3828 repository including the working directory. Specify -r. to get information
3826 repository including the working directory. Specify -r. to get information
3829 of the working directory parent without scanning uncommitted changes.
3827 of the working directory parent without scanning uncommitted changes.
3830
3828
3831 Specifying a path to a repository root or Mercurial bundle will
3829 Specifying a path to a repository root or Mercurial bundle will
3832 cause lookup to operate on that repository/bundle.
3830 cause lookup to operate on that repository/bundle.
3833
3831
3834 .. container:: verbose
3832 .. container:: verbose
3835
3833
3836 Template:
3834 Template:
3837
3835
3838 The following keywords are supported in addition to the common template
3836 The following keywords are supported in addition to the common template
3839 keywords and functions. See also :hg:`help templates`.
3837 keywords and functions. See also :hg:`help templates`.
3840
3838
3841 :dirty: String. Character ``+`` denoting if the working directory has
3839 :dirty: String. Character ``+`` denoting if the working directory has
3842 uncommitted changes.
3840 uncommitted changes.
3843 :id: String. One or two nodes, optionally followed by ``+``.
3841 :id: String. One or two nodes, optionally followed by ``+``.
3844 :parents: List of strings. Parent nodes of the changeset.
3842 :parents: List of strings. Parent nodes of the changeset.
3845
3843
3846 Examples:
3844 Examples:
3847
3845
3848 - generate a build identifier for the working directory::
3846 - generate a build identifier for the working directory::
3849
3847
3850 hg id --id > build-id.dat
3848 hg id --id > build-id.dat
3851
3849
3852 - find the revision corresponding to a tag::
3850 - find the revision corresponding to a tag::
3853
3851
3854 hg id -n -r 1.3
3852 hg id -n -r 1.3
3855
3853
3856 - check the most recent revision of a remote repository::
3854 - check the most recent revision of a remote repository::
3857
3855
3858 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3856 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3859
3857
3860 See :hg:`log` for generating more information about specific revisions,
3858 See :hg:`log` for generating more information about specific revisions,
3861 including full hash identifiers.
3859 including full hash identifiers.
3862
3860
3863 Returns 0 if successful.
3861 Returns 0 if successful.
3864 """
3862 """
3865
3863
3866 opts = pycompat.byteskwargs(opts)
3864 opts = pycompat.byteskwargs(opts)
3867 if not repo and not source:
3865 if not repo and not source:
3868 raise error.InputError(
3866 raise error.InputError(
3869 _(b"there is no Mercurial repository here (.hg not found)")
3867 _(b"there is no Mercurial repository here (.hg not found)")
3870 )
3868 )
3871
3869
3872 default = not (num or id or branch or tags or bookmarks)
3870 default = not (num or id or branch or tags or bookmarks)
3873 output = []
3871 output = []
3874 revs = []
3872 revs = []
3875
3873
3876 peer = None
3874 peer = None
3877 try:
3875 try:
3878 if source:
3876 if source:
3879 source, branches = urlutil.get_unique_pull_path(
3877 source, branches = urlutil.get_unique_pull_path(
3880 b'identify', repo, ui, source
3878 b'identify', repo, ui, source
3881 )
3879 )
3882 # only pass ui when no repo
3880 # only pass ui when no repo
3883 peer = hg.peer(repo or ui, opts, source)
3881 peer = hg.peer(repo or ui, opts, source)
3884 repo = peer.local()
3882 repo = peer.local()
3885 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3883 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3886
3884
3887 fm = ui.formatter(b'identify', opts)
3885 fm = ui.formatter(b'identify', opts)
3888 fm.startitem()
3886 fm.startitem()
3889
3887
3890 if not repo:
3888 if not repo:
3891 if num or branch or tags:
3889 if num or branch or tags:
3892 raise error.InputError(
3890 raise error.InputError(
3893 _(b"can't query remote revision number, branch, or tags")
3891 _(b"can't query remote revision number, branch, or tags")
3894 )
3892 )
3895 if not rev and revs:
3893 if not rev and revs:
3896 rev = revs[0]
3894 rev = revs[0]
3897 if not rev:
3895 if not rev:
3898 rev = b"tip"
3896 rev = b"tip"
3899
3897
3900 remoterev = peer.lookup(rev)
3898 remoterev = peer.lookup(rev)
3901 hexrev = fm.hexfunc(remoterev)
3899 hexrev = fm.hexfunc(remoterev)
3902 if default or id:
3900 if default or id:
3903 output = [hexrev]
3901 output = [hexrev]
3904 fm.data(id=hexrev)
3902 fm.data(id=hexrev)
3905
3903
3906 @util.cachefunc
3904 @util.cachefunc
3907 def getbms():
3905 def getbms():
3908 bms = []
3906 bms = []
3909
3907
3910 if b'bookmarks' in peer.listkeys(b'namespaces'):
3908 if b'bookmarks' in peer.listkeys(b'namespaces'):
3911 hexremoterev = hex(remoterev)
3909 hexremoterev = hex(remoterev)
3912 bms = [
3910 bms = [
3913 bm
3911 bm
3914 for bm, bmr in peer.listkeys(b'bookmarks').items()
3912 for bm, bmr in peer.listkeys(b'bookmarks').items()
3915 if bmr == hexremoterev
3913 if bmr == hexremoterev
3916 ]
3914 ]
3917
3915
3918 return sorted(bms)
3916 return sorted(bms)
3919
3917
3920 if fm.isplain():
3918 if fm.isplain():
3921 if bookmarks:
3919 if bookmarks:
3922 output.extend(getbms())
3920 output.extend(getbms())
3923 elif default and not ui.quiet:
3921 elif default and not ui.quiet:
3924 # multiple bookmarks for a single parent separated by '/'
3922 # multiple bookmarks for a single parent separated by '/'
3925 bm = b'/'.join(getbms())
3923 bm = b'/'.join(getbms())
3926 if bm:
3924 if bm:
3927 output.append(bm)
3925 output.append(bm)
3928 else:
3926 else:
3929 fm.data(node=hex(remoterev))
3927 fm.data(node=hex(remoterev))
3930 if bookmarks or b'bookmarks' in fm.datahint():
3928 if bookmarks or b'bookmarks' in fm.datahint():
3931 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3929 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3932 else:
3930 else:
3933 if rev:
3931 if rev:
3934 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3932 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3935 ctx = logcmdutil.revsingle(repo, rev, None)
3933 ctx = logcmdutil.revsingle(repo, rev, None)
3936
3934
3937 if ctx.rev() is None:
3935 if ctx.rev() is None:
3938 ctx = repo[None]
3936 ctx = repo[None]
3939 parents = ctx.parents()
3937 parents = ctx.parents()
3940 taglist = []
3938 taglist = []
3941 for p in parents:
3939 for p in parents:
3942 taglist.extend(p.tags())
3940 taglist.extend(p.tags())
3943
3941
3944 dirty = b""
3942 dirty = b""
3945 if ctx.dirty(missing=True, merge=False, branch=False):
3943 if ctx.dirty(missing=True, merge=False, branch=False):
3946 dirty = b'+'
3944 dirty = b'+'
3947 fm.data(dirty=dirty)
3945 fm.data(dirty=dirty)
3948
3946
3949 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3947 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3950 if default or id:
3948 if default or id:
3951 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3949 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3952 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3950 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3953
3951
3954 if num:
3952 if num:
3955 numoutput = [b"%d" % p.rev() for p in parents]
3953 numoutput = [b"%d" % p.rev() for p in parents]
3956 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3954 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3957
3955
3958 fm.data(
3956 fm.data(
3959 parents=fm.formatlist(
3957 parents=fm.formatlist(
3960 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3958 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3961 )
3959 )
3962 )
3960 )
3963 else:
3961 else:
3964 hexoutput = fm.hexfunc(ctx.node())
3962 hexoutput = fm.hexfunc(ctx.node())
3965 if default or id:
3963 if default or id:
3966 output = [hexoutput]
3964 output = [hexoutput]
3967 fm.data(id=hexoutput)
3965 fm.data(id=hexoutput)
3968
3966
3969 if num:
3967 if num:
3970 output.append(pycompat.bytestr(ctx.rev()))
3968 output.append(pycompat.bytestr(ctx.rev()))
3971 taglist = ctx.tags()
3969 taglist = ctx.tags()
3972
3970
3973 if default and not ui.quiet:
3971 if default and not ui.quiet:
3974 b = ctx.branch()
3972 b = ctx.branch()
3975 if b != b'default':
3973 if b != b'default':
3976 output.append(b"(%s)" % b)
3974 output.append(b"(%s)" % b)
3977
3975
3978 # multiple tags for a single parent separated by '/'
3976 # multiple tags for a single parent separated by '/'
3979 t = b'/'.join(taglist)
3977 t = b'/'.join(taglist)
3980 if t:
3978 if t:
3981 output.append(t)
3979 output.append(t)
3982
3980
3983 # multiple bookmarks for a single parent separated by '/'
3981 # multiple bookmarks for a single parent separated by '/'
3984 bm = b'/'.join(ctx.bookmarks())
3982 bm = b'/'.join(ctx.bookmarks())
3985 if bm:
3983 if bm:
3986 output.append(bm)
3984 output.append(bm)
3987 else:
3985 else:
3988 if branch:
3986 if branch:
3989 output.append(ctx.branch())
3987 output.append(ctx.branch())
3990
3988
3991 if tags:
3989 if tags:
3992 output.extend(taglist)
3990 output.extend(taglist)
3993
3991
3994 if bookmarks:
3992 if bookmarks:
3995 output.extend(ctx.bookmarks())
3993 output.extend(ctx.bookmarks())
3996
3994
3997 fm.data(node=ctx.hex())
3995 fm.data(node=ctx.hex())
3998 fm.data(branch=ctx.branch())
3996 fm.data(branch=ctx.branch())
3999 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
3997 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4000 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
3998 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4001 fm.context(ctx=ctx)
3999 fm.context(ctx=ctx)
4002
4000
4003 fm.plain(b"%s\n" % b' '.join(output))
4001 fm.plain(b"%s\n" % b' '.join(output))
4004 fm.end()
4002 fm.end()
4005 finally:
4003 finally:
4006 if peer:
4004 if peer:
4007 peer.close()
4005 peer.close()
4008
4006
4009
4007
4010 @command(
4008 @command(
4011 b'import|patch',
4009 b'import|patch',
4012 [
4010 [
4013 (
4011 (
4014 b'p',
4012 b'p',
4015 b'strip',
4013 b'strip',
4016 1,
4014 1,
4017 _(
4015 _(
4018 b'directory strip option for patch. This has the same '
4016 b'directory strip option for patch. This has the same '
4019 b'meaning as the corresponding patch option'
4017 b'meaning as the corresponding patch option'
4020 ),
4018 ),
4021 _(b'NUM'),
4019 _(b'NUM'),
4022 ),
4020 ),
4023 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4021 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4024 (b'', b'secret', None, _(b'use the secret phase for committing')),
4022 (b'', b'secret', None, _(b'use the secret phase for committing')),
4025 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4023 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4026 (
4024 (
4027 b'f',
4025 b'f',
4028 b'force',
4026 b'force',
4029 None,
4027 None,
4030 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4028 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4031 ),
4029 ),
4032 (
4030 (
4033 b'',
4031 b'',
4034 b'no-commit',
4032 b'no-commit',
4035 None,
4033 None,
4036 _(b"don't commit, just update the working directory"),
4034 _(b"don't commit, just update the working directory"),
4037 ),
4035 ),
4038 (
4036 (
4039 b'',
4037 b'',
4040 b'bypass',
4038 b'bypass',
4041 None,
4039 None,
4042 _(b"apply patch without touching the working directory"),
4040 _(b"apply patch without touching the working directory"),
4043 ),
4041 ),
4044 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4042 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4045 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4043 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4046 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4044 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4047 (
4045 (
4048 b'',
4046 b'',
4049 b'import-branch',
4047 b'import-branch',
4050 None,
4048 None,
4051 _(b'use any branch information in patch (implied by --exact)'),
4049 _(b'use any branch information in patch (implied by --exact)'),
4052 ),
4050 ),
4053 ]
4051 ]
4054 + commitopts
4052 + commitopts
4055 + commitopts2
4053 + commitopts2
4056 + similarityopts,
4054 + similarityopts,
4057 _(b'[OPTION]... PATCH...'),
4055 _(b'[OPTION]... PATCH...'),
4058 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4056 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4059 )
4057 )
4060 def import_(ui, repo, patch1=None, *patches, **opts):
4058 def import_(ui, repo, patch1=None, *patches, **opts):
4061 """import an ordered set of patches
4059 """import an ordered set of patches
4062
4060
4063 Import a list of patches and commit them individually (unless
4061 Import a list of patches and commit them individually (unless
4064 --no-commit is specified).
4062 --no-commit is specified).
4065
4063
4066 To read a patch from standard input (stdin), use "-" as the patch
4064 To read a patch from standard input (stdin), use "-" as the patch
4067 name. If a URL is specified, the patch will be downloaded from
4065 name. If a URL is specified, the patch will be downloaded from
4068 there.
4066 there.
4069
4067
4070 Import first applies changes to the working directory (unless
4068 Import first applies changes to the working directory (unless
4071 --bypass is specified), import will abort if there are outstanding
4069 --bypass is specified), import will abort if there are outstanding
4072 changes.
4070 changes.
4073
4071
4074 Use --bypass to apply and commit patches directly to the
4072 Use --bypass to apply and commit patches directly to the
4075 repository, without affecting the working directory. Without
4073 repository, without affecting the working directory. Without
4076 --exact, patches will be applied on top of the working directory
4074 --exact, patches will be applied on top of the working directory
4077 parent revision.
4075 parent revision.
4078
4076
4079 You can import a patch straight from a mail message. Even patches
4077 You can import a patch straight from a mail message. Even patches
4080 as attachments work (to use the body part, it must have type
4078 as attachments work (to use the body part, it must have type
4081 text/plain or text/x-patch). From and Subject headers of email
4079 text/plain or text/x-patch). From and Subject headers of email
4082 message are used as default committer and commit message. All
4080 message are used as default committer and commit message. All
4083 text/plain body parts before first diff are added to the commit
4081 text/plain body parts before first diff are added to the commit
4084 message.
4082 message.
4085
4083
4086 If the imported patch was generated by :hg:`export`, user and
4084 If the imported patch was generated by :hg:`export`, user and
4087 description from patch override values from message headers and
4085 description from patch override values from message headers and
4088 body. Values given on command line with -m/--message and -u/--user
4086 body. Values given on command line with -m/--message and -u/--user
4089 override these.
4087 override these.
4090
4088
4091 If --exact is specified, import will set the working directory to
4089 If --exact is specified, import will set the working directory to
4092 the parent of each patch before applying it, and will abort if the
4090 the parent of each patch before applying it, and will abort if the
4093 resulting changeset has a different ID than the one recorded in
4091 resulting changeset has a different ID than the one recorded in
4094 the patch. This will guard against various ways that portable
4092 the patch. This will guard against various ways that portable
4095 patch formats and mail systems might fail to transfer Mercurial
4093 patch formats and mail systems might fail to transfer Mercurial
4096 data or metadata. See :hg:`bundle` for lossless transmission.
4094 data or metadata. See :hg:`bundle` for lossless transmission.
4097
4095
4098 Use --partial to ensure a changeset will be created from the patch
4096 Use --partial to ensure a changeset will be created from the patch
4099 even if some hunks fail to apply. Hunks that fail to apply will be
4097 even if some hunks fail to apply. Hunks that fail to apply will be
4100 written to a <target-file>.rej file. Conflicts can then be resolved
4098 written to a <target-file>.rej file. Conflicts can then be resolved
4101 by hand before :hg:`commit --amend` is run to update the created
4099 by hand before :hg:`commit --amend` is run to update the created
4102 changeset. This flag exists to let people import patches that
4100 changeset. This flag exists to let people import patches that
4103 partially apply without losing the associated metadata (author,
4101 partially apply without losing the associated metadata (author,
4104 date, description, ...).
4102 date, description, ...).
4105
4103
4106 .. note::
4104 .. note::
4107
4105
4108 When no hunks apply cleanly, :hg:`import --partial` will create
4106 When no hunks apply cleanly, :hg:`import --partial` will create
4109 an empty changeset, importing only the patch metadata.
4107 an empty changeset, importing only the patch metadata.
4110
4108
4111 With -s/--similarity, hg will attempt to discover renames and
4109 With -s/--similarity, hg will attempt to discover renames and
4112 copies in the patch in the same way as :hg:`addremove`.
4110 copies in the patch in the same way as :hg:`addremove`.
4113
4111
4114 It is possible to use external patch programs to perform the patch
4112 It is possible to use external patch programs to perform the patch
4115 by setting the ``ui.patch`` configuration option. For the default
4113 by setting the ``ui.patch`` configuration option. For the default
4116 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4114 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4117 See :hg:`help config` for more information about configuration
4115 See :hg:`help config` for more information about configuration
4118 files and how to use these options.
4116 files and how to use these options.
4119
4117
4120 See :hg:`help dates` for a list of formats valid for -d/--date.
4118 See :hg:`help dates` for a list of formats valid for -d/--date.
4121
4119
4122 .. container:: verbose
4120 .. container:: verbose
4123
4121
4124 Examples:
4122 Examples:
4125
4123
4126 - import a traditional patch from a website and detect renames::
4124 - import a traditional patch from a website and detect renames::
4127
4125
4128 hg import -s 80 http://example.com/bugfix.patch
4126 hg import -s 80 http://example.com/bugfix.patch
4129
4127
4130 - import a changeset from an hgweb server::
4128 - import a changeset from an hgweb server::
4131
4129
4132 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4130 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4133
4131
4134 - import all the patches in an Unix-style mbox::
4132 - import all the patches in an Unix-style mbox::
4135
4133
4136 hg import incoming-patches.mbox
4134 hg import incoming-patches.mbox
4137
4135
4138 - import patches from stdin::
4136 - import patches from stdin::
4139
4137
4140 hg import -
4138 hg import -
4141
4139
4142 - attempt to exactly restore an exported changeset (not always
4140 - attempt to exactly restore an exported changeset (not always
4143 possible)::
4141 possible)::
4144
4142
4145 hg import --exact proposed-fix.patch
4143 hg import --exact proposed-fix.patch
4146
4144
4147 - use an external tool to apply a patch which is too fuzzy for
4145 - use an external tool to apply a patch which is too fuzzy for
4148 the default internal tool.
4146 the default internal tool.
4149
4147
4150 hg import --config ui.patch="patch --merge" fuzzy.patch
4148 hg import --config ui.patch="patch --merge" fuzzy.patch
4151
4149
4152 - change the default fuzzing from 2 to a less strict 7
4150 - change the default fuzzing from 2 to a less strict 7
4153
4151
4154 hg import --config ui.fuzz=7 fuzz.patch
4152 hg import --config ui.fuzz=7 fuzz.patch
4155
4153
4156 Returns 0 on success, 1 on partial success (see --partial).
4154 Returns 0 on success, 1 on partial success (see --partial).
4157 """
4155 """
4158
4156
4159 cmdutil.check_incompatible_arguments(
4157 cmdutil.check_incompatible_arguments(
4160 opts, 'no_commit', ['bypass', 'secret']
4158 opts, 'no_commit', ['bypass', 'secret']
4161 )
4159 )
4162 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4160 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4163 opts = pycompat.byteskwargs(opts)
4161 opts = pycompat.byteskwargs(opts)
4164 if not patch1:
4162 if not patch1:
4165 raise error.InputError(_(b'need at least one patch to import'))
4163 raise error.InputError(_(b'need at least one patch to import'))
4166
4164
4167 patches = (patch1,) + patches
4165 patches = (patch1,) + patches
4168
4166
4169 date = opts.get(b'date')
4167 date = opts.get(b'date')
4170 if date:
4168 if date:
4171 opts[b'date'] = dateutil.parsedate(date)
4169 opts[b'date'] = dateutil.parsedate(date)
4172
4170
4173 exact = opts.get(b'exact')
4171 exact = opts.get(b'exact')
4174 update = not opts.get(b'bypass')
4172 update = not opts.get(b'bypass')
4175 try:
4173 try:
4176 sim = float(opts.get(b'similarity') or 0)
4174 sim = float(opts.get(b'similarity') or 0)
4177 except ValueError:
4175 except ValueError:
4178 raise error.InputError(_(b'similarity must be a number'))
4176 raise error.InputError(_(b'similarity must be a number'))
4179 if sim < 0 or sim > 100:
4177 if sim < 0 or sim > 100:
4180 raise error.InputError(_(b'similarity must be between 0 and 100'))
4178 raise error.InputError(_(b'similarity must be between 0 and 100'))
4181 if sim and not update:
4179 if sim and not update:
4182 raise error.InputError(_(b'cannot use --similarity with --bypass'))
4180 raise error.InputError(_(b'cannot use --similarity with --bypass'))
4183
4181
4184 base = opts[b"base"]
4182 base = opts[b"base"]
4185 msgs = []
4183 msgs = []
4186 ret = 0
4184 ret = 0
4187
4185
4188 with repo.wlock():
4186 with repo.wlock():
4189 if update:
4187 if update:
4190 cmdutil.checkunfinished(repo)
4188 cmdutil.checkunfinished(repo)
4191 if exact or not opts.get(b'force'):
4189 if exact or not opts.get(b'force'):
4192 cmdutil.bailifchanged(repo)
4190 cmdutil.bailifchanged(repo)
4193
4191
4194 if not opts.get(b'no_commit'):
4192 if not opts.get(b'no_commit'):
4195 lock = repo.lock
4193 lock = repo.lock
4196 tr = lambda: repo.transaction(b'import')
4194 tr = lambda: repo.transaction(b'import')
4197 dsguard = util.nullcontextmanager
4195 dsguard = util.nullcontextmanager
4198 else:
4196 else:
4199 lock = util.nullcontextmanager
4197 lock = util.nullcontextmanager
4200 tr = util.nullcontextmanager
4198 tr = util.nullcontextmanager
4201 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4199 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4202 with lock(), tr(), dsguard():
4200 with lock(), tr(), dsguard():
4203 parents = repo[None].parents()
4201 parents = repo[None].parents()
4204 for patchurl in patches:
4202 for patchurl in patches:
4205 if patchurl == b'-':
4203 if patchurl == b'-':
4206 ui.status(_(b'applying patch from stdin\n'))
4204 ui.status(_(b'applying patch from stdin\n'))
4207 patchfile = ui.fin
4205 patchfile = ui.fin
4208 patchurl = b'stdin' # for error message
4206 patchurl = b'stdin' # for error message
4209 else:
4207 else:
4210 patchurl = os.path.join(base, patchurl)
4208 patchurl = os.path.join(base, patchurl)
4211 ui.status(_(b'applying %s\n') % patchurl)
4209 ui.status(_(b'applying %s\n') % patchurl)
4212 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4210 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4213
4211
4214 haspatch = False
4212 haspatch = False
4215 for hunk in patch.split(patchfile):
4213 for hunk in patch.split(patchfile):
4216 with patch.extract(ui, hunk) as patchdata:
4214 with patch.extract(ui, hunk) as patchdata:
4217 msg, node, rej = cmdutil.tryimportone(
4215 msg, node, rej = cmdutil.tryimportone(
4218 ui, repo, patchdata, parents, opts, msgs, hg.clean
4216 ui, repo, patchdata, parents, opts, msgs, hg.clean
4219 )
4217 )
4220 if msg:
4218 if msg:
4221 haspatch = True
4219 haspatch = True
4222 ui.note(msg + b'\n')
4220 ui.note(msg + b'\n')
4223 if update or exact:
4221 if update or exact:
4224 parents = repo[None].parents()
4222 parents = repo[None].parents()
4225 else:
4223 else:
4226 parents = [repo[node]]
4224 parents = [repo[node]]
4227 if rej:
4225 if rej:
4228 ui.write_err(_(b"patch applied partially\n"))
4226 ui.write_err(_(b"patch applied partially\n"))
4229 ui.write_err(
4227 ui.write_err(
4230 _(
4228 _(
4231 b"(fix the .rej files and run "
4229 b"(fix the .rej files and run "
4232 b"`hg commit --amend`)\n"
4230 b"`hg commit --amend`)\n"
4233 )
4231 )
4234 )
4232 )
4235 ret = 1
4233 ret = 1
4236 break
4234 break
4237
4235
4238 if not haspatch:
4236 if not haspatch:
4239 raise error.InputError(_(b'%s: no diffs found') % patchurl)
4237 raise error.InputError(_(b'%s: no diffs found') % patchurl)
4240
4238
4241 if msgs:
4239 if msgs:
4242 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4240 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4243 return ret
4241 return ret
4244
4242
4245
4243
4246 @command(
4244 @command(
4247 b'incoming|in',
4245 b'incoming|in',
4248 [
4246 [
4249 (
4247 (
4250 b'f',
4248 b'f',
4251 b'force',
4249 b'force',
4252 None,
4250 None,
4253 _(b'run even if remote repository is unrelated'),
4251 _(b'run even if remote repository is unrelated'),
4254 ),
4252 ),
4255 (b'n', b'newest-first', None, _(b'show newest record first')),
4253 (b'n', b'newest-first', None, _(b'show newest record first')),
4256 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4254 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4257 (
4255 (
4258 b'r',
4256 b'r',
4259 b'rev',
4257 b'rev',
4260 [],
4258 [],
4261 _(b'a remote changeset intended to be added'),
4259 _(b'a remote changeset intended to be added'),
4262 _(b'REV'),
4260 _(b'REV'),
4263 ),
4261 ),
4264 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4262 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4265 (
4263 (
4266 b'b',
4264 b'b',
4267 b'branch',
4265 b'branch',
4268 [],
4266 [],
4269 _(b'a specific branch you would like to pull'),
4267 _(b'a specific branch you would like to pull'),
4270 _(b'BRANCH'),
4268 _(b'BRANCH'),
4271 ),
4269 ),
4272 ]
4270 ]
4273 + logopts
4271 + logopts
4274 + remoteopts
4272 + remoteopts
4275 + subrepoopts,
4273 + subrepoopts,
4276 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4274 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4277 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4275 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4278 )
4276 )
4279 def incoming(ui, repo, source=b"default", **opts):
4277 def incoming(ui, repo, source=b"default", **opts):
4280 """show new changesets found in source
4278 """show new changesets found in source
4281
4279
4282 Show new changesets found in the specified path/URL or the default
4280 Show new changesets found in the specified path/URL or the default
4283 pull location. These are the changesets that would have been pulled
4281 pull location. These are the changesets that would have been pulled
4284 by :hg:`pull` at the time you issued this command.
4282 by :hg:`pull` at the time you issued this command.
4285
4283
4286 See pull for valid source format details.
4284 See pull for valid source format details.
4287
4285
4288 .. container:: verbose
4286 .. container:: verbose
4289
4287
4290 With -B/--bookmarks, the result of bookmark comparison between
4288 With -B/--bookmarks, the result of bookmark comparison between
4291 local and remote repositories is displayed. With -v/--verbose,
4289 local and remote repositories is displayed. With -v/--verbose,
4292 status is also displayed for each bookmark like below::
4290 status is also displayed for each bookmark like below::
4293
4291
4294 BM1 01234567890a added
4292 BM1 01234567890a added
4295 BM2 1234567890ab advanced
4293 BM2 1234567890ab advanced
4296 BM3 234567890abc diverged
4294 BM3 234567890abc diverged
4297 BM4 34567890abcd changed
4295 BM4 34567890abcd changed
4298
4296
4299 The action taken locally when pulling depends on the
4297 The action taken locally when pulling depends on the
4300 status of each bookmark:
4298 status of each bookmark:
4301
4299
4302 :``added``: pull will create it
4300 :``added``: pull will create it
4303 :``advanced``: pull will update it
4301 :``advanced``: pull will update it
4304 :``diverged``: pull will create a divergent bookmark
4302 :``diverged``: pull will create a divergent bookmark
4305 :``changed``: result depends on remote changesets
4303 :``changed``: result depends on remote changesets
4306
4304
4307 From the point of view of pulling behavior, bookmark
4305 From the point of view of pulling behavior, bookmark
4308 existing only in the remote repository are treated as ``added``,
4306 existing only in the remote repository are treated as ``added``,
4309 even if it is in fact locally deleted.
4307 even if it is in fact locally deleted.
4310
4308
4311 .. container:: verbose
4309 .. container:: verbose
4312
4310
4313 For remote repository, using --bundle avoids downloading the
4311 For remote repository, using --bundle avoids downloading the
4314 changesets twice if the incoming is followed by a pull.
4312 changesets twice if the incoming is followed by a pull.
4315
4313
4316 Examples:
4314 Examples:
4317
4315
4318 - show incoming changes with patches and full description::
4316 - show incoming changes with patches and full description::
4319
4317
4320 hg incoming -vp
4318 hg incoming -vp
4321
4319
4322 - show incoming changes excluding merges, store a bundle::
4320 - show incoming changes excluding merges, store a bundle::
4323
4321
4324 hg in -vpM --bundle incoming.hg
4322 hg in -vpM --bundle incoming.hg
4325 hg pull incoming.hg
4323 hg pull incoming.hg
4326
4324
4327 - briefly list changes inside a bundle::
4325 - briefly list changes inside a bundle::
4328
4326
4329 hg in changes.hg -T "{desc|firstline}\\n"
4327 hg in changes.hg -T "{desc|firstline}\\n"
4330
4328
4331 Returns 0 if there are incoming changes, 1 otherwise.
4329 Returns 0 if there are incoming changes, 1 otherwise.
4332 """
4330 """
4333 opts = pycompat.byteskwargs(opts)
4331 opts = pycompat.byteskwargs(opts)
4334 if opts.get(b'graph'):
4332 if opts.get(b'graph'):
4335 logcmdutil.checkunsupportedgraphflags([], opts)
4333 logcmdutil.checkunsupportedgraphflags([], opts)
4336
4334
4337 def display(other, chlist, displayer):
4335 def display(other, chlist, displayer):
4338 revdag = logcmdutil.graphrevs(other, chlist, opts)
4336 revdag = logcmdutil.graphrevs(other, chlist, opts)
4339 logcmdutil.displaygraph(
4337 logcmdutil.displaygraph(
4340 ui, repo, revdag, displayer, graphmod.asciiedges
4338 ui, repo, revdag, displayer, graphmod.asciiedges
4341 )
4339 )
4342
4340
4343 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4341 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4344 return 0
4342 return 0
4345
4343
4346 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4344 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4347
4345
4348 if opts.get(b'bookmarks'):
4346 if opts.get(b'bookmarks'):
4349 srcs = urlutil.get_pull_paths(repo, ui, [source])
4347 srcs = urlutil.get_pull_paths(repo, ui, [source])
4350 for path in srcs:
4348 for path in srcs:
4351 source, branches = urlutil.parseurl(
4349 source, branches = urlutil.parseurl(
4352 path.rawloc, opts.get(b'branch')
4350 path.rawloc, opts.get(b'branch')
4353 )
4351 )
4354 other = hg.peer(repo, opts, source)
4352 other = hg.peer(repo, opts, source)
4355 try:
4353 try:
4356 if b'bookmarks' not in other.listkeys(b'namespaces'):
4354 if b'bookmarks' not in other.listkeys(b'namespaces'):
4357 ui.warn(_(b"remote doesn't support bookmarks\n"))
4355 ui.warn(_(b"remote doesn't support bookmarks\n"))
4358 return 0
4356 return 0
4359 ui.pager(b'incoming')
4357 ui.pager(b'incoming')
4360 ui.status(
4358 ui.status(
4361 _(b'comparing with %s\n') % urlutil.hidepassword(source)
4359 _(b'comparing with %s\n') % urlutil.hidepassword(source)
4362 )
4360 )
4363 return bookmarks.incoming(
4361 return bookmarks.incoming(
4364 ui, repo, other, mode=path.bookmarks_mode
4362 ui, repo, other, mode=path.bookmarks_mode
4365 )
4363 )
4366 finally:
4364 finally:
4367 other.close()
4365 other.close()
4368
4366
4369 return hg.incoming(ui, repo, source, opts)
4367 return hg.incoming(ui, repo, source, opts)
4370
4368
4371
4369
4372 @command(
4370 @command(
4373 b'init',
4371 b'init',
4374 remoteopts,
4372 remoteopts,
4375 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4373 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4376 helpcategory=command.CATEGORY_REPO_CREATION,
4374 helpcategory=command.CATEGORY_REPO_CREATION,
4377 helpbasic=True,
4375 helpbasic=True,
4378 norepo=True,
4376 norepo=True,
4379 )
4377 )
4380 def init(ui, dest=b".", **opts):
4378 def init(ui, dest=b".", **opts):
4381 """create a new repository in the given directory
4379 """create a new repository in the given directory
4382
4380
4383 Initialize a new repository in the given directory. If the given
4381 Initialize a new repository in the given directory. If the given
4384 directory does not exist, it will be created.
4382 directory does not exist, it will be created.
4385
4383
4386 If no directory is given, the current directory is used.
4384 If no directory is given, the current directory is used.
4387
4385
4388 It is possible to specify an ``ssh://`` URL as the destination.
4386 It is possible to specify an ``ssh://`` URL as the destination.
4389 See :hg:`help urls` for more information.
4387 See :hg:`help urls` for more information.
4390
4388
4391 Returns 0 on success.
4389 Returns 0 on success.
4392 """
4390 """
4393 opts = pycompat.byteskwargs(opts)
4391 opts = pycompat.byteskwargs(opts)
4394 path = urlutil.get_clone_path(ui, dest)[1]
4392 path = urlutil.get_clone_path(ui, dest)[1]
4395 peer = hg.peer(ui, opts, path, create=True)
4393 peer = hg.peer(ui, opts, path, create=True)
4396 peer.close()
4394 peer.close()
4397
4395
4398
4396
4399 @command(
4397 @command(
4400 b'locate',
4398 b'locate',
4401 [
4399 [
4402 (
4400 (
4403 b'r',
4401 b'r',
4404 b'rev',
4402 b'rev',
4405 b'',
4403 b'',
4406 _(b'search the repository as it is in REV'),
4404 _(b'search the repository as it is in REV'),
4407 _(b'REV'),
4405 _(b'REV'),
4408 ),
4406 ),
4409 (
4407 (
4410 b'0',
4408 b'0',
4411 b'print0',
4409 b'print0',
4412 None,
4410 None,
4413 _(b'end filenames with NUL, for use with xargs'),
4411 _(b'end filenames with NUL, for use with xargs'),
4414 ),
4412 ),
4415 (
4413 (
4416 b'f',
4414 b'f',
4417 b'fullpath',
4415 b'fullpath',
4418 None,
4416 None,
4419 _(b'print complete paths from the filesystem root'),
4417 _(b'print complete paths from the filesystem root'),
4420 ),
4418 ),
4421 ]
4419 ]
4422 + walkopts,
4420 + walkopts,
4423 _(b'[OPTION]... [PATTERN]...'),
4421 _(b'[OPTION]... [PATTERN]...'),
4424 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4422 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4425 )
4423 )
4426 def locate(ui, repo, *pats, **opts):
4424 def locate(ui, repo, *pats, **opts):
4427 """locate files matching specific patterns (DEPRECATED)
4425 """locate files matching specific patterns (DEPRECATED)
4428
4426
4429 Print files under Mercurial control in the working directory whose
4427 Print files under Mercurial control in the working directory whose
4430 names match the given patterns.
4428 names match the given patterns.
4431
4429
4432 By default, this command searches all directories in the working
4430 By default, this command searches all directories in the working
4433 directory. To search just the current directory and its
4431 directory. To search just the current directory and its
4434 subdirectories, use "--include .".
4432 subdirectories, use "--include .".
4435
4433
4436 If no patterns are given to match, this command prints the names
4434 If no patterns are given to match, this command prints the names
4437 of all files under Mercurial control in the working directory.
4435 of all files under Mercurial control in the working directory.
4438
4436
4439 If you want to feed the output of this command into the "xargs"
4437 If you want to feed the output of this command into the "xargs"
4440 command, use the -0 option to both this command and "xargs". This
4438 command, use the -0 option to both this command and "xargs". This
4441 will avoid the problem of "xargs" treating single filenames that
4439 will avoid the problem of "xargs" treating single filenames that
4442 contain whitespace as multiple filenames.
4440 contain whitespace as multiple filenames.
4443
4441
4444 See :hg:`help files` for a more versatile command.
4442 See :hg:`help files` for a more versatile command.
4445
4443
4446 Returns 0 if a match is found, 1 otherwise.
4444 Returns 0 if a match is found, 1 otherwise.
4447 """
4445 """
4448 opts = pycompat.byteskwargs(opts)
4446 opts = pycompat.byteskwargs(opts)
4449 if opts.get(b'print0'):
4447 if opts.get(b'print0'):
4450 end = b'\0'
4448 end = b'\0'
4451 else:
4449 else:
4452 end = b'\n'
4450 end = b'\n'
4453 ctx = logcmdutil.revsingle(repo, opts.get(b'rev'), None)
4451 ctx = logcmdutil.revsingle(repo, opts.get(b'rev'), None)
4454
4452
4455 ret = 1
4453 ret = 1
4456 m = scmutil.match(
4454 m = scmutil.match(
4457 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4455 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4458 )
4456 )
4459
4457
4460 ui.pager(b'locate')
4458 ui.pager(b'locate')
4461 if ctx.rev() is None:
4459 if ctx.rev() is None:
4462 # When run on the working copy, "locate" includes removed files, so
4460 # When run on the working copy, "locate" includes removed files, so
4463 # we get the list of files from the dirstate.
4461 # we get the list of files from the dirstate.
4464 filesgen = sorted(repo.dirstate.matches(m))
4462 filesgen = sorted(repo.dirstate.matches(m))
4465 else:
4463 else:
4466 filesgen = ctx.matches(m)
4464 filesgen = ctx.matches(m)
4467 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4465 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4468 for abs in filesgen:
4466 for abs in filesgen:
4469 if opts.get(b'fullpath'):
4467 if opts.get(b'fullpath'):
4470 ui.write(repo.wjoin(abs), end)
4468 ui.write(repo.wjoin(abs), end)
4471 else:
4469 else:
4472 ui.write(uipathfn(abs), end)
4470 ui.write(uipathfn(abs), end)
4473 ret = 0
4471 ret = 0
4474
4472
4475 return ret
4473 return ret
4476
4474
4477
4475
4478 @command(
4476 @command(
4479 b'log|history',
4477 b'log|history',
4480 [
4478 [
4481 (
4479 (
4482 b'f',
4480 b'f',
4483 b'follow',
4481 b'follow',
4484 None,
4482 None,
4485 _(
4483 _(
4486 b'follow changeset history, or file history across copies and renames'
4484 b'follow changeset history, or file history across copies and renames'
4487 ),
4485 ),
4488 ),
4486 ),
4489 (
4487 (
4490 b'',
4488 b'',
4491 b'follow-first',
4489 b'follow-first',
4492 None,
4490 None,
4493 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4491 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4494 ),
4492 ),
4495 (
4493 (
4496 b'd',
4494 b'd',
4497 b'date',
4495 b'date',
4498 b'',
4496 b'',
4499 _(b'show revisions matching date spec'),
4497 _(b'show revisions matching date spec'),
4500 _(b'DATE'),
4498 _(b'DATE'),
4501 ),
4499 ),
4502 (b'C', b'copies', None, _(b'show copied files')),
4500 (b'C', b'copies', None, _(b'show copied files')),
4503 (
4501 (
4504 b'k',
4502 b'k',
4505 b'keyword',
4503 b'keyword',
4506 [],
4504 [],
4507 _(b'do case-insensitive search for a given text'),
4505 _(b'do case-insensitive search for a given text'),
4508 _(b'TEXT'),
4506 _(b'TEXT'),
4509 ),
4507 ),
4510 (
4508 (
4511 b'r',
4509 b'r',
4512 b'rev',
4510 b'rev',
4513 [],
4511 [],
4514 _(b'revisions to select or follow from'),
4512 _(b'revisions to select or follow from'),
4515 _(b'REV'),
4513 _(b'REV'),
4516 ),
4514 ),
4517 (
4515 (
4518 b'L',
4516 b'L',
4519 b'line-range',
4517 b'line-range',
4520 [],
4518 [],
4521 _(b'follow line range of specified file (EXPERIMENTAL)'),
4519 _(b'follow line range of specified file (EXPERIMENTAL)'),
4522 _(b'FILE,RANGE'),
4520 _(b'FILE,RANGE'),
4523 ),
4521 ),
4524 (
4522 (
4525 b'',
4523 b'',
4526 b'removed',
4524 b'removed',
4527 None,
4525 None,
4528 _(b'include revisions where files were removed'),
4526 _(b'include revisions where files were removed'),
4529 ),
4527 ),
4530 (
4528 (
4531 b'm',
4529 b'm',
4532 b'only-merges',
4530 b'only-merges',
4533 None,
4531 None,
4534 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4532 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4535 ),
4533 ),
4536 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4534 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4537 (
4535 (
4538 b'',
4536 b'',
4539 b'only-branch',
4537 b'only-branch',
4540 [],
4538 [],
4541 _(
4539 _(
4542 b'show only changesets within the given named branch (DEPRECATED)'
4540 b'show only changesets within the given named branch (DEPRECATED)'
4543 ),
4541 ),
4544 _(b'BRANCH'),
4542 _(b'BRANCH'),
4545 ),
4543 ),
4546 (
4544 (
4547 b'b',
4545 b'b',
4548 b'branch',
4546 b'branch',
4549 [],
4547 [],
4550 _(b'show changesets within the given named branch'),
4548 _(b'show changesets within the given named branch'),
4551 _(b'BRANCH'),
4549 _(b'BRANCH'),
4552 ),
4550 ),
4553 (
4551 (
4554 b'B',
4552 b'B',
4555 b'bookmark',
4553 b'bookmark',
4556 [],
4554 [],
4557 _(b"show changesets within the given bookmark"),
4555 _(b"show changesets within the given bookmark"),
4558 _(b'BOOKMARK'),
4556 _(b'BOOKMARK'),
4559 ),
4557 ),
4560 (
4558 (
4561 b'P',
4559 b'P',
4562 b'prune',
4560 b'prune',
4563 [],
4561 [],
4564 _(b'do not display revision or any of its ancestors'),
4562 _(b'do not display revision or any of its ancestors'),
4565 _(b'REV'),
4563 _(b'REV'),
4566 ),
4564 ),
4567 ]
4565 ]
4568 + logopts
4566 + logopts
4569 + walkopts,
4567 + walkopts,
4570 _(b'[OPTION]... [FILE]'),
4568 _(b'[OPTION]... [FILE]'),
4571 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4569 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4572 helpbasic=True,
4570 helpbasic=True,
4573 inferrepo=True,
4571 inferrepo=True,
4574 intents={INTENT_READONLY},
4572 intents={INTENT_READONLY},
4575 )
4573 )
4576 def log(ui, repo, *pats, **opts):
4574 def log(ui, repo, *pats, **opts):
4577 """show revision history of entire repository or files
4575 """show revision history of entire repository or files
4578
4576
4579 Print the revision history of the specified files or the entire
4577 Print the revision history of the specified files or the entire
4580 project.
4578 project.
4581
4579
4582 If no revision range is specified, the default is ``tip:0`` unless
4580 If no revision range is specified, the default is ``tip:0`` unless
4583 --follow is set.
4581 --follow is set.
4584
4582
4585 File history is shown without following rename or copy history of
4583 File history is shown without following rename or copy history of
4586 files. Use -f/--follow with a filename to follow history across
4584 files. Use -f/--follow with a filename to follow history across
4587 renames and copies. --follow without a filename will only show
4585 renames and copies. --follow without a filename will only show
4588 ancestors of the starting revisions. The starting revisions can be
4586 ancestors of the starting revisions. The starting revisions can be
4589 specified by -r/--rev, which default to the working directory parent.
4587 specified by -r/--rev, which default to the working directory parent.
4590
4588
4591 By default this command prints revision number and changeset id,
4589 By default this command prints revision number and changeset id,
4592 tags, non-trivial parents, user, date and time, and a summary for
4590 tags, non-trivial parents, user, date and time, and a summary for
4593 each commit. When the -v/--verbose switch is used, the list of
4591 each commit. When the -v/--verbose switch is used, the list of
4594 changed files and full commit message are shown.
4592 changed files and full commit message are shown.
4595
4593
4596 With --graph the revisions are shown as an ASCII art DAG with the most
4594 With --graph the revisions are shown as an ASCII art DAG with the most
4597 recent changeset at the top.
4595 recent changeset at the top.
4598 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4596 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4599 involved in an unresolved merge conflict, '_' closes a branch,
4597 involved in an unresolved merge conflict, '_' closes a branch,
4600 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4598 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4601 changeset from the lines below is a parent of the 'o' merge on the same
4599 changeset from the lines below is a parent of the 'o' merge on the same
4602 line.
4600 line.
4603 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4601 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4604 of a '|' indicates one or more revisions in a path are omitted.
4602 of a '|' indicates one or more revisions in a path are omitted.
4605
4603
4606 .. container:: verbose
4604 .. container:: verbose
4607
4605
4608 Use -L/--line-range FILE,M:N options to follow the history of lines
4606 Use -L/--line-range FILE,M:N options to follow the history of lines
4609 from M to N in FILE. With -p/--patch only diff hunks affecting
4607 from M to N in FILE. With -p/--patch only diff hunks affecting
4610 specified line range will be shown. This option requires --follow;
4608 specified line range will be shown. This option requires --follow;
4611 it can be specified multiple times. Currently, this option is not
4609 it can be specified multiple times. Currently, this option is not
4612 compatible with --graph. This option is experimental.
4610 compatible with --graph. This option is experimental.
4613
4611
4614 .. note::
4612 .. note::
4615
4613
4616 :hg:`log --patch` may generate unexpected diff output for merge
4614 :hg:`log --patch` may generate unexpected diff output for merge
4617 changesets, as it will only compare the merge changeset against
4615 changesets, as it will only compare the merge changeset against
4618 its first parent. Also, only files different from BOTH parents
4616 its first parent. Also, only files different from BOTH parents
4619 will appear in files:.
4617 will appear in files:.
4620
4618
4621 .. note::
4619 .. note::
4622
4620
4623 For performance reasons, :hg:`log FILE` may omit duplicate changes
4621 For performance reasons, :hg:`log FILE` may omit duplicate changes
4624 made on branches and will not show removals or mode changes. To
4622 made on branches and will not show removals or mode changes. To
4625 see all such changes, use the --removed switch.
4623 see all such changes, use the --removed switch.
4626
4624
4627 .. container:: verbose
4625 .. container:: verbose
4628
4626
4629 .. note::
4627 .. note::
4630
4628
4631 The history resulting from -L/--line-range options depends on diff
4629 The history resulting from -L/--line-range options depends on diff
4632 options; for instance if white-spaces are ignored, respective changes
4630 options; for instance if white-spaces are ignored, respective changes
4633 with only white-spaces in specified line range will not be listed.
4631 with only white-spaces in specified line range will not be listed.
4634
4632
4635 .. container:: verbose
4633 .. container:: verbose
4636
4634
4637 Some examples:
4635 Some examples:
4638
4636
4639 - changesets with full descriptions and file lists::
4637 - changesets with full descriptions and file lists::
4640
4638
4641 hg log -v
4639 hg log -v
4642
4640
4643 - changesets ancestral to the working directory::
4641 - changesets ancestral to the working directory::
4644
4642
4645 hg log -f
4643 hg log -f
4646
4644
4647 - last 10 commits on the current branch::
4645 - last 10 commits on the current branch::
4648
4646
4649 hg log -l 10 -b .
4647 hg log -l 10 -b .
4650
4648
4651 - changesets showing all modifications of a file, including removals::
4649 - changesets showing all modifications of a file, including removals::
4652
4650
4653 hg log --removed file.c
4651 hg log --removed file.c
4654
4652
4655 - all changesets that touch a directory, with diffs, excluding merges::
4653 - all changesets that touch a directory, with diffs, excluding merges::
4656
4654
4657 hg log -Mp lib/
4655 hg log -Mp lib/
4658
4656
4659 - all revision numbers that match a keyword::
4657 - all revision numbers that match a keyword::
4660
4658
4661 hg log -k bug --template "{rev}\\n"
4659 hg log -k bug --template "{rev}\\n"
4662
4660
4663 - the full hash identifier of the working directory parent::
4661 - the full hash identifier of the working directory parent::
4664
4662
4665 hg log -r . --template "{node}\\n"
4663 hg log -r . --template "{node}\\n"
4666
4664
4667 - list available log templates::
4665 - list available log templates::
4668
4666
4669 hg log -T list
4667 hg log -T list
4670
4668
4671 - check if a given changeset is included in a tagged release::
4669 - check if a given changeset is included in a tagged release::
4672
4670
4673 hg log -r "a21ccf and ancestor(1.9)"
4671 hg log -r "a21ccf and ancestor(1.9)"
4674
4672
4675 - find all changesets by some user in a date range::
4673 - find all changesets by some user in a date range::
4676
4674
4677 hg log -k alice -d "may 2008 to jul 2008"
4675 hg log -k alice -d "may 2008 to jul 2008"
4678
4676
4679 - summary of all changesets after the last tag::
4677 - summary of all changesets after the last tag::
4680
4678
4681 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4679 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4682
4680
4683 - changesets touching lines 13 to 23 for file.c::
4681 - changesets touching lines 13 to 23 for file.c::
4684
4682
4685 hg log -L file.c,13:23
4683 hg log -L file.c,13:23
4686
4684
4687 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4685 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4688 main.c with patch::
4686 main.c with patch::
4689
4687
4690 hg log -L file.c,13:23 -L main.c,2:6 -p
4688 hg log -L file.c,13:23 -L main.c,2:6 -p
4691
4689
4692 See :hg:`help dates` for a list of formats valid for -d/--date.
4690 See :hg:`help dates` for a list of formats valid for -d/--date.
4693
4691
4694 See :hg:`help revisions` for more about specifying and ordering
4692 See :hg:`help revisions` for more about specifying and ordering
4695 revisions.
4693 revisions.
4696
4694
4697 See :hg:`help templates` for more about pre-packaged styles and
4695 See :hg:`help templates` for more about pre-packaged styles and
4698 specifying custom templates. The default template used by the log
4696 specifying custom templates. The default template used by the log
4699 command can be customized via the ``command-templates.log`` configuration
4697 command can be customized via the ``command-templates.log`` configuration
4700 setting.
4698 setting.
4701
4699
4702 Returns 0 on success.
4700 Returns 0 on success.
4703
4701
4704 """
4702 """
4705 opts = pycompat.byteskwargs(opts)
4703 opts = pycompat.byteskwargs(opts)
4706 linerange = opts.get(b'line_range')
4704 linerange = opts.get(b'line_range')
4707
4705
4708 if linerange and not opts.get(b'follow'):
4706 if linerange and not opts.get(b'follow'):
4709 raise error.InputError(_(b'--line-range requires --follow'))
4707 raise error.InputError(_(b'--line-range requires --follow'))
4710
4708
4711 if linerange and pats:
4709 if linerange and pats:
4712 # TODO: take pats as patterns with no line-range filter
4710 # TODO: take pats as patterns with no line-range filter
4713 raise error.InputError(
4711 raise error.InputError(
4714 _(b'FILE arguments are not compatible with --line-range option')
4712 _(b'FILE arguments are not compatible with --line-range option')
4715 )
4713 )
4716
4714
4717 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4715 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4718 walk_opts = logcmdutil.parseopts(ui, pats, opts)
4716 walk_opts = logcmdutil.parseopts(ui, pats, opts)
4719 revs, differ = logcmdutil.getrevs(repo, walk_opts)
4717 revs, differ = logcmdutil.getrevs(repo, walk_opts)
4720 if linerange:
4718 if linerange:
4721 # TODO: should follow file history from logcmdutil._initialrevs(),
4719 # TODO: should follow file history from logcmdutil._initialrevs(),
4722 # then filter the result by logcmdutil._makerevset() and --limit
4720 # then filter the result by logcmdutil._makerevset() and --limit
4723 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4721 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4724
4722
4725 getcopies = None
4723 getcopies = None
4726 if opts.get(b'copies'):
4724 if opts.get(b'copies'):
4727 endrev = None
4725 endrev = None
4728 if revs:
4726 if revs:
4729 endrev = revs.max() + 1
4727 endrev = revs.max() + 1
4730 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4728 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4731
4729
4732 ui.pager(b'log')
4730 ui.pager(b'log')
4733 displayer = logcmdutil.changesetdisplayer(
4731 displayer = logcmdutil.changesetdisplayer(
4734 ui, repo, opts, differ, buffered=True
4732 ui, repo, opts, differ, buffered=True
4735 )
4733 )
4736 if opts.get(b'graph'):
4734 if opts.get(b'graph'):
4737 displayfn = logcmdutil.displaygraphrevs
4735 displayfn = logcmdutil.displaygraphrevs
4738 else:
4736 else:
4739 displayfn = logcmdutil.displayrevs
4737 displayfn = logcmdutil.displayrevs
4740 displayfn(ui, repo, revs, displayer, getcopies)
4738 displayfn(ui, repo, revs, displayer, getcopies)
4741
4739
4742
4740
4743 @command(
4741 @command(
4744 b'manifest',
4742 b'manifest',
4745 [
4743 [
4746 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4744 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4747 (b'', b'all', False, _(b"list files from all revisions")),
4745 (b'', b'all', False, _(b"list files from all revisions")),
4748 ]
4746 ]
4749 + formatteropts,
4747 + formatteropts,
4750 _(b'[-r REV]'),
4748 _(b'[-r REV]'),
4751 helpcategory=command.CATEGORY_MAINTENANCE,
4749 helpcategory=command.CATEGORY_MAINTENANCE,
4752 intents={INTENT_READONLY},
4750 intents={INTENT_READONLY},
4753 )
4751 )
4754 def manifest(ui, repo, node=None, rev=None, **opts):
4752 def manifest(ui, repo, node=None, rev=None, **opts):
4755 """output the current or given revision of the project manifest
4753 """output the current or given revision of the project manifest
4756
4754
4757 Print a list of version controlled files for the given revision.
4755 Print a list of version controlled files for the given revision.
4758 If no revision is given, the first parent of the working directory
4756 If no revision is given, the first parent of the working directory
4759 is used, or the null revision if no revision is checked out.
4757 is used, or the null revision if no revision is checked out.
4760
4758
4761 With -v, print file permissions, symlink and executable bits.
4759 With -v, print file permissions, symlink and executable bits.
4762 With --debug, print file revision hashes.
4760 With --debug, print file revision hashes.
4763
4761
4764 If option --all is specified, the list of all files from all revisions
4762 If option --all is specified, the list of all files from all revisions
4765 is printed. This includes deleted and renamed files.
4763 is printed. This includes deleted and renamed files.
4766
4764
4767 Returns 0 on success.
4765 Returns 0 on success.
4768 """
4766 """
4769 opts = pycompat.byteskwargs(opts)
4767 opts = pycompat.byteskwargs(opts)
4770 fm = ui.formatter(b'manifest', opts)
4768 fm = ui.formatter(b'manifest', opts)
4771
4769
4772 if opts.get(b'all'):
4770 if opts.get(b'all'):
4773 if rev or node:
4771 if rev or node:
4774 raise error.InputError(_(b"can't specify a revision with --all"))
4772 raise error.InputError(_(b"can't specify a revision with --all"))
4775
4773
4776 res = set()
4774 res = set()
4777 for rev in repo:
4775 for rev in repo:
4778 ctx = repo[rev]
4776 ctx = repo[rev]
4779 res |= set(ctx.files())
4777 res |= set(ctx.files())
4780
4778
4781 ui.pager(b'manifest')
4779 ui.pager(b'manifest')
4782 for f in sorted(res):
4780 for f in sorted(res):
4783 fm.startitem()
4781 fm.startitem()
4784 fm.write(b"path", b'%s\n', f)
4782 fm.write(b"path", b'%s\n', f)
4785 fm.end()
4783 fm.end()
4786 return
4784 return
4787
4785
4788 if rev and node:
4786 if rev and node:
4789 raise error.InputError(_(b"please specify just one revision"))
4787 raise error.InputError(_(b"please specify just one revision"))
4790
4788
4791 if not node:
4789 if not node:
4792 node = rev
4790 node = rev
4793
4791
4794 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4792 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4795 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4793 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4796 if node:
4794 if node:
4797 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4795 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4798 ctx = logcmdutil.revsingle(repo, node)
4796 ctx = logcmdutil.revsingle(repo, node)
4799 mf = ctx.manifest()
4797 mf = ctx.manifest()
4800 ui.pager(b'manifest')
4798 ui.pager(b'manifest')
4801 for f in ctx:
4799 for f in ctx:
4802 fm.startitem()
4800 fm.startitem()
4803 fm.context(ctx=ctx)
4801 fm.context(ctx=ctx)
4804 fl = ctx[f].flags()
4802 fl = ctx[f].flags()
4805 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4803 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4806 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4804 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4807 fm.write(b'path', b'%s\n', f)
4805 fm.write(b'path', b'%s\n', f)
4808 fm.end()
4806 fm.end()
4809
4807
4810
4808
4811 @command(
4809 @command(
4812 b'merge',
4810 b'merge',
4813 [
4811 [
4814 (
4812 (
4815 b'f',
4813 b'f',
4816 b'force',
4814 b'force',
4817 None,
4815 None,
4818 _(b'force a merge including outstanding changes (DEPRECATED)'),
4816 _(b'force a merge including outstanding changes (DEPRECATED)'),
4819 ),
4817 ),
4820 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4818 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4821 (
4819 (
4822 b'P',
4820 b'P',
4823 b'preview',
4821 b'preview',
4824 None,
4822 None,
4825 _(b'review revisions to merge (no merge is performed)'),
4823 _(b'review revisions to merge (no merge is performed)'),
4826 ),
4824 ),
4827 (b'', b'abort', None, _(b'abort the ongoing merge')),
4825 (b'', b'abort', None, _(b'abort the ongoing merge')),
4828 ]
4826 ]
4829 + mergetoolopts,
4827 + mergetoolopts,
4830 _(b'[-P] [[-r] REV]'),
4828 _(b'[-P] [[-r] REV]'),
4831 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4829 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4832 helpbasic=True,
4830 helpbasic=True,
4833 )
4831 )
4834 def merge(ui, repo, node=None, **opts):
4832 def merge(ui, repo, node=None, **opts):
4835 """merge another revision into working directory
4833 """merge another revision into working directory
4836
4834
4837 The current working directory is updated with all changes made in
4835 The current working directory is updated with all changes made in
4838 the requested revision since the last common predecessor revision.
4836 the requested revision since the last common predecessor revision.
4839
4837
4840 Files that changed between either parent are marked as changed for
4838 Files that changed between either parent are marked as changed for
4841 the next commit and a commit must be performed before any further
4839 the next commit and a commit must be performed before any further
4842 updates to the repository are allowed. The next commit will have
4840 updates to the repository are allowed. The next commit will have
4843 two parents.
4841 two parents.
4844
4842
4845 ``--tool`` can be used to specify the merge tool used for file
4843 ``--tool`` can be used to specify the merge tool used for file
4846 merges. It overrides the HGMERGE environment variable and your
4844 merges. It overrides the HGMERGE environment variable and your
4847 configuration files. See :hg:`help merge-tools` for options.
4845 configuration files. See :hg:`help merge-tools` for options.
4848
4846
4849 If no revision is specified, the working directory's parent is a
4847 If no revision is specified, the working directory's parent is a
4850 head revision, and the current branch contains exactly one other
4848 head revision, and the current branch contains exactly one other
4851 head, the other head is merged with by default. Otherwise, an
4849 head, the other head is merged with by default. Otherwise, an
4852 explicit revision with which to merge must be provided.
4850 explicit revision with which to merge must be provided.
4853
4851
4854 See :hg:`help resolve` for information on handling file conflicts.
4852 See :hg:`help resolve` for information on handling file conflicts.
4855
4853
4856 To undo an uncommitted merge, use :hg:`merge --abort` which
4854 To undo an uncommitted merge, use :hg:`merge --abort` which
4857 will check out a clean copy of the original merge parent, losing
4855 will check out a clean copy of the original merge parent, losing
4858 all changes.
4856 all changes.
4859
4857
4860 Returns 0 on success, 1 if there are unresolved files.
4858 Returns 0 on success, 1 if there are unresolved files.
4861 """
4859 """
4862
4860
4863 opts = pycompat.byteskwargs(opts)
4861 opts = pycompat.byteskwargs(opts)
4864 abort = opts.get(b'abort')
4862 abort = opts.get(b'abort')
4865 if abort and repo.dirstate.p2() == repo.nullid:
4863 if abort and repo.dirstate.p2() == repo.nullid:
4866 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4864 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4867 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4865 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4868 if abort:
4866 if abort:
4869 state = cmdutil.getunfinishedstate(repo)
4867 state = cmdutil.getunfinishedstate(repo)
4870 if state and state._opname != b'merge':
4868 if state and state._opname != b'merge':
4871 raise error.StateError(
4869 raise error.StateError(
4872 _(b'cannot abort merge with %s in progress') % (state._opname),
4870 _(b'cannot abort merge with %s in progress') % (state._opname),
4873 hint=state.hint(),
4871 hint=state.hint(),
4874 )
4872 )
4875 if node:
4873 if node:
4876 raise error.InputError(_(b"cannot specify a node with --abort"))
4874 raise error.InputError(_(b"cannot specify a node with --abort"))
4877 return hg.abortmerge(repo.ui, repo)
4875 return hg.abortmerge(repo.ui, repo)
4878
4876
4879 if opts.get(b'rev') and node:
4877 if opts.get(b'rev') and node:
4880 raise error.InputError(_(b"please specify just one revision"))
4878 raise error.InputError(_(b"please specify just one revision"))
4881 if not node:
4879 if not node:
4882 node = opts.get(b'rev')
4880 node = opts.get(b'rev')
4883
4881
4884 if node:
4882 if node:
4885 ctx = logcmdutil.revsingle(repo, node)
4883 ctx = logcmdutil.revsingle(repo, node)
4886 else:
4884 else:
4887 if ui.configbool(b'commands', b'merge.require-rev'):
4885 if ui.configbool(b'commands', b'merge.require-rev'):
4888 raise error.InputError(
4886 raise error.InputError(
4889 _(
4887 _(
4890 b'configuration requires specifying revision to merge '
4888 b'configuration requires specifying revision to merge '
4891 b'with'
4889 b'with'
4892 )
4890 )
4893 )
4891 )
4894 ctx = repo[destutil.destmerge(repo)]
4892 ctx = repo[destutil.destmerge(repo)]
4895
4893
4896 if ctx.node() is None:
4894 if ctx.node() is None:
4897 raise error.InputError(
4895 raise error.InputError(
4898 _(b'merging with the working copy has no effect')
4896 _(b'merging with the working copy has no effect')
4899 )
4897 )
4900
4898
4901 if opts.get(b'preview'):
4899 if opts.get(b'preview'):
4902 # find nodes that are ancestors of p2 but not of p1
4900 # find nodes that are ancestors of p2 but not of p1
4903 p1 = repo[b'.'].node()
4901 p1 = repo[b'.'].node()
4904 p2 = ctx.node()
4902 p2 = ctx.node()
4905 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4903 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4906
4904
4907 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4905 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4908 for node in nodes:
4906 for node in nodes:
4909 displayer.show(repo[node])
4907 displayer.show(repo[node])
4910 displayer.close()
4908 displayer.close()
4911 return 0
4909 return 0
4912
4910
4913 # ui.forcemerge is an internal variable, do not document
4911 # ui.forcemerge is an internal variable, do not document
4914 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4912 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4915 with ui.configoverride(overrides, b'merge'):
4913 with ui.configoverride(overrides, b'merge'):
4916 force = opts.get(b'force')
4914 force = opts.get(b'force')
4917 labels = [b'working copy', b'merge rev', b'common ancestor']
4915 labels = [b'working copy', b'merge rev', b'common ancestor']
4918 return hg.merge(ctx, force=force, labels=labels)
4916 return hg.merge(ctx, force=force, labels=labels)
4919
4917
4920
4918
4921 statemod.addunfinished(
4919 statemod.addunfinished(
4922 b'merge',
4920 b'merge',
4923 fname=None,
4921 fname=None,
4924 clearable=True,
4922 clearable=True,
4925 allowcommit=True,
4923 allowcommit=True,
4926 cmdmsg=_(b'outstanding uncommitted merge'),
4924 cmdmsg=_(b'outstanding uncommitted merge'),
4927 abortfunc=hg.abortmerge,
4925 abortfunc=hg.abortmerge,
4928 statushint=_(
4926 statushint=_(
4929 b'To continue: hg commit\nTo abort: hg merge --abort'
4927 b'To continue: hg commit\nTo abort: hg merge --abort'
4930 ),
4928 ),
4931 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4929 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4932 )
4930 )
4933
4931
4934
4932
4935 @command(
4933 @command(
4936 b'outgoing|out',
4934 b'outgoing|out',
4937 [
4935 [
4938 (
4936 (
4939 b'f',
4937 b'f',
4940 b'force',
4938 b'force',
4941 None,
4939 None,
4942 _(b'run even when the destination is unrelated'),
4940 _(b'run even when the destination is unrelated'),
4943 ),
4941 ),
4944 (
4942 (
4945 b'r',
4943 b'r',
4946 b'rev',
4944 b'rev',
4947 [],
4945 [],
4948 _(b'a changeset intended to be included in the destination'),
4946 _(b'a changeset intended to be included in the destination'),
4949 _(b'REV'),
4947 _(b'REV'),
4950 ),
4948 ),
4951 (b'n', b'newest-first', None, _(b'show newest record first')),
4949 (b'n', b'newest-first', None, _(b'show newest record first')),
4952 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4950 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4953 (
4951 (
4954 b'b',
4952 b'b',
4955 b'branch',
4953 b'branch',
4956 [],
4954 [],
4957 _(b'a specific branch you would like to push'),
4955 _(b'a specific branch you would like to push'),
4958 _(b'BRANCH'),
4956 _(b'BRANCH'),
4959 ),
4957 ),
4960 ]
4958 ]
4961 + logopts
4959 + logopts
4962 + remoteopts
4960 + remoteopts
4963 + subrepoopts,
4961 + subrepoopts,
4964 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]...'),
4962 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]...'),
4965 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4963 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4966 )
4964 )
4967 def outgoing(ui, repo, *dests, **opts):
4965 def outgoing(ui, repo, *dests, **opts):
4968 """show changesets not found in the destination
4966 """show changesets not found in the destination
4969
4967
4970 Show changesets not found in the specified destination repository
4968 Show changesets not found in the specified destination repository
4971 or the default push location. These are the changesets that would
4969 or the default push location. These are the changesets that would
4972 be pushed if a push was requested.
4970 be pushed if a push was requested.
4973
4971
4974 See pull for details of valid destination formats.
4972 See pull for details of valid destination formats.
4975
4973
4976 .. container:: verbose
4974 .. container:: verbose
4977
4975
4978 With -B/--bookmarks, the result of bookmark comparison between
4976 With -B/--bookmarks, the result of bookmark comparison between
4979 local and remote repositories is displayed. With -v/--verbose,
4977 local and remote repositories is displayed. With -v/--verbose,
4980 status is also displayed for each bookmark like below::
4978 status is also displayed for each bookmark like below::
4981
4979
4982 BM1 01234567890a added
4980 BM1 01234567890a added
4983 BM2 deleted
4981 BM2 deleted
4984 BM3 234567890abc advanced
4982 BM3 234567890abc advanced
4985 BM4 34567890abcd diverged
4983 BM4 34567890abcd diverged
4986 BM5 4567890abcde changed
4984 BM5 4567890abcde changed
4987
4985
4988 The action taken when pushing depends on the
4986 The action taken when pushing depends on the
4989 status of each bookmark:
4987 status of each bookmark:
4990
4988
4991 :``added``: push with ``-B`` will create it
4989 :``added``: push with ``-B`` will create it
4992 :``deleted``: push with ``-B`` will delete it
4990 :``deleted``: push with ``-B`` will delete it
4993 :``advanced``: push will update it
4991 :``advanced``: push will update it
4994 :``diverged``: push with ``-B`` will update it
4992 :``diverged``: push with ``-B`` will update it
4995 :``changed``: push with ``-B`` will update it
4993 :``changed``: push with ``-B`` will update it
4996
4994
4997 From the point of view of pushing behavior, bookmarks
4995 From the point of view of pushing behavior, bookmarks
4998 existing only in the remote repository are treated as
4996 existing only in the remote repository are treated as
4999 ``deleted``, even if it is in fact added remotely.
4997 ``deleted``, even if it is in fact added remotely.
5000
4998
5001 Returns 0 if there are outgoing changes, 1 otherwise.
4999 Returns 0 if there are outgoing changes, 1 otherwise.
5002 """
5000 """
5003 opts = pycompat.byteskwargs(opts)
5001 opts = pycompat.byteskwargs(opts)
5004 if opts.get(b'bookmarks'):
5002 if opts.get(b'bookmarks'):
5005 for path in urlutil.get_push_paths(repo, ui, dests):
5003 for path in urlutil.get_push_paths(repo, ui, dests):
5006 dest = path.pushloc or path.loc
5004 dest = path.pushloc or path.loc
5007 other = hg.peer(repo, opts, dest)
5005 other = hg.peer(repo, opts, dest)
5008 try:
5006 try:
5009 if b'bookmarks' not in other.listkeys(b'namespaces'):
5007 if b'bookmarks' not in other.listkeys(b'namespaces'):
5010 ui.warn(_(b"remote doesn't support bookmarks\n"))
5008 ui.warn(_(b"remote doesn't support bookmarks\n"))
5011 return 0
5009 return 0
5012 ui.status(
5010 ui.status(
5013 _(b'comparing with %s\n') % urlutil.hidepassword(dest)
5011 _(b'comparing with %s\n') % urlutil.hidepassword(dest)
5014 )
5012 )
5015 ui.pager(b'outgoing')
5013 ui.pager(b'outgoing')
5016 return bookmarks.outgoing(ui, repo, other)
5014 return bookmarks.outgoing(ui, repo, other)
5017 finally:
5015 finally:
5018 other.close()
5016 other.close()
5019
5017
5020 return hg.outgoing(ui, repo, dests, opts)
5018 return hg.outgoing(ui, repo, dests, opts)
5021
5019
5022
5020
5023 @command(
5021 @command(
5024 b'parents',
5022 b'parents',
5025 [
5023 [
5026 (
5024 (
5027 b'r',
5025 b'r',
5028 b'rev',
5026 b'rev',
5029 b'',
5027 b'',
5030 _(b'show parents of the specified revision'),
5028 _(b'show parents of the specified revision'),
5031 _(b'REV'),
5029 _(b'REV'),
5032 ),
5030 ),
5033 ]
5031 ]
5034 + templateopts,
5032 + templateopts,
5035 _(b'[-r REV] [FILE]'),
5033 _(b'[-r REV] [FILE]'),
5036 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5034 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5037 inferrepo=True,
5035 inferrepo=True,
5038 )
5036 )
5039 def parents(ui, repo, file_=None, **opts):
5037 def parents(ui, repo, file_=None, **opts):
5040 """show the parents of the working directory or revision (DEPRECATED)
5038 """show the parents of the working directory or revision (DEPRECATED)
5041
5039
5042 Print the working directory's parent revisions. If a revision is
5040 Print the working directory's parent revisions. If a revision is
5043 given via -r/--rev, the parent of that revision will be printed.
5041 given via -r/--rev, the parent of that revision will be printed.
5044 If a file argument is given, the revision in which the file was
5042 If a file argument is given, the revision in which the file was
5045 last changed (before the working directory revision or the
5043 last changed (before the working directory revision or the
5046 argument to --rev if given) is printed.
5044 argument to --rev if given) is printed.
5047
5045
5048 This command is equivalent to::
5046 This command is equivalent to::
5049
5047
5050 hg log -r "p1()+p2()" or
5048 hg log -r "p1()+p2()" or
5051 hg log -r "p1(REV)+p2(REV)" or
5049 hg log -r "p1(REV)+p2(REV)" or
5052 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5050 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5053 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5051 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5054
5052
5055 See :hg:`summary` and :hg:`help revsets` for related information.
5053 See :hg:`summary` and :hg:`help revsets` for related information.
5056
5054
5057 Returns 0 on success.
5055 Returns 0 on success.
5058 """
5056 """
5059
5057
5060 opts = pycompat.byteskwargs(opts)
5058 opts = pycompat.byteskwargs(opts)
5061 rev = opts.get(b'rev')
5059 rev = opts.get(b'rev')
5062 if rev:
5060 if rev:
5063 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5061 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5064 ctx = logcmdutil.revsingle(repo, rev, None)
5062 ctx = logcmdutil.revsingle(repo, rev, None)
5065
5063
5066 if file_:
5064 if file_:
5067 m = scmutil.match(ctx, (file_,), opts)
5065 m = scmutil.match(ctx, (file_,), opts)
5068 if m.anypats() or len(m.files()) != 1:
5066 if m.anypats() or len(m.files()) != 1:
5069 raise error.InputError(_(b'can only specify an explicit filename'))
5067 raise error.InputError(_(b'can only specify an explicit filename'))
5070 file_ = m.files()[0]
5068 file_ = m.files()[0]
5071 filenodes = []
5069 filenodes = []
5072 for cp in ctx.parents():
5070 for cp in ctx.parents():
5073 if not cp:
5071 if not cp:
5074 continue
5072 continue
5075 try:
5073 try:
5076 filenodes.append(cp.filenode(file_))
5074 filenodes.append(cp.filenode(file_))
5077 except error.LookupError:
5075 except error.LookupError:
5078 pass
5076 pass
5079 if not filenodes:
5077 if not filenodes:
5080 raise error.InputError(_(b"'%s' not found in manifest") % file_)
5078 raise error.InputError(_(b"'%s' not found in manifest") % file_)
5081 p = []
5079 p = []
5082 for fn in filenodes:
5080 for fn in filenodes:
5083 fctx = repo.filectx(file_, fileid=fn)
5081 fctx = repo.filectx(file_, fileid=fn)
5084 p.append(fctx.node())
5082 p.append(fctx.node())
5085 else:
5083 else:
5086 p = [cp.node() for cp in ctx.parents()]
5084 p = [cp.node() for cp in ctx.parents()]
5087
5085
5088 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5086 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5089 for n in p:
5087 for n in p:
5090 if n != repo.nullid:
5088 if n != repo.nullid:
5091 displayer.show(repo[n])
5089 displayer.show(repo[n])
5092 displayer.close()
5090 displayer.close()
5093
5091
5094
5092
5095 @command(
5093 @command(
5096 b'paths',
5094 b'paths',
5097 formatteropts,
5095 formatteropts,
5098 _(b'[NAME]'),
5096 _(b'[NAME]'),
5099 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5097 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5100 optionalrepo=True,
5098 optionalrepo=True,
5101 intents={INTENT_READONLY},
5099 intents={INTENT_READONLY},
5102 )
5100 )
5103 def paths(ui, repo, search=None, **opts):
5101 def paths(ui, repo, search=None, **opts):
5104 """show aliases for remote repositories
5102 """show aliases for remote repositories
5105
5103
5106 Show definition of symbolic path name NAME. If no name is given,
5104 Show definition of symbolic path name NAME. If no name is given,
5107 show definition of all available names.
5105 show definition of all available names.
5108
5106
5109 Option -q/--quiet suppresses all output when searching for NAME
5107 Option -q/--quiet suppresses all output when searching for NAME
5110 and shows only the path names when listing all definitions.
5108 and shows only the path names when listing all definitions.
5111
5109
5112 Path names are defined in the [paths] section of your
5110 Path names are defined in the [paths] section of your
5113 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5111 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5114 repository, ``.hg/hgrc`` is used, too.
5112 repository, ``.hg/hgrc`` is used, too.
5115
5113
5116 The path names ``default`` and ``default-push`` have a special
5114 The path names ``default`` and ``default-push`` have a special
5117 meaning. When performing a push or pull operation, they are used
5115 meaning. When performing a push or pull operation, they are used
5118 as fallbacks if no location is specified on the command-line.
5116 as fallbacks if no location is specified on the command-line.
5119 When ``default-push`` is set, it will be used for push and
5117 When ``default-push`` is set, it will be used for push and
5120 ``default`` will be used for pull; otherwise ``default`` is used
5118 ``default`` will be used for pull; otherwise ``default`` is used
5121 as the fallback for both. When cloning a repository, the clone
5119 as the fallback for both. When cloning a repository, the clone
5122 source is written as ``default`` in ``.hg/hgrc``.
5120 source is written as ``default`` in ``.hg/hgrc``.
5123
5121
5124 .. note::
5122 .. note::
5125
5123
5126 ``default`` and ``default-push`` apply to all inbound (e.g.
5124 ``default`` and ``default-push`` apply to all inbound (e.g.
5127 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5125 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5128 and :hg:`bundle`) operations.
5126 and :hg:`bundle`) operations.
5129
5127
5130 See :hg:`help urls` for more information.
5128 See :hg:`help urls` for more information.
5131
5129
5132 .. container:: verbose
5130 .. container:: verbose
5133
5131
5134 Template:
5132 Template:
5135
5133
5136 The following keywords are supported. See also :hg:`help templates`.
5134 The following keywords are supported. See also :hg:`help templates`.
5137
5135
5138 :name: String. Symbolic name of the path alias.
5136 :name: String. Symbolic name of the path alias.
5139 :pushurl: String. URL for push operations.
5137 :pushurl: String. URL for push operations.
5140 :url: String. URL or directory path for the other operations.
5138 :url: String. URL or directory path for the other operations.
5141
5139
5142 Returns 0 on success.
5140 Returns 0 on success.
5143 """
5141 """
5144
5142
5145 opts = pycompat.byteskwargs(opts)
5143 opts = pycompat.byteskwargs(opts)
5146
5144
5147 pathitems = urlutil.list_paths(ui, search)
5145 pathitems = urlutil.list_paths(ui, search)
5148 ui.pager(b'paths')
5146 ui.pager(b'paths')
5149
5147
5150 fm = ui.formatter(b'paths', opts)
5148 fm = ui.formatter(b'paths', opts)
5151 if fm.isplain():
5149 if fm.isplain():
5152 hidepassword = urlutil.hidepassword
5150 hidepassword = urlutil.hidepassword
5153 else:
5151 else:
5154 hidepassword = bytes
5152 hidepassword = bytes
5155 if ui.quiet:
5153 if ui.quiet:
5156 namefmt = b'%s\n'
5154 namefmt = b'%s\n'
5157 else:
5155 else:
5158 namefmt = b'%s = '
5156 namefmt = b'%s = '
5159 showsubopts = not search and not ui.quiet
5157 showsubopts = not search and not ui.quiet
5160
5158
5161 for name, path in pathitems:
5159 for name, path in pathitems:
5162 fm.startitem()
5160 fm.startitem()
5163 fm.condwrite(not search, b'name', namefmt, name)
5161 fm.condwrite(not search, b'name', namefmt, name)
5164 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5162 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5165 for subopt, value in sorted(path.suboptions.items()):
5163 for subopt, value in sorted(path.suboptions.items()):
5166 assert subopt not in (b'name', b'url')
5164 assert subopt not in (b'name', b'url')
5167 if showsubopts:
5165 if showsubopts:
5168 fm.plain(b'%s:%s = ' % (name, subopt))
5166 fm.plain(b'%s:%s = ' % (name, subopt))
5169 if isinstance(value, bool):
5167 if isinstance(value, bool):
5170 if value:
5168 if value:
5171 value = b'yes'
5169 value = b'yes'
5172 else:
5170 else:
5173 value = b'no'
5171 value = b'no'
5174 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5172 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5175
5173
5176 fm.end()
5174 fm.end()
5177
5175
5178 if search and not pathitems:
5176 if search and not pathitems:
5179 if not ui.quiet:
5177 if not ui.quiet:
5180 ui.warn(_(b"not found!\n"))
5178 ui.warn(_(b"not found!\n"))
5181 return 1
5179 return 1
5182 else:
5180 else:
5183 return 0
5181 return 0
5184
5182
5185
5183
5186 @command(
5184 @command(
5187 b'phase',
5185 b'phase',
5188 [
5186 [
5189 (b'p', b'public', False, _(b'set changeset phase to public')),
5187 (b'p', b'public', False, _(b'set changeset phase to public')),
5190 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5188 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5191 (b's', b'secret', False, _(b'set changeset phase to secret')),
5189 (b's', b'secret', False, _(b'set changeset phase to secret')),
5192 (b'f', b'force', False, _(b'allow to move boundary backward')),
5190 (b'f', b'force', False, _(b'allow to move boundary backward')),
5193 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5191 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5194 ],
5192 ],
5195 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5193 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5196 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5194 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5197 )
5195 )
5198 def phase(ui, repo, *revs, **opts):
5196 def phase(ui, repo, *revs, **opts):
5199 """set or show the current phase name
5197 """set or show the current phase name
5200
5198
5201 With no argument, show the phase name of the current revision(s).
5199 With no argument, show the phase name of the current revision(s).
5202
5200
5203 With one of -p/--public, -d/--draft or -s/--secret, change the
5201 With one of -p/--public, -d/--draft or -s/--secret, change the
5204 phase value of the specified revisions.
5202 phase value of the specified revisions.
5205
5203
5206 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5204 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5207 lower phase to a higher phase. Phases are ordered as follows::
5205 lower phase to a higher phase. Phases are ordered as follows::
5208
5206
5209 public < draft < secret
5207 public < draft < secret
5210
5208
5211 Returns 0 on success, 1 if some phases could not be changed.
5209 Returns 0 on success, 1 if some phases could not be changed.
5212
5210
5213 (For more information about the phases concept, see :hg:`help phases`.)
5211 (For more information about the phases concept, see :hg:`help phases`.)
5214 """
5212 """
5215 opts = pycompat.byteskwargs(opts)
5213 opts = pycompat.byteskwargs(opts)
5216 # search for a unique phase argument
5214 # search for a unique phase argument
5217 targetphase = None
5215 targetphase = None
5218 for idx, name in enumerate(phases.cmdphasenames):
5216 for idx, name in enumerate(phases.cmdphasenames):
5219 if opts[name]:
5217 if opts[name]:
5220 if targetphase is not None:
5218 if targetphase is not None:
5221 raise error.InputError(_(b'only one phase can be specified'))
5219 raise error.InputError(_(b'only one phase can be specified'))
5222 targetphase = idx
5220 targetphase = idx
5223
5221
5224 # look for specified revision
5222 # look for specified revision
5225 revs = list(revs)
5223 revs = list(revs)
5226 revs.extend(opts[b'rev'])
5224 revs.extend(opts[b'rev'])
5227 if revs:
5225 if revs:
5228 revs = logcmdutil.revrange(repo, revs)
5226 revs = logcmdutil.revrange(repo, revs)
5229 else:
5227 else:
5230 # display both parents as the second parent phase can influence
5228 # display both parents as the second parent phase can influence
5231 # the phase of a merge commit
5229 # the phase of a merge commit
5232 revs = [c.rev() for c in repo[None].parents()]
5230 revs = [c.rev() for c in repo[None].parents()]
5233
5231
5234 ret = 0
5232 ret = 0
5235 if targetphase is None:
5233 if targetphase is None:
5236 # display
5234 # display
5237 for r in revs:
5235 for r in revs:
5238 ctx = repo[r]
5236 ctx = repo[r]
5239 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5237 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5240 else:
5238 else:
5241 with repo.lock(), repo.transaction(b"phase") as tr:
5239 with repo.lock(), repo.transaction(b"phase") as tr:
5242 # set phase
5240 # set phase
5243 if not revs:
5241 if not revs:
5244 raise error.InputError(_(b'empty revision set'))
5242 raise error.InputError(_(b'empty revision set'))
5245 nodes = [repo[r].node() for r in revs]
5243 nodes = [repo[r].node() for r in revs]
5246 # moving revision from public to draft may hide them
5244 # moving revision from public to draft may hide them
5247 # We have to check result on an unfiltered repository
5245 # We have to check result on an unfiltered repository
5248 unfi = repo.unfiltered()
5246 unfi = repo.unfiltered()
5249 getphase = unfi._phasecache.phase
5247 getphase = unfi._phasecache.phase
5250 olddata = [getphase(unfi, r) for r in unfi]
5248 olddata = [getphase(unfi, r) for r in unfi]
5251 phases.advanceboundary(repo, tr, targetphase, nodes)
5249 phases.advanceboundary(repo, tr, targetphase, nodes)
5252 if opts[b'force']:
5250 if opts[b'force']:
5253 phases.retractboundary(repo, tr, targetphase, nodes)
5251 phases.retractboundary(repo, tr, targetphase, nodes)
5254 getphase = unfi._phasecache.phase
5252 getphase = unfi._phasecache.phase
5255 newdata = [getphase(unfi, r) for r in unfi]
5253 newdata = [getphase(unfi, r) for r in unfi]
5256 changes = sum(newdata[r] != olddata[r] for r in unfi)
5254 changes = sum(newdata[r] != olddata[r] for r in unfi)
5257 cl = unfi.changelog
5255 cl = unfi.changelog
5258 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5256 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5259 if rejected:
5257 if rejected:
5260 ui.warn(
5258 ui.warn(
5261 _(
5259 _(
5262 b'cannot move %i changesets to a higher '
5260 b'cannot move %i changesets to a higher '
5263 b'phase, use --force\n'
5261 b'phase, use --force\n'
5264 )
5262 )
5265 % len(rejected)
5263 % len(rejected)
5266 )
5264 )
5267 ret = 1
5265 ret = 1
5268 if changes:
5266 if changes:
5269 msg = _(b'phase changed for %i changesets\n') % changes
5267 msg = _(b'phase changed for %i changesets\n') % changes
5270 if ret:
5268 if ret:
5271 ui.status(msg)
5269 ui.status(msg)
5272 else:
5270 else:
5273 ui.note(msg)
5271 ui.note(msg)
5274 else:
5272 else:
5275 ui.warn(_(b'no phases changed\n'))
5273 ui.warn(_(b'no phases changed\n'))
5276 return ret
5274 return ret
5277
5275
5278
5276
5279 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5277 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5280 """Run after a changegroup has been added via pull/unbundle
5278 """Run after a changegroup has been added via pull/unbundle
5281
5279
5282 This takes arguments below:
5280 This takes arguments below:
5283
5281
5284 :modheads: change of heads by pull/unbundle
5282 :modheads: change of heads by pull/unbundle
5285 :optupdate: updating working directory is needed or not
5283 :optupdate: updating working directory is needed or not
5286 :checkout: update destination revision (or None to default destination)
5284 :checkout: update destination revision (or None to default destination)
5287 :brev: a name, which might be a bookmark to be activated after updating
5285 :brev: a name, which might be a bookmark to be activated after updating
5288
5286
5289 return True if update raise any conflict, False otherwise.
5287 return True if update raise any conflict, False otherwise.
5290 """
5288 """
5291 if modheads == 0:
5289 if modheads == 0:
5292 return False
5290 return False
5293 if optupdate:
5291 if optupdate:
5294 try:
5292 try:
5295 return hg.updatetotally(ui, repo, checkout, brev)
5293 return hg.updatetotally(ui, repo, checkout, brev)
5296 except error.UpdateAbort as inst:
5294 except error.UpdateAbort as inst:
5297 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5295 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5298 hint = inst.hint
5296 hint = inst.hint
5299 raise error.UpdateAbort(msg, hint=hint)
5297 raise error.UpdateAbort(msg, hint=hint)
5300 if modheads is not None and modheads > 1:
5298 if modheads is not None and modheads > 1:
5301 currentbranchheads = len(repo.branchheads())
5299 currentbranchheads = len(repo.branchheads())
5302 if currentbranchheads == modheads:
5300 if currentbranchheads == modheads:
5303 ui.status(
5301 ui.status(
5304 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5302 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5305 )
5303 )
5306 elif currentbranchheads > 1:
5304 elif currentbranchheads > 1:
5307 ui.status(
5305 ui.status(
5308 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5306 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5309 )
5307 )
5310 else:
5308 else:
5311 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5309 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5312 elif not ui.configbool(b'commands', b'update.requiredest'):
5310 elif not ui.configbool(b'commands', b'update.requiredest'):
5313 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5311 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5314 return False
5312 return False
5315
5313
5316
5314
5317 @command(
5315 @command(
5318 b'pull',
5316 b'pull',
5319 [
5317 [
5320 (
5318 (
5321 b'u',
5319 b'u',
5322 b'update',
5320 b'update',
5323 None,
5321 None,
5324 _(b'update to new branch head if new descendants were pulled'),
5322 _(b'update to new branch head if new descendants were pulled'),
5325 ),
5323 ),
5326 (
5324 (
5327 b'f',
5325 b'f',
5328 b'force',
5326 b'force',
5329 None,
5327 None,
5330 _(b'run even when remote repository is unrelated'),
5328 _(b'run even when remote repository is unrelated'),
5331 ),
5329 ),
5332 (
5330 (
5333 b'',
5331 b'',
5334 b'confirm',
5332 b'confirm',
5335 None,
5333 None,
5336 _(b'confirm pull before applying changes'),
5334 _(b'confirm pull before applying changes'),
5337 ),
5335 ),
5338 (
5336 (
5339 b'r',
5337 b'r',
5340 b'rev',
5338 b'rev',
5341 [],
5339 [],
5342 _(b'a remote changeset intended to be added'),
5340 _(b'a remote changeset intended to be added'),
5343 _(b'REV'),
5341 _(b'REV'),
5344 ),
5342 ),
5345 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5343 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5346 (
5344 (
5347 b'b',
5345 b'b',
5348 b'branch',
5346 b'branch',
5349 [],
5347 [],
5350 _(b'a specific branch you would like to pull'),
5348 _(b'a specific branch you would like to pull'),
5351 _(b'BRANCH'),
5349 _(b'BRANCH'),
5352 ),
5350 ),
5353 ]
5351 ]
5354 + remoteopts,
5352 + remoteopts,
5355 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]...'),
5353 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]...'),
5356 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5354 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5357 helpbasic=True,
5355 helpbasic=True,
5358 )
5356 )
5359 def pull(ui, repo, *sources, **opts):
5357 def pull(ui, repo, *sources, **opts):
5360 """pull changes from the specified source
5358 """pull changes from the specified source
5361
5359
5362 Pull changes from a remote repository to a local one.
5360 Pull changes from a remote repository to a local one.
5363
5361
5364 This finds all changes from the repository at the specified path
5362 This finds all changes from the repository at the specified path
5365 or URL and adds them to a local repository (the current one unless
5363 or URL and adds them to a local repository (the current one unless
5366 -R is specified). By default, this does not update the copy of the
5364 -R is specified). By default, this does not update the copy of the
5367 project in the working directory.
5365 project in the working directory.
5368
5366
5369 When cloning from servers that support it, Mercurial may fetch
5367 When cloning from servers that support it, Mercurial may fetch
5370 pre-generated data. When this is done, hooks operating on incoming
5368 pre-generated data. When this is done, hooks operating on incoming
5371 changesets and changegroups may fire more than once, once for each
5369 changesets and changegroups may fire more than once, once for each
5372 pre-generated bundle and as well as for any additional remaining
5370 pre-generated bundle and as well as for any additional remaining
5373 data. See :hg:`help -e clonebundles` for more.
5371 data. See :hg:`help -e clonebundles` for more.
5374
5372
5375 Use :hg:`incoming` if you want to see what would have been added
5373 Use :hg:`incoming` if you want to see what would have been added
5376 by a pull at the time you issued this command. If you then decide
5374 by a pull at the time you issued this command. If you then decide
5377 to add those changes to the repository, you should use :hg:`pull
5375 to add those changes to the repository, you should use :hg:`pull
5378 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5376 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5379
5377
5380 If SOURCE is omitted, the 'default' path will be used.
5378 If SOURCE is omitted, the 'default' path will be used.
5381 See :hg:`help urls` for more information.
5379 See :hg:`help urls` for more information.
5382
5380
5383 If multiple sources are specified, they will be pulled sequentially as if
5381 If multiple sources are specified, they will be pulled sequentially as if
5384 the command was run multiple time. If --update is specify and the command
5382 the command was run multiple time. If --update is specify and the command
5385 will stop at the first failed --update.
5383 will stop at the first failed --update.
5386
5384
5387 Specifying bookmark as ``.`` is equivalent to specifying the active
5385 Specifying bookmark as ``.`` is equivalent to specifying the active
5388 bookmark's name.
5386 bookmark's name.
5389
5387
5390 Returns 0 on success, 1 if an update had unresolved files.
5388 Returns 0 on success, 1 if an update had unresolved files.
5391 """
5389 """
5392
5390
5393 opts = pycompat.byteskwargs(opts)
5391 opts = pycompat.byteskwargs(opts)
5394 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5392 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5395 b'update'
5393 b'update'
5396 ):
5394 ):
5397 msg = _(b'update destination required by configuration')
5395 msg = _(b'update destination required by configuration')
5398 hint = _(b'use hg pull followed by hg update DEST')
5396 hint = _(b'use hg pull followed by hg update DEST')
5399 raise error.InputError(msg, hint=hint)
5397 raise error.InputError(msg, hint=hint)
5400
5398
5401 for path in urlutil.get_pull_paths(repo, ui, sources):
5399 for path in urlutil.get_pull_paths(repo, ui, sources):
5402 source, branches = urlutil.parseurl(path.rawloc, opts.get(b'branch'))
5400 source, branches = urlutil.parseurl(path.rawloc, opts.get(b'branch'))
5403 ui.status(_(b'pulling from %s\n') % urlutil.hidepassword(source))
5401 ui.status(_(b'pulling from %s\n') % urlutil.hidepassword(source))
5404 ui.flush()
5402 ui.flush()
5405 other = hg.peer(repo, opts, source)
5403 other = hg.peer(repo, opts, source)
5406 update_conflict = None
5404 update_conflict = None
5407 try:
5405 try:
5408 revs, checkout = hg.addbranchrevs(
5406 revs, checkout = hg.addbranchrevs(
5409 repo, other, branches, opts.get(b'rev')
5407 repo, other, branches, opts.get(b'rev')
5410 )
5408 )
5411
5409
5412 pullopargs = {}
5410 pullopargs = {}
5413
5411
5414 nodes = None
5412 nodes = None
5415 if opts.get(b'bookmark') or revs:
5413 if opts.get(b'bookmark') or revs:
5416 # The list of bookmark used here is the same used to actually update
5414 # The list of bookmark used here is the same used to actually update
5417 # the bookmark names, to avoid the race from issue 4689 and we do
5415 # the bookmark names, to avoid the race from issue 4689 and we do
5418 # all lookup and bookmark queries in one go so they see the same
5416 # all lookup and bookmark queries in one go so they see the same
5419 # version of the server state (issue 4700).
5417 # version of the server state (issue 4700).
5420 nodes = []
5418 nodes = []
5421 fnodes = []
5419 fnodes = []
5422 revs = revs or []
5420 revs = revs or []
5423 if revs and not other.capable(b'lookup'):
5421 if revs and not other.capable(b'lookup'):
5424 err = _(
5422 err = _(
5425 b"other repository doesn't support revision lookup, "
5423 b"other repository doesn't support revision lookup, "
5426 b"so a rev cannot be specified."
5424 b"so a rev cannot be specified."
5427 )
5425 )
5428 raise error.Abort(err)
5426 raise error.Abort(err)
5429 with other.commandexecutor() as e:
5427 with other.commandexecutor() as e:
5430 fremotebookmarks = e.callcommand(
5428 fremotebookmarks = e.callcommand(
5431 b'listkeys', {b'namespace': b'bookmarks'}
5429 b'listkeys', {b'namespace': b'bookmarks'}
5432 )
5430 )
5433 for r in revs:
5431 for r in revs:
5434 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5432 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5435 remotebookmarks = fremotebookmarks.result()
5433 remotebookmarks = fremotebookmarks.result()
5436 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5434 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5437 pullopargs[b'remotebookmarks'] = remotebookmarks
5435 pullopargs[b'remotebookmarks'] = remotebookmarks
5438 for b in opts.get(b'bookmark', []):
5436 for b in opts.get(b'bookmark', []):
5439 b = repo._bookmarks.expandname(b)
5437 b = repo._bookmarks.expandname(b)
5440 if b not in remotebookmarks:
5438 if b not in remotebookmarks:
5441 raise error.InputError(
5439 raise error.InputError(
5442 _(b'remote bookmark %s not found!') % b
5440 _(b'remote bookmark %s not found!') % b
5443 )
5441 )
5444 nodes.append(remotebookmarks[b])
5442 nodes.append(remotebookmarks[b])
5445 for i, rev in enumerate(revs):
5443 for i, rev in enumerate(revs):
5446 node = fnodes[i].result()
5444 node = fnodes[i].result()
5447 nodes.append(node)
5445 nodes.append(node)
5448 if rev == checkout:
5446 if rev == checkout:
5449 checkout = node
5447 checkout = node
5450
5448
5451 wlock = util.nullcontextmanager()
5449 wlock = util.nullcontextmanager()
5452 if opts.get(b'update'):
5450 if opts.get(b'update'):
5453 wlock = repo.wlock()
5451 wlock = repo.wlock()
5454 with wlock:
5452 with wlock:
5455 pullopargs.update(opts.get(b'opargs', {}))
5453 pullopargs.update(opts.get(b'opargs', {}))
5456 modheads = exchange.pull(
5454 modheads = exchange.pull(
5457 repo,
5455 repo,
5458 other,
5456 other,
5459 path=path,
5457 path=path,
5460 heads=nodes,
5458 heads=nodes,
5461 force=opts.get(b'force'),
5459 force=opts.get(b'force'),
5462 bookmarks=opts.get(b'bookmark', ()),
5460 bookmarks=opts.get(b'bookmark', ()),
5463 opargs=pullopargs,
5461 opargs=pullopargs,
5464 confirm=opts.get(b'confirm'),
5462 confirm=opts.get(b'confirm'),
5465 ).cgresult
5463 ).cgresult
5466
5464
5467 # brev is a name, which might be a bookmark to be activated at
5465 # brev is a name, which might be a bookmark to be activated at
5468 # the end of the update. In other words, it is an explicit
5466 # the end of the update. In other words, it is an explicit
5469 # destination of the update
5467 # destination of the update
5470 brev = None
5468 brev = None
5471
5469
5472 if checkout:
5470 if checkout:
5473 checkout = repo.unfiltered().changelog.rev(checkout)
5471 checkout = repo.unfiltered().changelog.rev(checkout)
5474
5472
5475 # order below depends on implementation of
5473 # order below depends on implementation of
5476 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5474 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5477 # because 'checkout' is determined without it.
5475 # because 'checkout' is determined without it.
5478 if opts.get(b'rev'):
5476 if opts.get(b'rev'):
5479 brev = opts[b'rev'][0]
5477 brev = opts[b'rev'][0]
5480 elif opts.get(b'branch'):
5478 elif opts.get(b'branch'):
5481 brev = opts[b'branch'][0]
5479 brev = opts[b'branch'][0]
5482 else:
5480 else:
5483 brev = branches[0]
5481 brev = branches[0]
5484 repo._subtoppath = source
5482 repo._subtoppath = source
5485 try:
5483 try:
5486 update_conflict = postincoming(
5484 update_conflict = postincoming(
5487 ui, repo, modheads, opts.get(b'update'), checkout, brev
5485 ui, repo, modheads, opts.get(b'update'), checkout, brev
5488 )
5486 )
5489 except error.FilteredRepoLookupError as exc:
5487 except error.FilteredRepoLookupError as exc:
5490 msg = _(b'cannot update to target: %s') % exc.args[0]
5488 msg = _(b'cannot update to target: %s') % exc.args[0]
5491 exc.args = (msg,) + exc.args[1:]
5489 exc.args = (msg,) + exc.args[1:]
5492 raise
5490 raise
5493 finally:
5491 finally:
5494 del repo._subtoppath
5492 del repo._subtoppath
5495
5493
5496 finally:
5494 finally:
5497 other.close()
5495 other.close()
5498 # skip the remaining pull source if they are some conflict.
5496 # skip the remaining pull source if they are some conflict.
5499 if update_conflict:
5497 if update_conflict:
5500 break
5498 break
5501 if update_conflict:
5499 if update_conflict:
5502 return 1
5500 return 1
5503 else:
5501 else:
5504 return 0
5502 return 0
5505
5503
5506
5504
5507 @command(
5505 @command(
5508 b'purge|clean',
5506 b'purge|clean',
5509 [
5507 [
5510 (b'a', b'abort-on-err', None, _(b'abort if an error occurs')),
5508 (b'a', b'abort-on-err', None, _(b'abort if an error occurs')),
5511 (b'', b'all', None, _(b'purge ignored files too')),
5509 (b'', b'all', None, _(b'purge ignored files too')),
5512 (b'i', b'ignored', None, _(b'purge only ignored files')),
5510 (b'i', b'ignored', None, _(b'purge only ignored files')),
5513 (b'', b'dirs', None, _(b'purge empty directories')),
5511 (b'', b'dirs', None, _(b'purge empty directories')),
5514 (b'', b'files', None, _(b'purge files')),
5512 (b'', b'files', None, _(b'purge files')),
5515 (b'p', b'print', None, _(b'print filenames instead of deleting them')),
5513 (b'p', b'print', None, _(b'print filenames instead of deleting them')),
5516 (
5514 (
5517 b'0',
5515 b'0',
5518 b'print0',
5516 b'print0',
5519 None,
5517 None,
5520 _(
5518 _(
5521 b'end filenames with NUL, for use with xargs'
5519 b'end filenames with NUL, for use with xargs'
5522 b' (implies -p/--print)'
5520 b' (implies -p/--print)'
5523 ),
5521 ),
5524 ),
5522 ),
5525 (b'', b'confirm', None, _(b'ask before permanently deleting files')),
5523 (b'', b'confirm', None, _(b'ask before permanently deleting files')),
5526 ]
5524 ]
5527 + cmdutil.walkopts,
5525 + cmdutil.walkopts,
5528 _(b'hg purge [OPTION]... [DIR]...'),
5526 _(b'hg purge [OPTION]... [DIR]...'),
5529 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5527 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5530 )
5528 )
5531 def purge(ui, repo, *dirs, **opts):
5529 def purge(ui, repo, *dirs, **opts):
5532 """removes files not tracked by Mercurial
5530 """removes files not tracked by Mercurial
5533
5531
5534 Delete files not known to Mercurial. This is useful to test local
5532 Delete files not known to Mercurial. This is useful to test local
5535 and uncommitted changes in an otherwise-clean source tree.
5533 and uncommitted changes in an otherwise-clean source tree.
5536
5534
5537 This means that purge will delete the following by default:
5535 This means that purge will delete the following by default:
5538
5536
5539 - Unknown files: files marked with "?" by :hg:`status`
5537 - Unknown files: files marked with "?" by :hg:`status`
5540 - Empty directories: in fact Mercurial ignores directories unless
5538 - Empty directories: in fact Mercurial ignores directories unless
5541 they contain files under source control management
5539 they contain files under source control management
5542
5540
5543 But it will leave untouched:
5541 But it will leave untouched:
5544
5542
5545 - Modified and unmodified tracked files
5543 - Modified and unmodified tracked files
5546 - Ignored files (unless -i or --all is specified)
5544 - Ignored files (unless -i or --all is specified)
5547 - New files added to the repository (with :hg:`add`)
5545 - New files added to the repository (with :hg:`add`)
5548
5546
5549 The --files and --dirs options can be used to direct purge to delete
5547 The --files and --dirs options can be used to direct purge to delete
5550 only files, only directories, or both. If neither option is given,
5548 only files, only directories, or both. If neither option is given,
5551 both will be deleted.
5549 both will be deleted.
5552
5550
5553 If directories are given on the command line, only files in these
5551 If directories are given on the command line, only files in these
5554 directories are considered.
5552 directories are considered.
5555
5553
5556 Be careful with purge, as you could irreversibly delete some files
5554 Be careful with purge, as you could irreversibly delete some files
5557 you forgot to add to the repository. If you only want to print the
5555 you forgot to add to the repository. If you only want to print the
5558 list of files that this program would delete, use the --print
5556 list of files that this program would delete, use the --print
5559 option.
5557 option.
5560 """
5558 """
5561 opts = pycompat.byteskwargs(opts)
5559 opts = pycompat.byteskwargs(opts)
5562 cmdutil.check_at_most_one_arg(opts, b'all', b'ignored')
5560 cmdutil.check_at_most_one_arg(opts, b'all', b'ignored')
5563
5561
5564 act = not opts.get(b'print')
5562 act = not opts.get(b'print')
5565 eol = b'\n'
5563 eol = b'\n'
5566 if opts.get(b'print0'):
5564 if opts.get(b'print0'):
5567 eol = b'\0'
5565 eol = b'\0'
5568 act = False # --print0 implies --print
5566 act = False # --print0 implies --print
5569 if opts.get(b'all', False):
5567 if opts.get(b'all', False):
5570 ignored = True
5568 ignored = True
5571 unknown = True
5569 unknown = True
5572 else:
5570 else:
5573 ignored = opts.get(b'ignored', False)
5571 ignored = opts.get(b'ignored', False)
5574 unknown = not ignored
5572 unknown = not ignored
5575
5573
5576 removefiles = opts.get(b'files')
5574 removefiles = opts.get(b'files')
5577 removedirs = opts.get(b'dirs')
5575 removedirs = opts.get(b'dirs')
5578 confirm = opts.get(b'confirm')
5576 confirm = opts.get(b'confirm')
5579 if confirm is None:
5577 if confirm is None:
5580 try:
5578 try:
5581 extensions.find(b'purge')
5579 extensions.find(b'purge')
5582 confirm = False
5580 confirm = False
5583 except KeyError:
5581 except KeyError:
5584 confirm = True
5582 confirm = True
5585
5583
5586 if not removefiles and not removedirs:
5584 if not removefiles and not removedirs:
5587 removefiles = True
5585 removefiles = True
5588 removedirs = True
5586 removedirs = True
5589
5587
5590 match = scmutil.match(repo[None], dirs, opts)
5588 match = scmutil.match(repo[None], dirs, opts)
5591
5589
5592 paths = mergemod.purge(
5590 paths = mergemod.purge(
5593 repo,
5591 repo,
5594 match,
5592 match,
5595 unknown=unknown,
5593 unknown=unknown,
5596 ignored=ignored,
5594 ignored=ignored,
5597 removeemptydirs=removedirs,
5595 removeemptydirs=removedirs,
5598 removefiles=removefiles,
5596 removefiles=removefiles,
5599 abortonerror=opts.get(b'abort_on_err'),
5597 abortonerror=opts.get(b'abort_on_err'),
5600 noop=not act,
5598 noop=not act,
5601 confirm=confirm,
5599 confirm=confirm,
5602 )
5600 )
5603
5601
5604 for path in paths:
5602 for path in paths:
5605 if not act:
5603 if not act:
5606 ui.write(b'%s%s' % (path, eol))
5604 ui.write(b'%s%s' % (path, eol))
5607
5605
5608
5606
5609 @command(
5607 @command(
5610 b'push',
5608 b'push',
5611 [
5609 [
5612 (b'f', b'force', None, _(b'force push')),
5610 (b'f', b'force', None, _(b'force push')),
5613 (
5611 (
5614 b'r',
5612 b'r',
5615 b'rev',
5613 b'rev',
5616 [],
5614 [],
5617 _(b'a changeset intended to be included in the destination'),
5615 _(b'a changeset intended to be included in the destination'),
5618 _(b'REV'),
5616 _(b'REV'),
5619 ),
5617 ),
5620 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5618 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5621 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5619 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5622 (
5620 (
5623 b'b',
5621 b'b',
5624 b'branch',
5622 b'branch',
5625 [],
5623 [],
5626 _(b'a specific branch you would like to push'),
5624 _(b'a specific branch you would like to push'),
5627 _(b'BRANCH'),
5625 _(b'BRANCH'),
5628 ),
5626 ),
5629 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5627 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5630 (
5628 (
5631 b'',
5629 b'',
5632 b'pushvars',
5630 b'pushvars',
5633 [],
5631 [],
5634 _(b'variables that can be sent to server (ADVANCED)'),
5632 _(b'variables that can be sent to server (ADVANCED)'),
5635 ),
5633 ),
5636 (
5634 (
5637 b'',
5635 b'',
5638 b'publish',
5636 b'publish',
5639 False,
5637 False,
5640 _(b'push the changeset as public (EXPERIMENTAL)'),
5638 _(b'push the changeset as public (EXPERIMENTAL)'),
5641 ),
5639 ),
5642 ]
5640 ]
5643 + remoteopts,
5641 + remoteopts,
5644 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]...'),
5642 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]...'),
5645 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5643 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5646 helpbasic=True,
5644 helpbasic=True,
5647 )
5645 )
5648 def push(ui, repo, *dests, **opts):
5646 def push(ui, repo, *dests, **opts):
5649 """push changes to the specified destination
5647 """push changes to the specified destination
5650
5648
5651 Push changesets from the local repository to the specified
5649 Push changesets from the local repository to the specified
5652 destination.
5650 destination.
5653
5651
5654 This operation is symmetrical to pull: it is identical to a pull
5652 This operation is symmetrical to pull: it is identical to a pull
5655 in the destination repository from the current one.
5653 in the destination repository from the current one.
5656
5654
5657 By default, push will not allow creation of new heads at the
5655 By default, push will not allow creation of new heads at the
5658 destination, since multiple heads would make it unclear which head
5656 destination, since multiple heads would make it unclear which head
5659 to use. In this situation, it is recommended to pull and merge
5657 to use. In this situation, it is recommended to pull and merge
5660 before pushing.
5658 before pushing.
5661
5659
5662 Use --new-branch if you want to allow push to create a new named
5660 Use --new-branch if you want to allow push to create a new named
5663 branch that is not present at the destination. This allows you to
5661 branch that is not present at the destination. This allows you to
5664 only create a new branch without forcing other changes.
5662 only create a new branch without forcing other changes.
5665
5663
5666 .. note::
5664 .. note::
5667
5665
5668 Extra care should be taken with the -f/--force option,
5666 Extra care should be taken with the -f/--force option,
5669 which will push all new heads on all branches, an action which will
5667 which will push all new heads on all branches, an action which will
5670 almost always cause confusion for collaborators.
5668 almost always cause confusion for collaborators.
5671
5669
5672 If -r/--rev is used, the specified revision and all its ancestors
5670 If -r/--rev is used, the specified revision and all its ancestors
5673 will be pushed to the remote repository.
5671 will be pushed to the remote repository.
5674
5672
5675 If -B/--bookmark is used, the specified bookmarked revision, its
5673 If -B/--bookmark is used, the specified bookmarked revision, its
5676 ancestors, and the bookmark will be pushed to the remote
5674 ancestors, and the bookmark will be pushed to the remote
5677 repository. Specifying ``.`` is equivalent to specifying the active
5675 repository. Specifying ``.`` is equivalent to specifying the active
5678 bookmark's name. Use the --all-bookmarks option for pushing all
5676 bookmark's name. Use the --all-bookmarks option for pushing all
5679 current bookmarks.
5677 current bookmarks.
5680
5678
5681 Please see :hg:`help urls` for important details about ``ssh://``
5679 Please see :hg:`help urls` for important details about ``ssh://``
5682 URLs. If DESTINATION is omitted, a default path will be used.
5680 URLs. If DESTINATION is omitted, a default path will be used.
5683
5681
5684 When passed multiple destinations, push will process them one after the
5682 When passed multiple destinations, push will process them one after the
5685 other, but stop should an error occur.
5683 other, but stop should an error occur.
5686
5684
5687 .. container:: verbose
5685 .. container:: verbose
5688
5686
5689 The --pushvars option sends strings to the server that become
5687 The --pushvars option sends strings to the server that become
5690 environment variables prepended with ``HG_USERVAR_``. For example,
5688 environment variables prepended with ``HG_USERVAR_``. For example,
5691 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5689 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5692 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5690 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5693
5691
5694 pushvars can provide for user-overridable hooks as well as set debug
5692 pushvars can provide for user-overridable hooks as well as set debug
5695 levels. One example is having a hook that blocks commits containing
5693 levels. One example is having a hook that blocks commits containing
5696 conflict markers, but enables the user to override the hook if the file
5694 conflict markers, but enables the user to override the hook if the file
5697 is using conflict markers for testing purposes or the file format has
5695 is using conflict markers for testing purposes or the file format has
5698 strings that look like conflict markers.
5696 strings that look like conflict markers.
5699
5697
5700 By default, servers will ignore `--pushvars`. To enable it add the
5698 By default, servers will ignore `--pushvars`. To enable it add the
5701 following to your configuration file::
5699 following to your configuration file::
5702
5700
5703 [push]
5701 [push]
5704 pushvars.server = true
5702 pushvars.server = true
5705
5703
5706 Returns 0 if push was successful, 1 if nothing to push.
5704 Returns 0 if push was successful, 1 if nothing to push.
5707 """
5705 """
5708
5706
5709 opts = pycompat.byteskwargs(opts)
5707 opts = pycompat.byteskwargs(opts)
5710
5708
5711 if opts.get(b'all_bookmarks'):
5709 if opts.get(b'all_bookmarks'):
5712 cmdutil.check_incompatible_arguments(
5710 cmdutil.check_incompatible_arguments(
5713 opts,
5711 opts,
5714 b'all_bookmarks',
5712 b'all_bookmarks',
5715 [b'bookmark', b'rev'],
5713 [b'bookmark', b'rev'],
5716 )
5714 )
5717 opts[b'bookmark'] = list(repo._bookmarks)
5715 opts[b'bookmark'] = list(repo._bookmarks)
5718
5716
5719 if opts.get(b'bookmark'):
5717 if opts.get(b'bookmark'):
5720 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5718 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5721 for b in opts[b'bookmark']:
5719 for b in opts[b'bookmark']:
5722 # translate -B options to -r so changesets get pushed
5720 # translate -B options to -r so changesets get pushed
5723 b = repo._bookmarks.expandname(b)
5721 b = repo._bookmarks.expandname(b)
5724 if b in repo._bookmarks:
5722 if b in repo._bookmarks:
5725 opts.setdefault(b'rev', []).append(b)
5723 opts.setdefault(b'rev', []).append(b)
5726 else:
5724 else:
5727 # if we try to push a deleted bookmark, translate it to null
5725 # if we try to push a deleted bookmark, translate it to null
5728 # this lets simultaneous -r, -b options continue working
5726 # this lets simultaneous -r, -b options continue working
5729 opts.setdefault(b'rev', []).append(b"null")
5727 opts.setdefault(b'rev', []).append(b"null")
5730
5728
5731 some_pushed = False
5729 some_pushed = False
5732 result = 0
5730 result = 0
5733 for path in urlutil.get_push_paths(repo, ui, dests):
5731 for path in urlutil.get_push_paths(repo, ui, dests):
5734 dest = path.pushloc or path.loc
5732 dest = path.pushloc or path.loc
5735 branches = (path.branch, opts.get(b'branch') or [])
5733 branches = (path.branch, opts.get(b'branch') or [])
5736 ui.status(_(b'pushing to %s\n') % urlutil.hidepassword(dest))
5734 ui.status(_(b'pushing to %s\n') % urlutil.hidepassword(dest))
5737 revs, checkout = hg.addbranchrevs(
5735 revs, checkout = hg.addbranchrevs(
5738 repo, repo, branches, opts.get(b'rev')
5736 repo, repo, branches, opts.get(b'rev')
5739 )
5737 )
5740 other = hg.peer(repo, opts, dest)
5738 other = hg.peer(repo, opts, dest)
5741
5739
5742 try:
5740 try:
5743 if revs:
5741 if revs:
5744 revs = [repo[r].node() for r in logcmdutil.revrange(repo, revs)]
5742 revs = [repo[r].node() for r in logcmdutil.revrange(repo, revs)]
5745 if not revs:
5743 if not revs:
5746 raise error.InputError(
5744 raise error.InputError(
5747 _(b"specified revisions evaluate to an empty set"),
5745 _(b"specified revisions evaluate to an empty set"),
5748 hint=_(b"use different revision arguments"),
5746 hint=_(b"use different revision arguments"),
5749 )
5747 )
5750 elif path.pushrev:
5748 elif path.pushrev:
5751 # It doesn't make any sense to specify ancestor revisions. So limit
5749 # It doesn't make any sense to specify ancestor revisions. So limit
5752 # to DAG heads to make discovery simpler.
5750 # to DAG heads to make discovery simpler.
5753 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5751 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5754 revs = scmutil.revrange(repo, [expr])
5752 revs = scmutil.revrange(repo, [expr])
5755 revs = [repo[rev].node() for rev in revs]
5753 revs = [repo[rev].node() for rev in revs]
5756 if not revs:
5754 if not revs:
5757 raise error.InputError(
5755 raise error.InputError(
5758 _(
5756 _(
5759 b'default push revset for path evaluates to an empty set'
5757 b'default push revset for path evaluates to an empty set'
5760 )
5758 )
5761 )
5759 )
5762 elif ui.configbool(b'commands', b'push.require-revs'):
5760 elif ui.configbool(b'commands', b'push.require-revs'):
5763 raise error.InputError(
5761 raise error.InputError(
5764 _(b'no revisions specified to push'),
5762 _(b'no revisions specified to push'),
5765 hint=_(b'did you mean "hg push -r ."?'),
5763 hint=_(b'did you mean "hg push -r ."?'),
5766 )
5764 )
5767
5765
5768 repo._subtoppath = dest
5766 repo._subtoppath = dest
5769 try:
5767 try:
5770 # push subrepos depth-first for coherent ordering
5768 # push subrepos depth-first for coherent ordering
5771 c = repo[b'.']
5769 c = repo[b'.']
5772 subs = c.substate # only repos that are committed
5770 subs = c.substate # only repos that are committed
5773 for s in sorted(subs):
5771 for s in sorted(subs):
5774 sub_result = c.sub(s).push(opts)
5772 sub_result = c.sub(s).push(opts)
5775 if sub_result == 0:
5773 if sub_result == 0:
5776 return 1
5774 return 1
5777 finally:
5775 finally:
5778 del repo._subtoppath
5776 del repo._subtoppath
5779
5777
5780 opargs = dict(
5778 opargs = dict(
5781 opts.get(b'opargs', {})
5779 opts.get(b'opargs', {})
5782 ) # copy opargs since we may mutate it
5780 ) # copy opargs since we may mutate it
5783 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5781 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5784
5782
5785 pushop = exchange.push(
5783 pushop = exchange.push(
5786 repo,
5784 repo,
5787 other,
5785 other,
5788 opts.get(b'force'),
5786 opts.get(b'force'),
5789 revs=revs,
5787 revs=revs,
5790 newbranch=opts.get(b'new_branch'),
5788 newbranch=opts.get(b'new_branch'),
5791 bookmarks=opts.get(b'bookmark', ()),
5789 bookmarks=opts.get(b'bookmark', ()),
5792 publish=opts.get(b'publish'),
5790 publish=opts.get(b'publish'),
5793 opargs=opargs,
5791 opargs=opargs,
5794 )
5792 )
5795
5793
5796 if pushop.cgresult == 0:
5794 if pushop.cgresult == 0:
5797 result = 1
5795 result = 1
5798 elif pushop.cgresult is not None:
5796 elif pushop.cgresult is not None:
5799 some_pushed = True
5797 some_pushed = True
5800
5798
5801 if pushop.bkresult is not None:
5799 if pushop.bkresult is not None:
5802 if pushop.bkresult == 2:
5800 if pushop.bkresult == 2:
5803 result = 2
5801 result = 2
5804 elif not result and pushop.bkresult:
5802 elif not result and pushop.bkresult:
5805 result = 2
5803 result = 2
5806
5804
5807 if result:
5805 if result:
5808 break
5806 break
5809
5807
5810 finally:
5808 finally:
5811 other.close()
5809 other.close()
5812 if result == 0 and not some_pushed:
5810 if result == 0 and not some_pushed:
5813 result = 1
5811 result = 1
5814 return result
5812 return result
5815
5813
5816
5814
5817 @command(
5815 @command(
5818 b'recover',
5816 b'recover',
5819 [
5817 [
5820 (b'', b'verify', False, b"run `hg verify` after successful recover"),
5818 (b'', b'verify', False, b"run `hg verify` after successful recover"),
5821 ],
5819 ],
5822 helpcategory=command.CATEGORY_MAINTENANCE,
5820 helpcategory=command.CATEGORY_MAINTENANCE,
5823 )
5821 )
5824 def recover(ui, repo, **opts):
5822 def recover(ui, repo, **opts):
5825 """roll back an interrupted transaction
5823 """roll back an interrupted transaction
5826
5824
5827 Recover from an interrupted commit or pull.
5825 Recover from an interrupted commit or pull.
5828
5826
5829 This command tries to fix the repository status after an
5827 This command tries to fix the repository status after an
5830 interrupted operation. It should only be necessary when Mercurial
5828 interrupted operation. It should only be necessary when Mercurial
5831 suggests it.
5829 suggests it.
5832
5830
5833 Returns 0 if successful, 1 if nothing to recover or verify fails.
5831 Returns 0 if successful, 1 if nothing to recover or verify fails.
5834 """
5832 """
5835 ret = repo.recover()
5833 ret = repo.recover()
5836 if ret:
5834 if ret:
5837 if opts['verify']:
5835 if opts['verify']:
5838 return hg.verify(repo)
5836 return hg.verify(repo)
5839 else:
5837 else:
5840 msg = _(
5838 msg = _(
5841 b"(verify step skipped, run `hg verify` to check your "
5839 b"(verify step skipped, run `hg verify` to check your "
5842 b"repository content)\n"
5840 b"repository content)\n"
5843 )
5841 )
5844 ui.warn(msg)
5842 ui.warn(msg)
5845 return 0
5843 return 0
5846 return 1
5844 return 1
5847
5845
5848
5846
5849 @command(
5847 @command(
5850 b'remove|rm',
5848 b'remove|rm',
5851 [
5849 [
5852 (b'A', b'after', None, _(b'record delete for missing files')),
5850 (b'A', b'after', None, _(b'record delete for missing files')),
5853 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5851 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5854 ]
5852 ]
5855 + subrepoopts
5853 + subrepoopts
5856 + walkopts
5854 + walkopts
5857 + dryrunopts,
5855 + dryrunopts,
5858 _(b'[OPTION]... FILE...'),
5856 _(b'[OPTION]... FILE...'),
5859 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5857 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5860 helpbasic=True,
5858 helpbasic=True,
5861 inferrepo=True,
5859 inferrepo=True,
5862 )
5860 )
5863 def remove(ui, repo, *pats, **opts):
5861 def remove(ui, repo, *pats, **opts):
5864 """remove the specified files on the next commit
5862 """remove the specified files on the next commit
5865
5863
5866 Schedule the indicated files for removal from the current branch.
5864 Schedule the indicated files for removal from the current branch.
5867
5865
5868 This command schedules the files to be removed at the next commit.
5866 This command schedules the files to be removed at the next commit.
5869 To undo a remove before that, see :hg:`revert`. To undo added
5867 To undo a remove before that, see :hg:`revert`. To undo added
5870 files, see :hg:`forget`.
5868 files, see :hg:`forget`.
5871
5869
5872 .. container:: verbose
5870 .. container:: verbose
5873
5871
5874 -A/--after can be used to remove only files that have already
5872 -A/--after can be used to remove only files that have already
5875 been deleted, -f/--force can be used to force deletion, and -Af
5873 been deleted, -f/--force can be used to force deletion, and -Af
5876 can be used to remove files from the next revision without
5874 can be used to remove files from the next revision without
5877 deleting them from the working directory.
5875 deleting them from the working directory.
5878
5876
5879 The following table details the behavior of remove for different
5877 The following table details the behavior of remove for different
5880 file states (columns) and option combinations (rows). The file
5878 file states (columns) and option combinations (rows). The file
5881 states are Added [A], Clean [C], Modified [M] and Missing [!]
5879 states are Added [A], Clean [C], Modified [M] and Missing [!]
5882 (as reported by :hg:`status`). The actions are Warn, Remove
5880 (as reported by :hg:`status`). The actions are Warn, Remove
5883 (from branch) and Delete (from disk):
5881 (from branch) and Delete (from disk):
5884
5882
5885 ========= == == == ==
5883 ========= == == == ==
5886 opt/state A C M !
5884 opt/state A C M !
5887 ========= == == == ==
5885 ========= == == == ==
5888 none W RD W R
5886 none W RD W R
5889 -f R RD RD R
5887 -f R RD RD R
5890 -A W W W R
5888 -A W W W R
5891 -Af R R R R
5889 -Af R R R R
5892 ========= == == == ==
5890 ========= == == == ==
5893
5891
5894 .. note::
5892 .. note::
5895
5893
5896 :hg:`remove` never deletes files in Added [A] state from the
5894 :hg:`remove` never deletes files in Added [A] state from the
5897 working directory, not even if ``--force`` is specified.
5895 working directory, not even if ``--force`` is specified.
5898
5896
5899 Returns 0 on success, 1 if any warnings encountered.
5897 Returns 0 on success, 1 if any warnings encountered.
5900 """
5898 """
5901
5899
5902 opts = pycompat.byteskwargs(opts)
5900 opts = pycompat.byteskwargs(opts)
5903 after, force = opts.get(b'after'), opts.get(b'force')
5901 after, force = opts.get(b'after'), opts.get(b'force')
5904 dryrun = opts.get(b'dry_run')
5902 dryrun = opts.get(b'dry_run')
5905 if not pats and not after:
5903 if not pats and not after:
5906 raise error.InputError(_(b'no files specified'))
5904 raise error.InputError(_(b'no files specified'))
5907
5905
5908 m = scmutil.match(repo[None], pats, opts)
5906 m = scmutil.match(repo[None], pats, opts)
5909 subrepos = opts.get(b'subrepos')
5907 subrepos = opts.get(b'subrepos')
5910 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5908 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5911 return cmdutil.remove(
5909 return cmdutil.remove(
5912 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5910 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5913 )
5911 )
5914
5912
5915
5913
5916 @command(
5914 @command(
5917 b'rename|move|mv',
5915 b'rename|move|mv',
5918 [
5916 [
5919 (b'', b'forget', None, _(b'unmark a destination file as renamed')),
5917 (b'', b'forget', None, _(b'unmark a destination file as renamed')),
5920 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5918 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5921 (
5919 (
5922 b'',
5920 b'',
5923 b'at-rev',
5921 b'at-rev',
5924 b'',
5922 b'',
5925 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5923 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5926 _(b'REV'),
5924 _(b'REV'),
5927 ),
5925 ),
5928 (
5926 (
5929 b'f',
5927 b'f',
5930 b'force',
5928 b'force',
5931 None,
5929 None,
5932 _(b'forcibly move over an existing managed file'),
5930 _(b'forcibly move over an existing managed file'),
5933 ),
5931 ),
5934 ]
5932 ]
5935 + walkopts
5933 + walkopts
5936 + dryrunopts,
5934 + dryrunopts,
5937 _(b'[OPTION]... SOURCE... DEST'),
5935 _(b'[OPTION]... SOURCE... DEST'),
5938 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5936 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5939 )
5937 )
5940 def rename(ui, repo, *pats, **opts):
5938 def rename(ui, repo, *pats, **opts):
5941 """rename files; equivalent of copy + remove
5939 """rename files; equivalent of copy + remove
5942
5940
5943 Mark dest as copies of sources; mark sources for deletion. If dest
5941 Mark dest as copies of sources; mark sources for deletion. If dest
5944 is a directory, copies are put in that directory. If dest is a
5942 is a directory, copies are put in that directory. If dest is a
5945 file, there can only be one source.
5943 file, there can only be one source.
5946
5944
5947 By default, this command copies the contents of files as they
5945 By default, this command copies the contents of files as they
5948 exist in the working directory. If invoked with -A/--after, the
5946 exist in the working directory. If invoked with -A/--after, the
5949 operation is recorded, but no copying is performed.
5947 operation is recorded, but no copying is performed.
5950
5948
5951 To undo marking a destination file as renamed, use --forget. With that
5949 To undo marking a destination file as renamed, use --forget. With that
5952 option, all given (positional) arguments are unmarked as renames. The
5950 option, all given (positional) arguments are unmarked as renames. The
5953 destination file(s) will be left in place (still tracked). The source
5951 destination file(s) will be left in place (still tracked). The source
5954 file(s) will not be restored. Note that :hg:`rename --forget` behaves
5952 file(s) will not be restored. Note that :hg:`rename --forget` behaves
5955 the same way as :hg:`copy --forget`.
5953 the same way as :hg:`copy --forget`.
5956
5954
5957 This command takes effect with the next commit by default.
5955 This command takes effect with the next commit by default.
5958
5956
5959 Returns 0 on success, 1 if errors are encountered.
5957 Returns 0 on success, 1 if errors are encountered.
5960 """
5958 """
5961 opts = pycompat.byteskwargs(opts)
5959 opts = pycompat.byteskwargs(opts)
5962 with repo.wlock():
5960 with repo.wlock():
5963 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5961 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5964
5962
5965
5963
5966 @command(
5964 @command(
5967 b'resolve',
5965 b'resolve',
5968 [
5966 [
5969 (b'a', b'all', None, _(b'select all unresolved files')),
5967 (b'a', b'all', None, _(b'select all unresolved files')),
5970 (b'l', b'list', None, _(b'list state of files needing merge')),
5968 (b'l', b'list', None, _(b'list state of files needing merge')),
5971 (b'm', b'mark', None, _(b'mark files as resolved')),
5969 (b'm', b'mark', None, _(b'mark files as resolved')),
5972 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5970 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5973 (b'n', b'no-status', None, _(b'hide status prefix')),
5971 (b'n', b'no-status', None, _(b'hide status prefix')),
5974 (b'', b're-merge', None, _(b're-merge files')),
5972 (b'', b're-merge', None, _(b're-merge files')),
5975 ]
5973 ]
5976 + mergetoolopts
5974 + mergetoolopts
5977 + walkopts
5975 + walkopts
5978 + formatteropts,
5976 + formatteropts,
5979 _(b'[OPTION]... [FILE]...'),
5977 _(b'[OPTION]... [FILE]...'),
5980 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5978 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5981 inferrepo=True,
5979 inferrepo=True,
5982 )
5980 )
5983 def resolve(ui, repo, *pats, **opts):
5981 def resolve(ui, repo, *pats, **opts):
5984 """redo merges or set/view the merge status of files
5982 """redo merges or set/view the merge status of files
5985
5983
5986 Merges with unresolved conflicts are often the result of
5984 Merges with unresolved conflicts are often the result of
5987 non-interactive merging using the ``internal:merge`` configuration
5985 non-interactive merging using the ``internal:merge`` configuration
5988 setting, or a command-line merge tool like ``diff3``. The resolve
5986 setting, or a command-line merge tool like ``diff3``. The resolve
5989 command is used to manage the files involved in a merge, after
5987 command is used to manage the files involved in a merge, after
5990 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5988 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5991 working directory must have two parents). See :hg:`help
5989 working directory must have two parents). See :hg:`help
5992 merge-tools` for information on configuring merge tools.
5990 merge-tools` for information on configuring merge tools.
5993
5991
5994 The resolve command can be used in the following ways:
5992 The resolve command can be used in the following ways:
5995
5993
5996 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5994 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5997 the specified files, discarding any previous merge attempts. Re-merging
5995 the specified files, discarding any previous merge attempts. Re-merging
5998 is not performed for files already marked as resolved. Use ``--all/-a``
5996 is not performed for files already marked as resolved. Use ``--all/-a``
5999 to select all unresolved files. ``--tool`` can be used to specify
5997 to select all unresolved files. ``--tool`` can be used to specify
6000 the merge tool used for the given files. It overrides the HGMERGE
5998 the merge tool used for the given files. It overrides the HGMERGE
6001 environment variable and your configuration files. Previous file
5999 environment variable and your configuration files. Previous file
6002 contents are saved with a ``.orig`` suffix.
6000 contents are saved with a ``.orig`` suffix.
6003
6001
6004 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
6002 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
6005 (e.g. after having manually fixed-up the files). The default is
6003 (e.g. after having manually fixed-up the files). The default is
6006 to mark all unresolved files.
6004 to mark all unresolved files.
6007
6005
6008 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
6006 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
6009 default is to mark all resolved files.
6007 default is to mark all resolved files.
6010
6008
6011 - :hg:`resolve -l`: list files which had or still have conflicts.
6009 - :hg:`resolve -l`: list files which had or still have conflicts.
6012 In the printed list, ``U`` = unresolved and ``R`` = resolved.
6010 In the printed list, ``U`` = unresolved and ``R`` = resolved.
6013 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
6011 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
6014 the list. See :hg:`help filesets` for details.
6012 the list. See :hg:`help filesets` for details.
6015
6013
6016 .. note::
6014 .. note::
6017
6015
6018 Mercurial will not let you commit files with unresolved merge
6016 Mercurial will not let you commit files with unresolved merge
6019 conflicts. You must use :hg:`resolve -m ...` before you can
6017 conflicts. You must use :hg:`resolve -m ...` before you can
6020 commit after a conflicting merge.
6018 commit after a conflicting merge.
6021
6019
6022 .. container:: verbose
6020 .. container:: verbose
6023
6021
6024 Template:
6022 Template:
6025
6023
6026 The following keywords are supported in addition to the common template
6024 The following keywords are supported in addition to the common template
6027 keywords and functions. See also :hg:`help templates`.
6025 keywords and functions. See also :hg:`help templates`.
6028
6026
6029 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
6027 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
6030 :path: String. Repository-absolute path of the file.
6028 :path: String. Repository-absolute path of the file.
6031
6029
6032 Returns 0 on success, 1 if any files fail a resolve attempt.
6030 Returns 0 on success, 1 if any files fail a resolve attempt.
6033 """
6031 """
6034
6032
6035 opts = pycompat.byteskwargs(opts)
6033 opts = pycompat.byteskwargs(opts)
6036 confirm = ui.configbool(b'commands', b'resolve.confirm')
6034 confirm = ui.configbool(b'commands', b'resolve.confirm')
6037 flaglist = b'all mark unmark list no_status re_merge'.split()
6035 flaglist = b'all mark unmark list no_status re_merge'.split()
6038 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
6036 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
6039
6037
6040 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
6038 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
6041 if actioncount > 1:
6039 if actioncount > 1:
6042 raise error.InputError(_(b"too many actions specified"))
6040 raise error.InputError(_(b"too many actions specified"))
6043 elif actioncount == 0 and ui.configbool(
6041 elif actioncount == 0 and ui.configbool(
6044 b'commands', b'resolve.explicit-re-merge'
6042 b'commands', b'resolve.explicit-re-merge'
6045 ):
6043 ):
6046 hint = _(b'use --mark, --unmark, --list or --re-merge')
6044 hint = _(b'use --mark, --unmark, --list or --re-merge')
6047 raise error.InputError(_(b'no action specified'), hint=hint)
6045 raise error.InputError(_(b'no action specified'), hint=hint)
6048 if pats and all:
6046 if pats and all:
6049 raise error.InputError(_(b"can't specify --all and patterns"))
6047 raise error.InputError(_(b"can't specify --all and patterns"))
6050 if not (all or pats or show or mark or unmark):
6048 if not (all or pats or show or mark or unmark):
6051 raise error.InputError(
6049 raise error.InputError(
6052 _(b'no files or directories specified'),
6050 _(b'no files or directories specified'),
6053 hint=b'use --all to re-merge all unresolved files',
6051 hint=b'use --all to re-merge all unresolved files',
6054 )
6052 )
6055
6053
6056 if confirm:
6054 if confirm:
6057 if all:
6055 if all:
6058 if ui.promptchoice(
6056 if ui.promptchoice(
6059 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
6057 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
6060 ):
6058 ):
6061 raise error.CanceledError(_(b'user quit'))
6059 raise error.CanceledError(_(b'user quit'))
6062 if mark and not pats:
6060 if mark and not pats:
6063 if ui.promptchoice(
6061 if ui.promptchoice(
6064 _(
6062 _(
6065 b'mark all unresolved files as resolved (yn)?'
6063 b'mark all unresolved files as resolved (yn)?'
6066 b'$$ &Yes $$ &No'
6064 b'$$ &Yes $$ &No'
6067 )
6065 )
6068 ):
6066 ):
6069 raise error.CanceledError(_(b'user quit'))
6067 raise error.CanceledError(_(b'user quit'))
6070 if unmark and not pats:
6068 if unmark and not pats:
6071 if ui.promptchoice(
6069 if ui.promptchoice(
6072 _(
6070 _(
6073 b'mark all resolved files as unresolved (yn)?'
6071 b'mark all resolved files as unresolved (yn)?'
6074 b'$$ &Yes $$ &No'
6072 b'$$ &Yes $$ &No'
6075 )
6073 )
6076 ):
6074 ):
6077 raise error.CanceledError(_(b'user quit'))
6075 raise error.CanceledError(_(b'user quit'))
6078
6076
6079 uipathfn = scmutil.getuipathfn(repo)
6077 uipathfn = scmutil.getuipathfn(repo)
6080
6078
6081 if show:
6079 if show:
6082 ui.pager(b'resolve')
6080 ui.pager(b'resolve')
6083 fm = ui.formatter(b'resolve', opts)
6081 fm = ui.formatter(b'resolve', opts)
6084 ms = mergestatemod.mergestate.read(repo)
6082 ms = mergestatemod.mergestate.read(repo)
6085 wctx = repo[None]
6083 wctx = repo[None]
6086 m = scmutil.match(wctx, pats, opts)
6084 m = scmutil.match(wctx, pats, opts)
6087
6085
6088 # Labels and keys based on merge state. Unresolved path conflicts show
6086 # Labels and keys based on merge state. Unresolved path conflicts show
6089 # as 'P'. Resolved path conflicts show as 'R', the same as normal
6087 # as 'P'. Resolved path conflicts show as 'R', the same as normal
6090 # resolved conflicts.
6088 # resolved conflicts.
6091 mergestateinfo = {
6089 mergestateinfo = {
6092 mergestatemod.MERGE_RECORD_UNRESOLVED: (
6090 mergestatemod.MERGE_RECORD_UNRESOLVED: (
6093 b'resolve.unresolved',
6091 b'resolve.unresolved',
6094 b'U',
6092 b'U',
6095 ),
6093 ),
6096 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
6094 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
6097 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
6095 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
6098 b'resolve.unresolved',
6096 b'resolve.unresolved',
6099 b'P',
6097 b'P',
6100 ),
6098 ),
6101 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
6099 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
6102 b'resolve.resolved',
6100 b'resolve.resolved',
6103 b'R',
6101 b'R',
6104 ),
6102 ),
6105 }
6103 }
6106
6104
6107 for f in ms:
6105 for f in ms:
6108 if not m(f):
6106 if not m(f):
6109 continue
6107 continue
6110
6108
6111 label, key = mergestateinfo[ms[f]]
6109 label, key = mergestateinfo[ms[f]]
6112 fm.startitem()
6110 fm.startitem()
6113 fm.context(ctx=wctx)
6111 fm.context(ctx=wctx)
6114 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
6112 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
6115 fm.data(path=f)
6113 fm.data(path=f)
6116 fm.plain(b'%s\n' % uipathfn(f), label=label)
6114 fm.plain(b'%s\n' % uipathfn(f), label=label)
6117 fm.end()
6115 fm.end()
6118 return 0
6116 return 0
6119
6117
6120 with repo.wlock():
6118 with repo.wlock():
6121 ms = mergestatemod.mergestate.read(repo)
6119 ms = mergestatemod.mergestate.read(repo)
6122
6120
6123 if not (ms.active() or repo.dirstate.p2() != repo.nullid):
6121 if not (ms.active() or repo.dirstate.p2() != repo.nullid):
6124 raise error.StateError(
6122 raise error.StateError(
6125 _(b'resolve command not applicable when not merging')
6123 _(b'resolve command not applicable when not merging')
6126 )
6124 )
6127
6125
6128 wctx = repo[None]
6126 wctx = repo[None]
6129 m = scmutil.match(wctx, pats, opts)
6127 m = scmutil.match(wctx, pats, opts)
6130 ret = 0
6128 ret = 0
6131 didwork = False
6129 didwork = False
6132
6130
6133 hasconflictmarkers = []
6131 hasconflictmarkers = []
6134 if mark:
6132 if mark:
6135 markcheck = ui.config(b'commands', b'resolve.mark-check')
6133 markcheck = ui.config(b'commands', b'resolve.mark-check')
6136 if markcheck not in [b'warn', b'abort']:
6134 if markcheck not in [b'warn', b'abort']:
6137 # Treat all invalid / unrecognized values as 'none'.
6135 # Treat all invalid / unrecognized values as 'none'.
6138 markcheck = False
6136 markcheck = False
6139 for f in ms:
6137 for f in ms:
6140 if not m(f):
6138 if not m(f):
6141 continue
6139 continue
6142
6140
6143 didwork = True
6141 didwork = True
6144
6142
6145 # path conflicts must be resolved manually
6143 # path conflicts must be resolved manually
6146 if ms[f] in (
6144 if ms[f] in (
6147 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
6145 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
6148 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
6146 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
6149 ):
6147 ):
6150 if mark:
6148 if mark:
6151 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
6149 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
6152 elif unmark:
6150 elif unmark:
6153 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
6151 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
6154 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
6152 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
6155 ui.warn(
6153 ui.warn(
6156 _(b'%s: path conflict must be resolved manually\n')
6154 _(b'%s: path conflict must be resolved manually\n')
6157 % uipathfn(f)
6155 % uipathfn(f)
6158 )
6156 )
6159 continue
6157 continue
6160
6158
6161 if mark:
6159 if mark:
6162 if markcheck:
6160 if markcheck:
6163 fdata = repo.wvfs.tryread(f)
6161 fdata = repo.wvfs.tryread(f)
6164 if (
6162 if (
6165 filemerge.hasconflictmarkers(fdata)
6163 filemerge.hasconflictmarkers(fdata)
6166 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6164 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6167 ):
6165 ):
6168 hasconflictmarkers.append(f)
6166 hasconflictmarkers.append(f)
6169 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6167 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6170 elif unmark:
6168 elif unmark:
6171 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6169 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6172 else:
6170 else:
6173 # backup pre-resolve (merge uses .orig for its own purposes)
6171 # backup pre-resolve (merge uses .orig for its own purposes)
6174 a = repo.wjoin(f)
6172 a = repo.wjoin(f)
6175 try:
6173 try:
6176 util.copyfile(a, a + b".resolve")
6174 util.copyfile(a, a + b".resolve")
6177 except FileNotFoundError:
6175 except FileNotFoundError:
6178 pass
6176 pass
6179
6177
6180 try:
6178 try:
6181 # preresolve file
6179 # preresolve file
6182 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6180 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6183 with ui.configoverride(overrides, b'resolve'):
6181 with ui.configoverride(overrides, b'resolve'):
6184 r = ms.resolve(f, wctx)
6182 r = ms.resolve(f, wctx)
6185 if r:
6183 if r:
6186 ret = 1
6184 ret = 1
6187 finally:
6185 finally:
6188 ms.commit()
6186 ms.commit()
6189
6187
6190 # replace filemerge's .orig file with our resolve file
6188 # replace filemerge's .orig file with our resolve file
6191 try:
6189 try:
6192 util.rename(
6190 util.rename(
6193 a + b".resolve", scmutil.backuppath(ui, repo, f)
6191 a + b".resolve", scmutil.backuppath(ui, repo, f)
6194 )
6192 )
6195 except FileNotFoundError:
6193 except FileNotFoundError:
6196 pass
6194 pass
6197
6195
6198 if hasconflictmarkers:
6196 if hasconflictmarkers:
6199 ui.warn(
6197 ui.warn(
6200 _(
6198 _(
6201 b'warning: the following files still have conflict '
6199 b'warning: the following files still have conflict '
6202 b'markers:\n'
6200 b'markers:\n'
6203 )
6201 )
6204 + b''.join(
6202 + b''.join(
6205 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6203 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6206 )
6204 )
6207 )
6205 )
6208 if markcheck == b'abort' and not all and not pats:
6206 if markcheck == b'abort' and not all and not pats:
6209 raise error.StateError(
6207 raise error.StateError(
6210 _(b'conflict markers detected'),
6208 _(b'conflict markers detected'),
6211 hint=_(b'use --all to mark anyway'),
6209 hint=_(b'use --all to mark anyway'),
6212 )
6210 )
6213
6211
6214 ms.commit()
6212 ms.commit()
6215 branchmerge = repo.dirstate.p2() != repo.nullid
6213 branchmerge = repo.dirstate.p2() != repo.nullid
6216 # resolve is not doing a parent change here, however, `record updates`
6214 # resolve is not doing a parent change here, however, `record updates`
6217 # will call some dirstate API that at intended for parent changes call.
6215 # will call some dirstate API that at intended for parent changes call.
6218 # Ideally we would not need this and could implement a lighter version
6216 # Ideally we would not need this and could implement a lighter version
6219 # of the recordupdateslogic that will not have to deal with the part
6217 # of the recordupdateslogic that will not have to deal with the part
6220 # related to parent changes. However this would requires that:
6218 # related to parent changes. However this would requires that:
6221 # - we are sure we passed around enough information at update/merge
6219 # - we are sure we passed around enough information at update/merge
6222 # time to no longer needs it at `hg resolve time`
6220 # time to no longer needs it at `hg resolve time`
6223 # - we are sure we store that information well enough to be able to reuse it
6221 # - we are sure we store that information well enough to be able to reuse it
6224 # - we are the necessary logic to reuse it right.
6222 # - we are the necessary logic to reuse it right.
6225 #
6223 #
6226 # All this should eventually happens, but in the mean time, we use this
6224 # All this should eventually happens, but in the mean time, we use this
6227 # context manager slightly out of the context it should be.
6225 # context manager slightly out of the context it should be.
6228 with repo.dirstate.parentchange():
6226 with repo.dirstate.parentchange():
6229 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6227 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6230
6228
6231 if not didwork and pats:
6229 if not didwork and pats:
6232 hint = None
6230 hint = None
6233 if not any([p for p in pats if p.find(b':') >= 0]):
6231 if not any([p for p in pats if p.find(b':') >= 0]):
6234 pats = [b'path:%s' % p for p in pats]
6232 pats = [b'path:%s' % p for p in pats]
6235 m = scmutil.match(wctx, pats, opts)
6233 m = scmutil.match(wctx, pats, opts)
6236 for f in ms:
6234 for f in ms:
6237 if not m(f):
6235 if not m(f):
6238 continue
6236 continue
6239
6237
6240 def flag(o):
6238 def flag(o):
6241 if o == b're_merge':
6239 if o == b're_merge':
6242 return b'--re-merge '
6240 return b'--re-merge '
6243 return b'-%s ' % o[0:1]
6241 return b'-%s ' % o[0:1]
6244
6242
6245 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6243 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6246 hint = _(b"(try: hg resolve %s%s)\n") % (
6244 hint = _(b"(try: hg resolve %s%s)\n") % (
6247 flags,
6245 flags,
6248 b' '.join(pats),
6246 b' '.join(pats),
6249 )
6247 )
6250 break
6248 break
6251 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6249 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6252 if hint:
6250 if hint:
6253 ui.warn(hint)
6251 ui.warn(hint)
6254
6252
6255 unresolvedf = ms.unresolvedcount()
6253 unresolvedf = ms.unresolvedcount()
6256 if not unresolvedf:
6254 if not unresolvedf:
6257 ui.status(_(b'(no more unresolved files)\n'))
6255 ui.status(_(b'(no more unresolved files)\n'))
6258 cmdutil.checkafterresolved(repo)
6256 cmdutil.checkafterresolved(repo)
6259
6257
6260 return ret
6258 return ret
6261
6259
6262
6260
6263 @command(
6261 @command(
6264 b'revert',
6262 b'revert',
6265 [
6263 [
6266 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6264 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6267 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6265 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6268 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6266 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6269 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6267 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6270 (b'i', b'interactive', None, _(b'interactively select the changes')),
6268 (b'i', b'interactive', None, _(b'interactively select the changes')),
6271 ]
6269 ]
6272 + walkopts
6270 + walkopts
6273 + dryrunopts,
6271 + dryrunopts,
6274 _(b'[OPTION]... [-r REV] [NAME]...'),
6272 _(b'[OPTION]... [-r REV] [NAME]...'),
6275 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6273 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6276 )
6274 )
6277 def revert(ui, repo, *pats, **opts):
6275 def revert(ui, repo, *pats, **opts):
6278 """restore files to their checkout state
6276 """restore files to their checkout state
6279
6277
6280 .. note::
6278 .. note::
6281
6279
6282 To check out earlier revisions, you should use :hg:`update REV`.
6280 To check out earlier revisions, you should use :hg:`update REV`.
6283 To cancel an uncommitted merge (and lose your changes),
6281 To cancel an uncommitted merge (and lose your changes),
6284 use :hg:`merge --abort`.
6282 use :hg:`merge --abort`.
6285
6283
6286 With no revision specified, revert the specified files or directories
6284 With no revision specified, revert the specified files or directories
6287 to the contents they had in the parent of the working directory.
6285 to the contents they had in the parent of the working directory.
6288 This restores the contents of files to an unmodified
6286 This restores the contents of files to an unmodified
6289 state and unschedules adds, removes, copies, and renames. If the
6287 state and unschedules adds, removes, copies, and renames. If the
6290 working directory has two parents, you must explicitly specify a
6288 working directory has two parents, you must explicitly specify a
6291 revision.
6289 revision.
6292
6290
6293 Using the -r/--rev or -d/--date options, revert the given files or
6291 Using the -r/--rev or -d/--date options, revert the given files or
6294 directories to their states as of a specific revision. Because
6292 directories to their states as of a specific revision. Because
6295 revert does not change the working directory parents, this will
6293 revert does not change the working directory parents, this will
6296 cause these files to appear modified. This can be helpful to "back
6294 cause these files to appear modified. This can be helpful to "back
6297 out" some or all of an earlier change. See :hg:`backout` for a
6295 out" some or all of an earlier change. See :hg:`backout` for a
6298 related method.
6296 related method.
6299
6297
6300 Modified files are saved with a .orig suffix before reverting.
6298 Modified files are saved with a .orig suffix before reverting.
6301 To disable these backups, use --no-backup. It is possible to store
6299 To disable these backups, use --no-backup. It is possible to store
6302 the backup files in a custom directory relative to the root of the
6300 the backup files in a custom directory relative to the root of the
6303 repository by setting the ``ui.origbackuppath`` configuration
6301 repository by setting the ``ui.origbackuppath`` configuration
6304 option.
6302 option.
6305
6303
6306 See :hg:`help dates` for a list of formats valid for -d/--date.
6304 See :hg:`help dates` for a list of formats valid for -d/--date.
6307
6305
6308 See :hg:`help backout` for a way to reverse the effect of an
6306 See :hg:`help backout` for a way to reverse the effect of an
6309 earlier changeset.
6307 earlier changeset.
6310
6308
6311 Returns 0 on success.
6309 Returns 0 on success.
6312 """
6310 """
6313
6311
6314 opts = pycompat.byteskwargs(opts)
6312 opts = pycompat.byteskwargs(opts)
6315 if opts.get(b"date"):
6313 if opts.get(b"date"):
6316 cmdutil.check_incompatible_arguments(opts, b'date', [b'rev'])
6314 cmdutil.check_incompatible_arguments(opts, b'date', [b'rev'])
6317 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6315 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6318
6316
6319 parent, p2 = repo.dirstate.parents()
6317 parent, p2 = repo.dirstate.parents()
6320 if not opts.get(b'rev') and p2 != repo.nullid:
6318 if not opts.get(b'rev') and p2 != repo.nullid:
6321 # revert after merge is a trap for new users (issue2915)
6319 # revert after merge is a trap for new users (issue2915)
6322 raise error.InputError(
6320 raise error.InputError(
6323 _(b'uncommitted merge with no revision specified'),
6321 _(b'uncommitted merge with no revision specified'),
6324 hint=_(b"use 'hg update' or see 'hg help revert'"),
6322 hint=_(b"use 'hg update' or see 'hg help revert'"),
6325 )
6323 )
6326
6324
6327 rev = opts.get(b'rev')
6325 rev = opts.get(b'rev')
6328 if rev:
6326 if rev:
6329 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6327 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6330 ctx = logcmdutil.revsingle(repo, rev)
6328 ctx = logcmdutil.revsingle(repo, rev)
6331
6329
6332 if not (
6330 if not (
6333 pats
6331 pats
6334 or opts.get(b'include')
6332 or opts.get(b'include')
6335 or opts.get(b'exclude')
6333 or opts.get(b'exclude')
6336 or opts.get(b'all')
6334 or opts.get(b'all')
6337 or opts.get(b'interactive')
6335 or opts.get(b'interactive')
6338 ):
6336 ):
6339 msg = _(b"no files or directories specified")
6337 msg = _(b"no files or directories specified")
6340 if p2 != repo.nullid:
6338 if p2 != repo.nullid:
6341 hint = _(
6339 hint = _(
6342 b"uncommitted merge, use --all to discard all changes,"
6340 b"uncommitted merge, use --all to discard all changes,"
6343 b" or 'hg update -C .' to abort the merge"
6341 b" or 'hg update -C .' to abort the merge"
6344 )
6342 )
6345 raise error.InputError(msg, hint=hint)
6343 raise error.InputError(msg, hint=hint)
6346 dirty = any(repo.status())
6344 dirty = any(repo.status())
6347 node = ctx.node()
6345 node = ctx.node()
6348 if node != parent:
6346 if node != parent:
6349 if dirty:
6347 if dirty:
6350 hint = (
6348 hint = (
6351 _(
6349 _(
6352 b"uncommitted changes, use --all to discard all"
6350 b"uncommitted changes, use --all to discard all"
6353 b" changes, or 'hg update %d' to update"
6351 b" changes, or 'hg update %d' to update"
6354 )
6352 )
6355 % ctx.rev()
6353 % ctx.rev()
6356 )
6354 )
6357 else:
6355 else:
6358 hint = (
6356 hint = (
6359 _(
6357 _(
6360 b"use --all to revert all files,"
6358 b"use --all to revert all files,"
6361 b" or 'hg update %d' to update"
6359 b" or 'hg update %d' to update"
6362 )
6360 )
6363 % ctx.rev()
6361 % ctx.rev()
6364 )
6362 )
6365 elif dirty:
6363 elif dirty:
6366 hint = _(b"uncommitted changes, use --all to discard all changes")
6364 hint = _(b"uncommitted changes, use --all to discard all changes")
6367 else:
6365 else:
6368 hint = _(b"use --all to revert all files")
6366 hint = _(b"use --all to revert all files")
6369 raise error.InputError(msg, hint=hint)
6367 raise error.InputError(msg, hint=hint)
6370
6368
6371 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6369 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6372
6370
6373
6371
6374 @command(
6372 @command(
6375 b'rollback',
6373 b'rollback',
6376 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6374 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6377 helpcategory=command.CATEGORY_MAINTENANCE,
6375 helpcategory=command.CATEGORY_MAINTENANCE,
6378 )
6376 )
6379 def rollback(ui, repo, **opts):
6377 def rollback(ui, repo, **opts):
6380 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6378 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6381
6379
6382 Please use :hg:`commit --amend` instead of rollback to correct
6380 Please use :hg:`commit --amend` instead of rollback to correct
6383 mistakes in the last commit.
6381 mistakes in the last commit.
6384
6382
6385 This command should be used with care. There is only one level of
6383 This command should be used with care. There is only one level of
6386 rollback, and there is no way to undo a rollback. It will also
6384 rollback, and there is no way to undo a rollback. It will also
6387 restore the dirstate at the time of the last transaction, losing
6385 restore the dirstate at the time of the last transaction, losing
6388 any dirstate changes since that time. This command does not alter
6386 any dirstate changes since that time. This command does not alter
6389 the working directory.
6387 the working directory.
6390
6388
6391 Transactions are used to encapsulate the effects of all commands
6389 Transactions are used to encapsulate the effects of all commands
6392 that create new changesets or propagate existing changesets into a
6390 that create new changesets or propagate existing changesets into a
6393 repository.
6391 repository.
6394
6392
6395 .. container:: verbose
6393 .. container:: verbose
6396
6394
6397 For example, the following commands are transactional, and their
6395 For example, the following commands are transactional, and their
6398 effects can be rolled back:
6396 effects can be rolled back:
6399
6397
6400 - commit
6398 - commit
6401 - import
6399 - import
6402 - pull
6400 - pull
6403 - push (with this repository as the destination)
6401 - push (with this repository as the destination)
6404 - unbundle
6402 - unbundle
6405
6403
6406 To avoid permanent data loss, rollback will refuse to rollback a
6404 To avoid permanent data loss, rollback will refuse to rollback a
6407 commit transaction if it isn't checked out. Use --force to
6405 commit transaction if it isn't checked out. Use --force to
6408 override this protection.
6406 override this protection.
6409
6407
6410 The rollback command can be entirely disabled by setting the
6408 The rollback command can be entirely disabled by setting the
6411 ``ui.rollback`` configuration setting to false. If you're here
6409 ``ui.rollback`` configuration setting to false. If you're here
6412 because you want to use rollback and it's disabled, you can
6410 because you want to use rollback and it's disabled, you can
6413 re-enable the command by setting ``ui.rollback`` to true.
6411 re-enable the command by setting ``ui.rollback`` to true.
6414
6412
6415 This command is not intended for use on public repositories. Once
6413 This command is not intended for use on public repositories. Once
6416 changes are visible for pull by other users, rolling a transaction
6414 changes are visible for pull by other users, rolling a transaction
6417 back locally is ineffective (someone else may already have pulled
6415 back locally is ineffective (someone else may already have pulled
6418 the changes). Furthermore, a race is possible with readers of the
6416 the changes). Furthermore, a race is possible with readers of the
6419 repository; for example an in-progress pull from the repository
6417 repository; for example an in-progress pull from the repository
6420 may fail if a rollback is performed.
6418 may fail if a rollback is performed.
6421
6419
6422 Returns 0 on success, 1 if no rollback data is available.
6420 Returns 0 on success, 1 if no rollback data is available.
6423 """
6421 """
6424 if not ui.configbool(b'ui', b'rollback'):
6422 if not ui.configbool(b'ui', b'rollback'):
6425 raise error.Abort(
6423 raise error.Abort(
6426 _(b'rollback is disabled because it is unsafe'),
6424 _(b'rollback is disabled because it is unsafe'),
6427 hint=b'see `hg help -v rollback` for information',
6425 hint=b'see `hg help -v rollback` for information',
6428 )
6426 )
6429 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6427 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6430
6428
6431
6429
6432 @command(
6430 @command(
6433 b'root',
6431 b'root',
6434 [] + formatteropts,
6432 [] + formatteropts,
6435 intents={INTENT_READONLY},
6433 intents={INTENT_READONLY},
6436 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6434 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6437 )
6435 )
6438 def root(ui, repo, **opts):
6436 def root(ui, repo, **opts):
6439 """print the root (top) of the current working directory
6437 """print the root (top) of the current working directory
6440
6438
6441 Print the root directory of the current repository.
6439 Print the root directory of the current repository.
6442
6440
6443 .. container:: verbose
6441 .. container:: verbose
6444
6442
6445 Template:
6443 Template:
6446
6444
6447 The following keywords are supported in addition to the common template
6445 The following keywords are supported in addition to the common template
6448 keywords and functions. See also :hg:`help templates`.
6446 keywords and functions. See also :hg:`help templates`.
6449
6447
6450 :hgpath: String. Path to the .hg directory.
6448 :hgpath: String. Path to the .hg directory.
6451 :storepath: String. Path to the directory holding versioned data.
6449 :storepath: String. Path to the directory holding versioned data.
6452
6450
6453 Returns 0 on success.
6451 Returns 0 on success.
6454 """
6452 """
6455 opts = pycompat.byteskwargs(opts)
6453 opts = pycompat.byteskwargs(opts)
6456 with ui.formatter(b'root', opts) as fm:
6454 with ui.formatter(b'root', opts) as fm:
6457 fm.startitem()
6455 fm.startitem()
6458 fm.write(b'reporoot', b'%s\n', repo.root)
6456 fm.write(b'reporoot', b'%s\n', repo.root)
6459 fm.data(hgpath=repo.path, storepath=repo.spath)
6457 fm.data(hgpath=repo.path, storepath=repo.spath)
6460
6458
6461
6459
6462 @command(
6460 @command(
6463 b'serve',
6461 b'serve',
6464 [
6462 [
6465 (
6463 (
6466 b'A',
6464 b'A',
6467 b'accesslog',
6465 b'accesslog',
6468 b'',
6466 b'',
6469 _(b'name of access log file to write to'),
6467 _(b'name of access log file to write to'),
6470 _(b'FILE'),
6468 _(b'FILE'),
6471 ),
6469 ),
6472 (b'd', b'daemon', None, _(b'run server in background')),
6470 (b'd', b'daemon', None, _(b'run server in background')),
6473 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6471 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6474 (
6472 (
6475 b'E',
6473 b'E',
6476 b'errorlog',
6474 b'errorlog',
6477 b'',
6475 b'',
6478 _(b'name of error log file to write to'),
6476 _(b'name of error log file to write to'),
6479 _(b'FILE'),
6477 _(b'FILE'),
6480 ),
6478 ),
6481 # use string type, then we can check if something was passed
6479 # use string type, then we can check if something was passed
6482 (
6480 (
6483 b'p',
6481 b'p',
6484 b'port',
6482 b'port',
6485 b'',
6483 b'',
6486 _(b'port to listen on (default: 8000)'),
6484 _(b'port to listen on (default: 8000)'),
6487 _(b'PORT'),
6485 _(b'PORT'),
6488 ),
6486 ),
6489 (
6487 (
6490 b'a',
6488 b'a',
6491 b'address',
6489 b'address',
6492 b'',
6490 b'',
6493 _(b'address to listen on (default: all interfaces)'),
6491 _(b'address to listen on (default: all interfaces)'),
6494 _(b'ADDR'),
6492 _(b'ADDR'),
6495 ),
6493 ),
6496 (
6494 (
6497 b'',
6495 b'',
6498 b'prefix',
6496 b'prefix',
6499 b'',
6497 b'',
6500 _(b'prefix path to serve from (default: server root)'),
6498 _(b'prefix path to serve from (default: server root)'),
6501 _(b'PREFIX'),
6499 _(b'PREFIX'),
6502 ),
6500 ),
6503 (
6501 (
6504 b'n',
6502 b'n',
6505 b'name',
6503 b'name',
6506 b'',
6504 b'',
6507 _(b'name to show in web pages (default: working directory)'),
6505 _(b'name to show in web pages (default: working directory)'),
6508 _(b'NAME'),
6506 _(b'NAME'),
6509 ),
6507 ),
6510 (
6508 (
6511 b'',
6509 b'',
6512 b'web-conf',
6510 b'web-conf',
6513 b'',
6511 b'',
6514 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6512 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6515 _(b'FILE'),
6513 _(b'FILE'),
6516 ),
6514 ),
6517 (
6515 (
6518 b'',
6516 b'',
6519 b'webdir-conf',
6517 b'webdir-conf',
6520 b'',
6518 b'',
6521 _(b'name of the hgweb config file (DEPRECATED)'),
6519 _(b'name of the hgweb config file (DEPRECATED)'),
6522 _(b'FILE'),
6520 _(b'FILE'),
6523 ),
6521 ),
6524 (
6522 (
6525 b'',
6523 b'',
6526 b'pid-file',
6524 b'pid-file',
6527 b'',
6525 b'',
6528 _(b'name of file to write process ID to'),
6526 _(b'name of file to write process ID to'),
6529 _(b'FILE'),
6527 _(b'FILE'),
6530 ),
6528 ),
6531 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6529 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6532 (
6530 (
6533 b'',
6531 b'',
6534 b'cmdserver',
6532 b'cmdserver',
6535 b'',
6533 b'',
6536 _(b'for remote clients (ADVANCED)'),
6534 _(b'for remote clients (ADVANCED)'),
6537 _(b'MODE'),
6535 _(b'MODE'),
6538 ),
6536 ),
6539 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6537 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6540 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6538 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6541 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6539 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6542 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6540 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6543 (b'', b'print-url', None, _(b'start and print only the URL')),
6541 (b'', b'print-url', None, _(b'start and print only the URL')),
6544 ]
6542 ]
6545 + subrepoopts,
6543 + subrepoopts,
6546 _(b'[OPTION]...'),
6544 _(b'[OPTION]...'),
6547 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6545 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6548 helpbasic=True,
6546 helpbasic=True,
6549 optionalrepo=True,
6547 optionalrepo=True,
6550 )
6548 )
6551 def serve(ui, repo, **opts):
6549 def serve(ui, repo, **opts):
6552 """start stand-alone webserver
6550 """start stand-alone webserver
6553
6551
6554 Start a local HTTP repository browser and pull server. You can use
6552 Start a local HTTP repository browser and pull server. You can use
6555 this for ad-hoc sharing and browsing of repositories. It is
6553 this for ad-hoc sharing and browsing of repositories. It is
6556 recommended to use a real web server to serve a repository for
6554 recommended to use a real web server to serve a repository for
6557 longer periods of time.
6555 longer periods of time.
6558
6556
6559 Please note that the server does not implement access control.
6557 Please note that the server does not implement access control.
6560 This means that, by default, anybody can read from the server and
6558 This means that, by default, anybody can read from the server and
6561 nobody can write to it by default. Set the ``web.allow-push``
6559 nobody can write to it by default. Set the ``web.allow-push``
6562 option to ``*`` to allow everybody to push to the server. You
6560 option to ``*`` to allow everybody to push to the server. You
6563 should use a real web server if you need to authenticate users.
6561 should use a real web server if you need to authenticate users.
6564
6562
6565 By default, the server logs accesses to stdout and errors to
6563 By default, the server logs accesses to stdout and errors to
6566 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6564 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6567 files.
6565 files.
6568
6566
6569 To have the server choose a free port number to listen on, specify
6567 To have the server choose a free port number to listen on, specify
6570 a port number of 0; in this case, the server will print the port
6568 a port number of 0; in this case, the server will print the port
6571 number it uses.
6569 number it uses.
6572
6570
6573 Returns 0 on success.
6571 Returns 0 on success.
6574 """
6572 """
6575
6573
6576 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6574 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6577 opts = pycompat.byteskwargs(opts)
6575 opts = pycompat.byteskwargs(opts)
6578 if opts[b"print_url"] and ui.verbose:
6576 if opts[b"print_url"] and ui.verbose:
6579 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6577 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6580
6578
6581 if opts[b"stdio"]:
6579 if opts[b"stdio"]:
6582 if repo is None:
6580 if repo is None:
6583 raise error.RepoError(
6581 raise error.RepoError(
6584 _(b"there is no Mercurial repository here (.hg not found)")
6582 _(b"there is no Mercurial repository here (.hg not found)")
6585 )
6583 )
6586 s = wireprotoserver.sshserver(ui, repo)
6584 s = wireprotoserver.sshserver(ui, repo)
6587 s.serve_forever()
6585 s.serve_forever()
6588 return
6586 return
6589
6587
6590 service = server.createservice(ui, repo, opts)
6588 service = server.createservice(ui, repo, opts)
6591 return server.runservice(opts, initfn=service.init, runfn=service.run)
6589 return server.runservice(opts, initfn=service.init, runfn=service.run)
6592
6590
6593
6591
6594 @command(
6592 @command(
6595 b'shelve',
6593 b'shelve',
6596 [
6594 [
6597 (
6595 (
6598 b'A',
6596 b'A',
6599 b'addremove',
6597 b'addremove',
6600 None,
6598 None,
6601 _(b'mark new/missing files as added/removed before shelving'),
6599 _(b'mark new/missing files as added/removed before shelving'),
6602 ),
6600 ),
6603 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6601 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6604 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6602 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6605 (
6603 (
6606 b'',
6604 b'',
6607 b'date',
6605 b'date',
6608 b'',
6606 b'',
6609 _(b'shelve with the specified commit date'),
6607 _(b'shelve with the specified commit date'),
6610 _(b'DATE'),
6608 _(b'DATE'),
6611 ),
6609 ),
6612 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6610 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6613 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6611 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6614 (
6612 (
6615 b'k',
6613 b'k',
6616 b'keep',
6614 b'keep',
6617 False,
6615 False,
6618 _(b'shelve, but keep changes in the working directory'),
6616 _(b'shelve, but keep changes in the working directory'),
6619 ),
6617 ),
6620 (b'l', b'list', None, _(b'list current shelves')),
6618 (b'l', b'list', None, _(b'list current shelves')),
6621 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6619 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6622 (
6620 (
6623 b'n',
6621 b'n',
6624 b'name',
6622 b'name',
6625 b'',
6623 b'',
6626 _(b'use the given name for the shelved commit'),
6624 _(b'use the given name for the shelved commit'),
6627 _(b'NAME'),
6625 _(b'NAME'),
6628 ),
6626 ),
6629 (
6627 (
6630 b'p',
6628 b'p',
6631 b'patch',
6629 b'patch',
6632 None,
6630 None,
6633 _(
6631 _(
6634 b'output patches for changes (provide the names of the shelved '
6632 b'output patches for changes (provide the names of the shelved '
6635 b'changes as positional arguments)'
6633 b'changes as positional arguments)'
6636 ),
6634 ),
6637 ),
6635 ),
6638 (b'i', b'interactive', None, _(b'interactive mode')),
6636 (b'i', b'interactive', None, _(b'interactive mode')),
6639 (
6637 (
6640 b'',
6638 b'',
6641 b'stat',
6639 b'stat',
6642 None,
6640 None,
6643 _(
6641 _(
6644 b'output diffstat-style summary of changes (provide the names of '
6642 b'output diffstat-style summary of changes (provide the names of '
6645 b'the shelved changes as positional arguments)'
6643 b'the shelved changes as positional arguments)'
6646 ),
6644 ),
6647 ),
6645 ),
6648 ]
6646 ]
6649 + cmdutil.walkopts,
6647 + cmdutil.walkopts,
6650 _(b'hg shelve [OPTION]... [FILE]...'),
6648 _(b'hg shelve [OPTION]... [FILE]...'),
6651 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6649 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6652 )
6650 )
6653 def shelve(ui, repo, *pats, **opts):
6651 def shelve(ui, repo, *pats, **opts):
6654 """save and set aside changes from the working directory
6652 """save and set aside changes from the working directory
6655
6653
6656 Shelving takes files that "hg status" reports as not clean, saves
6654 Shelving takes files that "hg status" reports as not clean, saves
6657 the modifications to a bundle (a shelved change), and reverts the
6655 the modifications to a bundle (a shelved change), and reverts the
6658 files so that their state in the working directory becomes clean.
6656 files so that their state in the working directory becomes clean.
6659
6657
6660 To restore these changes to the working directory, using "hg
6658 To restore these changes to the working directory, using "hg
6661 unshelve"; this will work even if you switch to a different
6659 unshelve"; this will work even if you switch to a different
6662 commit.
6660 commit.
6663
6661
6664 When no files are specified, "hg shelve" saves all not-clean
6662 When no files are specified, "hg shelve" saves all not-clean
6665 files. If specific files or directories are named, only changes to
6663 files. If specific files or directories are named, only changes to
6666 those files are shelved.
6664 those files are shelved.
6667
6665
6668 In bare shelve (when no files are specified, without interactive,
6666 In bare shelve (when no files are specified, without interactive,
6669 include and exclude option), shelving remembers information if the
6667 include and exclude option), shelving remembers information if the
6670 working directory was on newly created branch, in other words working
6668 working directory was on newly created branch, in other words working
6671 directory was on different branch than its first parent. In this
6669 directory was on different branch than its first parent. In this
6672 situation unshelving restores branch information to the working directory.
6670 situation unshelving restores branch information to the working directory.
6673
6671
6674 Each shelved change has a name that makes it easier to find later.
6672 Each shelved change has a name that makes it easier to find later.
6675 The name of a shelved change defaults to being based on the active
6673 The name of a shelved change defaults to being based on the active
6676 bookmark, or if there is no active bookmark, the current named
6674 bookmark, or if there is no active bookmark, the current named
6677 branch. To specify a different name, use ``--name``.
6675 branch. To specify a different name, use ``--name``.
6678
6676
6679 To see a list of existing shelved changes, use the ``--list``
6677 To see a list of existing shelved changes, use the ``--list``
6680 option. For each shelved change, this will print its name, age,
6678 option. For each shelved change, this will print its name, age,
6681 and description; use ``--patch`` or ``--stat`` for more details.
6679 and description; use ``--patch`` or ``--stat`` for more details.
6682
6680
6683 To delete specific shelved changes, use ``--delete``. To delete
6681 To delete specific shelved changes, use ``--delete``. To delete
6684 all shelved changes, use ``--cleanup``.
6682 all shelved changes, use ``--cleanup``.
6685 """
6683 """
6686 opts = pycompat.byteskwargs(opts)
6684 opts = pycompat.byteskwargs(opts)
6687 allowables = [
6685 allowables = [
6688 (b'addremove', {b'create'}), # 'create' is pseudo action
6686 (b'addremove', {b'create'}), # 'create' is pseudo action
6689 (b'unknown', {b'create'}),
6687 (b'unknown', {b'create'}),
6690 (b'cleanup', {b'cleanup'}),
6688 (b'cleanup', {b'cleanup'}),
6691 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6689 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6692 (b'delete', {b'delete'}),
6690 (b'delete', {b'delete'}),
6693 (b'edit', {b'create'}),
6691 (b'edit', {b'create'}),
6694 (b'keep', {b'create'}),
6692 (b'keep', {b'create'}),
6695 (b'list', {b'list'}),
6693 (b'list', {b'list'}),
6696 (b'message', {b'create'}),
6694 (b'message', {b'create'}),
6697 (b'name', {b'create'}),
6695 (b'name', {b'create'}),
6698 (b'patch', {b'patch', b'list'}),
6696 (b'patch', {b'patch', b'list'}),
6699 (b'stat', {b'stat', b'list'}),
6697 (b'stat', {b'stat', b'list'}),
6700 ]
6698 ]
6701
6699
6702 def checkopt(opt):
6700 def checkopt(opt):
6703 if opts.get(opt):
6701 if opts.get(opt):
6704 for i, allowable in allowables:
6702 for i, allowable in allowables:
6705 if opts[i] and opt not in allowable:
6703 if opts[i] and opt not in allowable:
6706 raise error.InputError(
6704 raise error.InputError(
6707 _(
6705 _(
6708 b"options '--%s' and '--%s' may not be "
6706 b"options '--%s' and '--%s' may not be "
6709 b"used together"
6707 b"used together"
6710 )
6708 )
6711 % (opt, i)
6709 % (opt, i)
6712 )
6710 )
6713 return True
6711 return True
6714
6712
6715 if checkopt(b'cleanup'):
6713 if checkopt(b'cleanup'):
6716 if pats:
6714 if pats:
6717 raise error.InputError(
6715 raise error.InputError(
6718 _(b"cannot specify names when using '--cleanup'")
6716 _(b"cannot specify names when using '--cleanup'")
6719 )
6717 )
6720 return shelvemod.cleanupcmd(ui, repo)
6718 return shelvemod.cleanupcmd(ui, repo)
6721 elif checkopt(b'delete'):
6719 elif checkopt(b'delete'):
6722 return shelvemod.deletecmd(ui, repo, pats)
6720 return shelvemod.deletecmd(ui, repo, pats)
6723 elif checkopt(b'list'):
6721 elif checkopt(b'list'):
6724 return shelvemod.listcmd(ui, repo, pats, opts)
6722 return shelvemod.listcmd(ui, repo, pats, opts)
6725 elif checkopt(b'patch') or checkopt(b'stat'):
6723 elif checkopt(b'patch') or checkopt(b'stat'):
6726 return shelvemod.patchcmds(ui, repo, pats, opts)
6724 return shelvemod.patchcmds(ui, repo, pats, opts)
6727 else:
6725 else:
6728 return shelvemod.createcmd(ui, repo, pats, opts)
6726 return shelvemod.createcmd(ui, repo, pats, opts)
6729
6727
6730
6728
6731 _NOTTERSE = b'nothing'
6729 _NOTTERSE = b'nothing'
6732
6730
6733
6731
6734 @command(
6732 @command(
6735 b'status|st',
6733 b'status|st',
6736 [
6734 [
6737 (b'A', b'all', None, _(b'show status of all files')),
6735 (b'A', b'all', None, _(b'show status of all files')),
6738 (b'm', b'modified', None, _(b'show only modified files')),
6736 (b'm', b'modified', None, _(b'show only modified files')),
6739 (b'a', b'added', None, _(b'show only added files')),
6737 (b'a', b'added', None, _(b'show only added files')),
6740 (b'r', b'removed', None, _(b'show only removed files')),
6738 (b'r', b'removed', None, _(b'show only removed files')),
6741 (b'd', b'deleted', None, _(b'show only missing files')),
6739 (b'd', b'deleted', None, _(b'show only missing files')),
6742 (b'c', b'clean', None, _(b'show only files without changes')),
6740 (b'c', b'clean', None, _(b'show only files without changes')),
6743 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6741 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6744 (b'i', b'ignored', None, _(b'show only ignored files')),
6742 (b'i', b'ignored', None, _(b'show only ignored files')),
6745 (b'n', b'no-status', None, _(b'hide status prefix')),
6743 (b'n', b'no-status', None, _(b'hide status prefix')),
6746 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6744 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6747 (
6745 (
6748 b'C',
6746 b'C',
6749 b'copies',
6747 b'copies',
6750 None,
6748 None,
6751 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6749 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6752 ),
6750 ),
6753 (
6751 (
6754 b'0',
6752 b'0',
6755 b'print0',
6753 b'print0',
6756 None,
6754 None,
6757 _(b'end filenames with NUL, for use with xargs'),
6755 _(b'end filenames with NUL, for use with xargs'),
6758 ),
6756 ),
6759 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6757 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6760 (
6758 (
6761 b'',
6759 b'',
6762 b'change',
6760 b'change',
6763 b'',
6761 b'',
6764 _(b'list the changed files of a revision'),
6762 _(b'list the changed files of a revision'),
6765 _(b'REV'),
6763 _(b'REV'),
6766 ),
6764 ),
6767 ]
6765 ]
6768 + walkopts
6766 + walkopts
6769 + subrepoopts
6767 + subrepoopts
6770 + formatteropts,
6768 + formatteropts,
6771 _(b'[OPTION]... [FILE]...'),
6769 _(b'[OPTION]... [FILE]...'),
6772 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6770 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6773 helpbasic=True,
6771 helpbasic=True,
6774 inferrepo=True,
6772 inferrepo=True,
6775 intents={INTENT_READONLY},
6773 intents={INTENT_READONLY},
6776 )
6774 )
6777 def status(ui, repo, *pats, **opts):
6775 def status(ui, repo, *pats, **opts):
6778 """show changed files in the working directory
6776 """show changed files in the working directory
6779
6777
6780 Show status of files in the repository. If names are given, only
6778 Show status of files in the repository. If names are given, only
6781 files that match are shown. Files that are clean or ignored or
6779 files that match are shown. Files that are clean or ignored or
6782 the source of a copy/move operation, are not listed unless
6780 the source of a copy/move operation, are not listed unless
6783 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6781 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6784 Unless options described with "show only ..." are given, the
6782 Unless options described with "show only ..." are given, the
6785 options -mardu are used.
6783 options -mardu are used.
6786
6784
6787 Option -q/--quiet hides untracked (unknown and ignored) files
6785 Option -q/--quiet hides untracked (unknown and ignored) files
6788 unless explicitly requested with -u/--unknown or -i/--ignored.
6786 unless explicitly requested with -u/--unknown or -i/--ignored.
6789
6787
6790 .. note::
6788 .. note::
6791
6789
6792 :hg:`status` may appear to disagree with diff if permissions have
6790 :hg:`status` may appear to disagree with diff if permissions have
6793 changed or a merge has occurred. The standard diff format does
6791 changed or a merge has occurred. The standard diff format does
6794 not report permission changes and diff only reports changes
6792 not report permission changes and diff only reports changes
6795 relative to one merge parent.
6793 relative to one merge parent.
6796
6794
6797 If one revision is given, it is used as the base revision.
6795 If one revision is given, it is used as the base revision.
6798 If two revisions are given, the differences between them are
6796 If two revisions are given, the differences between them are
6799 shown. The --change option can also be used as a shortcut to list
6797 shown. The --change option can also be used as a shortcut to list
6800 the changed files of a revision from its first parent.
6798 the changed files of a revision from its first parent.
6801
6799
6802 The codes used to show the status of files are::
6800 The codes used to show the status of files are::
6803
6801
6804 M = modified
6802 M = modified
6805 A = added
6803 A = added
6806 R = removed
6804 R = removed
6807 C = clean
6805 C = clean
6808 ! = missing (deleted by non-hg command, but still tracked)
6806 ! = missing (deleted by non-hg command, but still tracked)
6809 ? = not tracked
6807 ? = not tracked
6810 I = ignored
6808 I = ignored
6811 = origin of the previous file (with --copies)
6809 = origin of the previous file (with --copies)
6812
6810
6813 .. container:: verbose
6811 .. container:: verbose
6814
6812
6815 The -t/--terse option abbreviates the output by showing only the directory
6813 The -t/--terse option abbreviates the output by showing only the directory
6816 name if all the files in it share the same status. The option takes an
6814 name if all the files in it share the same status. The option takes an
6817 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6815 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6818 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6816 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6819 for 'ignored' and 'c' for clean.
6817 for 'ignored' and 'c' for clean.
6820
6818
6821 It abbreviates only those statuses which are passed. Note that clean and
6819 It abbreviates only those statuses which are passed. Note that clean and
6822 ignored files are not displayed with '--terse ic' unless the -c/--clean
6820 ignored files are not displayed with '--terse ic' unless the -c/--clean
6823 and -i/--ignored options are also used.
6821 and -i/--ignored options are also used.
6824
6822
6825 The -v/--verbose option shows information when the repository is in an
6823 The -v/--verbose option shows information when the repository is in an
6826 unfinished merge, shelve, rebase state etc. You can have this behavior
6824 unfinished merge, shelve, rebase state etc. You can have this behavior
6827 turned on by default by enabling the ``commands.status.verbose`` option.
6825 turned on by default by enabling the ``commands.status.verbose`` option.
6828
6826
6829 You can skip displaying some of these states by setting
6827 You can skip displaying some of these states by setting
6830 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6828 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6831 'histedit', 'merge', 'rebase', or 'unshelve'.
6829 'histedit', 'merge', 'rebase', or 'unshelve'.
6832
6830
6833 Template:
6831 Template:
6834
6832
6835 The following keywords are supported in addition to the common template
6833 The following keywords are supported in addition to the common template
6836 keywords and functions. See also :hg:`help templates`.
6834 keywords and functions. See also :hg:`help templates`.
6837
6835
6838 :path: String. Repository-absolute path of the file.
6836 :path: String. Repository-absolute path of the file.
6839 :source: String. Repository-absolute path of the file originated from.
6837 :source: String. Repository-absolute path of the file originated from.
6840 Available if ``--copies`` is specified.
6838 Available if ``--copies`` is specified.
6841 :status: String. Character denoting file's status.
6839 :status: String. Character denoting file's status.
6842
6840
6843 Examples:
6841 Examples:
6844
6842
6845 - show changes in the working directory relative to a
6843 - show changes in the working directory relative to a
6846 changeset::
6844 changeset::
6847
6845
6848 hg status --rev 9353
6846 hg status --rev 9353
6849
6847
6850 - show changes in the working directory relative to the
6848 - show changes in the working directory relative to the
6851 current directory (see :hg:`help patterns` for more information)::
6849 current directory (see :hg:`help patterns` for more information)::
6852
6850
6853 hg status re:
6851 hg status re:
6854
6852
6855 - show all changes including copies in an existing changeset::
6853 - show all changes including copies in an existing changeset::
6856
6854
6857 hg status --copies --change 9353
6855 hg status --copies --change 9353
6858
6856
6859 - get a NUL separated list of added files, suitable for xargs::
6857 - get a NUL separated list of added files, suitable for xargs::
6860
6858
6861 hg status -an0
6859 hg status -an0
6862
6860
6863 - show more information about the repository status, abbreviating
6861 - show more information about the repository status, abbreviating
6864 added, removed, modified, deleted, and untracked paths::
6862 added, removed, modified, deleted, and untracked paths::
6865
6863
6866 hg status -v -t mardu
6864 hg status -v -t mardu
6867
6865
6868 Returns 0 on success.
6866 Returns 0 on success.
6869
6867
6870 """
6868 """
6871
6869
6872 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6870 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6873 opts = pycompat.byteskwargs(opts)
6871 opts = pycompat.byteskwargs(opts)
6874 revs = opts.get(b'rev', [])
6872 revs = opts.get(b'rev', [])
6875 change = opts.get(b'change', b'')
6873 change = opts.get(b'change', b'')
6876 terse = opts.get(b'terse', _NOTTERSE)
6874 terse = opts.get(b'terse', _NOTTERSE)
6877 if terse is _NOTTERSE:
6875 if terse is _NOTTERSE:
6878 if revs:
6876 if revs:
6879 terse = b''
6877 terse = b''
6880 else:
6878 else:
6881 terse = ui.config(b'commands', b'status.terse')
6879 terse = ui.config(b'commands', b'status.terse')
6882
6880
6883 if revs and terse:
6881 if revs and terse:
6884 msg = _(b'cannot use --terse with --rev')
6882 msg = _(b'cannot use --terse with --rev')
6885 raise error.InputError(msg)
6883 raise error.InputError(msg)
6886 elif change:
6884 elif change:
6887 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6885 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6888 ctx2 = logcmdutil.revsingle(repo, change, None)
6886 ctx2 = logcmdutil.revsingle(repo, change, None)
6889 ctx1 = ctx2.p1()
6887 ctx1 = ctx2.p1()
6890 else:
6888 else:
6891 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6889 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6892 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
6890 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
6893
6891
6894 forcerelativevalue = None
6892 forcerelativevalue = None
6895 if ui.hasconfig(b'commands', b'status.relative'):
6893 if ui.hasconfig(b'commands', b'status.relative'):
6896 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6894 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6897 uipathfn = scmutil.getuipathfn(
6895 uipathfn = scmutil.getuipathfn(
6898 repo,
6896 repo,
6899 legacyrelativevalue=bool(pats),
6897 legacyrelativevalue=bool(pats),
6900 forcerelativevalue=forcerelativevalue,
6898 forcerelativevalue=forcerelativevalue,
6901 )
6899 )
6902
6900
6903 if opts.get(b'print0'):
6901 if opts.get(b'print0'):
6904 end = b'\0'
6902 end = b'\0'
6905 else:
6903 else:
6906 end = b'\n'
6904 end = b'\n'
6907 states = b'modified added removed deleted unknown ignored clean'.split()
6905 states = b'modified added removed deleted unknown ignored clean'.split()
6908 show = [k for k in states if opts.get(k)]
6906 show = [k for k in states if opts.get(k)]
6909 if opts.get(b'all'):
6907 if opts.get(b'all'):
6910 show += ui.quiet and (states[:4] + [b'clean']) or states
6908 show += ui.quiet and (states[:4] + [b'clean']) or states
6911
6909
6912 if not show:
6910 if not show:
6913 if ui.quiet:
6911 if ui.quiet:
6914 show = states[:4]
6912 show = states[:4]
6915 else:
6913 else:
6916 show = states[:5]
6914 show = states[:5]
6917
6915
6918 m = scmutil.match(ctx2, pats, opts)
6916 m = scmutil.match(ctx2, pats, opts)
6919 if terse:
6917 if terse:
6920 # we need to compute clean and unknown to terse
6918 # we need to compute clean and unknown to terse
6921 stat = repo.status(
6919 stat = repo.status(
6922 ctx1.node(),
6920 ctx1.node(),
6923 ctx2.node(),
6921 ctx2.node(),
6924 m,
6922 m,
6925 b'ignored' in show or b'i' in terse,
6923 b'ignored' in show or b'i' in terse,
6926 clean=True,
6924 clean=True,
6927 unknown=True,
6925 unknown=True,
6928 listsubrepos=opts.get(b'subrepos'),
6926 listsubrepos=opts.get(b'subrepos'),
6929 )
6927 )
6930
6928
6931 stat = cmdutil.tersedir(stat, terse)
6929 stat = cmdutil.tersedir(stat, terse)
6932 else:
6930 else:
6933 stat = repo.status(
6931 stat = repo.status(
6934 ctx1.node(),
6932 ctx1.node(),
6935 ctx2.node(),
6933 ctx2.node(),
6936 m,
6934 m,
6937 b'ignored' in show,
6935 b'ignored' in show,
6938 b'clean' in show,
6936 b'clean' in show,
6939 b'unknown' in show,
6937 b'unknown' in show,
6940 opts.get(b'subrepos'),
6938 opts.get(b'subrepos'),
6941 )
6939 )
6942
6940
6943 changestates = zip(
6941 changestates = zip(
6944 states,
6942 states,
6945 pycompat.iterbytestr(b'MAR!?IC'),
6943 pycompat.iterbytestr(b'MAR!?IC'),
6946 [getattr(stat, s.decode('utf8')) for s in states],
6944 [getattr(stat, s.decode('utf8')) for s in states],
6947 )
6945 )
6948
6946
6949 copy = {}
6947 copy = {}
6950 if (
6948 if (
6951 opts.get(b'all')
6949 opts.get(b'all')
6952 or opts.get(b'copies')
6950 or opts.get(b'copies')
6953 or ui.configbool(b'ui', b'statuscopies')
6951 or ui.configbool(b'ui', b'statuscopies')
6954 ) and not opts.get(b'no_status'):
6952 ) and not opts.get(b'no_status'):
6955 copy = copies.pathcopies(ctx1, ctx2, m)
6953 copy = copies.pathcopies(ctx1, ctx2, m)
6956
6954
6957 morestatus = None
6955 morestatus = None
6958 if (
6956 if (
6959 (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
6957 (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
6960 and not ui.plain()
6958 and not ui.plain()
6961 and not opts.get(b'print0')
6959 and not opts.get(b'print0')
6962 ):
6960 ):
6963 morestatus = cmdutil.readmorestatus(repo)
6961 morestatus = cmdutil.readmorestatus(repo)
6964
6962
6965 ui.pager(b'status')
6963 ui.pager(b'status')
6966 fm = ui.formatter(b'status', opts)
6964 fm = ui.formatter(b'status', opts)
6967 fmt = b'%s' + end
6965 fmt = b'%s' + end
6968 showchar = not opts.get(b'no_status')
6966 showchar = not opts.get(b'no_status')
6969
6967
6970 for state, char, files in changestates:
6968 for state, char, files in changestates:
6971 if state in show:
6969 if state in show:
6972 label = b'status.' + state
6970 label = b'status.' + state
6973 for f in files:
6971 for f in files:
6974 fm.startitem()
6972 fm.startitem()
6975 fm.context(ctx=ctx2)
6973 fm.context(ctx=ctx2)
6976 fm.data(itemtype=b'file', path=f)
6974 fm.data(itemtype=b'file', path=f)
6977 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6975 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6978 fm.plain(fmt % uipathfn(f), label=label)
6976 fm.plain(fmt % uipathfn(f), label=label)
6979 if f in copy:
6977 if f in copy:
6980 fm.data(source=copy[f])
6978 fm.data(source=copy[f])
6981 fm.plain(
6979 fm.plain(
6982 (b' %s' + end) % uipathfn(copy[f]),
6980 (b' %s' + end) % uipathfn(copy[f]),
6983 label=b'status.copied',
6981 label=b'status.copied',
6984 )
6982 )
6985 if morestatus:
6983 if morestatus:
6986 morestatus.formatfile(f, fm)
6984 morestatus.formatfile(f, fm)
6987
6985
6988 if morestatus:
6986 if morestatus:
6989 morestatus.formatfooter(fm)
6987 morestatus.formatfooter(fm)
6990 fm.end()
6988 fm.end()
6991
6989
6992
6990
6993 @command(
6991 @command(
6994 b'summary|sum',
6992 b'summary|sum',
6995 [(b'', b'remote', None, _(b'check for push and pull'))],
6993 [(b'', b'remote', None, _(b'check for push and pull'))],
6996 b'[--remote]',
6994 b'[--remote]',
6997 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6995 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6998 helpbasic=True,
6996 helpbasic=True,
6999 intents={INTENT_READONLY},
6997 intents={INTENT_READONLY},
7000 )
6998 )
7001 def summary(ui, repo, **opts):
6999 def summary(ui, repo, **opts):
7002 """summarize working directory state
7000 """summarize working directory state
7003
7001
7004 This generates a brief summary of the working directory state,
7002 This generates a brief summary of the working directory state,
7005 including parents, branch, commit status, phase and available updates.
7003 including parents, branch, commit status, phase and available updates.
7006
7004
7007 With the --remote option, this will check the default paths for
7005 With the --remote option, this will check the default paths for
7008 incoming and outgoing changes. This can be time-consuming.
7006 incoming and outgoing changes. This can be time-consuming.
7009
7007
7010 Returns 0 on success.
7008 Returns 0 on success.
7011 """
7009 """
7012
7010
7013 opts = pycompat.byteskwargs(opts)
7011 opts = pycompat.byteskwargs(opts)
7014 ui.pager(b'summary')
7012 ui.pager(b'summary')
7015 ctx = repo[None]
7013 ctx = repo[None]
7016 parents = ctx.parents()
7014 parents = ctx.parents()
7017 pnode = parents[0].node()
7015 pnode = parents[0].node()
7018 marks = []
7016 marks = []
7019
7017
7020 try:
7018 try:
7021 ms = mergestatemod.mergestate.read(repo)
7019 ms = mergestatemod.mergestate.read(repo)
7022 except error.UnsupportedMergeRecords as e:
7020 except error.UnsupportedMergeRecords as e:
7023 s = b' '.join(e.recordtypes)
7021 s = b' '.join(e.recordtypes)
7024 ui.warn(
7022 ui.warn(
7025 _(b'warning: merge state has unsupported record types: %s\n') % s
7023 _(b'warning: merge state has unsupported record types: %s\n') % s
7026 )
7024 )
7027 unresolved = []
7025 unresolved = []
7028 else:
7026 else:
7029 unresolved = list(ms.unresolved())
7027 unresolved = list(ms.unresolved())
7030
7028
7031 for p in parents:
7029 for p in parents:
7032 # label with log.changeset (instead of log.parent) since this
7030 # label with log.changeset (instead of log.parent) since this
7033 # shows a working directory parent *changeset*:
7031 # shows a working directory parent *changeset*:
7034 # i18n: column positioning for "hg summary"
7032 # i18n: column positioning for "hg summary"
7035 ui.write(
7033 ui.write(
7036 _(b'parent: %d:%s ') % (p.rev(), p),
7034 _(b'parent: %d:%s ') % (p.rev(), p),
7037 label=logcmdutil.changesetlabels(p),
7035 label=logcmdutil.changesetlabels(p),
7038 )
7036 )
7039 ui.write(b' '.join(p.tags()), label=b'log.tag')
7037 ui.write(b' '.join(p.tags()), label=b'log.tag')
7040 if p.bookmarks():
7038 if p.bookmarks():
7041 marks.extend(p.bookmarks())
7039 marks.extend(p.bookmarks())
7042 if p.rev() == -1:
7040 if p.rev() == -1:
7043 if not len(repo):
7041 if not len(repo):
7044 ui.write(_(b' (empty repository)'))
7042 ui.write(_(b' (empty repository)'))
7045 else:
7043 else:
7046 ui.write(_(b' (no revision checked out)'))
7044 ui.write(_(b' (no revision checked out)'))
7047 if p.obsolete():
7045 if p.obsolete():
7048 ui.write(_(b' (obsolete)'))
7046 ui.write(_(b' (obsolete)'))
7049 if p.isunstable():
7047 if p.isunstable():
7050 instabilities = (
7048 instabilities = (
7051 ui.label(instability, b'trouble.%s' % instability)
7049 ui.label(instability, b'trouble.%s' % instability)
7052 for instability in p.instabilities()
7050 for instability in p.instabilities()
7053 )
7051 )
7054 ui.write(b' (' + b', '.join(instabilities) + b')')
7052 ui.write(b' (' + b', '.join(instabilities) + b')')
7055 ui.write(b'\n')
7053 ui.write(b'\n')
7056 if p.description():
7054 if p.description():
7057 ui.status(
7055 ui.status(
7058 b' ' + p.description().splitlines()[0].strip() + b'\n',
7056 b' ' + p.description().splitlines()[0].strip() + b'\n',
7059 label=b'log.summary',
7057 label=b'log.summary',
7060 )
7058 )
7061
7059
7062 branch = ctx.branch()
7060 branch = ctx.branch()
7063 bheads = repo.branchheads(branch)
7061 bheads = repo.branchheads(branch)
7064 # i18n: column positioning for "hg summary"
7062 # i18n: column positioning for "hg summary"
7065 m = _(b'branch: %s\n') % branch
7063 m = _(b'branch: %s\n') % branch
7066 if branch != b'default':
7064 if branch != b'default':
7067 ui.write(m, label=b'log.branch')
7065 ui.write(m, label=b'log.branch')
7068 else:
7066 else:
7069 ui.status(m, label=b'log.branch')
7067 ui.status(m, label=b'log.branch')
7070
7068
7071 if marks:
7069 if marks:
7072 active = repo._activebookmark
7070 active = repo._activebookmark
7073 # i18n: column positioning for "hg summary"
7071 # i18n: column positioning for "hg summary"
7074 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
7072 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
7075 if active is not None:
7073 if active is not None:
7076 if active in marks:
7074 if active in marks:
7077 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
7075 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
7078 marks.remove(active)
7076 marks.remove(active)
7079 else:
7077 else:
7080 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
7078 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
7081 for m in marks:
7079 for m in marks:
7082 ui.write(b' ' + m, label=b'log.bookmark')
7080 ui.write(b' ' + m, label=b'log.bookmark')
7083 ui.write(b'\n', label=b'log.bookmark')
7081 ui.write(b'\n', label=b'log.bookmark')
7084
7082
7085 status = repo.status(unknown=True)
7083 status = repo.status(unknown=True)
7086
7084
7087 c = repo.dirstate.copies()
7085 c = repo.dirstate.copies()
7088 copied, renamed = [], []
7086 copied, renamed = [], []
7089 for d, s in c.items():
7087 for d, s in c.items():
7090 if s in status.removed:
7088 if s in status.removed:
7091 status.removed.remove(s)
7089 status.removed.remove(s)
7092 renamed.append(d)
7090 renamed.append(d)
7093 else:
7091 else:
7094 copied.append(d)
7092 copied.append(d)
7095 if d in status.added:
7093 if d in status.added:
7096 status.added.remove(d)
7094 status.added.remove(d)
7097
7095
7098 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7096 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7099
7097
7100 labels = [
7098 labels = [
7101 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7099 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7102 (ui.label(_(b'%d added'), b'status.added'), status.added),
7100 (ui.label(_(b'%d added'), b'status.added'), status.added),
7103 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7101 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7104 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7102 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7105 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7103 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7106 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7104 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7107 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7105 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7108 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7106 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7109 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7107 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7110 ]
7108 ]
7111 t = []
7109 t = []
7112 for l, s in labels:
7110 for l, s in labels:
7113 if s:
7111 if s:
7114 t.append(l % len(s))
7112 t.append(l % len(s))
7115
7113
7116 t = b', '.join(t)
7114 t = b', '.join(t)
7117 cleanworkdir = False
7115 cleanworkdir = False
7118
7116
7119 if repo.vfs.exists(b'graftstate'):
7117 if repo.vfs.exists(b'graftstate'):
7120 t += _(b' (graft in progress)')
7118 t += _(b' (graft in progress)')
7121 if repo.vfs.exists(b'updatestate'):
7119 if repo.vfs.exists(b'updatestate'):
7122 t += _(b' (interrupted update)')
7120 t += _(b' (interrupted update)')
7123 elif len(parents) > 1:
7121 elif len(parents) > 1:
7124 t += _(b' (merge)')
7122 t += _(b' (merge)')
7125 elif branch != parents[0].branch():
7123 elif branch != parents[0].branch():
7126 t += _(b' (new branch)')
7124 t += _(b' (new branch)')
7127 elif parents[0].closesbranch() and pnode in repo.branchheads(
7125 elif parents[0].closesbranch() and pnode in repo.branchheads(
7128 branch, closed=True
7126 branch, closed=True
7129 ):
7127 ):
7130 t += _(b' (head closed)')
7128 t += _(b' (head closed)')
7131 elif not (
7129 elif not (
7132 status.modified
7130 status.modified
7133 or status.added
7131 or status.added
7134 or status.removed
7132 or status.removed
7135 or renamed
7133 or renamed
7136 or copied
7134 or copied
7137 or subs
7135 or subs
7138 ):
7136 ):
7139 t += _(b' (clean)')
7137 t += _(b' (clean)')
7140 cleanworkdir = True
7138 cleanworkdir = True
7141 elif pnode not in bheads:
7139 elif pnode not in bheads:
7142 t += _(b' (new branch head)')
7140 t += _(b' (new branch head)')
7143
7141
7144 if parents:
7142 if parents:
7145 pendingphase = max(p.phase() for p in parents)
7143 pendingphase = max(p.phase() for p in parents)
7146 else:
7144 else:
7147 pendingphase = phases.public
7145 pendingphase = phases.public
7148
7146
7149 if pendingphase > phases.newcommitphase(ui):
7147 if pendingphase > phases.newcommitphase(ui):
7150 t += b' (%s)' % phases.phasenames[pendingphase]
7148 t += b' (%s)' % phases.phasenames[pendingphase]
7151
7149
7152 if cleanworkdir:
7150 if cleanworkdir:
7153 # i18n: column positioning for "hg summary"
7151 # i18n: column positioning for "hg summary"
7154 ui.status(_(b'commit: %s\n') % t.strip())
7152 ui.status(_(b'commit: %s\n') % t.strip())
7155 else:
7153 else:
7156 # i18n: column positioning for "hg summary"
7154 # i18n: column positioning for "hg summary"
7157 ui.write(_(b'commit: %s\n') % t.strip())
7155 ui.write(_(b'commit: %s\n') % t.strip())
7158
7156
7159 # all ancestors of branch heads - all ancestors of parent = new csets
7157 # all ancestors of branch heads - all ancestors of parent = new csets
7160 new = len(
7158 new = len(
7161 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7159 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7162 )
7160 )
7163
7161
7164 if new == 0:
7162 if new == 0:
7165 # i18n: column positioning for "hg summary"
7163 # i18n: column positioning for "hg summary"
7166 ui.status(_(b'update: (current)\n'))
7164 ui.status(_(b'update: (current)\n'))
7167 elif pnode not in bheads:
7165 elif pnode not in bheads:
7168 # i18n: column positioning for "hg summary"
7166 # i18n: column positioning for "hg summary"
7169 ui.write(_(b'update: %d new changesets (update)\n') % new)
7167 ui.write(_(b'update: %d new changesets (update)\n') % new)
7170 else:
7168 else:
7171 # i18n: column positioning for "hg summary"
7169 # i18n: column positioning for "hg summary"
7172 ui.write(
7170 ui.write(
7173 _(b'update: %d new changesets, %d branch heads (merge)\n')
7171 _(b'update: %d new changesets, %d branch heads (merge)\n')
7174 % (new, len(bheads))
7172 % (new, len(bheads))
7175 )
7173 )
7176
7174
7177 t = []
7175 t = []
7178 draft = len(repo.revs(b'draft()'))
7176 draft = len(repo.revs(b'draft()'))
7179 if draft:
7177 if draft:
7180 t.append(_(b'%d draft') % draft)
7178 t.append(_(b'%d draft') % draft)
7181 secret = len(repo.revs(b'secret()'))
7179 secret = len(repo.revs(b'secret()'))
7182 if secret:
7180 if secret:
7183 t.append(_(b'%d secret') % secret)
7181 t.append(_(b'%d secret') % secret)
7184
7182
7185 if draft or secret:
7183 if draft or secret:
7186 ui.status(_(b'phases: %s\n') % b', '.join(t))
7184 ui.status(_(b'phases: %s\n') % b', '.join(t))
7187
7185
7188 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7186 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7189 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7187 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7190 numtrouble = len(repo.revs(trouble + b"()"))
7188 numtrouble = len(repo.revs(trouble + b"()"))
7191 # We write all the possibilities to ease translation
7189 # We write all the possibilities to ease translation
7192 troublemsg = {
7190 troublemsg = {
7193 b"orphan": _(b"orphan: %d changesets"),
7191 b"orphan": _(b"orphan: %d changesets"),
7194 b"contentdivergent": _(b"content-divergent: %d changesets"),
7192 b"contentdivergent": _(b"content-divergent: %d changesets"),
7195 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7193 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7196 }
7194 }
7197 if numtrouble > 0:
7195 if numtrouble > 0:
7198 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7196 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7199
7197
7200 cmdutil.summaryhooks(ui, repo)
7198 cmdutil.summaryhooks(ui, repo)
7201
7199
7202 if opts.get(b'remote'):
7200 if opts.get(b'remote'):
7203 needsincoming, needsoutgoing = True, True
7201 needsincoming, needsoutgoing = True, True
7204 else:
7202 else:
7205 needsincoming, needsoutgoing = False, False
7203 needsincoming, needsoutgoing = False, False
7206 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7204 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7207 if i:
7205 if i:
7208 needsincoming = True
7206 needsincoming = True
7209 if o:
7207 if o:
7210 needsoutgoing = True
7208 needsoutgoing = True
7211 if not needsincoming and not needsoutgoing:
7209 if not needsincoming and not needsoutgoing:
7212 return
7210 return
7213
7211
7214 def getincoming():
7212 def getincoming():
7215 # XXX We should actually skip this if no default is specified, instead
7213 # XXX We should actually skip this if no default is specified, instead
7216 # of passing "default" which will resolve as "./default/" if no default
7214 # of passing "default" which will resolve as "./default/" if no default
7217 # path is defined.
7215 # path is defined.
7218 source, branches = urlutil.get_unique_pull_path(
7216 source, branches = urlutil.get_unique_pull_path(
7219 b'summary', repo, ui, b'default'
7217 b'summary', repo, ui, b'default'
7220 )
7218 )
7221 sbranch = branches[0]
7219 sbranch = branches[0]
7222 try:
7220 try:
7223 other = hg.peer(repo, {}, source)
7221 other = hg.peer(repo, {}, source)
7224 except error.RepoError:
7222 except error.RepoError:
7225 if opts.get(b'remote'):
7223 if opts.get(b'remote'):
7226 raise
7224 raise
7227 return source, sbranch, None, None, None
7225 return source, sbranch, None, None, None
7228 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7226 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7229 if revs:
7227 if revs:
7230 revs = [other.lookup(rev) for rev in revs]
7228 revs = [other.lookup(rev) for rev in revs]
7231 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(source))
7229 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(source))
7232 with repo.ui.silent():
7230 with repo.ui.silent():
7233 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7231 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7234 return source, sbranch, other, commoninc, commoninc[1]
7232 return source, sbranch, other, commoninc, commoninc[1]
7235
7233
7236 if needsincoming:
7234 if needsincoming:
7237 source, sbranch, sother, commoninc, incoming = getincoming()
7235 source, sbranch, sother, commoninc, incoming = getincoming()
7238 else:
7236 else:
7239 source = sbranch = sother = commoninc = incoming = None
7237 source = sbranch = sother = commoninc = incoming = None
7240
7238
7241 def getoutgoing():
7239 def getoutgoing():
7242 # XXX We should actually skip this if no default is specified, instead
7240 # XXX We should actually skip this if no default is specified, instead
7243 # of passing "default" which will resolve as "./default/" if no default
7241 # of passing "default" which will resolve as "./default/" if no default
7244 # path is defined.
7242 # path is defined.
7245 d = None
7243 d = None
7246 if b'default-push' in ui.paths:
7244 if b'default-push' in ui.paths:
7247 d = b'default-push'
7245 d = b'default-push'
7248 elif b'default' in ui.paths:
7246 elif b'default' in ui.paths:
7249 d = b'default'
7247 d = b'default'
7250 if d is not None:
7248 if d is not None:
7251 path = urlutil.get_unique_push_path(b'summary', repo, ui, d)
7249 path = urlutil.get_unique_push_path(b'summary', repo, ui, d)
7252 dest = path.pushloc or path.loc
7250 dest = path.pushloc or path.loc
7253 dbranch = path.branch
7251 dbranch = path.branch
7254 else:
7252 else:
7255 dest = b'default'
7253 dest = b'default'
7256 dbranch = None
7254 dbranch = None
7257 revs, checkout = hg.addbranchrevs(repo, repo, (dbranch, []), None)
7255 revs, checkout = hg.addbranchrevs(repo, repo, (dbranch, []), None)
7258 if source != dest:
7256 if source != dest:
7259 try:
7257 try:
7260 dother = hg.peer(repo, {}, dest)
7258 dother = hg.peer(repo, {}, dest)
7261 except error.RepoError:
7259 except error.RepoError:
7262 if opts.get(b'remote'):
7260 if opts.get(b'remote'):
7263 raise
7261 raise
7264 return dest, dbranch, None, None
7262 return dest, dbranch, None, None
7265 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(dest))
7263 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(dest))
7266 elif sother is None:
7264 elif sother is None:
7267 # there is no explicit destination peer, but source one is invalid
7265 # there is no explicit destination peer, but source one is invalid
7268 return dest, dbranch, None, None
7266 return dest, dbranch, None, None
7269 else:
7267 else:
7270 dother = sother
7268 dother = sother
7271 if source != dest or (sbranch is not None and sbranch != dbranch):
7269 if source != dest or (sbranch is not None and sbranch != dbranch):
7272 common = None
7270 common = None
7273 else:
7271 else:
7274 common = commoninc
7272 common = commoninc
7275 if revs:
7273 if revs:
7276 revs = [repo.lookup(rev) for rev in revs]
7274 revs = [repo.lookup(rev) for rev in revs]
7277 with repo.ui.silent():
7275 with repo.ui.silent():
7278 outgoing = discovery.findcommonoutgoing(
7276 outgoing = discovery.findcommonoutgoing(
7279 repo, dother, onlyheads=revs, commoninc=common
7277 repo, dother, onlyheads=revs, commoninc=common
7280 )
7278 )
7281 return dest, dbranch, dother, outgoing
7279 return dest, dbranch, dother, outgoing
7282
7280
7283 if needsoutgoing:
7281 if needsoutgoing:
7284 dest, dbranch, dother, outgoing = getoutgoing()
7282 dest, dbranch, dother, outgoing = getoutgoing()
7285 else:
7283 else:
7286 dest = dbranch = dother = outgoing = None
7284 dest = dbranch = dother = outgoing = None
7287
7285
7288 if opts.get(b'remote'):
7286 if opts.get(b'remote'):
7289 # Help pytype. --remote sets both `needsincoming` and `needsoutgoing`.
7287 # Help pytype. --remote sets both `needsincoming` and `needsoutgoing`.
7290 # The former always sets `sother` (or raises an exception if it can't);
7288 # The former always sets `sother` (or raises an exception if it can't);
7291 # the latter always sets `outgoing`.
7289 # the latter always sets `outgoing`.
7292 assert sother is not None
7290 assert sother is not None
7293 assert outgoing is not None
7291 assert outgoing is not None
7294
7292
7295 t = []
7293 t = []
7296 if incoming:
7294 if incoming:
7297 t.append(_(b'1 or more incoming'))
7295 t.append(_(b'1 or more incoming'))
7298 o = outgoing.missing
7296 o = outgoing.missing
7299 if o:
7297 if o:
7300 t.append(_(b'%d outgoing') % len(o))
7298 t.append(_(b'%d outgoing') % len(o))
7301 other = dother or sother
7299 other = dother or sother
7302 if b'bookmarks' in other.listkeys(b'namespaces'):
7300 if b'bookmarks' in other.listkeys(b'namespaces'):
7303 counts = bookmarks.summary(repo, other)
7301 counts = bookmarks.summary(repo, other)
7304 if counts[0] > 0:
7302 if counts[0] > 0:
7305 t.append(_(b'%d incoming bookmarks') % counts[0])
7303 t.append(_(b'%d incoming bookmarks') % counts[0])
7306 if counts[1] > 0:
7304 if counts[1] > 0:
7307 t.append(_(b'%d outgoing bookmarks') % counts[1])
7305 t.append(_(b'%d outgoing bookmarks') % counts[1])
7308
7306
7309 if t:
7307 if t:
7310 # i18n: column positioning for "hg summary"
7308 # i18n: column positioning for "hg summary"
7311 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7309 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7312 else:
7310 else:
7313 # i18n: column positioning for "hg summary"
7311 # i18n: column positioning for "hg summary"
7314 ui.status(_(b'remote: (synced)\n'))
7312 ui.status(_(b'remote: (synced)\n'))
7315
7313
7316 cmdutil.summaryremotehooks(
7314 cmdutil.summaryremotehooks(
7317 ui,
7315 ui,
7318 repo,
7316 repo,
7319 opts,
7317 opts,
7320 (
7318 (
7321 (source, sbranch, sother, commoninc),
7319 (source, sbranch, sother, commoninc),
7322 (dest, dbranch, dother, outgoing),
7320 (dest, dbranch, dother, outgoing),
7323 ),
7321 ),
7324 )
7322 )
7325
7323
7326
7324
7327 @command(
7325 @command(
7328 b'tag',
7326 b'tag',
7329 [
7327 [
7330 (b'f', b'force', None, _(b'force tag')),
7328 (b'f', b'force', None, _(b'force tag')),
7331 (b'l', b'local', None, _(b'make the tag local')),
7329 (b'l', b'local', None, _(b'make the tag local')),
7332 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7330 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7333 (b'', b'remove', None, _(b'remove a tag')),
7331 (b'', b'remove', None, _(b'remove a tag')),
7334 # -l/--local is already there, commitopts cannot be used
7332 # -l/--local is already there, commitopts cannot be used
7335 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7333 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7336 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7334 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7337 ]
7335 ]
7338 + commitopts2,
7336 + commitopts2,
7339 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7337 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7340 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7338 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7341 )
7339 )
7342 def tag(ui, repo, name1, *names, **opts):
7340 def tag(ui, repo, name1, *names, **opts):
7343 """add one or more tags for the current or given revision
7341 """add one or more tags for the current or given revision
7344
7342
7345 Name a particular revision using <name>.
7343 Name a particular revision using <name>.
7346
7344
7347 Tags are used to name particular revisions of the repository and are
7345 Tags are used to name particular revisions of the repository and are
7348 very useful to compare different revisions, to go back to significant
7346 very useful to compare different revisions, to go back to significant
7349 earlier versions or to mark branch points as releases, etc. Changing
7347 earlier versions or to mark branch points as releases, etc. Changing
7350 an existing tag is normally disallowed; use -f/--force to override.
7348 an existing tag is normally disallowed; use -f/--force to override.
7351
7349
7352 If no revision is given, the parent of the working directory is
7350 If no revision is given, the parent of the working directory is
7353 used.
7351 used.
7354
7352
7355 To facilitate version control, distribution, and merging of tags,
7353 To facilitate version control, distribution, and merging of tags,
7356 they are stored as a file named ".hgtags" which is managed similarly
7354 they are stored as a file named ".hgtags" which is managed similarly
7357 to other project files and can be hand-edited if necessary. This
7355 to other project files and can be hand-edited if necessary. This
7358 also means that tagging creates a new commit. The file
7356 also means that tagging creates a new commit. The file
7359 ".hg/localtags" is used for local tags (not shared among
7357 ".hg/localtags" is used for local tags (not shared among
7360 repositories).
7358 repositories).
7361
7359
7362 Tag commits are usually made at the head of a branch. If the parent
7360 Tag commits are usually made at the head of a branch. If the parent
7363 of the working directory is not a branch head, :hg:`tag` aborts; use
7361 of the working directory is not a branch head, :hg:`tag` aborts; use
7364 -f/--force to force the tag commit to be based on a non-head
7362 -f/--force to force the tag commit to be based on a non-head
7365 changeset.
7363 changeset.
7366
7364
7367 See :hg:`help dates` for a list of formats valid for -d/--date.
7365 See :hg:`help dates` for a list of formats valid for -d/--date.
7368
7366
7369 Since tag names have priority over branch names during revision
7367 Since tag names have priority over branch names during revision
7370 lookup, using an existing branch name as a tag name is discouraged.
7368 lookup, using an existing branch name as a tag name is discouraged.
7371
7369
7372 Returns 0 on success.
7370 Returns 0 on success.
7373 """
7371 """
7374 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7372 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7375 opts = pycompat.byteskwargs(opts)
7373 opts = pycompat.byteskwargs(opts)
7376 with repo.wlock(), repo.lock():
7374 with repo.wlock(), repo.lock():
7377 rev_ = b"."
7375 rev_ = b"."
7378 names = [t.strip() for t in (name1,) + names]
7376 names = [t.strip() for t in (name1,) + names]
7379 if len(names) != len(set(names)):
7377 if len(names) != len(set(names)):
7380 raise error.InputError(_(b'tag names must be unique'))
7378 raise error.InputError(_(b'tag names must be unique'))
7381 for n in names:
7379 for n in names:
7382 scmutil.checknewlabel(repo, n, b'tag')
7380 scmutil.checknewlabel(repo, n, b'tag')
7383 if not n:
7381 if not n:
7384 raise error.InputError(
7382 raise error.InputError(
7385 _(b'tag names cannot consist entirely of whitespace')
7383 _(b'tag names cannot consist entirely of whitespace')
7386 )
7384 )
7387 if opts.get(b'rev'):
7385 if opts.get(b'rev'):
7388 rev_ = opts[b'rev']
7386 rev_ = opts[b'rev']
7389 message = opts.get(b'message')
7387 message = opts.get(b'message')
7390 if opts.get(b'remove'):
7388 if opts.get(b'remove'):
7391 if opts.get(b'local'):
7389 if opts.get(b'local'):
7392 expectedtype = b'local'
7390 expectedtype = b'local'
7393 else:
7391 else:
7394 expectedtype = b'global'
7392 expectedtype = b'global'
7395
7393
7396 for n in names:
7394 for n in names:
7397 if repo.tagtype(n) == b'global':
7395 if repo.tagtype(n) == b'global':
7398 alltags = tagsmod.findglobaltags(ui, repo)
7396 alltags = tagsmod.findglobaltags(ui, repo)
7399 if alltags[n][0] == repo.nullid:
7397 if alltags[n][0] == repo.nullid:
7400 raise error.InputError(
7398 raise error.InputError(
7401 _(b"tag '%s' is already removed") % n
7399 _(b"tag '%s' is already removed") % n
7402 )
7400 )
7403 if not repo.tagtype(n):
7401 if not repo.tagtype(n):
7404 raise error.InputError(_(b"tag '%s' does not exist") % n)
7402 raise error.InputError(_(b"tag '%s' does not exist") % n)
7405 if repo.tagtype(n) != expectedtype:
7403 if repo.tagtype(n) != expectedtype:
7406 if expectedtype == b'global':
7404 if expectedtype == b'global':
7407 raise error.InputError(
7405 raise error.InputError(
7408 _(b"tag '%s' is not a global tag") % n
7406 _(b"tag '%s' is not a global tag") % n
7409 )
7407 )
7410 else:
7408 else:
7411 raise error.InputError(
7409 raise error.InputError(
7412 _(b"tag '%s' is not a local tag") % n
7410 _(b"tag '%s' is not a local tag") % n
7413 )
7411 )
7414 rev_ = b'null'
7412 rev_ = b'null'
7415 if not message:
7413 if not message:
7416 # we don't translate commit messages
7414 # we don't translate commit messages
7417 message = b'Removed tag %s' % b', '.join(names)
7415 message = b'Removed tag %s' % b', '.join(names)
7418 elif not opts.get(b'force'):
7416 elif not opts.get(b'force'):
7419 for n in names:
7417 for n in names:
7420 if n in repo.tags():
7418 if n in repo.tags():
7421 raise error.InputError(
7419 raise error.InputError(
7422 _(b"tag '%s' already exists (use -f to force)") % n
7420 _(b"tag '%s' already exists (use -f to force)") % n
7423 )
7421 )
7424 if not opts.get(b'local'):
7422 if not opts.get(b'local'):
7425 p1, p2 = repo.dirstate.parents()
7423 p1, p2 = repo.dirstate.parents()
7426 if p2 != repo.nullid:
7424 if p2 != repo.nullid:
7427 raise error.StateError(_(b'uncommitted merge'))
7425 raise error.StateError(_(b'uncommitted merge'))
7428 bheads = repo.branchheads()
7426 bheads = repo.branchheads()
7429 if not opts.get(b'force') and bheads and p1 not in bheads:
7427 if not opts.get(b'force') and bheads and p1 not in bheads:
7430 raise error.InputError(
7428 raise error.InputError(
7431 _(
7429 _(
7432 b'working directory is not at a branch head '
7430 b'working directory is not at a branch head '
7433 b'(use -f to force)'
7431 b'(use -f to force)'
7434 )
7432 )
7435 )
7433 )
7436 node = logcmdutil.revsingle(repo, rev_).node()
7434 node = logcmdutil.revsingle(repo, rev_).node()
7437
7435
7438 if not message:
7436 if not message:
7439 # we don't translate commit messages
7437 # we don't translate commit messages
7440 message = b'Added tag %s for changeset %s' % (
7438 message = b'Added tag %s for changeset %s' % (
7441 b', '.join(names),
7439 b', '.join(names),
7442 short(node),
7440 short(node),
7443 )
7441 )
7444
7442
7445 date = opts.get(b'date')
7443 date = opts.get(b'date')
7446 if date:
7444 if date:
7447 date = dateutil.parsedate(date)
7445 date = dateutil.parsedate(date)
7448
7446
7449 if opts.get(b'remove'):
7447 if opts.get(b'remove'):
7450 editform = b'tag.remove'
7448 editform = b'tag.remove'
7451 else:
7449 else:
7452 editform = b'tag.add'
7450 editform = b'tag.add'
7453 editor = cmdutil.getcommiteditor(
7451 editor = cmdutil.getcommiteditor(
7454 editform=editform, **pycompat.strkwargs(opts)
7452 editform=editform, **pycompat.strkwargs(opts)
7455 )
7453 )
7456
7454
7457 # don't allow tagging the null rev
7455 # don't allow tagging the null rev
7458 if (
7456 if (
7459 not opts.get(b'remove')
7457 not opts.get(b'remove')
7460 and logcmdutil.revsingle(repo, rev_).rev() == nullrev
7458 and logcmdutil.revsingle(repo, rev_).rev() == nullrev
7461 ):
7459 ):
7462 raise error.InputError(_(b"cannot tag null revision"))
7460 raise error.InputError(_(b"cannot tag null revision"))
7463
7461
7464 tagsmod.tag(
7462 tagsmod.tag(
7465 repo,
7463 repo,
7466 names,
7464 names,
7467 node,
7465 node,
7468 message,
7466 message,
7469 opts.get(b'local'),
7467 opts.get(b'local'),
7470 opts.get(b'user'),
7468 opts.get(b'user'),
7471 date,
7469 date,
7472 editor=editor,
7470 editor=editor,
7473 )
7471 )
7474
7472
7475
7473
7476 @command(
7474 @command(
7477 b'tags',
7475 b'tags',
7478 formatteropts,
7476 formatteropts,
7479 b'',
7477 b'',
7480 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7478 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7481 intents={INTENT_READONLY},
7479 intents={INTENT_READONLY},
7482 )
7480 )
7483 def tags(ui, repo, **opts):
7481 def tags(ui, repo, **opts):
7484 """list repository tags
7482 """list repository tags
7485
7483
7486 This lists both regular and local tags. When the -v/--verbose
7484 This lists both regular and local tags. When the -v/--verbose
7487 switch is used, a third column "local" is printed for local tags.
7485 switch is used, a third column "local" is printed for local tags.
7488 When the -q/--quiet switch is used, only the tag name is printed.
7486 When the -q/--quiet switch is used, only the tag name is printed.
7489
7487
7490 .. container:: verbose
7488 .. container:: verbose
7491
7489
7492 Template:
7490 Template:
7493
7491
7494 The following keywords are supported in addition to the common template
7492 The following keywords are supported in addition to the common template
7495 keywords and functions such as ``{tag}``. See also
7493 keywords and functions such as ``{tag}``. See also
7496 :hg:`help templates`.
7494 :hg:`help templates`.
7497
7495
7498 :type: String. ``local`` for local tags.
7496 :type: String. ``local`` for local tags.
7499
7497
7500 Returns 0 on success.
7498 Returns 0 on success.
7501 """
7499 """
7502
7500
7503 opts = pycompat.byteskwargs(opts)
7501 opts = pycompat.byteskwargs(opts)
7504 ui.pager(b'tags')
7502 ui.pager(b'tags')
7505 fm = ui.formatter(b'tags', opts)
7503 fm = ui.formatter(b'tags', opts)
7506 hexfunc = fm.hexfunc
7504 hexfunc = fm.hexfunc
7507
7505
7508 for t, n in reversed(repo.tagslist()):
7506 for t, n in reversed(repo.tagslist()):
7509 hn = hexfunc(n)
7507 hn = hexfunc(n)
7510 label = b'tags.normal'
7508 label = b'tags.normal'
7511 tagtype = repo.tagtype(t)
7509 tagtype = repo.tagtype(t)
7512 if not tagtype or tagtype == b'global':
7510 if not tagtype or tagtype == b'global':
7513 tagtype = b''
7511 tagtype = b''
7514 else:
7512 else:
7515 label = b'tags.' + tagtype
7513 label = b'tags.' + tagtype
7516
7514
7517 fm.startitem()
7515 fm.startitem()
7518 fm.context(repo=repo)
7516 fm.context(repo=repo)
7519 fm.write(b'tag', b'%s', t, label=label)
7517 fm.write(b'tag', b'%s', t, label=label)
7520 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7518 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7521 fm.condwrite(
7519 fm.condwrite(
7522 not ui.quiet,
7520 not ui.quiet,
7523 b'rev node',
7521 b'rev node',
7524 fmt,
7522 fmt,
7525 repo.changelog.rev(n),
7523 repo.changelog.rev(n),
7526 hn,
7524 hn,
7527 label=label,
7525 label=label,
7528 )
7526 )
7529 fm.condwrite(
7527 fm.condwrite(
7530 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7528 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7531 )
7529 )
7532 fm.plain(b'\n')
7530 fm.plain(b'\n')
7533 fm.end()
7531 fm.end()
7534
7532
7535
7533
7536 @command(
7534 @command(
7537 b'tip',
7535 b'tip',
7538 [
7536 [
7539 (b'p', b'patch', None, _(b'show patch')),
7537 (b'p', b'patch', None, _(b'show patch')),
7540 (b'g', b'git', None, _(b'use git extended diff format')),
7538 (b'g', b'git', None, _(b'use git extended diff format')),
7541 ]
7539 ]
7542 + templateopts,
7540 + templateopts,
7543 _(b'[-p] [-g]'),
7541 _(b'[-p] [-g]'),
7544 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7542 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7545 )
7543 )
7546 def tip(ui, repo, **opts):
7544 def tip(ui, repo, **opts):
7547 """show the tip revision (DEPRECATED)
7545 """show the tip revision (DEPRECATED)
7548
7546
7549 The tip revision (usually just called the tip) is the changeset
7547 The tip revision (usually just called the tip) is the changeset
7550 most recently added to the repository (and therefore the most
7548 most recently added to the repository (and therefore the most
7551 recently changed head).
7549 recently changed head).
7552
7550
7553 If you have just made a commit, that commit will be the tip. If
7551 If you have just made a commit, that commit will be the tip. If
7554 you have just pulled changes from another repository, the tip of
7552 you have just pulled changes from another repository, the tip of
7555 that repository becomes the current tip. The "tip" tag is special
7553 that repository becomes the current tip. The "tip" tag is special
7556 and cannot be renamed or assigned to a different changeset.
7554 and cannot be renamed or assigned to a different changeset.
7557
7555
7558 This command is deprecated, please use :hg:`heads` instead.
7556 This command is deprecated, please use :hg:`heads` instead.
7559
7557
7560 Returns 0 on success.
7558 Returns 0 on success.
7561 """
7559 """
7562 opts = pycompat.byteskwargs(opts)
7560 opts = pycompat.byteskwargs(opts)
7563 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7561 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7564 displayer.show(repo[b'tip'])
7562 displayer.show(repo[b'tip'])
7565 displayer.close()
7563 displayer.close()
7566
7564
7567
7565
7568 @command(
7566 @command(
7569 b'unbundle',
7567 b'unbundle',
7570 [
7568 [
7571 (
7569 (
7572 b'u',
7570 b'u',
7573 b'update',
7571 b'update',
7574 None,
7572 None,
7575 _(b'update to new branch head if changesets were unbundled'),
7573 _(b'update to new branch head if changesets were unbundled'),
7576 )
7574 )
7577 ],
7575 ],
7578 _(b'[-u] FILE...'),
7576 _(b'[-u] FILE...'),
7579 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7577 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7580 )
7578 )
7581 def unbundle(ui, repo, fname1, *fnames, **opts):
7579 def unbundle(ui, repo, fname1, *fnames, **opts):
7582 """apply one or more bundle files
7580 """apply one or more bundle files
7583
7581
7584 Apply one or more bundle files generated by :hg:`bundle`.
7582 Apply one or more bundle files generated by :hg:`bundle`.
7585
7583
7586 Returns 0 on success, 1 if an update has unresolved files.
7584 Returns 0 on success, 1 if an update has unresolved files.
7587 """
7585 """
7588 fnames = (fname1,) + fnames
7586 fnames = (fname1,) + fnames
7589
7587
7590 with repo.lock():
7588 with repo.lock():
7591 for fname in fnames:
7589 for fname in fnames:
7592 f = hg.openpath(ui, fname)
7590 f = hg.openpath(ui, fname)
7593 gen = exchange.readbundle(ui, f, fname)
7591 gen = exchange.readbundle(ui, f, fname)
7594 if isinstance(gen, streamclone.streamcloneapplier):
7592 if isinstance(gen, streamclone.streamcloneapplier):
7595 raise error.InputError(
7593 raise error.InputError(
7596 _(
7594 _(
7597 b'packed bundles cannot be applied with '
7595 b'packed bundles cannot be applied with '
7598 b'"hg unbundle"'
7596 b'"hg unbundle"'
7599 ),
7597 ),
7600 hint=_(b'use "hg debugapplystreamclonebundle"'),
7598 hint=_(b'use "hg debugapplystreamclonebundle"'),
7601 )
7599 )
7602 url = b'bundle:' + fname
7600 url = b'bundle:' + fname
7603 try:
7601 try:
7604 txnname = b'unbundle'
7602 txnname = b'unbundle'
7605 if not isinstance(gen, bundle2.unbundle20):
7603 if not isinstance(gen, bundle2.unbundle20):
7606 txnname = b'unbundle\n%s' % urlutil.hidepassword(url)
7604 txnname = b'unbundle\n%s' % urlutil.hidepassword(url)
7607 with repo.transaction(txnname) as tr:
7605 with repo.transaction(txnname) as tr:
7608 op = bundle2.applybundle(
7606 op = bundle2.applybundle(
7609 repo, gen, tr, source=b'unbundle', url=url
7607 repo, gen, tr, source=b'unbundle', url=url
7610 )
7608 )
7611 except error.BundleUnknownFeatureError as exc:
7609 except error.BundleUnknownFeatureError as exc:
7612 raise error.Abort(
7610 raise error.Abort(
7613 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7611 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7614 hint=_(
7612 hint=_(
7615 b"see https://mercurial-scm.org/"
7613 b"see https://mercurial-scm.org/"
7616 b"wiki/BundleFeature for more "
7614 b"wiki/BundleFeature for more "
7617 b"information"
7615 b"information"
7618 ),
7616 ),
7619 )
7617 )
7620 modheads = bundle2.combinechangegroupresults(op)
7618 modheads = bundle2.combinechangegroupresults(op)
7621
7619
7622 if postincoming(ui, repo, modheads, opts.get('update'), None, None):
7620 if postincoming(ui, repo, modheads, opts.get('update'), None, None):
7623 return 1
7621 return 1
7624 else:
7622 else:
7625 return 0
7623 return 0
7626
7624
7627
7625
7628 @command(
7626 @command(
7629 b'unshelve',
7627 b'unshelve',
7630 [
7628 [
7631 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7629 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7632 (
7630 (
7633 b'c',
7631 b'c',
7634 b'continue',
7632 b'continue',
7635 None,
7633 None,
7636 _(b'continue an incomplete unshelve operation'),
7634 _(b'continue an incomplete unshelve operation'),
7637 ),
7635 ),
7638 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7636 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7639 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7637 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7640 (
7638 (
7641 b'n',
7639 b'n',
7642 b'name',
7640 b'name',
7643 b'',
7641 b'',
7644 _(b'restore shelved change with given name'),
7642 _(b'restore shelved change with given name'),
7645 _(b'NAME'),
7643 _(b'NAME'),
7646 ),
7644 ),
7647 (b't', b'tool', b'', _(b'specify merge tool')),
7645 (b't', b'tool', b'', _(b'specify merge tool')),
7648 (
7646 (
7649 b'',
7647 b'',
7650 b'date',
7648 b'date',
7651 b'',
7649 b'',
7652 _(b'set date for temporary commits (DEPRECATED)'),
7650 _(b'set date for temporary commits (DEPRECATED)'),
7653 _(b'DATE'),
7651 _(b'DATE'),
7654 ),
7652 ),
7655 ],
7653 ],
7656 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7654 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7657 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7655 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7658 )
7656 )
7659 def unshelve(ui, repo, *shelved, **opts):
7657 def unshelve(ui, repo, *shelved, **opts):
7660 """restore a shelved change to the working directory
7658 """restore a shelved change to the working directory
7661
7659
7662 This command accepts an optional name of a shelved change to
7660 This command accepts an optional name of a shelved change to
7663 restore. If none is given, the most recent shelved change is used.
7661 restore. If none is given, the most recent shelved change is used.
7664
7662
7665 If a shelved change is applied successfully, the bundle that
7663 If a shelved change is applied successfully, the bundle that
7666 contains the shelved changes is moved to a backup location
7664 contains the shelved changes is moved to a backup location
7667 (.hg/shelve-backup).
7665 (.hg/shelve-backup).
7668
7666
7669 Since you can restore a shelved change on top of an arbitrary
7667 Since you can restore a shelved change on top of an arbitrary
7670 commit, it is possible that unshelving will result in a conflict
7668 commit, it is possible that unshelving will result in a conflict
7671 between your changes and the commits you are unshelving onto. If
7669 between your changes and the commits you are unshelving onto. If
7672 this occurs, you must resolve the conflict, then use
7670 this occurs, you must resolve the conflict, then use
7673 ``--continue`` to complete the unshelve operation. (The bundle
7671 ``--continue`` to complete the unshelve operation. (The bundle
7674 will not be moved until you successfully complete the unshelve.)
7672 will not be moved until you successfully complete the unshelve.)
7675
7673
7676 (Alternatively, you can use ``--abort`` to abandon an unshelve
7674 (Alternatively, you can use ``--abort`` to abandon an unshelve
7677 that causes a conflict. This reverts the unshelved changes, and
7675 that causes a conflict. This reverts the unshelved changes, and
7678 leaves the bundle in place.)
7676 leaves the bundle in place.)
7679
7677
7680 If bare shelved change (without interactive, include and exclude
7678 If bare shelved change (without interactive, include and exclude
7681 option) was done on newly created branch it would restore branch
7679 option) was done on newly created branch it would restore branch
7682 information to the working directory.
7680 information to the working directory.
7683
7681
7684 After a successful unshelve, the shelved changes are stored in a
7682 After a successful unshelve, the shelved changes are stored in a
7685 backup directory. Only the N most recent backups are kept. N
7683 backup directory. Only the N most recent backups are kept. N
7686 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7684 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7687 configuration option.
7685 configuration option.
7688
7686
7689 .. container:: verbose
7687 .. container:: verbose
7690
7688
7691 Timestamp in seconds is used to decide order of backups. More
7689 Timestamp in seconds is used to decide order of backups. More
7692 than ``maxbackups`` backups are kept, if same timestamp
7690 than ``maxbackups`` backups are kept, if same timestamp
7693 prevents from deciding exact order of them, for safety.
7691 prevents from deciding exact order of them, for safety.
7694
7692
7695 Selected changes can be unshelved with ``--interactive`` flag.
7693 Selected changes can be unshelved with ``--interactive`` flag.
7696 The working directory is updated with the selected changes, and
7694 The working directory is updated with the selected changes, and
7697 only the unselected changes remain shelved.
7695 only the unselected changes remain shelved.
7698 Note: The whole shelve is applied to working directory first before
7696 Note: The whole shelve is applied to working directory first before
7699 running interactively. So, this will bring up all the conflicts between
7697 running interactively. So, this will bring up all the conflicts between
7700 working directory and the shelve, irrespective of which changes will be
7698 working directory and the shelve, irrespective of which changes will be
7701 unshelved.
7699 unshelved.
7702 """
7700 """
7703 with repo.wlock():
7701 with repo.wlock():
7704 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7702 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7705
7703
7706
7704
7707 statemod.addunfinished(
7705 statemod.addunfinished(
7708 b'unshelve',
7706 b'unshelve',
7709 fname=b'shelvedstate',
7707 fname=b'shelvedstate',
7710 continueflag=True,
7708 continueflag=True,
7711 abortfunc=shelvemod.hgabortunshelve,
7709 abortfunc=shelvemod.hgabortunshelve,
7712 continuefunc=shelvemod.hgcontinueunshelve,
7710 continuefunc=shelvemod.hgcontinueunshelve,
7713 cmdmsg=_(b'unshelve already in progress'),
7711 cmdmsg=_(b'unshelve already in progress'),
7714 )
7712 )
7715
7713
7716
7714
7717 @command(
7715 @command(
7718 b'update|up|checkout|co',
7716 b'update|up|checkout|co',
7719 [
7717 [
7720 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7718 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7721 (b'c', b'check', None, _(b'require clean working directory')),
7719 (b'c', b'check', None, _(b'require clean working directory')),
7722 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7720 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7723 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7721 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7724 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7722 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7725 ]
7723 ]
7726 + mergetoolopts,
7724 + mergetoolopts,
7727 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7725 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7728 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7726 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7729 helpbasic=True,
7727 helpbasic=True,
7730 )
7728 )
7731 def update(ui, repo, node=None, **opts):
7729 def update(ui, repo, node=None, **opts):
7732 """update working directory (or switch revisions)
7730 """update working directory (or switch revisions)
7733
7731
7734 Update the repository's working directory to the specified
7732 Update the repository's working directory to the specified
7735 changeset. If no changeset is specified, update to the tip of the
7733 changeset. If no changeset is specified, update to the tip of the
7736 current named branch and move the active bookmark (see :hg:`help
7734 current named branch and move the active bookmark (see :hg:`help
7737 bookmarks`).
7735 bookmarks`).
7738
7736
7739 Update sets the working directory's parent revision to the specified
7737 Update sets the working directory's parent revision to the specified
7740 changeset (see :hg:`help parents`).
7738 changeset (see :hg:`help parents`).
7741
7739
7742 If the changeset is not a descendant or ancestor of the working
7740 If the changeset is not a descendant or ancestor of the working
7743 directory's parent and there are uncommitted changes, the update is
7741 directory's parent and there are uncommitted changes, the update is
7744 aborted. With the -c/--check option, the working directory is checked
7742 aborted. With the -c/--check option, the working directory is checked
7745 for uncommitted changes; if none are found, the working directory is
7743 for uncommitted changes; if none are found, the working directory is
7746 updated to the specified changeset.
7744 updated to the specified changeset.
7747
7745
7748 .. container:: verbose
7746 .. container:: verbose
7749
7747
7750 The -C/--clean, -c/--check, and -m/--merge options control what
7748 The -C/--clean, -c/--check, and -m/--merge options control what
7751 happens if the working directory contains uncommitted changes.
7749 happens if the working directory contains uncommitted changes.
7752 At most of one of them can be specified.
7750 At most of one of them can be specified.
7753
7751
7754 1. If no option is specified, and if
7752 1. If no option is specified, and if
7755 the requested changeset is an ancestor or descendant of
7753 the requested changeset is an ancestor or descendant of
7756 the working directory's parent, the uncommitted changes
7754 the working directory's parent, the uncommitted changes
7757 are merged into the requested changeset and the merged
7755 are merged into the requested changeset and the merged
7758 result is left uncommitted. If the requested changeset is
7756 result is left uncommitted. If the requested changeset is
7759 not an ancestor or descendant (that is, it is on another
7757 not an ancestor or descendant (that is, it is on another
7760 branch), the update is aborted and the uncommitted changes
7758 branch), the update is aborted and the uncommitted changes
7761 are preserved.
7759 are preserved.
7762
7760
7763 2. With the -m/--merge option, the update is allowed even if the
7761 2. With the -m/--merge option, the update is allowed even if the
7764 requested changeset is not an ancestor or descendant of
7762 requested changeset is not an ancestor or descendant of
7765 the working directory's parent.
7763 the working directory's parent.
7766
7764
7767 3. With the -c/--check option, the update is aborted and the
7765 3. With the -c/--check option, the update is aborted and the
7768 uncommitted changes are preserved.
7766 uncommitted changes are preserved.
7769
7767
7770 4. With the -C/--clean option, uncommitted changes are discarded and
7768 4. With the -C/--clean option, uncommitted changes are discarded and
7771 the working directory is updated to the requested changeset.
7769 the working directory is updated to the requested changeset.
7772
7770
7773 To cancel an uncommitted merge (and lose your changes), use
7771 To cancel an uncommitted merge (and lose your changes), use
7774 :hg:`merge --abort`.
7772 :hg:`merge --abort`.
7775
7773
7776 Use null as the changeset to remove the working directory (like
7774 Use null as the changeset to remove the working directory (like
7777 :hg:`clone -U`).
7775 :hg:`clone -U`).
7778
7776
7779 If you want to revert just one file to an older revision, use
7777 If you want to revert just one file to an older revision, use
7780 :hg:`revert [-r REV] NAME`.
7778 :hg:`revert [-r REV] NAME`.
7781
7779
7782 See :hg:`help dates` for a list of formats valid for -d/--date.
7780 See :hg:`help dates` for a list of formats valid for -d/--date.
7783
7781
7784 Returns 0 on success, 1 if there are unresolved files.
7782 Returns 0 on success, 1 if there are unresolved files.
7785 """
7783 """
7786 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7784 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7787 rev = opts.get('rev')
7785 rev = opts.get('rev')
7788 date = opts.get('date')
7786 date = opts.get('date')
7789 clean = opts.get('clean')
7787 clean = opts.get('clean')
7790 check = opts.get('check')
7788 check = opts.get('check')
7791 merge = opts.get('merge')
7789 merge = opts.get('merge')
7792 if rev and node:
7790 if rev and node:
7793 raise error.InputError(_(b"please specify just one revision"))
7791 raise error.InputError(_(b"please specify just one revision"))
7794
7792
7795 if ui.configbool(b'commands', b'update.requiredest'):
7793 if ui.configbool(b'commands', b'update.requiredest'):
7796 if not node and not rev and not date:
7794 if not node and not rev and not date:
7797 raise error.InputError(
7795 raise error.InputError(
7798 _(b'you must specify a destination'),
7796 _(b'you must specify a destination'),
7799 hint=_(b'for example: hg update ".::"'),
7797 hint=_(b'for example: hg update ".::"'),
7800 )
7798 )
7801
7799
7802 if rev is None or rev == b'':
7800 if rev is None or rev == b'':
7803 rev = node
7801 rev = node
7804
7802
7805 if date and rev is not None:
7803 if date and rev is not None:
7806 raise error.InputError(_(b"you can't specify a revision and a date"))
7804 raise error.InputError(_(b"you can't specify a revision and a date"))
7807
7805
7808 updatecheck = None
7806 updatecheck = None
7809 if check or merge is not None and not merge:
7807 if check or merge is not None and not merge:
7810 updatecheck = b'abort'
7808 updatecheck = b'abort'
7811 elif merge or check is not None and not check:
7809 elif merge or check is not None and not check:
7812 updatecheck = b'none'
7810 updatecheck = b'none'
7813
7811
7814 with repo.wlock():
7812 with repo.wlock():
7815 cmdutil.clearunfinished(repo)
7813 cmdutil.clearunfinished(repo)
7816 if date:
7814 if date:
7817 rev = cmdutil.finddate(ui, repo, date)
7815 rev = cmdutil.finddate(ui, repo, date)
7818
7816
7819 # if we defined a bookmark, we have to remember the original name
7817 # if we defined a bookmark, we have to remember the original name
7820 brev = rev
7818 brev = rev
7821 if rev:
7819 if rev:
7822 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7820 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7823 ctx = logcmdutil.revsingle(repo, rev, default=None)
7821 ctx = logcmdutil.revsingle(repo, rev, default=None)
7824 rev = ctx.rev()
7822 rev = ctx.rev()
7825 hidden = ctx.hidden()
7823 hidden = ctx.hidden()
7826 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7824 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7827 with ui.configoverride(overrides, b'update'):
7825 with ui.configoverride(overrides, b'update'):
7828 ret = hg.updatetotally(
7826 ret = hg.updatetotally(
7829 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7827 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7830 )
7828 )
7831 if hidden:
7829 if hidden:
7832 ctxstr = ctx.hex()[:12]
7830 ctxstr = ctx.hex()[:12]
7833 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7831 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7834
7832
7835 if ctx.obsolete():
7833 if ctx.obsolete():
7836 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7834 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7837 ui.warn(b"(%s)\n" % obsfatemsg)
7835 ui.warn(b"(%s)\n" % obsfatemsg)
7838 return ret
7836 return ret
7839
7837
7840
7838
7841 @command(
7839 @command(
7842 b'verify',
7840 b'verify',
7843 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7841 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7844 helpcategory=command.CATEGORY_MAINTENANCE,
7842 helpcategory=command.CATEGORY_MAINTENANCE,
7845 )
7843 )
7846 def verify(ui, repo, **opts):
7844 def verify(ui, repo, **opts):
7847 """verify the integrity of the repository
7845 """verify the integrity of the repository
7848
7846
7849 Verify the integrity of the current repository.
7847 Verify the integrity of the current repository.
7850
7848
7851 This will perform an extensive check of the repository's
7849 This will perform an extensive check of the repository's
7852 integrity, validating the hashes and checksums of each entry in
7850 integrity, validating the hashes and checksums of each entry in
7853 the changelog, manifest, and tracked files, as well as the
7851 the changelog, manifest, and tracked files, as well as the
7854 integrity of their crosslinks and indices.
7852 integrity of their crosslinks and indices.
7855
7853
7856 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7854 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7857 for more information about recovery from corruption of the
7855 for more information about recovery from corruption of the
7858 repository.
7856 repository.
7859
7857
7860 Returns 0 on success, 1 if errors are encountered.
7858 Returns 0 on success, 1 if errors are encountered.
7861 """
7859 """
7862 opts = pycompat.byteskwargs(opts)
7860 opts = pycompat.byteskwargs(opts)
7863
7861
7864 level = None
7862 level = None
7865 if opts[b'full']:
7863 if opts[b'full']:
7866 level = verifymod.VERIFY_FULL
7864 level = verifymod.VERIFY_FULL
7867 return hg.verify(repo, level)
7865 return hg.verify(repo, level)
7868
7866
7869
7867
7870 @command(
7868 @command(
7871 b'version',
7869 b'version',
7872 [] + formatteropts,
7870 [] + formatteropts,
7873 helpcategory=command.CATEGORY_HELP,
7871 helpcategory=command.CATEGORY_HELP,
7874 norepo=True,
7872 norepo=True,
7875 intents={INTENT_READONLY},
7873 intents={INTENT_READONLY},
7876 )
7874 )
7877 def version_(ui, **opts):
7875 def version_(ui, **opts):
7878 """output version and copyright information
7876 """output version and copyright information
7879
7877
7880 .. container:: verbose
7878 .. container:: verbose
7881
7879
7882 Template:
7880 Template:
7883
7881
7884 The following keywords are supported. See also :hg:`help templates`.
7882 The following keywords are supported. See also :hg:`help templates`.
7885
7883
7886 :extensions: List of extensions.
7884 :extensions: List of extensions.
7887 :ver: String. Version number.
7885 :ver: String. Version number.
7888
7886
7889 And each entry of ``{extensions}`` provides the following sub-keywords
7887 And each entry of ``{extensions}`` provides the following sub-keywords
7890 in addition to ``{ver}``.
7888 in addition to ``{ver}``.
7891
7889
7892 :bundled: Boolean. True if included in the release.
7890 :bundled: Boolean. True if included in the release.
7893 :name: String. Extension name.
7891 :name: String. Extension name.
7894 """
7892 """
7895 opts = pycompat.byteskwargs(opts)
7893 opts = pycompat.byteskwargs(opts)
7896 if ui.verbose:
7894 if ui.verbose:
7897 ui.pager(b'version')
7895 ui.pager(b'version')
7898 fm = ui.formatter(b"version", opts)
7896 fm = ui.formatter(b"version", opts)
7899 fm.startitem()
7897 fm.startitem()
7900 fm.write(
7898 fm.write(
7901 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7899 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7902 )
7900 )
7903 license = _(
7901 license = _(
7904 b"(see https://mercurial-scm.org for more information)\n"
7902 b"(see https://mercurial-scm.org for more information)\n"
7905 b"\nCopyright (C) 2005-2022 Olivia Mackall and others\n"
7903 b"\nCopyright (C) 2005-2022 Olivia Mackall and others\n"
7906 b"This is free software; see the source for copying conditions. "
7904 b"This is free software; see the source for copying conditions. "
7907 b"There is NO\nwarranty; "
7905 b"There is NO\nwarranty; "
7908 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7906 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7909 )
7907 )
7910 if not ui.quiet:
7908 if not ui.quiet:
7911 fm.plain(license)
7909 fm.plain(license)
7912
7910
7913 if ui.verbose:
7911 if ui.verbose:
7914 fm.plain(_(b"\nEnabled extensions:\n\n"))
7912 fm.plain(_(b"\nEnabled extensions:\n\n"))
7915 # format names and versions into columns
7913 # format names and versions into columns
7916 names = []
7914 names = []
7917 vers = []
7915 vers = []
7918 isinternals = []
7916 isinternals = []
7919 for name, module in sorted(extensions.extensions()):
7917 for name, module in sorted(extensions.extensions()):
7920 names.append(name)
7918 names.append(name)
7921 vers.append(extensions.moduleversion(module) or None)
7919 vers.append(extensions.moduleversion(module) or None)
7922 isinternals.append(extensions.ismoduleinternal(module))
7920 isinternals.append(extensions.ismoduleinternal(module))
7923 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7921 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7924 if names:
7922 if names:
7925 namefmt = b" %%-%ds " % max(len(n) for n in names)
7923 namefmt = b" %%-%ds " % max(len(n) for n in names)
7926 places = [_(b"external"), _(b"internal")]
7924 places = [_(b"external"), _(b"internal")]
7927 for n, v, p in zip(names, vers, isinternals):
7925 for n, v, p in zip(names, vers, isinternals):
7928 fn.startitem()
7926 fn.startitem()
7929 fn.condwrite(ui.verbose, b"name", namefmt, n)
7927 fn.condwrite(ui.verbose, b"name", namefmt, n)
7930 if ui.verbose:
7928 if ui.verbose:
7931 fn.plain(b"%s " % places[p])
7929 fn.plain(b"%s " % places[p])
7932 fn.data(bundled=p)
7930 fn.data(bundled=p)
7933 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7931 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7934 if ui.verbose:
7932 if ui.verbose:
7935 fn.plain(b"\n")
7933 fn.plain(b"\n")
7936 fn.end()
7934 fn.end()
7937 fm.end()
7935 fm.end()
7938
7936
7939
7937
7940 def loadcmdtable(ui, name, cmdtable):
7938 def loadcmdtable(ui, name, cmdtable):
7941 """Load command functions from specified cmdtable"""
7939 """Load command functions from specified cmdtable"""
7942 overrides = [cmd for cmd in cmdtable if cmd in table]
7940 overrides = [cmd for cmd in cmdtable if cmd in table]
7943 if overrides:
7941 if overrides:
7944 ui.warn(
7942 ui.warn(
7945 _(b"extension '%s' overrides commands: %s\n")
7943 _(b"extension '%s' overrides commands: %s\n")
7946 % (name, b" ".join(overrides))
7944 % (name, b" ".join(overrides))
7947 )
7945 )
7948 table.update(cmdtable)
7946 table.update(cmdtable)
General Comments 0
You need to be logged in to leave comments. Login now