##// END OF EJS Templates
bundle: introduce an higher level function to write bundle on disk...
marmoute -
r32216:9dc36df7 default
parent child Browse files
Show More
@@ -1,1669 +1,1705 b''
1 # bundle2.py - generic container format to transmit arbitrary data.
1 # bundle2.py - generic container format to transmit arbitrary data.
2 #
2 #
3 # Copyright 2013 Facebook, Inc.
3 # Copyright 2013 Facebook, Inc.
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 """Handling of the new bundle2 format
7 """Handling of the new bundle2 format
8
8
9 The goal of bundle2 is to act as an atomically packet to transmit a set of
9 The goal of bundle2 is to act as an atomically packet to transmit a set of
10 payloads in an application agnostic way. It consist in a sequence of "parts"
10 payloads in an application agnostic way. It consist in a sequence of "parts"
11 that will be handed to and processed by the application layer.
11 that will be handed to and processed by the application layer.
12
12
13
13
14 General format architecture
14 General format architecture
15 ===========================
15 ===========================
16
16
17 The format is architectured as follow
17 The format is architectured as follow
18
18
19 - magic string
19 - magic string
20 - stream level parameters
20 - stream level parameters
21 - payload parts (any number)
21 - payload parts (any number)
22 - end of stream marker.
22 - end of stream marker.
23
23
24 the Binary format
24 the Binary format
25 ============================
25 ============================
26
26
27 All numbers are unsigned and big-endian.
27 All numbers are unsigned and big-endian.
28
28
29 stream level parameters
29 stream level parameters
30 ------------------------
30 ------------------------
31
31
32 Binary format is as follow
32 Binary format is as follow
33
33
34 :params size: int32
34 :params size: int32
35
35
36 The total number of Bytes used by the parameters
36 The total number of Bytes used by the parameters
37
37
38 :params value: arbitrary number of Bytes
38 :params value: arbitrary number of Bytes
39
39
40 A blob of `params size` containing the serialized version of all stream level
40 A blob of `params size` containing the serialized version of all stream level
41 parameters.
41 parameters.
42
42
43 The blob contains a space separated list of parameters. Parameters with value
43 The blob contains a space separated list of parameters. Parameters with value
44 are stored in the form `<name>=<value>`. Both name and value are urlquoted.
44 are stored in the form `<name>=<value>`. Both name and value are urlquoted.
45
45
46 Empty name are obviously forbidden.
46 Empty name are obviously forbidden.
47
47
48 Name MUST start with a letter. If this first letter is lower case, the
48 Name MUST start with a letter. If this first letter is lower case, the
49 parameter is advisory and can be safely ignored. However when the first
49 parameter is advisory and can be safely ignored. However when the first
50 letter is capital, the parameter is mandatory and the bundling process MUST
50 letter is capital, the parameter is mandatory and the bundling process MUST
51 stop if he is not able to proceed it.
51 stop if he is not able to proceed it.
52
52
53 Stream parameters use a simple textual format for two main reasons:
53 Stream parameters use a simple textual format for two main reasons:
54
54
55 - Stream level parameters should remain simple and we want to discourage any
55 - Stream level parameters should remain simple and we want to discourage any
56 crazy usage.
56 crazy usage.
57 - Textual data allow easy human inspection of a bundle2 header in case of
57 - Textual data allow easy human inspection of a bundle2 header in case of
58 troubles.
58 troubles.
59
59
60 Any Applicative level options MUST go into a bundle2 part instead.
60 Any Applicative level options MUST go into a bundle2 part instead.
61
61
62 Payload part
62 Payload part
63 ------------------------
63 ------------------------
64
64
65 Binary format is as follow
65 Binary format is as follow
66
66
67 :header size: int32
67 :header size: int32
68
68
69 The total number of Bytes used by the part header. When the header is empty
69 The total number of Bytes used by the part header. When the header is empty
70 (size = 0) this is interpreted as the end of stream marker.
70 (size = 0) this is interpreted as the end of stream marker.
71
71
72 :header:
72 :header:
73
73
74 The header defines how to interpret the part. It contains two piece of
74 The header defines how to interpret the part. It contains two piece of
75 data: the part type, and the part parameters.
75 data: the part type, and the part parameters.
76
76
77 The part type is used to route an application level handler, that can
77 The part type is used to route an application level handler, that can
78 interpret payload.
78 interpret payload.
79
79
80 Part parameters are passed to the application level handler. They are
80 Part parameters are passed to the application level handler. They are
81 meant to convey information that will help the application level object to
81 meant to convey information that will help the application level object to
82 interpret the part payload.
82 interpret the part payload.
83
83
84 The binary format of the header is has follow
84 The binary format of the header is has follow
85
85
86 :typesize: (one byte)
86 :typesize: (one byte)
87
87
88 :parttype: alphanumerical part name (restricted to [a-zA-Z0-9_:-]*)
88 :parttype: alphanumerical part name (restricted to [a-zA-Z0-9_:-]*)
89
89
90 :partid: A 32bits integer (unique in the bundle) that can be used to refer
90 :partid: A 32bits integer (unique in the bundle) that can be used to refer
91 to this part.
91 to this part.
92
92
93 :parameters:
93 :parameters:
94
94
95 Part's parameter may have arbitrary content, the binary structure is::
95 Part's parameter may have arbitrary content, the binary structure is::
96
96
97 <mandatory-count><advisory-count><param-sizes><param-data>
97 <mandatory-count><advisory-count><param-sizes><param-data>
98
98
99 :mandatory-count: 1 byte, number of mandatory parameters
99 :mandatory-count: 1 byte, number of mandatory parameters
100
100
101 :advisory-count: 1 byte, number of advisory parameters
101 :advisory-count: 1 byte, number of advisory parameters
102
102
103 :param-sizes:
103 :param-sizes:
104
104
105 N couple of bytes, where N is the total number of parameters. Each
105 N couple of bytes, where N is the total number of parameters. Each
106 couple contains (<size-of-key>, <size-of-value) for one parameter.
106 couple contains (<size-of-key>, <size-of-value) for one parameter.
107
107
108 :param-data:
108 :param-data:
109
109
110 A blob of bytes from which each parameter key and value can be
110 A blob of bytes from which each parameter key and value can be
111 retrieved using the list of size couples stored in the previous
111 retrieved using the list of size couples stored in the previous
112 field.
112 field.
113
113
114 Mandatory parameters comes first, then the advisory ones.
114 Mandatory parameters comes first, then the advisory ones.
115
115
116 Each parameter's key MUST be unique within the part.
116 Each parameter's key MUST be unique within the part.
117
117
118 :payload:
118 :payload:
119
119
120 payload is a series of `<chunksize><chunkdata>`.
120 payload is a series of `<chunksize><chunkdata>`.
121
121
122 `chunksize` is an int32, `chunkdata` are plain bytes (as much as
122 `chunksize` is an int32, `chunkdata` are plain bytes (as much as
123 `chunksize` says)` The payload part is concluded by a zero size chunk.
123 `chunksize` says)` The payload part is concluded by a zero size chunk.
124
124
125 The current implementation always produces either zero or one chunk.
125 The current implementation always produces either zero or one chunk.
126 This is an implementation limitation that will ultimately be lifted.
126 This is an implementation limitation that will ultimately be lifted.
127
127
128 `chunksize` can be negative to trigger special case processing. No such
128 `chunksize` can be negative to trigger special case processing. No such
129 processing is in place yet.
129 processing is in place yet.
130
130
131 Bundle processing
131 Bundle processing
132 ============================
132 ============================
133
133
134 Each part is processed in order using a "part handler". Handler are registered
134 Each part is processed in order using a "part handler". Handler are registered
135 for a certain part type.
135 for a certain part type.
136
136
137 The matching of a part to its handler is case insensitive. The case of the
137 The matching of a part to its handler is case insensitive. The case of the
138 part type is used to know if a part is mandatory or advisory. If the Part type
138 part type is used to know if a part is mandatory or advisory. If the Part type
139 contains any uppercase char it is considered mandatory. When no handler is
139 contains any uppercase char it is considered mandatory. When no handler is
140 known for a Mandatory part, the process is aborted and an exception is raised.
140 known for a Mandatory part, the process is aborted and an exception is raised.
141 If the part is advisory and no handler is known, the part is ignored. When the
141 If the part is advisory and no handler is known, the part is ignored. When the
142 process is aborted, the full bundle is still read from the stream to keep the
142 process is aborted, the full bundle is still read from the stream to keep the
143 channel usable. But none of the part read from an abort are processed. In the
143 channel usable. But none of the part read from an abort are processed. In the
144 future, dropping the stream may become an option for channel we do not care to
144 future, dropping the stream may become an option for channel we do not care to
145 preserve.
145 preserve.
146 """
146 """
147
147
148 from __future__ import absolute_import
148 from __future__ import absolute_import
149
149
150 import errno
150 import errno
151 import re
151 import re
152 import string
152 import string
153 import struct
153 import struct
154 import sys
154 import sys
155
155
156 from .i18n import _
156 from .i18n import _
157 from . import (
157 from . import (
158 changegroup,
158 changegroup,
159 error,
159 error,
160 obsolete,
160 obsolete,
161 pushkey,
161 pushkey,
162 pycompat,
162 pycompat,
163 tags,
163 tags,
164 url,
164 url,
165 util,
165 util,
166 )
166 )
167
167
168 urlerr = util.urlerr
168 urlerr = util.urlerr
169 urlreq = util.urlreq
169 urlreq = util.urlreq
170
170
171 _pack = struct.pack
171 _pack = struct.pack
172 _unpack = struct.unpack
172 _unpack = struct.unpack
173
173
174 _fstreamparamsize = '>i'
174 _fstreamparamsize = '>i'
175 _fpartheadersize = '>i'
175 _fpartheadersize = '>i'
176 _fparttypesize = '>B'
176 _fparttypesize = '>B'
177 _fpartid = '>I'
177 _fpartid = '>I'
178 _fpayloadsize = '>i'
178 _fpayloadsize = '>i'
179 _fpartparamcount = '>BB'
179 _fpartparamcount = '>BB'
180
180
181 preferedchunksize = 4096
181 preferedchunksize = 4096
182
182
183 _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]')
183 _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]')
184
184
185 def outdebug(ui, message):
185 def outdebug(ui, message):
186 """debug regarding output stream (bundling)"""
186 """debug regarding output stream (bundling)"""
187 if ui.configbool('devel', 'bundle2.debug', False):
187 if ui.configbool('devel', 'bundle2.debug', False):
188 ui.debug('bundle2-output: %s\n' % message)
188 ui.debug('bundle2-output: %s\n' % message)
189
189
190 def indebug(ui, message):
190 def indebug(ui, message):
191 """debug on input stream (unbundling)"""
191 """debug on input stream (unbundling)"""
192 if ui.configbool('devel', 'bundle2.debug', False):
192 if ui.configbool('devel', 'bundle2.debug', False):
193 ui.debug('bundle2-input: %s\n' % message)
193 ui.debug('bundle2-input: %s\n' % message)
194
194
195 def validateparttype(parttype):
195 def validateparttype(parttype):
196 """raise ValueError if a parttype contains invalid character"""
196 """raise ValueError if a parttype contains invalid character"""
197 if _parttypeforbidden.search(parttype):
197 if _parttypeforbidden.search(parttype):
198 raise ValueError(parttype)
198 raise ValueError(parttype)
199
199
200 def _makefpartparamsizes(nbparams):
200 def _makefpartparamsizes(nbparams):
201 """return a struct format to read part parameter sizes
201 """return a struct format to read part parameter sizes
202
202
203 The number parameters is variable so we need to build that format
203 The number parameters is variable so we need to build that format
204 dynamically.
204 dynamically.
205 """
205 """
206 return '>'+('BB'*nbparams)
206 return '>'+('BB'*nbparams)
207
207
208 parthandlermapping = {}
208 parthandlermapping = {}
209
209
210 def parthandler(parttype, params=()):
210 def parthandler(parttype, params=()):
211 """decorator that register a function as a bundle2 part handler
211 """decorator that register a function as a bundle2 part handler
212
212
213 eg::
213 eg::
214
214
215 @parthandler('myparttype', ('mandatory', 'param', 'handled'))
215 @parthandler('myparttype', ('mandatory', 'param', 'handled'))
216 def myparttypehandler(...):
216 def myparttypehandler(...):
217 '''process a part of type "my part".'''
217 '''process a part of type "my part".'''
218 ...
218 ...
219 """
219 """
220 validateparttype(parttype)
220 validateparttype(parttype)
221 def _decorator(func):
221 def _decorator(func):
222 lparttype = parttype.lower() # enforce lower case matching.
222 lparttype = parttype.lower() # enforce lower case matching.
223 assert lparttype not in parthandlermapping
223 assert lparttype not in parthandlermapping
224 parthandlermapping[lparttype] = func
224 parthandlermapping[lparttype] = func
225 func.params = frozenset(params)
225 func.params = frozenset(params)
226 return func
226 return func
227 return _decorator
227 return _decorator
228
228
229 class unbundlerecords(object):
229 class unbundlerecords(object):
230 """keep record of what happens during and unbundle
230 """keep record of what happens during and unbundle
231
231
232 New records are added using `records.add('cat', obj)`. Where 'cat' is a
232 New records are added using `records.add('cat', obj)`. Where 'cat' is a
233 category of record and obj is an arbitrary object.
233 category of record and obj is an arbitrary object.
234
234
235 `records['cat']` will return all entries of this category 'cat'.
235 `records['cat']` will return all entries of this category 'cat'.
236
236
237 Iterating on the object itself will yield `('category', obj)` tuples
237 Iterating on the object itself will yield `('category', obj)` tuples
238 for all entries.
238 for all entries.
239
239
240 All iterations happens in chronological order.
240 All iterations happens in chronological order.
241 """
241 """
242
242
243 def __init__(self):
243 def __init__(self):
244 self._categories = {}
244 self._categories = {}
245 self._sequences = []
245 self._sequences = []
246 self._replies = {}
246 self._replies = {}
247
247
248 def add(self, category, entry, inreplyto=None):
248 def add(self, category, entry, inreplyto=None):
249 """add a new record of a given category.
249 """add a new record of a given category.
250
250
251 The entry can then be retrieved in the list returned by
251 The entry can then be retrieved in the list returned by
252 self['category']."""
252 self['category']."""
253 self._categories.setdefault(category, []).append(entry)
253 self._categories.setdefault(category, []).append(entry)
254 self._sequences.append((category, entry))
254 self._sequences.append((category, entry))
255 if inreplyto is not None:
255 if inreplyto is not None:
256 self.getreplies(inreplyto).add(category, entry)
256 self.getreplies(inreplyto).add(category, entry)
257
257
258 def getreplies(self, partid):
258 def getreplies(self, partid):
259 """get the records that are replies to a specific part"""
259 """get the records that are replies to a specific part"""
260 return self._replies.setdefault(partid, unbundlerecords())
260 return self._replies.setdefault(partid, unbundlerecords())
261
261
262 def __getitem__(self, cat):
262 def __getitem__(self, cat):
263 return tuple(self._categories.get(cat, ()))
263 return tuple(self._categories.get(cat, ()))
264
264
265 def __iter__(self):
265 def __iter__(self):
266 return iter(self._sequences)
266 return iter(self._sequences)
267
267
268 def __len__(self):
268 def __len__(self):
269 return len(self._sequences)
269 return len(self._sequences)
270
270
271 def __nonzero__(self):
271 def __nonzero__(self):
272 return bool(self._sequences)
272 return bool(self._sequences)
273
273
274 __bool__ = __nonzero__
274 __bool__ = __nonzero__
275
275
276 class bundleoperation(object):
276 class bundleoperation(object):
277 """an object that represents a single bundling process
277 """an object that represents a single bundling process
278
278
279 Its purpose is to carry unbundle-related objects and states.
279 Its purpose is to carry unbundle-related objects and states.
280
280
281 A new object should be created at the beginning of each bundle processing.
281 A new object should be created at the beginning of each bundle processing.
282 The object is to be returned by the processing function.
282 The object is to be returned by the processing function.
283
283
284 The object has very little content now it will ultimately contain:
284 The object has very little content now it will ultimately contain:
285 * an access to the repo the bundle is applied to,
285 * an access to the repo the bundle is applied to,
286 * a ui object,
286 * a ui object,
287 * a way to retrieve a transaction to add changes to the repo,
287 * a way to retrieve a transaction to add changes to the repo,
288 * a way to record the result of processing each part,
288 * a way to record the result of processing each part,
289 * a way to construct a bundle response when applicable.
289 * a way to construct a bundle response when applicable.
290 """
290 """
291
291
292 def __init__(self, repo, transactiongetter, captureoutput=True):
292 def __init__(self, repo, transactiongetter, captureoutput=True):
293 self.repo = repo
293 self.repo = repo
294 self.ui = repo.ui
294 self.ui = repo.ui
295 self.records = unbundlerecords()
295 self.records = unbundlerecords()
296 self.gettransaction = transactiongetter
296 self.gettransaction = transactiongetter
297 self.reply = None
297 self.reply = None
298 self.captureoutput = captureoutput
298 self.captureoutput = captureoutput
299
299
300 class TransactionUnavailable(RuntimeError):
300 class TransactionUnavailable(RuntimeError):
301 pass
301 pass
302
302
303 def _notransaction():
303 def _notransaction():
304 """default method to get a transaction while processing a bundle
304 """default method to get a transaction while processing a bundle
305
305
306 Raise an exception to highlight the fact that no transaction was expected
306 Raise an exception to highlight the fact that no transaction was expected
307 to be created"""
307 to be created"""
308 raise TransactionUnavailable()
308 raise TransactionUnavailable()
309
309
310 def applybundle(repo, unbundler, tr, source=None, url=None, op=None):
310 def applybundle(repo, unbundler, tr, source=None, url=None, op=None):
311 # transform me into unbundler.apply() as soon as the freeze is lifted
311 # transform me into unbundler.apply() as soon as the freeze is lifted
312 tr.hookargs['bundle2'] = '1'
312 tr.hookargs['bundle2'] = '1'
313 if source is not None and 'source' not in tr.hookargs:
313 if source is not None and 'source' not in tr.hookargs:
314 tr.hookargs['source'] = source
314 tr.hookargs['source'] = source
315 if url is not None and 'url' not in tr.hookargs:
315 if url is not None and 'url' not in tr.hookargs:
316 tr.hookargs['url'] = url
316 tr.hookargs['url'] = url
317 return processbundle(repo, unbundler, lambda: tr, op=op)
317 return processbundle(repo, unbundler, lambda: tr, op=op)
318
318
319 def processbundle(repo, unbundler, transactiongetter=None, op=None):
319 def processbundle(repo, unbundler, transactiongetter=None, op=None):
320 """This function process a bundle, apply effect to/from a repo
320 """This function process a bundle, apply effect to/from a repo
321
321
322 It iterates over each part then searches for and uses the proper handling
322 It iterates over each part then searches for and uses the proper handling
323 code to process the part. Parts are processed in order.
323 code to process the part. Parts are processed in order.
324
324
325 Unknown Mandatory part will abort the process.
325 Unknown Mandatory part will abort the process.
326
326
327 It is temporarily possible to provide a prebuilt bundleoperation to the
327 It is temporarily possible to provide a prebuilt bundleoperation to the
328 function. This is used to ensure output is properly propagated in case of
328 function. This is used to ensure output is properly propagated in case of
329 an error during the unbundling. This output capturing part will likely be
329 an error during the unbundling. This output capturing part will likely be
330 reworked and this ability will probably go away in the process.
330 reworked and this ability will probably go away in the process.
331 """
331 """
332 if op is None:
332 if op is None:
333 if transactiongetter is None:
333 if transactiongetter is None:
334 transactiongetter = _notransaction
334 transactiongetter = _notransaction
335 op = bundleoperation(repo, transactiongetter)
335 op = bundleoperation(repo, transactiongetter)
336 # todo:
336 # todo:
337 # - replace this is a init function soon.
337 # - replace this is a init function soon.
338 # - exception catching
338 # - exception catching
339 unbundler.params
339 unbundler.params
340 if repo.ui.debugflag:
340 if repo.ui.debugflag:
341 msg = ['bundle2-input-bundle:']
341 msg = ['bundle2-input-bundle:']
342 if unbundler.params:
342 if unbundler.params:
343 msg.append(' %i params')
343 msg.append(' %i params')
344 if op.gettransaction is None:
344 if op.gettransaction is None:
345 msg.append(' no-transaction')
345 msg.append(' no-transaction')
346 else:
346 else:
347 msg.append(' with-transaction')
347 msg.append(' with-transaction')
348 msg.append('\n')
348 msg.append('\n')
349 repo.ui.debug(''.join(msg))
349 repo.ui.debug(''.join(msg))
350 iterparts = enumerate(unbundler.iterparts())
350 iterparts = enumerate(unbundler.iterparts())
351 part = None
351 part = None
352 nbpart = 0
352 nbpart = 0
353 try:
353 try:
354 for nbpart, part in iterparts:
354 for nbpart, part in iterparts:
355 _processpart(op, part)
355 _processpart(op, part)
356 except Exception as exc:
356 except Exception as exc:
357 # Any exceptions seeking to the end of the bundle at this point are
357 # Any exceptions seeking to the end of the bundle at this point are
358 # almost certainly related to the underlying stream being bad.
358 # almost certainly related to the underlying stream being bad.
359 # And, chances are that the exception we're handling is related to
359 # And, chances are that the exception we're handling is related to
360 # getting in that bad state. So, we swallow the seeking error and
360 # getting in that bad state. So, we swallow the seeking error and
361 # re-raise the original error.
361 # re-raise the original error.
362 seekerror = False
362 seekerror = False
363 try:
363 try:
364 for nbpart, part in iterparts:
364 for nbpart, part in iterparts:
365 # consume the bundle content
365 # consume the bundle content
366 part.seek(0, 2)
366 part.seek(0, 2)
367 except Exception:
367 except Exception:
368 seekerror = True
368 seekerror = True
369
369
370 # Small hack to let caller code distinguish exceptions from bundle2
370 # Small hack to let caller code distinguish exceptions from bundle2
371 # processing from processing the old format. This is mostly
371 # processing from processing the old format. This is mostly
372 # needed to handle different return codes to unbundle according to the
372 # needed to handle different return codes to unbundle according to the
373 # type of bundle. We should probably clean up or drop this return code
373 # type of bundle. We should probably clean up or drop this return code
374 # craziness in a future version.
374 # craziness in a future version.
375 exc.duringunbundle2 = True
375 exc.duringunbundle2 = True
376 salvaged = []
376 salvaged = []
377 replycaps = None
377 replycaps = None
378 if op.reply is not None:
378 if op.reply is not None:
379 salvaged = op.reply.salvageoutput()
379 salvaged = op.reply.salvageoutput()
380 replycaps = op.reply.capabilities
380 replycaps = op.reply.capabilities
381 exc._replycaps = replycaps
381 exc._replycaps = replycaps
382 exc._bundle2salvagedoutput = salvaged
382 exc._bundle2salvagedoutput = salvaged
383
383
384 # Re-raising from a variable loses the original stack. So only use
384 # Re-raising from a variable loses the original stack. So only use
385 # that form if we need to.
385 # that form if we need to.
386 if seekerror:
386 if seekerror:
387 raise exc
387 raise exc
388 else:
388 else:
389 raise
389 raise
390 finally:
390 finally:
391 repo.ui.debug('bundle2-input-bundle: %i parts total\n' % nbpart)
391 repo.ui.debug('bundle2-input-bundle: %i parts total\n' % nbpart)
392
392
393 return op
393 return op
394
394
395 def _processpart(op, part):
395 def _processpart(op, part):
396 """process a single part from a bundle
396 """process a single part from a bundle
397
397
398 The part is guaranteed to have been fully consumed when the function exits
398 The part is guaranteed to have been fully consumed when the function exits
399 (even if an exception is raised)."""
399 (even if an exception is raised)."""
400 status = 'unknown' # used by debug output
400 status = 'unknown' # used by debug output
401 hardabort = False
401 hardabort = False
402 try:
402 try:
403 try:
403 try:
404 handler = parthandlermapping.get(part.type)
404 handler = parthandlermapping.get(part.type)
405 if handler is None:
405 if handler is None:
406 status = 'unsupported-type'
406 status = 'unsupported-type'
407 raise error.BundleUnknownFeatureError(parttype=part.type)
407 raise error.BundleUnknownFeatureError(parttype=part.type)
408 indebug(op.ui, 'found a handler for part %r' % part.type)
408 indebug(op.ui, 'found a handler for part %r' % part.type)
409 unknownparams = part.mandatorykeys - handler.params
409 unknownparams = part.mandatorykeys - handler.params
410 if unknownparams:
410 if unknownparams:
411 unknownparams = list(unknownparams)
411 unknownparams = list(unknownparams)
412 unknownparams.sort()
412 unknownparams.sort()
413 status = 'unsupported-params (%s)' % unknownparams
413 status = 'unsupported-params (%s)' % unknownparams
414 raise error.BundleUnknownFeatureError(parttype=part.type,
414 raise error.BundleUnknownFeatureError(parttype=part.type,
415 params=unknownparams)
415 params=unknownparams)
416 status = 'supported'
416 status = 'supported'
417 except error.BundleUnknownFeatureError as exc:
417 except error.BundleUnknownFeatureError as exc:
418 if part.mandatory: # mandatory parts
418 if part.mandatory: # mandatory parts
419 raise
419 raise
420 indebug(op.ui, 'ignoring unsupported advisory part %s' % exc)
420 indebug(op.ui, 'ignoring unsupported advisory part %s' % exc)
421 return # skip to part processing
421 return # skip to part processing
422 finally:
422 finally:
423 if op.ui.debugflag:
423 if op.ui.debugflag:
424 msg = ['bundle2-input-part: "%s"' % part.type]
424 msg = ['bundle2-input-part: "%s"' % part.type]
425 if not part.mandatory:
425 if not part.mandatory:
426 msg.append(' (advisory)')
426 msg.append(' (advisory)')
427 nbmp = len(part.mandatorykeys)
427 nbmp = len(part.mandatorykeys)
428 nbap = len(part.params) - nbmp
428 nbap = len(part.params) - nbmp
429 if nbmp or nbap:
429 if nbmp or nbap:
430 msg.append(' (params:')
430 msg.append(' (params:')
431 if nbmp:
431 if nbmp:
432 msg.append(' %i mandatory' % nbmp)
432 msg.append(' %i mandatory' % nbmp)
433 if nbap:
433 if nbap:
434 msg.append(' %i advisory' % nbmp)
434 msg.append(' %i advisory' % nbmp)
435 msg.append(')')
435 msg.append(')')
436 msg.append(' %s\n' % status)
436 msg.append(' %s\n' % status)
437 op.ui.debug(''.join(msg))
437 op.ui.debug(''.join(msg))
438
438
439 # handler is called outside the above try block so that we don't
439 # handler is called outside the above try block so that we don't
440 # risk catching KeyErrors from anything other than the
440 # risk catching KeyErrors from anything other than the
441 # parthandlermapping lookup (any KeyError raised by handler()
441 # parthandlermapping lookup (any KeyError raised by handler()
442 # itself represents a defect of a different variety).
442 # itself represents a defect of a different variety).
443 output = None
443 output = None
444 if op.captureoutput and op.reply is not None:
444 if op.captureoutput and op.reply is not None:
445 op.ui.pushbuffer(error=True, subproc=True)
445 op.ui.pushbuffer(error=True, subproc=True)
446 output = ''
446 output = ''
447 try:
447 try:
448 handler(op, part)
448 handler(op, part)
449 finally:
449 finally:
450 if output is not None:
450 if output is not None:
451 output = op.ui.popbuffer()
451 output = op.ui.popbuffer()
452 if output:
452 if output:
453 outpart = op.reply.newpart('output', data=output,
453 outpart = op.reply.newpart('output', data=output,
454 mandatory=False)
454 mandatory=False)
455 outpart.addparam('in-reply-to', str(part.id), mandatory=False)
455 outpart.addparam('in-reply-to', str(part.id), mandatory=False)
456 # If exiting or interrupted, do not attempt to seek the stream in the
456 # If exiting or interrupted, do not attempt to seek the stream in the
457 # finally block below. This makes abort faster.
457 # finally block below. This makes abort faster.
458 except (SystemExit, KeyboardInterrupt):
458 except (SystemExit, KeyboardInterrupt):
459 hardabort = True
459 hardabort = True
460 raise
460 raise
461 finally:
461 finally:
462 # consume the part content to not corrupt the stream.
462 # consume the part content to not corrupt the stream.
463 if not hardabort:
463 if not hardabort:
464 part.seek(0, 2)
464 part.seek(0, 2)
465
465
466
466
467 def decodecaps(blob):
467 def decodecaps(blob):
468 """decode a bundle2 caps bytes blob into a dictionary
468 """decode a bundle2 caps bytes blob into a dictionary
469
469
470 The blob is a list of capabilities (one per line)
470 The blob is a list of capabilities (one per line)
471 Capabilities may have values using a line of the form::
471 Capabilities may have values using a line of the form::
472
472
473 capability=value1,value2,value3
473 capability=value1,value2,value3
474
474
475 The values are always a list."""
475 The values are always a list."""
476 caps = {}
476 caps = {}
477 for line in blob.splitlines():
477 for line in blob.splitlines():
478 if not line:
478 if not line:
479 continue
479 continue
480 if '=' not in line:
480 if '=' not in line:
481 key, vals = line, ()
481 key, vals = line, ()
482 else:
482 else:
483 key, vals = line.split('=', 1)
483 key, vals = line.split('=', 1)
484 vals = vals.split(',')
484 vals = vals.split(',')
485 key = urlreq.unquote(key)
485 key = urlreq.unquote(key)
486 vals = [urlreq.unquote(v) for v in vals]
486 vals = [urlreq.unquote(v) for v in vals]
487 caps[key] = vals
487 caps[key] = vals
488 return caps
488 return caps
489
489
490 def encodecaps(caps):
490 def encodecaps(caps):
491 """encode a bundle2 caps dictionary into a bytes blob"""
491 """encode a bundle2 caps dictionary into a bytes blob"""
492 chunks = []
492 chunks = []
493 for ca in sorted(caps):
493 for ca in sorted(caps):
494 vals = caps[ca]
494 vals = caps[ca]
495 ca = urlreq.quote(ca)
495 ca = urlreq.quote(ca)
496 vals = [urlreq.quote(v) for v in vals]
496 vals = [urlreq.quote(v) for v in vals]
497 if vals:
497 if vals:
498 ca = "%s=%s" % (ca, ','.join(vals))
498 ca = "%s=%s" % (ca, ','.join(vals))
499 chunks.append(ca)
499 chunks.append(ca)
500 return '\n'.join(chunks)
500 return '\n'.join(chunks)
501
501
502 bundletypes = {
502 bundletypes = {
503 "": ("", 'UN'), # only when using unbundle on ssh and old http servers
503 "": ("", 'UN'), # only when using unbundle on ssh and old http servers
504 # since the unification ssh accepts a header but there
504 # since the unification ssh accepts a header but there
505 # is no capability signaling it.
505 # is no capability signaling it.
506 "HG20": (), # special-cased below
506 "HG20": (), # special-cased below
507 "HG10UN": ("HG10UN", 'UN'),
507 "HG10UN": ("HG10UN", 'UN'),
508 "HG10BZ": ("HG10", 'BZ'),
508 "HG10BZ": ("HG10", 'BZ'),
509 "HG10GZ": ("HG10GZ", 'GZ'),
509 "HG10GZ": ("HG10GZ", 'GZ'),
510 }
510 }
511
511
512 # hgweb uses this list to communicate its preferred type
512 # hgweb uses this list to communicate its preferred type
513 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
513 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
514
514
515 class bundle20(object):
515 class bundle20(object):
516 """represent an outgoing bundle2 container
516 """represent an outgoing bundle2 container
517
517
518 Use the `addparam` method to add stream level parameter. and `newpart` to
518 Use the `addparam` method to add stream level parameter. and `newpart` to
519 populate it. Then call `getchunks` to retrieve all the binary chunks of
519 populate it. Then call `getchunks` to retrieve all the binary chunks of
520 data that compose the bundle2 container."""
520 data that compose the bundle2 container."""
521
521
522 _magicstring = 'HG20'
522 _magicstring = 'HG20'
523
523
524 def __init__(self, ui, capabilities=()):
524 def __init__(self, ui, capabilities=()):
525 self.ui = ui
525 self.ui = ui
526 self._params = []
526 self._params = []
527 self._parts = []
527 self._parts = []
528 self.capabilities = dict(capabilities)
528 self.capabilities = dict(capabilities)
529 self._compengine = util.compengines.forbundletype('UN')
529 self._compengine = util.compengines.forbundletype('UN')
530 self._compopts = None
530 self._compopts = None
531
531
532 def setcompression(self, alg, compopts=None):
532 def setcompression(self, alg, compopts=None):
533 """setup core part compression to <alg>"""
533 """setup core part compression to <alg>"""
534 if alg in (None, 'UN'):
534 if alg in (None, 'UN'):
535 return
535 return
536 assert not any(n.lower() == 'compression' for n, v in self._params)
536 assert not any(n.lower() == 'compression' for n, v in self._params)
537 self.addparam('Compression', alg)
537 self.addparam('Compression', alg)
538 self._compengine = util.compengines.forbundletype(alg)
538 self._compengine = util.compengines.forbundletype(alg)
539 self._compopts = compopts
539 self._compopts = compopts
540
540
541 @property
541 @property
542 def nbparts(self):
542 def nbparts(self):
543 """total number of parts added to the bundler"""
543 """total number of parts added to the bundler"""
544 return len(self._parts)
544 return len(self._parts)
545
545
546 # methods used to defines the bundle2 content
546 # methods used to defines the bundle2 content
547 def addparam(self, name, value=None):
547 def addparam(self, name, value=None):
548 """add a stream level parameter"""
548 """add a stream level parameter"""
549 if not name:
549 if not name:
550 raise ValueError('empty parameter name')
550 raise ValueError('empty parameter name')
551 if name[0] not in string.letters:
551 if name[0] not in string.letters:
552 raise ValueError('non letter first character: %r' % name)
552 raise ValueError('non letter first character: %r' % name)
553 self._params.append((name, value))
553 self._params.append((name, value))
554
554
555 def addpart(self, part):
555 def addpart(self, part):
556 """add a new part to the bundle2 container
556 """add a new part to the bundle2 container
557
557
558 Parts contains the actual applicative payload."""
558 Parts contains the actual applicative payload."""
559 assert part.id is None
559 assert part.id is None
560 part.id = len(self._parts) # very cheap counter
560 part.id = len(self._parts) # very cheap counter
561 self._parts.append(part)
561 self._parts.append(part)
562
562
563 def newpart(self, typeid, *args, **kwargs):
563 def newpart(self, typeid, *args, **kwargs):
564 """create a new part and add it to the containers
564 """create a new part and add it to the containers
565
565
566 As the part is directly added to the containers. For now, this means
566 As the part is directly added to the containers. For now, this means
567 that any failure to properly initialize the part after calling
567 that any failure to properly initialize the part after calling
568 ``newpart`` should result in a failure of the whole bundling process.
568 ``newpart`` should result in a failure of the whole bundling process.
569
569
570 You can still fall back to manually create and add if you need better
570 You can still fall back to manually create and add if you need better
571 control."""
571 control."""
572 part = bundlepart(typeid, *args, **kwargs)
572 part = bundlepart(typeid, *args, **kwargs)
573 self.addpart(part)
573 self.addpart(part)
574 return part
574 return part
575
575
576 # methods used to generate the bundle2 stream
576 # methods used to generate the bundle2 stream
577 def getchunks(self):
577 def getchunks(self):
578 if self.ui.debugflag:
578 if self.ui.debugflag:
579 msg = ['bundle2-output-bundle: "%s",' % self._magicstring]
579 msg = ['bundle2-output-bundle: "%s",' % self._magicstring]
580 if self._params:
580 if self._params:
581 msg.append(' (%i params)' % len(self._params))
581 msg.append(' (%i params)' % len(self._params))
582 msg.append(' %i parts total\n' % len(self._parts))
582 msg.append(' %i parts total\n' % len(self._parts))
583 self.ui.debug(''.join(msg))
583 self.ui.debug(''.join(msg))
584 outdebug(self.ui, 'start emission of %s stream' % self._magicstring)
584 outdebug(self.ui, 'start emission of %s stream' % self._magicstring)
585 yield self._magicstring
585 yield self._magicstring
586 param = self._paramchunk()
586 param = self._paramchunk()
587 outdebug(self.ui, 'bundle parameter: %s' % param)
587 outdebug(self.ui, 'bundle parameter: %s' % param)
588 yield _pack(_fstreamparamsize, len(param))
588 yield _pack(_fstreamparamsize, len(param))
589 if param:
589 if param:
590 yield param
590 yield param
591 for chunk in self._compengine.compressstream(self._getcorechunk(),
591 for chunk in self._compengine.compressstream(self._getcorechunk(),
592 self._compopts):
592 self._compopts):
593 yield chunk
593 yield chunk
594
594
595 def _paramchunk(self):
595 def _paramchunk(self):
596 """return a encoded version of all stream parameters"""
596 """return a encoded version of all stream parameters"""
597 blocks = []
597 blocks = []
598 for par, value in self._params:
598 for par, value in self._params:
599 par = urlreq.quote(par)
599 par = urlreq.quote(par)
600 if value is not None:
600 if value is not None:
601 value = urlreq.quote(value)
601 value = urlreq.quote(value)
602 par = '%s=%s' % (par, value)
602 par = '%s=%s' % (par, value)
603 blocks.append(par)
603 blocks.append(par)
604 return ' '.join(blocks)
604 return ' '.join(blocks)
605
605
606 def _getcorechunk(self):
606 def _getcorechunk(self):
607 """yield chunk for the core part of the bundle
607 """yield chunk for the core part of the bundle
608
608
609 (all but headers and parameters)"""
609 (all but headers and parameters)"""
610 outdebug(self.ui, 'start of parts')
610 outdebug(self.ui, 'start of parts')
611 for part in self._parts:
611 for part in self._parts:
612 outdebug(self.ui, 'bundle part: "%s"' % part.type)
612 outdebug(self.ui, 'bundle part: "%s"' % part.type)
613 for chunk in part.getchunks(ui=self.ui):
613 for chunk in part.getchunks(ui=self.ui):
614 yield chunk
614 yield chunk
615 outdebug(self.ui, 'end of bundle')
615 outdebug(self.ui, 'end of bundle')
616 yield _pack(_fpartheadersize, 0)
616 yield _pack(_fpartheadersize, 0)
617
617
618
618
619 def salvageoutput(self):
619 def salvageoutput(self):
620 """return a list with a copy of all output parts in the bundle
620 """return a list with a copy of all output parts in the bundle
621
621
622 This is meant to be used during error handling to make sure we preserve
622 This is meant to be used during error handling to make sure we preserve
623 server output"""
623 server output"""
624 salvaged = []
624 salvaged = []
625 for part in self._parts:
625 for part in self._parts:
626 if part.type.startswith('output'):
626 if part.type.startswith('output'):
627 salvaged.append(part.copy())
627 salvaged.append(part.copy())
628 return salvaged
628 return salvaged
629
629
630
630
631 class unpackermixin(object):
631 class unpackermixin(object):
632 """A mixin to extract bytes and struct data from a stream"""
632 """A mixin to extract bytes and struct data from a stream"""
633
633
634 def __init__(self, fp):
634 def __init__(self, fp):
635 self._fp = fp
635 self._fp = fp
636
636
637 def _unpack(self, format):
637 def _unpack(self, format):
638 """unpack this struct format from the stream
638 """unpack this struct format from the stream
639
639
640 This method is meant for internal usage by the bundle2 protocol only.
640 This method is meant for internal usage by the bundle2 protocol only.
641 They directly manipulate the low level stream including bundle2 level
641 They directly manipulate the low level stream including bundle2 level
642 instruction.
642 instruction.
643
643
644 Do not use it to implement higher-level logic or methods."""
644 Do not use it to implement higher-level logic or methods."""
645 data = self._readexact(struct.calcsize(format))
645 data = self._readexact(struct.calcsize(format))
646 return _unpack(format, data)
646 return _unpack(format, data)
647
647
648 def _readexact(self, size):
648 def _readexact(self, size):
649 """read exactly <size> bytes from the stream
649 """read exactly <size> bytes from the stream
650
650
651 This method is meant for internal usage by the bundle2 protocol only.
651 This method is meant for internal usage by the bundle2 protocol only.
652 They directly manipulate the low level stream including bundle2 level
652 They directly manipulate the low level stream including bundle2 level
653 instruction.
653 instruction.
654
654
655 Do not use it to implement higher-level logic or methods."""
655 Do not use it to implement higher-level logic or methods."""
656 return changegroup.readexactly(self._fp, size)
656 return changegroup.readexactly(self._fp, size)
657
657
658 def getunbundler(ui, fp, magicstring=None):
658 def getunbundler(ui, fp, magicstring=None):
659 """return a valid unbundler object for a given magicstring"""
659 """return a valid unbundler object for a given magicstring"""
660 if magicstring is None:
660 if magicstring is None:
661 magicstring = changegroup.readexactly(fp, 4)
661 magicstring = changegroup.readexactly(fp, 4)
662 magic, version = magicstring[0:2], magicstring[2:4]
662 magic, version = magicstring[0:2], magicstring[2:4]
663 if magic != 'HG':
663 if magic != 'HG':
664 raise error.Abort(_('not a Mercurial bundle'))
664 raise error.Abort(_('not a Mercurial bundle'))
665 unbundlerclass = formatmap.get(version)
665 unbundlerclass = formatmap.get(version)
666 if unbundlerclass is None:
666 if unbundlerclass is None:
667 raise error.Abort(_('unknown bundle version %s') % version)
667 raise error.Abort(_('unknown bundle version %s') % version)
668 unbundler = unbundlerclass(ui, fp)
668 unbundler = unbundlerclass(ui, fp)
669 indebug(ui, 'start processing of %s stream' % magicstring)
669 indebug(ui, 'start processing of %s stream' % magicstring)
670 return unbundler
670 return unbundler
671
671
672 class unbundle20(unpackermixin):
672 class unbundle20(unpackermixin):
673 """interpret a bundle2 stream
673 """interpret a bundle2 stream
674
674
675 This class is fed with a binary stream and yields parts through its
675 This class is fed with a binary stream and yields parts through its
676 `iterparts` methods."""
676 `iterparts` methods."""
677
677
678 _magicstring = 'HG20'
678 _magicstring = 'HG20'
679
679
680 def __init__(self, ui, fp):
680 def __init__(self, ui, fp):
681 """If header is specified, we do not read it out of the stream."""
681 """If header is specified, we do not read it out of the stream."""
682 self.ui = ui
682 self.ui = ui
683 self._compengine = util.compengines.forbundletype('UN')
683 self._compengine = util.compengines.forbundletype('UN')
684 self._compressed = None
684 self._compressed = None
685 super(unbundle20, self).__init__(fp)
685 super(unbundle20, self).__init__(fp)
686
686
687 @util.propertycache
687 @util.propertycache
688 def params(self):
688 def params(self):
689 """dictionary of stream level parameters"""
689 """dictionary of stream level parameters"""
690 indebug(self.ui, 'reading bundle2 stream parameters')
690 indebug(self.ui, 'reading bundle2 stream parameters')
691 params = {}
691 params = {}
692 paramssize = self._unpack(_fstreamparamsize)[0]
692 paramssize = self._unpack(_fstreamparamsize)[0]
693 if paramssize < 0:
693 if paramssize < 0:
694 raise error.BundleValueError('negative bundle param size: %i'
694 raise error.BundleValueError('negative bundle param size: %i'
695 % paramssize)
695 % paramssize)
696 if paramssize:
696 if paramssize:
697 params = self._readexact(paramssize)
697 params = self._readexact(paramssize)
698 params = self._processallparams(params)
698 params = self._processallparams(params)
699 return params
699 return params
700
700
701 def _processallparams(self, paramsblock):
701 def _processallparams(self, paramsblock):
702 """"""
702 """"""
703 params = util.sortdict()
703 params = util.sortdict()
704 for p in paramsblock.split(' '):
704 for p in paramsblock.split(' '):
705 p = p.split('=', 1)
705 p = p.split('=', 1)
706 p = [urlreq.unquote(i) for i in p]
706 p = [urlreq.unquote(i) for i in p]
707 if len(p) < 2:
707 if len(p) < 2:
708 p.append(None)
708 p.append(None)
709 self._processparam(*p)
709 self._processparam(*p)
710 params[p[0]] = p[1]
710 params[p[0]] = p[1]
711 return params
711 return params
712
712
713
713
714 def _processparam(self, name, value):
714 def _processparam(self, name, value):
715 """process a parameter, applying its effect if needed
715 """process a parameter, applying its effect if needed
716
716
717 Parameter starting with a lower case letter are advisory and will be
717 Parameter starting with a lower case letter are advisory and will be
718 ignored when unknown. Those starting with an upper case letter are
718 ignored when unknown. Those starting with an upper case letter are
719 mandatory and will this function will raise a KeyError when unknown.
719 mandatory and will this function will raise a KeyError when unknown.
720
720
721 Note: no option are currently supported. Any input will be either
721 Note: no option are currently supported. Any input will be either
722 ignored or failing.
722 ignored or failing.
723 """
723 """
724 if not name:
724 if not name:
725 raise ValueError('empty parameter name')
725 raise ValueError('empty parameter name')
726 if name[0] not in string.letters:
726 if name[0] not in string.letters:
727 raise ValueError('non letter first character: %r' % name)
727 raise ValueError('non letter first character: %r' % name)
728 try:
728 try:
729 handler = b2streamparamsmap[name.lower()]
729 handler = b2streamparamsmap[name.lower()]
730 except KeyError:
730 except KeyError:
731 if name[0].islower():
731 if name[0].islower():
732 indebug(self.ui, "ignoring unknown parameter %r" % name)
732 indebug(self.ui, "ignoring unknown parameter %r" % name)
733 else:
733 else:
734 raise error.BundleUnknownFeatureError(params=(name,))
734 raise error.BundleUnknownFeatureError(params=(name,))
735 else:
735 else:
736 handler(self, name, value)
736 handler(self, name, value)
737
737
738 def _forwardchunks(self):
738 def _forwardchunks(self):
739 """utility to transfer a bundle2 as binary
739 """utility to transfer a bundle2 as binary
740
740
741 This is made necessary by the fact the 'getbundle' command over 'ssh'
741 This is made necessary by the fact the 'getbundle' command over 'ssh'
742 have no way to know then the reply end, relying on the bundle to be
742 have no way to know then the reply end, relying on the bundle to be
743 interpreted to know its end. This is terrible and we are sorry, but we
743 interpreted to know its end. This is terrible and we are sorry, but we
744 needed to move forward to get general delta enabled.
744 needed to move forward to get general delta enabled.
745 """
745 """
746 yield self._magicstring
746 yield self._magicstring
747 assert 'params' not in vars(self)
747 assert 'params' not in vars(self)
748 paramssize = self._unpack(_fstreamparamsize)[0]
748 paramssize = self._unpack(_fstreamparamsize)[0]
749 if paramssize < 0:
749 if paramssize < 0:
750 raise error.BundleValueError('negative bundle param size: %i'
750 raise error.BundleValueError('negative bundle param size: %i'
751 % paramssize)
751 % paramssize)
752 yield _pack(_fstreamparamsize, paramssize)
752 yield _pack(_fstreamparamsize, paramssize)
753 if paramssize:
753 if paramssize:
754 params = self._readexact(paramssize)
754 params = self._readexact(paramssize)
755 self._processallparams(params)
755 self._processallparams(params)
756 yield params
756 yield params
757 assert self._compengine.bundletype == 'UN'
757 assert self._compengine.bundletype == 'UN'
758 # From there, payload might need to be decompressed
758 # From there, payload might need to be decompressed
759 self._fp = self._compengine.decompressorreader(self._fp)
759 self._fp = self._compengine.decompressorreader(self._fp)
760 emptycount = 0
760 emptycount = 0
761 while emptycount < 2:
761 while emptycount < 2:
762 # so we can brainlessly loop
762 # so we can brainlessly loop
763 assert _fpartheadersize == _fpayloadsize
763 assert _fpartheadersize == _fpayloadsize
764 size = self._unpack(_fpartheadersize)[0]
764 size = self._unpack(_fpartheadersize)[0]
765 yield _pack(_fpartheadersize, size)
765 yield _pack(_fpartheadersize, size)
766 if size:
766 if size:
767 emptycount = 0
767 emptycount = 0
768 else:
768 else:
769 emptycount += 1
769 emptycount += 1
770 continue
770 continue
771 if size == flaginterrupt:
771 if size == flaginterrupt:
772 continue
772 continue
773 elif size < 0:
773 elif size < 0:
774 raise error.BundleValueError('negative chunk size: %i')
774 raise error.BundleValueError('negative chunk size: %i')
775 yield self._readexact(size)
775 yield self._readexact(size)
776
776
777
777
778 def iterparts(self):
778 def iterparts(self):
779 """yield all parts contained in the stream"""
779 """yield all parts contained in the stream"""
780 # make sure param have been loaded
780 # make sure param have been loaded
781 self.params
781 self.params
782 # From there, payload need to be decompressed
782 # From there, payload need to be decompressed
783 self._fp = self._compengine.decompressorreader(self._fp)
783 self._fp = self._compengine.decompressorreader(self._fp)
784 indebug(self.ui, 'start extraction of bundle2 parts')
784 indebug(self.ui, 'start extraction of bundle2 parts')
785 headerblock = self._readpartheader()
785 headerblock = self._readpartheader()
786 while headerblock is not None:
786 while headerblock is not None:
787 part = unbundlepart(self.ui, headerblock, self._fp)
787 part = unbundlepart(self.ui, headerblock, self._fp)
788 yield part
788 yield part
789 part.seek(0, 2)
789 part.seek(0, 2)
790 headerblock = self._readpartheader()
790 headerblock = self._readpartheader()
791 indebug(self.ui, 'end of bundle2 stream')
791 indebug(self.ui, 'end of bundle2 stream')
792
792
793 def _readpartheader(self):
793 def _readpartheader(self):
794 """reads a part header size and return the bytes blob
794 """reads a part header size and return the bytes blob
795
795
796 returns None if empty"""
796 returns None if empty"""
797 headersize = self._unpack(_fpartheadersize)[0]
797 headersize = self._unpack(_fpartheadersize)[0]
798 if headersize < 0:
798 if headersize < 0:
799 raise error.BundleValueError('negative part header size: %i'
799 raise error.BundleValueError('negative part header size: %i'
800 % headersize)
800 % headersize)
801 indebug(self.ui, 'part header size: %i' % headersize)
801 indebug(self.ui, 'part header size: %i' % headersize)
802 if headersize:
802 if headersize:
803 return self._readexact(headersize)
803 return self._readexact(headersize)
804 return None
804 return None
805
805
806 def compressed(self):
806 def compressed(self):
807 self.params # load params
807 self.params # load params
808 return self._compressed
808 return self._compressed
809
809
810 def close(self):
810 def close(self):
811 """close underlying file"""
811 """close underlying file"""
812 if util.safehasattr(self._fp, 'close'):
812 if util.safehasattr(self._fp, 'close'):
813 return self._fp.close()
813 return self._fp.close()
814
814
815 formatmap = {'20': unbundle20}
815 formatmap = {'20': unbundle20}
816
816
817 b2streamparamsmap = {}
817 b2streamparamsmap = {}
818
818
819 def b2streamparamhandler(name):
819 def b2streamparamhandler(name):
820 """register a handler for a stream level parameter"""
820 """register a handler for a stream level parameter"""
821 def decorator(func):
821 def decorator(func):
822 assert name not in formatmap
822 assert name not in formatmap
823 b2streamparamsmap[name] = func
823 b2streamparamsmap[name] = func
824 return func
824 return func
825 return decorator
825 return decorator
826
826
827 @b2streamparamhandler('compression')
827 @b2streamparamhandler('compression')
828 def processcompression(unbundler, param, value):
828 def processcompression(unbundler, param, value):
829 """read compression parameter and install payload decompression"""
829 """read compression parameter and install payload decompression"""
830 if value not in util.compengines.supportedbundletypes:
830 if value not in util.compengines.supportedbundletypes:
831 raise error.BundleUnknownFeatureError(params=(param,),
831 raise error.BundleUnknownFeatureError(params=(param,),
832 values=(value,))
832 values=(value,))
833 unbundler._compengine = util.compengines.forbundletype(value)
833 unbundler._compengine = util.compengines.forbundletype(value)
834 if value is not None:
834 if value is not None:
835 unbundler._compressed = True
835 unbundler._compressed = True
836
836
837 class bundlepart(object):
837 class bundlepart(object):
838 """A bundle2 part contains application level payload
838 """A bundle2 part contains application level payload
839
839
840 The part `type` is used to route the part to the application level
840 The part `type` is used to route the part to the application level
841 handler.
841 handler.
842
842
843 The part payload is contained in ``part.data``. It could be raw bytes or a
843 The part payload is contained in ``part.data``. It could be raw bytes or a
844 generator of byte chunks.
844 generator of byte chunks.
845
845
846 You can add parameters to the part using the ``addparam`` method.
846 You can add parameters to the part using the ``addparam`` method.
847 Parameters can be either mandatory (default) or advisory. Remote side
847 Parameters can be either mandatory (default) or advisory. Remote side
848 should be able to safely ignore the advisory ones.
848 should be able to safely ignore the advisory ones.
849
849
850 Both data and parameters cannot be modified after the generation has begun.
850 Both data and parameters cannot be modified after the generation has begun.
851 """
851 """
852
852
853 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
853 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
854 data='', mandatory=True):
854 data='', mandatory=True):
855 validateparttype(parttype)
855 validateparttype(parttype)
856 self.id = None
856 self.id = None
857 self.type = parttype
857 self.type = parttype
858 self._data = data
858 self._data = data
859 self._mandatoryparams = list(mandatoryparams)
859 self._mandatoryparams = list(mandatoryparams)
860 self._advisoryparams = list(advisoryparams)
860 self._advisoryparams = list(advisoryparams)
861 # checking for duplicated entries
861 # checking for duplicated entries
862 self._seenparams = set()
862 self._seenparams = set()
863 for pname, __ in self._mandatoryparams + self._advisoryparams:
863 for pname, __ in self._mandatoryparams + self._advisoryparams:
864 if pname in self._seenparams:
864 if pname in self._seenparams:
865 raise error.ProgrammingError('duplicated params: %s' % pname)
865 raise error.ProgrammingError('duplicated params: %s' % pname)
866 self._seenparams.add(pname)
866 self._seenparams.add(pname)
867 # status of the part's generation:
867 # status of the part's generation:
868 # - None: not started,
868 # - None: not started,
869 # - False: currently generated,
869 # - False: currently generated,
870 # - True: generation done.
870 # - True: generation done.
871 self._generated = None
871 self._generated = None
872 self.mandatory = mandatory
872 self.mandatory = mandatory
873
873
874 def __repr__(self):
874 def __repr__(self):
875 cls = "%s.%s" % (self.__class__.__module__, self.__class__.__name__)
875 cls = "%s.%s" % (self.__class__.__module__, self.__class__.__name__)
876 return ('<%s object at %x; id: %s; type: %s; mandatory: %s>'
876 return ('<%s object at %x; id: %s; type: %s; mandatory: %s>'
877 % (cls, id(self), self.id, self.type, self.mandatory))
877 % (cls, id(self), self.id, self.type, self.mandatory))
878
878
879 def copy(self):
879 def copy(self):
880 """return a copy of the part
880 """return a copy of the part
881
881
882 The new part have the very same content but no partid assigned yet.
882 The new part have the very same content but no partid assigned yet.
883 Parts with generated data cannot be copied."""
883 Parts with generated data cannot be copied."""
884 assert not util.safehasattr(self.data, 'next')
884 assert not util.safehasattr(self.data, 'next')
885 return self.__class__(self.type, self._mandatoryparams,
885 return self.__class__(self.type, self._mandatoryparams,
886 self._advisoryparams, self._data, self.mandatory)
886 self._advisoryparams, self._data, self.mandatory)
887
887
888 # methods used to defines the part content
888 # methods used to defines the part content
889 @property
889 @property
890 def data(self):
890 def data(self):
891 return self._data
891 return self._data
892
892
893 @data.setter
893 @data.setter
894 def data(self, data):
894 def data(self, data):
895 if self._generated is not None:
895 if self._generated is not None:
896 raise error.ReadOnlyPartError('part is being generated')
896 raise error.ReadOnlyPartError('part is being generated')
897 self._data = data
897 self._data = data
898
898
899 @property
899 @property
900 def mandatoryparams(self):
900 def mandatoryparams(self):
901 # make it an immutable tuple to force people through ``addparam``
901 # make it an immutable tuple to force people through ``addparam``
902 return tuple(self._mandatoryparams)
902 return tuple(self._mandatoryparams)
903
903
904 @property
904 @property
905 def advisoryparams(self):
905 def advisoryparams(self):
906 # make it an immutable tuple to force people through ``addparam``
906 # make it an immutable tuple to force people through ``addparam``
907 return tuple(self._advisoryparams)
907 return tuple(self._advisoryparams)
908
908
909 def addparam(self, name, value='', mandatory=True):
909 def addparam(self, name, value='', mandatory=True):
910 """add a parameter to the part
910 """add a parameter to the part
911
911
912 If 'mandatory' is set to True, the remote handler must claim support
912 If 'mandatory' is set to True, the remote handler must claim support
913 for this parameter or the unbundling will be aborted.
913 for this parameter or the unbundling will be aborted.
914
914
915 The 'name' and 'value' cannot exceed 255 bytes each.
915 The 'name' and 'value' cannot exceed 255 bytes each.
916 """
916 """
917 if self._generated is not None:
917 if self._generated is not None:
918 raise error.ReadOnlyPartError('part is being generated')
918 raise error.ReadOnlyPartError('part is being generated')
919 if name in self._seenparams:
919 if name in self._seenparams:
920 raise ValueError('duplicated params: %s' % name)
920 raise ValueError('duplicated params: %s' % name)
921 self._seenparams.add(name)
921 self._seenparams.add(name)
922 params = self._advisoryparams
922 params = self._advisoryparams
923 if mandatory:
923 if mandatory:
924 params = self._mandatoryparams
924 params = self._mandatoryparams
925 params.append((name, value))
925 params.append((name, value))
926
926
927 # methods used to generates the bundle2 stream
927 # methods used to generates the bundle2 stream
928 def getchunks(self, ui):
928 def getchunks(self, ui):
929 if self._generated is not None:
929 if self._generated is not None:
930 raise error.ProgrammingError('part can only be consumed once')
930 raise error.ProgrammingError('part can only be consumed once')
931 self._generated = False
931 self._generated = False
932
932
933 if ui.debugflag:
933 if ui.debugflag:
934 msg = ['bundle2-output-part: "%s"' % self.type]
934 msg = ['bundle2-output-part: "%s"' % self.type]
935 if not self.mandatory:
935 if not self.mandatory:
936 msg.append(' (advisory)')
936 msg.append(' (advisory)')
937 nbmp = len(self.mandatoryparams)
937 nbmp = len(self.mandatoryparams)
938 nbap = len(self.advisoryparams)
938 nbap = len(self.advisoryparams)
939 if nbmp or nbap:
939 if nbmp or nbap:
940 msg.append(' (params:')
940 msg.append(' (params:')
941 if nbmp:
941 if nbmp:
942 msg.append(' %i mandatory' % nbmp)
942 msg.append(' %i mandatory' % nbmp)
943 if nbap:
943 if nbap:
944 msg.append(' %i advisory' % nbmp)
944 msg.append(' %i advisory' % nbmp)
945 msg.append(')')
945 msg.append(')')
946 if not self.data:
946 if not self.data:
947 msg.append(' empty payload')
947 msg.append(' empty payload')
948 elif util.safehasattr(self.data, 'next'):
948 elif util.safehasattr(self.data, 'next'):
949 msg.append(' streamed payload')
949 msg.append(' streamed payload')
950 else:
950 else:
951 msg.append(' %i bytes payload' % len(self.data))
951 msg.append(' %i bytes payload' % len(self.data))
952 msg.append('\n')
952 msg.append('\n')
953 ui.debug(''.join(msg))
953 ui.debug(''.join(msg))
954
954
955 #### header
955 #### header
956 if self.mandatory:
956 if self.mandatory:
957 parttype = self.type.upper()
957 parttype = self.type.upper()
958 else:
958 else:
959 parttype = self.type.lower()
959 parttype = self.type.lower()
960 outdebug(ui, 'part %s: "%s"' % (self.id, parttype))
960 outdebug(ui, 'part %s: "%s"' % (self.id, parttype))
961 ## parttype
961 ## parttype
962 header = [_pack(_fparttypesize, len(parttype)),
962 header = [_pack(_fparttypesize, len(parttype)),
963 parttype, _pack(_fpartid, self.id),
963 parttype, _pack(_fpartid, self.id),
964 ]
964 ]
965 ## parameters
965 ## parameters
966 # count
966 # count
967 manpar = self.mandatoryparams
967 manpar = self.mandatoryparams
968 advpar = self.advisoryparams
968 advpar = self.advisoryparams
969 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
969 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
970 # size
970 # size
971 parsizes = []
971 parsizes = []
972 for key, value in manpar:
972 for key, value in manpar:
973 parsizes.append(len(key))
973 parsizes.append(len(key))
974 parsizes.append(len(value))
974 parsizes.append(len(value))
975 for key, value in advpar:
975 for key, value in advpar:
976 parsizes.append(len(key))
976 parsizes.append(len(key))
977 parsizes.append(len(value))
977 parsizes.append(len(value))
978 paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes)
978 paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes)
979 header.append(paramsizes)
979 header.append(paramsizes)
980 # key, value
980 # key, value
981 for key, value in manpar:
981 for key, value in manpar:
982 header.append(key)
982 header.append(key)
983 header.append(value)
983 header.append(value)
984 for key, value in advpar:
984 for key, value in advpar:
985 header.append(key)
985 header.append(key)
986 header.append(value)
986 header.append(value)
987 ## finalize header
987 ## finalize header
988 headerchunk = ''.join(header)
988 headerchunk = ''.join(header)
989 outdebug(ui, 'header chunk size: %i' % len(headerchunk))
989 outdebug(ui, 'header chunk size: %i' % len(headerchunk))
990 yield _pack(_fpartheadersize, len(headerchunk))
990 yield _pack(_fpartheadersize, len(headerchunk))
991 yield headerchunk
991 yield headerchunk
992 ## payload
992 ## payload
993 try:
993 try:
994 for chunk in self._payloadchunks():
994 for chunk in self._payloadchunks():
995 outdebug(ui, 'payload chunk size: %i' % len(chunk))
995 outdebug(ui, 'payload chunk size: %i' % len(chunk))
996 yield _pack(_fpayloadsize, len(chunk))
996 yield _pack(_fpayloadsize, len(chunk))
997 yield chunk
997 yield chunk
998 except GeneratorExit:
998 except GeneratorExit:
999 # GeneratorExit means that nobody is listening for our
999 # GeneratorExit means that nobody is listening for our
1000 # results anyway, so just bail quickly rather than trying
1000 # results anyway, so just bail quickly rather than trying
1001 # to produce an error part.
1001 # to produce an error part.
1002 ui.debug('bundle2-generatorexit\n')
1002 ui.debug('bundle2-generatorexit\n')
1003 raise
1003 raise
1004 except BaseException as exc:
1004 except BaseException as exc:
1005 # backup exception data for later
1005 # backup exception data for later
1006 ui.debug('bundle2-input-stream-interrupt: encoding exception %s'
1006 ui.debug('bundle2-input-stream-interrupt: encoding exception %s'
1007 % exc)
1007 % exc)
1008 tb = sys.exc_info()[2]
1008 tb = sys.exc_info()[2]
1009 msg = 'unexpected error: %s' % exc
1009 msg = 'unexpected error: %s' % exc
1010 interpart = bundlepart('error:abort', [('message', msg)],
1010 interpart = bundlepart('error:abort', [('message', msg)],
1011 mandatory=False)
1011 mandatory=False)
1012 interpart.id = 0
1012 interpart.id = 0
1013 yield _pack(_fpayloadsize, -1)
1013 yield _pack(_fpayloadsize, -1)
1014 for chunk in interpart.getchunks(ui=ui):
1014 for chunk in interpart.getchunks(ui=ui):
1015 yield chunk
1015 yield chunk
1016 outdebug(ui, 'closing payload chunk')
1016 outdebug(ui, 'closing payload chunk')
1017 # abort current part payload
1017 # abort current part payload
1018 yield _pack(_fpayloadsize, 0)
1018 yield _pack(_fpayloadsize, 0)
1019 pycompat.raisewithtb(exc, tb)
1019 pycompat.raisewithtb(exc, tb)
1020 # end of payload
1020 # end of payload
1021 outdebug(ui, 'closing payload chunk')
1021 outdebug(ui, 'closing payload chunk')
1022 yield _pack(_fpayloadsize, 0)
1022 yield _pack(_fpayloadsize, 0)
1023 self._generated = True
1023 self._generated = True
1024
1024
1025 def _payloadchunks(self):
1025 def _payloadchunks(self):
1026 """yield chunks of a the part payload
1026 """yield chunks of a the part payload
1027
1027
1028 Exists to handle the different methods to provide data to a part."""
1028 Exists to handle the different methods to provide data to a part."""
1029 # we only support fixed size data now.
1029 # we only support fixed size data now.
1030 # This will be improved in the future.
1030 # This will be improved in the future.
1031 if util.safehasattr(self.data, 'next'):
1031 if util.safehasattr(self.data, 'next'):
1032 buff = util.chunkbuffer(self.data)
1032 buff = util.chunkbuffer(self.data)
1033 chunk = buff.read(preferedchunksize)
1033 chunk = buff.read(preferedchunksize)
1034 while chunk:
1034 while chunk:
1035 yield chunk
1035 yield chunk
1036 chunk = buff.read(preferedchunksize)
1036 chunk = buff.read(preferedchunksize)
1037 elif len(self.data):
1037 elif len(self.data):
1038 yield self.data
1038 yield self.data
1039
1039
1040
1040
1041 flaginterrupt = -1
1041 flaginterrupt = -1
1042
1042
1043 class interrupthandler(unpackermixin):
1043 class interrupthandler(unpackermixin):
1044 """read one part and process it with restricted capability
1044 """read one part and process it with restricted capability
1045
1045
1046 This allows to transmit exception raised on the producer size during part
1046 This allows to transmit exception raised on the producer size during part
1047 iteration while the consumer is reading a part.
1047 iteration while the consumer is reading a part.
1048
1048
1049 Part processed in this manner only have access to a ui object,"""
1049 Part processed in this manner only have access to a ui object,"""
1050
1050
1051 def __init__(self, ui, fp):
1051 def __init__(self, ui, fp):
1052 super(interrupthandler, self).__init__(fp)
1052 super(interrupthandler, self).__init__(fp)
1053 self.ui = ui
1053 self.ui = ui
1054
1054
1055 def _readpartheader(self):
1055 def _readpartheader(self):
1056 """reads a part header size and return the bytes blob
1056 """reads a part header size and return the bytes blob
1057
1057
1058 returns None if empty"""
1058 returns None if empty"""
1059 headersize = self._unpack(_fpartheadersize)[0]
1059 headersize = self._unpack(_fpartheadersize)[0]
1060 if headersize < 0:
1060 if headersize < 0:
1061 raise error.BundleValueError('negative part header size: %i'
1061 raise error.BundleValueError('negative part header size: %i'
1062 % headersize)
1062 % headersize)
1063 indebug(self.ui, 'part header size: %i\n' % headersize)
1063 indebug(self.ui, 'part header size: %i\n' % headersize)
1064 if headersize:
1064 if headersize:
1065 return self._readexact(headersize)
1065 return self._readexact(headersize)
1066 return None
1066 return None
1067
1067
1068 def __call__(self):
1068 def __call__(self):
1069
1069
1070 self.ui.debug('bundle2-input-stream-interrupt:'
1070 self.ui.debug('bundle2-input-stream-interrupt:'
1071 ' opening out of band context\n')
1071 ' opening out of band context\n')
1072 indebug(self.ui, 'bundle2 stream interruption, looking for a part.')
1072 indebug(self.ui, 'bundle2 stream interruption, looking for a part.')
1073 headerblock = self._readpartheader()
1073 headerblock = self._readpartheader()
1074 if headerblock is None:
1074 if headerblock is None:
1075 indebug(self.ui, 'no part found during interruption.')
1075 indebug(self.ui, 'no part found during interruption.')
1076 return
1076 return
1077 part = unbundlepart(self.ui, headerblock, self._fp)
1077 part = unbundlepart(self.ui, headerblock, self._fp)
1078 op = interruptoperation(self.ui)
1078 op = interruptoperation(self.ui)
1079 _processpart(op, part)
1079 _processpart(op, part)
1080 self.ui.debug('bundle2-input-stream-interrupt:'
1080 self.ui.debug('bundle2-input-stream-interrupt:'
1081 ' closing out of band context\n')
1081 ' closing out of band context\n')
1082
1082
1083 class interruptoperation(object):
1083 class interruptoperation(object):
1084 """A limited operation to be use by part handler during interruption
1084 """A limited operation to be use by part handler during interruption
1085
1085
1086 It only have access to an ui object.
1086 It only have access to an ui object.
1087 """
1087 """
1088
1088
1089 def __init__(self, ui):
1089 def __init__(self, ui):
1090 self.ui = ui
1090 self.ui = ui
1091 self.reply = None
1091 self.reply = None
1092 self.captureoutput = False
1092 self.captureoutput = False
1093
1093
1094 @property
1094 @property
1095 def repo(self):
1095 def repo(self):
1096 raise error.ProgrammingError('no repo access from stream interruption')
1096 raise error.ProgrammingError('no repo access from stream interruption')
1097
1097
1098 def gettransaction(self):
1098 def gettransaction(self):
1099 raise TransactionUnavailable('no repo access from stream interruption')
1099 raise TransactionUnavailable('no repo access from stream interruption')
1100
1100
1101 class unbundlepart(unpackermixin):
1101 class unbundlepart(unpackermixin):
1102 """a bundle part read from a bundle"""
1102 """a bundle part read from a bundle"""
1103
1103
1104 def __init__(self, ui, header, fp):
1104 def __init__(self, ui, header, fp):
1105 super(unbundlepart, self).__init__(fp)
1105 super(unbundlepart, self).__init__(fp)
1106 self._seekable = (util.safehasattr(fp, 'seek') and
1106 self._seekable = (util.safehasattr(fp, 'seek') and
1107 util.safehasattr(fp, 'tell'))
1107 util.safehasattr(fp, 'tell'))
1108 self.ui = ui
1108 self.ui = ui
1109 # unbundle state attr
1109 # unbundle state attr
1110 self._headerdata = header
1110 self._headerdata = header
1111 self._headeroffset = 0
1111 self._headeroffset = 0
1112 self._initialized = False
1112 self._initialized = False
1113 self.consumed = False
1113 self.consumed = False
1114 # part data
1114 # part data
1115 self.id = None
1115 self.id = None
1116 self.type = None
1116 self.type = None
1117 self.mandatoryparams = None
1117 self.mandatoryparams = None
1118 self.advisoryparams = None
1118 self.advisoryparams = None
1119 self.params = None
1119 self.params = None
1120 self.mandatorykeys = ()
1120 self.mandatorykeys = ()
1121 self._payloadstream = None
1121 self._payloadstream = None
1122 self._readheader()
1122 self._readheader()
1123 self._mandatory = None
1123 self._mandatory = None
1124 self._chunkindex = [] #(payload, file) position tuples for chunk starts
1124 self._chunkindex = [] #(payload, file) position tuples for chunk starts
1125 self._pos = 0
1125 self._pos = 0
1126
1126
1127 def _fromheader(self, size):
1127 def _fromheader(self, size):
1128 """return the next <size> byte from the header"""
1128 """return the next <size> byte from the header"""
1129 offset = self._headeroffset
1129 offset = self._headeroffset
1130 data = self._headerdata[offset:(offset + size)]
1130 data = self._headerdata[offset:(offset + size)]
1131 self._headeroffset = offset + size
1131 self._headeroffset = offset + size
1132 return data
1132 return data
1133
1133
1134 def _unpackheader(self, format):
1134 def _unpackheader(self, format):
1135 """read given format from header
1135 """read given format from header
1136
1136
1137 This automatically compute the size of the format to read."""
1137 This automatically compute the size of the format to read."""
1138 data = self._fromheader(struct.calcsize(format))
1138 data = self._fromheader(struct.calcsize(format))
1139 return _unpack(format, data)
1139 return _unpack(format, data)
1140
1140
1141 def _initparams(self, mandatoryparams, advisoryparams):
1141 def _initparams(self, mandatoryparams, advisoryparams):
1142 """internal function to setup all logic related parameters"""
1142 """internal function to setup all logic related parameters"""
1143 # make it read only to prevent people touching it by mistake.
1143 # make it read only to prevent people touching it by mistake.
1144 self.mandatoryparams = tuple(mandatoryparams)
1144 self.mandatoryparams = tuple(mandatoryparams)
1145 self.advisoryparams = tuple(advisoryparams)
1145 self.advisoryparams = tuple(advisoryparams)
1146 # user friendly UI
1146 # user friendly UI
1147 self.params = util.sortdict(self.mandatoryparams)
1147 self.params = util.sortdict(self.mandatoryparams)
1148 self.params.update(self.advisoryparams)
1148 self.params.update(self.advisoryparams)
1149 self.mandatorykeys = frozenset(p[0] for p in mandatoryparams)
1149 self.mandatorykeys = frozenset(p[0] for p in mandatoryparams)
1150
1150
1151 def _payloadchunks(self, chunknum=0):
1151 def _payloadchunks(self, chunknum=0):
1152 '''seek to specified chunk and start yielding data'''
1152 '''seek to specified chunk and start yielding data'''
1153 if len(self._chunkindex) == 0:
1153 if len(self._chunkindex) == 0:
1154 assert chunknum == 0, 'Must start with chunk 0'
1154 assert chunknum == 0, 'Must start with chunk 0'
1155 self._chunkindex.append((0, self._tellfp()))
1155 self._chunkindex.append((0, self._tellfp()))
1156 else:
1156 else:
1157 assert chunknum < len(self._chunkindex), \
1157 assert chunknum < len(self._chunkindex), \
1158 'Unknown chunk %d' % chunknum
1158 'Unknown chunk %d' % chunknum
1159 self._seekfp(self._chunkindex[chunknum][1])
1159 self._seekfp(self._chunkindex[chunknum][1])
1160
1160
1161 pos = self._chunkindex[chunknum][0]
1161 pos = self._chunkindex[chunknum][0]
1162 payloadsize = self._unpack(_fpayloadsize)[0]
1162 payloadsize = self._unpack(_fpayloadsize)[0]
1163 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1163 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1164 while payloadsize:
1164 while payloadsize:
1165 if payloadsize == flaginterrupt:
1165 if payloadsize == flaginterrupt:
1166 # interruption detection, the handler will now read a
1166 # interruption detection, the handler will now read a
1167 # single part and process it.
1167 # single part and process it.
1168 interrupthandler(self.ui, self._fp)()
1168 interrupthandler(self.ui, self._fp)()
1169 elif payloadsize < 0:
1169 elif payloadsize < 0:
1170 msg = 'negative payload chunk size: %i' % payloadsize
1170 msg = 'negative payload chunk size: %i' % payloadsize
1171 raise error.BundleValueError(msg)
1171 raise error.BundleValueError(msg)
1172 else:
1172 else:
1173 result = self._readexact(payloadsize)
1173 result = self._readexact(payloadsize)
1174 chunknum += 1
1174 chunknum += 1
1175 pos += payloadsize
1175 pos += payloadsize
1176 if chunknum == len(self._chunkindex):
1176 if chunknum == len(self._chunkindex):
1177 self._chunkindex.append((pos, self._tellfp()))
1177 self._chunkindex.append((pos, self._tellfp()))
1178 yield result
1178 yield result
1179 payloadsize = self._unpack(_fpayloadsize)[0]
1179 payloadsize = self._unpack(_fpayloadsize)[0]
1180 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1180 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1181
1181
1182 def _findchunk(self, pos):
1182 def _findchunk(self, pos):
1183 '''for a given payload position, return a chunk number and offset'''
1183 '''for a given payload position, return a chunk number and offset'''
1184 for chunk, (ppos, fpos) in enumerate(self._chunkindex):
1184 for chunk, (ppos, fpos) in enumerate(self._chunkindex):
1185 if ppos == pos:
1185 if ppos == pos:
1186 return chunk, 0
1186 return chunk, 0
1187 elif ppos > pos:
1187 elif ppos > pos:
1188 return chunk - 1, pos - self._chunkindex[chunk - 1][0]
1188 return chunk - 1, pos - self._chunkindex[chunk - 1][0]
1189 raise ValueError('Unknown chunk')
1189 raise ValueError('Unknown chunk')
1190
1190
1191 def _readheader(self):
1191 def _readheader(self):
1192 """read the header and setup the object"""
1192 """read the header and setup the object"""
1193 typesize = self._unpackheader(_fparttypesize)[0]
1193 typesize = self._unpackheader(_fparttypesize)[0]
1194 self.type = self._fromheader(typesize)
1194 self.type = self._fromheader(typesize)
1195 indebug(self.ui, 'part type: "%s"' % self.type)
1195 indebug(self.ui, 'part type: "%s"' % self.type)
1196 self.id = self._unpackheader(_fpartid)[0]
1196 self.id = self._unpackheader(_fpartid)[0]
1197 indebug(self.ui, 'part id: "%s"' % self.id)
1197 indebug(self.ui, 'part id: "%s"' % self.id)
1198 # extract mandatory bit from type
1198 # extract mandatory bit from type
1199 self.mandatory = (self.type != self.type.lower())
1199 self.mandatory = (self.type != self.type.lower())
1200 self.type = self.type.lower()
1200 self.type = self.type.lower()
1201 ## reading parameters
1201 ## reading parameters
1202 # param count
1202 # param count
1203 mancount, advcount = self._unpackheader(_fpartparamcount)
1203 mancount, advcount = self._unpackheader(_fpartparamcount)
1204 indebug(self.ui, 'part parameters: %i' % (mancount + advcount))
1204 indebug(self.ui, 'part parameters: %i' % (mancount + advcount))
1205 # param size
1205 # param size
1206 fparamsizes = _makefpartparamsizes(mancount + advcount)
1206 fparamsizes = _makefpartparamsizes(mancount + advcount)
1207 paramsizes = self._unpackheader(fparamsizes)
1207 paramsizes = self._unpackheader(fparamsizes)
1208 # make it a list of couple again
1208 # make it a list of couple again
1209 paramsizes = zip(paramsizes[::2], paramsizes[1::2])
1209 paramsizes = zip(paramsizes[::2], paramsizes[1::2])
1210 # split mandatory from advisory
1210 # split mandatory from advisory
1211 mansizes = paramsizes[:mancount]
1211 mansizes = paramsizes[:mancount]
1212 advsizes = paramsizes[mancount:]
1212 advsizes = paramsizes[mancount:]
1213 # retrieve param value
1213 # retrieve param value
1214 manparams = []
1214 manparams = []
1215 for key, value in mansizes:
1215 for key, value in mansizes:
1216 manparams.append((self._fromheader(key), self._fromheader(value)))
1216 manparams.append((self._fromheader(key), self._fromheader(value)))
1217 advparams = []
1217 advparams = []
1218 for key, value in advsizes:
1218 for key, value in advsizes:
1219 advparams.append((self._fromheader(key), self._fromheader(value)))
1219 advparams.append((self._fromheader(key), self._fromheader(value)))
1220 self._initparams(manparams, advparams)
1220 self._initparams(manparams, advparams)
1221 ## part payload
1221 ## part payload
1222 self._payloadstream = util.chunkbuffer(self._payloadchunks())
1222 self._payloadstream = util.chunkbuffer(self._payloadchunks())
1223 # we read the data, tell it
1223 # we read the data, tell it
1224 self._initialized = True
1224 self._initialized = True
1225
1225
1226 def read(self, size=None):
1226 def read(self, size=None):
1227 """read payload data"""
1227 """read payload data"""
1228 if not self._initialized:
1228 if not self._initialized:
1229 self._readheader()
1229 self._readheader()
1230 if size is None:
1230 if size is None:
1231 data = self._payloadstream.read()
1231 data = self._payloadstream.read()
1232 else:
1232 else:
1233 data = self._payloadstream.read(size)
1233 data = self._payloadstream.read(size)
1234 self._pos += len(data)
1234 self._pos += len(data)
1235 if size is None or len(data) < size:
1235 if size is None or len(data) < size:
1236 if not self.consumed and self._pos:
1236 if not self.consumed and self._pos:
1237 self.ui.debug('bundle2-input-part: total payload size %i\n'
1237 self.ui.debug('bundle2-input-part: total payload size %i\n'
1238 % self._pos)
1238 % self._pos)
1239 self.consumed = True
1239 self.consumed = True
1240 return data
1240 return data
1241
1241
1242 def tell(self):
1242 def tell(self):
1243 return self._pos
1243 return self._pos
1244
1244
1245 def seek(self, offset, whence=0):
1245 def seek(self, offset, whence=0):
1246 if whence == 0:
1246 if whence == 0:
1247 newpos = offset
1247 newpos = offset
1248 elif whence == 1:
1248 elif whence == 1:
1249 newpos = self._pos + offset
1249 newpos = self._pos + offset
1250 elif whence == 2:
1250 elif whence == 2:
1251 if not self.consumed:
1251 if not self.consumed:
1252 self.read()
1252 self.read()
1253 newpos = self._chunkindex[-1][0] - offset
1253 newpos = self._chunkindex[-1][0] - offset
1254 else:
1254 else:
1255 raise ValueError('Unknown whence value: %r' % (whence,))
1255 raise ValueError('Unknown whence value: %r' % (whence,))
1256
1256
1257 if newpos > self._chunkindex[-1][0] and not self.consumed:
1257 if newpos > self._chunkindex[-1][0] and not self.consumed:
1258 self.read()
1258 self.read()
1259 if not 0 <= newpos <= self._chunkindex[-1][0]:
1259 if not 0 <= newpos <= self._chunkindex[-1][0]:
1260 raise ValueError('Offset out of range')
1260 raise ValueError('Offset out of range')
1261
1261
1262 if self._pos != newpos:
1262 if self._pos != newpos:
1263 chunk, internaloffset = self._findchunk(newpos)
1263 chunk, internaloffset = self._findchunk(newpos)
1264 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk))
1264 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk))
1265 adjust = self.read(internaloffset)
1265 adjust = self.read(internaloffset)
1266 if len(adjust) != internaloffset:
1266 if len(adjust) != internaloffset:
1267 raise error.Abort(_('Seek failed\n'))
1267 raise error.Abort(_('Seek failed\n'))
1268 self._pos = newpos
1268 self._pos = newpos
1269
1269
1270 def _seekfp(self, offset, whence=0):
1270 def _seekfp(self, offset, whence=0):
1271 """move the underlying file pointer
1271 """move the underlying file pointer
1272
1272
1273 This method is meant for internal usage by the bundle2 protocol only.
1273 This method is meant for internal usage by the bundle2 protocol only.
1274 They directly manipulate the low level stream including bundle2 level
1274 They directly manipulate the low level stream including bundle2 level
1275 instruction.
1275 instruction.
1276
1276
1277 Do not use it to implement higher-level logic or methods."""
1277 Do not use it to implement higher-level logic or methods."""
1278 if self._seekable:
1278 if self._seekable:
1279 return self._fp.seek(offset, whence)
1279 return self._fp.seek(offset, whence)
1280 else:
1280 else:
1281 raise NotImplementedError(_('File pointer is not seekable'))
1281 raise NotImplementedError(_('File pointer is not seekable'))
1282
1282
1283 def _tellfp(self):
1283 def _tellfp(self):
1284 """return the file offset, or None if file is not seekable
1284 """return the file offset, or None if file is not seekable
1285
1285
1286 This method is meant for internal usage by the bundle2 protocol only.
1286 This method is meant for internal usage by the bundle2 protocol only.
1287 They directly manipulate the low level stream including bundle2 level
1287 They directly manipulate the low level stream including bundle2 level
1288 instruction.
1288 instruction.
1289
1289
1290 Do not use it to implement higher-level logic or methods."""
1290 Do not use it to implement higher-level logic or methods."""
1291 if self._seekable:
1291 if self._seekable:
1292 try:
1292 try:
1293 return self._fp.tell()
1293 return self._fp.tell()
1294 except IOError as e:
1294 except IOError as e:
1295 if e.errno == errno.ESPIPE:
1295 if e.errno == errno.ESPIPE:
1296 self._seekable = False
1296 self._seekable = False
1297 else:
1297 else:
1298 raise
1298 raise
1299 return None
1299 return None
1300
1300
1301 # These are only the static capabilities.
1301 # These are only the static capabilities.
1302 # Check the 'getrepocaps' function for the rest.
1302 # Check the 'getrepocaps' function for the rest.
1303 capabilities = {'HG20': (),
1303 capabilities = {'HG20': (),
1304 'error': ('abort', 'unsupportedcontent', 'pushraced',
1304 'error': ('abort', 'unsupportedcontent', 'pushraced',
1305 'pushkey'),
1305 'pushkey'),
1306 'listkeys': (),
1306 'listkeys': (),
1307 'pushkey': (),
1307 'pushkey': (),
1308 'digests': tuple(sorted(util.DIGESTS.keys())),
1308 'digests': tuple(sorted(util.DIGESTS.keys())),
1309 'remote-changegroup': ('http', 'https'),
1309 'remote-changegroup': ('http', 'https'),
1310 'hgtagsfnodes': (),
1310 'hgtagsfnodes': (),
1311 }
1311 }
1312
1312
1313 def getrepocaps(repo, allowpushback=False):
1313 def getrepocaps(repo, allowpushback=False):
1314 """return the bundle2 capabilities for a given repo
1314 """return the bundle2 capabilities for a given repo
1315
1315
1316 Exists to allow extensions (like evolution) to mutate the capabilities.
1316 Exists to allow extensions (like evolution) to mutate the capabilities.
1317 """
1317 """
1318 caps = capabilities.copy()
1318 caps = capabilities.copy()
1319 caps['changegroup'] = tuple(sorted(
1319 caps['changegroup'] = tuple(sorted(
1320 changegroup.supportedincomingversions(repo)))
1320 changegroup.supportedincomingversions(repo)))
1321 if obsolete.isenabled(repo, obsolete.exchangeopt):
1321 if obsolete.isenabled(repo, obsolete.exchangeopt):
1322 supportedformat = tuple('V%i' % v for v in obsolete.formats)
1322 supportedformat = tuple('V%i' % v for v in obsolete.formats)
1323 caps['obsmarkers'] = supportedformat
1323 caps['obsmarkers'] = supportedformat
1324 if allowpushback:
1324 if allowpushback:
1325 caps['pushback'] = ()
1325 caps['pushback'] = ()
1326 return caps
1326 return caps
1327
1327
1328 def bundle2caps(remote):
1328 def bundle2caps(remote):
1329 """return the bundle capabilities of a peer as dict"""
1329 """return the bundle capabilities of a peer as dict"""
1330 raw = remote.capable('bundle2')
1330 raw = remote.capable('bundle2')
1331 if not raw and raw != '':
1331 if not raw and raw != '':
1332 return {}
1332 return {}
1333 capsblob = urlreq.unquote(remote.capable('bundle2'))
1333 capsblob = urlreq.unquote(remote.capable('bundle2'))
1334 return decodecaps(capsblob)
1334 return decodecaps(capsblob)
1335
1335
1336 def obsmarkersversion(caps):
1336 def obsmarkersversion(caps):
1337 """extract the list of supported obsmarkers versions from a bundle2caps dict
1337 """extract the list of supported obsmarkers versions from a bundle2caps dict
1338 """
1338 """
1339 obscaps = caps.get('obsmarkers', ())
1339 obscaps = caps.get('obsmarkers', ())
1340 return [int(c[1:]) for c in obscaps if c.startswith('V')]
1340 return [int(c[1:]) for c in obscaps if c.startswith('V')]
1341
1341
1342 def writenewbundle(ui, repo, source, filename, bundletype, outgoing, opts,
1343 vfs=None, compression=None, compopts=None):
1344 if bundletype.startswith('HG10'):
1345 cg = changegroup.getchangegroup(repo, source, outgoing, version='01')
1346 return writebundle(ui, cg, filename, bundletype, vfs=vfs,
1347 compression=compression, compopts=compopts)
1348 elif not bundletype.startswith('HG20'):
1349 raise error.ProgrammingError('unknown bundle type: %s' % bundletype)
1350
1351 bundle = bundle20(ui)
1352 bundle.setcompression(compression, compopts)
1353 _addpartsfromopts(ui, repo, bundle, source, outgoing, opts)
1354 chunkiter = bundle.getchunks()
1355
1356 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1357
1358 def _addpartsfromopts(ui, repo, bundler, source, outgoing, opts):
1359 # We should eventually reconcile this logic with the one behind
1360 # 'exchange.getbundle2partsgenerator'.
1361 #
1362 # The type of input from 'getbundle' and 'writenewbundle' are a bit
1363 # different right now. So we keep them separated for now for the sake of
1364 # simplicity.
1365
1366 # we always want a changegroup in such bundle
1367 cgversion = opts.get('cg.version')
1368 if cgversion is None:
1369 cgversion = changegroup.safeversion(repo)
1370 cg = changegroup.getchangegroup(repo, source, outgoing,
1371 version=cgversion)
1372 part = bundler.newpart('changegroup', data=cg.getchunks())
1373 part.addparam('version', cg.version)
1374 if 'clcount' in cg.extras:
1375 part.addparam('nbchanges', str(cg.extras['clcount']),
1376 mandatory=False)
1377
1342 def writebundle(ui, cg, filename, bundletype, vfs=None, compression=None,
1378 def writebundle(ui, cg, filename, bundletype, vfs=None, compression=None,
1343 compopts=None):
1379 compopts=None):
1344 """Write a bundle file and return its filename.
1380 """Write a bundle file and return its filename.
1345
1381
1346 Existing files will not be overwritten.
1382 Existing files will not be overwritten.
1347 If no filename is specified, a temporary file is created.
1383 If no filename is specified, a temporary file is created.
1348 bz2 compression can be turned off.
1384 bz2 compression can be turned off.
1349 The bundle file will be deleted in case of errors.
1385 The bundle file will be deleted in case of errors.
1350 """
1386 """
1351
1387
1352 if bundletype == "HG20":
1388 if bundletype == "HG20":
1353 bundle = bundle20(ui)
1389 bundle = bundle20(ui)
1354 bundle.setcompression(compression, compopts)
1390 bundle.setcompression(compression, compopts)
1355 part = bundle.newpart('changegroup', data=cg.getchunks())
1391 part = bundle.newpart('changegroup', data=cg.getchunks())
1356 part.addparam('version', cg.version)
1392 part.addparam('version', cg.version)
1357 if 'clcount' in cg.extras:
1393 if 'clcount' in cg.extras:
1358 part.addparam('nbchanges', str(cg.extras['clcount']),
1394 part.addparam('nbchanges', str(cg.extras['clcount']),
1359 mandatory=False)
1395 mandatory=False)
1360 chunkiter = bundle.getchunks()
1396 chunkiter = bundle.getchunks()
1361 else:
1397 else:
1362 # compression argument is only for the bundle2 case
1398 # compression argument is only for the bundle2 case
1363 assert compression is None
1399 assert compression is None
1364 if cg.version != '01':
1400 if cg.version != '01':
1365 raise error.Abort(_('old bundle types only supports v1 '
1401 raise error.Abort(_('old bundle types only supports v1 '
1366 'changegroups'))
1402 'changegroups'))
1367 header, comp = bundletypes[bundletype]
1403 header, comp = bundletypes[bundletype]
1368 if comp not in util.compengines.supportedbundletypes:
1404 if comp not in util.compengines.supportedbundletypes:
1369 raise error.Abort(_('unknown stream compression type: %s')
1405 raise error.Abort(_('unknown stream compression type: %s')
1370 % comp)
1406 % comp)
1371 compengine = util.compengines.forbundletype(comp)
1407 compengine = util.compengines.forbundletype(comp)
1372 def chunkiter():
1408 def chunkiter():
1373 yield header
1409 yield header
1374 for chunk in compengine.compressstream(cg.getchunks(), compopts):
1410 for chunk in compengine.compressstream(cg.getchunks(), compopts):
1375 yield chunk
1411 yield chunk
1376 chunkiter = chunkiter()
1412 chunkiter = chunkiter()
1377
1413
1378 # parse the changegroup data, otherwise we will block
1414 # parse the changegroup data, otherwise we will block
1379 # in case of sshrepo because we don't know the end of the stream
1415 # in case of sshrepo because we don't know the end of the stream
1380 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1416 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1381
1417
1382 @parthandler('changegroup', ('version', 'nbchanges', 'treemanifest'))
1418 @parthandler('changegroup', ('version', 'nbchanges', 'treemanifest'))
1383 def handlechangegroup(op, inpart):
1419 def handlechangegroup(op, inpart):
1384 """apply a changegroup part on the repo
1420 """apply a changegroup part on the repo
1385
1421
1386 This is a very early implementation that will massive rework before being
1422 This is a very early implementation that will massive rework before being
1387 inflicted to any end-user.
1423 inflicted to any end-user.
1388 """
1424 """
1389 # Make sure we trigger a transaction creation
1425 # Make sure we trigger a transaction creation
1390 #
1426 #
1391 # The addchangegroup function will get a transaction object by itself, but
1427 # The addchangegroup function will get a transaction object by itself, but
1392 # we need to make sure we trigger the creation of a transaction object used
1428 # we need to make sure we trigger the creation of a transaction object used
1393 # for the whole processing scope.
1429 # for the whole processing scope.
1394 op.gettransaction()
1430 op.gettransaction()
1395 unpackerversion = inpart.params.get('version', '01')
1431 unpackerversion = inpart.params.get('version', '01')
1396 # We should raise an appropriate exception here
1432 # We should raise an appropriate exception here
1397 cg = changegroup.getunbundler(unpackerversion, inpart, None)
1433 cg = changegroup.getunbundler(unpackerversion, inpart, None)
1398 # the source and url passed here are overwritten by the one contained in
1434 # the source and url passed here are overwritten by the one contained in
1399 # the transaction.hookargs argument. So 'bundle2' is a placeholder
1435 # the transaction.hookargs argument. So 'bundle2' is a placeholder
1400 nbchangesets = None
1436 nbchangesets = None
1401 if 'nbchanges' in inpart.params:
1437 if 'nbchanges' in inpart.params:
1402 nbchangesets = int(inpart.params.get('nbchanges'))
1438 nbchangesets = int(inpart.params.get('nbchanges'))
1403 if ('treemanifest' in inpart.params and
1439 if ('treemanifest' in inpart.params and
1404 'treemanifest' not in op.repo.requirements):
1440 'treemanifest' not in op.repo.requirements):
1405 if len(op.repo.changelog) != 0:
1441 if len(op.repo.changelog) != 0:
1406 raise error.Abort(_(
1442 raise error.Abort(_(
1407 "bundle contains tree manifests, but local repo is "
1443 "bundle contains tree manifests, but local repo is "
1408 "non-empty and does not use tree manifests"))
1444 "non-empty and does not use tree manifests"))
1409 op.repo.requirements.add('treemanifest')
1445 op.repo.requirements.add('treemanifest')
1410 op.repo._applyopenerreqs()
1446 op.repo._applyopenerreqs()
1411 op.repo._writerequirements()
1447 op.repo._writerequirements()
1412 ret = cg.apply(op.repo, 'bundle2', 'bundle2', expectedtotal=nbchangesets)
1448 ret = cg.apply(op.repo, 'bundle2', 'bundle2', expectedtotal=nbchangesets)
1413 op.records.add('changegroup', {'return': ret})
1449 op.records.add('changegroup', {'return': ret})
1414 if op.reply is not None:
1450 if op.reply is not None:
1415 # This is definitely not the final form of this
1451 # This is definitely not the final form of this
1416 # return. But one need to start somewhere.
1452 # return. But one need to start somewhere.
1417 part = op.reply.newpart('reply:changegroup', mandatory=False)
1453 part = op.reply.newpart('reply:changegroup', mandatory=False)
1418 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1454 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1419 part.addparam('return', '%i' % ret, mandatory=False)
1455 part.addparam('return', '%i' % ret, mandatory=False)
1420 assert not inpart.read()
1456 assert not inpart.read()
1421
1457
1422 _remotechangegroupparams = tuple(['url', 'size', 'digests'] +
1458 _remotechangegroupparams = tuple(['url', 'size', 'digests'] +
1423 ['digest:%s' % k for k in util.DIGESTS.keys()])
1459 ['digest:%s' % k for k in util.DIGESTS.keys()])
1424 @parthandler('remote-changegroup', _remotechangegroupparams)
1460 @parthandler('remote-changegroup', _remotechangegroupparams)
1425 def handleremotechangegroup(op, inpart):
1461 def handleremotechangegroup(op, inpart):
1426 """apply a bundle10 on the repo, given an url and validation information
1462 """apply a bundle10 on the repo, given an url and validation information
1427
1463
1428 All the information about the remote bundle to import are given as
1464 All the information about the remote bundle to import are given as
1429 parameters. The parameters include:
1465 parameters. The parameters include:
1430 - url: the url to the bundle10.
1466 - url: the url to the bundle10.
1431 - size: the bundle10 file size. It is used to validate what was
1467 - size: the bundle10 file size. It is used to validate what was
1432 retrieved by the client matches the server knowledge about the bundle.
1468 retrieved by the client matches the server knowledge about the bundle.
1433 - digests: a space separated list of the digest types provided as
1469 - digests: a space separated list of the digest types provided as
1434 parameters.
1470 parameters.
1435 - digest:<digest-type>: the hexadecimal representation of the digest with
1471 - digest:<digest-type>: the hexadecimal representation of the digest with
1436 that name. Like the size, it is used to validate what was retrieved by
1472 that name. Like the size, it is used to validate what was retrieved by
1437 the client matches what the server knows about the bundle.
1473 the client matches what the server knows about the bundle.
1438
1474
1439 When multiple digest types are given, all of them are checked.
1475 When multiple digest types are given, all of them are checked.
1440 """
1476 """
1441 try:
1477 try:
1442 raw_url = inpart.params['url']
1478 raw_url = inpart.params['url']
1443 except KeyError:
1479 except KeyError:
1444 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'url')
1480 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'url')
1445 parsed_url = util.url(raw_url)
1481 parsed_url = util.url(raw_url)
1446 if parsed_url.scheme not in capabilities['remote-changegroup']:
1482 if parsed_url.scheme not in capabilities['remote-changegroup']:
1447 raise error.Abort(_('remote-changegroup does not support %s urls') %
1483 raise error.Abort(_('remote-changegroup does not support %s urls') %
1448 parsed_url.scheme)
1484 parsed_url.scheme)
1449
1485
1450 try:
1486 try:
1451 size = int(inpart.params['size'])
1487 size = int(inpart.params['size'])
1452 except ValueError:
1488 except ValueError:
1453 raise error.Abort(_('remote-changegroup: invalid value for param "%s"')
1489 raise error.Abort(_('remote-changegroup: invalid value for param "%s"')
1454 % 'size')
1490 % 'size')
1455 except KeyError:
1491 except KeyError:
1456 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'size')
1492 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'size')
1457
1493
1458 digests = {}
1494 digests = {}
1459 for typ in inpart.params.get('digests', '').split():
1495 for typ in inpart.params.get('digests', '').split():
1460 param = 'digest:%s' % typ
1496 param = 'digest:%s' % typ
1461 try:
1497 try:
1462 value = inpart.params[param]
1498 value = inpart.params[param]
1463 except KeyError:
1499 except KeyError:
1464 raise error.Abort(_('remote-changegroup: missing "%s" param') %
1500 raise error.Abort(_('remote-changegroup: missing "%s" param') %
1465 param)
1501 param)
1466 digests[typ] = value
1502 digests[typ] = value
1467
1503
1468 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests)
1504 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests)
1469
1505
1470 # Make sure we trigger a transaction creation
1506 # Make sure we trigger a transaction creation
1471 #
1507 #
1472 # The addchangegroup function will get a transaction object by itself, but
1508 # The addchangegroup function will get a transaction object by itself, but
1473 # we need to make sure we trigger the creation of a transaction object used
1509 # we need to make sure we trigger the creation of a transaction object used
1474 # for the whole processing scope.
1510 # for the whole processing scope.
1475 op.gettransaction()
1511 op.gettransaction()
1476 from . import exchange
1512 from . import exchange
1477 cg = exchange.readbundle(op.repo.ui, real_part, raw_url)
1513 cg = exchange.readbundle(op.repo.ui, real_part, raw_url)
1478 if not isinstance(cg, changegroup.cg1unpacker):
1514 if not isinstance(cg, changegroup.cg1unpacker):
1479 raise error.Abort(_('%s: not a bundle version 1.0') %
1515 raise error.Abort(_('%s: not a bundle version 1.0') %
1480 util.hidepassword(raw_url))
1516 util.hidepassword(raw_url))
1481 ret = cg.apply(op.repo, 'bundle2', 'bundle2')
1517 ret = cg.apply(op.repo, 'bundle2', 'bundle2')
1482 op.records.add('changegroup', {'return': ret})
1518 op.records.add('changegroup', {'return': ret})
1483 if op.reply is not None:
1519 if op.reply is not None:
1484 # This is definitely not the final form of this
1520 # This is definitely not the final form of this
1485 # return. But one need to start somewhere.
1521 # return. But one need to start somewhere.
1486 part = op.reply.newpart('reply:changegroup')
1522 part = op.reply.newpart('reply:changegroup')
1487 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1523 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1488 part.addparam('return', '%i' % ret, mandatory=False)
1524 part.addparam('return', '%i' % ret, mandatory=False)
1489 try:
1525 try:
1490 real_part.validate()
1526 real_part.validate()
1491 except error.Abort as e:
1527 except error.Abort as e:
1492 raise error.Abort(_('bundle at %s is corrupted:\n%s') %
1528 raise error.Abort(_('bundle at %s is corrupted:\n%s') %
1493 (util.hidepassword(raw_url), str(e)))
1529 (util.hidepassword(raw_url), str(e)))
1494 assert not inpart.read()
1530 assert not inpart.read()
1495
1531
1496 @parthandler('reply:changegroup', ('return', 'in-reply-to'))
1532 @parthandler('reply:changegroup', ('return', 'in-reply-to'))
1497 def handlereplychangegroup(op, inpart):
1533 def handlereplychangegroup(op, inpart):
1498 ret = int(inpart.params['return'])
1534 ret = int(inpart.params['return'])
1499 replyto = int(inpart.params['in-reply-to'])
1535 replyto = int(inpart.params['in-reply-to'])
1500 op.records.add('changegroup', {'return': ret}, replyto)
1536 op.records.add('changegroup', {'return': ret}, replyto)
1501
1537
1502 @parthandler('check:heads')
1538 @parthandler('check:heads')
1503 def handlecheckheads(op, inpart):
1539 def handlecheckheads(op, inpart):
1504 """check that head of the repo did not change
1540 """check that head of the repo did not change
1505
1541
1506 This is used to detect a push race when using unbundle.
1542 This is used to detect a push race when using unbundle.
1507 This replaces the "heads" argument of unbundle."""
1543 This replaces the "heads" argument of unbundle."""
1508 h = inpart.read(20)
1544 h = inpart.read(20)
1509 heads = []
1545 heads = []
1510 while len(h) == 20:
1546 while len(h) == 20:
1511 heads.append(h)
1547 heads.append(h)
1512 h = inpart.read(20)
1548 h = inpart.read(20)
1513 assert not h
1549 assert not h
1514 # Trigger a transaction so that we are guaranteed to have the lock now.
1550 # Trigger a transaction so that we are guaranteed to have the lock now.
1515 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1551 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1516 op.gettransaction()
1552 op.gettransaction()
1517 if sorted(heads) != sorted(op.repo.heads()):
1553 if sorted(heads) != sorted(op.repo.heads()):
1518 raise error.PushRaced('repository changed while pushing - '
1554 raise error.PushRaced('repository changed while pushing - '
1519 'please try again')
1555 'please try again')
1520
1556
1521 @parthandler('output')
1557 @parthandler('output')
1522 def handleoutput(op, inpart):
1558 def handleoutput(op, inpart):
1523 """forward output captured on the server to the client"""
1559 """forward output captured on the server to the client"""
1524 for line in inpart.read().splitlines():
1560 for line in inpart.read().splitlines():
1525 op.ui.status(_('remote: %s\n') % line)
1561 op.ui.status(_('remote: %s\n') % line)
1526
1562
1527 @parthandler('replycaps')
1563 @parthandler('replycaps')
1528 def handlereplycaps(op, inpart):
1564 def handlereplycaps(op, inpart):
1529 """Notify that a reply bundle should be created
1565 """Notify that a reply bundle should be created
1530
1566
1531 The payload contains the capabilities information for the reply"""
1567 The payload contains the capabilities information for the reply"""
1532 caps = decodecaps(inpart.read())
1568 caps = decodecaps(inpart.read())
1533 if op.reply is None:
1569 if op.reply is None:
1534 op.reply = bundle20(op.ui, caps)
1570 op.reply = bundle20(op.ui, caps)
1535
1571
1536 class AbortFromPart(error.Abort):
1572 class AbortFromPart(error.Abort):
1537 """Sub-class of Abort that denotes an error from a bundle2 part."""
1573 """Sub-class of Abort that denotes an error from a bundle2 part."""
1538
1574
1539 @parthandler('error:abort', ('message', 'hint'))
1575 @parthandler('error:abort', ('message', 'hint'))
1540 def handleerrorabort(op, inpart):
1576 def handleerrorabort(op, inpart):
1541 """Used to transmit abort error over the wire"""
1577 """Used to transmit abort error over the wire"""
1542 raise AbortFromPart(inpart.params['message'],
1578 raise AbortFromPart(inpart.params['message'],
1543 hint=inpart.params.get('hint'))
1579 hint=inpart.params.get('hint'))
1544
1580
1545 @parthandler('error:pushkey', ('namespace', 'key', 'new', 'old', 'ret',
1581 @parthandler('error:pushkey', ('namespace', 'key', 'new', 'old', 'ret',
1546 'in-reply-to'))
1582 'in-reply-to'))
1547 def handleerrorpushkey(op, inpart):
1583 def handleerrorpushkey(op, inpart):
1548 """Used to transmit failure of a mandatory pushkey over the wire"""
1584 """Used to transmit failure of a mandatory pushkey over the wire"""
1549 kwargs = {}
1585 kwargs = {}
1550 for name in ('namespace', 'key', 'new', 'old', 'ret'):
1586 for name in ('namespace', 'key', 'new', 'old', 'ret'):
1551 value = inpart.params.get(name)
1587 value = inpart.params.get(name)
1552 if value is not None:
1588 if value is not None:
1553 kwargs[name] = value
1589 kwargs[name] = value
1554 raise error.PushkeyFailed(inpart.params['in-reply-to'], **kwargs)
1590 raise error.PushkeyFailed(inpart.params['in-reply-to'], **kwargs)
1555
1591
1556 @parthandler('error:unsupportedcontent', ('parttype', 'params'))
1592 @parthandler('error:unsupportedcontent', ('parttype', 'params'))
1557 def handleerrorunsupportedcontent(op, inpart):
1593 def handleerrorunsupportedcontent(op, inpart):
1558 """Used to transmit unknown content error over the wire"""
1594 """Used to transmit unknown content error over the wire"""
1559 kwargs = {}
1595 kwargs = {}
1560 parttype = inpart.params.get('parttype')
1596 parttype = inpart.params.get('parttype')
1561 if parttype is not None:
1597 if parttype is not None:
1562 kwargs['parttype'] = parttype
1598 kwargs['parttype'] = parttype
1563 params = inpart.params.get('params')
1599 params = inpart.params.get('params')
1564 if params is not None:
1600 if params is not None:
1565 kwargs['params'] = params.split('\0')
1601 kwargs['params'] = params.split('\0')
1566
1602
1567 raise error.BundleUnknownFeatureError(**kwargs)
1603 raise error.BundleUnknownFeatureError(**kwargs)
1568
1604
1569 @parthandler('error:pushraced', ('message',))
1605 @parthandler('error:pushraced', ('message',))
1570 def handleerrorpushraced(op, inpart):
1606 def handleerrorpushraced(op, inpart):
1571 """Used to transmit push race error over the wire"""
1607 """Used to transmit push race error over the wire"""
1572 raise error.ResponseError(_('push failed:'), inpart.params['message'])
1608 raise error.ResponseError(_('push failed:'), inpart.params['message'])
1573
1609
1574 @parthandler('listkeys', ('namespace',))
1610 @parthandler('listkeys', ('namespace',))
1575 def handlelistkeys(op, inpart):
1611 def handlelistkeys(op, inpart):
1576 """retrieve pushkey namespace content stored in a bundle2"""
1612 """retrieve pushkey namespace content stored in a bundle2"""
1577 namespace = inpart.params['namespace']
1613 namespace = inpart.params['namespace']
1578 r = pushkey.decodekeys(inpart.read())
1614 r = pushkey.decodekeys(inpart.read())
1579 op.records.add('listkeys', (namespace, r))
1615 op.records.add('listkeys', (namespace, r))
1580
1616
1581 @parthandler('pushkey', ('namespace', 'key', 'old', 'new'))
1617 @parthandler('pushkey', ('namespace', 'key', 'old', 'new'))
1582 def handlepushkey(op, inpart):
1618 def handlepushkey(op, inpart):
1583 """process a pushkey request"""
1619 """process a pushkey request"""
1584 dec = pushkey.decode
1620 dec = pushkey.decode
1585 namespace = dec(inpart.params['namespace'])
1621 namespace = dec(inpart.params['namespace'])
1586 key = dec(inpart.params['key'])
1622 key = dec(inpart.params['key'])
1587 old = dec(inpart.params['old'])
1623 old = dec(inpart.params['old'])
1588 new = dec(inpart.params['new'])
1624 new = dec(inpart.params['new'])
1589 # Grab the transaction to ensure that we have the lock before performing the
1625 # Grab the transaction to ensure that we have the lock before performing the
1590 # pushkey.
1626 # pushkey.
1591 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1627 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1592 op.gettransaction()
1628 op.gettransaction()
1593 ret = op.repo.pushkey(namespace, key, old, new)
1629 ret = op.repo.pushkey(namespace, key, old, new)
1594 record = {'namespace': namespace,
1630 record = {'namespace': namespace,
1595 'key': key,
1631 'key': key,
1596 'old': old,
1632 'old': old,
1597 'new': new}
1633 'new': new}
1598 op.records.add('pushkey', record)
1634 op.records.add('pushkey', record)
1599 if op.reply is not None:
1635 if op.reply is not None:
1600 rpart = op.reply.newpart('reply:pushkey')
1636 rpart = op.reply.newpart('reply:pushkey')
1601 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1637 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1602 rpart.addparam('return', '%i' % ret, mandatory=False)
1638 rpart.addparam('return', '%i' % ret, mandatory=False)
1603 if inpart.mandatory and not ret:
1639 if inpart.mandatory and not ret:
1604 kwargs = {}
1640 kwargs = {}
1605 for key in ('namespace', 'key', 'new', 'old', 'ret'):
1641 for key in ('namespace', 'key', 'new', 'old', 'ret'):
1606 if key in inpart.params:
1642 if key in inpart.params:
1607 kwargs[key] = inpart.params[key]
1643 kwargs[key] = inpart.params[key]
1608 raise error.PushkeyFailed(partid=str(inpart.id), **kwargs)
1644 raise error.PushkeyFailed(partid=str(inpart.id), **kwargs)
1609
1645
1610 @parthandler('reply:pushkey', ('return', 'in-reply-to'))
1646 @parthandler('reply:pushkey', ('return', 'in-reply-to'))
1611 def handlepushkeyreply(op, inpart):
1647 def handlepushkeyreply(op, inpart):
1612 """retrieve the result of a pushkey request"""
1648 """retrieve the result of a pushkey request"""
1613 ret = int(inpart.params['return'])
1649 ret = int(inpart.params['return'])
1614 partid = int(inpart.params['in-reply-to'])
1650 partid = int(inpart.params['in-reply-to'])
1615 op.records.add('pushkey', {'return': ret}, partid)
1651 op.records.add('pushkey', {'return': ret}, partid)
1616
1652
1617 @parthandler('obsmarkers')
1653 @parthandler('obsmarkers')
1618 def handleobsmarker(op, inpart):
1654 def handleobsmarker(op, inpart):
1619 """add a stream of obsmarkers to the repo"""
1655 """add a stream of obsmarkers to the repo"""
1620 tr = op.gettransaction()
1656 tr = op.gettransaction()
1621 markerdata = inpart.read()
1657 markerdata = inpart.read()
1622 if op.ui.config('experimental', 'obsmarkers-exchange-debug', False):
1658 if op.ui.config('experimental', 'obsmarkers-exchange-debug', False):
1623 op.ui.write(('obsmarker-exchange: %i bytes received\n')
1659 op.ui.write(('obsmarker-exchange: %i bytes received\n')
1624 % len(markerdata))
1660 % len(markerdata))
1625 # The mergemarkers call will crash if marker creation is not enabled.
1661 # The mergemarkers call will crash if marker creation is not enabled.
1626 # we want to avoid this if the part is advisory.
1662 # we want to avoid this if the part is advisory.
1627 if not inpart.mandatory and op.repo.obsstore.readonly:
1663 if not inpart.mandatory and op.repo.obsstore.readonly:
1628 op.repo.ui.debug('ignoring obsolescence markers, feature not enabled')
1664 op.repo.ui.debug('ignoring obsolescence markers, feature not enabled')
1629 return
1665 return
1630 new = op.repo.obsstore.mergemarkers(tr, markerdata)
1666 new = op.repo.obsstore.mergemarkers(tr, markerdata)
1631 if new:
1667 if new:
1632 op.repo.ui.status(_('%i new obsolescence markers\n') % new)
1668 op.repo.ui.status(_('%i new obsolescence markers\n') % new)
1633 op.records.add('obsmarkers', {'new': new})
1669 op.records.add('obsmarkers', {'new': new})
1634 if op.reply is not None:
1670 if op.reply is not None:
1635 rpart = op.reply.newpart('reply:obsmarkers')
1671 rpart = op.reply.newpart('reply:obsmarkers')
1636 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1672 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1637 rpart.addparam('new', '%i' % new, mandatory=False)
1673 rpart.addparam('new', '%i' % new, mandatory=False)
1638
1674
1639
1675
1640 @parthandler('reply:obsmarkers', ('new', 'in-reply-to'))
1676 @parthandler('reply:obsmarkers', ('new', 'in-reply-to'))
1641 def handleobsmarkerreply(op, inpart):
1677 def handleobsmarkerreply(op, inpart):
1642 """retrieve the result of a pushkey request"""
1678 """retrieve the result of a pushkey request"""
1643 ret = int(inpart.params['new'])
1679 ret = int(inpart.params['new'])
1644 partid = int(inpart.params['in-reply-to'])
1680 partid = int(inpart.params['in-reply-to'])
1645 op.records.add('obsmarkers', {'new': ret}, partid)
1681 op.records.add('obsmarkers', {'new': ret}, partid)
1646
1682
1647 @parthandler('hgtagsfnodes')
1683 @parthandler('hgtagsfnodes')
1648 def handlehgtagsfnodes(op, inpart):
1684 def handlehgtagsfnodes(op, inpart):
1649 """Applies .hgtags fnodes cache entries to the local repo.
1685 """Applies .hgtags fnodes cache entries to the local repo.
1650
1686
1651 Payload is pairs of 20 byte changeset nodes and filenodes.
1687 Payload is pairs of 20 byte changeset nodes and filenodes.
1652 """
1688 """
1653 # Grab the transaction so we ensure that we have the lock at this point.
1689 # Grab the transaction so we ensure that we have the lock at this point.
1654 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1690 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1655 op.gettransaction()
1691 op.gettransaction()
1656 cache = tags.hgtagsfnodescache(op.repo.unfiltered())
1692 cache = tags.hgtagsfnodescache(op.repo.unfiltered())
1657
1693
1658 count = 0
1694 count = 0
1659 while True:
1695 while True:
1660 node = inpart.read(20)
1696 node = inpart.read(20)
1661 fnode = inpart.read(20)
1697 fnode = inpart.read(20)
1662 if len(node) < 20 or len(fnode) < 20:
1698 if len(node) < 20 or len(fnode) < 20:
1663 op.ui.debug('ignoring incomplete received .hgtags fnodes data\n')
1699 op.ui.debug('ignoring incomplete received .hgtags fnodes data\n')
1664 break
1700 break
1665 cache.setfnode(node, fnode)
1701 cache.setfnode(node, fnode)
1666 count += 1
1702 count += 1
1667
1703
1668 cache.write()
1704 cache.write()
1669 op.ui.debug('applied %i hgtags fnodes cache entries\n' % count)
1705 op.ui.debug('applied %i hgtags fnodes cache entries\n' % count)
@@ -1,5517 +1,5518 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import difflib
10 import difflib
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 hex,
17 hex,
18 nullid,
18 nullid,
19 nullrev,
19 nullrev,
20 short,
20 short,
21 )
21 )
22 from . import (
22 from . import (
23 archival,
23 archival,
24 bookmarks,
24 bookmarks,
25 bundle2,
25 bundle2,
26 changegroup,
26 changegroup,
27 cmdutil,
27 cmdutil,
28 copies,
28 copies,
29 destutil,
29 destutil,
30 dirstateguard,
30 dirstateguard,
31 discovery,
31 discovery,
32 encoding,
32 encoding,
33 error,
33 error,
34 exchange,
34 exchange,
35 extensions,
35 extensions,
36 graphmod,
36 graphmod,
37 hbisect,
37 hbisect,
38 help,
38 help,
39 hg,
39 hg,
40 lock as lockmod,
40 lock as lockmod,
41 merge as mergemod,
41 merge as mergemod,
42 obsolete,
42 obsolete,
43 patch,
43 patch,
44 phases,
44 phases,
45 pycompat,
45 pycompat,
46 rcutil,
46 rcutil,
47 revsetlang,
47 revsetlang,
48 scmutil,
48 scmutil,
49 server,
49 server,
50 sshserver,
50 sshserver,
51 streamclone,
51 streamclone,
52 tags as tagsmod,
52 tags as tagsmod,
53 templatekw,
53 templatekw,
54 ui as uimod,
54 ui as uimod,
55 util,
55 util,
56 )
56 )
57
57
58 release = lockmod.release
58 release = lockmod.release
59
59
60 table = {}
60 table = {}
61
61
62 command = cmdutil.command(table)
62 command = cmdutil.command(table)
63
63
64 # label constants
64 # label constants
65 # until 3.5, bookmarks.current was the advertised name, not
65 # until 3.5, bookmarks.current was the advertised name, not
66 # bookmarks.active, so we must use both to avoid breaking old
66 # bookmarks.active, so we must use both to avoid breaking old
67 # custom styles
67 # custom styles
68 activebookmarklabel = 'bookmarks.active bookmarks.current'
68 activebookmarklabel = 'bookmarks.active bookmarks.current'
69
69
70 # common command options
70 # common command options
71
71
72 globalopts = [
72 globalopts = [
73 ('R', 'repository', '',
73 ('R', 'repository', '',
74 _('repository root directory or name of overlay bundle file'),
74 _('repository root directory or name of overlay bundle file'),
75 _('REPO')),
75 _('REPO')),
76 ('', 'cwd', '',
76 ('', 'cwd', '',
77 _('change working directory'), _('DIR')),
77 _('change working directory'), _('DIR')),
78 ('y', 'noninteractive', None,
78 ('y', 'noninteractive', None,
79 _('do not prompt, automatically pick the first choice for all prompts')),
79 _('do not prompt, automatically pick the first choice for all prompts')),
80 ('q', 'quiet', None, _('suppress output')),
80 ('q', 'quiet', None, _('suppress output')),
81 ('v', 'verbose', None, _('enable additional output')),
81 ('v', 'verbose', None, _('enable additional output')),
82 ('', 'color', '',
82 ('', 'color', '',
83 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
83 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
84 # and should not be translated
84 # and should not be translated
85 _("when to colorize (boolean, always, auto, never, or debug)"),
85 _("when to colorize (boolean, always, auto, never, or debug)"),
86 _('TYPE')),
86 _('TYPE')),
87 ('', 'config', [],
87 ('', 'config', [],
88 _('set/override config option (use \'section.name=value\')'),
88 _('set/override config option (use \'section.name=value\')'),
89 _('CONFIG')),
89 _('CONFIG')),
90 ('', 'debug', None, _('enable debugging output')),
90 ('', 'debug', None, _('enable debugging output')),
91 ('', 'debugger', None, _('start debugger')),
91 ('', 'debugger', None, _('start debugger')),
92 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
92 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
93 _('ENCODE')),
93 _('ENCODE')),
94 ('', 'encodingmode', encoding.encodingmode,
94 ('', 'encodingmode', encoding.encodingmode,
95 _('set the charset encoding mode'), _('MODE')),
95 _('set the charset encoding mode'), _('MODE')),
96 ('', 'traceback', None, _('always print a traceback on exception')),
96 ('', 'traceback', None, _('always print a traceback on exception')),
97 ('', 'time', None, _('time how long the command takes')),
97 ('', 'time', None, _('time how long the command takes')),
98 ('', 'profile', None, _('print command execution profile')),
98 ('', 'profile', None, _('print command execution profile')),
99 ('', 'version', None, _('output version information and exit')),
99 ('', 'version', None, _('output version information and exit')),
100 ('h', 'help', None, _('display help and exit')),
100 ('h', 'help', None, _('display help and exit')),
101 ('', 'hidden', False, _('consider hidden changesets')),
101 ('', 'hidden', False, _('consider hidden changesets')),
102 ('', 'pager', 'auto',
102 ('', 'pager', 'auto',
103 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
103 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
104 ]
104 ]
105
105
106 dryrunopts = [('n', 'dry-run', None,
106 dryrunopts = [('n', 'dry-run', None,
107 _('do not perform actions, just print output'))]
107 _('do not perform actions, just print output'))]
108
108
109 remoteopts = [
109 remoteopts = [
110 ('e', 'ssh', '',
110 ('e', 'ssh', '',
111 _('specify ssh command to use'), _('CMD')),
111 _('specify ssh command to use'), _('CMD')),
112 ('', 'remotecmd', '',
112 ('', 'remotecmd', '',
113 _('specify hg command to run on the remote side'), _('CMD')),
113 _('specify hg command to run on the remote side'), _('CMD')),
114 ('', 'insecure', None,
114 ('', 'insecure', None,
115 _('do not verify server certificate (ignoring web.cacerts config)')),
115 _('do not verify server certificate (ignoring web.cacerts config)')),
116 ]
116 ]
117
117
118 walkopts = [
118 walkopts = [
119 ('I', 'include', [],
119 ('I', 'include', [],
120 _('include names matching the given patterns'), _('PATTERN')),
120 _('include names matching the given patterns'), _('PATTERN')),
121 ('X', 'exclude', [],
121 ('X', 'exclude', [],
122 _('exclude names matching the given patterns'), _('PATTERN')),
122 _('exclude names matching the given patterns'), _('PATTERN')),
123 ]
123 ]
124
124
125 commitopts = [
125 commitopts = [
126 ('m', 'message', '',
126 ('m', 'message', '',
127 _('use text as commit message'), _('TEXT')),
127 _('use text as commit message'), _('TEXT')),
128 ('l', 'logfile', '',
128 ('l', 'logfile', '',
129 _('read commit message from file'), _('FILE')),
129 _('read commit message from file'), _('FILE')),
130 ]
130 ]
131
131
132 commitopts2 = [
132 commitopts2 = [
133 ('d', 'date', '',
133 ('d', 'date', '',
134 _('record the specified date as commit date'), _('DATE')),
134 _('record the specified date as commit date'), _('DATE')),
135 ('u', 'user', '',
135 ('u', 'user', '',
136 _('record the specified user as committer'), _('USER')),
136 _('record the specified user as committer'), _('USER')),
137 ]
137 ]
138
138
139 # hidden for now
139 # hidden for now
140 formatteropts = [
140 formatteropts = [
141 ('T', 'template', '',
141 ('T', 'template', '',
142 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
142 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
143 ]
143 ]
144
144
145 templateopts = [
145 templateopts = [
146 ('', 'style', '',
146 ('', 'style', '',
147 _('display using template map file (DEPRECATED)'), _('STYLE')),
147 _('display using template map file (DEPRECATED)'), _('STYLE')),
148 ('T', 'template', '',
148 ('T', 'template', '',
149 _('display with template'), _('TEMPLATE')),
149 _('display with template'), _('TEMPLATE')),
150 ]
150 ]
151
151
152 logopts = [
152 logopts = [
153 ('p', 'patch', None, _('show patch')),
153 ('p', 'patch', None, _('show patch')),
154 ('g', 'git', None, _('use git extended diff format')),
154 ('g', 'git', None, _('use git extended diff format')),
155 ('l', 'limit', '',
155 ('l', 'limit', '',
156 _('limit number of changes displayed'), _('NUM')),
156 _('limit number of changes displayed'), _('NUM')),
157 ('M', 'no-merges', None, _('do not show merges')),
157 ('M', 'no-merges', None, _('do not show merges')),
158 ('', 'stat', None, _('output diffstat-style summary of changes')),
158 ('', 'stat', None, _('output diffstat-style summary of changes')),
159 ('G', 'graph', None, _("show the revision DAG")),
159 ('G', 'graph', None, _("show the revision DAG")),
160 ] + templateopts
160 ] + templateopts
161
161
162 diffopts = [
162 diffopts = [
163 ('a', 'text', None, _('treat all files as text')),
163 ('a', 'text', None, _('treat all files as text')),
164 ('g', 'git', None, _('use git extended diff format')),
164 ('g', 'git', None, _('use git extended diff format')),
165 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
165 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
166 ('', 'nodates', None, _('omit dates from diff headers'))
166 ('', 'nodates', None, _('omit dates from diff headers'))
167 ]
167 ]
168
168
169 diffwsopts = [
169 diffwsopts = [
170 ('w', 'ignore-all-space', None,
170 ('w', 'ignore-all-space', None,
171 _('ignore white space when comparing lines')),
171 _('ignore white space when comparing lines')),
172 ('b', 'ignore-space-change', None,
172 ('b', 'ignore-space-change', None,
173 _('ignore changes in the amount of white space')),
173 _('ignore changes in the amount of white space')),
174 ('B', 'ignore-blank-lines', None,
174 ('B', 'ignore-blank-lines', None,
175 _('ignore changes whose lines are all blank')),
175 _('ignore changes whose lines are all blank')),
176 ]
176 ]
177
177
178 diffopts2 = [
178 diffopts2 = [
179 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
179 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
180 ('p', 'show-function', None, _('show which function each change is in')),
180 ('p', 'show-function', None, _('show which function each change is in')),
181 ('', 'reverse', None, _('produce a diff that undoes the changes')),
181 ('', 'reverse', None, _('produce a diff that undoes the changes')),
182 ] + diffwsopts + [
182 ] + diffwsopts + [
183 ('U', 'unified', '',
183 ('U', 'unified', '',
184 _('number of lines of context to show'), _('NUM')),
184 _('number of lines of context to show'), _('NUM')),
185 ('', 'stat', None, _('output diffstat-style summary of changes')),
185 ('', 'stat', None, _('output diffstat-style summary of changes')),
186 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
186 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
187 ]
187 ]
188
188
189 mergetoolopts = [
189 mergetoolopts = [
190 ('t', 'tool', '', _('specify merge tool')),
190 ('t', 'tool', '', _('specify merge tool')),
191 ]
191 ]
192
192
193 similarityopts = [
193 similarityopts = [
194 ('s', 'similarity', '',
194 ('s', 'similarity', '',
195 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
195 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
196 ]
196 ]
197
197
198 subrepoopts = [
198 subrepoopts = [
199 ('S', 'subrepos', None,
199 ('S', 'subrepos', None,
200 _('recurse into subrepositories'))
200 _('recurse into subrepositories'))
201 ]
201 ]
202
202
203 debugrevlogopts = [
203 debugrevlogopts = [
204 ('c', 'changelog', False, _('open changelog')),
204 ('c', 'changelog', False, _('open changelog')),
205 ('m', 'manifest', False, _('open manifest')),
205 ('m', 'manifest', False, _('open manifest')),
206 ('', 'dir', '', _('open directory manifest')),
206 ('', 'dir', '', _('open directory manifest')),
207 ]
207 ]
208
208
209 # Commands start here, listed alphabetically
209 # Commands start here, listed alphabetically
210
210
211 @command('^add',
211 @command('^add',
212 walkopts + subrepoopts + dryrunopts,
212 walkopts + subrepoopts + dryrunopts,
213 _('[OPTION]... [FILE]...'),
213 _('[OPTION]... [FILE]...'),
214 inferrepo=True)
214 inferrepo=True)
215 def add(ui, repo, *pats, **opts):
215 def add(ui, repo, *pats, **opts):
216 """add the specified files on the next commit
216 """add the specified files on the next commit
217
217
218 Schedule files to be version controlled and added to the
218 Schedule files to be version controlled and added to the
219 repository.
219 repository.
220
220
221 The files will be added to the repository at the next commit. To
221 The files will be added to the repository at the next commit. To
222 undo an add before that, see :hg:`forget`.
222 undo an add before that, see :hg:`forget`.
223
223
224 If no names are given, add all files to the repository (except
224 If no names are given, add all files to the repository (except
225 files matching ``.hgignore``).
225 files matching ``.hgignore``).
226
226
227 .. container:: verbose
227 .. container:: verbose
228
228
229 Examples:
229 Examples:
230
230
231 - New (unknown) files are added
231 - New (unknown) files are added
232 automatically by :hg:`add`::
232 automatically by :hg:`add`::
233
233
234 $ ls
234 $ ls
235 foo.c
235 foo.c
236 $ hg status
236 $ hg status
237 ? foo.c
237 ? foo.c
238 $ hg add
238 $ hg add
239 adding foo.c
239 adding foo.c
240 $ hg status
240 $ hg status
241 A foo.c
241 A foo.c
242
242
243 - Specific files to be added can be specified::
243 - Specific files to be added can be specified::
244
244
245 $ ls
245 $ ls
246 bar.c foo.c
246 bar.c foo.c
247 $ hg status
247 $ hg status
248 ? bar.c
248 ? bar.c
249 ? foo.c
249 ? foo.c
250 $ hg add bar.c
250 $ hg add bar.c
251 $ hg status
251 $ hg status
252 A bar.c
252 A bar.c
253 ? foo.c
253 ? foo.c
254
254
255 Returns 0 if all files are successfully added.
255 Returns 0 if all files are successfully added.
256 """
256 """
257
257
258 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
258 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
259 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
259 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
260 return rejected and 1 or 0
260 return rejected and 1 or 0
261
261
262 @command('addremove',
262 @command('addremove',
263 similarityopts + subrepoopts + walkopts + dryrunopts,
263 similarityopts + subrepoopts + walkopts + dryrunopts,
264 _('[OPTION]... [FILE]...'),
264 _('[OPTION]... [FILE]...'),
265 inferrepo=True)
265 inferrepo=True)
266 def addremove(ui, repo, *pats, **opts):
266 def addremove(ui, repo, *pats, **opts):
267 """add all new files, delete all missing files
267 """add all new files, delete all missing files
268
268
269 Add all new files and remove all missing files from the
269 Add all new files and remove all missing files from the
270 repository.
270 repository.
271
271
272 Unless names are given, new files are ignored if they match any of
272 Unless names are given, new files are ignored if they match any of
273 the patterns in ``.hgignore``. As with add, these changes take
273 the patterns in ``.hgignore``. As with add, these changes take
274 effect at the next commit.
274 effect at the next commit.
275
275
276 Use the -s/--similarity option to detect renamed files. This
276 Use the -s/--similarity option to detect renamed files. This
277 option takes a percentage between 0 (disabled) and 100 (files must
277 option takes a percentage between 0 (disabled) and 100 (files must
278 be identical) as its parameter. With a parameter greater than 0,
278 be identical) as its parameter. With a parameter greater than 0,
279 this compares every removed file with every added file and records
279 this compares every removed file with every added file and records
280 those similar enough as renames. Detecting renamed files this way
280 those similar enough as renames. Detecting renamed files this way
281 can be expensive. After using this option, :hg:`status -C` can be
281 can be expensive. After using this option, :hg:`status -C` can be
282 used to check which files were identified as moved or renamed. If
282 used to check which files were identified as moved or renamed. If
283 not specified, -s/--similarity defaults to 100 and only renames of
283 not specified, -s/--similarity defaults to 100 and only renames of
284 identical files are detected.
284 identical files are detected.
285
285
286 .. container:: verbose
286 .. container:: verbose
287
287
288 Examples:
288 Examples:
289
289
290 - A number of files (bar.c and foo.c) are new,
290 - A number of files (bar.c and foo.c) are new,
291 while foobar.c has been removed (without using :hg:`remove`)
291 while foobar.c has been removed (without using :hg:`remove`)
292 from the repository::
292 from the repository::
293
293
294 $ ls
294 $ ls
295 bar.c foo.c
295 bar.c foo.c
296 $ hg status
296 $ hg status
297 ! foobar.c
297 ! foobar.c
298 ? bar.c
298 ? bar.c
299 ? foo.c
299 ? foo.c
300 $ hg addremove
300 $ hg addremove
301 adding bar.c
301 adding bar.c
302 adding foo.c
302 adding foo.c
303 removing foobar.c
303 removing foobar.c
304 $ hg status
304 $ hg status
305 A bar.c
305 A bar.c
306 A foo.c
306 A foo.c
307 R foobar.c
307 R foobar.c
308
308
309 - A file foobar.c was moved to foo.c without using :hg:`rename`.
309 - A file foobar.c was moved to foo.c without using :hg:`rename`.
310 Afterwards, it was edited slightly::
310 Afterwards, it was edited slightly::
311
311
312 $ ls
312 $ ls
313 foo.c
313 foo.c
314 $ hg status
314 $ hg status
315 ! foobar.c
315 ! foobar.c
316 ? foo.c
316 ? foo.c
317 $ hg addremove --similarity 90
317 $ hg addremove --similarity 90
318 removing foobar.c
318 removing foobar.c
319 adding foo.c
319 adding foo.c
320 recording removal of foobar.c as rename to foo.c (94% similar)
320 recording removal of foobar.c as rename to foo.c (94% similar)
321 $ hg status -C
321 $ hg status -C
322 A foo.c
322 A foo.c
323 foobar.c
323 foobar.c
324 R foobar.c
324 R foobar.c
325
325
326 Returns 0 if all files are successfully added.
326 Returns 0 if all files are successfully added.
327 """
327 """
328 opts = pycompat.byteskwargs(opts)
328 opts = pycompat.byteskwargs(opts)
329 try:
329 try:
330 sim = float(opts.get('similarity') or 100)
330 sim = float(opts.get('similarity') or 100)
331 except ValueError:
331 except ValueError:
332 raise error.Abort(_('similarity must be a number'))
332 raise error.Abort(_('similarity must be a number'))
333 if sim < 0 or sim > 100:
333 if sim < 0 or sim > 100:
334 raise error.Abort(_('similarity must be between 0 and 100'))
334 raise error.Abort(_('similarity must be between 0 and 100'))
335 matcher = scmutil.match(repo[None], pats, opts)
335 matcher = scmutil.match(repo[None], pats, opts)
336 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
336 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
337
337
338 @command('^annotate|blame',
338 @command('^annotate|blame',
339 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
339 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
340 ('', 'follow', None,
340 ('', 'follow', None,
341 _('follow copies/renames and list the filename (DEPRECATED)')),
341 _('follow copies/renames and list the filename (DEPRECATED)')),
342 ('', 'no-follow', None, _("don't follow copies and renames")),
342 ('', 'no-follow', None, _("don't follow copies and renames")),
343 ('a', 'text', None, _('treat all files as text')),
343 ('a', 'text', None, _('treat all files as text')),
344 ('u', 'user', None, _('list the author (long with -v)')),
344 ('u', 'user', None, _('list the author (long with -v)')),
345 ('f', 'file', None, _('list the filename')),
345 ('f', 'file', None, _('list the filename')),
346 ('d', 'date', None, _('list the date (short with -q)')),
346 ('d', 'date', None, _('list the date (short with -q)')),
347 ('n', 'number', None, _('list the revision number (default)')),
347 ('n', 'number', None, _('list the revision number (default)')),
348 ('c', 'changeset', None, _('list the changeset')),
348 ('c', 'changeset', None, _('list the changeset')),
349 ('l', 'line-number', None, _('show line number at the first appearance'))
349 ('l', 'line-number', None, _('show line number at the first appearance'))
350 ] + diffwsopts + walkopts + formatteropts,
350 ] + diffwsopts + walkopts + formatteropts,
351 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
351 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
352 inferrepo=True)
352 inferrepo=True)
353 def annotate(ui, repo, *pats, **opts):
353 def annotate(ui, repo, *pats, **opts):
354 """show changeset information by line for each file
354 """show changeset information by line for each file
355
355
356 List changes in files, showing the revision id responsible for
356 List changes in files, showing the revision id responsible for
357 each line.
357 each line.
358
358
359 This command is useful for discovering when a change was made and
359 This command is useful for discovering when a change was made and
360 by whom.
360 by whom.
361
361
362 If you include --file, --user, or --date, the revision number is
362 If you include --file, --user, or --date, the revision number is
363 suppressed unless you also include --number.
363 suppressed unless you also include --number.
364
364
365 Without the -a/--text option, annotate will avoid processing files
365 Without the -a/--text option, annotate will avoid processing files
366 it detects as binary. With -a, annotate will annotate the file
366 it detects as binary. With -a, annotate will annotate the file
367 anyway, although the results will probably be neither useful
367 anyway, although the results will probably be neither useful
368 nor desirable.
368 nor desirable.
369
369
370 Returns 0 on success.
370 Returns 0 on success.
371 """
371 """
372 opts = pycompat.byteskwargs(opts)
372 opts = pycompat.byteskwargs(opts)
373 if not pats:
373 if not pats:
374 raise error.Abort(_('at least one filename or pattern is required'))
374 raise error.Abort(_('at least one filename or pattern is required'))
375
375
376 if opts.get('follow'):
376 if opts.get('follow'):
377 # --follow is deprecated and now just an alias for -f/--file
377 # --follow is deprecated and now just an alias for -f/--file
378 # to mimic the behavior of Mercurial before version 1.5
378 # to mimic the behavior of Mercurial before version 1.5
379 opts['file'] = True
379 opts['file'] = True
380
380
381 ctx = scmutil.revsingle(repo, opts.get('rev'))
381 ctx = scmutil.revsingle(repo, opts.get('rev'))
382
382
383 fm = ui.formatter('annotate', opts)
383 fm = ui.formatter('annotate', opts)
384 if ui.quiet:
384 if ui.quiet:
385 datefunc = util.shortdate
385 datefunc = util.shortdate
386 else:
386 else:
387 datefunc = util.datestr
387 datefunc = util.datestr
388 if ctx.rev() is None:
388 if ctx.rev() is None:
389 def hexfn(node):
389 def hexfn(node):
390 if node is None:
390 if node is None:
391 return None
391 return None
392 else:
392 else:
393 return fm.hexfunc(node)
393 return fm.hexfunc(node)
394 if opts.get('changeset'):
394 if opts.get('changeset'):
395 # omit "+" suffix which is appended to node hex
395 # omit "+" suffix which is appended to node hex
396 def formatrev(rev):
396 def formatrev(rev):
397 if rev is None:
397 if rev is None:
398 return '%d' % ctx.p1().rev()
398 return '%d' % ctx.p1().rev()
399 else:
399 else:
400 return '%d' % rev
400 return '%d' % rev
401 else:
401 else:
402 def formatrev(rev):
402 def formatrev(rev):
403 if rev is None:
403 if rev is None:
404 return '%d+' % ctx.p1().rev()
404 return '%d+' % ctx.p1().rev()
405 else:
405 else:
406 return '%d ' % rev
406 return '%d ' % rev
407 def formathex(hex):
407 def formathex(hex):
408 if hex is None:
408 if hex is None:
409 return '%s+' % fm.hexfunc(ctx.p1().node())
409 return '%s+' % fm.hexfunc(ctx.p1().node())
410 else:
410 else:
411 return '%s ' % hex
411 return '%s ' % hex
412 else:
412 else:
413 hexfn = fm.hexfunc
413 hexfn = fm.hexfunc
414 formatrev = formathex = str
414 formatrev = formathex = str
415
415
416 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
416 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
417 ('number', ' ', lambda x: x[0].rev(), formatrev),
417 ('number', ' ', lambda x: x[0].rev(), formatrev),
418 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
418 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
419 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
419 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
420 ('file', ' ', lambda x: x[0].path(), str),
420 ('file', ' ', lambda x: x[0].path(), str),
421 ('line_number', ':', lambda x: x[1], str),
421 ('line_number', ':', lambda x: x[1], str),
422 ]
422 ]
423 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
423 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
424
424
425 if (not opts.get('user') and not opts.get('changeset')
425 if (not opts.get('user') and not opts.get('changeset')
426 and not opts.get('date') and not opts.get('file')):
426 and not opts.get('date') and not opts.get('file')):
427 opts['number'] = True
427 opts['number'] = True
428
428
429 linenumber = opts.get('line_number') is not None
429 linenumber = opts.get('line_number') is not None
430 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
430 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
431 raise error.Abort(_('at least one of -n/-c is required for -l'))
431 raise error.Abort(_('at least one of -n/-c is required for -l'))
432
432
433 ui.pager('annotate')
433 ui.pager('annotate')
434
434
435 if fm.isplain():
435 if fm.isplain():
436 def makefunc(get, fmt):
436 def makefunc(get, fmt):
437 return lambda x: fmt(get(x))
437 return lambda x: fmt(get(x))
438 else:
438 else:
439 def makefunc(get, fmt):
439 def makefunc(get, fmt):
440 return get
440 return get
441 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
441 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
442 if opts.get(op)]
442 if opts.get(op)]
443 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
443 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
444 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
444 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
445 if opts.get(op))
445 if opts.get(op))
446
446
447 def bad(x, y):
447 def bad(x, y):
448 raise error.Abort("%s: %s" % (x, y))
448 raise error.Abort("%s: %s" % (x, y))
449
449
450 m = scmutil.match(ctx, pats, opts, badfn=bad)
450 m = scmutil.match(ctx, pats, opts, badfn=bad)
451
451
452 follow = not opts.get('no_follow')
452 follow = not opts.get('no_follow')
453 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
453 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
454 whitespace=True)
454 whitespace=True)
455 for abs in ctx.walk(m):
455 for abs in ctx.walk(m):
456 fctx = ctx[abs]
456 fctx = ctx[abs]
457 if not opts.get('text') and fctx.isbinary():
457 if not opts.get('text') and fctx.isbinary():
458 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
458 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
459 continue
459 continue
460
460
461 lines = fctx.annotate(follow=follow, linenumber=linenumber,
461 lines = fctx.annotate(follow=follow, linenumber=linenumber,
462 diffopts=diffopts)
462 diffopts=diffopts)
463 if not lines:
463 if not lines:
464 continue
464 continue
465 formats = []
465 formats = []
466 pieces = []
466 pieces = []
467
467
468 for f, sep in funcmap:
468 for f, sep in funcmap:
469 l = [f(n) for n, dummy in lines]
469 l = [f(n) for n, dummy in lines]
470 if fm.isplain():
470 if fm.isplain():
471 sizes = [encoding.colwidth(x) for x in l]
471 sizes = [encoding.colwidth(x) for x in l]
472 ml = max(sizes)
472 ml = max(sizes)
473 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
473 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
474 else:
474 else:
475 formats.append(['%s' for x in l])
475 formats.append(['%s' for x in l])
476 pieces.append(l)
476 pieces.append(l)
477
477
478 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
478 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
479 fm.startitem()
479 fm.startitem()
480 fm.write(fields, "".join(f), *p)
480 fm.write(fields, "".join(f), *p)
481 fm.write('line', ": %s", l[1])
481 fm.write('line', ": %s", l[1])
482
482
483 if not lines[-1][1].endswith('\n'):
483 if not lines[-1][1].endswith('\n'):
484 fm.plain('\n')
484 fm.plain('\n')
485
485
486 fm.end()
486 fm.end()
487
487
488 @command('archive',
488 @command('archive',
489 [('', 'no-decode', None, _('do not pass files through decoders')),
489 [('', 'no-decode', None, _('do not pass files through decoders')),
490 ('p', 'prefix', '', _('directory prefix for files in archive'),
490 ('p', 'prefix', '', _('directory prefix for files in archive'),
491 _('PREFIX')),
491 _('PREFIX')),
492 ('r', 'rev', '', _('revision to distribute'), _('REV')),
492 ('r', 'rev', '', _('revision to distribute'), _('REV')),
493 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
493 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
494 ] + subrepoopts + walkopts,
494 ] + subrepoopts + walkopts,
495 _('[OPTION]... DEST'))
495 _('[OPTION]... DEST'))
496 def archive(ui, repo, dest, **opts):
496 def archive(ui, repo, dest, **opts):
497 '''create an unversioned archive of a repository revision
497 '''create an unversioned archive of a repository revision
498
498
499 By default, the revision used is the parent of the working
499 By default, the revision used is the parent of the working
500 directory; use -r/--rev to specify a different revision.
500 directory; use -r/--rev to specify a different revision.
501
501
502 The archive type is automatically detected based on file
502 The archive type is automatically detected based on file
503 extension (to override, use -t/--type).
503 extension (to override, use -t/--type).
504
504
505 .. container:: verbose
505 .. container:: verbose
506
506
507 Examples:
507 Examples:
508
508
509 - create a zip file containing the 1.0 release::
509 - create a zip file containing the 1.0 release::
510
510
511 hg archive -r 1.0 project-1.0.zip
511 hg archive -r 1.0 project-1.0.zip
512
512
513 - create a tarball excluding .hg files::
513 - create a tarball excluding .hg files::
514
514
515 hg archive project.tar.gz -X ".hg*"
515 hg archive project.tar.gz -X ".hg*"
516
516
517 Valid types are:
517 Valid types are:
518
518
519 :``files``: a directory full of files (default)
519 :``files``: a directory full of files (default)
520 :``tar``: tar archive, uncompressed
520 :``tar``: tar archive, uncompressed
521 :``tbz2``: tar archive, compressed using bzip2
521 :``tbz2``: tar archive, compressed using bzip2
522 :``tgz``: tar archive, compressed using gzip
522 :``tgz``: tar archive, compressed using gzip
523 :``uzip``: zip archive, uncompressed
523 :``uzip``: zip archive, uncompressed
524 :``zip``: zip archive, compressed using deflate
524 :``zip``: zip archive, compressed using deflate
525
525
526 The exact name of the destination archive or directory is given
526 The exact name of the destination archive or directory is given
527 using a format string; see :hg:`help export` for details.
527 using a format string; see :hg:`help export` for details.
528
528
529 Each member added to an archive file has a directory prefix
529 Each member added to an archive file has a directory prefix
530 prepended. Use -p/--prefix to specify a format string for the
530 prepended. Use -p/--prefix to specify a format string for the
531 prefix. The default is the basename of the archive, with suffixes
531 prefix. The default is the basename of the archive, with suffixes
532 removed.
532 removed.
533
533
534 Returns 0 on success.
534 Returns 0 on success.
535 '''
535 '''
536
536
537 opts = pycompat.byteskwargs(opts)
537 opts = pycompat.byteskwargs(opts)
538 ctx = scmutil.revsingle(repo, opts.get('rev'))
538 ctx = scmutil.revsingle(repo, opts.get('rev'))
539 if not ctx:
539 if not ctx:
540 raise error.Abort(_('no working directory: please specify a revision'))
540 raise error.Abort(_('no working directory: please specify a revision'))
541 node = ctx.node()
541 node = ctx.node()
542 dest = cmdutil.makefilename(repo, dest, node)
542 dest = cmdutil.makefilename(repo, dest, node)
543 if os.path.realpath(dest) == repo.root:
543 if os.path.realpath(dest) == repo.root:
544 raise error.Abort(_('repository root cannot be destination'))
544 raise error.Abort(_('repository root cannot be destination'))
545
545
546 kind = opts.get('type') or archival.guesskind(dest) or 'files'
546 kind = opts.get('type') or archival.guesskind(dest) or 'files'
547 prefix = opts.get('prefix')
547 prefix = opts.get('prefix')
548
548
549 if dest == '-':
549 if dest == '-':
550 if kind == 'files':
550 if kind == 'files':
551 raise error.Abort(_('cannot archive plain files to stdout'))
551 raise error.Abort(_('cannot archive plain files to stdout'))
552 dest = cmdutil.makefileobj(repo, dest)
552 dest = cmdutil.makefileobj(repo, dest)
553 if not prefix:
553 if not prefix:
554 prefix = os.path.basename(repo.root) + '-%h'
554 prefix = os.path.basename(repo.root) + '-%h'
555
555
556 prefix = cmdutil.makefilename(repo, prefix, node)
556 prefix = cmdutil.makefilename(repo, prefix, node)
557 matchfn = scmutil.match(ctx, [], opts)
557 matchfn = scmutil.match(ctx, [], opts)
558 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
558 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
559 matchfn, prefix, subrepos=opts.get('subrepos'))
559 matchfn, prefix, subrepos=opts.get('subrepos'))
560
560
561 @command('backout',
561 @command('backout',
562 [('', 'merge', None, _('merge with old dirstate parent after backout')),
562 [('', 'merge', None, _('merge with old dirstate parent after backout')),
563 ('', 'commit', None,
563 ('', 'commit', None,
564 _('commit if no conflicts were encountered (DEPRECATED)')),
564 _('commit if no conflicts were encountered (DEPRECATED)')),
565 ('', 'no-commit', None, _('do not commit')),
565 ('', 'no-commit', None, _('do not commit')),
566 ('', 'parent', '',
566 ('', 'parent', '',
567 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
567 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
568 ('r', 'rev', '', _('revision to backout'), _('REV')),
568 ('r', 'rev', '', _('revision to backout'), _('REV')),
569 ('e', 'edit', False, _('invoke editor on commit messages')),
569 ('e', 'edit', False, _('invoke editor on commit messages')),
570 ] + mergetoolopts + walkopts + commitopts + commitopts2,
570 ] + mergetoolopts + walkopts + commitopts + commitopts2,
571 _('[OPTION]... [-r] REV'))
571 _('[OPTION]... [-r] REV'))
572 def backout(ui, repo, node=None, rev=None, **opts):
572 def backout(ui, repo, node=None, rev=None, **opts):
573 '''reverse effect of earlier changeset
573 '''reverse effect of earlier changeset
574
574
575 Prepare a new changeset with the effect of REV undone in the
575 Prepare a new changeset with the effect of REV undone in the
576 current working directory. If no conflicts were encountered,
576 current working directory. If no conflicts were encountered,
577 it will be committed immediately.
577 it will be committed immediately.
578
578
579 If REV is the parent of the working directory, then this new changeset
579 If REV is the parent of the working directory, then this new changeset
580 is committed automatically (unless --no-commit is specified).
580 is committed automatically (unless --no-commit is specified).
581
581
582 .. note::
582 .. note::
583
583
584 :hg:`backout` cannot be used to fix either an unwanted or
584 :hg:`backout` cannot be used to fix either an unwanted or
585 incorrect merge.
585 incorrect merge.
586
586
587 .. container:: verbose
587 .. container:: verbose
588
588
589 Examples:
589 Examples:
590
590
591 - Reverse the effect of the parent of the working directory.
591 - Reverse the effect of the parent of the working directory.
592 This backout will be committed immediately::
592 This backout will be committed immediately::
593
593
594 hg backout -r .
594 hg backout -r .
595
595
596 - Reverse the effect of previous bad revision 23::
596 - Reverse the effect of previous bad revision 23::
597
597
598 hg backout -r 23
598 hg backout -r 23
599
599
600 - Reverse the effect of previous bad revision 23 and
600 - Reverse the effect of previous bad revision 23 and
601 leave changes uncommitted::
601 leave changes uncommitted::
602
602
603 hg backout -r 23 --no-commit
603 hg backout -r 23 --no-commit
604 hg commit -m "Backout revision 23"
604 hg commit -m "Backout revision 23"
605
605
606 By default, the pending changeset will have one parent,
606 By default, the pending changeset will have one parent,
607 maintaining a linear history. With --merge, the pending
607 maintaining a linear history. With --merge, the pending
608 changeset will instead have two parents: the old parent of the
608 changeset will instead have two parents: the old parent of the
609 working directory and a new child of REV that simply undoes REV.
609 working directory and a new child of REV that simply undoes REV.
610
610
611 Before version 1.7, the behavior without --merge was equivalent
611 Before version 1.7, the behavior without --merge was equivalent
612 to specifying --merge followed by :hg:`update --clean .` to
612 to specifying --merge followed by :hg:`update --clean .` to
613 cancel the merge and leave the child of REV as a head to be
613 cancel the merge and leave the child of REV as a head to be
614 merged separately.
614 merged separately.
615
615
616 See :hg:`help dates` for a list of formats valid for -d/--date.
616 See :hg:`help dates` for a list of formats valid for -d/--date.
617
617
618 See :hg:`help revert` for a way to restore files to the state
618 See :hg:`help revert` for a way to restore files to the state
619 of another revision.
619 of another revision.
620
620
621 Returns 0 on success, 1 if nothing to backout or there are unresolved
621 Returns 0 on success, 1 if nothing to backout or there are unresolved
622 files.
622 files.
623 '''
623 '''
624 wlock = lock = None
624 wlock = lock = None
625 try:
625 try:
626 wlock = repo.wlock()
626 wlock = repo.wlock()
627 lock = repo.lock()
627 lock = repo.lock()
628 return _dobackout(ui, repo, node, rev, **opts)
628 return _dobackout(ui, repo, node, rev, **opts)
629 finally:
629 finally:
630 release(lock, wlock)
630 release(lock, wlock)
631
631
632 def _dobackout(ui, repo, node=None, rev=None, **opts):
632 def _dobackout(ui, repo, node=None, rev=None, **opts):
633 opts = pycompat.byteskwargs(opts)
633 opts = pycompat.byteskwargs(opts)
634 if opts.get('commit') and opts.get('no_commit'):
634 if opts.get('commit') and opts.get('no_commit'):
635 raise error.Abort(_("cannot use --commit with --no-commit"))
635 raise error.Abort(_("cannot use --commit with --no-commit"))
636 if opts.get('merge') and opts.get('no_commit'):
636 if opts.get('merge') and opts.get('no_commit'):
637 raise error.Abort(_("cannot use --merge with --no-commit"))
637 raise error.Abort(_("cannot use --merge with --no-commit"))
638
638
639 if rev and node:
639 if rev and node:
640 raise error.Abort(_("please specify just one revision"))
640 raise error.Abort(_("please specify just one revision"))
641
641
642 if not rev:
642 if not rev:
643 rev = node
643 rev = node
644
644
645 if not rev:
645 if not rev:
646 raise error.Abort(_("please specify a revision to backout"))
646 raise error.Abort(_("please specify a revision to backout"))
647
647
648 date = opts.get('date')
648 date = opts.get('date')
649 if date:
649 if date:
650 opts['date'] = util.parsedate(date)
650 opts['date'] = util.parsedate(date)
651
651
652 cmdutil.checkunfinished(repo)
652 cmdutil.checkunfinished(repo)
653 cmdutil.bailifchanged(repo)
653 cmdutil.bailifchanged(repo)
654 node = scmutil.revsingle(repo, rev).node()
654 node = scmutil.revsingle(repo, rev).node()
655
655
656 op1, op2 = repo.dirstate.parents()
656 op1, op2 = repo.dirstate.parents()
657 if not repo.changelog.isancestor(node, op1):
657 if not repo.changelog.isancestor(node, op1):
658 raise error.Abort(_('cannot backout change that is not an ancestor'))
658 raise error.Abort(_('cannot backout change that is not an ancestor'))
659
659
660 p1, p2 = repo.changelog.parents(node)
660 p1, p2 = repo.changelog.parents(node)
661 if p1 == nullid:
661 if p1 == nullid:
662 raise error.Abort(_('cannot backout a change with no parents'))
662 raise error.Abort(_('cannot backout a change with no parents'))
663 if p2 != nullid:
663 if p2 != nullid:
664 if not opts.get('parent'):
664 if not opts.get('parent'):
665 raise error.Abort(_('cannot backout a merge changeset'))
665 raise error.Abort(_('cannot backout a merge changeset'))
666 p = repo.lookup(opts['parent'])
666 p = repo.lookup(opts['parent'])
667 if p not in (p1, p2):
667 if p not in (p1, p2):
668 raise error.Abort(_('%s is not a parent of %s') %
668 raise error.Abort(_('%s is not a parent of %s') %
669 (short(p), short(node)))
669 (short(p), short(node)))
670 parent = p
670 parent = p
671 else:
671 else:
672 if opts.get('parent'):
672 if opts.get('parent'):
673 raise error.Abort(_('cannot use --parent on non-merge changeset'))
673 raise error.Abort(_('cannot use --parent on non-merge changeset'))
674 parent = p1
674 parent = p1
675
675
676 # the backout should appear on the same branch
676 # the backout should appear on the same branch
677 branch = repo.dirstate.branch()
677 branch = repo.dirstate.branch()
678 bheads = repo.branchheads(branch)
678 bheads = repo.branchheads(branch)
679 rctx = scmutil.revsingle(repo, hex(parent))
679 rctx = scmutil.revsingle(repo, hex(parent))
680 if not opts.get('merge') and op1 != node:
680 if not opts.get('merge') and op1 != node:
681 dsguard = dirstateguard.dirstateguard(repo, 'backout')
681 dsguard = dirstateguard.dirstateguard(repo, 'backout')
682 try:
682 try:
683 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
683 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
684 'backout')
684 'backout')
685 stats = mergemod.update(repo, parent, True, True, node, False)
685 stats = mergemod.update(repo, parent, True, True, node, False)
686 repo.setparents(op1, op2)
686 repo.setparents(op1, op2)
687 dsguard.close()
687 dsguard.close()
688 hg._showstats(repo, stats)
688 hg._showstats(repo, stats)
689 if stats[3]:
689 if stats[3]:
690 repo.ui.status(_("use 'hg resolve' to retry unresolved "
690 repo.ui.status(_("use 'hg resolve' to retry unresolved "
691 "file merges\n"))
691 "file merges\n"))
692 return 1
692 return 1
693 finally:
693 finally:
694 ui.setconfig('ui', 'forcemerge', '', '')
694 ui.setconfig('ui', 'forcemerge', '', '')
695 lockmod.release(dsguard)
695 lockmod.release(dsguard)
696 else:
696 else:
697 hg.clean(repo, node, show_stats=False)
697 hg.clean(repo, node, show_stats=False)
698 repo.dirstate.setbranch(branch)
698 repo.dirstate.setbranch(branch)
699 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
699 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
700
700
701 if opts.get('no_commit'):
701 if opts.get('no_commit'):
702 msg = _("changeset %s backed out, "
702 msg = _("changeset %s backed out, "
703 "don't forget to commit.\n")
703 "don't forget to commit.\n")
704 ui.status(msg % short(node))
704 ui.status(msg % short(node))
705 return 0
705 return 0
706
706
707 def commitfunc(ui, repo, message, match, opts):
707 def commitfunc(ui, repo, message, match, opts):
708 editform = 'backout'
708 editform = 'backout'
709 e = cmdutil.getcommiteditor(editform=editform,
709 e = cmdutil.getcommiteditor(editform=editform,
710 **pycompat.strkwargs(opts))
710 **pycompat.strkwargs(opts))
711 if not message:
711 if not message:
712 # we don't translate commit messages
712 # we don't translate commit messages
713 message = "Backed out changeset %s" % short(node)
713 message = "Backed out changeset %s" % short(node)
714 e = cmdutil.getcommiteditor(edit=True, editform=editform)
714 e = cmdutil.getcommiteditor(edit=True, editform=editform)
715 return repo.commit(message, opts.get('user'), opts.get('date'),
715 return repo.commit(message, opts.get('user'), opts.get('date'),
716 match, editor=e)
716 match, editor=e)
717 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
717 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
718 if not newnode:
718 if not newnode:
719 ui.status(_("nothing changed\n"))
719 ui.status(_("nothing changed\n"))
720 return 1
720 return 1
721 cmdutil.commitstatus(repo, newnode, branch, bheads)
721 cmdutil.commitstatus(repo, newnode, branch, bheads)
722
722
723 def nice(node):
723 def nice(node):
724 return '%d:%s' % (repo.changelog.rev(node), short(node))
724 return '%d:%s' % (repo.changelog.rev(node), short(node))
725 ui.status(_('changeset %s backs out changeset %s\n') %
725 ui.status(_('changeset %s backs out changeset %s\n') %
726 (nice(repo.changelog.tip()), nice(node)))
726 (nice(repo.changelog.tip()), nice(node)))
727 if opts.get('merge') and op1 != node:
727 if opts.get('merge') and op1 != node:
728 hg.clean(repo, op1, show_stats=False)
728 hg.clean(repo, op1, show_stats=False)
729 ui.status(_('merging with changeset %s\n')
729 ui.status(_('merging with changeset %s\n')
730 % nice(repo.changelog.tip()))
730 % nice(repo.changelog.tip()))
731 try:
731 try:
732 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
732 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
733 'backout')
733 'backout')
734 return hg.merge(repo, hex(repo.changelog.tip()))
734 return hg.merge(repo, hex(repo.changelog.tip()))
735 finally:
735 finally:
736 ui.setconfig('ui', 'forcemerge', '', '')
736 ui.setconfig('ui', 'forcemerge', '', '')
737 return 0
737 return 0
738
738
739 @command('bisect',
739 @command('bisect',
740 [('r', 'reset', False, _('reset bisect state')),
740 [('r', 'reset', False, _('reset bisect state')),
741 ('g', 'good', False, _('mark changeset good')),
741 ('g', 'good', False, _('mark changeset good')),
742 ('b', 'bad', False, _('mark changeset bad')),
742 ('b', 'bad', False, _('mark changeset bad')),
743 ('s', 'skip', False, _('skip testing changeset')),
743 ('s', 'skip', False, _('skip testing changeset')),
744 ('e', 'extend', False, _('extend the bisect range')),
744 ('e', 'extend', False, _('extend the bisect range')),
745 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
745 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
746 ('U', 'noupdate', False, _('do not update to target'))],
746 ('U', 'noupdate', False, _('do not update to target'))],
747 _("[-gbsr] [-U] [-c CMD] [REV]"))
747 _("[-gbsr] [-U] [-c CMD] [REV]"))
748 def bisect(ui, repo, rev=None, extra=None, command=None,
748 def bisect(ui, repo, rev=None, extra=None, command=None,
749 reset=None, good=None, bad=None, skip=None, extend=None,
749 reset=None, good=None, bad=None, skip=None, extend=None,
750 noupdate=None):
750 noupdate=None):
751 """subdivision search of changesets
751 """subdivision search of changesets
752
752
753 This command helps to find changesets which introduce problems. To
753 This command helps to find changesets which introduce problems. To
754 use, mark the earliest changeset you know exhibits the problem as
754 use, mark the earliest changeset you know exhibits the problem as
755 bad, then mark the latest changeset which is free from the problem
755 bad, then mark the latest changeset which is free from the problem
756 as good. Bisect will update your working directory to a revision
756 as good. Bisect will update your working directory to a revision
757 for testing (unless the -U/--noupdate option is specified). Once
757 for testing (unless the -U/--noupdate option is specified). Once
758 you have performed tests, mark the working directory as good or
758 you have performed tests, mark the working directory as good or
759 bad, and bisect will either update to another candidate changeset
759 bad, and bisect will either update to another candidate changeset
760 or announce that it has found the bad revision.
760 or announce that it has found the bad revision.
761
761
762 As a shortcut, you can also use the revision argument to mark a
762 As a shortcut, you can also use the revision argument to mark a
763 revision as good or bad without checking it out first.
763 revision as good or bad without checking it out first.
764
764
765 If you supply a command, it will be used for automatic bisection.
765 If you supply a command, it will be used for automatic bisection.
766 The environment variable HG_NODE will contain the ID of the
766 The environment variable HG_NODE will contain the ID of the
767 changeset being tested. The exit status of the command will be
767 changeset being tested. The exit status of the command will be
768 used to mark revisions as good or bad: status 0 means good, 125
768 used to mark revisions as good or bad: status 0 means good, 125
769 means to skip the revision, 127 (command not found) will abort the
769 means to skip the revision, 127 (command not found) will abort the
770 bisection, and any other non-zero exit status means the revision
770 bisection, and any other non-zero exit status means the revision
771 is bad.
771 is bad.
772
772
773 .. container:: verbose
773 .. container:: verbose
774
774
775 Some examples:
775 Some examples:
776
776
777 - start a bisection with known bad revision 34, and good revision 12::
777 - start a bisection with known bad revision 34, and good revision 12::
778
778
779 hg bisect --bad 34
779 hg bisect --bad 34
780 hg bisect --good 12
780 hg bisect --good 12
781
781
782 - advance the current bisection by marking current revision as good or
782 - advance the current bisection by marking current revision as good or
783 bad::
783 bad::
784
784
785 hg bisect --good
785 hg bisect --good
786 hg bisect --bad
786 hg bisect --bad
787
787
788 - mark the current revision, or a known revision, to be skipped (e.g. if
788 - mark the current revision, or a known revision, to be skipped (e.g. if
789 that revision is not usable because of another issue)::
789 that revision is not usable because of another issue)::
790
790
791 hg bisect --skip
791 hg bisect --skip
792 hg bisect --skip 23
792 hg bisect --skip 23
793
793
794 - skip all revisions that do not touch directories ``foo`` or ``bar``::
794 - skip all revisions that do not touch directories ``foo`` or ``bar``::
795
795
796 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
796 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
797
797
798 - forget the current bisection::
798 - forget the current bisection::
799
799
800 hg bisect --reset
800 hg bisect --reset
801
801
802 - use 'make && make tests' to automatically find the first broken
802 - use 'make && make tests' to automatically find the first broken
803 revision::
803 revision::
804
804
805 hg bisect --reset
805 hg bisect --reset
806 hg bisect --bad 34
806 hg bisect --bad 34
807 hg bisect --good 12
807 hg bisect --good 12
808 hg bisect --command "make && make tests"
808 hg bisect --command "make && make tests"
809
809
810 - see all changesets whose states are already known in the current
810 - see all changesets whose states are already known in the current
811 bisection::
811 bisection::
812
812
813 hg log -r "bisect(pruned)"
813 hg log -r "bisect(pruned)"
814
814
815 - see the changeset currently being bisected (especially useful
815 - see the changeset currently being bisected (especially useful
816 if running with -U/--noupdate)::
816 if running with -U/--noupdate)::
817
817
818 hg log -r "bisect(current)"
818 hg log -r "bisect(current)"
819
819
820 - see all changesets that took part in the current bisection::
820 - see all changesets that took part in the current bisection::
821
821
822 hg log -r "bisect(range)"
822 hg log -r "bisect(range)"
823
823
824 - you can even get a nice graph::
824 - you can even get a nice graph::
825
825
826 hg log --graph -r "bisect(range)"
826 hg log --graph -r "bisect(range)"
827
827
828 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
828 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
829
829
830 Returns 0 on success.
830 Returns 0 on success.
831 """
831 """
832 # backward compatibility
832 # backward compatibility
833 if rev in "good bad reset init".split():
833 if rev in "good bad reset init".split():
834 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
834 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
835 cmd, rev, extra = rev, extra, None
835 cmd, rev, extra = rev, extra, None
836 if cmd == "good":
836 if cmd == "good":
837 good = True
837 good = True
838 elif cmd == "bad":
838 elif cmd == "bad":
839 bad = True
839 bad = True
840 else:
840 else:
841 reset = True
841 reset = True
842 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
842 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
843 raise error.Abort(_('incompatible arguments'))
843 raise error.Abort(_('incompatible arguments'))
844
844
845 if reset:
845 if reset:
846 hbisect.resetstate(repo)
846 hbisect.resetstate(repo)
847 return
847 return
848
848
849 state = hbisect.load_state(repo)
849 state = hbisect.load_state(repo)
850
850
851 # update state
851 # update state
852 if good or bad or skip:
852 if good or bad or skip:
853 if rev:
853 if rev:
854 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
854 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
855 else:
855 else:
856 nodes = [repo.lookup('.')]
856 nodes = [repo.lookup('.')]
857 if good:
857 if good:
858 state['good'] += nodes
858 state['good'] += nodes
859 elif bad:
859 elif bad:
860 state['bad'] += nodes
860 state['bad'] += nodes
861 elif skip:
861 elif skip:
862 state['skip'] += nodes
862 state['skip'] += nodes
863 hbisect.save_state(repo, state)
863 hbisect.save_state(repo, state)
864 if not (state['good'] and state['bad']):
864 if not (state['good'] and state['bad']):
865 return
865 return
866
866
867 def mayupdate(repo, node, show_stats=True):
867 def mayupdate(repo, node, show_stats=True):
868 """common used update sequence"""
868 """common used update sequence"""
869 if noupdate:
869 if noupdate:
870 return
870 return
871 cmdutil.checkunfinished(repo)
871 cmdutil.checkunfinished(repo)
872 cmdutil.bailifchanged(repo)
872 cmdutil.bailifchanged(repo)
873 return hg.clean(repo, node, show_stats=show_stats)
873 return hg.clean(repo, node, show_stats=show_stats)
874
874
875 displayer = cmdutil.show_changeset(ui, repo, {})
875 displayer = cmdutil.show_changeset(ui, repo, {})
876
876
877 if command:
877 if command:
878 changesets = 1
878 changesets = 1
879 if noupdate:
879 if noupdate:
880 try:
880 try:
881 node = state['current'][0]
881 node = state['current'][0]
882 except LookupError:
882 except LookupError:
883 raise error.Abort(_('current bisect revision is unknown - '
883 raise error.Abort(_('current bisect revision is unknown - '
884 'start a new bisect to fix'))
884 'start a new bisect to fix'))
885 else:
885 else:
886 node, p2 = repo.dirstate.parents()
886 node, p2 = repo.dirstate.parents()
887 if p2 != nullid:
887 if p2 != nullid:
888 raise error.Abort(_('current bisect revision is a merge'))
888 raise error.Abort(_('current bisect revision is a merge'))
889 if rev:
889 if rev:
890 node = repo[scmutil.revsingle(repo, rev, node)].node()
890 node = repo[scmutil.revsingle(repo, rev, node)].node()
891 try:
891 try:
892 while changesets:
892 while changesets:
893 # update state
893 # update state
894 state['current'] = [node]
894 state['current'] = [node]
895 hbisect.save_state(repo, state)
895 hbisect.save_state(repo, state)
896 status = ui.system(command, environ={'HG_NODE': hex(node)},
896 status = ui.system(command, environ={'HG_NODE': hex(node)},
897 blockedtag='bisect_check')
897 blockedtag='bisect_check')
898 if status == 125:
898 if status == 125:
899 transition = "skip"
899 transition = "skip"
900 elif status == 0:
900 elif status == 0:
901 transition = "good"
901 transition = "good"
902 # status < 0 means process was killed
902 # status < 0 means process was killed
903 elif status == 127:
903 elif status == 127:
904 raise error.Abort(_("failed to execute %s") % command)
904 raise error.Abort(_("failed to execute %s") % command)
905 elif status < 0:
905 elif status < 0:
906 raise error.Abort(_("%s killed") % command)
906 raise error.Abort(_("%s killed") % command)
907 else:
907 else:
908 transition = "bad"
908 transition = "bad"
909 state[transition].append(node)
909 state[transition].append(node)
910 ctx = repo[node]
910 ctx = repo[node]
911 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
911 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
912 hbisect.checkstate(state)
912 hbisect.checkstate(state)
913 # bisect
913 # bisect
914 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
914 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
915 # update to next check
915 # update to next check
916 node = nodes[0]
916 node = nodes[0]
917 mayupdate(repo, node, show_stats=False)
917 mayupdate(repo, node, show_stats=False)
918 finally:
918 finally:
919 state['current'] = [node]
919 state['current'] = [node]
920 hbisect.save_state(repo, state)
920 hbisect.save_state(repo, state)
921 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
921 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
922 return
922 return
923
923
924 hbisect.checkstate(state)
924 hbisect.checkstate(state)
925
925
926 # actually bisect
926 # actually bisect
927 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
927 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
928 if extend:
928 if extend:
929 if not changesets:
929 if not changesets:
930 extendnode = hbisect.extendrange(repo, state, nodes, good)
930 extendnode = hbisect.extendrange(repo, state, nodes, good)
931 if extendnode is not None:
931 if extendnode is not None:
932 ui.write(_("Extending search to changeset %d:%s\n")
932 ui.write(_("Extending search to changeset %d:%s\n")
933 % (extendnode.rev(), extendnode))
933 % (extendnode.rev(), extendnode))
934 state['current'] = [extendnode.node()]
934 state['current'] = [extendnode.node()]
935 hbisect.save_state(repo, state)
935 hbisect.save_state(repo, state)
936 return mayupdate(repo, extendnode.node())
936 return mayupdate(repo, extendnode.node())
937 raise error.Abort(_("nothing to extend"))
937 raise error.Abort(_("nothing to extend"))
938
938
939 if changesets == 0:
939 if changesets == 0:
940 hbisect.printresult(ui, repo, state, displayer, nodes, good)
940 hbisect.printresult(ui, repo, state, displayer, nodes, good)
941 else:
941 else:
942 assert len(nodes) == 1 # only a single node can be tested next
942 assert len(nodes) == 1 # only a single node can be tested next
943 node = nodes[0]
943 node = nodes[0]
944 # compute the approximate number of remaining tests
944 # compute the approximate number of remaining tests
945 tests, size = 0, 2
945 tests, size = 0, 2
946 while size <= changesets:
946 while size <= changesets:
947 tests, size = tests + 1, size * 2
947 tests, size = tests + 1, size * 2
948 rev = repo.changelog.rev(node)
948 rev = repo.changelog.rev(node)
949 ui.write(_("Testing changeset %d:%s "
949 ui.write(_("Testing changeset %d:%s "
950 "(%d changesets remaining, ~%d tests)\n")
950 "(%d changesets remaining, ~%d tests)\n")
951 % (rev, short(node), changesets, tests))
951 % (rev, short(node), changesets, tests))
952 state['current'] = [node]
952 state['current'] = [node]
953 hbisect.save_state(repo, state)
953 hbisect.save_state(repo, state)
954 return mayupdate(repo, node)
954 return mayupdate(repo, node)
955
955
956 @command('bookmarks|bookmark',
956 @command('bookmarks|bookmark',
957 [('f', 'force', False, _('force')),
957 [('f', 'force', False, _('force')),
958 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
958 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
959 ('d', 'delete', False, _('delete a given bookmark')),
959 ('d', 'delete', False, _('delete a given bookmark')),
960 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
960 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
961 ('i', 'inactive', False, _('mark a bookmark inactive')),
961 ('i', 'inactive', False, _('mark a bookmark inactive')),
962 ] + formatteropts,
962 ] + formatteropts,
963 _('hg bookmarks [OPTIONS]... [NAME]...'))
963 _('hg bookmarks [OPTIONS]... [NAME]...'))
964 def bookmark(ui, repo, *names, **opts):
964 def bookmark(ui, repo, *names, **opts):
965 '''create a new bookmark or list existing bookmarks
965 '''create a new bookmark or list existing bookmarks
966
966
967 Bookmarks are labels on changesets to help track lines of development.
967 Bookmarks are labels on changesets to help track lines of development.
968 Bookmarks are unversioned and can be moved, renamed and deleted.
968 Bookmarks are unversioned and can be moved, renamed and deleted.
969 Deleting or moving a bookmark has no effect on the associated changesets.
969 Deleting or moving a bookmark has no effect on the associated changesets.
970
970
971 Creating or updating to a bookmark causes it to be marked as 'active'.
971 Creating or updating to a bookmark causes it to be marked as 'active'.
972 The active bookmark is indicated with a '*'.
972 The active bookmark is indicated with a '*'.
973 When a commit is made, the active bookmark will advance to the new commit.
973 When a commit is made, the active bookmark will advance to the new commit.
974 A plain :hg:`update` will also advance an active bookmark, if possible.
974 A plain :hg:`update` will also advance an active bookmark, if possible.
975 Updating away from a bookmark will cause it to be deactivated.
975 Updating away from a bookmark will cause it to be deactivated.
976
976
977 Bookmarks can be pushed and pulled between repositories (see
977 Bookmarks can be pushed and pulled between repositories (see
978 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
978 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
979 diverged, a new 'divergent bookmark' of the form 'name@path' will
979 diverged, a new 'divergent bookmark' of the form 'name@path' will
980 be created. Using :hg:`merge` will resolve the divergence.
980 be created. Using :hg:`merge` will resolve the divergence.
981
981
982 A bookmark named '@' has the special property that :hg:`clone` will
982 A bookmark named '@' has the special property that :hg:`clone` will
983 check it out by default if it exists.
983 check it out by default if it exists.
984
984
985 .. container:: verbose
985 .. container:: verbose
986
986
987 Examples:
987 Examples:
988
988
989 - create an active bookmark for a new line of development::
989 - create an active bookmark for a new line of development::
990
990
991 hg book new-feature
991 hg book new-feature
992
992
993 - create an inactive bookmark as a place marker::
993 - create an inactive bookmark as a place marker::
994
994
995 hg book -i reviewed
995 hg book -i reviewed
996
996
997 - create an inactive bookmark on another changeset::
997 - create an inactive bookmark on another changeset::
998
998
999 hg book -r .^ tested
999 hg book -r .^ tested
1000
1000
1001 - rename bookmark turkey to dinner::
1001 - rename bookmark turkey to dinner::
1002
1002
1003 hg book -m turkey dinner
1003 hg book -m turkey dinner
1004
1004
1005 - move the '@' bookmark from another branch::
1005 - move the '@' bookmark from another branch::
1006
1006
1007 hg book -f @
1007 hg book -f @
1008 '''
1008 '''
1009 opts = pycompat.byteskwargs(opts)
1009 opts = pycompat.byteskwargs(opts)
1010 force = opts.get('force')
1010 force = opts.get('force')
1011 rev = opts.get('rev')
1011 rev = opts.get('rev')
1012 delete = opts.get('delete')
1012 delete = opts.get('delete')
1013 rename = opts.get('rename')
1013 rename = opts.get('rename')
1014 inactive = opts.get('inactive')
1014 inactive = opts.get('inactive')
1015
1015
1016 def checkformat(mark):
1016 def checkformat(mark):
1017 mark = mark.strip()
1017 mark = mark.strip()
1018 if not mark:
1018 if not mark:
1019 raise error.Abort(_("bookmark names cannot consist entirely of "
1019 raise error.Abort(_("bookmark names cannot consist entirely of "
1020 "whitespace"))
1020 "whitespace"))
1021 scmutil.checknewlabel(repo, mark, 'bookmark')
1021 scmutil.checknewlabel(repo, mark, 'bookmark')
1022 return mark
1022 return mark
1023
1023
1024 def checkconflict(repo, mark, cur, force=False, target=None):
1024 def checkconflict(repo, mark, cur, force=False, target=None):
1025 if mark in marks and not force:
1025 if mark in marks and not force:
1026 if target:
1026 if target:
1027 if marks[mark] == target and target == cur:
1027 if marks[mark] == target and target == cur:
1028 # re-activating a bookmark
1028 # re-activating a bookmark
1029 return
1029 return
1030 anc = repo.changelog.ancestors([repo[target].rev()])
1030 anc = repo.changelog.ancestors([repo[target].rev()])
1031 bmctx = repo[marks[mark]]
1031 bmctx = repo[marks[mark]]
1032 divs = [repo[b].node() for b in marks
1032 divs = [repo[b].node() for b in marks
1033 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1033 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1034
1034
1035 # allow resolving a single divergent bookmark even if moving
1035 # allow resolving a single divergent bookmark even if moving
1036 # the bookmark across branches when a revision is specified
1036 # the bookmark across branches when a revision is specified
1037 # that contains a divergent bookmark
1037 # that contains a divergent bookmark
1038 if bmctx.rev() not in anc and target in divs:
1038 if bmctx.rev() not in anc and target in divs:
1039 bookmarks.deletedivergent(repo, [target], mark)
1039 bookmarks.deletedivergent(repo, [target], mark)
1040 return
1040 return
1041
1041
1042 deletefrom = [b for b in divs
1042 deletefrom = [b for b in divs
1043 if repo[b].rev() in anc or b == target]
1043 if repo[b].rev() in anc or b == target]
1044 bookmarks.deletedivergent(repo, deletefrom, mark)
1044 bookmarks.deletedivergent(repo, deletefrom, mark)
1045 if bookmarks.validdest(repo, bmctx, repo[target]):
1045 if bookmarks.validdest(repo, bmctx, repo[target]):
1046 ui.status(_("moving bookmark '%s' forward from %s\n") %
1046 ui.status(_("moving bookmark '%s' forward from %s\n") %
1047 (mark, short(bmctx.node())))
1047 (mark, short(bmctx.node())))
1048 return
1048 return
1049 raise error.Abort(_("bookmark '%s' already exists "
1049 raise error.Abort(_("bookmark '%s' already exists "
1050 "(use -f to force)") % mark)
1050 "(use -f to force)") % mark)
1051 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1051 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1052 and not force):
1052 and not force):
1053 raise error.Abort(
1053 raise error.Abort(
1054 _("a bookmark cannot have the name of an existing branch"))
1054 _("a bookmark cannot have the name of an existing branch"))
1055
1055
1056 if delete and rename:
1056 if delete and rename:
1057 raise error.Abort(_("--delete and --rename are incompatible"))
1057 raise error.Abort(_("--delete and --rename are incompatible"))
1058 if delete and rev:
1058 if delete and rev:
1059 raise error.Abort(_("--rev is incompatible with --delete"))
1059 raise error.Abort(_("--rev is incompatible with --delete"))
1060 if rename and rev:
1060 if rename and rev:
1061 raise error.Abort(_("--rev is incompatible with --rename"))
1061 raise error.Abort(_("--rev is incompatible with --rename"))
1062 if not names and (delete or rev):
1062 if not names and (delete or rev):
1063 raise error.Abort(_("bookmark name required"))
1063 raise error.Abort(_("bookmark name required"))
1064
1064
1065 if delete or rename or names or inactive:
1065 if delete or rename or names or inactive:
1066 wlock = lock = tr = None
1066 wlock = lock = tr = None
1067 try:
1067 try:
1068 wlock = repo.wlock()
1068 wlock = repo.wlock()
1069 lock = repo.lock()
1069 lock = repo.lock()
1070 cur = repo.changectx('.').node()
1070 cur = repo.changectx('.').node()
1071 marks = repo._bookmarks
1071 marks = repo._bookmarks
1072 if delete:
1072 if delete:
1073 tr = repo.transaction('bookmark')
1073 tr = repo.transaction('bookmark')
1074 for mark in names:
1074 for mark in names:
1075 if mark not in marks:
1075 if mark not in marks:
1076 raise error.Abort(_("bookmark '%s' does not exist") %
1076 raise error.Abort(_("bookmark '%s' does not exist") %
1077 mark)
1077 mark)
1078 if mark == repo._activebookmark:
1078 if mark == repo._activebookmark:
1079 bookmarks.deactivate(repo)
1079 bookmarks.deactivate(repo)
1080 del marks[mark]
1080 del marks[mark]
1081
1081
1082 elif rename:
1082 elif rename:
1083 tr = repo.transaction('bookmark')
1083 tr = repo.transaction('bookmark')
1084 if not names:
1084 if not names:
1085 raise error.Abort(_("new bookmark name required"))
1085 raise error.Abort(_("new bookmark name required"))
1086 elif len(names) > 1:
1086 elif len(names) > 1:
1087 raise error.Abort(_("only one new bookmark name allowed"))
1087 raise error.Abort(_("only one new bookmark name allowed"))
1088 mark = checkformat(names[0])
1088 mark = checkformat(names[0])
1089 if rename not in marks:
1089 if rename not in marks:
1090 raise error.Abort(_("bookmark '%s' does not exist")
1090 raise error.Abort(_("bookmark '%s' does not exist")
1091 % rename)
1091 % rename)
1092 checkconflict(repo, mark, cur, force)
1092 checkconflict(repo, mark, cur, force)
1093 marks[mark] = marks[rename]
1093 marks[mark] = marks[rename]
1094 if repo._activebookmark == rename and not inactive:
1094 if repo._activebookmark == rename and not inactive:
1095 bookmarks.activate(repo, mark)
1095 bookmarks.activate(repo, mark)
1096 del marks[rename]
1096 del marks[rename]
1097 elif names:
1097 elif names:
1098 tr = repo.transaction('bookmark')
1098 tr = repo.transaction('bookmark')
1099 newact = None
1099 newact = None
1100 for mark in names:
1100 for mark in names:
1101 mark = checkformat(mark)
1101 mark = checkformat(mark)
1102 if newact is None:
1102 if newact is None:
1103 newact = mark
1103 newact = mark
1104 if inactive and mark == repo._activebookmark:
1104 if inactive and mark == repo._activebookmark:
1105 bookmarks.deactivate(repo)
1105 bookmarks.deactivate(repo)
1106 return
1106 return
1107 tgt = cur
1107 tgt = cur
1108 if rev:
1108 if rev:
1109 tgt = scmutil.revsingle(repo, rev).node()
1109 tgt = scmutil.revsingle(repo, rev).node()
1110 checkconflict(repo, mark, cur, force, tgt)
1110 checkconflict(repo, mark, cur, force, tgt)
1111 marks[mark] = tgt
1111 marks[mark] = tgt
1112 if not inactive and cur == marks[newact] and not rev:
1112 if not inactive and cur == marks[newact] and not rev:
1113 bookmarks.activate(repo, newact)
1113 bookmarks.activate(repo, newact)
1114 elif cur != tgt and newact == repo._activebookmark:
1114 elif cur != tgt and newact == repo._activebookmark:
1115 bookmarks.deactivate(repo)
1115 bookmarks.deactivate(repo)
1116 elif inactive:
1116 elif inactive:
1117 if len(marks) == 0:
1117 if len(marks) == 0:
1118 ui.status(_("no bookmarks set\n"))
1118 ui.status(_("no bookmarks set\n"))
1119 elif not repo._activebookmark:
1119 elif not repo._activebookmark:
1120 ui.status(_("no active bookmark\n"))
1120 ui.status(_("no active bookmark\n"))
1121 else:
1121 else:
1122 bookmarks.deactivate(repo)
1122 bookmarks.deactivate(repo)
1123 if tr is not None:
1123 if tr is not None:
1124 marks.recordchange(tr)
1124 marks.recordchange(tr)
1125 tr.close()
1125 tr.close()
1126 finally:
1126 finally:
1127 lockmod.release(tr, lock, wlock)
1127 lockmod.release(tr, lock, wlock)
1128 else: # show bookmarks
1128 else: # show bookmarks
1129 fm = ui.formatter('bookmarks', opts)
1129 fm = ui.formatter('bookmarks', opts)
1130 hexfn = fm.hexfunc
1130 hexfn = fm.hexfunc
1131 marks = repo._bookmarks
1131 marks = repo._bookmarks
1132 if len(marks) == 0 and fm.isplain():
1132 if len(marks) == 0 and fm.isplain():
1133 ui.status(_("no bookmarks set\n"))
1133 ui.status(_("no bookmarks set\n"))
1134 for bmark, n in sorted(marks.iteritems()):
1134 for bmark, n in sorted(marks.iteritems()):
1135 active = repo._activebookmark
1135 active = repo._activebookmark
1136 if bmark == active:
1136 if bmark == active:
1137 prefix, label = '*', activebookmarklabel
1137 prefix, label = '*', activebookmarklabel
1138 else:
1138 else:
1139 prefix, label = ' ', ''
1139 prefix, label = ' ', ''
1140
1140
1141 fm.startitem()
1141 fm.startitem()
1142 if not ui.quiet:
1142 if not ui.quiet:
1143 fm.plain(' %s ' % prefix, label=label)
1143 fm.plain(' %s ' % prefix, label=label)
1144 fm.write('bookmark', '%s', bmark, label=label)
1144 fm.write('bookmark', '%s', bmark, label=label)
1145 pad = " " * (25 - encoding.colwidth(bmark))
1145 pad = " " * (25 - encoding.colwidth(bmark))
1146 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1146 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1147 repo.changelog.rev(n), hexfn(n), label=label)
1147 repo.changelog.rev(n), hexfn(n), label=label)
1148 fm.data(active=(bmark == active))
1148 fm.data(active=(bmark == active))
1149 fm.plain('\n')
1149 fm.plain('\n')
1150 fm.end()
1150 fm.end()
1151
1151
1152 @command('branch',
1152 @command('branch',
1153 [('f', 'force', None,
1153 [('f', 'force', None,
1154 _('set branch name even if it shadows an existing branch')),
1154 _('set branch name even if it shadows an existing branch')),
1155 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1155 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1156 _('[-fC] [NAME]'))
1156 _('[-fC] [NAME]'))
1157 def branch(ui, repo, label=None, **opts):
1157 def branch(ui, repo, label=None, **opts):
1158 """set or show the current branch name
1158 """set or show the current branch name
1159
1159
1160 .. note::
1160 .. note::
1161
1161
1162 Branch names are permanent and global. Use :hg:`bookmark` to create a
1162 Branch names are permanent and global. Use :hg:`bookmark` to create a
1163 light-weight bookmark instead. See :hg:`help glossary` for more
1163 light-weight bookmark instead. See :hg:`help glossary` for more
1164 information about named branches and bookmarks.
1164 information about named branches and bookmarks.
1165
1165
1166 With no argument, show the current branch name. With one argument,
1166 With no argument, show the current branch name. With one argument,
1167 set the working directory branch name (the branch will not exist
1167 set the working directory branch name (the branch will not exist
1168 in the repository until the next commit). Standard practice
1168 in the repository until the next commit). Standard practice
1169 recommends that primary development take place on the 'default'
1169 recommends that primary development take place on the 'default'
1170 branch.
1170 branch.
1171
1171
1172 Unless -f/--force is specified, branch will not let you set a
1172 Unless -f/--force is specified, branch will not let you set a
1173 branch name that already exists.
1173 branch name that already exists.
1174
1174
1175 Use -C/--clean to reset the working directory branch to that of
1175 Use -C/--clean to reset the working directory branch to that of
1176 the parent of the working directory, negating a previous branch
1176 the parent of the working directory, negating a previous branch
1177 change.
1177 change.
1178
1178
1179 Use the command :hg:`update` to switch to an existing branch. Use
1179 Use the command :hg:`update` to switch to an existing branch. Use
1180 :hg:`commit --close-branch` to mark this branch head as closed.
1180 :hg:`commit --close-branch` to mark this branch head as closed.
1181 When all heads of a branch are closed, the branch will be
1181 When all heads of a branch are closed, the branch will be
1182 considered closed.
1182 considered closed.
1183
1183
1184 Returns 0 on success.
1184 Returns 0 on success.
1185 """
1185 """
1186 opts = pycompat.byteskwargs(opts)
1186 opts = pycompat.byteskwargs(opts)
1187 if label:
1187 if label:
1188 label = label.strip()
1188 label = label.strip()
1189
1189
1190 if not opts.get('clean') and not label:
1190 if not opts.get('clean') and not label:
1191 ui.write("%s\n" % repo.dirstate.branch())
1191 ui.write("%s\n" % repo.dirstate.branch())
1192 return
1192 return
1193
1193
1194 with repo.wlock():
1194 with repo.wlock():
1195 if opts.get('clean'):
1195 if opts.get('clean'):
1196 label = repo[None].p1().branch()
1196 label = repo[None].p1().branch()
1197 repo.dirstate.setbranch(label)
1197 repo.dirstate.setbranch(label)
1198 ui.status(_('reset working directory to branch %s\n') % label)
1198 ui.status(_('reset working directory to branch %s\n') % label)
1199 elif label:
1199 elif label:
1200 if not opts.get('force') and label in repo.branchmap():
1200 if not opts.get('force') and label in repo.branchmap():
1201 if label not in [p.branch() for p in repo[None].parents()]:
1201 if label not in [p.branch() for p in repo[None].parents()]:
1202 raise error.Abort(_('a branch of the same name already'
1202 raise error.Abort(_('a branch of the same name already'
1203 ' exists'),
1203 ' exists'),
1204 # i18n: "it" refers to an existing branch
1204 # i18n: "it" refers to an existing branch
1205 hint=_("use 'hg update' to switch to it"))
1205 hint=_("use 'hg update' to switch to it"))
1206 scmutil.checknewlabel(repo, label, 'branch')
1206 scmutil.checknewlabel(repo, label, 'branch')
1207 repo.dirstate.setbranch(label)
1207 repo.dirstate.setbranch(label)
1208 ui.status(_('marked working directory as branch %s\n') % label)
1208 ui.status(_('marked working directory as branch %s\n') % label)
1209
1209
1210 # find any open named branches aside from default
1210 # find any open named branches aside from default
1211 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1211 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1212 if n != "default" and not c]
1212 if n != "default" and not c]
1213 if not others:
1213 if not others:
1214 ui.status(_('(branches are permanent and global, '
1214 ui.status(_('(branches are permanent and global, '
1215 'did you want a bookmark?)\n'))
1215 'did you want a bookmark?)\n'))
1216
1216
1217 @command('branches',
1217 @command('branches',
1218 [('a', 'active', False,
1218 [('a', 'active', False,
1219 _('show only branches that have unmerged heads (DEPRECATED)')),
1219 _('show only branches that have unmerged heads (DEPRECATED)')),
1220 ('c', 'closed', False, _('show normal and closed branches')),
1220 ('c', 'closed', False, _('show normal and closed branches')),
1221 ] + formatteropts,
1221 ] + formatteropts,
1222 _('[-c]'))
1222 _('[-c]'))
1223 def branches(ui, repo, active=False, closed=False, **opts):
1223 def branches(ui, repo, active=False, closed=False, **opts):
1224 """list repository named branches
1224 """list repository named branches
1225
1225
1226 List the repository's named branches, indicating which ones are
1226 List the repository's named branches, indicating which ones are
1227 inactive. If -c/--closed is specified, also list branches which have
1227 inactive. If -c/--closed is specified, also list branches which have
1228 been marked closed (see :hg:`commit --close-branch`).
1228 been marked closed (see :hg:`commit --close-branch`).
1229
1229
1230 Use the command :hg:`update` to switch to an existing branch.
1230 Use the command :hg:`update` to switch to an existing branch.
1231
1231
1232 Returns 0.
1232 Returns 0.
1233 """
1233 """
1234
1234
1235 opts = pycompat.byteskwargs(opts)
1235 opts = pycompat.byteskwargs(opts)
1236 ui.pager('branches')
1236 ui.pager('branches')
1237 fm = ui.formatter('branches', opts)
1237 fm = ui.formatter('branches', opts)
1238 hexfunc = fm.hexfunc
1238 hexfunc = fm.hexfunc
1239
1239
1240 allheads = set(repo.heads())
1240 allheads = set(repo.heads())
1241 branches = []
1241 branches = []
1242 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1242 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1243 isactive = not isclosed and bool(set(heads) & allheads)
1243 isactive = not isclosed and bool(set(heads) & allheads)
1244 branches.append((tag, repo[tip], isactive, not isclosed))
1244 branches.append((tag, repo[tip], isactive, not isclosed))
1245 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1245 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1246 reverse=True)
1246 reverse=True)
1247
1247
1248 for tag, ctx, isactive, isopen in branches:
1248 for tag, ctx, isactive, isopen in branches:
1249 if active and not isactive:
1249 if active and not isactive:
1250 continue
1250 continue
1251 if isactive:
1251 if isactive:
1252 label = 'branches.active'
1252 label = 'branches.active'
1253 notice = ''
1253 notice = ''
1254 elif not isopen:
1254 elif not isopen:
1255 if not closed:
1255 if not closed:
1256 continue
1256 continue
1257 label = 'branches.closed'
1257 label = 'branches.closed'
1258 notice = _(' (closed)')
1258 notice = _(' (closed)')
1259 else:
1259 else:
1260 label = 'branches.inactive'
1260 label = 'branches.inactive'
1261 notice = _(' (inactive)')
1261 notice = _(' (inactive)')
1262 current = (tag == repo.dirstate.branch())
1262 current = (tag == repo.dirstate.branch())
1263 if current:
1263 if current:
1264 label = 'branches.current'
1264 label = 'branches.current'
1265
1265
1266 fm.startitem()
1266 fm.startitem()
1267 fm.write('branch', '%s', tag, label=label)
1267 fm.write('branch', '%s', tag, label=label)
1268 rev = ctx.rev()
1268 rev = ctx.rev()
1269 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1269 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1270 fmt = ' ' * padsize + ' %d:%s'
1270 fmt = ' ' * padsize + ' %d:%s'
1271 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1271 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1272 label='log.changeset changeset.%s' % ctx.phasestr())
1272 label='log.changeset changeset.%s' % ctx.phasestr())
1273 fm.context(ctx=ctx)
1273 fm.context(ctx=ctx)
1274 fm.data(active=isactive, closed=not isopen, current=current)
1274 fm.data(active=isactive, closed=not isopen, current=current)
1275 if not ui.quiet:
1275 if not ui.quiet:
1276 fm.plain(notice)
1276 fm.plain(notice)
1277 fm.plain('\n')
1277 fm.plain('\n')
1278 fm.end()
1278 fm.end()
1279
1279
1280 @command('bundle',
1280 @command('bundle',
1281 [('f', 'force', None, _('run even when the destination is unrelated')),
1281 [('f', 'force', None, _('run even when the destination is unrelated')),
1282 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1282 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1283 _('REV')),
1283 _('REV')),
1284 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1284 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1285 _('BRANCH')),
1285 _('BRANCH')),
1286 ('', 'base', [],
1286 ('', 'base', [],
1287 _('a base changeset assumed to be available at the destination'),
1287 _('a base changeset assumed to be available at the destination'),
1288 _('REV')),
1288 _('REV')),
1289 ('a', 'all', None, _('bundle all changesets in the repository')),
1289 ('a', 'all', None, _('bundle all changesets in the repository')),
1290 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1290 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1291 ] + remoteopts,
1291 ] + remoteopts,
1292 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1292 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1293 def bundle(ui, repo, fname, dest=None, **opts):
1293 def bundle(ui, repo, fname, dest=None, **opts):
1294 """create a bundle file
1294 """create a bundle file
1295
1295
1296 Generate a bundle file containing data to be added to a repository.
1296 Generate a bundle file containing data to be added to a repository.
1297
1297
1298 To create a bundle containing all changesets, use -a/--all
1298 To create a bundle containing all changesets, use -a/--all
1299 (or --base null). Otherwise, hg assumes the destination will have
1299 (or --base null). Otherwise, hg assumes the destination will have
1300 all the nodes you specify with --base parameters. Otherwise, hg
1300 all the nodes you specify with --base parameters. Otherwise, hg
1301 will assume the repository has all the nodes in destination, or
1301 will assume the repository has all the nodes in destination, or
1302 default-push/default if no destination is specified.
1302 default-push/default if no destination is specified.
1303
1303
1304 You can change bundle format with the -t/--type option. See
1304 You can change bundle format with the -t/--type option. See
1305 :hg:`help bundlespec` for documentation on this format. By default,
1305 :hg:`help bundlespec` for documentation on this format. By default,
1306 the most appropriate format is used and compression defaults to
1306 the most appropriate format is used and compression defaults to
1307 bzip2.
1307 bzip2.
1308
1308
1309 The bundle file can then be transferred using conventional means
1309 The bundle file can then be transferred using conventional means
1310 and applied to another repository with the unbundle or pull
1310 and applied to another repository with the unbundle or pull
1311 command. This is useful when direct push and pull are not
1311 command. This is useful when direct push and pull are not
1312 available or when exporting an entire repository is undesirable.
1312 available or when exporting an entire repository is undesirable.
1313
1313
1314 Applying bundles preserves all changeset contents including
1314 Applying bundles preserves all changeset contents including
1315 permissions, copy/rename information, and revision history.
1315 permissions, copy/rename information, and revision history.
1316
1316
1317 Returns 0 on success, 1 if no changes found.
1317 Returns 0 on success, 1 if no changes found.
1318 """
1318 """
1319 opts = pycompat.byteskwargs(opts)
1319 opts = pycompat.byteskwargs(opts)
1320 revs = None
1320 revs = None
1321 if 'rev' in opts:
1321 if 'rev' in opts:
1322 revstrings = opts['rev']
1322 revstrings = opts['rev']
1323 revs = scmutil.revrange(repo, revstrings)
1323 revs = scmutil.revrange(repo, revstrings)
1324 if revstrings and not revs:
1324 if revstrings and not revs:
1325 raise error.Abort(_('no commits to bundle'))
1325 raise error.Abort(_('no commits to bundle'))
1326
1326
1327 bundletype = opts.get('type', 'bzip2').lower()
1327 bundletype = opts.get('type', 'bzip2').lower()
1328 try:
1328 try:
1329 bcompression, cgversion, params = exchange.parsebundlespec(
1329 bcompression, cgversion, params = exchange.parsebundlespec(
1330 repo, bundletype, strict=False)
1330 repo, bundletype, strict=False)
1331 except error.UnsupportedBundleSpecification as e:
1331 except error.UnsupportedBundleSpecification as e:
1332 raise error.Abort(str(e),
1332 raise error.Abort(str(e),
1333 hint=_("see 'hg help bundlespec' for supported "
1333 hint=_("see 'hg help bundlespec' for supported "
1334 "values for --type"))
1334 "values for --type"))
1335
1335
1336 # Packed bundles are a pseudo bundle format for now.
1336 # Packed bundles are a pseudo bundle format for now.
1337 if cgversion == 's1':
1337 if cgversion == 's1':
1338 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1338 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1339 hint=_("use 'hg debugcreatestreamclonebundle'"))
1339 hint=_("use 'hg debugcreatestreamclonebundle'"))
1340
1340
1341 if opts.get('all'):
1341 if opts.get('all'):
1342 if dest:
1342 if dest:
1343 raise error.Abort(_("--all is incompatible with specifying "
1343 raise error.Abort(_("--all is incompatible with specifying "
1344 "a destination"))
1344 "a destination"))
1345 if opts.get('base'):
1345 if opts.get('base'):
1346 ui.warn(_("ignoring --base because --all was specified\n"))
1346 ui.warn(_("ignoring --base because --all was specified\n"))
1347 base = ['null']
1347 base = ['null']
1348 else:
1348 else:
1349 base = scmutil.revrange(repo, opts.get('base'))
1349 base = scmutil.revrange(repo, opts.get('base'))
1350 if cgversion not in changegroup.supportedoutgoingversions(repo):
1350 if cgversion not in changegroup.supportedoutgoingversions(repo):
1351 raise error.Abort(_("repository does not support bundle version %s") %
1351 raise error.Abort(_("repository does not support bundle version %s") %
1352 cgversion)
1352 cgversion)
1353
1353
1354 if base:
1354 if base:
1355 if dest:
1355 if dest:
1356 raise error.Abort(_("--base is incompatible with specifying "
1356 raise error.Abort(_("--base is incompatible with specifying "
1357 "a destination"))
1357 "a destination"))
1358 common = [repo.lookup(rev) for rev in base]
1358 common = [repo.lookup(rev) for rev in base]
1359 heads = revs and map(repo.lookup, revs) or None
1359 heads = revs and map(repo.lookup, revs) or None
1360 outgoing = discovery.outgoing(repo, common, heads)
1360 outgoing = discovery.outgoing(repo, common, heads)
1361 else:
1361 else:
1362 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1362 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1363 dest, branches = hg.parseurl(dest, opts.get('branch'))
1363 dest, branches = hg.parseurl(dest, opts.get('branch'))
1364 other = hg.peer(repo, opts, dest)
1364 other = hg.peer(repo, opts, dest)
1365 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1365 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1366 heads = revs and map(repo.lookup, revs) or revs
1366 heads = revs and map(repo.lookup, revs) or revs
1367 outgoing = discovery.findcommonoutgoing(repo, other,
1367 outgoing = discovery.findcommonoutgoing(repo, other,
1368 onlyheads=heads,
1368 onlyheads=heads,
1369 force=opts.get('force'),
1369 force=opts.get('force'),
1370 portable=True)
1370 portable=True)
1371
1371
1372 if not outgoing.missing:
1372 if not outgoing.missing:
1373 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1373 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1374 return 1
1374 return 1
1375
1375
1376 if cgversion == '01': #bundle1
1376 if cgversion == '01': #bundle1
1377 if bcompression is None:
1377 if bcompression is None:
1378 bcompression = 'UN'
1378 bcompression = 'UN'
1379 bversion = 'HG10' + bcompression
1379 bversion = 'HG10' + bcompression
1380 bcompression = None
1380 bcompression = None
1381 elif cgversion in ('02', '03'):
1381 elif cgversion in ('02', '03'):
1382 bversion = 'HG20'
1382 bversion = 'HG20'
1383 else:
1383 else:
1384 raise error.ProgrammingError(
1384 raise error.ProgrammingError(
1385 'bundle: unexpected changegroup version %s' % cgversion)
1385 'bundle: unexpected changegroup version %s' % cgversion)
1386
1386
1387 # TODO compression options should be derived from bundlespec parsing.
1387 # TODO compression options should be derived from bundlespec parsing.
1388 # This is a temporary hack to allow adjusting bundle compression
1388 # This is a temporary hack to allow adjusting bundle compression
1389 # level without a) formalizing the bundlespec changes to declare it
1389 # level without a) formalizing the bundlespec changes to declare it
1390 # b) introducing a command flag.
1390 # b) introducing a command flag.
1391 compopts = {}
1391 compopts = {}
1392 complevel = ui.configint('experimental', 'bundlecomplevel')
1392 complevel = ui.configint('experimental', 'bundlecomplevel')
1393 if complevel is not None:
1393 if complevel is not None:
1394 compopts['level'] = complevel
1394 compopts['level'] = complevel
1395
1395
1396 cg = changegroup.getchangegroup(repo, 'bundle', outgoing, version=cgversion)
1396
1397
1397 contentopts = {'cg.version': cgversion}
1398 bundle2.writebundle(ui, cg, fname, bversion, compression=bcompression,
1398 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1399 compopts=compopts)
1399 contentopts, compression=bcompression,
1400 compopts=compopts)
1400
1401
1401 @command('cat',
1402 @command('cat',
1402 [('o', 'output', '',
1403 [('o', 'output', '',
1403 _('print output to file with formatted name'), _('FORMAT')),
1404 _('print output to file with formatted name'), _('FORMAT')),
1404 ('r', 'rev', '', _('print the given revision'), _('REV')),
1405 ('r', 'rev', '', _('print the given revision'), _('REV')),
1405 ('', 'decode', None, _('apply any matching decode filter')),
1406 ('', 'decode', None, _('apply any matching decode filter')),
1406 ] + walkopts,
1407 ] + walkopts,
1407 _('[OPTION]... FILE...'),
1408 _('[OPTION]... FILE...'),
1408 inferrepo=True)
1409 inferrepo=True)
1409 def cat(ui, repo, file1, *pats, **opts):
1410 def cat(ui, repo, file1, *pats, **opts):
1410 """output the current or given revision of files
1411 """output the current or given revision of files
1411
1412
1412 Print the specified files as they were at the given revision. If
1413 Print the specified files as they were at the given revision. If
1413 no revision is given, the parent of the working directory is used.
1414 no revision is given, the parent of the working directory is used.
1414
1415
1415 Output may be to a file, in which case the name of the file is
1416 Output may be to a file, in which case the name of the file is
1416 given using a format string. The formatting rules as follows:
1417 given using a format string. The formatting rules as follows:
1417
1418
1418 :``%%``: literal "%" character
1419 :``%%``: literal "%" character
1419 :``%s``: basename of file being printed
1420 :``%s``: basename of file being printed
1420 :``%d``: dirname of file being printed, or '.' if in repository root
1421 :``%d``: dirname of file being printed, or '.' if in repository root
1421 :``%p``: root-relative path name of file being printed
1422 :``%p``: root-relative path name of file being printed
1422 :``%H``: changeset hash (40 hexadecimal digits)
1423 :``%H``: changeset hash (40 hexadecimal digits)
1423 :``%R``: changeset revision number
1424 :``%R``: changeset revision number
1424 :``%h``: short-form changeset hash (12 hexadecimal digits)
1425 :``%h``: short-form changeset hash (12 hexadecimal digits)
1425 :``%r``: zero-padded changeset revision number
1426 :``%r``: zero-padded changeset revision number
1426 :``%b``: basename of the exporting repository
1427 :``%b``: basename of the exporting repository
1427
1428
1428 Returns 0 on success.
1429 Returns 0 on success.
1429 """
1430 """
1430 ctx = scmutil.revsingle(repo, opts.get('rev'))
1431 ctx = scmutil.revsingle(repo, opts.get('rev'))
1431 m = scmutil.match(ctx, (file1,) + pats, opts)
1432 m = scmutil.match(ctx, (file1,) + pats, opts)
1432
1433
1433 ui.pager('cat')
1434 ui.pager('cat')
1434 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1435 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1435
1436
1436 @command('^clone',
1437 @command('^clone',
1437 [('U', 'noupdate', None, _('the clone will include an empty working '
1438 [('U', 'noupdate', None, _('the clone will include an empty working '
1438 'directory (only a repository)')),
1439 'directory (only a repository)')),
1439 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1440 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1440 _('REV')),
1441 _('REV')),
1441 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1442 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1442 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1443 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1443 ('', 'pull', None, _('use pull protocol to copy metadata')),
1444 ('', 'pull', None, _('use pull protocol to copy metadata')),
1444 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1445 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1445 ] + remoteopts,
1446 ] + remoteopts,
1446 _('[OPTION]... SOURCE [DEST]'),
1447 _('[OPTION]... SOURCE [DEST]'),
1447 norepo=True)
1448 norepo=True)
1448 def clone(ui, source, dest=None, **opts):
1449 def clone(ui, source, dest=None, **opts):
1449 """make a copy of an existing repository
1450 """make a copy of an existing repository
1450
1451
1451 Create a copy of an existing repository in a new directory.
1452 Create a copy of an existing repository in a new directory.
1452
1453
1453 If no destination directory name is specified, it defaults to the
1454 If no destination directory name is specified, it defaults to the
1454 basename of the source.
1455 basename of the source.
1455
1456
1456 The location of the source is added to the new repository's
1457 The location of the source is added to the new repository's
1457 ``.hg/hgrc`` file, as the default to be used for future pulls.
1458 ``.hg/hgrc`` file, as the default to be used for future pulls.
1458
1459
1459 Only local paths and ``ssh://`` URLs are supported as
1460 Only local paths and ``ssh://`` URLs are supported as
1460 destinations. For ``ssh://`` destinations, no working directory or
1461 destinations. For ``ssh://`` destinations, no working directory or
1461 ``.hg/hgrc`` will be created on the remote side.
1462 ``.hg/hgrc`` will be created on the remote side.
1462
1463
1463 If the source repository has a bookmark called '@' set, that
1464 If the source repository has a bookmark called '@' set, that
1464 revision will be checked out in the new repository by default.
1465 revision will be checked out in the new repository by default.
1465
1466
1466 To check out a particular version, use -u/--update, or
1467 To check out a particular version, use -u/--update, or
1467 -U/--noupdate to create a clone with no working directory.
1468 -U/--noupdate to create a clone with no working directory.
1468
1469
1469 To pull only a subset of changesets, specify one or more revisions
1470 To pull only a subset of changesets, specify one or more revisions
1470 identifiers with -r/--rev or branches with -b/--branch. The
1471 identifiers with -r/--rev or branches with -b/--branch. The
1471 resulting clone will contain only the specified changesets and
1472 resulting clone will contain only the specified changesets and
1472 their ancestors. These options (or 'clone src#rev dest') imply
1473 their ancestors. These options (or 'clone src#rev dest') imply
1473 --pull, even for local source repositories.
1474 --pull, even for local source repositories.
1474
1475
1475 .. note::
1476 .. note::
1476
1477
1477 Specifying a tag will include the tagged changeset but not the
1478 Specifying a tag will include the tagged changeset but not the
1478 changeset containing the tag.
1479 changeset containing the tag.
1479
1480
1480 .. container:: verbose
1481 .. container:: verbose
1481
1482
1482 For efficiency, hardlinks are used for cloning whenever the
1483 For efficiency, hardlinks are used for cloning whenever the
1483 source and destination are on the same filesystem (note this
1484 source and destination are on the same filesystem (note this
1484 applies only to the repository data, not to the working
1485 applies only to the repository data, not to the working
1485 directory). Some filesystems, such as AFS, implement hardlinking
1486 directory). Some filesystems, such as AFS, implement hardlinking
1486 incorrectly, but do not report errors. In these cases, use the
1487 incorrectly, but do not report errors. In these cases, use the
1487 --pull option to avoid hardlinking.
1488 --pull option to avoid hardlinking.
1488
1489
1489 In some cases, you can clone repositories and the working
1490 In some cases, you can clone repositories and the working
1490 directory using full hardlinks with ::
1491 directory using full hardlinks with ::
1491
1492
1492 $ cp -al REPO REPOCLONE
1493 $ cp -al REPO REPOCLONE
1493
1494
1494 This is the fastest way to clone, but it is not always safe. The
1495 This is the fastest way to clone, but it is not always safe. The
1495 operation is not atomic (making sure REPO is not modified during
1496 operation is not atomic (making sure REPO is not modified during
1496 the operation is up to you) and you have to make sure your
1497 the operation is up to you) and you have to make sure your
1497 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1498 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1498 so). Also, this is not compatible with certain extensions that
1499 so). Also, this is not compatible with certain extensions that
1499 place their metadata under the .hg directory, such as mq.
1500 place their metadata under the .hg directory, such as mq.
1500
1501
1501 Mercurial will update the working directory to the first applicable
1502 Mercurial will update the working directory to the first applicable
1502 revision from this list:
1503 revision from this list:
1503
1504
1504 a) null if -U or the source repository has no changesets
1505 a) null if -U or the source repository has no changesets
1505 b) if -u . and the source repository is local, the first parent of
1506 b) if -u . and the source repository is local, the first parent of
1506 the source repository's working directory
1507 the source repository's working directory
1507 c) the changeset specified with -u (if a branch name, this means the
1508 c) the changeset specified with -u (if a branch name, this means the
1508 latest head of that branch)
1509 latest head of that branch)
1509 d) the changeset specified with -r
1510 d) the changeset specified with -r
1510 e) the tipmost head specified with -b
1511 e) the tipmost head specified with -b
1511 f) the tipmost head specified with the url#branch source syntax
1512 f) the tipmost head specified with the url#branch source syntax
1512 g) the revision marked with the '@' bookmark, if present
1513 g) the revision marked with the '@' bookmark, if present
1513 h) the tipmost head of the default branch
1514 h) the tipmost head of the default branch
1514 i) tip
1515 i) tip
1515
1516
1516 When cloning from servers that support it, Mercurial may fetch
1517 When cloning from servers that support it, Mercurial may fetch
1517 pre-generated data from a server-advertised URL. When this is done,
1518 pre-generated data from a server-advertised URL. When this is done,
1518 hooks operating on incoming changesets and changegroups may fire twice,
1519 hooks operating on incoming changesets and changegroups may fire twice,
1519 once for the bundle fetched from the URL and another for any additional
1520 once for the bundle fetched from the URL and another for any additional
1520 data not fetched from this URL. In addition, if an error occurs, the
1521 data not fetched from this URL. In addition, if an error occurs, the
1521 repository may be rolled back to a partial clone. This behavior may
1522 repository may be rolled back to a partial clone. This behavior may
1522 change in future releases. See :hg:`help -e clonebundles` for more.
1523 change in future releases. See :hg:`help -e clonebundles` for more.
1523
1524
1524 Examples:
1525 Examples:
1525
1526
1526 - clone a remote repository to a new directory named hg/::
1527 - clone a remote repository to a new directory named hg/::
1527
1528
1528 hg clone https://www.mercurial-scm.org/repo/hg/
1529 hg clone https://www.mercurial-scm.org/repo/hg/
1529
1530
1530 - create a lightweight local clone::
1531 - create a lightweight local clone::
1531
1532
1532 hg clone project/ project-feature/
1533 hg clone project/ project-feature/
1533
1534
1534 - clone from an absolute path on an ssh server (note double-slash)::
1535 - clone from an absolute path on an ssh server (note double-slash)::
1535
1536
1536 hg clone ssh://user@server//home/projects/alpha/
1537 hg clone ssh://user@server//home/projects/alpha/
1537
1538
1538 - do a high-speed clone over a LAN while checking out a
1539 - do a high-speed clone over a LAN while checking out a
1539 specified version::
1540 specified version::
1540
1541
1541 hg clone --uncompressed http://server/repo -u 1.5
1542 hg clone --uncompressed http://server/repo -u 1.5
1542
1543
1543 - create a repository without changesets after a particular revision::
1544 - create a repository without changesets after a particular revision::
1544
1545
1545 hg clone -r 04e544 experimental/ good/
1546 hg clone -r 04e544 experimental/ good/
1546
1547
1547 - clone (and track) a particular named branch::
1548 - clone (and track) a particular named branch::
1548
1549
1549 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1550 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1550
1551
1551 See :hg:`help urls` for details on specifying URLs.
1552 See :hg:`help urls` for details on specifying URLs.
1552
1553
1553 Returns 0 on success.
1554 Returns 0 on success.
1554 """
1555 """
1555 opts = pycompat.byteskwargs(opts)
1556 opts = pycompat.byteskwargs(opts)
1556 if opts.get('noupdate') and opts.get('updaterev'):
1557 if opts.get('noupdate') and opts.get('updaterev'):
1557 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1558 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1558
1559
1559 r = hg.clone(ui, opts, source, dest,
1560 r = hg.clone(ui, opts, source, dest,
1560 pull=opts.get('pull'),
1561 pull=opts.get('pull'),
1561 stream=opts.get('uncompressed'),
1562 stream=opts.get('uncompressed'),
1562 rev=opts.get('rev'),
1563 rev=opts.get('rev'),
1563 update=opts.get('updaterev') or not opts.get('noupdate'),
1564 update=opts.get('updaterev') or not opts.get('noupdate'),
1564 branch=opts.get('branch'),
1565 branch=opts.get('branch'),
1565 shareopts=opts.get('shareopts'))
1566 shareopts=opts.get('shareopts'))
1566
1567
1567 return r is None
1568 return r is None
1568
1569
1569 @command('^commit|ci',
1570 @command('^commit|ci',
1570 [('A', 'addremove', None,
1571 [('A', 'addremove', None,
1571 _('mark new/missing files as added/removed before committing')),
1572 _('mark new/missing files as added/removed before committing')),
1572 ('', 'close-branch', None,
1573 ('', 'close-branch', None,
1573 _('mark a branch head as closed')),
1574 _('mark a branch head as closed')),
1574 ('', 'amend', None, _('amend the parent of the working directory')),
1575 ('', 'amend', None, _('amend the parent of the working directory')),
1575 ('s', 'secret', None, _('use the secret phase for committing')),
1576 ('s', 'secret', None, _('use the secret phase for committing')),
1576 ('e', 'edit', None, _('invoke editor on commit messages')),
1577 ('e', 'edit', None, _('invoke editor on commit messages')),
1577 ('i', 'interactive', None, _('use interactive mode')),
1578 ('i', 'interactive', None, _('use interactive mode')),
1578 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1579 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1579 _('[OPTION]... [FILE]...'),
1580 _('[OPTION]... [FILE]...'),
1580 inferrepo=True)
1581 inferrepo=True)
1581 def commit(ui, repo, *pats, **opts):
1582 def commit(ui, repo, *pats, **opts):
1582 """commit the specified files or all outstanding changes
1583 """commit the specified files or all outstanding changes
1583
1584
1584 Commit changes to the given files into the repository. Unlike a
1585 Commit changes to the given files into the repository. Unlike a
1585 centralized SCM, this operation is a local operation. See
1586 centralized SCM, this operation is a local operation. See
1586 :hg:`push` for a way to actively distribute your changes.
1587 :hg:`push` for a way to actively distribute your changes.
1587
1588
1588 If a list of files is omitted, all changes reported by :hg:`status`
1589 If a list of files is omitted, all changes reported by :hg:`status`
1589 will be committed.
1590 will be committed.
1590
1591
1591 If you are committing the result of a merge, do not provide any
1592 If you are committing the result of a merge, do not provide any
1592 filenames or -I/-X filters.
1593 filenames or -I/-X filters.
1593
1594
1594 If no commit message is specified, Mercurial starts your
1595 If no commit message is specified, Mercurial starts your
1595 configured editor where you can enter a message. In case your
1596 configured editor where you can enter a message. In case your
1596 commit fails, you will find a backup of your message in
1597 commit fails, you will find a backup of your message in
1597 ``.hg/last-message.txt``.
1598 ``.hg/last-message.txt``.
1598
1599
1599 The --close-branch flag can be used to mark the current branch
1600 The --close-branch flag can be used to mark the current branch
1600 head closed. When all heads of a branch are closed, the branch
1601 head closed. When all heads of a branch are closed, the branch
1601 will be considered closed and no longer listed.
1602 will be considered closed and no longer listed.
1602
1603
1603 The --amend flag can be used to amend the parent of the
1604 The --amend flag can be used to amend the parent of the
1604 working directory with a new commit that contains the changes
1605 working directory with a new commit that contains the changes
1605 in the parent in addition to those currently reported by :hg:`status`,
1606 in the parent in addition to those currently reported by :hg:`status`,
1606 if there are any. The old commit is stored in a backup bundle in
1607 if there are any. The old commit is stored in a backup bundle in
1607 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1608 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1608 on how to restore it).
1609 on how to restore it).
1609
1610
1610 Message, user and date are taken from the amended commit unless
1611 Message, user and date are taken from the amended commit unless
1611 specified. When a message isn't specified on the command line,
1612 specified. When a message isn't specified on the command line,
1612 the editor will open with the message of the amended commit.
1613 the editor will open with the message of the amended commit.
1613
1614
1614 It is not possible to amend public changesets (see :hg:`help phases`)
1615 It is not possible to amend public changesets (see :hg:`help phases`)
1615 or changesets that have children.
1616 or changesets that have children.
1616
1617
1617 See :hg:`help dates` for a list of formats valid for -d/--date.
1618 See :hg:`help dates` for a list of formats valid for -d/--date.
1618
1619
1619 Returns 0 on success, 1 if nothing changed.
1620 Returns 0 on success, 1 if nothing changed.
1620
1621
1621 .. container:: verbose
1622 .. container:: verbose
1622
1623
1623 Examples:
1624 Examples:
1624
1625
1625 - commit all files ending in .py::
1626 - commit all files ending in .py::
1626
1627
1627 hg commit --include "set:**.py"
1628 hg commit --include "set:**.py"
1628
1629
1629 - commit all non-binary files::
1630 - commit all non-binary files::
1630
1631
1631 hg commit --exclude "set:binary()"
1632 hg commit --exclude "set:binary()"
1632
1633
1633 - amend the current commit and set the date to now::
1634 - amend the current commit and set the date to now::
1634
1635
1635 hg commit --amend --date now
1636 hg commit --amend --date now
1636 """
1637 """
1637 wlock = lock = None
1638 wlock = lock = None
1638 try:
1639 try:
1639 wlock = repo.wlock()
1640 wlock = repo.wlock()
1640 lock = repo.lock()
1641 lock = repo.lock()
1641 return _docommit(ui, repo, *pats, **opts)
1642 return _docommit(ui, repo, *pats, **opts)
1642 finally:
1643 finally:
1643 release(lock, wlock)
1644 release(lock, wlock)
1644
1645
1645 def _docommit(ui, repo, *pats, **opts):
1646 def _docommit(ui, repo, *pats, **opts):
1646 if opts.get(r'interactive'):
1647 if opts.get(r'interactive'):
1647 opts.pop(r'interactive')
1648 opts.pop(r'interactive')
1648 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1649 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1649 cmdutil.recordfilter, *pats,
1650 cmdutil.recordfilter, *pats,
1650 **opts)
1651 **opts)
1651 # ret can be 0 (no changes to record) or the value returned by
1652 # ret can be 0 (no changes to record) or the value returned by
1652 # commit(), 1 if nothing changed or None on success.
1653 # commit(), 1 if nothing changed or None on success.
1653 return 1 if ret == 0 else ret
1654 return 1 if ret == 0 else ret
1654
1655
1655 opts = pycompat.byteskwargs(opts)
1656 opts = pycompat.byteskwargs(opts)
1656 if opts.get('subrepos'):
1657 if opts.get('subrepos'):
1657 if opts.get('amend'):
1658 if opts.get('amend'):
1658 raise error.Abort(_('cannot amend with --subrepos'))
1659 raise error.Abort(_('cannot amend with --subrepos'))
1659 # Let --subrepos on the command line override config setting.
1660 # Let --subrepos on the command line override config setting.
1660 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1661 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1661
1662
1662 cmdutil.checkunfinished(repo, commit=True)
1663 cmdutil.checkunfinished(repo, commit=True)
1663
1664
1664 branch = repo[None].branch()
1665 branch = repo[None].branch()
1665 bheads = repo.branchheads(branch)
1666 bheads = repo.branchheads(branch)
1666
1667
1667 extra = {}
1668 extra = {}
1668 if opts.get('close_branch'):
1669 if opts.get('close_branch'):
1669 extra['close'] = 1
1670 extra['close'] = 1
1670
1671
1671 if not bheads:
1672 if not bheads:
1672 raise error.Abort(_('can only close branch heads'))
1673 raise error.Abort(_('can only close branch heads'))
1673 elif opts.get('amend'):
1674 elif opts.get('amend'):
1674 if repo[None].parents()[0].p1().branch() != branch and \
1675 if repo[None].parents()[0].p1().branch() != branch and \
1675 repo[None].parents()[0].p2().branch() != branch:
1676 repo[None].parents()[0].p2().branch() != branch:
1676 raise error.Abort(_('can only close branch heads'))
1677 raise error.Abort(_('can only close branch heads'))
1677
1678
1678 if opts.get('amend'):
1679 if opts.get('amend'):
1679 if ui.configbool('ui', 'commitsubrepos'):
1680 if ui.configbool('ui', 'commitsubrepos'):
1680 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1681 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1681
1682
1682 old = repo['.']
1683 old = repo['.']
1683 if not old.mutable():
1684 if not old.mutable():
1684 raise error.Abort(_('cannot amend public changesets'))
1685 raise error.Abort(_('cannot amend public changesets'))
1685 if len(repo[None].parents()) > 1:
1686 if len(repo[None].parents()) > 1:
1686 raise error.Abort(_('cannot amend while merging'))
1687 raise error.Abort(_('cannot amend while merging'))
1687 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1688 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1688 if not allowunstable and old.children():
1689 if not allowunstable and old.children():
1689 raise error.Abort(_('cannot amend changeset with children'))
1690 raise error.Abort(_('cannot amend changeset with children'))
1690
1691
1691 # Currently histedit gets confused if an amend happens while histedit
1692 # Currently histedit gets confused if an amend happens while histedit
1692 # is in progress. Since we have a checkunfinished command, we are
1693 # is in progress. Since we have a checkunfinished command, we are
1693 # temporarily honoring it.
1694 # temporarily honoring it.
1694 #
1695 #
1695 # Note: eventually this guard will be removed. Please do not expect
1696 # Note: eventually this guard will be removed. Please do not expect
1696 # this behavior to remain.
1697 # this behavior to remain.
1697 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1698 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1698 cmdutil.checkunfinished(repo)
1699 cmdutil.checkunfinished(repo)
1699
1700
1700 # commitfunc is used only for temporary amend commit by cmdutil.amend
1701 # commitfunc is used only for temporary amend commit by cmdutil.amend
1701 def commitfunc(ui, repo, message, match, opts):
1702 def commitfunc(ui, repo, message, match, opts):
1702 return repo.commit(message,
1703 return repo.commit(message,
1703 opts.get('user') or old.user(),
1704 opts.get('user') or old.user(),
1704 opts.get('date') or old.date(),
1705 opts.get('date') or old.date(),
1705 match,
1706 match,
1706 extra=extra)
1707 extra=extra)
1707
1708
1708 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1709 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1709 if node == old.node():
1710 if node == old.node():
1710 ui.status(_("nothing changed\n"))
1711 ui.status(_("nothing changed\n"))
1711 return 1
1712 return 1
1712 else:
1713 else:
1713 def commitfunc(ui, repo, message, match, opts):
1714 def commitfunc(ui, repo, message, match, opts):
1714 overrides = {}
1715 overrides = {}
1715 if opts.get('secret'):
1716 if opts.get('secret'):
1716 overrides[('phases', 'new-commit')] = 'secret'
1717 overrides[('phases', 'new-commit')] = 'secret'
1717
1718
1718 baseui = repo.baseui
1719 baseui = repo.baseui
1719 with baseui.configoverride(overrides, 'commit'):
1720 with baseui.configoverride(overrides, 'commit'):
1720 with ui.configoverride(overrides, 'commit'):
1721 with ui.configoverride(overrides, 'commit'):
1721 editform = cmdutil.mergeeditform(repo[None],
1722 editform = cmdutil.mergeeditform(repo[None],
1722 'commit.normal')
1723 'commit.normal')
1723 editor = cmdutil.getcommiteditor(
1724 editor = cmdutil.getcommiteditor(
1724 editform=editform, **pycompat.strkwargs(opts))
1725 editform=editform, **pycompat.strkwargs(opts))
1725 return repo.commit(message,
1726 return repo.commit(message,
1726 opts.get('user'),
1727 opts.get('user'),
1727 opts.get('date'),
1728 opts.get('date'),
1728 match,
1729 match,
1729 editor=editor,
1730 editor=editor,
1730 extra=extra)
1731 extra=extra)
1731
1732
1732 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1733 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1733
1734
1734 if not node:
1735 if not node:
1735 stat = cmdutil.postcommitstatus(repo, pats, opts)
1736 stat = cmdutil.postcommitstatus(repo, pats, opts)
1736 if stat[3]:
1737 if stat[3]:
1737 ui.status(_("nothing changed (%d missing files, see "
1738 ui.status(_("nothing changed (%d missing files, see "
1738 "'hg status')\n") % len(stat[3]))
1739 "'hg status')\n") % len(stat[3]))
1739 else:
1740 else:
1740 ui.status(_("nothing changed\n"))
1741 ui.status(_("nothing changed\n"))
1741 return 1
1742 return 1
1742
1743
1743 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1744 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1744
1745
1745 @command('config|showconfig|debugconfig',
1746 @command('config|showconfig|debugconfig',
1746 [('u', 'untrusted', None, _('show untrusted configuration options')),
1747 [('u', 'untrusted', None, _('show untrusted configuration options')),
1747 ('e', 'edit', None, _('edit user config')),
1748 ('e', 'edit', None, _('edit user config')),
1748 ('l', 'local', None, _('edit repository config')),
1749 ('l', 'local', None, _('edit repository config')),
1749 ('g', 'global', None, _('edit global config'))] + formatteropts,
1750 ('g', 'global', None, _('edit global config'))] + formatteropts,
1750 _('[-u] [NAME]...'),
1751 _('[-u] [NAME]...'),
1751 optionalrepo=True)
1752 optionalrepo=True)
1752 def config(ui, repo, *values, **opts):
1753 def config(ui, repo, *values, **opts):
1753 """show combined config settings from all hgrc files
1754 """show combined config settings from all hgrc files
1754
1755
1755 With no arguments, print names and values of all config items.
1756 With no arguments, print names and values of all config items.
1756
1757
1757 With one argument of the form section.name, print just the value
1758 With one argument of the form section.name, print just the value
1758 of that config item.
1759 of that config item.
1759
1760
1760 With multiple arguments, print names and values of all config
1761 With multiple arguments, print names and values of all config
1761 items with matching section names.
1762 items with matching section names.
1762
1763
1763 With --edit, start an editor on the user-level config file. With
1764 With --edit, start an editor on the user-level config file. With
1764 --global, edit the system-wide config file. With --local, edit the
1765 --global, edit the system-wide config file. With --local, edit the
1765 repository-level config file.
1766 repository-level config file.
1766
1767
1767 With --debug, the source (filename and line number) is printed
1768 With --debug, the source (filename and line number) is printed
1768 for each config item.
1769 for each config item.
1769
1770
1770 See :hg:`help config` for more information about config files.
1771 See :hg:`help config` for more information about config files.
1771
1772
1772 Returns 0 on success, 1 if NAME does not exist.
1773 Returns 0 on success, 1 if NAME does not exist.
1773
1774
1774 """
1775 """
1775
1776
1776 opts = pycompat.byteskwargs(opts)
1777 opts = pycompat.byteskwargs(opts)
1777 if opts.get('edit') or opts.get('local') or opts.get('global'):
1778 if opts.get('edit') or opts.get('local') or opts.get('global'):
1778 if opts.get('local') and opts.get('global'):
1779 if opts.get('local') and opts.get('global'):
1779 raise error.Abort(_("can't use --local and --global together"))
1780 raise error.Abort(_("can't use --local and --global together"))
1780
1781
1781 if opts.get('local'):
1782 if opts.get('local'):
1782 if not repo:
1783 if not repo:
1783 raise error.Abort(_("can't use --local outside a repository"))
1784 raise error.Abort(_("can't use --local outside a repository"))
1784 paths = [repo.vfs.join('hgrc')]
1785 paths = [repo.vfs.join('hgrc')]
1785 elif opts.get('global'):
1786 elif opts.get('global'):
1786 paths = rcutil.systemrcpath()
1787 paths = rcutil.systemrcpath()
1787 else:
1788 else:
1788 paths = rcutil.userrcpath()
1789 paths = rcutil.userrcpath()
1789
1790
1790 for f in paths:
1791 for f in paths:
1791 if os.path.exists(f):
1792 if os.path.exists(f):
1792 break
1793 break
1793 else:
1794 else:
1794 if opts.get('global'):
1795 if opts.get('global'):
1795 samplehgrc = uimod.samplehgrcs['global']
1796 samplehgrc = uimod.samplehgrcs['global']
1796 elif opts.get('local'):
1797 elif opts.get('local'):
1797 samplehgrc = uimod.samplehgrcs['local']
1798 samplehgrc = uimod.samplehgrcs['local']
1798 else:
1799 else:
1799 samplehgrc = uimod.samplehgrcs['user']
1800 samplehgrc = uimod.samplehgrcs['user']
1800
1801
1801 f = paths[0]
1802 f = paths[0]
1802 fp = open(f, "w")
1803 fp = open(f, "w")
1803 fp.write(samplehgrc)
1804 fp.write(samplehgrc)
1804 fp.close()
1805 fp.close()
1805
1806
1806 editor = ui.geteditor()
1807 editor = ui.geteditor()
1807 ui.system("%s \"%s\"" % (editor, f),
1808 ui.system("%s \"%s\"" % (editor, f),
1808 onerr=error.Abort, errprefix=_("edit failed"),
1809 onerr=error.Abort, errprefix=_("edit failed"),
1809 blockedtag='config_edit')
1810 blockedtag='config_edit')
1810 return
1811 return
1811 ui.pager('config')
1812 ui.pager('config')
1812 fm = ui.formatter('config', opts)
1813 fm = ui.formatter('config', opts)
1813 for t, f in rcutil.rccomponents():
1814 for t, f in rcutil.rccomponents():
1814 if t == 'path':
1815 if t == 'path':
1815 ui.debug('read config from: %s\n' % f)
1816 ui.debug('read config from: %s\n' % f)
1816 elif t == 'items':
1817 elif t == 'items':
1817 for section, name, value, source in f:
1818 for section, name, value, source in f:
1818 ui.debug('set config by: %s\n' % source)
1819 ui.debug('set config by: %s\n' % source)
1819 else:
1820 else:
1820 raise error.ProgrammingError('unknown rctype: %s' % t)
1821 raise error.ProgrammingError('unknown rctype: %s' % t)
1821 untrusted = bool(opts.get('untrusted'))
1822 untrusted = bool(opts.get('untrusted'))
1822 if values:
1823 if values:
1823 sections = [v for v in values if '.' not in v]
1824 sections = [v for v in values if '.' not in v]
1824 items = [v for v in values if '.' in v]
1825 items = [v for v in values if '.' in v]
1825 if len(items) > 1 or items and sections:
1826 if len(items) > 1 or items and sections:
1826 raise error.Abort(_('only one config item permitted'))
1827 raise error.Abort(_('only one config item permitted'))
1827 matched = False
1828 matched = False
1828 for section, name, value in ui.walkconfig(untrusted=untrusted):
1829 for section, name, value in ui.walkconfig(untrusted=untrusted):
1829 source = ui.configsource(section, name, untrusted)
1830 source = ui.configsource(section, name, untrusted)
1830 value = pycompat.bytestr(value)
1831 value = pycompat.bytestr(value)
1831 if fm.isplain():
1832 if fm.isplain():
1832 source = source or 'none'
1833 source = source or 'none'
1833 value = value.replace('\n', '\\n')
1834 value = value.replace('\n', '\\n')
1834 entryname = section + '.' + name
1835 entryname = section + '.' + name
1835 if values:
1836 if values:
1836 for v in values:
1837 for v in values:
1837 if v == section:
1838 if v == section:
1838 fm.startitem()
1839 fm.startitem()
1839 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1840 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1840 fm.write('name value', '%s=%s\n', entryname, value)
1841 fm.write('name value', '%s=%s\n', entryname, value)
1841 matched = True
1842 matched = True
1842 elif v == entryname:
1843 elif v == entryname:
1843 fm.startitem()
1844 fm.startitem()
1844 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1845 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1845 fm.write('value', '%s\n', value)
1846 fm.write('value', '%s\n', value)
1846 fm.data(name=entryname)
1847 fm.data(name=entryname)
1847 matched = True
1848 matched = True
1848 else:
1849 else:
1849 fm.startitem()
1850 fm.startitem()
1850 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1851 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1851 fm.write('name value', '%s=%s\n', entryname, value)
1852 fm.write('name value', '%s=%s\n', entryname, value)
1852 matched = True
1853 matched = True
1853 fm.end()
1854 fm.end()
1854 if matched:
1855 if matched:
1855 return 0
1856 return 0
1856 return 1
1857 return 1
1857
1858
1858 @command('copy|cp',
1859 @command('copy|cp',
1859 [('A', 'after', None, _('record a copy that has already occurred')),
1860 [('A', 'after', None, _('record a copy that has already occurred')),
1860 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1861 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1861 ] + walkopts + dryrunopts,
1862 ] + walkopts + dryrunopts,
1862 _('[OPTION]... [SOURCE]... DEST'))
1863 _('[OPTION]... [SOURCE]... DEST'))
1863 def copy(ui, repo, *pats, **opts):
1864 def copy(ui, repo, *pats, **opts):
1864 """mark files as copied for the next commit
1865 """mark files as copied for the next commit
1865
1866
1866 Mark dest as having copies of source files. If dest is a
1867 Mark dest as having copies of source files. If dest is a
1867 directory, copies are put in that directory. If dest is a file,
1868 directory, copies are put in that directory. If dest is a file,
1868 the source must be a single file.
1869 the source must be a single file.
1869
1870
1870 By default, this command copies the contents of files as they
1871 By default, this command copies the contents of files as they
1871 exist in the working directory. If invoked with -A/--after, the
1872 exist in the working directory. If invoked with -A/--after, the
1872 operation is recorded, but no copying is performed.
1873 operation is recorded, but no copying is performed.
1873
1874
1874 This command takes effect with the next commit. To undo a copy
1875 This command takes effect with the next commit. To undo a copy
1875 before that, see :hg:`revert`.
1876 before that, see :hg:`revert`.
1876
1877
1877 Returns 0 on success, 1 if errors are encountered.
1878 Returns 0 on success, 1 if errors are encountered.
1878 """
1879 """
1879 opts = pycompat.byteskwargs(opts)
1880 opts = pycompat.byteskwargs(opts)
1880 with repo.wlock(False):
1881 with repo.wlock(False):
1881 return cmdutil.copy(ui, repo, pats, opts)
1882 return cmdutil.copy(ui, repo, pats, opts)
1882
1883
1883 @command('^diff',
1884 @command('^diff',
1884 [('r', 'rev', [], _('revision'), _('REV')),
1885 [('r', 'rev', [], _('revision'), _('REV')),
1885 ('c', 'change', '', _('change made by revision'), _('REV'))
1886 ('c', 'change', '', _('change made by revision'), _('REV'))
1886 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1887 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1887 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1888 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1888 inferrepo=True)
1889 inferrepo=True)
1889 def diff(ui, repo, *pats, **opts):
1890 def diff(ui, repo, *pats, **opts):
1890 """diff repository (or selected files)
1891 """diff repository (or selected files)
1891
1892
1892 Show differences between revisions for the specified files.
1893 Show differences between revisions for the specified files.
1893
1894
1894 Differences between files are shown using the unified diff format.
1895 Differences between files are shown using the unified diff format.
1895
1896
1896 .. note::
1897 .. note::
1897
1898
1898 :hg:`diff` may generate unexpected results for merges, as it will
1899 :hg:`diff` may generate unexpected results for merges, as it will
1899 default to comparing against the working directory's first
1900 default to comparing against the working directory's first
1900 parent changeset if no revisions are specified.
1901 parent changeset if no revisions are specified.
1901
1902
1902 When two revision arguments are given, then changes are shown
1903 When two revision arguments are given, then changes are shown
1903 between those revisions. If only one revision is specified then
1904 between those revisions. If only one revision is specified then
1904 that revision is compared to the working directory, and, when no
1905 that revision is compared to the working directory, and, when no
1905 revisions are specified, the working directory files are compared
1906 revisions are specified, the working directory files are compared
1906 to its first parent.
1907 to its first parent.
1907
1908
1908 Alternatively you can specify -c/--change with a revision to see
1909 Alternatively you can specify -c/--change with a revision to see
1909 the changes in that changeset relative to its first parent.
1910 the changes in that changeset relative to its first parent.
1910
1911
1911 Without the -a/--text option, diff will avoid generating diffs of
1912 Without the -a/--text option, diff will avoid generating diffs of
1912 files it detects as binary. With -a, diff will generate a diff
1913 files it detects as binary. With -a, diff will generate a diff
1913 anyway, probably with undesirable results.
1914 anyway, probably with undesirable results.
1914
1915
1915 Use the -g/--git option to generate diffs in the git extended diff
1916 Use the -g/--git option to generate diffs in the git extended diff
1916 format. For more information, read :hg:`help diffs`.
1917 format. For more information, read :hg:`help diffs`.
1917
1918
1918 .. container:: verbose
1919 .. container:: verbose
1919
1920
1920 Examples:
1921 Examples:
1921
1922
1922 - compare a file in the current working directory to its parent::
1923 - compare a file in the current working directory to its parent::
1923
1924
1924 hg diff foo.c
1925 hg diff foo.c
1925
1926
1926 - compare two historical versions of a directory, with rename info::
1927 - compare two historical versions of a directory, with rename info::
1927
1928
1928 hg diff --git -r 1.0:1.2 lib/
1929 hg diff --git -r 1.0:1.2 lib/
1929
1930
1930 - get change stats relative to the last change on some date::
1931 - get change stats relative to the last change on some date::
1931
1932
1932 hg diff --stat -r "date('may 2')"
1933 hg diff --stat -r "date('may 2')"
1933
1934
1934 - diff all newly-added files that contain a keyword::
1935 - diff all newly-added files that contain a keyword::
1935
1936
1936 hg diff "set:added() and grep(GNU)"
1937 hg diff "set:added() and grep(GNU)"
1937
1938
1938 - compare a revision and its parents::
1939 - compare a revision and its parents::
1939
1940
1940 hg diff -c 9353 # compare against first parent
1941 hg diff -c 9353 # compare against first parent
1941 hg diff -r 9353^:9353 # same using revset syntax
1942 hg diff -r 9353^:9353 # same using revset syntax
1942 hg diff -r 9353^2:9353 # compare against the second parent
1943 hg diff -r 9353^2:9353 # compare against the second parent
1943
1944
1944 Returns 0 on success.
1945 Returns 0 on success.
1945 """
1946 """
1946
1947
1947 opts = pycompat.byteskwargs(opts)
1948 opts = pycompat.byteskwargs(opts)
1948 revs = opts.get('rev')
1949 revs = opts.get('rev')
1949 change = opts.get('change')
1950 change = opts.get('change')
1950 stat = opts.get('stat')
1951 stat = opts.get('stat')
1951 reverse = opts.get('reverse')
1952 reverse = opts.get('reverse')
1952
1953
1953 if revs and change:
1954 if revs and change:
1954 msg = _('cannot specify --rev and --change at the same time')
1955 msg = _('cannot specify --rev and --change at the same time')
1955 raise error.Abort(msg)
1956 raise error.Abort(msg)
1956 elif change:
1957 elif change:
1957 node2 = scmutil.revsingle(repo, change, None).node()
1958 node2 = scmutil.revsingle(repo, change, None).node()
1958 node1 = repo[node2].p1().node()
1959 node1 = repo[node2].p1().node()
1959 else:
1960 else:
1960 node1, node2 = scmutil.revpair(repo, revs)
1961 node1, node2 = scmutil.revpair(repo, revs)
1961
1962
1962 if reverse:
1963 if reverse:
1963 node1, node2 = node2, node1
1964 node1, node2 = node2, node1
1964
1965
1965 diffopts = patch.diffallopts(ui, opts)
1966 diffopts = patch.diffallopts(ui, opts)
1966 m = scmutil.match(repo[node2], pats, opts)
1967 m = scmutil.match(repo[node2], pats, opts)
1967 ui.pager('diff')
1968 ui.pager('diff')
1968 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1969 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1969 listsubrepos=opts.get('subrepos'),
1970 listsubrepos=opts.get('subrepos'),
1970 root=opts.get('root'))
1971 root=opts.get('root'))
1971
1972
1972 @command('^export',
1973 @command('^export',
1973 [('o', 'output', '',
1974 [('o', 'output', '',
1974 _('print output to file with formatted name'), _('FORMAT')),
1975 _('print output to file with formatted name'), _('FORMAT')),
1975 ('', 'switch-parent', None, _('diff against the second parent')),
1976 ('', 'switch-parent', None, _('diff against the second parent')),
1976 ('r', 'rev', [], _('revisions to export'), _('REV')),
1977 ('r', 'rev', [], _('revisions to export'), _('REV')),
1977 ] + diffopts,
1978 ] + diffopts,
1978 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
1979 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
1979 def export(ui, repo, *changesets, **opts):
1980 def export(ui, repo, *changesets, **opts):
1980 """dump the header and diffs for one or more changesets
1981 """dump the header and diffs for one or more changesets
1981
1982
1982 Print the changeset header and diffs for one or more revisions.
1983 Print the changeset header and diffs for one or more revisions.
1983 If no revision is given, the parent of the working directory is used.
1984 If no revision is given, the parent of the working directory is used.
1984
1985
1985 The information shown in the changeset header is: author, date,
1986 The information shown in the changeset header is: author, date,
1986 branch name (if non-default), changeset hash, parent(s) and commit
1987 branch name (if non-default), changeset hash, parent(s) and commit
1987 comment.
1988 comment.
1988
1989
1989 .. note::
1990 .. note::
1990
1991
1991 :hg:`export` may generate unexpected diff output for merge
1992 :hg:`export` may generate unexpected diff output for merge
1992 changesets, as it will compare the merge changeset against its
1993 changesets, as it will compare the merge changeset against its
1993 first parent only.
1994 first parent only.
1994
1995
1995 Output may be to a file, in which case the name of the file is
1996 Output may be to a file, in which case the name of the file is
1996 given using a format string. The formatting rules are as follows:
1997 given using a format string. The formatting rules are as follows:
1997
1998
1998 :``%%``: literal "%" character
1999 :``%%``: literal "%" character
1999 :``%H``: changeset hash (40 hexadecimal digits)
2000 :``%H``: changeset hash (40 hexadecimal digits)
2000 :``%N``: number of patches being generated
2001 :``%N``: number of patches being generated
2001 :``%R``: changeset revision number
2002 :``%R``: changeset revision number
2002 :``%b``: basename of the exporting repository
2003 :``%b``: basename of the exporting repository
2003 :``%h``: short-form changeset hash (12 hexadecimal digits)
2004 :``%h``: short-form changeset hash (12 hexadecimal digits)
2004 :``%m``: first line of the commit message (only alphanumeric characters)
2005 :``%m``: first line of the commit message (only alphanumeric characters)
2005 :``%n``: zero-padded sequence number, starting at 1
2006 :``%n``: zero-padded sequence number, starting at 1
2006 :``%r``: zero-padded changeset revision number
2007 :``%r``: zero-padded changeset revision number
2007
2008
2008 Without the -a/--text option, export will avoid generating diffs
2009 Without the -a/--text option, export will avoid generating diffs
2009 of files it detects as binary. With -a, export will generate a
2010 of files it detects as binary. With -a, export will generate a
2010 diff anyway, probably with undesirable results.
2011 diff anyway, probably with undesirable results.
2011
2012
2012 Use the -g/--git option to generate diffs in the git extended diff
2013 Use the -g/--git option to generate diffs in the git extended diff
2013 format. See :hg:`help diffs` for more information.
2014 format. See :hg:`help diffs` for more information.
2014
2015
2015 With the --switch-parent option, the diff will be against the
2016 With the --switch-parent option, the diff will be against the
2016 second parent. It can be useful to review a merge.
2017 second parent. It can be useful to review a merge.
2017
2018
2018 .. container:: verbose
2019 .. container:: verbose
2019
2020
2020 Examples:
2021 Examples:
2021
2022
2022 - use export and import to transplant a bugfix to the current
2023 - use export and import to transplant a bugfix to the current
2023 branch::
2024 branch::
2024
2025
2025 hg export -r 9353 | hg import -
2026 hg export -r 9353 | hg import -
2026
2027
2027 - export all the changesets between two revisions to a file with
2028 - export all the changesets between two revisions to a file with
2028 rename information::
2029 rename information::
2029
2030
2030 hg export --git -r 123:150 > changes.txt
2031 hg export --git -r 123:150 > changes.txt
2031
2032
2032 - split outgoing changes into a series of patches with
2033 - split outgoing changes into a series of patches with
2033 descriptive names::
2034 descriptive names::
2034
2035
2035 hg export -r "outgoing()" -o "%n-%m.patch"
2036 hg export -r "outgoing()" -o "%n-%m.patch"
2036
2037
2037 Returns 0 on success.
2038 Returns 0 on success.
2038 """
2039 """
2039 opts = pycompat.byteskwargs(opts)
2040 opts = pycompat.byteskwargs(opts)
2040 changesets += tuple(opts.get('rev', []))
2041 changesets += tuple(opts.get('rev', []))
2041 if not changesets:
2042 if not changesets:
2042 changesets = ['.']
2043 changesets = ['.']
2043 revs = scmutil.revrange(repo, changesets)
2044 revs = scmutil.revrange(repo, changesets)
2044 if not revs:
2045 if not revs:
2045 raise error.Abort(_("export requires at least one changeset"))
2046 raise error.Abort(_("export requires at least one changeset"))
2046 if len(revs) > 1:
2047 if len(revs) > 1:
2047 ui.note(_('exporting patches:\n'))
2048 ui.note(_('exporting patches:\n'))
2048 else:
2049 else:
2049 ui.note(_('exporting patch:\n'))
2050 ui.note(_('exporting patch:\n'))
2050 ui.pager('export')
2051 ui.pager('export')
2051 cmdutil.export(repo, revs, template=opts.get('output'),
2052 cmdutil.export(repo, revs, template=opts.get('output'),
2052 switch_parent=opts.get('switch_parent'),
2053 switch_parent=opts.get('switch_parent'),
2053 opts=patch.diffallopts(ui, opts))
2054 opts=patch.diffallopts(ui, opts))
2054
2055
2055 @command('files',
2056 @command('files',
2056 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2057 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2057 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2058 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2058 ] + walkopts + formatteropts + subrepoopts,
2059 ] + walkopts + formatteropts + subrepoopts,
2059 _('[OPTION]... [FILE]...'))
2060 _('[OPTION]... [FILE]...'))
2060 def files(ui, repo, *pats, **opts):
2061 def files(ui, repo, *pats, **opts):
2061 """list tracked files
2062 """list tracked files
2062
2063
2063 Print files under Mercurial control in the working directory or
2064 Print files under Mercurial control in the working directory or
2064 specified revision for given files (excluding removed files).
2065 specified revision for given files (excluding removed files).
2065 Files can be specified as filenames or filesets.
2066 Files can be specified as filenames or filesets.
2066
2067
2067 If no files are given to match, this command prints the names
2068 If no files are given to match, this command prints the names
2068 of all files under Mercurial control.
2069 of all files under Mercurial control.
2069
2070
2070 .. container:: verbose
2071 .. container:: verbose
2071
2072
2072 Examples:
2073 Examples:
2073
2074
2074 - list all files under the current directory::
2075 - list all files under the current directory::
2075
2076
2076 hg files .
2077 hg files .
2077
2078
2078 - shows sizes and flags for current revision::
2079 - shows sizes and flags for current revision::
2079
2080
2080 hg files -vr .
2081 hg files -vr .
2081
2082
2082 - list all files named README::
2083 - list all files named README::
2083
2084
2084 hg files -I "**/README"
2085 hg files -I "**/README"
2085
2086
2086 - list all binary files::
2087 - list all binary files::
2087
2088
2088 hg files "set:binary()"
2089 hg files "set:binary()"
2089
2090
2090 - find files containing a regular expression::
2091 - find files containing a regular expression::
2091
2092
2092 hg files "set:grep('bob')"
2093 hg files "set:grep('bob')"
2093
2094
2094 - search tracked file contents with xargs and grep::
2095 - search tracked file contents with xargs and grep::
2095
2096
2096 hg files -0 | xargs -0 grep foo
2097 hg files -0 | xargs -0 grep foo
2097
2098
2098 See :hg:`help patterns` and :hg:`help filesets` for more information
2099 See :hg:`help patterns` and :hg:`help filesets` for more information
2099 on specifying file patterns.
2100 on specifying file patterns.
2100
2101
2101 Returns 0 if a match is found, 1 otherwise.
2102 Returns 0 if a match is found, 1 otherwise.
2102
2103
2103 """
2104 """
2104
2105
2105 opts = pycompat.byteskwargs(opts)
2106 opts = pycompat.byteskwargs(opts)
2106 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2107 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2107
2108
2108 end = '\n'
2109 end = '\n'
2109 if opts.get('print0'):
2110 if opts.get('print0'):
2110 end = '\0'
2111 end = '\0'
2111 fmt = '%s' + end
2112 fmt = '%s' + end
2112
2113
2113 m = scmutil.match(ctx, pats, opts)
2114 m = scmutil.match(ctx, pats, opts)
2114 ui.pager('files')
2115 ui.pager('files')
2115 with ui.formatter('files', opts) as fm:
2116 with ui.formatter('files', opts) as fm:
2116 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2117 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2117
2118
2118 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2119 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2119 def forget(ui, repo, *pats, **opts):
2120 def forget(ui, repo, *pats, **opts):
2120 """forget the specified files on the next commit
2121 """forget the specified files on the next commit
2121
2122
2122 Mark the specified files so they will no longer be tracked
2123 Mark the specified files so they will no longer be tracked
2123 after the next commit.
2124 after the next commit.
2124
2125
2125 This only removes files from the current branch, not from the
2126 This only removes files from the current branch, not from the
2126 entire project history, and it does not delete them from the
2127 entire project history, and it does not delete them from the
2127 working directory.
2128 working directory.
2128
2129
2129 To delete the file from the working directory, see :hg:`remove`.
2130 To delete the file from the working directory, see :hg:`remove`.
2130
2131
2131 To undo a forget before the next commit, see :hg:`add`.
2132 To undo a forget before the next commit, see :hg:`add`.
2132
2133
2133 .. container:: verbose
2134 .. container:: verbose
2134
2135
2135 Examples:
2136 Examples:
2136
2137
2137 - forget newly-added binary files::
2138 - forget newly-added binary files::
2138
2139
2139 hg forget "set:added() and binary()"
2140 hg forget "set:added() and binary()"
2140
2141
2141 - forget files that would be excluded by .hgignore::
2142 - forget files that would be excluded by .hgignore::
2142
2143
2143 hg forget "set:hgignore()"
2144 hg forget "set:hgignore()"
2144
2145
2145 Returns 0 on success.
2146 Returns 0 on success.
2146 """
2147 """
2147
2148
2148 opts = pycompat.byteskwargs(opts)
2149 opts = pycompat.byteskwargs(opts)
2149 if not pats:
2150 if not pats:
2150 raise error.Abort(_('no files specified'))
2151 raise error.Abort(_('no files specified'))
2151
2152
2152 m = scmutil.match(repo[None], pats, opts)
2153 m = scmutil.match(repo[None], pats, opts)
2153 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2154 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2154 return rejected and 1 or 0
2155 return rejected and 1 or 0
2155
2156
2156 @command(
2157 @command(
2157 'graft',
2158 'graft',
2158 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2159 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2159 ('c', 'continue', False, _('resume interrupted graft')),
2160 ('c', 'continue', False, _('resume interrupted graft')),
2160 ('e', 'edit', False, _('invoke editor on commit messages')),
2161 ('e', 'edit', False, _('invoke editor on commit messages')),
2161 ('', 'log', None, _('append graft info to log message')),
2162 ('', 'log', None, _('append graft info to log message')),
2162 ('f', 'force', False, _('force graft')),
2163 ('f', 'force', False, _('force graft')),
2163 ('D', 'currentdate', False,
2164 ('D', 'currentdate', False,
2164 _('record the current date as commit date')),
2165 _('record the current date as commit date')),
2165 ('U', 'currentuser', False,
2166 ('U', 'currentuser', False,
2166 _('record the current user as committer'), _('DATE'))]
2167 _('record the current user as committer'), _('DATE'))]
2167 + commitopts2 + mergetoolopts + dryrunopts,
2168 + commitopts2 + mergetoolopts + dryrunopts,
2168 _('[OPTION]... [-r REV]... REV...'))
2169 _('[OPTION]... [-r REV]... REV...'))
2169 def graft(ui, repo, *revs, **opts):
2170 def graft(ui, repo, *revs, **opts):
2170 '''copy changes from other branches onto the current branch
2171 '''copy changes from other branches onto the current branch
2171
2172
2172 This command uses Mercurial's merge logic to copy individual
2173 This command uses Mercurial's merge logic to copy individual
2173 changes from other branches without merging branches in the
2174 changes from other branches without merging branches in the
2174 history graph. This is sometimes known as 'backporting' or
2175 history graph. This is sometimes known as 'backporting' or
2175 'cherry-picking'. By default, graft will copy user, date, and
2176 'cherry-picking'. By default, graft will copy user, date, and
2176 description from the source changesets.
2177 description from the source changesets.
2177
2178
2178 Changesets that are ancestors of the current revision, that have
2179 Changesets that are ancestors of the current revision, that have
2179 already been grafted, or that are merges will be skipped.
2180 already been grafted, or that are merges will be skipped.
2180
2181
2181 If --log is specified, log messages will have a comment appended
2182 If --log is specified, log messages will have a comment appended
2182 of the form::
2183 of the form::
2183
2184
2184 (grafted from CHANGESETHASH)
2185 (grafted from CHANGESETHASH)
2185
2186
2186 If --force is specified, revisions will be grafted even if they
2187 If --force is specified, revisions will be grafted even if they
2187 are already ancestors of or have been grafted to the destination.
2188 are already ancestors of or have been grafted to the destination.
2188 This is useful when the revisions have since been backed out.
2189 This is useful when the revisions have since been backed out.
2189
2190
2190 If a graft merge results in conflicts, the graft process is
2191 If a graft merge results in conflicts, the graft process is
2191 interrupted so that the current merge can be manually resolved.
2192 interrupted so that the current merge can be manually resolved.
2192 Once all conflicts are addressed, the graft process can be
2193 Once all conflicts are addressed, the graft process can be
2193 continued with the -c/--continue option.
2194 continued with the -c/--continue option.
2194
2195
2195 .. note::
2196 .. note::
2196
2197
2197 The -c/--continue option does not reapply earlier options, except
2198 The -c/--continue option does not reapply earlier options, except
2198 for --force.
2199 for --force.
2199
2200
2200 .. container:: verbose
2201 .. container:: verbose
2201
2202
2202 Examples:
2203 Examples:
2203
2204
2204 - copy a single change to the stable branch and edit its description::
2205 - copy a single change to the stable branch and edit its description::
2205
2206
2206 hg update stable
2207 hg update stable
2207 hg graft --edit 9393
2208 hg graft --edit 9393
2208
2209
2209 - graft a range of changesets with one exception, updating dates::
2210 - graft a range of changesets with one exception, updating dates::
2210
2211
2211 hg graft -D "2085::2093 and not 2091"
2212 hg graft -D "2085::2093 and not 2091"
2212
2213
2213 - continue a graft after resolving conflicts::
2214 - continue a graft after resolving conflicts::
2214
2215
2215 hg graft -c
2216 hg graft -c
2216
2217
2217 - show the source of a grafted changeset::
2218 - show the source of a grafted changeset::
2218
2219
2219 hg log --debug -r .
2220 hg log --debug -r .
2220
2221
2221 - show revisions sorted by date::
2222 - show revisions sorted by date::
2222
2223
2223 hg log -r "sort(all(), date)"
2224 hg log -r "sort(all(), date)"
2224
2225
2225 See :hg:`help revisions` for more about specifying revisions.
2226 See :hg:`help revisions` for more about specifying revisions.
2226
2227
2227 Returns 0 on successful completion.
2228 Returns 0 on successful completion.
2228 '''
2229 '''
2229 with repo.wlock():
2230 with repo.wlock():
2230 return _dograft(ui, repo, *revs, **opts)
2231 return _dograft(ui, repo, *revs, **opts)
2231
2232
2232 def _dograft(ui, repo, *revs, **opts):
2233 def _dograft(ui, repo, *revs, **opts):
2233 opts = pycompat.byteskwargs(opts)
2234 opts = pycompat.byteskwargs(opts)
2234 if revs and opts.get('rev'):
2235 if revs and opts.get('rev'):
2235 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2236 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2236 'revision ordering!\n'))
2237 'revision ordering!\n'))
2237
2238
2238 revs = list(revs)
2239 revs = list(revs)
2239 revs.extend(opts.get('rev'))
2240 revs.extend(opts.get('rev'))
2240
2241
2241 if not opts.get('user') and opts.get('currentuser'):
2242 if not opts.get('user') and opts.get('currentuser'):
2242 opts['user'] = ui.username()
2243 opts['user'] = ui.username()
2243 if not opts.get('date') and opts.get('currentdate'):
2244 if not opts.get('date') and opts.get('currentdate'):
2244 opts['date'] = "%d %d" % util.makedate()
2245 opts['date'] = "%d %d" % util.makedate()
2245
2246
2246 editor = cmdutil.getcommiteditor(editform='graft',
2247 editor = cmdutil.getcommiteditor(editform='graft',
2247 **pycompat.strkwargs(opts))
2248 **pycompat.strkwargs(opts))
2248
2249
2249 cont = False
2250 cont = False
2250 if opts.get('continue'):
2251 if opts.get('continue'):
2251 cont = True
2252 cont = True
2252 if revs:
2253 if revs:
2253 raise error.Abort(_("can't specify --continue and revisions"))
2254 raise error.Abort(_("can't specify --continue and revisions"))
2254 # read in unfinished revisions
2255 # read in unfinished revisions
2255 try:
2256 try:
2256 nodes = repo.vfs.read('graftstate').splitlines()
2257 nodes = repo.vfs.read('graftstate').splitlines()
2257 revs = [repo[node].rev() for node in nodes]
2258 revs = [repo[node].rev() for node in nodes]
2258 except IOError as inst:
2259 except IOError as inst:
2259 if inst.errno != errno.ENOENT:
2260 if inst.errno != errno.ENOENT:
2260 raise
2261 raise
2261 cmdutil.wrongtooltocontinue(repo, _('graft'))
2262 cmdutil.wrongtooltocontinue(repo, _('graft'))
2262 else:
2263 else:
2263 cmdutil.checkunfinished(repo)
2264 cmdutil.checkunfinished(repo)
2264 cmdutil.bailifchanged(repo)
2265 cmdutil.bailifchanged(repo)
2265 if not revs:
2266 if not revs:
2266 raise error.Abort(_('no revisions specified'))
2267 raise error.Abort(_('no revisions specified'))
2267 revs = scmutil.revrange(repo, revs)
2268 revs = scmutil.revrange(repo, revs)
2268
2269
2269 skipped = set()
2270 skipped = set()
2270 # check for merges
2271 # check for merges
2271 for rev in repo.revs('%ld and merge()', revs):
2272 for rev in repo.revs('%ld and merge()', revs):
2272 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2273 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2273 skipped.add(rev)
2274 skipped.add(rev)
2274 revs = [r for r in revs if r not in skipped]
2275 revs = [r for r in revs if r not in skipped]
2275 if not revs:
2276 if not revs:
2276 return -1
2277 return -1
2277
2278
2278 # Don't check in the --continue case, in effect retaining --force across
2279 # Don't check in the --continue case, in effect retaining --force across
2279 # --continues. That's because without --force, any revisions we decided to
2280 # --continues. That's because without --force, any revisions we decided to
2280 # skip would have been filtered out here, so they wouldn't have made their
2281 # skip would have been filtered out here, so they wouldn't have made their
2281 # way to the graftstate. With --force, any revisions we would have otherwise
2282 # way to the graftstate. With --force, any revisions we would have otherwise
2282 # skipped would not have been filtered out, and if they hadn't been applied
2283 # skipped would not have been filtered out, and if they hadn't been applied
2283 # already, they'd have been in the graftstate.
2284 # already, they'd have been in the graftstate.
2284 if not (cont or opts.get('force')):
2285 if not (cont or opts.get('force')):
2285 # check for ancestors of dest branch
2286 # check for ancestors of dest branch
2286 crev = repo['.'].rev()
2287 crev = repo['.'].rev()
2287 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2288 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2288 # XXX make this lazy in the future
2289 # XXX make this lazy in the future
2289 # don't mutate while iterating, create a copy
2290 # don't mutate while iterating, create a copy
2290 for rev in list(revs):
2291 for rev in list(revs):
2291 if rev in ancestors:
2292 if rev in ancestors:
2292 ui.warn(_('skipping ancestor revision %d:%s\n') %
2293 ui.warn(_('skipping ancestor revision %d:%s\n') %
2293 (rev, repo[rev]))
2294 (rev, repo[rev]))
2294 # XXX remove on list is slow
2295 # XXX remove on list is slow
2295 revs.remove(rev)
2296 revs.remove(rev)
2296 if not revs:
2297 if not revs:
2297 return -1
2298 return -1
2298
2299
2299 # analyze revs for earlier grafts
2300 # analyze revs for earlier grafts
2300 ids = {}
2301 ids = {}
2301 for ctx in repo.set("%ld", revs):
2302 for ctx in repo.set("%ld", revs):
2302 ids[ctx.hex()] = ctx.rev()
2303 ids[ctx.hex()] = ctx.rev()
2303 n = ctx.extra().get('source')
2304 n = ctx.extra().get('source')
2304 if n:
2305 if n:
2305 ids[n] = ctx.rev()
2306 ids[n] = ctx.rev()
2306
2307
2307 # check ancestors for earlier grafts
2308 # check ancestors for earlier grafts
2308 ui.debug('scanning for duplicate grafts\n')
2309 ui.debug('scanning for duplicate grafts\n')
2309
2310
2310 for rev in repo.changelog.findmissingrevs(revs, [crev]):
2311 for rev in repo.changelog.findmissingrevs(revs, [crev]):
2311 ctx = repo[rev]
2312 ctx = repo[rev]
2312 n = ctx.extra().get('source')
2313 n = ctx.extra().get('source')
2313 if n in ids:
2314 if n in ids:
2314 try:
2315 try:
2315 r = repo[n].rev()
2316 r = repo[n].rev()
2316 except error.RepoLookupError:
2317 except error.RepoLookupError:
2317 r = None
2318 r = None
2318 if r in revs:
2319 if r in revs:
2319 ui.warn(_('skipping revision %d:%s '
2320 ui.warn(_('skipping revision %d:%s '
2320 '(already grafted to %d:%s)\n')
2321 '(already grafted to %d:%s)\n')
2321 % (r, repo[r], rev, ctx))
2322 % (r, repo[r], rev, ctx))
2322 revs.remove(r)
2323 revs.remove(r)
2323 elif ids[n] in revs:
2324 elif ids[n] in revs:
2324 if r is None:
2325 if r is None:
2325 ui.warn(_('skipping already grafted revision %d:%s '
2326 ui.warn(_('skipping already grafted revision %d:%s '
2326 '(%d:%s also has unknown origin %s)\n')
2327 '(%d:%s also has unknown origin %s)\n')
2327 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2328 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2328 else:
2329 else:
2329 ui.warn(_('skipping already grafted revision %d:%s '
2330 ui.warn(_('skipping already grafted revision %d:%s '
2330 '(%d:%s also has origin %d:%s)\n')
2331 '(%d:%s also has origin %d:%s)\n')
2331 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2332 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2332 revs.remove(ids[n])
2333 revs.remove(ids[n])
2333 elif ctx.hex() in ids:
2334 elif ctx.hex() in ids:
2334 r = ids[ctx.hex()]
2335 r = ids[ctx.hex()]
2335 ui.warn(_('skipping already grafted revision %d:%s '
2336 ui.warn(_('skipping already grafted revision %d:%s '
2336 '(was grafted from %d:%s)\n') %
2337 '(was grafted from %d:%s)\n') %
2337 (r, repo[r], rev, ctx))
2338 (r, repo[r], rev, ctx))
2338 revs.remove(r)
2339 revs.remove(r)
2339 if not revs:
2340 if not revs:
2340 return -1
2341 return -1
2341
2342
2342 for pos, ctx in enumerate(repo.set("%ld", revs)):
2343 for pos, ctx in enumerate(repo.set("%ld", revs)):
2343 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2344 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2344 ctx.description().split('\n', 1)[0])
2345 ctx.description().split('\n', 1)[0])
2345 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2346 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2346 if names:
2347 if names:
2347 desc += ' (%s)' % ' '.join(names)
2348 desc += ' (%s)' % ' '.join(names)
2348 ui.status(_('grafting %s\n') % desc)
2349 ui.status(_('grafting %s\n') % desc)
2349 if opts.get('dry_run'):
2350 if opts.get('dry_run'):
2350 continue
2351 continue
2351
2352
2352 source = ctx.extra().get('source')
2353 source = ctx.extra().get('source')
2353 extra = {}
2354 extra = {}
2354 if source:
2355 if source:
2355 extra['source'] = source
2356 extra['source'] = source
2356 extra['intermediate-source'] = ctx.hex()
2357 extra['intermediate-source'] = ctx.hex()
2357 else:
2358 else:
2358 extra['source'] = ctx.hex()
2359 extra['source'] = ctx.hex()
2359 user = ctx.user()
2360 user = ctx.user()
2360 if opts.get('user'):
2361 if opts.get('user'):
2361 user = opts['user']
2362 user = opts['user']
2362 date = ctx.date()
2363 date = ctx.date()
2363 if opts.get('date'):
2364 if opts.get('date'):
2364 date = opts['date']
2365 date = opts['date']
2365 message = ctx.description()
2366 message = ctx.description()
2366 if opts.get('log'):
2367 if opts.get('log'):
2367 message += '\n(grafted from %s)' % ctx.hex()
2368 message += '\n(grafted from %s)' % ctx.hex()
2368
2369
2369 # we don't merge the first commit when continuing
2370 # we don't merge the first commit when continuing
2370 if not cont:
2371 if not cont:
2371 # perform the graft merge with p1(rev) as 'ancestor'
2372 # perform the graft merge with p1(rev) as 'ancestor'
2372 try:
2373 try:
2373 # ui.forcemerge is an internal variable, do not document
2374 # ui.forcemerge is an internal variable, do not document
2374 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2375 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2375 'graft')
2376 'graft')
2376 stats = mergemod.graft(repo, ctx, ctx.p1(),
2377 stats = mergemod.graft(repo, ctx, ctx.p1(),
2377 ['local', 'graft'])
2378 ['local', 'graft'])
2378 finally:
2379 finally:
2379 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2380 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2380 # report any conflicts
2381 # report any conflicts
2381 if stats and stats[3] > 0:
2382 if stats and stats[3] > 0:
2382 # write out state for --continue
2383 # write out state for --continue
2383 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2384 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2384 repo.vfs.write('graftstate', ''.join(nodelines))
2385 repo.vfs.write('graftstate', ''.join(nodelines))
2385 extra = ''
2386 extra = ''
2386 if opts.get('user'):
2387 if opts.get('user'):
2387 extra += ' --user %s' % util.shellquote(opts['user'])
2388 extra += ' --user %s' % util.shellquote(opts['user'])
2388 if opts.get('date'):
2389 if opts.get('date'):
2389 extra += ' --date %s' % util.shellquote(opts['date'])
2390 extra += ' --date %s' % util.shellquote(opts['date'])
2390 if opts.get('log'):
2391 if opts.get('log'):
2391 extra += ' --log'
2392 extra += ' --log'
2392 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2393 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2393 raise error.Abort(
2394 raise error.Abort(
2394 _("unresolved conflicts, can't continue"),
2395 _("unresolved conflicts, can't continue"),
2395 hint=hint)
2396 hint=hint)
2396 else:
2397 else:
2397 cont = False
2398 cont = False
2398
2399
2399 # commit
2400 # commit
2400 node = repo.commit(text=message, user=user,
2401 node = repo.commit(text=message, user=user,
2401 date=date, extra=extra, editor=editor)
2402 date=date, extra=extra, editor=editor)
2402 if node is None:
2403 if node is None:
2403 ui.warn(
2404 ui.warn(
2404 _('note: graft of %d:%s created no changes to commit\n') %
2405 _('note: graft of %d:%s created no changes to commit\n') %
2405 (ctx.rev(), ctx))
2406 (ctx.rev(), ctx))
2406
2407
2407 # remove state when we complete successfully
2408 # remove state when we complete successfully
2408 if not opts.get('dry_run'):
2409 if not opts.get('dry_run'):
2409 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2410 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2410
2411
2411 return 0
2412 return 0
2412
2413
2413 @command('grep',
2414 @command('grep',
2414 [('0', 'print0', None, _('end fields with NUL')),
2415 [('0', 'print0', None, _('end fields with NUL')),
2415 ('', 'all', None, _('print all revisions that match')),
2416 ('', 'all', None, _('print all revisions that match')),
2416 ('a', 'text', None, _('treat all files as text')),
2417 ('a', 'text', None, _('treat all files as text')),
2417 ('f', 'follow', None,
2418 ('f', 'follow', None,
2418 _('follow changeset history,'
2419 _('follow changeset history,'
2419 ' or file history across copies and renames')),
2420 ' or file history across copies and renames')),
2420 ('i', 'ignore-case', None, _('ignore case when matching')),
2421 ('i', 'ignore-case', None, _('ignore case when matching')),
2421 ('l', 'files-with-matches', None,
2422 ('l', 'files-with-matches', None,
2422 _('print only filenames and revisions that match')),
2423 _('print only filenames and revisions that match')),
2423 ('n', 'line-number', None, _('print matching line numbers')),
2424 ('n', 'line-number', None, _('print matching line numbers')),
2424 ('r', 'rev', [],
2425 ('r', 'rev', [],
2425 _('only search files changed within revision range'), _('REV')),
2426 _('only search files changed within revision range'), _('REV')),
2426 ('u', 'user', None, _('list the author (long with -v)')),
2427 ('u', 'user', None, _('list the author (long with -v)')),
2427 ('d', 'date', None, _('list the date (short with -q)')),
2428 ('d', 'date', None, _('list the date (short with -q)')),
2428 ] + formatteropts + walkopts,
2429 ] + formatteropts + walkopts,
2429 _('[OPTION]... PATTERN [FILE]...'),
2430 _('[OPTION]... PATTERN [FILE]...'),
2430 inferrepo=True)
2431 inferrepo=True)
2431 def grep(ui, repo, pattern, *pats, **opts):
2432 def grep(ui, repo, pattern, *pats, **opts):
2432 """search revision history for a pattern in specified files
2433 """search revision history for a pattern in specified files
2433
2434
2434 Search revision history for a regular expression in the specified
2435 Search revision history for a regular expression in the specified
2435 files or the entire project.
2436 files or the entire project.
2436
2437
2437 By default, grep prints the most recent revision number for each
2438 By default, grep prints the most recent revision number for each
2438 file in which it finds a match. To get it to print every revision
2439 file in which it finds a match. To get it to print every revision
2439 that contains a change in match status ("-" for a match that becomes
2440 that contains a change in match status ("-" for a match that becomes
2440 a non-match, or "+" for a non-match that becomes a match), use the
2441 a non-match, or "+" for a non-match that becomes a match), use the
2441 --all flag.
2442 --all flag.
2442
2443
2443 PATTERN can be any Python (roughly Perl-compatible) regular
2444 PATTERN can be any Python (roughly Perl-compatible) regular
2444 expression.
2445 expression.
2445
2446
2446 If no FILEs are specified (and -f/--follow isn't set), all files in
2447 If no FILEs are specified (and -f/--follow isn't set), all files in
2447 the repository are searched, including those that don't exist in the
2448 the repository are searched, including those that don't exist in the
2448 current branch or have been deleted in a prior changeset.
2449 current branch or have been deleted in a prior changeset.
2449
2450
2450 Returns 0 if a match is found, 1 otherwise.
2451 Returns 0 if a match is found, 1 otherwise.
2451 """
2452 """
2452 opts = pycompat.byteskwargs(opts)
2453 opts = pycompat.byteskwargs(opts)
2453 reflags = re.M
2454 reflags = re.M
2454 if opts.get('ignore_case'):
2455 if opts.get('ignore_case'):
2455 reflags |= re.I
2456 reflags |= re.I
2456 try:
2457 try:
2457 regexp = util.re.compile(pattern, reflags)
2458 regexp = util.re.compile(pattern, reflags)
2458 except re.error as inst:
2459 except re.error as inst:
2459 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2460 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2460 return 1
2461 return 1
2461 sep, eol = ':', '\n'
2462 sep, eol = ':', '\n'
2462 if opts.get('print0'):
2463 if opts.get('print0'):
2463 sep = eol = '\0'
2464 sep = eol = '\0'
2464
2465
2465 getfile = util.lrucachefunc(repo.file)
2466 getfile = util.lrucachefunc(repo.file)
2466
2467
2467 def matchlines(body):
2468 def matchlines(body):
2468 begin = 0
2469 begin = 0
2469 linenum = 0
2470 linenum = 0
2470 while begin < len(body):
2471 while begin < len(body):
2471 match = regexp.search(body, begin)
2472 match = regexp.search(body, begin)
2472 if not match:
2473 if not match:
2473 break
2474 break
2474 mstart, mend = match.span()
2475 mstart, mend = match.span()
2475 linenum += body.count('\n', begin, mstart) + 1
2476 linenum += body.count('\n', begin, mstart) + 1
2476 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2477 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2477 begin = body.find('\n', mend) + 1 or len(body) + 1
2478 begin = body.find('\n', mend) + 1 or len(body) + 1
2478 lend = begin - 1
2479 lend = begin - 1
2479 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2480 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2480
2481
2481 class linestate(object):
2482 class linestate(object):
2482 def __init__(self, line, linenum, colstart, colend):
2483 def __init__(self, line, linenum, colstart, colend):
2483 self.line = line
2484 self.line = line
2484 self.linenum = linenum
2485 self.linenum = linenum
2485 self.colstart = colstart
2486 self.colstart = colstart
2486 self.colend = colend
2487 self.colend = colend
2487
2488
2488 def __hash__(self):
2489 def __hash__(self):
2489 return hash((self.linenum, self.line))
2490 return hash((self.linenum, self.line))
2490
2491
2491 def __eq__(self, other):
2492 def __eq__(self, other):
2492 return self.line == other.line
2493 return self.line == other.line
2493
2494
2494 def findpos(self):
2495 def findpos(self):
2495 """Iterate all (start, end) indices of matches"""
2496 """Iterate all (start, end) indices of matches"""
2496 yield self.colstart, self.colend
2497 yield self.colstart, self.colend
2497 p = self.colend
2498 p = self.colend
2498 while p < len(self.line):
2499 while p < len(self.line):
2499 m = regexp.search(self.line, p)
2500 m = regexp.search(self.line, p)
2500 if not m:
2501 if not m:
2501 break
2502 break
2502 yield m.span()
2503 yield m.span()
2503 p = m.end()
2504 p = m.end()
2504
2505
2505 matches = {}
2506 matches = {}
2506 copies = {}
2507 copies = {}
2507 def grepbody(fn, rev, body):
2508 def grepbody(fn, rev, body):
2508 matches[rev].setdefault(fn, [])
2509 matches[rev].setdefault(fn, [])
2509 m = matches[rev][fn]
2510 m = matches[rev][fn]
2510 for lnum, cstart, cend, line in matchlines(body):
2511 for lnum, cstart, cend, line in matchlines(body):
2511 s = linestate(line, lnum, cstart, cend)
2512 s = linestate(line, lnum, cstart, cend)
2512 m.append(s)
2513 m.append(s)
2513
2514
2514 def difflinestates(a, b):
2515 def difflinestates(a, b):
2515 sm = difflib.SequenceMatcher(None, a, b)
2516 sm = difflib.SequenceMatcher(None, a, b)
2516 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2517 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2517 if tag == 'insert':
2518 if tag == 'insert':
2518 for i in xrange(blo, bhi):
2519 for i in xrange(blo, bhi):
2519 yield ('+', b[i])
2520 yield ('+', b[i])
2520 elif tag == 'delete':
2521 elif tag == 'delete':
2521 for i in xrange(alo, ahi):
2522 for i in xrange(alo, ahi):
2522 yield ('-', a[i])
2523 yield ('-', a[i])
2523 elif tag == 'replace':
2524 elif tag == 'replace':
2524 for i in xrange(alo, ahi):
2525 for i in xrange(alo, ahi):
2525 yield ('-', a[i])
2526 yield ('-', a[i])
2526 for i in xrange(blo, bhi):
2527 for i in xrange(blo, bhi):
2527 yield ('+', b[i])
2528 yield ('+', b[i])
2528
2529
2529 def display(fm, fn, ctx, pstates, states):
2530 def display(fm, fn, ctx, pstates, states):
2530 rev = ctx.rev()
2531 rev = ctx.rev()
2531 if fm.isplain():
2532 if fm.isplain():
2532 formatuser = ui.shortuser
2533 formatuser = ui.shortuser
2533 else:
2534 else:
2534 formatuser = str
2535 formatuser = str
2535 if ui.quiet:
2536 if ui.quiet:
2536 datefmt = '%Y-%m-%d'
2537 datefmt = '%Y-%m-%d'
2537 else:
2538 else:
2538 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2539 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2539 found = False
2540 found = False
2540 @util.cachefunc
2541 @util.cachefunc
2541 def binary():
2542 def binary():
2542 flog = getfile(fn)
2543 flog = getfile(fn)
2543 return util.binary(flog.read(ctx.filenode(fn)))
2544 return util.binary(flog.read(ctx.filenode(fn)))
2544
2545
2545 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2546 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2546 if opts.get('all'):
2547 if opts.get('all'):
2547 iter = difflinestates(pstates, states)
2548 iter = difflinestates(pstates, states)
2548 else:
2549 else:
2549 iter = [('', l) for l in states]
2550 iter = [('', l) for l in states]
2550 for change, l in iter:
2551 for change, l in iter:
2551 fm.startitem()
2552 fm.startitem()
2552 fm.data(node=fm.hexfunc(ctx.node()))
2553 fm.data(node=fm.hexfunc(ctx.node()))
2553 cols = [
2554 cols = [
2554 ('filename', fn, True),
2555 ('filename', fn, True),
2555 ('rev', rev, True),
2556 ('rev', rev, True),
2556 ('linenumber', l.linenum, opts.get('line_number')),
2557 ('linenumber', l.linenum, opts.get('line_number')),
2557 ]
2558 ]
2558 if opts.get('all'):
2559 if opts.get('all'):
2559 cols.append(('change', change, True))
2560 cols.append(('change', change, True))
2560 cols.extend([
2561 cols.extend([
2561 ('user', formatuser(ctx.user()), opts.get('user')),
2562 ('user', formatuser(ctx.user()), opts.get('user')),
2562 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2563 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2563 ])
2564 ])
2564 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2565 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2565 for name, data, cond in cols:
2566 for name, data, cond in cols:
2566 field = fieldnamemap.get(name, name)
2567 field = fieldnamemap.get(name, name)
2567 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2568 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2568 if cond and name != lastcol:
2569 if cond and name != lastcol:
2569 fm.plain(sep, label='grep.sep')
2570 fm.plain(sep, label='grep.sep')
2570 if not opts.get('files_with_matches'):
2571 if not opts.get('files_with_matches'):
2571 fm.plain(sep, label='grep.sep')
2572 fm.plain(sep, label='grep.sep')
2572 if not opts.get('text') and binary():
2573 if not opts.get('text') and binary():
2573 fm.plain(_(" Binary file matches"))
2574 fm.plain(_(" Binary file matches"))
2574 else:
2575 else:
2575 displaymatches(fm.nested('texts'), l)
2576 displaymatches(fm.nested('texts'), l)
2576 fm.plain(eol)
2577 fm.plain(eol)
2577 found = True
2578 found = True
2578 if opts.get('files_with_matches'):
2579 if opts.get('files_with_matches'):
2579 break
2580 break
2580 return found
2581 return found
2581
2582
2582 def displaymatches(fm, l):
2583 def displaymatches(fm, l):
2583 p = 0
2584 p = 0
2584 for s, e in l.findpos():
2585 for s, e in l.findpos():
2585 if p < s:
2586 if p < s:
2586 fm.startitem()
2587 fm.startitem()
2587 fm.write('text', '%s', l.line[p:s])
2588 fm.write('text', '%s', l.line[p:s])
2588 fm.data(matched=False)
2589 fm.data(matched=False)
2589 fm.startitem()
2590 fm.startitem()
2590 fm.write('text', '%s', l.line[s:e], label='grep.match')
2591 fm.write('text', '%s', l.line[s:e], label='grep.match')
2591 fm.data(matched=True)
2592 fm.data(matched=True)
2592 p = e
2593 p = e
2593 if p < len(l.line):
2594 if p < len(l.line):
2594 fm.startitem()
2595 fm.startitem()
2595 fm.write('text', '%s', l.line[p:])
2596 fm.write('text', '%s', l.line[p:])
2596 fm.data(matched=False)
2597 fm.data(matched=False)
2597 fm.end()
2598 fm.end()
2598
2599
2599 skip = {}
2600 skip = {}
2600 revfiles = {}
2601 revfiles = {}
2601 matchfn = scmutil.match(repo[None], pats, opts)
2602 matchfn = scmutil.match(repo[None], pats, opts)
2602 found = False
2603 found = False
2603 follow = opts.get('follow')
2604 follow = opts.get('follow')
2604
2605
2605 def prep(ctx, fns):
2606 def prep(ctx, fns):
2606 rev = ctx.rev()
2607 rev = ctx.rev()
2607 pctx = ctx.p1()
2608 pctx = ctx.p1()
2608 parent = pctx.rev()
2609 parent = pctx.rev()
2609 matches.setdefault(rev, {})
2610 matches.setdefault(rev, {})
2610 matches.setdefault(parent, {})
2611 matches.setdefault(parent, {})
2611 files = revfiles.setdefault(rev, [])
2612 files = revfiles.setdefault(rev, [])
2612 for fn in fns:
2613 for fn in fns:
2613 flog = getfile(fn)
2614 flog = getfile(fn)
2614 try:
2615 try:
2615 fnode = ctx.filenode(fn)
2616 fnode = ctx.filenode(fn)
2616 except error.LookupError:
2617 except error.LookupError:
2617 continue
2618 continue
2618
2619
2619 copied = flog.renamed(fnode)
2620 copied = flog.renamed(fnode)
2620 copy = follow and copied and copied[0]
2621 copy = follow and copied and copied[0]
2621 if copy:
2622 if copy:
2622 copies.setdefault(rev, {})[fn] = copy
2623 copies.setdefault(rev, {})[fn] = copy
2623 if fn in skip:
2624 if fn in skip:
2624 if copy:
2625 if copy:
2625 skip[copy] = True
2626 skip[copy] = True
2626 continue
2627 continue
2627 files.append(fn)
2628 files.append(fn)
2628
2629
2629 if fn not in matches[rev]:
2630 if fn not in matches[rev]:
2630 grepbody(fn, rev, flog.read(fnode))
2631 grepbody(fn, rev, flog.read(fnode))
2631
2632
2632 pfn = copy or fn
2633 pfn = copy or fn
2633 if pfn not in matches[parent]:
2634 if pfn not in matches[parent]:
2634 try:
2635 try:
2635 fnode = pctx.filenode(pfn)
2636 fnode = pctx.filenode(pfn)
2636 grepbody(pfn, parent, flog.read(fnode))
2637 grepbody(pfn, parent, flog.read(fnode))
2637 except error.LookupError:
2638 except error.LookupError:
2638 pass
2639 pass
2639
2640
2640 ui.pager('grep')
2641 ui.pager('grep')
2641 fm = ui.formatter('grep', opts)
2642 fm = ui.formatter('grep', opts)
2642 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2643 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2643 rev = ctx.rev()
2644 rev = ctx.rev()
2644 parent = ctx.p1().rev()
2645 parent = ctx.p1().rev()
2645 for fn in sorted(revfiles.get(rev, [])):
2646 for fn in sorted(revfiles.get(rev, [])):
2646 states = matches[rev][fn]
2647 states = matches[rev][fn]
2647 copy = copies.get(rev, {}).get(fn)
2648 copy = copies.get(rev, {}).get(fn)
2648 if fn in skip:
2649 if fn in skip:
2649 if copy:
2650 if copy:
2650 skip[copy] = True
2651 skip[copy] = True
2651 continue
2652 continue
2652 pstates = matches.get(parent, {}).get(copy or fn, [])
2653 pstates = matches.get(parent, {}).get(copy or fn, [])
2653 if pstates or states:
2654 if pstates or states:
2654 r = display(fm, fn, ctx, pstates, states)
2655 r = display(fm, fn, ctx, pstates, states)
2655 found = found or r
2656 found = found or r
2656 if r and not opts.get('all'):
2657 if r and not opts.get('all'):
2657 skip[fn] = True
2658 skip[fn] = True
2658 if copy:
2659 if copy:
2659 skip[copy] = True
2660 skip[copy] = True
2660 del matches[rev]
2661 del matches[rev]
2661 del revfiles[rev]
2662 del revfiles[rev]
2662 fm.end()
2663 fm.end()
2663
2664
2664 return not found
2665 return not found
2665
2666
2666 @command('heads',
2667 @command('heads',
2667 [('r', 'rev', '',
2668 [('r', 'rev', '',
2668 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2669 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2669 ('t', 'topo', False, _('show topological heads only')),
2670 ('t', 'topo', False, _('show topological heads only')),
2670 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2671 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2671 ('c', 'closed', False, _('show normal and closed branch heads')),
2672 ('c', 'closed', False, _('show normal and closed branch heads')),
2672 ] + templateopts,
2673 ] + templateopts,
2673 _('[-ct] [-r STARTREV] [REV]...'))
2674 _('[-ct] [-r STARTREV] [REV]...'))
2674 def heads(ui, repo, *branchrevs, **opts):
2675 def heads(ui, repo, *branchrevs, **opts):
2675 """show branch heads
2676 """show branch heads
2676
2677
2677 With no arguments, show all open branch heads in the repository.
2678 With no arguments, show all open branch heads in the repository.
2678 Branch heads are changesets that have no descendants on the
2679 Branch heads are changesets that have no descendants on the
2679 same branch. They are where development generally takes place and
2680 same branch. They are where development generally takes place and
2680 are the usual targets for update and merge operations.
2681 are the usual targets for update and merge operations.
2681
2682
2682 If one or more REVs are given, only open branch heads on the
2683 If one or more REVs are given, only open branch heads on the
2683 branches associated with the specified changesets are shown. This
2684 branches associated with the specified changesets are shown. This
2684 means that you can use :hg:`heads .` to see the heads on the
2685 means that you can use :hg:`heads .` to see the heads on the
2685 currently checked-out branch.
2686 currently checked-out branch.
2686
2687
2687 If -c/--closed is specified, also show branch heads marked closed
2688 If -c/--closed is specified, also show branch heads marked closed
2688 (see :hg:`commit --close-branch`).
2689 (see :hg:`commit --close-branch`).
2689
2690
2690 If STARTREV is specified, only those heads that are descendants of
2691 If STARTREV is specified, only those heads that are descendants of
2691 STARTREV will be displayed.
2692 STARTREV will be displayed.
2692
2693
2693 If -t/--topo is specified, named branch mechanics will be ignored and only
2694 If -t/--topo is specified, named branch mechanics will be ignored and only
2694 topological heads (changesets with no children) will be shown.
2695 topological heads (changesets with no children) will be shown.
2695
2696
2696 Returns 0 if matching heads are found, 1 if not.
2697 Returns 0 if matching heads are found, 1 if not.
2697 """
2698 """
2698
2699
2699 opts = pycompat.byteskwargs(opts)
2700 opts = pycompat.byteskwargs(opts)
2700 start = None
2701 start = None
2701 if 'rev' in opts:
2702 if 'rev' in opts:
2702 start = scmutil.revsingle(repo, opts['rev'], None).node()
2703 start = scmutil.revsingle(repo, opts['rev'], None).node()
2703
2704
2704 if opts.get('topo'):
2705 if opts.get('topo'):
2705 heads = [repo[h] for h in repo.heads(start)]
2706 heads = [repo[h] for h in repo.heads(start)]
2706 else:
2707 else:
2707 heads = []
2708 heads = []
2708 for branch in repo.branchmap():
2709 for branch in repo.branchmap():
2709 heads += repo.branchheads(branch, start, opts.get('closed'))
2710 heads += repo.branchheads(branch, start, opts.get('closed'))
2710 heads = [repo[h] for h in heads]
2711 heads = [repo[h] for h in heads]
2711
2712
2712 if branchrevs:
2713 if branchrevs:
2713 branches = set(repo[br].branch() for br in branchrevs)
2714 branches = set(repo[br].branch() for br in branchrevs)
2714 heads = [h for h in heads if h.branch() in branches]
2715 heads = [h for h in heads if h.branch() in branches]
2715
2716
2716 if opts.get('active') and branchrevs:
2717 if opts.get('active') and branchrevs:
2717 dagheads = repo.heads(start)
2718 dagheads = repo.heads(start)
2718 heads = [h for h in heads if h.node() in dagheads]
2719 heads = [h for h in heads if h.node() in dagheads]
2719
2720
2720 if branchrevs:
2721 if branchrevs:
2721 haveheads = set(h.branch() for h in heads)
2722 haveheads = set(h.branch() for h in heads)
2722 if branches - haveheads:
2723 if branches - haveheads:
2723 headless = ', '.join(b for b in branches - haveheads)
2724 headless = ', '.join(b for b in branches - haveheads)
2724 msg = _('no open branch heads found on branches %s')
2725 msg = _('no open branch heads found on branches %s')
2725 if opts.get('rev'):
2726 if opts.get('rev'):
2726 msg += _(' (started at %s)') % opts['rev']
2727 msg += _(' (started at %s)') % opts['rev']
2727 ui.warn((msg + '\n') % headless)
2728 ui.warn((msg + '\n') % headless)
2728
2729
2729 if not heads:
2730 if not heads:
2730 return 1
2731 return 1
2731
2732
2732 ui.pager('heads')
2733 ui.pager('heads')
2733 heads = sorted(heads, key=lambda x: -x.rev())
2734 heads = sorted(heads, key=lambda x: -x.rev())
2734 displayer = cmdutil.show_changeset(ui, repo, opts)
2735 displayer = cmdutil.show_changeset(ui, repo, opts)
2735 for ctx in heads:
2736 for ctx in heads:
2736 displayer.show(ctx)
2737 displayer.show(ctx)
2737 displayer.close()
2738 displayer.close()
2738
2739
2739 @command('help',
2740 @command('help',
2740 [('e', 'extension', None, _('show only help for extensions')),
2741 [('e', 'extension', None, _('show only help for extensions')),
2741 ('c', 'command', None, _('show only help for commands')),
2742 ('c', 'command', None, _('show only help for commands')),
2742 ('k', 'keyword', None, _('show topics matching keyword')),
2743 ('k', 'keyword', None, _('show topics matching keyword')),
2743 ('s', 'system', [], _('show help for specific platform(s)')),
2744 ('s', 'system', [], _('show help for specific platform(s)')),
2744 ],
2745 ],
2745 _('[-ecks] [TOPIC]'),
2746 _('[-ecks] [TOPIC]'),
2746 norepo=True)
2747 norepo=True)
2747 def help_(ui, name=None, **opts):
2748 def help_(ui, name=None, **opts):
2748 """show help for a given topic or a help overview
2749 """show help for a given topic or a help overview
2749
2750
2750 With no arguments, print a list of commands with short help messages.
2751 With no arguments, print a list of commands with short help messages.
2751
2752
2752 Given a topic, extension, or command name, print help for that
2753 Given a topic, extension, or command name, print help for that
2753 topic.
2754 topic.
2754
2755
2755 Returns 0 if successful.
2756 Returns 0 if successful.
2756 """
2757 """
2757
2758
2758 keep = opts.get(r'system') or []
2759 keep = opts.get(r'system') or []
2759 if len(keep) == 0:
2760 if len(keep) == 0:
2760 if pycompat.sysplatform.startswith('win'):
2761 if pycompat.sysplatform.startswith('win'):
2761 keep.append('windows')
2762 keep.append('windows')
2762 elif pycompat.sysplatform == 'OpenVMS':
2763 elif pycompat.sysplatform == 'OpenVMS':
2763 keep.append('vms')
2764 keep.append('vms')
2764 elif pycompat.sysplatform == 'plan9':
2765 elif pycompat.sysplatform == 'plan9':
2765 keep.append('plan9')
2766 keep.append('plan9')
2766 else:
2767 else:
2767 keep.append('unix')
2768 keep.append('unix')
2768 keep.append(pycompat.sysplatform.lower())
2769 keep.append(pycompat.sysplatform.lower())
2769 if ui.verbose:
2770 if ui.verbose:
2770 keep.append('verbose')
2771 keep.append('verbose')
2771
2772
2772 formatted = help.formattedhelp(ui, name, keep=keep, **opts)
2773 formatted = help.formattedhelp(ui, name, keep=keep, **opts)
2773 ui.pager('help')
2774 ui.pager('help')
2774 ui.write(formatted)
2775 ui.write(formatted)
2775
2776
2776
2777
2777 @command('identify|id',
2778 @command('identify|id',
2778 [('r', 'rev', '',
2779 [('r', 'rev', '',
2779 _('identify the specified revision'), _('REV')),
2780 _('identify the specified revision'), _('REV')),
2780 ('n', 'num', None, _('show local revision number')),
2781 ('n', 'num', None, _('show local revision number')),
2781 ('i', 'id', None, _('show global revision id')),
2782 ('i', 'id', None, _('show global revision id')),
2782 ('b', 'branch', None, _('show branch')),
2783 ('b', 'branch', None, _('show branch')),
2783 ('t', 'tags', None, _('show tags')),
2784 ('t', 'tags', None, _('show tags')),
2784 ('B', 'bookmarks', None, _('show bookmarks')),
2785 ('B', 'bookmarks', None, _('show bookmarks')),
2785 ] + remoteopts,
2786 ] + remoteopts,
2786 _('[-nibtB] [-r REV] [SOURCE]'),
2787 _('[-nibtB] [-r REV] [SOURCE]'),
2787 optionalrepo=True)
2788 optionalrepo=True)
2788 def identify(ui, repo, source=None, rev=None,
2789 def identify(ui, repo, source=None, rev=None,
2789 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2790 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2790 """identify the working directory or specified revision
2791 """identify the working directory or specified revision
2791
2792
2792 Print a summary identifying the repository state at REV using one or
2793 Print a summary identifying the repository state at REV using one or
2793 two parent hash identifiers, followed by a "+" if the working
2794 two parent hash identifiers, followed by a "+" if the working
2794 directory has uncommitted changes, the branch name (if not default),
2795 directory has uncommitted changes, the branch name (if not default),
2795 a list of tags, and a list of bookmarks.
2796 a list of tags, and a list of bookmarks.
2796
2797
2797 When REV is not given, print a summary of the current state of the
2798 When REV is not given, print a summary of the current state of the
2798 repository.
2799 repository.
2799
2800
2800 Specifying a path to a repository root or Mercurial bundle will
2801 Specifying a path to a repository root or Mercurial bundle will
2801 cause lookup to operate on that repository/bundle.
2802 cause lookup to operate on that repository/bundle.
2802
2803
2803 .. container:: verbose
2804 .. container:: verbose
2804
2805
2805 Examples:
2806 Examples:
2806
2807
2807 - generate a build identifier for the working directory::
2808 - generate a build identifier for the working directory::
2808
2809
2809 hg id --id > build-id.dat
2810 hg id --id > build-id.dat
2810
2811
2811 - find the revision corresponding to a tag::
2812 - find the revision corresponding to a tag::
2812
2813
2813 hg id -n -r 1.3
2814 hg id -n -r 1.3
2814
2815
2815 - check the most recent revision of a remote repository::
2816 - check the most recent revision of a remote repository::
2816
2817
2817 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2818 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2818
2819
2819 See :hg:`log` for generating more information about specific revisions,
2820 See :hg:`log` for generating more information about specific revisions,
2820 including full hash identifiers.
2821 including full hash identifiers.
2821
2822
2822 Returns 0 if successful.
2823 Returns 0 if successful.
2823 """
2824 """
2824
2825
2825 opts = pycompat.byteskwargs(opts)
2826 opts = pycompat.byteskwargs(opts)
2826 if not repo and not source:
2827 if not repo and not source:
2827 raise error.Abort(_("there is no Mercurial repository here "
2828 raise error.Abort(_("there is no Mercurial repository here "
2828 "(.hg not found)"))
2829 "(.hg not found)"))
2829
2830
2830 if ui.debugflag:
2831 if ui.debugflag:
2831 hexfunc = hex
2832 hexfunc = hex
2832 else:
2833 else:
2833 hexfunc = short
2834 hexfunc = short
2834 default = not (num or id or branch or tags or bookmarks)
2835 default = not (num or id or branch or tags or bookmarks)
2835 output = []
2836 output = []
2836 revs = []
2837 revs = []
2837
2838
2838 if source:
2839 if source:
2839 source, branches = hg.parseurl(ui.expandpath(source))
2840 source, branches = hg.parseurl(ui.expandpath(source))
2840 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2841 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2841 repo = peer.local()
2842 repo = peer.local()
2842 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2843 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2843
2844
2844 if not repo:
2845 if not repo:
2845 if num or branch or tags:
2846 if num or branch or tags:
2846 raise error.Abort(
2847 raise error.Abort(
2847 _("can't query remote revision number, branch, or tags"))
2848 _("can't query remote revision number, branch, or tags"))
2848 if not rev and revs:
2849 if not rev and revs:
2849 rev = revs[0]
2850 rev = revs[0]
2850 if not rev:
2851 if not rev:
2851 rev = "tip"
2852 rev = "tip"
2852
2853
2853 remoterev = peer.lookup(rev)
2854 remoterev = peer.lookup(rev)
2854 if default or id:
2855 if default or id:
2855 output = [hexfunc(remoterev)]
2856 output = [hexfunc(remoterev)]
2856
2857
2857 def getbms():
2858 def getbms():
2858 bms = []
2859 bms = []
2859
2860
2860 if 'bookmarks' in peer.listkeys('namespaces'):
2861 if 'bookmarks' in peer.listkeys('namespaces'):
2861 hexremoterev = hex(remoterev)
2862 hexremoterev = hex(remoterev)
2862 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2863 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2863 if bmr == hexremoterev]
2864 if bmr == hexremoterev]
2864
2865
2865 return sorted(bms)
2866 return sorted(bms)
2866
2867
2867 if bookmarks:
2868 if bookmarks:
2868 output.extend(getbms())
2869 output.extend(getbms())
2869 elif default and not ui.quiet:
2870 elif default and not ui.quiet:
2870 # multiple bookmarks for a single parent separated by '/'
2871 # multiple bookmarks for a single parent separated by '/'
2871 bm = '/'.join(getbms())
2872 bm = '/'.join(getbms())
2872 if bm:
2873 if bm:
2873 output.append(bm)
2874 output.append(bm)
2874 else:
2875 else:
2875 ctx = scmutil.revsingle(repo, rev, None)
2876 ctx = scmutil.revsingle(repo, rev, None)
2876
2877
2877 if ctx.rev() is None:
2878 if ctx.rev() is None:
2878 ctx = repo[None]
2879 ctx = repo[None]
2879 parents = ctx.parents()
2880 parents = ctx.parents()
2880 taglist = []
2881 taglist = []
2881 for p in parents:
2882 for p in parents:
2882 taglist.extend(p.tags())
2883 taglist.extend(p.tags())
2883
2884
2884 changed = ""
2885 changed = ""
2885 if default or id or num:
2886 if default or id or num:
2886 if (any(repo.status())
2887 if (any(repo.status())
2887 or any(ctx.sub(s).dirty() for s in ctx.substate)):
2888 or any(ctx.sub(s).dirty() for s in ctx.substate)):
2888 changed = '+'
2889 changed = '+'
2889 if default or id:
2890 if default or id:
2890 output = ["%s%s" %
2891 output = ["%s%s" %
2891 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2892 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2892 if num:
2893 if num:
2893 output.append("%s%s" %
2894 output.append("%s%s" %
2894 ('+'.join([str(p.rev()) for p in parents]), changed))
2895 ('+'.join([str(p.rev()) for p in parents]), changed))
2895 else:
2896 else:
2896 if default or id:
2897 if default or id:
2897 output = [hexfunc(ctx.node())]
2898 output = [hexfunc(ctx.node())]
2898 if num:
2899 if num:
2899 output.append(str(ctx.rev()))
2900 output.append(str(ctx.rev()))
2900 taglist = ctx.tags()
2901 taglist = ctx.tags()
2901
2902
2902 if default and not ui.quiet:
2903 if default and not ui.quiet:
2903 b = ctx.branch()
2904 b = ctx.branch()
2904 if b != 'default':
2905 if b != 'default':
2905 output.append("(%s)" % b)
2906 output.append("(%s)" % b)
2906
2907
2907 # multiple tags for a single parent separated by '/'
2908 # multiple tags for a single parent separated by '/'
2908 t = '/'.join(taglist)
2909 t = '/'.join(taglist)
2909 if t:
2910 if t:
2910 output.append(t)
2911 output.append(t)
2911
2912
2912 # multiple bookmarks for a single parent separated by '/'
2913 # multiple bookmarks for a single parent separated by '/'
2913 bm = '/'.join(ctx.bookmarks())
2914 bm = '/'.join(ctx.bookmarks())
2914 if bm:
2915 if bm:
2915 output.append(bm)
2916 output.append(bm)
2916 else:
2917 else:
2917 if branch:
2918 if branch:
2918 output.append(ctx.branch())
2919 output.append(ctx.branch())
2919
2920
2920 if tags:
2921 if tags:
2921 output.extend(taglist)
2922 output.extend(taglist)
2922
2923
2923 if bookmarks:
2924 if bookmarks:
2924 output.extend(ctx.bookmarks())
2925 output.extend(ctx.bookmarks())
2925
2926
2926 ui.write("%s\n" % ' '.join(output))
2927 ui.write("%s\n" % ' '.join(output))
2927
2928
2928 @command('import|patch',
2929 @command('import|patch',
2929 [('p', 'strip', 1,
2930 [('p', 'strip', 1,
2930 _('directory strip option for patch. This has the same '
2931 _('directory strip option for patch. This has the same '
2931 'meaning as the corresponding patch option'), _('NUM')),
2932 'meaning as the corresponding patch option'), _('NUM')),
2932 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2933 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2933 ('e', 'edit', False, _('invoke editor on commit messages')),
2934 ('e', 'edit', False, _('invoke editor on commit messages')),
2934 ('f', 'force', None,
2935 ('f', 'force', None,
2935 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2936 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2936 ('', 'no-commit', None,
2937 ('', 'no-commit', None,
2937 _("don't commit, just update the working directory")),
2938 _("don't commit, just update the working directory")),
2938 ('', 'bypass', None,
2939 ('', 'bypass', None,
2939 _("apply patch without touching the working directory")),
2940 _("apply patch without touching the working directory")),
2940 ('', 'partial', None,
2941 ('', 'partial', None,
2941 _('commit even if some hunks fail')),
2942 _('commit even if some hunks fail')),
2942 ('', 'exact', None,
2943 ('', 'exact', None,
2943 _('abort if patch would apply lossily')),
2944 _('abort if patch would apply lossily')),
2944 ('', 'prefix', '',
2945 ('', 'prefix', '',
2945 _('apply patch to subdirectory'), _('DIR')),
2946 _('apply patch to subdirectory'), _('DIR')),
2946 ('', 'import-branch', None,
2947 ('', 'import-branch', None,
2947 _('use any branch information in patch (implied by --exact)'))] +
2948 _('use any branch information in patch (implied by --exact)'))] +
2948 commitopts + commitopts2 + similarityopts,
2949 commitopts + commitopts2 + similarityopts,
2949 _('[OPTION]... PATCH...'))
2950 _('[OPTION]... PATCH...'))
2950 def import_(ui, repo, patch1=None, *patches, **opts):
2951 def import_(ui, repo, patch1=None, *patches, **opts):
2951 """import an ordered set of patches
2952 """import an ordered set of patches
2952
2953
2953 Import a list of patches and commit them individually (unless
2954 Import a list of patches and commit them individually (unless
2954 --no-commit is specified).
2955 --no-commit is specified).
2955
2956
2956 To read a patch from standard input (stdin), use "-" as the patch
2957 To read a patch from standard input (stdin), use "-" as the patch
2957 name. If a URL is specified, the patch will be downloaded from
2958 name. If a URL is specified, the patch will be downloaded from
2958 there.
2959 there.
2959
2960
2960 Import first applies changes to the working directory (unless
2961 Import first applies changes to the working directory (unless
2961 --bypass is specified), import will abort if there are outstanding
2962 --bypass is specified), import will abort if there are outstanding
2962 changes.
2963 changes.
2963
2964
2964 Use --bypass to apply and commit patches directly to the
2965 Use --bypass to apply and commit patches directly to the
2965 repository, without affecting the working directory. Without
2966 repository, without affecting the working directory. Without
2966 --exact, patches will be applied on top of the working directory
2967 --exact, patches will be applied on top of the working directory
2967 parent revision.
2968 parent revision.
2968
2969
2969 You can import a patch straight from a mail message. Even patches
2970 You can import a patch straight from a mail message. Even patches
2970 as attachments work (to use the body part, it must have type
2971 as attachments work (to use the body part, it must have type
2971 text/plain or text/x-patch). From and Subject headers of email
2972 text/plain or text/x-patch). From and Subject headers of email
2972 message are used as default committer and commit message. All
2973 message are used as default committer and commit message. All
2973 text/plain body parts before first diff are added to the commit
2974 text/plain body parts before first diff are added to the commit
2974 message.
2975 message.
2975
2976
2976 If the imported patch was generated by :hg:`export`, user and
2977 If the imported patch was generated by :hg:`export`, user and
2977 description from patch override values from message headers and
2978 description from patch override values from message headers and
2978 body. Values given on command line with -m/--message and -u/--user
2979 body. Values given on command line with -m/--message and -u/--user
2979 override these.
2980 override these.
2980
2981
2981 If --exact is specified, import will set the working directory to
2982 If --exact is specified, import will set the working directory to
2982 the parent of each patch before applying it, and will abort if the
2983 the parent of each patch before applying it, and will abort if the
2983 resulting changeset has a different ID than the one recorded in
2984 resulting changeset has a different ID than the one recorded in
2984 the patch. This will guard against various ways that portable
2985 the patch. This will guard against various ways that portable
2985 patch formats and mail systems might fail to transfer Mercurial
2986 patch formats and mail systems might fail to transfer Mercurial
2986 data or metadata. See :hg:`bundle` for lossless transmission.
2987 data or metadata. See :hg:`bundle` for lossless transmission.
2987
2988
2988 Use --partial to ensure a changeset will be created from the patch
2989 Use --partial to ensure a changeset will be created from the patch
2989 even if some hunks fail to apply. Hunks that fail to apply will be
2990 even if some hunks fail to apply. Hunks that fail to apply will be
2990 written to a <target-file>.rej file. Conflicts can then be resolved
2991 written to a <target-file>.rej file. Conflicts can then be resolved
2991 by hand before :hg:`commit --amend` is run to update the created
2992 by hand before :hg:`commit --amend` is run to update the created
2992 changeset. This flag exists to let people import patches that
2993 changeset. This flag exists to let people import patches that
2993 partially apply without losing the associated metadata (author,
2994 partially apply without losing the associated metadata (author,
2994 date, description, ...).
2995 date, description, ...).
2995
2996
2996 .. note::
2997 .. note::
2997
2998
2998 When no hunks apply cleanly, :hg:`import --partial` will create
2999 When no hunks apply cleanly, :hg:`import --partial` will create
2999 an empty changeset, importing only the patch metadata.
3000 an empty changeset, importing only the patch metadata.
3000
3001
3001 With -s/--similarity, hg will attempt to discover renames and
3002 With -s/--similarity, hg will attempt to discover renames and
3002 copies in the patch in the same way as :hg:`addremove`.
3003 copies in the patch in the same way as :hg:`addremove`.
3003
3004
3004 It is possible to use external patch programs to perform the patch
3005 It is possible to use external patch programs to perform the patch
3005 by setting the ``ui.patch`` configuration option. For the default
3006 by setting the ``ui.patch`` configuration option. For the default
3006 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3007 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3007 See :hg:`help config` for more information about configuration
3008 See :hg:`help config` for more information about configuration
3008 files and how to use these options.
3009 files and how to use these options.
3009
3010
3010 See :hg:`help dates` for a list of formats valid for -d/--date.
3011 See :hg:`help dates` for a list of formats valid for -d/--date.
3011
3012
3012 .. container:: verbose
3013 .. container:: verbose
3013
3014
3014 Examples:
3015 Examples:
3015
3016
3016 - import a traditional patch from a website and detect renames::
3017 - import a traditional patch from a website and detect renames::
3017
3018
3018 hg import -s 80 http://example.com/bugfix.patch
3019 hg import -s 80 http://example.com/bugfix.patch
3019
3020
3020 - import a changeset from an hgweb server::
3021 - import a changeset from an hgweb server::
3021
3022
3022 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3023 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3023
3024
3024 - import all the patches in an Unix-style mbox::
3025 - import all the patches in an Unix-style mbox::
3025
3026
3026 hg import incoming-patches.mbox
3027 hg import incoming-patches.mbox
3027
3028
3028 - import patches from stdin::
3029 - import patches from stdin::
3029
3030
3030 hg import -
3031 hg import -
3031
3032
3032 - attempt to exactly restore an exported changeset (not always
3033 - attempt to exactly restore an exported changeset (not always
3033 possible)::
3034 possible)::
3034
3035
3035 hg import --exact proposed-fix.patch
3036 hg import --exact proposed-fix.patch
3036
3037
3037 - use an external tool to apply a patch which is too fuzzy for
3038 - use an external tool to apply a patch which is too fuzzy for
3038 the default internal tool.
3039 the default internal tool.
3039
3040
3040 hg import --config ui.patch="patch --merge" fuzzy.patch
3041 hg import --config ui.patch="patch --merge" fuzzy.patch
3041
3042
3042 - change the default fuzzing from 2 to a less strict 7
3043 - change the default fuzzing from 2 to a less strict 7
3043
3044
3044 hg import --config ui.fuzz=7 fuzz.patch
3045 hg import --config ui.fuzz=7 fuzz.patch
3045
3046
3046 Returns 0 on success, 1 on partial success (see --partial).
3047 Returns 0 on success, 1 on partial success (see --partial).
3047 """
3048 """
3048
3049
3049 opts = pycompat.byteskwargs(opts)
3050 opts = pycompat.byteskwargs(opts)
3050 if not patch1:
3051 if not patch1:
3051 raise error.Abort(_('need at least one patch to import'))
3052 raise error.Abort(_('need at least one patch to import'))
3052
3053
3053 patches = (patch1,) + patches
3054 patches = (patch1,) + patches
3054
3055
3055 date = opts.get('date')
3056 date = opts.get('date')
3056 if date:
3057 if date:
3057 opts['date'] = util.parsedate(date)
3058 opts['date'] = util.parsedate(date)
3058
3059
3059 exact = opts.get('exact')
3060 exact = opts.get('exact')
3060 update = not opts.get('bypass')
3061 update = not opts.get('bypass')
3061 if not update and opts.get('no_commit'):
3062 if not update and opts.get('no_commit'):
3062 raise error.Abort(_('cannot use --no-commit with --bypass'))
3063 raise error.Abort(_('cannot use --no-commit with --bypass'))
3063 try:
3064 try:
3064 sim = float(opts.get('similarity') or 0)
3065 sim = float(opts.get('similarity') or 0)
3065 except ValueError:
3066 except ValueError:
3066 raise error.Abort(_('similarity must be a number'))
3067 raise error.Abort(_('similarity must be a number'))
3067 if sim < 0 or sim > 100:
3068 if sim < 0 or sim > 100:
3068 raise error.Abort(_('similarity must be between 0 and 100'))
3069 raise error.Abort(_('similarity must be between 0 and 100'))
3069 if sim and not update:
3070 if sim and not update:
3070 raise error.Abort(_('cannot use --similarity with --bypass'))
3071 raise error.Abort(_('cannot use --similarity with --bypass'))
3071 if exact:
3072 if exact:
3072 if opts.get('edit'):
3073 if opts.get('edit'):
3073 raise error.Abort(_('cannot use --exact with --edit'))
3074 raise error.Abort(_('cannot use --exact with --edit'))
3074 if opts.get('prefix'):
3075 if opts.get('prefix'):
3075 raise error.Abort(_('cannot use --exact with --prefix'))
3076 raise error.Abort(_('cannot use --exact with --prefix'))
3076
3077
3077 base = opts["base"]
3078 base = opts["base"]
3078 wlock = dsguard = lock = tr = None
3079 wlock = dsguard = lock = tr = None
3079 msgs = []
3080 msgs = []
3080 ret = 0
3081 ret = 0
3081
3082
3082
3083
3083 try:
3084 try:
3084 wlock = repo.wlock()
3085 wlock = repo.wlock()
3085
3086
3086 if update:
3087 if update:
3087 cmdutil.checkunfinished(repo)
3088 cmdutil.checkunfinished(repo)
3088 if (exact or not opts.get('force')):
3089 if (exact or not opts.get('force')):
3089 cmdutil.bailifchanged(repo)
3090 cmdutil.bailifchanged(repo)
3090
3091
3091 if not opts.get('no_commit'):
3092 if not opts.get('no_commit'):
3092 lock = repo.lock()
3093 lock = repo.lock()
3093 tr = repo.transaction('import')
3094 tr = repo.transaction('import')
3094 else:
3095 else:
3095 dsguard = dirstateguard.dirstateguard(repo, 'import')
3096 dsguard = dirstateguard.dirstateguard(repo, 'import')
3096 parents = repo[None].parents()
3097 parents = repo[None].parents()
3097 for patchurl in patches:
3098 for patchurl in patches:
3098 if patchurl == '-':
3099 if patchurl == '-':
3099 ui.status(_('applying patch from stdin\n'))
3100 ui.status(_('applying patch from stdin\n'))
3100 patchfile = ui.fin
3101 patchfile = ui.fin
3101 patchurl = 'stdin' # for error message
3102 patchurl = 'stdin' # for error message
3102 else:
3103 else:
3103 patchurl = os.path.join(base, patchurl)
3104 patchurl = os.path.join(base, patchurl)
3104 ui.status(_('applying %s\n') % patchurl)
3105 ui.status(_('applying %s\n') % patchurl)
3105 patchfile = hg.openpath(ui, patchurl)
3106 patchfile = hg.openpath(ui, patchurl)
3106
3107
3107 haspatch = False
3108 haspatch = False
3108 for hunk in patch.split(patchfile):
3109 for hunk in patch.split(patchfile):
3109 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3110 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3110 parents, opts,
3111 parents, opts,
3111 msgs, hg.clean)
3112 msgs, hg.clean)
3112 if msg:
3113 if msg:
3113 haspatch = True
3114 haspatch = True
3114 ui.note(msg + '\n')
3115 ui.note(msg + '\n')
3115 if update or exact:
3116 if update or exact:
3116 parents = repo[None].parents()
3117 parents = repo[None].parents()
3117 else:
3118 else:
3118 parents = [repo[node]]
3119 parents = [repo[node]]
3119 if rej:
3120 if rej:
3120 ui.write_err(_("patch applied partially\n"))
3121 ui.write_err(_("patch applied partially\n"))
3121 ui.write_err(_("(fix the .rej files and run "
3122 ui.write_err(_("(fix the .rej files and run "
3122 "`hg commit --amend`)\n"))
3123 "`hg commit --amend`)\n"))
3123 ret = 1
3124 ret = 1
3124 break
3125 break
3125
3126
3126 if not haspatch:
3127 if not haspatch:
3127 raise error.Abort(_('%s: no diffs found') % patchurl)
3128 raise error.Abort(_('%s: no diffs found') % patchurl)
3128
3129
3129 if tr:
3130 if tr:
3130 tr.close()
3131 tr.close()
3131 if msgs:
3132 if msgs:
3132 repo.savecommitmessage('\n* * *\n'.join(msgs))
3133 repo.savecommitmessage('\n* * *\n'.join(msgs))
3133 if dsguard:
3134 if dsguard:
3134 dsguard.close()
3135 dsguard.close()
3135 return ret
3136 return ret
3136 finally:
3137 finally:
3137 if tr:
3138 if tr:
3138 tr.release()
3139 tr.release()
3139 release(lock, dsguard, wlock)
3140 release(lock, dsguard, wlock)
3140
3141
3141 @command('incoming|in',
3142 @command('incoming|in',
3142 [('f', 'force', None,
3143 [('f', 'force', None,
3143 _('run even if remote repository is unrelated')),
3144 _('run even if remote repository is unrelated')),
3144 ('n', 'newest-first', None, _('show newest record first')),
3145 ('n', 'newest-first', None, _('show newest record first')),
3145 ('', 'bundle', '',
3146 ('', 'bundle', '',
3146 _('file to store the bundles into'), _('FILE')),
3147 _('file to store the bundles into'), _('FILE')),
3147 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3148 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3148 ('B', 'bookmarks', False, _("compare bookmarks")),
3149 ('B', 'bookmarks', False, _("compare bookmarks")),
3149 ('b', 'branch', [],
3150 ('b', 'branch', [],
3150 _('a specific branch you would like to pull'), _('BRANCH')),
3151 _('a specific branch you would like to pull'), _('BRANCH')),
3151 ] + logopts + remoteopts + subrepoopts,
3152 ] + logopts + remoteopts + subrepoopts,
3152 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3153 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3153 def incoming(ui, repo, source="default", **opts):
3154 def incoming(ui, repo, source="default", **opts):
3154 """show new changesets found in source
3155 """show new changesets found in source
3155
3156
3156 Show new changesets found in the specified path/URL or the default
3157 Show new changesets found in the specified path/URL or the default
3157 pull location. These are the changesets that would have been pulled
3158 pull location. These are the changesets that would have been pulled
3158 if a pull at the time you issued this command.
3159 if a pull at the time you issued this command.
3159
3160
3160 See pull for valid source format details.
3161 See pull for valid source format details.
3161
3162
3162 .. container:: verbose
3163 .. container:: verbose
3163
3164
3164 With -B/--bookmarks, the result of bookmark comparison between
3165 With -B/--bookmarks, the result of bookmark comparison between
3165 local and remote repositories is displayed. With -v/--verbose,
3166 local and remote repositories is displayed. With -v/--verbose,
3166 status is also displayed for each bookmark like below::
3167 status is also displayed for each bookmark like below::
3167
3168
3168 BM1 01234567890a added
3169 BM1 01234567890a added
3169 BM2 1234567890ab advanced
3170 BM2 1234567890ab advanced
3170 BM3 234567890abc diverged
3171 BM3 234567890abc diverged
3171 BM4 34567890abcd changed
3172 BM4 34567890abcd changed
3172
3173
3173 The action taken locally when pulling depends on the
3174 The action taken locally when pulling depends on the
3174 status of each bookmark:
3175 status of each bookmark:
3175
3176
3176 :``added``: pull will create it
3177 :``added``: pull will create it
3177 :``advanced``: pull will update it
3178 :``advanced``: pull will update it
3178 :``diverged``: pull will create a divergent bookmark
3179 :``diverged``: pull will create a divergent bookmark
3179 :``changed``: result depends on remote changesets
3180 :``changed``: result depends on remote changesets
3180
3181
3181 From the point of view of pulling behavior, bookmark
3182 From the point of view of pulling behavior, bookmark
3182 existing only in the remote repository are treated as ``added``,
3183 existing only in the remote repository are treated as ``added``,
3183 even if it is in fact locally deleted.
3184 even if it is in fact locally deleted.
3184
3185
3185 .. container:: verbose
3186 .. container:: verbose
3186
3187
3187 For remote repository, using --bundle avoids downloading the
3188 For remote repository, using --bundle avoids downloading the
3188 changesets twice if the incoming is followed by a pull.
3189 changesets twice if the incoming is followed by a pull.
3189
3190
3190 Examples:
3191 Examples:
3191
3192
3192 - show incoming changes with patches and full description::
3193 - show incoming changes with patches and full description::
3193
3194
3194 hg incoming -vp
3195 hg incoming -vp
3195
3196
3196 - show incoming changes excluding merges, store a bundle::
3197 - show incoming changes excluding merges, store a bundle::
3197
3198
3198 hg in -vpM --bundle incoming.hg
3199 hg in -vpM --bundle incoming.hg
3199 hg pull incoming.hg
3200 hg pull incoming.hg
3200
3201
3201 - briefly list changes inside a bundle::
3202 - briefly list changes inside a bundle::
3202
3203
3203 hg in changes.hg -T "{desc|firstline}\\n"
3204 hg in changes.hg -T "{desc|firstline}\\n"
3204
3205
3205 Returns 0 if there are incoming changes, 1 otherwise.
3206 Returns 0 if there are incoming changes, 1 otherwise.
3206 """
3207 """
3207 opts = pycompat.byteskwargs(opts)
3208 opts = pycompat.byteskwargs(opts)
3208 if opts.get('graph'):
3209 if opts.get('graph'):
3209 cmdutil.checkunsupportedgraphflags([], opts)
3210 cmdutil.checkunsupportedgraphflags([], opts)
3210 def display(other, chlist, displayer):
3211 def display(other, chlist, displayer):
3211 revdag = cmdutil.graphrevs(other, chlist, opts)
3212 revdag = cmdutil.graphrevs(other, chlist, opts)
3212 cmdutil.displaygraph(ui, repo, revdag, displayer,
3213 cmdutil.displaygraph(ui, repo, revdag, displayer,
3213 graphmod.asciiedges)
3214 graphmod.asciiedges)
3214
3215
3215 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3216 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3216 return 0
3217 return 0
3217
3218
3218 if opts.get('bundle') and opts.get('subrepos'):
3219 if opts.get('bundle') and opts.get('subrepos'):
3219 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3220 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3220
3221
3221 if opts.get('bookmarks'):
3222 if opts.get('bookmarks'):
3222 source, branches = hg.parseurl(ui.expandpath(source),
3223 source, branches = hg.parseurl(ui.expandpath(source),
3223 opts.get('branch'))
3224 opts.get('branch'))
3224 other = hg.peer(repo, opts, source)
3225 other = hg.peer(repo, opts, source)
3225 if 'bookmarks' not in other.listkeys('namespaces'):
3226 if 'bookmarks' not in other.listkeys('namespaces'):
3226 ui.warn(_("remote doesn't support bookmarks\n"))
3227 ui.warn(_("remote doesn't support bookmarks\n"))
3227 return 0
3228 return 0
3228 ui.pager('incoming')
3229 ui.pager('incoming')
3229 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3230 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3230 return bookmarks.incoming(ui, repo, other)
3231 return bookmarks.incoming(ui, repo, other)
3231
3232
3232 repo._subtoppath = ui.expandpath(source)
3233 repo._subtoppath = ui.expandpath(source)
3233 try:
3234 try:
3234 return hg.incoming(ui, repo, source, opts)
3235 return hg.incoming(ui, repo, source, opts)
3235 finally:
3236 finally:
3236 del repo._subtoppath
3237 del repo._subtoppath
3237
3238
3238
3239
3239 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3240 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3240 norepo=True)
3241 norepo=True)
3241 def init(ui, dest=".", **opts):
3242 def init(ui, dest=".", **opts):
3242 """create a new repository in the given directory
3243 """create a new repository in the given directory
3243
3244
3244 Initialize a new repository in the given directory. If the given
3245 Initialize a new repository in the given directory. If the given
3245 directory does not exist, it will be created.
3246 directory does not exist, it will be created.
3246
3247
3247 If no directory is given, the current directory is used.
3248 If no directory is given, the current directory is used.
3248
3249
3249 It is possible to specify an ``ssh://`` URL as the destination.
3250 It is possible to specify an ``ssh://`` URL as the destination.
3250 See :hg:`help urls` for more information.
3251 See :hg:`help urls` for more information.
3251
3252
3252 Returns 0 on success.
3253 Returns 0 on success.
3253 """
3254 """
3254 opts = pycompat.byteskwargs(opts)
3255 opts = pycompat.byteskwargs(opts)
3255 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3256 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3256
3257
3257 @command('locate',
3258 @command('locate',
3258 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3259 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3259 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3260 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3260 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3261 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3261 ] + walkopts,
3262 ] + walkopts,
3262 _('[OPTION]... [PATTERN]...'))
3263 _('[OPTION]... [PATTERN]...'))
3263 def locate(ui, repo, *pats, **opts):
3264 def locate(ui, repo, *pats, **opts):
3264 """locate files matching specific patterns (DEPRECATED)
3265 """locate files matching specific patterns (DEPRECATED)
3265
3266
3266 Print files under Mercurial control in the working directory whose
3267 Print files under Mercurial control in the working directory whose
3267 names match the given patterns.
3268 names match the given patterns.
3268
3269
3269 By default, this command searches all directories in the working
3270 By default, this command searches all directories in the working
3270 directory. To search just the current directory and its
3271 directory. To search just the current directory and its
3271 subdirectories, use "--include .".
3272 subdirectories, use "--include .".
3272
3273
3273 If no patterns are given to match, this command prints the names
3274 If no patterns are given to match, this command prints the names
3274 of all files under Mercurial control in the working directory.
3275 of all files under Mercurial control in the working directory.
3275
3276
3276 If you want to feed the output of this command into the "xargs"
3277 If you want to feed the output of this command into the "xargs"
3277 command, use the -0 option to both this command and "xargs". This
3278 command, use the -0 option to both this command and "xargs". This
3278 will avoid the problem of "xargs" treating single filenames that
3279 will avoid the problem of "xargs" treating single filenames that
3279 contain whitespace as multiple filenames.
3280 contain whitespace as multiple filenames.
3280
3281
3281 See :hg:`help files` for a more versatile command.
3282 See :hg:`help files` for a more versatile command.
3282
3283
3283 Returns 0 if a match is found, 1 otherwise.
3284 Returns 0 if a match is found, 1 otherwise.
3284 """
3285 """
3285 opts = pycompat.byteskwargs(opts)
3286 opts = pycompat.byteskwargs(opts)
3286 if opts.get('print0'):
3287 if opts.get('print0'):
3287 end = '\0'
3288 end = '\0'
3288 else:
3289 else:
3289 end = '\n'
3290 end = '\n'
3290 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3291 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3291
3292
3292 ret = 1
3293 ret = 1
3293 ctx = repo[rev]
3294 ctx = repo[rev]
3294 m = scmutil.match(ctx, pats, opts, default='relglob',
3295 m = scmutil.match(ctx, pats, opts, default='relglob',
3295 badfn=lambda x, y: False)
3296 badfn=lambda x, y: False)
3296
3297
3297 ui.pager('locate')
3298 ui.pager('locate')
3298 for abs in ctx.matches(m):
3299 for abs in ctx.matches(m):
3299 if opts.get('fullpath'):
3300 if opts.get('fullpath'):
3300 ui.write(repo.wjoin(abs), end)
3301 ui.write(repo.wjoin(abs), end)
3301 else:
3302 else:
3302 ui.write(((pats and m.rel(abs)) or abs), end)
3303 ui.write(((pats and m.rel(abs)) or abs), end)
3303 ret = 0
3304 ret = 0
3304
3305
3305 return ret
3306 return ret
3306
3307
3307 @command('^log|history',
3308 @command('^log|history',
3308 [('f', 'follow', None,
3309 [('f', 'follow', None,
3309 _('follow changeset history, or file history across copies and renames')),
3310 _('follow changeset history, or file history across copies and renames')),
3310 ('', 'follow-first', None,
3311 ('', 'follow-first', None,
3311 _('only follow the first parent of merge changesets (DEPRECATED)')),
3312 _('only follow the first parent of merge changesets (DEPRECATED)')),
3312 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3313 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3313 ('C', 'copies', None, _('show copied files')),
3314 ('C', 'copies', None, _('show copied files')),
3314 ('k', 'keyword', [],
3315 ('k', 'keyword', [],
3315 _('do case-insensitive search for a given text'), _('TEXT')),
3316 _('do case-insensitive search for a given text'), _('TEXT')),
3316 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3317 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3317 ('', 'removed', None, _('include revisions where files were removed')),
3318 ('', 'removed', None, _('include revisions where files were removed')),
3318 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3319 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3319 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3320 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3320 ('', 'only-branch', [],
3321 ('', 'only-branch', [],
3321 _('show only changesets within the given named branch (DEPRECATED)'),
3322 _('show only changesets within the given named branch (DEPRECATED)'),
3322 _('BRANCH')),
3323 _('BRANCH')),
3323 ('b', 'branch', [],
3324 ('b', 'branch', [],
3324 _('show changesets within the given named branch'), _('BRANCH')),
3325 _('show changesets within the given named branch'), _('BRANCH')),
3325 ('P', 'prune', [],
3326 ('P', 'prune', [],
3326 _('do not display revision or any of its ancestors'), _('REV')),
3327 _('do not display revision or any of its ancestors'), _('REV')),
3327 ] + logopts + walkopts,
3328 ] + logopts + walkopts,
3328 _('[OPTION]... [FILE]'),
3329 _('[OPTION]... [FILE]'),
3329 inferrepo=True)
3330 inferrepo=True)
3330 def log(ui, repo, *pats, **opts):
3331 def log(ui, repo, *pats, **opts):
3331 """show revision history of entire repository or files
3332 """show revision history of entire repository or files
3332
3333
3333 Print the revision history of the specified files or the entire
3334 Print the revision history of the specified files or the entire
3334 project.
3335 project.
3335
3336
3336 If no revision range is specified, the default is ``tip:0`` unless
3337 If no revision range is specified, the default is ``tip:0`` unless
3337 --follow is set, in which case the working directory parent is
3338 --follow is set, in which case the working directory parent is
3338 used as the starting revision.
3339 used as the starting revision.
3339
3340
3340 File history is shown without following rename or copy history of
3341 File history is shown without following rename or copy history of
3341 files. Use -f/--follow with a filename to follow history across
3342 files. Use -f/--follow with a filename to follow history across
3342 renames and copies. --follow without a filename will only show
3343 renames and copies. --follow without a filename will only show
3343 ancestors or descendants of the starting revision.
3344 ancestors or descendants of the starting revision.
3344
3345
3345 By default this command prints revision number and changeset id,
3346 By default this command prints revision number and changeset id,
3346 tags, non-trivial parents, user, date and time, and a summary for
3347 tags, non-trivial parents, user, date and time, and a summary for
3347 each commit. When the -v/--verbose switch is used, the list of
3348 each commit. When the -v/--verbose switch is used, the list of
3348 changed files and full commit message are shown.
3349 changed files and full commit message are shown.
3349
3350
3350 With --graph the revisions are shown as an ASCII art DAG with the most
3351 With --graph the revisions are shown as an ASCII art DAG with the most
3351 recent changeset at the top.
3352 recent changeset at the top.
3352 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3353 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3353 and '+' represents a fork where the changeset from the lines below is a
3354 and '+' represents a fork where the changeset from the lines below is a
3354 parent of the 'o' merge on the same line.
3355 parent of the 'o' merge on the same line.
3355 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3356 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3356 of a '|' indicates one or more revisions in a path are omitted.
3357 of a '|' indicates one or more revisions in a path are omitted.
3357
3358
3358 .. note::
3359 .. note::
3359
3360
3360 :hg:`log --patch` may generate unexpected diff output for merge
3361 :hg:`log --patch` may generate unexpected diff output for merge
3361 changesets, as it will only compare the merge changeset against
3362 changesets, as it will only compare the merge changeset against
3362 its first parent. Also, only files different from BOTH parents
3363 its first parent. Also, only files different from BOTH parents
3363 will appear in files:.
3364 will appear in files:.
3364
3365
3365 .. note::
3366 .. note::
3366
3367
3367 For performance reasons, :hg:`log FILE` may omit duplicate changes
3368 For performance reasons, :hg:`log FILE` may omit duplicate changes
3368 made on branches and will not show removals or mode changes. To
3369 made on branches and will not show removals or mode changes. To
3369 see all such changes, use the --removed switch.
3370 see all such changes, use the --removed switch.
3370
3371
3371 .. container:: verbose
3372 .. container:: verbose
3372
3373
3373 Some examples:
3374 Some examples:
3374
3375
3375 - changesets with full descriptions and file lists::
3376 - changesets with full descriptions and file lists::
3376
3377
3377 hg log -v
3378 hg log -v
3378
3379
3379 - changesets ancestral to the working directory::
3380 - changesets ancestral to the working directory::
3380
3381
3381 hg log -f
3382 hg log -f
3382
3383
3383 - last 10 commits on the current branch::
3384 - last 10 commits on the current branch::
3384
3385
3385 hg log -l 10 -b .
3386 hg log -l 10 -b .
3386
3387
3387 - changesets showing all modifications of a file, including removals::
3388 - changesets showing all modifications of a file, including removals::
3388
3389
3389 hg log --removed file.c
3390 hg log --removed file.c
3390
3391
3391 - all changesets that touch a directory, with diffs, excluding merges::
3392 - all changesets that touch a directory, with diffs, excluding merges::
3392
3393
3393 hg log -Mp lib/
3394 hg log -Mp lib/
3394
3395
3395 - all revision numbers that match a keyword::
3396 - all revision numbers that match a keyword::
3396
3397
3397 hg log -k bug --template "{rev}\\n"
3398 hg log -k bug --template "{rev}\\n"
3398
3399
3399 - the full hash identifier of the working directory parent::
3400 - the full hash identifier of the working directory parent::
3400
3401
3401 hg log -r . --template "{node}\\n"
3402 hg log -r . --template "{node}\\n"
3402
3403
3403 - list available log templates::
3404 - list available log templates::
3404
3405
3405 hg log -T list
3406 hg log -T list
3406
3407
3407 - check if a given changeset is included in a tagged release::
3408 - check if a given changeset is included in a tagged release::
3408
3409
3409 hg log -r "a21ccf and ancestor(1.9)"
3410 hg log -r "a21ccf and ancestor(1.9)"
3410
3411
3411 - find all changesets by some user in a date range::
3412 - find all changesets by some user in a date range::
3412
3413
3413 hg log -k alice -d "may 2008 to jul 2008"
3414 hg log -k alice -d "may 2008 to jul 2008"
3414
3415
3415 - summary of all changesets after the last tag::
3416 - summary of all changesets after the last tag::
3416
3417
3417 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3418 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3418
3419
3419 See :hg:`help dates` for a list of formats valid for -d/--date.
3420 See :hg:`help dates` for a list of formats valid for -d/--date.
3420
3421
3421 See :hg:`help revisions` for more about specifying and ordering
3422 See :hg:`help revisions` for more about specifying and ordering
3422 revisions.
3423 revisions.
3423
3424
3424 See :hg:`help templates` for more about pre-packaged styles and
3425 See :hg:`help templates` for more about pre-packaged styles and
3425 specifying custom templates.
3426 specifying custom templates.
3426
3427
3427 Returns 0 on success.
3428 Returns 0 on success.
3428
3429
3429 """
3430 """
3430 opts = pycompat.byteskwargs(opts)
3431 opts = pycompat.byteskwargs(opts)
3431 if opts.get('follow') and opts.get('rev'):
3432 if opts.get('follow') and opts.get('rev'):
3432 opts['rev'] = [revsetlang.formatspec('reverse(::%lr)', opts.get('rev'))]
3433 opts['rev'] = [revsetlang.formatspec('reverse(::%lr)', opts.get('rev'))]
3433 del opts['follow']
3434 del opts['follow']
3434
3435
3435 if opts.get('graph'):
3436 if opts.get('graph'):
3436 return cmdutil.graphlog(ui, repo, pats, opts)
3437 return cmdutil.graphlog(ui, repo, pats, opts)
3437
3438
3438 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3439 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3439 limit = cmdutil.loglimit(opts)
3440 limit = cmdutil.loglimit(opts)
3440 count = 0
3441 count = 0
3441
3442
3442 getrenamed = None
3443 getrenamed = None
3443 if opts.get('copies'):
3444 if opts.get('copies'):
3444 endrev = None
3445 endrev = None
3445 if opts.get('rev'):
3446 if opts.get('rev'):
3446 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3447 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3447 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3448 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3448
3449
3449 ui.pager('log')
3450 ui.pager('log')
3450 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3451 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3451 for rev in revs:
3452 for rev in revs:
3452 if count == limit:
3453 if count == limit:
3453 break
3454 break
3454 ctx = repo[rev]
3455 ctx = repo[rev]
3455 copies = None
3456 copies = None
3456 if getrenamed is not None and rev:
3457 if getrenamed is not None and rev:
3457 copies = []
3458 copies = []
3458 for fn in ctx.files():
3459 for fn in ctx.files():
3459 rename = getrenamed(fn, rev)
3460 rename = getrenamed(fn, rev)
3460 if rename:
3461 if rename:
3461 copies.append((fn, rename[0]))
3462 copies.append((fn, rename[0]))
3462 if filematcher:
3463 if filematcher:
3463 revmatchfn = filematcher(ctx.rev())
3464 revmatchfn = filematcher(ctx.rev())
3464 else:
3465 else:
3465 revmatchfn = None
3466 revmatchfn = None
3466 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3467 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3467 if displayer.flush(ctx):
3468 if displayer.flush(ctx):
3468 count += 1
3469 count += 1
3469
3470
3470 displayer.close()
3471 displayer.close()
3471
3472
3472 @command('manifest',
3473 @command('manifest',
3473 [('r', 'rev', '', _('revision to display'), _('REV')),
3474 [('r', 'rev', '', _('revision to display'), _('REV')),
3474 ('', 'all', False, _("list files from all revisions"))]
3475 ('', 'all', False, _("list files from all revisions"))]
3475 + formatteropts,
3476 + formatteropts,
3476 _('[-r REV]'))
3477 _('[-r REV]'))
3477 def manifest(ui, repo, node=None, rev=None, **opts):
3478 def manifest(ui, repo, node=None, rev=None, **opts):
3478 """output the current or given revision of the project manifest
3479 """output the current or given revision of the project manifest
3479
3480
3480 Print a list of version controlled files for the given revision.
3481 Print a list of version controlled files for the given revision.
3481 If no revision is given, the first parent of the working directory
3482 If no revision is given, the first parent of the working directory
3482 is used, or the null revision if no revision is checked out.
3483 is used, or the null revision if no revision is checked out.
3483
3484
3484 With -v, print file permissions, symlink and executable bits.
3485 With -v, print file permissions, symlink and executable bits.
3485 With --debug, print file revision hashes.
3486 With --debug, print file revision hashes.
3486
3487
3487 If option --all is specified, the list of all files from all revisions
3488 If option --all is specified, the list of all files from all revisions
3488 is printed. This includes deleted and renamed files.
3489 is printed. This includes deleted and renamed files.
3489
3490
3490 Returns 0 on success.
3491 Returns 0 on success.
3491 """
3492 """
3492 opts = pycompat.byteskwargs(opts)
3493 opts = pycompat.byteskwargs(opts)
3493 fm = ui.formatter('manifest', opts)
3494 fm = ui.formatter('manifest', opts)
3494
3495
3495 if opts.get('all'):
3496 if opts.get('all'):
3496 if rev or node:
3497 if rev or node:
3497 raise error.Abort(_("can't specify a revision with --all"))
3498 raise error.Abort(_("can't specify a revision with --all"))
3498
3499
3499 res = []
3500 res = []
3500 prefix = "data/"
3501 prefix = "data/"
3501 suffix = ".i"
3502 suffix = ".i"
3502 plen = len(prefix)
3503 plen = len(prefix)
3503 slen = len(suffix)
3504 slen = len(suffix)
3504 with repo.lock():
3505 with repo.lock():
3505 for fn, b, size in repo.store.datafiles():
3506 for fn, b, size in repo.store.datafiles():
3506 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3507 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3507 res.append(fn[plen:-slen])
3508 res.append(fn[plen:-slen])
3508 ui.pager('manifest')
3509 ui.pager('manifest')
3509 for f in res:
3510 for f in res:
3510 fm.startitem()
3511 fm.startitem()
3511 fm.write("path", '%s\n', f)
3512 fm.write("path", '%s\n', f)
3512 fm.end()
3513 fm.end()
3513 return
3514 return
3514
3515
3515 if rev and node:
3516 if rev and node:
3516 raise error.Abort(_("please specify just one revision"))
3517 raise error.Abort(_("please specify just one revision"))
3517
3518
3518 if not node:
3519 if not node:
3519 node = rev
3520 node = rev
3520
3521
3521 char = {'l': '@', 'x': '*', '': ''}
3522 char = {'l': '@', 'x': '*', '': ''}
3522 mode = {'l': '644', 'x': '755', '': '644'}
3523 mode = {'l': '644', 'x': '755', '': '644'}
3523 ctx = scmutil.revsingle(repo, node)
3524 ctx = scmutil.revsingle(repo, node)
3524 mf = ctx.manifest()
3525 mf = ctx.manifest()
3525 ui.pager('manifest')
3526 ui.pager('manifest')
3526 for f in ctx:
3527 for f in ctx:
3527 fm.startitem()
3528 fm.startitem()
3528 fl = ctx[f].flags()
3529 fl = ctx[f].flags()
3529 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3530 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3530 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3531 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3531 fm.write('path', '%s\n', f)
3532 fm.write('path', '%s\n', f)
3532 fm.end()
3533 fm.end()
3533
3534
3534 @command('^merge',
3535 @command('^merge',
3535 [('f', 'force', None,
3536 [('f', 'force', None,
3536 _('force a merge including outstanding changes (DEPRECATED)')),
3537 _('force a merge including outstanding changes (DEPRECATED)')),
3537 ('r', 'rev', '', _('revision to merge'), _('REV')),
3538 ('r', 'rev', '', _('revision to merge'), _('REV')),
3538 ('P', 'preview', None,
3539 ('P', 'preview', None,
3539 _('review revisions to merge (no merge is performed)'))
3540 _('review revisions to merge (no merge is performed)'))
3540 ] + mergetoolopts,
3541 ] + mergetoolopts,
3541 _('[-P] [[-r] REV]'))
3542 _('[-P] [[-r] REV]'))
3542 def merge(ui, repo, node=None, **opts):
3543 def merge(ui, repo, node=None, **opts):
3543 """merge another revision into working directory
3544 """merge another revision into working directory
3544
3545
3545 The current working directory is updated with all changes made in
3546 The current working directory is updated with all changes made in
3546 the requested revision since the last common predecessor revision.
3547 the requested revision since the last common predecessor revision.
3547
3548
3548 Files that changed between either parent are marked as changed for
3549 Files that changed between either parent are marked as changed for
3549 the next commit and a commit must be performed before any further
3550 the next commit and a commit must be performed before any further
3550 updates to the repository are allowed. The next commit will have
3551 updates to the repository are allowed. The next commit will have
3551 two parents.
3552 two parents.
3552
3553
3553 ``--tool`` can be used to specify the merge tool used for file
3554 ``--tool`` can be used to specify the merge tool used for file
3554 merges. It overrides the HGMERGE environment variable and your
3555 merges. It overrides the HGMERGE environment variable and your
3555 configuration files. See :hg:`help merge-tools` for options.
3556 configuration files. See :hg:`help merge-tools` for options.
3556
3557
3557 If no revision is specified, the working directory's parent is a
3558 If no revision is specified, the working directory's parent is a
3558 head revision, and the current branch contains exactly one other
3559 head revision, and the current branch contains exactly one other
3559 head, the other head is merged with by default. Otherwise, an
3560 head, the other head is merged with by default. Otherwise, an
3560 explicit revision with which to merge with must be provided.
3561 explicit revision with which to merge with must be provided.
3561
3562
3562 See :hg:`help resolve` for information on handling file conflicts.
3563 See :hg:`help resolve` for information on handling file conflicts.
3563
3564
3564 To undo an uncommitted merge, use :hg:`update --clean .` which
3565 To undo an uncommitted merge, use :hg:`update --clean .` which
3565 will check out a clean copy of the original merge parent, losing
3566 will check out a clean copy of the original merge parent, losing
3566 all changes.
3567 all changes.
3567
3568
3568 Returns 0 on success, 1 if there are unresolved files.
3569 Returns 0 on success, 1 if there are unresolved files.
3569 """
3570 """
3570
3571
3571 opts = pycompat.byteskwargs(opts)
3572 opts = pycompat.byteskwargs(opts)
3572 if opts.get('rev') and node:
3573 if opts.get('rev') and node:
3573 raise error.Abort(_("please specify just one revision"))
3574 raise error.Abort(_("please specify just one revision"))
3574 if not node:
3575 if not node:
3575 node = opts.get('rev')
3576 node = opts.get('rev')
3576
3577
3577 if node:
3578 if node:
3578 node = scmutil.revsingle(repo, node).node()
3579 node = scmutil.revsingle(repo, node).node()
3579
3580
3580 if not node:
3581 if not node:
3581 node = repo[destutil.destmerge(repo)].node()
3582 node = repo[destutil.destmerge(repo)].node()
3582
3583
3583 if opts.get('preview'):
3584 if opts.get('preview'):
3584 # find nodes that are ancestors of p2 but not of p1
3585 # find nodes that are ancestors of p2 but not of p1
3585 p1 = repo.lookup('.')
3586 p1 = repo.lookup('.')
3586 p2 = repo.lookup(node)
3587 p2 = repo.lookup(node)
3587 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3588 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3588
3589
3589 displayer = cmdutil.show_changeset(ui, repo, opts)
3590 displayer = cmdutil.show_changeset(ui, repo, opts)
3590 for node in nodes:
3591 for node in nodes:
3591 displayer.show(repo[node])
3592 displayer.show(repo[node])
3592 displayer.close()
3593 displayer.close()
3593 return 0
3594 return 0
3594
3595
3595 try:
3596 try:
3596 # ui.forcemerge is an internal variable, do not document
3597 # ui.forcemerge is an internal variable, do not document
3597 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3598 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3598 force = opts.get('force')
3599 force = opts.get('force')
3599 labels = ['working copy', 'merge rev']
3600 labels = ['working copy', 'merge rev']
3600 return hg.merge(repo, node, force=force, mergeforce=force,
3601 return hg.merge(repo, node, force=force, mergeforce=force,
3601 labels=labels)
3602 labels=labels)
3602 finally:
3603 finally:
3603 ui.setconfig('ui', 'forcemerge', '', 'merge')
3604 ui.setconfig('ui', 'forcemerge', '', 'merge')
3604
3605
3605 @command('outgoing|out',
3606 @command('outgoing|out',
3606 [('f', 'force', None, _('run even when the destination is unrelated')),
3607 [('f', 'force', None, _('run even when the destination is unrelated')),
3607 ('r', 'rev', [],
3608 ('r', 'rev', [],
3608 _('a changeset intended to be included in the destination'), _('REV')),
3609 _('a changeset intended to be included in the destination'), _('REV')),
3609 ('n', 'newest-first', None, _('show newest record first')),
3610 ('n', 'newest-first', None, _('show newest record first')),
3610 ('B', 'bookmarks', False, _('compare bookmarks')),
3611 ('B', 'bookmarks', False, _('compare bookmarks')),
3611 ('b', 'branch', [], _('a specific branch you would like to push'),
3612 ('b', 'branch', [], _('a specific branch you would like to push'),
3612 _('BRANCH')),
3613 _('BRANCH')),
3613 ] + logopts + remoteopts + subrepoopts,
3614 ] + logopts + remoteopts + subrepoopts,
3614 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3615 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3615 def outgoing(ui, repo, dest=None, **opts):
3616 def outgoing(ui, repo, dest=None, **opts):
3616 """show changesets not found in the destination
3617 """show changesets not found in the destination
3617
3618
3618 Show changesets not found in the specified destination repository
3619 Show changesets not found in the specified destination repository
3619 or the default push location. These are the changesets that would
3620 or the default push location. These are the changesets that would
3620 be pushed if a push was requested.
3621 be pushed if a push was requested.
3621
3622
3622 See pull for details of valid destination formats.
3623 See pull for details of valid destination formats.
3623
3624
3624 .. container:: verbose
3625 .. container:: verbose
3625
3626
3626 With -B/--bookmarks, the result of bookmark comparison between
3627 With -B/--bookmarks, the result of bookmark comparison between
3627 local and remote repositories is displayed. With -v/--verbose,
3628 local and remote repositories is displayed. With -v/--verbose,
3628 status is also displayed for each bookmark like below::
3629 status is also displayed for each bookmark like below::
3629
3630
3630 BM1 01234567890a added
3631 BM1 01234567890a added
3631 BM2 deleted
3632 BM2 deleted
3632 BM3 234567890abc advanced
3633 BM3 234567890abc advanced
3633 BM4 34567890abcd diverged
3634 BM4 34567890abcd diverged
3634 BM5 4567890abcde changed
3635 BM5 4567890abcde changed
3635
3636
3636 The action taken when pushing depends on the
3637 The action taken when pushing depends on the
3637 status of each bookmark:
3638 status of each bookmark:
3638
3639
3639 :``added``: push with ``-B`` will create it
3640 :``added``: push with ``-B`` will create it
3640 :``deleted``: push with ``-B`` will delete it
3641 :``deleted``: push with ``-B`` will delete it
3641 :``advanced``: push will update it
3642 :``advanced``: push will update it
3642 :``diverged``: push with ``-B`` will update it
3643 :``diverged``: push with ``-B`` will update it
3643 :``changed``: push with ``-B`` will update it
3644 :``changed``: push with ``-B`` will update it
3644
3645
3645 From the point of view of pushing behavior, bookmarks
3646 From the point of view of pushing behavior, bookmarks
3646 existing only in the remote repository are treated as
3647 existing only in the remote repository are treated as
3647 ``deleted``, even if it is in fact added remotely.
3648 ``deleted``, even if it is in fact added remotely.
3648
3649
3649 Returns 0 if there are outgoing changes, 1 otherwise.
3650 Returns 0 if there are outgoing changes, 1 otherwise.
3650 """
3651 """
3651 opts = pycompat.byteskwargs(opts)
3652 opts = pycompat.byteskwargs(opts)
3652 if opts.get('graph'):
3653 if opts.get('graph'):
3653 cmdutil.checkunsupportedgraphflags([], opts)
3654 cmdutil.checkunsupportedgraphflags([], opts)
3654 o, other = hg._outgoing(ui, repo, dest, opts)
3655 o, other = hg._outgoing(ui, repo, dest, opts)
3655 if not o:
3656 if not o:
3656 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3657 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3657 return
3658 return
3658
3659
3659 revdag = cmdutil.graphrevs(repo, o, opts)
3660 revdag = cmdutil.graphrevs(repo, o, opts)
3660 ui.pager('outgoing')
3661 ui.pager('outgoing')
3661 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3662 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3662 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3663 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3663 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3664 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3664 return 0
3665 return 0
3665
3666
3666 if opts.get('bookmarks'):
3667 if opts.get('bookmarks'):
3667 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3668 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3668 dest, branches = hg.parseurl(dest, opts.get('branch'))
3669 dest, branches = hg.parseurl(dest, opts.get('branch'))
3669 other = hg.peer(repo, opts, dest)
3670 other = hg.peer(repo, opts, dest)
3670 if 'bookmarks' not in other.listkeys('namespaces'):
3671 if 'bookmarks' not in other.listkeys('namespaces'):
3671 ui.warn(_("remote doesn't support bookmarks\n"))
3672 ui.warn(_("remote doesn't support bookmarks\n"))
3672 return 0
3673 return 0
3673 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3674 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3674 ui.pager('outgoing')
3675 ui.pager('outgoing')
3675 return bookmarks.outgoing(ui, repo, other)
3676 return bookmarks.outgoing(ui, repo, other)
3676
3677
3677 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3678 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3678 try:
3679 try:
3679 return hg.outgoing(ui, repo, dest, opts)
3680 return hg.outgoing(ui, repo, dest, opts)
3680 finally:
3681 finally:
3681 del repo._subtoppath
3682 del repo._subtoppath
3682
3683
3683 @command('parents',
3684 @command('parents',
3684 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3685 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3685 ] + templateopts,
3686 ] + templateopts,
3686 _('[-r REV] [FILE]'),
3687 _('[-r REV] [FILE]'),
3687 inferrepo=True)
3688 inferrepo=True)
3688 def parents(ui, repo, file_=None, **opts):
3689 def parents(ui, repo, file_=None, **opts):
3689 """show the parents of the working directory or revision (DEPRECATED)
3690 """show the parents of the working directory or revision (DEPRECATED)
3690
3691
3691 Print the working directory's parent revisions. If a revision is
3692 Print the working directory's parent revisions. If a revision is
3692 given via -r/--rev, the parent of that revision will be printed.
3693 given via -r/--rev, the parent of that revision will be printed.
3693 If a file argument is given, the revision in which the file was
3694 If a file argument is given, the revision in which the file was
3694 last changed (before the working directory revision or the
3695 last changed (before the working directory revision or the
3695 argument to --rev if given) is printed.
3696 argument to --rev if given) is printed.
3696
3697
3697 This command is equivalent to::
3698 This command is equivalent to::
3698
3699
3699 hg log -r "p1()+p2()" or
3700 hg log -r "p1()+p2()" or
3700 hg log -r "p1(REV)+p2(REV)" or
3701 hg log -r "p1(REV)+p2(REV)" or
3701 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3702 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3702 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3703 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3703
3704
3704 See :hg:`summary` and :hg:`help revsets` for related information.
3705 See :hg:`summary` and :hg:`help revsets` for related information.
3705
3706
3706 Returns 0 on success.
3707 Returns 0 on success.
3707 """
3708 """
3708
3709
3709 opts = pycompat.byteskwargs(opts)
3710 opts = pycompat.byteskwargs(opts)
3710 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3711 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3711
3712
3712 if file_:
3713 if file_:
3713 m = scmutil.match(ctx, (file_,), opts)
3714 m = scmutil.match(ctx, (file_,), opts)
3714 if m.anypats() or len(m.files()) != 1:
3715 if m.anypats() or len(m.files()) != 1:
3715 raise error.Abort(_('can only specify an explicit filename'))
3716 raise error.Abort(_('can only specify an explicit filename'))
3716 file_ = m.files()[0]
3717 file_ = m.files()[0]
3717 filenodes = []
3718 filenodes = []
3718 for cp in ctx.parents():
3719 for cp in ctx.parents():
3719 if not cp:
3720 if not cp:
3720 continue
3721 continue
3721 try:
3722 try:
3722 filenodes.append(cp.filenode(file_))
3723 filenodes.append(cp.filenode(file_))
3723 except error.LookupError:
3724 except error.LookupError:
3724 pass
3725 pass
3725 if not filenodes:
3726 if not filenodes:
3726 raise error.Abort(_("'%s' not found in manifest!") % file_)
3727 raise error.Abort(_("'%s' not found in manifest!") % file_)
3727 p = []
3728 p = []
3728 for fn in filenodes:
3729 for fn in filenodes:
3729 fctx = repo.filectx(file_, fileid=fn)
3730 fctx = repo.filectx(file_, fileid=fn)
3730 p.append(fctx.node())
3731 p.append(fctx.node())
3731 else:
3732 else:
3732 p = [cp.node() for cp in ctx.parents()]
3733 p = [cp.node() for cp in ctx.parents()]
3733
3734
3734 displayer = cmdutil.show_changeset(ui, repo, opts)
3735 displayer = cmdutil.show_changeset(ui, repo, opts)
3735 for n in p:
3736 for n in p:
3736 if n != nullid:
3737 if n != nullid:
3737 displayer.show(repo[n])
3738 displayer.show(repo[n])
3738 displayer.close()
3739 displayer.close()
3739
3740
3740 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
3741 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
3741 def paths(ui, repo, search=None, **opts):
3742 def paths(ui, repo, search=None, **opts):
3742 """show aliases for remote repositories
3743 """show aliases for remote repositories
3743
3744
3744 Show definition of symbolic path name NAME. If no name is given,
3745 Show definition of symbolic path name NAME. If no name is given,
3745 show definition of all available names.
3746 show definition of all available names.
3746
3747
3747 Option -q/--quiet suppresses all output when searching for NAME
3748 Option -q/--quiet suppresses all output when searching for NAME
3748 and shows only the path names when listing all definitions.
3749 and shows only the path names when listing all definitions.
3749
3750
3750 Path names are defined in the [paths] section of your
3751 Path names are defined in the [paths] section of your
3751 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3752 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3752 repository, ``.hg/hgrc`` is used, too.
3753 repository, ``.hg/hgrc`` is used, too.
3753
3754
3754 The path names ``default`` and ``default-push`` have a special
3755 The path names ``default`` and ``default-push`` have a special
3755 meaning. When performing a push or pull operation, they are used
3756 meaning. When performing a push or pull operation, they are used
3756 as fallbacks if no location is specified on the command-line.
3757 as fallbacks if no location is specified on the command-line.
3757 When ``default-push`` is set, it will be used for push and
3758 When ``default-push`` is set, it will be used for push and
3758 ``default`` will be used for pull; otherwise ``default`` is used
3759 ``default`` will be used for pull; otherwise ``default`` is used
3759 as the fallback for both. When cloning a repository, the clone
3760 as the fallback for both. When cloning a repository, the clone
3760 source is written as ``default`` in ``.hg/hgrc``.
3761 source is written as ``default`` in ``.hg/hgrc``.
3761
3762
3762 .. note::
3763 .. note::
3763
3764
3764 ``default`` and ``default-push`` apply to all inbound (e.g.
3765 ``default`` and ``default-push`` apply to all inbound (e.g.
3765 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3766 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3766 and :hg:`bundle`) operations.
3767 and :hg:`bundle`) operations.
3767
3768
3768 See :hg:`help urls` for more information.
3769 See :hg:`help urls` for more information.
3769
3770
3770 Returns 0 on success.
3771 Returns 0 on success.
3771 """
3772 """
3772
3773
3773 opts = pycompat.byteskwargs(opts)
3774 opts = pycompat.byteskwargs(opts)
3774 ui.pager('paths')
3775 ui.pager('paths')
3775 if search:
3776 if search:
3776 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3777 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3777 if name == search]
3778 if name == search]
3778 else:
3779 else:
3779 pathitems = sorted(ui.paths.iteritems())
3780 pathitems = sorted(ui.paths.iteritems())
3780
3781
3781 fm = ui.formatter('paths', opts)
3782 fm = ui.formatter('paths', opts)
3782 if fm.isplain():
3783 if fm.isplain():
3783 hidepassword = util.hidepassword
3784 hidepassword = util.hidepassword
3784 else:
3785 else:
3785 hidepassword = str
3786 hidepassword = str
3786 if ui.quiet:
3787 if ui.quiet:
3787 namefmt = '%s\n'
3788 namefmt = '%s\n'
3788 else:
3789 else:
3789 namefmt = '%s = '
3790 namefmt = '%s = '
3790 showsubopts = not search and not ui.quiet
3791 showsubopts = not search and not ui.quiet
3791
3792
3792 for name, path in pathitems:
3793 for name, path in pathitems:
3793 fm.startitem()
3794 fm.startitem()
3794 fm.condwrite(not search, 'name', namefmt, name)
3795 fm.condwrite(not search, 'name', namefmt, name)
3795 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3796 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3796 for subopt, value in sorted(path.suboptions.items()):
3797 for subopt, value in sorted(path.suboptions.items()):
3797 assert subopt not in ('name', 'url')
3798 assert subopt not in ('name', 'url')
3798 if showsubopts:
3799 if showsubopts:
3799 fm.plain('%s:%s = ' % (name, subopt))
3800 fm.plain('%s:%s = ' % (name, subopt))
3800 fm.condwrite(showsubopts, subopt, '%s\n', value)
3801 fm.condwrite(showsubopts, subopt, '%s\n', value)
3801
3802
3802 fm.end()
3803 fm.end()
3803
3804
3804 if search and not pathitems:
3805 if search and not pathitems:
3805 if not ui.quiet:
3806 if not ui.quiet:
3806 ui.warn(_("not found!\n"))
3807 ui.warn(_("not found!\n"))
3807 return 1
3808 return 1
3808 else:
3809 else:
3809 return 0
3810 return 0
3810
3811
3811 @command('phase',
3812 @command('phase',
3812 [('p', 'public', False, _('set changeset phase to public')),
3813 [('p', 'public', False, _('set changeset phase to public')),
3813 ('d', 'draft', False, _('set changeset phase to draft')),
3814 ('d', 'draft', False, _('set changeset phase to draft')),
3814 ('s', 'secret', False, _('set changeset phase to secret')),
3815 ('s', 'secret', False, _('set changeset phase to secret')),
3815 ('f', 'force', False, _('allow to move boundary backward')),
3816 ('f', 'force', False, _('allow to move boundary backward')),
3816 ('r', 'rev', [], _('target revision'), _('REV')),
3817 ('r', 'rev', [], _('target revision'), _('REV')),
3817 ],
3818 ],
3818 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3819 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3819 def phase(ui, repo, *revs, **opts):
3820 def phase(ui, repo, *revs, **opts):
3820 """set or show the current phase name
3821 """set or show the current phase name
3821
3822
3822 With no argument, show the phase name of the current revision(s).
3823 With no argument, show the phase name of the current revision(s).
3823
3824
3824 With one of -p/--public, -d/--draft or -s/--secret, change the
3825 With one of -p/--public, -d/--draft or -s/--secret, change the
3825 phase value of the specified revisions.
3826 phase value of the specified revisions.
3826
3827
3827 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
3828 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
3828 lower phase to an higher phase. Phases are ordered as follows::
3829 lower phase to an higher phase. Phases are ordered as follows::
3829
3830
3830 public < draft < secret
3831 public < draft < secret
3831
3832
3832 Returns 0 on success, 1 if some phases could not be changed.
3833 Returns 0 on success, 1 if some phases could not be changed.
3833
3834
3834 (For more information about the phases concept, see :hg:`help phases`.)
3835 (For more information about the phases concept, see :hg:`help phases`.)
3835 """
3836 """
3836 opts = pycompat.byteskwargs(opts)
3837 opts = pycompat.byteskwargs(opts)
3837 # search for a unique phase argument
3838 # search for a unique phase argument
3838 targetphase = None
3839 targetphase = None
3839 for idx, name in enumerate(phases.phasenames):
3840 for idx, name in enumerate(phases.phasenames):
3840 if opts[name]:
3841 if opts[name]:
3841 if targetphase is not None:
3842 if targetphase is not None:
3842 raise error.Abort(_('only one phase can be specified'))
3843 raise error.Abort(_('only one phase can be specified'))
3843 targetphase = idx
3844 targetphase = idx
3844
3845
3845 # look for specified revision
3846 # look for specified revision
3846 revs = list(revs)
3847 revs = list(revs)
3847 revs.extend(opts['rev'])
3848 revs.extend(opts['rev'])
3848 if not revs:
3849 if not revs:
3849 # display both parents as the second parent phase can influence
3850 # display both parents as the second parent phase can influence
3850 # the phase of a merge commit
3851 # the phase of a merge commit
3851 revs = [c.rev() for c in repo[None].parents()]
3852 revs = [c.rev() for c in repo[None].parents()]
3852
3853
3853 revs = scmutil.revrange(repo, revs)
3854 revs = scmutil.revrange(repo, revs)
3854
3855
3855 lock = None
3856 lock = None
3856 ret = 0
3857 ret = 0
3857 if targetphase is None:
3858 if targetphase is None:
3858 # display
3859 # display
3859 for r in revs:
3860 for r in revs:
3860 ctx = repo[r]
3861 ctx = repo[r]
3861 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3862 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3862 else:
3863 else:
3863 tr = None
3864 tr = None
3864 lock = repo.lock()
3865 lock = repo.lock()
3865 try:
3866 try:
3866 tr = repo.transaction("phase")
3867 tr = repo.transaction("phase")
3867 # set phase
3868 # set phase
3868 if not revs:
3869 if not revs:
3869 raise error.Abort(_('empty revision set'))
3870 raise error.Abort(_('empty revision set'))
3870 nodes = [repo[r].node() for r in revs]
3871 nodes = [repo[r].node() for r in revs]
3871 # moving revision from public to draft may hide them
3872 # moving revision from public to draft may hide them
3872 # We have to check result on an unfiltered repository
3873 # We have to check result on an unfiltered repository
3873 unfi = repo.unfiltered()
3874 unfi = repo.unfiltered()
3874 getphase = unfi._phasecache.phase
3875 getphase = unfi._phasecache.phase
3875 olddata = [getphase(unfi, r) for r in unfi]
3876 olddata = [getphase(unfi, r) for r in unfi]
3876 phases.advanceboundary(repo, tr, targetphase, nodes)
3877 phases.advanceboundary(repo, tr, targetphase, nodes)
3877 if opts['force']:
3878 if opts['force']:
3878 phases.retractboundary(repo, tr, targetphase, nodes)
3879 phases.retractboundary(repo, tr, targetphase, nodes)
3879 tr.close()
3880 tr.close()
3880 finally:
3881 finally:
3881 if tr is not None:
3882 if tr is not None:
3882 tr.release()
3883 tr.release()
3883 lock.release()
3884 lock.release()
3884 getphase = unfi._phasecache.phase
3885 getphase = unfi._phasecache.phase
3885 newdata = [getphase(unfi, r) for r in unfi]
3886 newdata = [getphase(unfi, r) for r in unfi]
3886 changes = sum(newdata[r] != olddata[r] for r in unfi)
3887 changes = sum(newdata[r] != olddata[r] for r in unfi)
3887 cl = unfi.changelog
3888 cl = unfi.changelog
3888 rejected = [n for n in nodes
3889 rejected = [n for n in nodes
3889 if newdata[cl.rev(n)] < targetphase]
3890 if newdata[cl.rev(n)] < targetphase]
3890 if rejected:
3891 if rejected:
3891 ui.warn(_('cannot move %i changesets to a higher '
3892 ui.warn(_('cannot move %i changesets to a higher '
3892 'phase, use --force\n') % len(rejected))
3893 'phase, use --force\n') % len(rejected))
3893 ret = 1
3894 ret = 1
3894 if changes:
3895 if changes:
3895 msg = _('phase changed for %i changesets\n') % changes
3896 msg = _('phase changed for %i changesets\n') % changes
3896 if ret:
3897 if ret:
3897 ui.status(msg)
3898 ui.status(msg)
3898 else:
3899 else:
3899 ui.note(msg)
3900 ui.note(msg)
3900 else:
3901 else:
3901 ui.warn(_('no phases changed\n'))
3902 ui.warn(_('no phases changed\n'))
3902 return ret
3903 return ret
3903
3904
3904 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3905 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3905 """Run after a changegroup has been added via pull/unbundle
3906 """Run after a changegroup has been added via pull/unbundle
3906
3907
3907 This takes arguments below:
3908 This takes arguments below:
3908
3909
3909 :modheads: change of heads by pull/unbundle
3910 :modheads: change of heads by pull/unbundle
3910 :optupdate: updating working directory is needed or not
3911 :optupdate: updating working directory is needed or not
3911 :checkout: update destination revision (or None to default destination)
3912 :checkout: update destination revision (or None to default destination)
3912 :brev: a name, which might be a bookmark to be activated after updating
3913 :brev: a name, which might be a bookmark to be activated after updating
3913 """
3914 """
3914 if modheads == 0:
3915 if modheads == 0:
3915 return
3916 return
3916 if optupdate:
3917 if optupdate:
3917 try:
3918 try:
3918 return hg.updatetotally(ui, repo, checkout, brev)
3919 return hg.updatetotally(ui, repo, checkout, brev)
3919 except error.UpdateAbort as inst:
3920 except error.UpdateAbort as inst:
3920 msg = _("not updating: %s") % str(inst)
3921 msg = _("not updating: %s") % str(inst)
3921 hint = inst.hint
3922 hint = inst.hint
3922 raise error.UpdateAbort(msg, hint=hint)
3923 raise error.UpdateAbort(msg, hint=hint)
3923 if modheads > 1:
3924 if modheads > 1:
3924 currentbranchheads = len(repo.branchheads())
3925 currentbranchheads = len(repo.branchheads())
3925 if currentbranchheads == modheads:
3926 if currentbranchheads == modheads:
3926 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3927 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3927 elif currentbranchheads > 1:
3928 elif currentbranchheads > 1:
3928 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3929 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3929 "merge)\n"))
3930 "merge)\n"))
3930 else:
3931 else:
3931 ui.status(_("(run 'hg heads' to see heads)\n"))
3932 ui.status(_("(run 'hg heads' to see heads)\n"))
3932 else:
3933 else:
3933 ui.status(_("(run 'hg update' to get a working copy)\n"))
3934 ui.status(_("(run 'hg update' to get a working copy)\n"))
3934
3935
3935 @command('^pull',
3936 @command('^pull',
3936 [('u', 'update', None,
3937 [('u', 'update', None,
3937 _('update to new branch head if changesets were pulled')),
3938 _('update to new branch head if changesets were pulled')),
3938 ('f', 'force', None, _('run even when remote repository is unrelated')),
3939 ('f', 'force', None, _('run even when remote repository is unrelated')),
3939 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3940 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3940 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3941 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3941 ('b', 'branch', [], _('a specific branch you would like to pull'),
3942 ('b', 'branch', [], _('a specific branch you would like to pull'),
3942 _('BRANCH')),
3943 _('BRANCH')),
3943 ] + remoteopts,
3944 ] + remoteopts,
3944 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3945 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3945 def pull(ui, repo, source="default", **opts):
3946 def pull(ui, repo, source="default", **opts):
3946 """pull changes from the specified source
3947 """pull changes from the specified source
3947
3948
3948 Pull changes from a remote repository to a local one.
3949 Pull changes from a remote repository to a local one.
3949
3950
3950 This finds all changes from the repository at the specified path
3951 This finds all changes from the repository at the specified path
3951 or URL and adds them to a local repository (the current one unless
3952 or URL and adds them to a local repository (the current one unless
3952 -R is specified). By default, this does not update the copy of the
3953 -R is specified). By default, this does not update the copy of the
3953 project in the working directory.
3954 project in the working directory.
3954
3955
3955 Use :hg:`incoming` if you want to see what would have been added
3956 Use :hg:`incoming` if you want to see what would have been added
3956 by a pull at the time you issued this command. If you then decide
3957 by a pull at the time you issued this command. If you then decide
3957 to add those changes to the repository, you should use :hg:`pull
3958 to add those changes to the repository, you should use :hg:`pull
3958 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3959 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3959
3960
3960 If SOURCE is omitted, the 'default' path will be used.
3961 If SOURCE is omitted, the 'default' path will be used.
3961 See :hg:`help urls` for more information.
3962 See :hg:`help urls` for more information.
3962
3963
3963 Specifying bookmark as ``.`` is equivalent to specifying the active
3964 Specifying bookmark as ``.`` is equivalent to specifying the active
3964 bookmark's name.
3965 bookmark's name.
3965
3966
3966 Returns 0 on success, 1 if an update had unresolved files.
3967 Returns 0 on success, 1 if an update had unresolved files.
3967 """
3968 """
3968
3969
3969 opts = pycompat.byteskwargs(opts)
3970 opts = pycompat.byteskwargs(opts)
3970 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3971 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3971 msg = _('update destination required by configuration')
3972 msg = _('update destination required by configuration')
3972 hint = _('use hg pull followed by hg update DEST')
3973 hint = _('use hg pull followed by hg update DEST')
3973 raise error.Abort(msg, hint=hint)
3974 raise error.Abort(msg, hint=hint)
3974
3975
3975 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3976 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3976 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3977 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3977 other = hg.peer(repo, opts, source)
3978 other = hg.peer(repo, opts, source)
3978 try:
3979 try:
3979 revs, checkout = hg.addbranchrevs(repo, other, branches,
3980 revs, checkout = hg.addbranchrevs(repo, other, branches,
3980 opts.get('rev'))
3981 opts.get('rev'))
3981
3982
3982
3983
3983 pullopargs = {}
3984 pullopargs = {}
3984 if opts.get('bookmark'):
3985 if opts.get('bookmark'):
3985 if not revs:
3986 if not revs:
3986 revs = []
3987 revs = []
3987 # The list of bookmark used here is not the one used to actually
3988 # The list of bookmark used here is not the one used to actually
3988 # update the bookmark name. This can result in the revision pulled
3989 # update the bookmark name. This can result in the revision pulled
3989 # not ending up with the name of the bookmark because of a race
3990 # not ending up with the name of the bookmark because of a race
3990 # condition on the server. (See issue 4689 for details)
3991 # condition on the server. (See issue 4689 for details)
3991 remotebookmarks = other.listkeys('bookmarks')
3992 remotebookmarks = other.listkeys('bookmarks')
3992 pullopargs['remotebookmarks'] = remotebookmarks
3993 pullopargs['remotebookmarks'] = remotebookmarks
3993 for b in opts['bookmark']:
3994 for b in opts['bookmark']:
3994 b = repo._bookmarks.expandname(b)
3995 b = repo._bookmarks.expandname(b)
3995 if b not in remotebookmarks:
3996 if b not in remotebookmarks:
3996 raise error.Abort(_('remote bookmark %s not found!') % b)
3997 raise error.Abort(_('remote bookmark %s not found!') % b)
3997 revs.append(remotebookmarks[b])
3998 revs.append(remotebookmarks[b])
3998
3999
3999 if revs:
4000 if revs:
4000 try:
4001 try:
4001 # When 'rev' is a bookmark name, we cannot guarantee that it
4002 # When 'rev' is a bookmark name, we cannot guarantee that it
4002 # will be updated with that name because of a race condition
4003 # will be updated with that name because of a race condition
4003 # server side. (See issue 4689 for details)
4004 # server side. (See issue 4689 for details)
4004 oldrevs = revs
4005 oldrevs = revs
4005 revs = [] # actually, nodes
4006 revs = [] # actually, nodes
4006 for r in oldrevs:
4007 for r in oldrevs:
4007 node = other.lookup(r)
4008 node = other.lookup(r)
4008 revs.append(node)
4009 revs.append(node)
4009 if r == checkout:
4010 if r == checkout:
4010 checkout = node
4011 checkout = node
4011 except error.CapabilityError:
4012 except error.CapabilityError:
4012 err = _("other repository doesn't support revision lookup, "
4013 err = _("other repository doesn't support revision lookup, "
4013 "so a rev cannot be specified.")
4014 "so a rev cannot be specified.")
4014 raise error.Abort(err)
4015 raise error.Abort(err)
4015
4016
4016 pullopargs.update(opts.get('opargs', {}))
4017 pullopargs.update(opts.get('opargs', {}))
4017 modheads = exchange.pull(repo, other, heads=revs,
4018 modheads = exchange.pull(repo, other, heads=revs,
4018 force=opts.get('force'),
4019 force=opts.get('force'),
4019 bookmarks=opts.get('bookmark', ()),
4020 bookmarks=opts.get('bookmark', ()),
4020 opargs=pullopargs).cgresult
4021 opargs=pullopargs).cgresult
4021
4022
4022 # brev is a name, which might be a bookmark to be activated at
4023 # brev is a name, which might be a bookmark to be activated at
4023 # the end of the update. In other words, it is an explicit
4024 # the end of the update. In other words, it is an explicit
4024 # destination of the update
4025 # destination of the update
4025 brev = None
4026 brev = None
4026
4027
4027 if checkout:
4028 if checkout:
4028 checkout = str(repo.changelog.rev(checkout))
4029 checkout = str(repo.changelog.rev(checkout))
4029
4030
4030 # order below depends on implementation of
4031 # order below depends on implementation of
4031 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4032 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4032 # because 'checkout' is determined without it.
4033 # because 'checkout' is determined without it.
4033 if opts.get('rev'):
4034 if opts.get('rev'):
4034 brev = opts['rev'][0]
4035 brev = opts['rev'][0]
4035 elif opts.get('branch'):
4036 elif opts.get('branch'):
4036 brev = opts['branch'][0]
4037 brev = opts['branch'][0]
4037 else:
4038 else:
4038 brev = branches[0]
4039 brev = branches[0]
4039 repo._subtoppath = source
4040 repo._subtoppath = source
4040 try:
4041 try:
4041 ret = postincoming(ui, repo, modheads, opts.get('update'),
4042 ret = postincoming(ui, repo, modheads, opts.get('update'),
4042 checkout, brev)
4043 checkout, brev)
4043
4044
4044 finally:
4045 finally:
4045 del repo._subtoppath
4046 del repo._subtoppath
4046
4047
4047 finally:
4048 finally:
4048 other.close()
4049 other.close()
4049 return ret
4050 return ret
4050
4051
4051 @command('^push',
4052 @command('^push',
4052 [('f', 'force', None, _('force push')),
4053 [('f', 'force', None, _('force push')),
4053 ('r', 'rev', [],
4054 ('r', 'rev', [],
4054 _('a changeset intended to be included in the destination'),
4055 _('a changeset intended to be included in the destination'),
4055 _('REV')),
4056 _('REV')),
4056 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4057 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4057 ('b', 'branch', [],
4058 ('b', 'branch', [],
4058 _('a specific branch you would like to push'), _('BRANCH')),
4059 _('a specific branch you would like to push'), _('BRANCH')),
4059 ('', 'new-branch', False, _('allow pushing a new branch')),
4060 ('', 'new-branch', False, _('allow pushing a new branch')),
4060 ] + remoteopts,
4061 ] + remoteopts,
4061 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4062 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4062 def push(ui, repo, dest=None, **opts):
4063 def push(ui, repo, dest=None, **opts):
4063 """push changes to the specified destination
4064 """push changes to the specified destination
4064
4065
4065 Push changesets from the local repository to the specified
4066 Push changesets from the local repository to the specified
4066 destination.
4067 destination.
4067
4068
4068 This operation is symmetrical to pull: it is identical to a pull
4069 This operation is symmetrical to pull: it is identical to a pull
4069 in the destination repository from the current one.
4070 in the destination repository from the current one.
4070
4071
4071 By default, push will not allow creation of new heads at the
4072 By default, push will not allow creation of new heads at the
4072 destination, since multiple heads would make it unclear which head
4073 destination, since multiple heads would make it unclear which head
4073 to use. In this situation, it is recommended to pull and merge
4074 to use. In this situation, it is recommended to pull and merge
4074 before pushing.
4075 before pushing.
4075
4076
4076 Use --new-branch if you want to allow push to create a new named
4077 Use --new-branch if you want to allow push to create a new named
4077 branch that is not present at the destination. This allows you to
4078 branch that is not present at the destination. This allows you to
4078 only create a new branch without forcing other changes.
4079 only create a new branch without forcing other changes.
4079
4080
4080 .. note::
4081 .. note::
4081
4082
4082 Extra care should be taken with the -f/--force option,
4083 Extra care should be taken with the -f/--force option,
4083 which will push all new heads on all branches, an action which will
4084 which will push all new heads on all branches, an action which will
4084 almost always cause confusion for collaborators.
4085 almost always cause confusion for collaborators.
4085
4086
4086 If -r/--rev is used, the specified revision and all its ancestors
4087 If -r/--rev is used, the specified revision and all its ancestors
4087 will be pushed to the remote repository.
4088 will be pushed to the remote repository.
4088
4089
4089 If -B/--bookmark is used, the specified bookmarked revision, its
4090 If -B/--bookmark is used, the specified bookmarked revision, its
4090 ancestors, and the bookmark will be pushed to the remote
4091 ancestors, and the bookmark will be pushed to the remote
4091 repository. Specifying ``.`` is equivalent to specifying the active
4092 repository. Specifying ``.`` is equivalent to specifying the active
4092 bookmark's name.
4093 bookmark's name.
4093
4094
4094 Please see :hg:`help urls` for important details about ``ssh://``
4095 Please see :hg:`help urls` for important details about ``ssh://``
4095 URLs. If DESTINATION is omitted, a default path will be used.
4096 URLs. If DESTINATION is omitted, a default path will be used.
4096
4097
4097 Returns 0 if push was successful, 1 if nothing to push.
4098 Returns 0 if push was successful, 1 if nothing to push.
4098 """
4099 """
4099
4100
4100 opts = pycompat.byteskwargs(opts)
4101 opts = pycompat.byteskwargs(opts)
4101 if opts.get('bookmark'):
4102 if opts.get('bookmark'):
4102 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4103 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4103 for b in opts['bookmark']:
4104 for b in opts['bookmark']:
4104 # translate -B options to -r so changesets get pushed
4105 # translate -B options to -r so changesets get pushed
4105 b = repo._bookmarks.expandname(b)
4106 b = repo._bookmarks.expandname(b)
4106 if b in repo._bookmarks:
4107 if b in repo._bookmarks:
4107 opts.setdefault('rev', []).append(b)
4108 opts.setdefault('rev', []).append(b)
4108 else:
4109 else:
4109 # if we try to push a deleted bookmark, translate it to null
4110 # if we try to push a deleted bookmark, translate it to null
4110 # this lets simultaneous -r, -b options continue working
4111 # this lets simultaneous -r, -b options continue working
4111 opts.setdefault('rev', []).append("null")
4112 opts.setdefault('rev', []).append("null")
4112
4113
4113 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4114 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4114 if not path:
4115 if not path:
4115 raise error.Abort(_('default repository not configured!'),
4116 raise error.Abort(_('default repository not configured!'),
4116 hint=_("see 'hg help config.paths'"))
4117 hint=_("see 'hg help config.paths'"))
4117 dest = path.pushloc or path.loc
4118 dest = path.pushloc or path.loc
4118 branches = (path.branch, opts.get('branch') or [])
4119 branches = (path.branch, opts.get('branch') or [])
4119 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4120 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4120 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4121 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4121 other = hg.peer(repo, opts, dest)
4122 other = hg.peer(repo, opts, dest)
4122
4123
4123 if revs:
4124 if revs:
4124 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4125 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4125 if not revs:
4126 if not revs:
4126 raise error.Abort(_("specified revisions evaluate to an empty set"),
4127 raise error.Abort(_("specified revisions evaluate to an empty set"),
4127 hint=_("use different revision arguments"))
4128 hint=_("use different revision arguments"))
4128 elif path.pushrev:
4129 elif path.pushrev:
4129 # It doesn't make any sense to specify ancestor revisions. So limit
4130 # It doesn't make any sense to specify ancestor revisions. So limit
4130 # to DAG heads to make discovery simpler.
4131 # to DAG heads to make discovery simpler.
4131 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4132 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4132 revs = scmutil.revrange(repo, [expr])
4133 revs = scmutil.revrange(repo, [expr])
4133 revs = [repo[rev].node() for rev in revs]
4134 revs = [repo[rev].node() for rev in revs]
4134 if not revs:
4135 if not revs:
4135 raise error.Abort(_('default push revset for path evaluates to an '
4136 raise error.Abort(_('default push revset for path evaluates to an '
4136 'empty set'))
4137 'empty set'))
4137
4138
4138 repo._subtoppath = dest
4139 repo._subtoppath = dest
4139 try:
4140 try:
4140 # push subrepos depth-first for coherent ordering
4141 # push subrepos depth-first for coherent ordering
4141 c = repo['']
4142 c = repo['']
4142 subs = c.substate # only repos that are committed
4143 subs = c.substate # only repos that are committed
4143 for s in sorted(subs):
4144 for s in sorted(subs):
4144 result = c.sub(s).push(opts)
4145 result = c.sub(s).push(opts)
4145 if result == 0:
4146 if result == 0:
4146 return not result
4147 return not result
4147 finally:
4148 finally:
4148 del repo._subtoppath
4149 del repo._subtoppath
4149 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4150 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4150 newbranch=opts.get('new_branch'),
4151 newbranch=opts.get('new_branch'),
4151 bookmarks=opts.get('bookmark', ()),
4152 bookmarks=opts.get('bookmark', ()),
4152 opargs=opts.get('opargs'))
4153 opargs=opts.get('opargs'))
4153
4154
4154 result = not pushop.cgresult
4155 result = not pushop.cgresult
4155
4156
4156 if pushop.bkresult is not None:
4157 if pushop.bkresult is not None:
4157 if pushop.bkresult == 2:
4158 if pushop.bkresult == 2:
4158 result = 2
4159 result = 2
4159 elif not result and pushop.bkresult:
4160 elif not result and pushop.bkresult:
4160 result = 2
4161 result = 2
4161
4162
4162 return result
4163 return result
4163
4164
4164 @command('recover', [])
4165 @command('recover', [])
4165 def recover(ui, repo):
4166 def recover(ui, repo):
4166 """roll back an interrupted transaction
4167 """roll back an interrupted transaction
4167
4168
4168 Recover from an interrupted commit or pull.
4169 Recover from an interrupted commit or pull.
4169
4170
4170 This command tries to fix the repository status after an
4171 This command tries to fix the repository status after an
4171 interrupted operation. It should only be necessary when Mercurial
4172 interrupted operation. It should only be necessary when Mercurial
4172 suggests it.
4173 suggests it.
4173
4174
4174 Returns 0 if successful, 1 if nothing to recover or verify fails.
4175 Returns 0 if successful, 1 if nothing to recover or verify fails.
4175 """
4176 """
4176 if repo.recover():
4177 if repo.recover():
4177 return hg.verify(repo)
4178 return hg.verify(repo)
4178 return 1
4179 return 1
4179
4180
4180 @command('^remove|rm',
4181 @command('^remove|rm',
4181 [('A', 'after', None, _('record delete for missing files')),
4182 [('A', 'after', None, _('record delete for missing files')),
4182 ('f', 'force', None,
4183 ('f', 'force', None,
4183 _('forget added files, delete modified files')),
4184 _('forget added files, delete modified files')),
4184 ] + subrepoopts + walkopts,
4185 ] + subrepoopts + walkopts,
4185 _('[OPTION]... FILE...'),
4186 _('[OPTION]... FILE...'),
4186 inferrepo=True)
4187 inferrepo=True)
4187 def remove(ui, repo, *pats, **opts):
4188 def remove(ui, repo, *pats, **opts):
4188 """remove the specified files on the next commit
4189 """remove the specified files on the next commit
4189
4190
4190 Schedule the indicated files for removal from the current branch.
4191 Schedule the indicated files for removal from the current branch.
4191
4192
4192 This command schedules the files to be removed at the next commit.
4193 This command schedules the files to be removed at the next commit.
4193 To undo a remove before that, see :hg:`revert`. To undo added
4194 To undo a remove before that, see :hg:`revert`. To undo added
4194 files, see :hg:`forget`.
4195 files, see :hg:`forget`.
4195
4196
4196 .. container:: verbose
4197 .. container:: verbose
4197
4198
4198 -A/--after can be used to remove only files that have already
4199 -A/--after can be used to remove only files that have already
4199 been deleted, -f/--force can be used to force deletion, and -Af
4200 been deleted, -f/--force can be used to force deletion, and -Af
4200 can be used to remove files from the next revision without
4201 can be used to remove files from the next revision without
4201 deleting them from the working directory.
4202 deleting them from the working directory.
4202
4203
4203 The following table details the behavior of remove for different
4204 The following table details the behavior of remove for different
4204 file states (columns) and option combinations (rows). The file
4205 file states (columns) and option combinations (rows). The file
4205 states are Added [A], Clean [C], Modified [M] and Missing [!]
4206 states are Added [A], Clean [C], Modified [M] and Missing [!]
4206 (as reported by :hg:`status`). The actions are Warn, Remove
4207 (as reported by :hg:`status`). The actions are Warn, Remove
4207 (from branch) and Delete (from disk):
4208 (from branch) and Delete (from disk):
4208
4209
4209 ========= == == == ==
4210 ========= == == == ==
4210 opt/state A C M !
4211 opt/state A C M !
4211 ========= == == == ==
4212 ========= == == == ==
4212 none W RD W R
4213 none W RD W R
4213 -f R RD RD R
4214 -f R RD RD R
4214 -A W W W R
4215 -A W W W R
4215 -Af R R R R
4216 -Af R R R R
4216 ========= == == == ==
4217 ========= == == == ==
4217
4218
4218 .. note::
4219 .. note::
4219
4220
4220 :hg:`remove` never deletes files in Added [A] state from the
4221 :hg:`remove` never deletes files in Added [A] state from the
4221 working directory, not even if ``--force`` is specified.
4222 working directory, not even if ``--force`` is specified.
4222
4223
4223 Returns 0 on success, 1 if any warnings encountered.
4224 Returns 0 on success, 1 if any warnings encountered.
4224 """
4225 """
4225
4226
4226 opts = pycompat.byteskwargs(opts)
4227 opts = pycompat.byteskwargs(opts)
4227 after, force = opts.get('after'), opts.get('force')
4228 after, force = opts.get('after'), opts.get('force')
4228 if not pats and not after:
4229 if not pats and not after:
4229 raise error.Abort(_('no files specified'))
4230 raise error.Abort(_('no files specified'))
4230
4231
4231 m = scmutil.match(repo[None], pats, opts)
4232 m = scmutil.match(repo[None], pats, opts)
4232 subrepos = opts.get('subrepos')
4233 subrepos = opts.get('subrepos')
4233 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4234 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4234
4235
4235 @command('rename|move|mv',
4236 @command('rename|move|mv',
4236 [('A', 'after', None, _('record a rename that has already occurred')),
4237 [('A', 'after', None, _('record a rename that has already occurred')),
4237 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4238 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4238 ] + walkopts + dryrunopts,
4239 ] + walkopts + dryrunopts,
4239 _('[OPTION]... SOURCE... DEST'))
4240 _('[OPTION]... SOURCE... DEST'))
4240 def rename(ui, repo, *pats, **opts):
4241 def rename(ui, repo, *pats, **opts):
4241 """rename files; equivalent of copy + remove
4242 """rename files; equivalent of copy + remove
4242
4243
4243 Mark dest as copies of sources; mark sources for deletion. If dest
4244 Mark dest as copies of sources; mark sources for deletion. If dest
4244 is a directory, copies are put in that directory. If dest is a
4245 is a directory, copies are put in that directory. If dest is a
4245 file, there can only be one source.
4246 file, there can only be one source.
4246
4247
4247 By default, this command copies the contents of files as they
4248 By default, this command copies the contents of files as they
4248 exist in the working directory. If invoked with -A/--after, the
4249 exist in the working directory. If invoked with -A/--after, the
4249 operation is recorded, but no copying is performed.
4250 operation is recorded, but no copying is performed.
4250
4251
4251 This command takes effect at the next commit. To undo a rename
4252 This command takes effect at the next commit. To undo a rename
4252 before that, see :hg:`revert`.
4253 before that, see :hg:`revert`.
4253
4254
4254 Returns 0 on success, 1 if errors are encountered.
4255 Returns 0 on success, 1 if errors are encountered.
4255 """
4256 """
4256 opts = pycompat.byteskwargs(opts)
4257 opts = pycompat.byteskwargs(opts)
4257 with repo.wlock(False):
4258 with repo.wlock(False):
4258 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4259 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4259
4260
4260 @command('resolve',
4261 @command('resolve',
4261 [('a', 'all', None, _('select all unresolved files')),
4262 [('a', 'all', None, _('select all unresolved files')),
4262 ('l', 'list', None, _('list state of files needing merge')),
4263 ('l', 'list', None, _('list state of files needing merge')),
4263 ('m', 'mark', None, _('mark files as resolved')),
4264 ('m', 'mark', None, _('mark files as resolved')),
4264 ('u', 'unmark', None, _('mark files as unresolved')),
4265 ('u', 'unmark', None, _('mark files as unresolved')),
4265 ('n', 'no-status', None, _('hide status prefix'))]
4266 ('n', 'no-status', None, _('hide status prefix'))]
4266 + mergetoolopts + walkopts + formatteropts,
4267 + mergetoolopts + walkopts + formatteropts,
4267 _('[OPTION]... [FILE]...'),
4268 _('[OPTION]... [FILE]...'),
4268 inferrepo=True)
4269 inferrepo=True)
4269 def resolve(ui, repo, *pats, **opts):
4270 def resolve(ui, repo, *pats, **opts):
4270 """redo merges or set/view the merge status of files
4271 """redo merges or set/view the merge status of files
4271
4272
4272 Merges with unresolved conflicts are often the result of
4273 Merges with unresolved conflicts are often the result of
4273 non-interactive merging using the ``internal:merge`` configuration
4274 non-interactive merging using the ``internal:merge`` configuration
4274 setting, or a command-line merge tool like ``diff3``. The resolve
4275 setting, or a command-line merge tool like ``diff3``. The resolve
4275 command is used to manage the files involved in a merge, after
4276 command is used to manage the files involved in a merge, after
4276 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4277 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4277 working directory must have two parents). See :hg:`help
4278 working directory must have two parents). See :hg:`help
4278 merge-tools` for information on configuring merge tools.
4279 merge-tools` for information on configuring merge tools.
4279
4280
4280 The resolve command can be used in the following ways:
4281 The resolve command can be used in the following ways:
4281
4282
4282 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4283 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4283 files, discarding any previous merge attempts. Re-merging is not
4284 files, discarding any previous merge attempts. Re-merging is not
4284 performed for files already marked as resolved. Use ``--all/-a``
4285 performed for files already marked as resolved. Use ``--all/-a``
4285 to select all unresolved files. ``--tool`` can be used to specify
4286 to select all unresolved files. ``--tool`` can be used to specify
4286 the merge tool used for the given files. It overrides the HGMERGE
4287 the merge tool used for the given files. It overrides the HGMERGE
4287 environment variable and your configuration files. Previous file
4288 environment variable and your configuration files. Previous file
4288 contents are saved with a ``.orig`` suffix.
4289 contents are saved with a ``.orig`` suffix.
4289
4290
4290 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4291 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4291 (e.g. after having manually fixed-up the files). The default is
4292 (e.g. after having manually fixed-up the files). The default is
4292 to mark all unresolved files.
4293 to mark all unresolved files.
4293
4294
4294 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4295 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4295 default is to mark all resolved files.
4296 default is to mark all resolved files.
4296
4297
4297 - :hg:`resolve -l`: list files which had or still have conflicts.
4298 - :hg:`resolve -l`: list files which had or still have conflicts.
4298 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4299 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4299 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4300 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4300 the list. See :hg:`help filesets` for details.
4301 the list. See :hg:`help filesets` for details.
4301
4302
4302 .. note::
4303 .. note::
4303
4304
4304 Mercurial will not let you commit files with unresolved merge
4305 Mercurial will not let you commit files with unresolved merge
4305 conflicts. You must use :hg:`resolve -m ...` before you can
4306 conflicts. You must use :hg:`resolve -m ...` before you can
4306 commit after a conflicting merge.
4307 commit after a conflicting merge.
4307
4308
4308 Returns 0 on success, 1 if any files fail a resolve attempt.
4309 Returns 0 on success, 1 if any files fail a resolve attempt.
4309 """
4310 """
4310
4311
4311 opts = pycompat.byteskwargs(opts)
4312 opts = pycompat.byteskwargs(opts)
4312 flaglist = 'all mark unmark list no_status'.split()
4313 flaglist = 'all mark unmark list no_status'.split()
4313 all, mark, unmark, show, nostatus = \
4314 all, mark, unmark, show, nostatus = \
4314 [opts.get(o) for o in flaglist]
4315 [opts.get(o) for o in flaglist]
4315
4316
4316 if (show and (mark or unmark)) or (mark and unmark):
4317 if (show and (mark or unmark)) or (mark and unmark):
4317 raise error.Abort(_("too many options specified"))
4318 raise error.Abort(_("too many options specified"))
4318 if pats and all:
4319 if pats and all:
4319 raise error.Abort(_("can't specify --all and patterns"))
4320 raise error.Abort(_("can't specify --all and patterns"))
4320 if not (all or pats or show or mark or unmark):
4321 if not (all or pats or show or mark or unmark):
4321 raise error.Abort(_('no files or directories specified'),
4322 raise error.Abort(_('no files or directories specified'),
4322 hint=('use --all to re-merge all unresolved files'))
4323 hint=('use --all to re-merge all unresolved files'))
4323
4324
4324 if show:
4325 if show:
4325 ui.pager('resolve')
4326 ui.pager('resolve')
4326 fm = ui.formatter('resolve', opts)
4327 fm = ui.formatter('resolve', opts)
4327 ms = mergemod.mergestate.read(repo)
4328 ms = mergemod.mergestate.read(repo)
4328 m = scmutil.match(repo[None], pats, opts)
4329 m = scmutil.match(repo[None], pats, opts)
4329 for f in ms:
4330 for f in ms:
4330 if not m(f):
4331 if not m(f):
4331 continue
4332 continue
4332 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
4333 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
4333 'd': 'driverresolved'}[ms[f]]
4334 'd': 'driverresolved'}[ms[f]]
4334 fm.startitem()
4335 fm.startitem()
4335 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
4336 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
4336 fm.write('path', '%s\n', f, label=l)
4337 fm.write('path', '%s\n', f, label=l)
4337 fm.end()
4338 fm.end()
4338 return 0
4339 return 0
4339
4340
4340 with repo.wlock():
4341 with repo.wlock():
4341 ms = mergemod.mergestate.read(repo)
4342 ms = mergemod.mergestate.read(repo)
4342
4343
4343 if not (ms.active() or repo.dirstate.p2() != nullid):
4344 if not (ms.active() or repo.dirstate.p2() != nullid):
4344 raise error.Abort(
4345 raise error.Abort(
4345 _('resolve command not applicable when not merging'))
4346 _('resolve command not applicable when not merging'))
4346
4347
4347 wctx = repo[None]
4348 wctx = repo[None]
4348
4349
4349 if ms.mergedriver and ms.mdstate() == 'u':
4350 if ms.mergedriver and ms.mdstate() == 'u':
4350 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4351 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4351 ms.commit()
4352 ms.commit()
4352 # allow mark and unmark to go through
4353 # allow mark and unmark to go through
4353 if not mark and not unmark and not proceed:
4354 if not mark and not unmark and not proceed:
4354 return 1
4355 return 1
4355
4356
4356 m = scmutil.match(wctx, pats, opts)
4357 m = scmutil.match(wctx, pats, opts)
4357 ret = 0
4358 ret = 0
4358 didwork = False
4359 didwork = False
4359 runconclude = False
4360 runconclude = False
4360
4361
4361 tocomplete = []
4362 tocomplete = []
4362 for f in ms:
4363 for f in ms:
4363 if not m(f):
4364 if not m(f):
4364 continue
4365 continue
4365
4366
4366 didwork = True
4367 didwork = True
4367
4368
4368 # don't let driver-resolved files be marked, and run the conclude
4369 # don't let driver-resolved files be marked, and run the conclude
4369 # step if asked to resolve
4370 # step if asked to resolve
4370 if ms[f] == "d":
4371 if ms[f] == "d":
4371 exact = m.exact(f)
4372 exact = m.exact(f)
4372 if mark:
4373 if mark:
4373 if exact:
4374 if exact:
4374 ui.warn(_('not marking %s as it is driver-resolved\n')
4375 ui.warn(_('not marking %s as it is driver-resolved\n')
4375 % f)
4376 % f)
4376 elif unmark:
4377 elif unmark:
4377 if exact:
4378 if exact:
4378 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4379 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4379 % f)
4380 % f)
4380 else:
4381 else:
4381 runconclude = True
4382 runconclude = True
4382 continue
4383 continue
4383
4384
4384 if mark:
4385 if mark:
4385 ms.mark(f, "r")
4386 ms.mark(f, "r")
4386 elif unmark:
4387 elif unmark:
4387 ms.mark(f, "u")
4388 ms.mark(f, "u")
4388 else:
4389 else:
4389 # backup pre-resolve (merge uses .orig for its own purposes)
4390 # backup pre-resolve (merge uses .orig for its own purposes)
4390 a = repo.wjoin(f)
4391 a = repo.wjoin(f)
4391 try:
4392 try:
4392 util.copyfile(a, a + ".resolve")
4393 util.copyfile(a, a + ".resolve")
4393 except (IOError, OSError) as inst:
4394 except (IOError, OSError) as inst:
4394 if inst.errno != errno.ENOENT:
4395 if inst.errno != errno.ENOENT:
4395 raise
4396 raise
4396
4397
4397 try:
4398 try:
4398 # preresolve file
4399 # preresolve file
4399 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4400 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4400 'resolve')
4401 'resolve')
4401 complete, r = ms.preresolve(f, wctx)
4402 complete, r = ms.preresolve(f, wctx)
4402 if not complete:
4403 if not complete:
4403 tocomplete.append(f)
4404 tocomplete.append(f)
4404 elif r:
4405 elif r:
4405 ret = 1
4406 ret = 1
4406 finally:
4407 finally:
4407 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4408 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4408 ms.commit()
4409 ms.commit()
4409
4410
4410 # replace filemerge's .orig file with our resolve file, but only
4411 # replace filemerge's .orig file with our resolve file, but only
4411 # for merges that are complete
4412 # for merges that are complete
4412 if complete:
4413 if complete:
4413 try:
4414 try:
4414 util.rename(a + ".resolve",
4415 util.rename(a + ".resolve",
4415 scmutil.origpath(ui, repo, a))
4416 scmutil.origpath(ui, repo, a))
4416 except OSError as inst:
4417 except OSError as inst:
4417 if inst.errno != errno.ENOENT:
4418 if inst.errno != errno.ENOENT:
4418 raise
4419 raise
4419
4420
4420 for f in tocomplete:
4421 for f in tocomplete:
4421 try:
4422 try:
4422 # resolve file
4423 # resolve file
4423 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4424 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4424 'resolve')
4425 'resolve')
4425 r = ms.resolve(f, wctx)
4426 r = ms.resolve(f, wctx)
4426 if r:
4427 if r:
4427 ret = 1
4428 ret = 1
4428 finally:
4429 finally:
4429 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4430 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4430 ms.commit()
4431 ms.commit()
4431
4432
4432 # replace filemerge's .orig file with our resolve file
4433 # replace filemerge's .orig file with our resolve file
4433 a = repo.wjoin(f)
4434 a = repo.wjoin(f)
4434 try:
4435 try:
4435 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4436 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4436 except OSError as inst:
4437 except OSError as inst:
4437 if inst.errno != errno.ENOENT:
4438 if inst.errno != errno.ENOENT:
4438 raise
4439 raise
4439
4440
4440 ms.commit()
4441 ms.commit()
4441 ms.recordactions()
4442 ms.recordactions()
4442
4443
4443 if not didwork and pats:
4444 if not didwork and pats:
4444 hint = None
4445 hint = None
4445 if not any([p for p in pats if p.find(':') >= 0]):
4446 if not any([p for p in pats if p.find(':') >= 0]):
4446 pats = ['path:%s' % p for p in pats]
4447 pats = ['path:%s' % p for p in pats]
4447 m = scmutil.match(wctx, pats, opts)
4448 m = scmutil.match(wctx, pats, opts)
4448 for f in ms:
4449 for f in ms:
4449 if not m(f):
4450 if not m(f):
4450 continue
4451 continue
4451 flags = ''.join(['-%s ' % o[0] for o in flaglist
4452 flags = ''.join(['-%s ' % o[0] for o in flaglist
4452 if opts.get(o)])
4453 if opts.get(o)])
4453 hint = _("(try: hg resolve %s%s)\n") % (
4454 hint = _("(try: hg resolve %s%s)\n") % (
4454 flags,
4455 flags,
4455 ' '.join(pats))
4456 ' '.join(pats))
4456 break
4457 break
4457 ui.warn(_("arguments do not match paths that need resolving\n"))
4458 ui.warn(_("arguments do not match paths that need resolving\n"))
4458 if hint:
4459 if hint:
4459 ui.warn(hint)
4460 ui.warn(hint)
4460 elif ms.mergedriver and ms.mdstate() != 's':
4461 elif ms.mergedriver and ms.mdstate() != 's':
4461 # run conclude step when either a driver-resolved file is requested
4462 # run conclude step when either a driver-resolved file is requested
4462 # or there are no driver-resolved files
4463 # or there are no driver-resolved files
4463 # we can't use 'ret' to determine whether any files are unresolved
4464 # we can't use 'ret' to determine whether any files are unresolved
4464 # because we might not have tried to resolve some
4465 # because we might not have tried to resolve some
4465 if ((runconclude or not list(ms.driverresolved()))
4466 if ((runconclude or not list(ms.driverresolved()))
4466 and not list(ms.unresolved())):
4467 and not list(ms.unresolved())):
4467 proceed = mergemod.driverconclude(repo, ms, wctx)
4468 proceed = mergemod.driverconclude(repo, ms, wctx)
4468 ms.commit()
4469 ms.commit()
4469 if not proceed:
4470 if not proceed:
4470 return 1
4471 return 1
4471
4472
4472 # Nudge users into finishing an unfinished operation
4473 # Nudge users into finishing an unfinished operation
4473 unresolvedf = list(ms.unresolved())
4474 unresolvedf = list(ms.unresolved())
4474 driverresolvedf = list(ms.driverresolved())
4475 driverresolvedf = list(ms.driverresolved())
4475 if not unresolvedf and not driverresolvedf:
4476 if not unresolvedf and not driverresolvedf:
4476 ui.status(_('(no more unresolved files)\n'))
4477 ui.status(_('(no more unresolved files)\n'))
4477 cmdutil.checkafterresolved(repo)
4478 cmdutil.checkafterresolved(repo)
4478 elif not unresolvedf:
4479 elif not unresolvedf:
4479 ui.status(_('(no more unresolved files -- '
4480 ui.status(_('(no more unresolved files -- '
4480 'run "hg resolve --all" to conclude)\n'))
4481 'run "hg resolve --all" to conclude)\n'))
4481
4482
4482 return ret
4483 return ret
4483
4484
4484 @command('revert',
4485 @command('revert',
4485 [('a', 'all', None, _('revert all changes when no arguments given')),
4486 [('a', 'all', None, _('revert all changes when no arguments given')),
4486 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4487 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4487 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4488 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4488 ('C', 'no-backup', None, _('do not save backup copies of files')),
4489 ('C', 'no-backup', None, _('do not save backup copies of files')),
4489 ('i', 'interactive', None,
4490 ('i', 'interactive', None,
4490 _('interactively select the changes (EXPERIMENTAL)')),
4491 _('interactively select the changes (EXPERIMENTAL)')),
4491 ] + walkopts + dryrunopts,
4492 ] + walkopts + dryrunopts,
4492 _('[OPTION]... [-r REV] [NAME]...'))
4493 _('[OPTION]... [-r REV] [NAME]...'))
4493 def revert(ui, repo, *pats, **opts):
4494 def revert(ui, repo, *pats, **opts):
4494 """restore files to their checkout state
4495 """restore files to their checkout state
4495
4496
4496 .. note::
4497 .. note::
4497
4498
4498 To check out earlier revisions, you should use :hg:`update REV`.
4499 To check out earlier revisions, you should use :hg:`update REV`.
4499 To cancel an uncommitted merge (and lose your changes),
4500 To cancel an uncommitted merge (and lose your changes),
4500 use :hg:`update --clean .`.
4501 use :hg:`update --clean .`.
4501
4502
4502 With no revision specified, revert the specified files or directories
4503 With no revision specified, revert the specified files or directories
4503 to the contents they had in the parent of the working directory.
4504 to the contents they had in the parent of the working directory.
4504 This restores the contents of files to an unmodified
4505 This restores the contents of files to an unmodified
4505 state and unschedules adds, removes, copies, and renames. If the
4506 state and unschedules adds, removes, copies, and renames. If the
4506 working directory has two parents, you must explicitly specify a
4507 working directory has two parents, you must explicitly specify a
4507 revision.
4508 revision.
4508
4509
4509 Using the -r/--rev or -d/--date options, revert the given files or
4510 Using the -r/--rev or -d/--date options, revert the given files or
4510 directories to their states as of a specific revision. Because
4511 directories to their states as of a specific revision. Because
4511 revert does not change the working directory parents, this will
4512 revert does not change the working directory parents, this will
4512 cause these files to appear modified. This can be helpful to "back
4513 cause these files to appear modified. This can be helpful to "back
4513 out" some or all of an earlier change. See :hg:`backout` for a
4514 out" some or all of an earlier change. See :hg:`backout` for a
4514 related method.
4515 related method.
4515
4516
4516 Modified files are saved with a .orig suffix before reverting.
4517 Modified files are saved with a .orig suffix before reverting.
4517 To disable these backups, use --no-backup. It is possible to store
4518 To disable these backups, use --no-backup. It is possible to store
4518 the backup files in a custom directory relative to the root of the
4519 the backup files in a custom directory relative to the root of the
4519 repository by setting the ``ui.origbackuppath`` configuration
4520 repository by setting the ``ui.origbackuppath`` configuration
4520 option.
4521 option.
4521
4522
4522 See :hg:`help dates` for a list of formats valid for -d/--date.
4523 See :hg:`help dates` for a list of formats valid for -d/--date.
4523
4524
4524 See :hg:`help backout` for a way to reverse the effect of an
4525 See :hg:`help backout` for a way to reverse the effect of an
4525 earlier changeset.
4526 earlier changeset.
4526
4527
4527 Returns 0 on success.
4528 Returns 0 on success.
4528 """
4529 """
4529
4530
4530 if opts.get("date"):
4531 if opts.get("date"):
4531 if opts.get("rev"):
4532 if opts.get("rev"):
4532 raise error.Abort(_("you can't specify a revision and a date"))
4533 raise error.Abort(_("you can't specify a revision and a date"))
4533 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4534 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4534
4535
4535 parent, p2 = repo.dirstate.parents()
4536 parent, p2 = repo.dirstate.parents()
4536 if not opts.get('rev') and p2 != nullid:
4537 if not opts.get('rev') and p2 != nullid:
4537 # revert after merge is a trap for new users (issue2915)
4538 # revert after merge is a trap for new users (issue2915)
4538 raise error.Abort(_('uncommitted merge with no revision specified'),
4539 raise error.Abort(_('uncommitted merge with no revision specified'),
4539 hint=_("use 'hg update' or see 'hg help revert'"))
4540 hint=_("use 'hg update' or see 'hg help revert'"))
4540
4541
4541 ctx = scmutil.revsingle(repo, opts.get('rev'))
4542 ctx = scmutil.revsingle(repo, opts.get('rev'))
4542
4543
4543 if (not (pats or opts.get('include') or opts.get('exclude') or
4544 if (not (pats or opts.get('include') or opts.get('exclude') or
4544 opts.get('all') or opts.get('interactive'))):
4545 opts.get('all') or opts.get('interactive'))):
4545 msg = _("no files or directories specified")
4546 msg = _("no files or directories specified")
4546 if p2 != nullid:
4547 if p2 != nullid:
4547 hint = _("uncommitted merge, use --all to discard all changes,"
4548 hint = _("uncommitted merge, use --all to discard all changes,"
4548 " or 'hg update -C .' to abort the merge")
4549 " or 'hg update -C .' to abort the merge")
4549 raise error.Abort(msg, hint=hint)
4550 raise error.Abort(msg, hint=hint)
4550 dirty = any(repo.status())
4551 dirty = any(repo.status())
4551 node = ctx.node()
4552 node = ctx.node()
4552 if node != parent:
4553 if node != parent:
4553 if dirty:
4554 if dirty:
4554 hint = _("uncommitted changes, use --all to discard all"
4555 hint = _("uncommitted changes, use --all to discard all"
4555 " changes, or 'hg update %s' to update") % ctx.rev()
4556 " changes, or 'hg update %s' to update") % ctx.rev()
4556 else:
4557 else:
4557 hint = _("use --all to revert all files,"
4558 hint = _("use --all to revert all files,"
4558 " or 'hg update %s' to update") % ctx.rev()
4559 " or 'hg update %s' to update") % ctx.rev()
4559 elif dirty:
4560 elif dirty:
4560 hint = _("uncommitted changes, use --all to discard all changes")
4561 hint = _("uncommitted changes, use --all to discard all changes")
4561 else:
4562 else:
4562 hint = _("use --all to revert all files")
4563 hint = _("use --all to revert all files")
4563 raise error.Abort(msg, hint=hint)
4564 raise error.Abort(msg, hint=hint)
4564
4565
4565 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4566 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4566
4567
4567 @command('rollback', dryrunopts +
4568 @command('rollback', dryrunopts +
4568 [('f', 'force', False, _('ignore safety measures'))])
4569 [('f', 'force', False, _('ignore safety measures'))])
4569 def rollback(ui, repo, **opts):
4570 def rollback(ui, repo, **opts):
4570 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4571 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4571
4572
4572 Please use :hg:`commit --amend` instead of rollback to correct
4573 Please use :hg:`commit --amend` instead of rollback to correct
4573 mistakes in the last commit.
4574 mistakes in the last commit.
4574
4575
4575 This command should be used with care. There is only one level of
4576 This command should be used with care. There is only one level of
4576 rollback, and there is no way to undo a rollback. It will also
4577 rollback, and there is no way to undo a rollback. It will also
4577 restore the dirstate at the time of the last transaction, losing
4578 restore the dirstate at the time of the last transaction, losing
4578 any dirstate changes since that time. This command does not alter
4579 any dirstate changes since that time. This command does not alter
4579 the working directory.
4580 the working directory.
4580
4581
4581 Transactions are used to encapsulate the effects of all commands
4582 Transactions are used to encapsulate the effects of all commands
4582 that create new changesets or propagate existing changesets into a
4583 that create new changesets or propagate existing changesets into a
4583 repository.
4584 repository.
4584
4585
4585 .. container:: verbose
4586 .. container:: verbose
4586
4587
4587 For example, the following commands are transactional, and their
4588 For example, the following commands are transactional, and their
4588 effects can be rolled back:
4589 effects can be rolled back:
4589
4590
4590 - commit
4591 - commit
4591 - import
4592 - import
4592 - pull
4593 - pull
4593 - push (with this repository as the destination)
4594 - push (with this repository as the destination)
4594 - unbundle
4595 - unbundle
4595
4596
4596 To avoid permanent data loss, rollback will refuse to rollback a
4597 To avoid permanent data loss, rollback will refuse to rollback a
4597 commit transaction if it isn't checked out. Use --force to
4598 commit transaction if it isn't checked out. Use --force to
4598 override this protection.
4599 override this protection.
4599
4600
4600 The rollback command can be entirely disabled by setting the
4601 The rollback command can be entirely disabled by setting the
4601 ``ui.rollback`` configuration setting to false. If you're here
4602 ``ui.rollback`` configuration setting to false. If you're here
4602 because you want to use rollback and it's disabled, you can
4603 because you want to use rollback and it's disabled, you can
4603 re-enable the command by setting ``ui.rollback`` to true.
4604 re-enable the command by setting ``ui.rollback`` to true.
4604
4605
4605 This command is not intended for use on public repositories. Once
4606 This command is not intended for use on public repositories. Once
4606 changes are visible for pull by other users, rolling a transaction
4607 changes are visible for pull by other users, rolling a transaction
4607 back locally is ineffective (someone else may already have pulled
4608 back locally is ineffective (someone else may already have pulled
4608 the changes). Furthermore, a race is possible with readers of the
4609 the changes). Furthermore, a race is possible with readers of the
4609 repository; for example an in-progress pull from the repository
4610 repository; for example an in-progress pull from the repository
4610 may fail if a rollback is performed.
4611 may fail if a rollback is performed.
4611
4612
4612 Returns 0 on success, 1 if no rollback data is available.
4613 Returns 0 on success, 1 if no rollback data is available.
4613 """
4614 """
4614 if not ui.configbool('ui', 'rollback', True):
4615 if not ui.configbool('ui', 'rollback', True):
4615 raise error.Abort(_('rollback is disabled because it is unsafe'),
4616 raise error.Abort(_('rollback is disabled because it is unsafe'),
4616 hint=('see `hg help -v rollback` for information'))
4617 hint=('see `hg help -v rollback` for information'))
4617 return repo.rollback(dryrun=opts.get(r'dry_run'),
4618 return repo.rollback(dryrun=opts.get(r'dry_run'),
4618 force=opts.get(r'force'))
4619 force=opts.get(r'force'))
4619
4620
4620 @command('root', [])
4621 @command('root', [])
4621 def root(ui, repo):
4622 def root(ui, repo):
4622 """print the root (top) of the current working directory
4623 """print the root (top) of the current working directory
4623
4624
4624 Print the root directory of the current repository.
4625 Print the root directory of the current repository.
4625
4626
4626 Returns 0 on success.
4627 Returns 0 on success.
4627 """
4628 """
4628 ui.write(repo.root + "\n")
4629 ui.write(repo.root + "\n")
4629
4630
4630 @command('^serve',
4631 @command('^serve',
4631 [('A', 'accesslog', '', _('name of access log file to write to'),
4632 [('A', 'accesslog', '', _('name of access log file to write to'),
4632 _('FILE')),
4633 _('FILE')),
4633 ('d', 'daemon', None, _('run server in background')),
4634 ('d', 'daemon', None, _('run server in background')),
4634 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4635 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4635 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4636 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4636 # use string type, then we can check if something was passed
4637 # use string type, then we can check if something was passed
4637 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4638 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4638 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4639 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4639 _('ADDR')),
4640 _('ADDR')),
4640 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4641 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4641 _('PREFIX')),
4642 _('PREFIX')),
4642 ('n', 'name', '',
4643 ('n', 'name', '',
4643 _('name to show in web pages (default: working directory)'), _('NAME')),
4644 _('name to show in web pages (default: working directory)'), _('NAME')),
4644 ('', 'web-conf', '',
4645 ('', 'web-conf', '',
4645 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4646 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4646 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4647 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4647 _('FILE')),
4648 _('FILE')),
4648 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4649 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4649 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4650 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4650 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4651 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4651 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4652 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4652 ('', 'style', '', _('template style to use'), _('STYLE')),
4653 ('', 'style', '', _('template style to use'), _('STYLE')),
4653 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4654 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4654 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4655 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4655 + subrepoopts,
4656 + subrepoopts,
4656 _('[OPTION]...'),
4657 _('[OPTION]...'),
4657 optionalrepo=True)
4658 optionalrepo=True)
4658 def serve(ui, repo, **opts):
4659 def serve(ui, repo, **opts):
4659 """start stand-alone webserver
4660 """start stand-alone webserver
4660
4661
4661 Start a local HTTP repository browser and pull server. You can use
4662 Start a local HTTP repository browser and pull server. You can use
4662 this for ad-hoc sharing and browsing of repositories. It is
4663 this for ad-hoc sharing and browsing of repositories. It is
4663 recommended to use a real web server to serve a repository for
4664 recommended to use a real web server to serve a repository for
4664 longer periods of time.
4665 longer periods of time.
4665
4666
4666 Please note that the server does not implement access control.
4667 Please note that the server does not implement access control.
4667 This means that, by default, anybody can read from the server and
4668 This means that, by default, anybody can read from the server and
4668 nobody can write to it by default. Set the ``web.allow_push``
4669 nobody can write to it by default. Set the ``web.allow_push``
4669 option to ``*`` to allow everybody to push to the server. You
4670 option to ``*`` to allow everybody to push to the server. You
4670 should use a real web server if you need to authenticate users.
4671 should use a real web server if you need to authenticate users.
4671
4672
4672 By default, the server logs accesses to stdout and errors to
4673 By default, the server logs accesses to stdout and errors to
4673 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4674 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4674 files.
4675 files.
4675
4676
4676 To have the server choose a free port number to listen on, specify
4677 To have the server choose a free port number to listen on, specify
4677 a port number of 0; in this case, the server will print the port
4678 a port number of 0; in this case, the server will print the port
4678 number it uses.
4679 number it uses.
4679
4680
4680 Returns 0 on success.
4681 Returns 0 on success.
4681 """
4682 """
4682
4683
4683 opts = pycompat.byteskwargs(opts)
4684 opts = pycompat.byteskwargs(opts)
4684 if opts["stdio"] and opts["cmdserver"]:
4685 if opts["stdio"] and opts["cmdserver"]:
4685 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4686 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4686
4687
4687 if opts["stdio"]:
4688 if opts["stdio"]:
4688 if repo is None:
4689 if repo is None:
4689 raise error.RepoError(_("there is no Mercurial repository here"
4690 raise error.RepoError(_("there is no Mercurial repository here"
4690 " (.hg not found)"))
4691 " (.hg not found)"))
4691 s = sshserver.sshserver(ui, repo)
4692 s = sshserver.sshserver(ui, repo)
4692 s.serve_forever()
4693 s.serve_forever()
4693
4694
4694 service = server.createservice(ui, repo, opts)
4695 service = server.createservice(ui, repo, opts)
4695 return server.runservice(opts, initfn=service.init, runfn=service.run)
4696 return server.runservice(opts, initfn=service.init, runfn=service.run)
4696
4697
4697 @command('^status|st',
4698 @command('^status|st',
4698 [('A', 'all', None, _('show status of all files')),
4699 [('A', 'all', None, _('show status of all files')),
4699 ('m', 'modified', None, _('show only modified files')),
4700 ('m', 'modified', None, _('show only modified files')),
4700 ('a', 'added', None, _('show only added files')),
4701 ('a', 'added', None, _('show only added files')),
4701 ('r', 'removed', None, _('show only removed files')),
4702 ('r', 'removed', None, _('show only removed files')),
4702 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4703 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4703 ('c', 'clean', None, _('show only files without changes')),
4704 ('c', 'clean', None, _('show only files without changes')),
4704 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4705 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4705 ('i', 'ignored', None, _('show only ignored files')),
4706 ('i', 'ignored', None, _('show only ignored files')),
4706 ('n', 'no-status', None, _('hide status prefix')),
4707 ('n', 'no-status', None, _('hide status prefix')),
4707 ('C', 'copies', None, _('show source of copied files')),
4708 ('C', 'copies', None, _('show source of copied files')),
4708 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4709 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4709 ('', 'rev', [], _('show difference from revision'), _('REV')),
4710 ('', 'rev', [], _('show difference from revision'), _('REV')),
4710 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4711 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4711 ] + walkopts + subrepoopts + formatteropts,
4712 ] + walkopts + subrepoopts + formatteropts,
4712 _('[OPTION]... [FILE]...'),
4713 _('[OPTION]... [FILE]...'),
4713 inferrepo=True)
4714 inferrepo=True)
4714 def status(ui, repo, *pats, **opts):
4715 def status(ui, repo, *pats, **opts):
4715 """show changed files in the working directory
4716 """show changed files in the working directory
4716
4717
4717 Show status of files in the repository. If names are given, only
4718 Show status of files in the repository. If names are given, only
4718 files that match are shown. Files that are clean or ignored or
4719 files that match are shown. Files that are clean or ignored or
4719 the source of a copy/move operation, are not listed unless
4720 the source of a copy/move operation, are not listed unless
4720 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4721 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4721 Unless options described with "show only ..." are given, the
4722 Unless options described with "show only ..." are given, the
4722 options -mardu are used.
4723 options -mardu are used.
4723
4724
4724 Option -q/--quiet hides untracked (unknown and ignored) files
4725 Option -q/--quiet hides untracked (unknown and ignored) files
4725 unless explicitly requested with -u/--unknown or -i/--ignored.
4726 unless explicitly requested with -u/--unknown or -i/--ignored.
4726
4727
4727 .. note::
4728 .. note::
4728
4729
4729 :hg:`status` may appear to disagree with diff if permissions have
4730 :hg:`status` may appear to disagree with diff if permissions have
4730 changed or a merge has occurred. The standard diff format does
4731 changed or a merge has occurred. The standard diff format does
4731 not report permission changes and diff only reports changes
4732 not report permission changes and diff only reports changes
4732 relative to one merge parent.
4733 relative to one merge parent.
4733
4734
4734 If one revision is given, it is used as the base revision.
4735 If one revision is given, it is used as the base revision.
4735 If two revisions are given, the differences between them are
4736 If two revisions are given, the differences between them are
4736 shown. The --change option can also be used as a shortcut to list
4737 shown. The --change option can also be used as a shortcut to list
4737 the changed files of a revision from its first parent.
4738 the changed files of a revision from its first parent.
4738
4739
4739 The codes used to show the status of files are::
4740 The codes used to show the status of files are::
4740
4741
4741 M = modified
4742 M = modified
4742 A = added
4743 A = added
4743 R = removed
4744 R = removed
4744 C = clean
4745 C = clean
4745 ! = missing (deleted by non-hg command, but still tracked)
4746 ! = missing (deleted by non-hg command, but still tracked)
4746 ? = not tracked
4747 ? = not tracked
4747 I = ignored
4748 I = ignored
4748 = origin of the previous file (with --copies)
4749 = origin of the previous file (with --copies)
4749
4750
4750 .. container:: verbose
4751 .. container:: verbose
4751
4752
4752 Examples:
4753 Examples:
4753
4754
4754 - show changes in the working directory relative to a
4755 - show changes in the working directory relative to a
4755 changeset::
4756 changeset::
4756
4757
4757 hg status --rev 9353
4758 hg status --rev 9353
4758
4759
4759 - show changes in the working directory relative to the
4760 - show changes in the working directory relative to the
4760 current directory (see :hg:`help patterns` for more information)::
4761 current directory (see :hg:`help patterns` for more information)::
4761
4762
4762 hg status re:
4763 hg status re:
4763
4764
4764 - show all changes including copies in an existing changeset::
4765 - show all changes including copies in an existing changeset::
4765
4766
4766 hg status --copies --change 9353
4767 hg status --copies --change 9353
4767
4768
4768 - get a NUL separated list of added files, suitable for xargs::
4769 - get a NUL separated list of added files, suitable for xargs::
4769
4770
4770 hg status -an0
4771 hg status -an0
4771
4772
4772 Returns 0 on success.
4773 Returns 0 on success.
4773 """
4774 """
4774
4775
4775 opts = pycompat.byteskwargs(opts)
4776 opts = pycompat.byteskwargs(opts)
4776 revs = opts.get('rev')
4777 revs = opts.get('rev')
4777 change = opts.get('change')
4778 change = opts.get('change')
4778
4779
4779 if revs and change:
4780 if revs and change:
4780 msg = _('cannot specify --rev and --change at the same time')
4781 msg = _('cannot specify --rev and --change at the same time')
4781 raise error.Abort(msg)
4782 raise error.Abort(msg)
4782 elif change:
4783 elif change:
4783 node2 = scmutil.revsingle(repo, change, None).node()
4784 node2 = scmutil.revsingle(repo, change, None).node()
4784 node1 = repo[node2].p1().node()
4785 node1 = repo[node2].p1().node()
4785 else:
4786 else:
4786 node1, node2 = scmutil.revpair(repo, revs)
4787 node1, node2 = scmutil.revpair(repo, revs)
4787
4788
4788 if pats or ui.configbool('commands', 'status.relative'):
4789 if pats or ui.configbool('commands', 'status.relative'):
4789 cwd = repo.getcwd()
4790 cwd = repo.getcwd()
4790 else:
4791 else:
4791 cwd = ''
4792 cwd = ''
4792
4793
4793 if opts.get('print0'):
4794 if opts.get('print0'):
4794 end = '\0'
4795 end = '\0'
4795 else:
4796 else:
4796 end = '\n'
4797 end = '\n'
4797 copy = {}
4798 copy = {}
4798 states = 'modified added removed deleted unknown ignored clean'.split()
4799 states = 'modified added removed deleted unknown ignored clean'.split()
4799 show = [k for k in states if opts.get(k)]
4800 show = [k for k in states if opts.get(k)]
4800 if opts.get('all'):
4801 if opts.get('all'):
4801 show += ui.quiet and (states[:4] + ['clean']) or states
4802 show += ui.quiet and (states[:4] + ['clean']) or states
4802 if not show:
4803 if not show:
4803 if ui.quiet:
4804 if ui.quiet:
4804 show = states[:4]
4805 show = states[:4]
4805 else:
4806 else:
4806 show = states[:5]
4807 show = states[:5]
4807
4808
4808 m = scmutil.match(repo[node2], pats, opts)
4809 m = scmutil.match(repo[node2], pats, opts)
4809 stat = repo.status(node1, node2, m,
4810 stat = repo.status(node1, node2, m,
4810 'ignored' in show, 'clean' in show, 'unknown' in show,
4811 'ignored' in show, 'clean' in show, 'unknown' in show,
4811 opts.get('subrepos'))
4812 opts.get('subrepos'))
4812 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4813 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4813
4814
4814 if (opts.get('all') or opts.get('copies')
4815 if (opts.get('all') or opts.get('copies')
4815 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4816 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4816 copy = copies.pathcopies(repo[node1], repo[node2], m)
4817 copy = copies.pathcopies(repo[node1], repo[node2], m)
4817
4818
4818 ui.pager('status')
4819 ui.pager('status')
4819 fm = ui.formatter('status', opts)
4820 fm = ui.formatter('status', opts)
4820 fmt = '%s' + end
4821 fmt = '%s' + end
4821 showchar = not opts.get('no_status')
4822 showchar = not opts.get('no_status')
4822
4823
4823 for state, char, files in changestates:
4824 for state, char, files in changestates:
4824 if state in show:
4825 if state in show:
4825 label = 'status.' + state
4826 label = 'status.' + state
4826 for f in files:
4827 for f in files:
4827 fm.startitem()
4828 fm.startitem()
4828 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4829 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4829 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4830 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4830 if f in copy:
4831 if f in copy:
4831 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4832 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4832 label='status.copied')
4833 label='status.copied')
4833 fm.end()
4834 fm.end()
4834
4835
4835 @command('^summary|sum',
4836 @command('^summary|sum',
4836 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4837 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4837 def summary(ui, repo, **opts):
4838 def summary(ui, repo, **opts):
4838 """summarize working directory state
4839 """summarize working directory state
4839
4840
4840 This generates a brief summary of the working directory state,
4841 This generates a brief summary of the working directory state,
4841 including parents, branch, commit status, phase and available updates.
4842 including parents, branch, commit status, phase and available updates.
4842
4843
4843 With the --remote option, this will check the default paths for
4844 With the --remote option, this will check the default paths for
4844 incoming and outgoing changes. This can be time-consuming.
4845 incoming and outgoing changes. This can be time-consuming.
4845
4846
4846 Returns 0 on success.
4847 Returns 0 on success.
4847 """
4848 """
4848
4849
4849 opts = pycompat.byteskwargs(opts)
4850 opts = pycompat.byteskwargs(opts)
4850 ui.pager('summary')
4851 ui.pager('summary')
4851 ctx = repo[None]
4852 ctx = repo[None]
4852 parents = ctx.parents()
4853 parents = ctx.parents()
4853 pnode = parents[0].node()
4854 pnode = parents[0].node()
4854 marks = []
4855 marks = []
4855
4856
4856 ms = None
4857 ms = None
4857 try:
4858 try:
4858 ms = mergemod.mergestate.read(repo)
4859 ms = mergemod.mergestate.read(repo)
4859 except error.UnsupportedMergeRecords as e:
4860 except error.UnsupportedMergeRecords as e:
4860 s = ' '.join(e.recordtypes)
4861 s = ' '.join(e.recordtypes)
4861 ui.warn(
4862 ui.warn(
4862 _('warning: merge state has unsupported record types: %s\n') % s)
4863 _('warning: merge state has unsupported record types: %s\n') % s)
4863 unresolved = 0
4864 unresolved = 0
4864 else:
4865 else:
4865 unresolved = [f for f in ms if ms[f] == 'u']
4866 unresolved = [f for f in ms if ms[f] == 'u']
4866
4867
4867 for p in parents:
4868 for p in parents:
4868 # label with log.changeset (instead of log.parent) since this
4869 # label with log.changeset (instead of log.parent) since this
4869 # shows a working directory parent *changeset*:
4870 # shows a working directory parent *changeset*:
4870 # i18n: column positioning for "hg summary"
4871 # i18n: column positioning for "hg summary"
4871 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4872 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4872 label=cmdutil._changesetlabels(p))
4873 label=cmdutil._changesetlabels(p))
4873 ui.write(' '.join(p.tags()), label='log.tag')
4874 ui.write(' '.join(p.tags()), label='log.tag')
4874 if p.bookmarks():
4875 if p.bookmarks():
4875 marks.extend(p.bookmarks())
4876 marks.extend(p.bookmarks())
4876 if p.rev() == -1:
4877 if p.rev() == -1:
4877 if not len(repo):
4878 if not len(repo):
4878 ui.write(_(' (empty repository)'))
4879 ui.write(_(' (empty repository)'))
4879 else:
4880 else:
4880 ui.write(_(' (no revision checked out)'))
4881 ui.write(_(' (no revision checked out)'))
4881 if p.obsolete():
4882 if p.obsolete():
4882 ui.write(_(' (obsolete)'))
4883 ui.write(_(' (obsolete)'))
4883 if p.troubled():
4884 if p.troubled():
4884 ui.write(' ('
4885 ui.write(' ('
4885 + ', '.join(ui.label(trouble, 'trouble.%s' % trouble)
4886 + ', '.join(ui.label(trouble, 'trouble.%s' % trouble)
4886 for trouble in p.troubles())
4887 for trouble in p.troubles())
4887 + ')')
4888 + ')')
4888 ui.write('\n')
4889 ui.write('\n')
4889 if p.description():
4890 if p.description():
4890 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4891 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4891 label='log.summary')
4892 label='log.summary')
4892
4893
4893 branch = ctx.branch()
4894 branch = ctx.branch()
4894 bheads = repo.branchheads(branch)
4895 bheads = repo.branchheads(branch)
4895 # i18n: column positioning for "hg summary"
4896 # i18n: column positioning for "hg summary"
4896 m = _('branch: %s\n') % branch
4897 m = _('branch: %s\n') % branch
4897 if branch != 'default':
4898 if branch != 'default':
4898 ui.write(m, label='log.branch')
4899 ui.write(m, label='log.branch')
4899 else:
4900 else:
4900 ui.status(m, label='log.branch')
4901 ui.status(m, label='log.branch')
4901
4902
4902 if marks:
4903 if marks:
4903 active = repo._activebookmark
4904 active = repo._activebookmark
4904 # i18n: column positioning for "hg summary"
4905 # i18n: column positioning for "hg summary"
4905 ui.write(_('bookmarks:'), label='log.bookmark')
4906 ui.write(_('bookmarks:'), label='log.bookmark')
4906 if active is not None:
4907 if active is not None:
4907 if active in marks:
4908 if active in marks:
4908 ui.write(' *' + active, label=activebookmarklabel)
4909 ui.write(' *' + active, label=activebookmarklabel)
4909 marks.remove(active)
4910 marks.remove(active)
4910 else:
4911 else:
4911 ui.write(' [%s]' % active, label=activebookmarklabel)
4912 ui.write(' [%s]' % active, label=activebookmarklabel)
4912 for m in marks:
4913 for m in marks:
4913 ui.write(' ' + m, label='log.bookmark')
4914 ui.write(' ' + m, label='log.bookmark')
4914 ui.write('\n', label='log.bookmark')
4915 ui.write('\n', label='log.bookmark')
4915
4916
4916 status = repo.status(unknown=True)
4917 status = repo.status(unknown=True)
4917
4918
4918 c = repo.dirstate.copies()
4919 c = repo.dirstate.copies()
4919 copied, renamed = [], []
4920 copied, renamed = [], []
4920 for d, s in c.iteritems():
4921 for d, s in c.iteritems():
4921 if s in status.removed:
4922 if s in status.removed:
4922 status.removed.remove(s)
4923 status.removed.remove(s)
4923 renamed.append(d)
4924 renamed.append(d)
4924 else:
4925 else:
4925 copied.append(d)
4926 copied.append(d)
4926 if d in status.added:
4927 if d in status.added:
4927 status.added.remove(d)
4928 status.added.remove(d)
4928
4929
4929 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4930 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4930
4931
4931 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
4932 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
4932 (ui.label(_('%d added'), 'status.added'), status.added),
4933 (ui.label(_('%d added'), 'status.added'), status.added),
4933 (ui.label(_('%d removed'), 'status.removed'), status.removed),
4934 (ui.label(_('%d removed'), 'status.removed'), status.removed),
4934 (ui.label(_('%d renamed'), 'status.copied'), renamed),
4935 (ui.label(_('%d renamed'), 'status.copied'), renamed),
4935 (ui.label(_('%d copied'), 'status.copied'), copied),
4936 (ui.label(_('%d copied'), 'status.copied'), copied),
4936 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
4937 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
4937 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
4938 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
4938 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
4939 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
4939 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
4940 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
4940 t = []
4941 t = []
4941 for l, s in labels:
4942 for l, s in labels:
4942 if s:
4943 if s:
4943 t.append(l % len(s))
4944 t.append(l % len(s))
4944
4945
4945 t = ', '.join(t)
4946 t = ', '.join(t)
4946 cleanworkdir = False
4947 cleanworkdir = False
4947
4948
4948 if repo.vfs.exists('graftstate'):
4949 if repo.vfs.exists('graftstate'):
4949 t += _(' (graft in progress)')
4950 t += _(' (graft in progress)')
4950 if repo.vfs.exists('updatestate'):
4951 if repo.vfs.exists('updatestate'):
4951 t += _(' (interrupted update)')
4952 t += _(' (interrupted update)')
4952 elif len(parents) > 1:
4953 elif len(parents) > 1:
4953 t += _(' (merge)')
4954 t += _(' (merge)')
4954 elif branch != parents[0].branch():
4955 elif branch != parents[0].branch():
4955 t += _(' (new branch)')
4956 t += _(' (new branch)')
4956 elif (parents[0].closesbranch() and
4957 elif (parents[0].closesbranch() and
4957 pnode in repo.branchheads(branch, closed=True)):
4958 pnode in repo.branchheads(branch, closed=True)):
4958 t += _(' (head closed)')
4959 t += _(' (head closed)')
4959 elif not (status.modified or status.added or status.removed or renamed or
4960 elif not (status.modified or status.added or status.removed or renamed or
4960 copied or subs):
4961 copied or subs):
4961 t += _(' (clean)')
4962 t += _(' (clean)')
4962 cleanworkdir = True
4963 cleanworkdir = True
4963 elif pnode not in bheads:
4964 elif pnode not in bheads:
4964 t += _(' (new branch head)')
4965 t += _(' (new branch head)')
4965
4966
4966 if parents:
4967 if parents:
4967 pendingphase = max(p.phase() for p in parents)
4968 pendingphase = max(p.phase() for p in parents)
4968 else:
4969 else:
4969 pendingphase = phases.public
4970 pendingphase = phases.public
4970
4971
4971 if pendingphase > phases.newcommitphase(ui):
4972 if pendingphase > phases.newcommitphase(ui):
4972 t += ' (%s)' % phases.phasenames[pendingphase]
4973 t += ' (%s)' % phases.phasenames[pendingphase]
4973
4974
4974 if cleanworkdir:
4975 if cleanworkdir:
4975 # i18n: column positioning for "hg summary"
4976 # i18n: column positioning for "hg summary"
4976 ui.status(_('commit: %s\n') % t.strip())
4977 ui.status(_('commit: %s\n') % t.strip())
4977 else:
4978 else:
4978 # i18n: column positioning for "hg summary"
4979 # i18n: column positioning for "hg summary"
4979 ui.write(_('commit: %s\n') % t.strip())
4980 ui.write(_('commit: %s\n') % t.strip())
4980
4981
4981 # all ancestors of branch heads - all ancestors of parent = new csets
4982 # all ancestors of branch heads - all ancestors of parent = new csets
4982 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
4983 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
4983 bheads))
4984 bheads))
4984
4985
4985 if new == 0:
4986 if new == 0:
4986 # i18n: column positioning for "hg summary"
4987 # i18n: column positioning for "hg summary"
4987 ui.status(_('update: (current)\n'))
4988 ui.status(_('update: (current)\n'))
4988 elif pnode not in bheads:
4989 elif pnode not in bheads:
4989 # i18n: column positioning for "hg summary"
4990 # i18n: column positioning for "hg summary"
4990 ui.write(_('update: %d new changesets (update)\n') % new)
4991 ui.write(_('update: %d new changesets (update)\n') % new)
4991 else:
4992 else:
4992 # i18n: column positioning for "hg summary"
4993 # i18n: column positioning for "hg summary"
4993 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4994 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4994 (new, len(bheads)))
4995 (new, len(bheads)))
4995
4996
4996 t = []
4997 t = []
4997 draft = len(repo.revs('draft()'))
4998 draft = len(repo.revs('draft()'))
4998 if draft:
4999 if draft:
4999 t.append(_('%d draft') % draft)
5000 t.append(_('%d draft') % draft)
5000 secret = len(repo.revs('secret()'))
5001 secret = len(repo.revs('secret()'))
5001 if secret:
5002 if secret:
5002 t.append(_('%d secret') % secret)
5003 t.append(_('%d secret') % secret)
5003
5004
5004 if draft or secret:
5005 if draft or secret:
5005 ui.status(_('phases: %s\n') % ', '.join(t))
5006 ui.status(_('phases: %s\n') % ', '.join(t))
5006
5007
5007 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5008 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5008 for trouble in ("unstable", "divergent", "bumped"):
5009 for trouble in ("unstable", "divergent", "bumped"):
5009 numtrouble = len(repo.revs(trouble + "()"))
5010 numtrouble = len(repo.revs(trouble + "()"))
5010 # We write all the possibilities to ease translation
5011 # We write all the possibilities to ease translation
5011 troublemsg = {
5012 troublemsg = {
5012 "unstable": _("unstable: %d changesets"),
5013 "unstable": _("unstable: %d changesets"),
5013 "divergent": _("divergent: %d changesets"),
5014 "divergent": _("divergent: %d changesets"),
5014 "bumped": _("bumped: %d changesets"),
5015 "bumped": _("bumped: %d changesets"),
5015 }
5016 }
5016 if numtrouble > 0:
5017 if numtrouble > 0:
5017 ui.status(troublemsg[trouble] % numtrouble + "\n")
5018 ui.status(troublemsg[trouble] % numtrouble + "\n")
5018
5019
5019 cmdutil.summaryhooks(ui, repo)
5020 cmdutil.summaryhooks(ui, repo)
5020
5021
5021 if opts.get('remote'):
5022 if opts.get('remote'):
5022 needsincoming, needsoutgoing = True, True
5023 needsincoming, needsoutgoing = True, True
5023 else:
5024 else:
5024 needsincoming, needsoutgoing = False, False
5025 needsincoming, needsoutgoing = False, False
5025 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5026 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5026 if i:
5027 if i:
5027 needsincoming = True
5028 needsincoming = True
5028 if o:
5029 if o:
5029 needsoutgoing = True
5030 needsoutgoing = True
5030 if not needsincoming and not needsoutgoing:
5031 if not needsincoming and not needsoutgoing:
5031 return
5032 return
5032
5033
5033 def getincoming():
5034 def getincoming():
5034 source, branches = hg.parseurl(ui.expandpath('default'))
5035 source, branches = hg.parseurl(ui.expandpath('default'))
5035 sbranch = branches[0]
5036 sbranch = branches[0]
5036 try:
5037 try:
5037 other = hg.peer(repo, {}, source)
5038 other = hg.peer(repo, {}, source)
5038 except error.RepoError:
5039 except error.RepoError:
5039 if opts.get('remote'):
5040 if opts.get('remote'):
5040 raise
5041 raise
5041 return source, sbranch, None, None, None
5042 return source, sbranch, None, None, None
5042 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5043 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5043 if revs:
5044 if revs:
5044 revs = [other.lookup(rev) for rev in revs]
5045 revs = [other.lookup(rev) for rev in revs]
5045 ui.debug('comparing with %s\n' % util.hidepassword(source))
5046 ui.debug('comparing with %s\n' % util.hidepassword(source))
5046 repo.ui.pushbuffer()
5047 repo.ui.pushbuffer()
5047 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5048 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5048 repo.ui.popbuffer()
5049 repo.ui.popbuffer()
5049 return source, sbranch, other, commoninc, commoninc[1]
5050 return source, sbranch, other, commoninc, commoninc[1]
5050
5051
5051 if needsincoming:
5052 if needsincoming:
5052 source, sbranch, sother, commoninc, incoming = getincoming()
5053 source, sbranch, sother, commoninc, incoming = getincoming()
5053 else:
5054 else:
5054 source = sbranch = sother = commoninc = incoming = None
5055 source = sbranch = sother = commoninc = incoming = None
5055
5056
5056 def getoutgoing():
5057 def getoutgoing():
5057 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5058 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5058 dbranch = branches[0]
5059 dbranch = branches[0]
5059 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5060 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5060 if source != dest:
5061 if source != dest:
5061 try:
5062 try:
5062 dother = hg.peer(repo, {}, dest)
5063 dother = hg.peer(repo, {}, dest)
5063 except error.RepoError:
5064 except error.RepoError:
5064 if opts.get('remote'):
5065 if opts.get('remote'):
5065 raise
5066 raise
5066 return dest, dbranch, None, None
5067 return dest, dbranch, None, None
5067 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5068 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5068 elif sother is None:
5069 elif sother is None:
5069 # there is no explicit destination peer, but source one is invalid
5070 # there is no explicit destination peer, but source one is invalid
5070 return dest, dbranch, None, None
5071 return dest, dbranch, None, None
5071 else:
5072 else:
5072 dother = sother
5073 dother = sother
5073 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5074 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5074 common = None
5075 common = None
5075 else:
5076 else:
5076 common = commoninc
5077 common = commoninc
5077 if revs:
5078 if revs:
5078 revs = [repo.lookup(rev) for rev in revs]
5079 revs = [repo.lookup(rev) for rev in revs]
5079 repo.ui.pushbuffer()
5080 repo.ui.pushbuffer()
5080 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5081 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5081 commoninc=common)
5082 commoninc=common)
5082 repo.ui.popbuffer()
5083 repo.ui.popbuffer()
5083 return dest, dbranch, dother, outgoing
5084 return dest, dbranch, dother, outgoing
5084
5085
5085 if needsoutgoing:
5086 if needsoutgoing:
5086 dest, dbranch, dother, outgoing = getoutgoing()
5087 dest, dbranch, dother, outgoing = getoutgoing()
5087 else:
5088 else:
5088 dest = dbranch = dother = outgoing = None
5089 dest = dbranch = dother = outgoing = None
5089
5090
5090 if opts.get('remote'):
5091 if opts.get('remote'):
5091 t = []
5092 t = []
5092 if incoming:
5093 if incoming:
5093 t.append(_('1 or more incoming'))
5094 t.append(_('1 or more incoming'))
5094 o = outgoing.missing
5095 o = outgoing.missing
5095 if o:
5096 if o:
5096 t.append(_('%d outgoing') % len(o))
5097 t.append(_('%d outgoing') % len(o))
5097 other = dother or sother
5098 other = dother or sother
5098 if 'bookmarks' in other.listkeys('namespaces'):
5099 if 'bookmarks' in other.listkeys('namespaces'):
5099 counts = bookmarks.summary(repo, other)
5100 counts = bookmarks.summary(repo, other)
5100 if counts[0] > 0:
5101 if counts[0] > 0:
5101 t.append(_('%d incoming bookmarks') % counts[0])
5102 t.append(_('%d incoming bookmarks') % counts[0])
5102 if counts[1] > 0:
5103 if counts[1] > 0:
5103 t.append(_('%d outgoing bookmarks') % counts[1])
5104 t.append(_('%d outgoing bookmarks') % counts[1])
5104
5105
5105 if t:
5106 if t:
5106 # i18n: column positioning for "hg summary"
5107 # i18n: column positioning for "hg summary"
5107 ui.write(_('remote: %s\n') % (', '.join(t)))
5108 ui.write(_('remote: %s\n') % (', '.join(t)))
5108 else:
5109 else:
5109 # i18n: column positioning for "hg summary"
5110 # i18n: column positioning for "hg summary"
5110 ui.status(_('remote: (synced)\n'))
5111 ui.status(_('remote: (synced)\n'))
5111
5112
5112 cmdutil.summaryremotehooks(ui, repo, opts,
5113 cmdutil.summaryremotehooks(ui, repo, opts,
5113 ((source, sbranch, sother, commoninc),
5114 ((source, sbranch, sother, commoninc),
5114 (dest, dbranch, dother, outgoing)))
5115 (dest, dbranch, dother, outgoing)))
5115
5116
5116 @command('tag',
5117 @command('tag',
5117 [('f', 'force', None, _('force tag')),
5118 [('f', 'force', None, _('force tag')),
5118 ('l', 'local', None, _('make the tag local')),
5119 ('l', 'local', None, _('make the tag local')),
5119 ('r', 'rev', '', _('revision to tag'), _('REV')),
5120 ('r', 'rev', '', _('revision to tag'), _('REV')),
5120 ('', 'remove', None, _('remove a tag')),
5121 ('', 'remove', None, _('remove a tag')),
5121 # -l/--local is already there, commitopts cannot be used
5122 # -l/--local is already there, commitopts cannot be used
5122 ('e', 'edit', None, _('invoke editor on commit messages')),
5123 ('e', 'edit', None, _('invoke editor on commit messages')),
5123 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5124 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5124 ] + commitopts2,
5125 ] + commitopts2,
5125 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5126 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5126 def tag(ui, repo, name1, *names, **opts):
5127 def tag(ui, repo, name1, *names, **opts):
5127 """add one or more tags for the current or given revision
5128 """add one or more tags for the current or given revision
5128
5129
5129 Name a particular revision using <name>.
5130 Name a particular revision using <name>.
5130
5131
5131 Tags are used to name particular revisions of the repository and are
5132 Tags are used to name particular revisions of the repository and are
5132 very useful to compare different revisions, to go back to significant
5133 very useful to compare different revisions, to go back to significant
5133 earlier versions or to mark branch points as releases, etc. Changing
5134 earlier versions or to mark branch points as releases, etc. Changing
5134 an existing tag is normally disallowed; use -f/--force to override.
5135 an existing tag is normally disallowed; use -f/--force to override.
5135
5136
5136 If no revision is given, the parent of the working directory is
5137 If no revision is given, the parent of the working directory is
5137 used.
5138 used.
5138
5139
5139 To facilitate version control, distribution, and merging of tags,
5140 To facilitate version control, distribution, and merging of tags,
5140 they are stored as a file named ".hgtags" which is managed similarly
5141 they are stored as a file named ".hgtags" which is managed similarly
5141 to other project files and can be hand-edited if necessary. This
5142 to other project files and can be hand-edited if necessary. This
5142 also means that tagging creates a new commit. The file
5143 also means that tagging creates a new commit. The file
5143 ".hg/localtags" is used for local tags (not shared among
5144 ".hg/localtags" is used for local tags (not shared among
5144 repositories).
5145 repositories).
5145
5146
5146 Tag commits are usually made at the head of a branch. If the parent
5147 Tag commits are usually made at the head of a branch. If the parent
5147 of the working directory is not a branch head, :hg:`tag` aborts; use
5148 of the working directory is not a branch head, :hg:`tag` aborts; use
5148 -f/--force to force the tag commit to be based on a non-head
5149 -f/--force to force the tag commit to be based on a non-head
5149 changeset.
5150 changeset.
5150
5151
5151 See :hg:`help dates` for a list of formats valid for -d/--date.
5152 See :hg:`help dates` for a list of formats valid for -d/--date.
5152
5153
5153 Since tag names have priority over branch names during revision
5154 Since tag names have priority over branch names during revision
5154 lookup, using an existing branch name as a tag name is discouraged.
5155 lookup, using an existing branch name as a tag name is discouraged.
5155
5156
5156 Returns 0 on success.
5157 Returns 0 on success.
5157 """
5158 """
5158 opts = pycompat.byteskwargs(opts)
5159 opts = pycompat.byteskwargs(opts)
5159 wlock = lock = None
5160 wlock = lock = None
5160 try:
5161 try:
5161 wlock = repo.wlock()
5162 wlock = repo.wlock()
5162 lock = repo.lock()
5163 lock = repo.lock()
5163 rev_ = "."
5164 rev_ = "."
5164 names = [t.strip() for t in (name1,) + names]
5165 names = [t.strip() for t in (name1,) + names]
5165 if len(names) != len(set(names)):
5166 if len(names) != len(set(names)):
5166 raise error.Abort(_('tag names must be unique'))
5167 raise error.Abort(_('tag names must be unique'))
5167 for n in names:
5168 for n in names:
5168 scmutil.checknewlabel(repo, n, 'tag')
5169 scmutil.checknewlabel(repo, n, 'tag')
5169 if not n:
5170 if not n:
5170 raise error.Abort(_('tag names cannot consist entirely of '
5171 raise error.Abort(_('tag names cannot consist entirely of '
5171 'whitespace'))
5172 'whitespace'))
5172 if opts.get('rev') and opts.get('remove'):
5173 if opts.get('rev') and opts.get('remove'):
5173 raise error.Abort(_("--rev and --remove are incompatible"))
5174 raise error.Abort(_("--rev and --remove are incompatible"))
5174 if opts.get('rev'):
5175 if opts.get('rev'):
5175 rev_ = opts['rev']
5176 rev_ = opts['rev']
5176 message = opts.get('message')
5177 message = opts.get('message')
5177 if opts.get('remove'):
5178 if opts.get('remove'):
5178 if opts.get('local'):
5179 if opts.get('local'):
5179 expectedtype = 'local'
5180 expectedtype = 'local'
5180 else:
5181 else:
5181 expectedtype = 'global'
5182 expectedtype = 'global'
5182
5183
5183 for n in names:
5184 for n in names:
5184 if not repo.tagtype(n):
5185 if not repo.tagtype(n):
5185 raise error.Abort(_("tag '%s' does not exist") % n)
5186 raise error.Abort(_("tag '%s' does not exist") % n)
5186 if repo.tagtype(n) != expectedtype:
5187 if repo.tagtype(n) != expectedtype:
5187 if expectedtype == 'global':
5188 if expectedtype == 'global':
5188 raise error.Abort(_("tag '%s' is not a global tag") % n)
5189 raise error.Abort(_("tag '%s' is not a global tag") % n)
5189 else:
5190 else:
5190 raise error.Abort(_("tag '%s' is not a local tag") % n)
5191 raise error.Abort(_("tag '%s' is not a local tag") % n)
5191 rev_ = 'null'
5192 rev_ = 'null'
5192 if not message:
5193 if not message:
5193 # we don't translate commit messages
5194 # we don't translate commit messages
5194 message = 'Removed tag %s' % ', '.join(names)
5195 message = 'Removed tag %s' % ', '.join(names)
5195 elif not opts.get('force'):
5196 elif not opts.get('force'):
5196 for n in names:
5197 for n in names:
5197 if n in repo.tags():
5198 if n in repo.tags():
5198 raise error.Abort(_("tag '%s' already exists "
5199 raise error.Abort(_("tag '%s' already exists "
5199 "(use -f to force)") % n)
5200 "(use -f to force)") % n)
5200 if not opts.get('local'):
5201 if not opts.get('local'):
5201 p1, p2 = repo.dirstate.parents()
5202 p1, p2 = repo.dirstate.parents()
5202 if p2 != nullid:
5203 if p2 != nullid:
5203 raise error.Abort(_('uncommitted merge'))
5204 raise error.Abort(_('uncommitted merge'))
5204 bheads = repo.branchheads()
5205 bheads = repo.branchheads()
5205 if not opts.get('force') and bheads and p1 not in bheads:
5206 if not opts.get('force') and bheads and p1 not in bheads:
5206 raise error.Abort(_('working directory is not at a branch head '
5207 raise error.Abort(_('working directory is not at a branch head '
5207 '(use -f to force)'))
5208 '(use -f to force)'))
5208 r = scmutil.revsingle(repo, rev_).node()
5209 r = scmutil.revsingle(repo, rev_).node()
5209
5210
5210 if not message:
5211 if not message:
5211 # we don't translate commit messages
5212 # we don't translate commit messages
5212 message = ('Added tag %s for changeset %s' %
5213 message = ('Added tag %s for changeset %s' %
5213 (', '.join(names), short(r)))
5214 (', '.join(names), short(r)))
5214
5215
5215 date = opts.get('date')
5216 date = opts.get('date')
5216 if date:
5217 if date:
5217 date = util.parsedate(date)
5218 date = util.parsedate(date)
5218
5219
5219 if opts.get('remove'):
5220 if opts.get('remove'):
5220 editform = 'tag.remove'
5221 editform = 'tag.remove'
5221 else:
5222 else:
5222 editform = 'tag.add'
5223 editform = 'tag.add'
5223 editor = cmdutil.getcommiteditor(editform=editform,
5224 editor = cmdutil.getcommiteditor(editform=editform,
5224 **pycompat.strkwargs(opts))
5225 **pycompat.strkwargs(opts))
5225
5226
5226 # don't allow tagging the null rev
5227 # don't allow tagging the null rev
5227 if (not opts.get('remove') and
5228 if (not opts.get('remove') and
5228 scmutil.revsingle(repo, rev_).rev() == nullrev):
5229 scmutil.revsingle(repo, rev_).rev() == nullrev):
5229 raise error.Abort(_("cannot tag null revision"))
5230 raise error.Abort(_("cannot tag null revision"))
5230
5231
5231 tagsmod.tag(repo, names, r, message, opts.get('local'),
5232 tagsmod.tag(repo, names, r, message, opts.get('local'),
5232 opts.get('user'), date, editor=editor)
5233 opts.get('user'), date, editor=editor)
5233 finally:
5234 finally:
5234 release(lock, wlock)
5235 release(lock, wlock)
5235
5236
5236 @command('tags', formatteropts, '')
5237 @command('tags', formatteropts, '')
5237 def tags(ui, repo, **opts):
5238 def tags(ui, repo, **opts):
5238 """list repository tags
5239 """list repository tags
5239
5240
5240 This lists both regular and local tags. When the -v/--verbose
5241 This lists both regular and local tags. When the -v/--verbose
5241 switch is used, a third column "local" is printed for local tags.
5242 switch is used, a third column "local" is printed for local tags.
5242 When the -q/--quiet switch is used, only the tag name is printed.
5243 When the -q/--quiet switch is used, only the tag name is printed.
5243
5244
5244 Returns 0 on success.
5245 Returns 0 on success.
5245 """
5246 """
5246
5247
5247 opts = pycompat.byteskwargs(opts)
5248 opts = pycompat.byteskwargs(opts)
5248 ui.pager('tags')
5249 ui.pager('tags')
5249 fm = ui.formatter('tags', opts)
5250 fm = ui.formatter('tags', opts)
5250 hexfunc = fm.hexfunc
5251 hexfunc = fm.hexfunc
5251 tagtype = ""
5252 tagtype = ""
5252
5253
5253 for t, n in reversed(repo.tagslist()):
5254 for t, n in reversed(repo.tagslist()):
5254 hn = hexfunc(n)
5255 hn = hexfunc(n)
5255 label = 'tags.normal'
5256 label = 'tags.normal'
5256 tagtype = ''
5257 tagtype = ''
5257 if repo.tagtype(t) == 'local':
5258 if repo.tagtype(t) == 'local':
5258 label = 'tags.local'
5259 label = 'tags.local'
5259 tagtype = 'local'
5260 tagtype = 'local'
5260
5261
5261 fm.startitem()
5262 fm.startitem()
5262 fm.write('tag', '%s', t, label=label)
5263 fm.write('tag', '%s', t, label=label)
5263 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5264 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5264 fm.condwrite(not ui.quiet, 'rev node', fmt,
5265 fm.condwrite(not ui.quiet, 'rev node', fmt,
5265 repo.changelog.rev(n), hn, label=label)
5266 repo.changelog.rev(n), hn, label=label)
5266 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5267 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5267 tagtype, label=label)
5268 tagtype, label=label)
5268 fm.plain('\n')
5269 fm.plain('\n')
5269 fm.end()
5270 fm.end()
5270
5271
5271 @command('tip',
5272 @command('tip',
5272 [('p', 'patch', None, _('show patch')),
5273 [('p', 'patch', None, _('show patch')),
5273 ('g', 'git', None, _('use git extended diff format')),
5274 ('g', 'git', None, _('use git extended diff format')),
5274 ] + templateopts,
5275 ] + templateopts,
5275 _('[-p] [-g]'))
5276 _('[-p] [-g]'))
5276 def tip(ui, repo, **opts):
5277 def tip(ui, repo, **opts):
5277 """show the tip revision (DEPRECATED)
5278 """show the tip revision (DEPRECATED)
5278
5279
5279 The tip revision (usually just called the tip) is the changeset
5280 The tip revision (usually just called the tip) is the changeset
5280 most recently added to the repository (and therefore the most
5281 most recently added to the repository (and therefore the most
5281 recently changed head).
5282 recently changed head).
5282
5283
5283 If you have just made a commit, that commit will be the tip. If
5284 If you have just made a commit, that commit will be the tip. If
5284 you have just pulled changes from another repository, the tip of
5285 you have just pulled changes from another repository, the tip of
5285 that repository becomes the current tip. The "tip" tag is special
5286 that repository becomes the current tip. The "tip" tag is special
5286 and cannot be renamed or assigned to a different changeset.
5287 and cannot be renamed or assigned to a different changeset.
5287
5288
5288 This command is deprecated, please use :hg:`heads` instead.
5289 This command is deprecated, please use :hg:`heads` instead.
5289
5290
5290 Returns 0 on success.
5291 Returns 0 on success.
5291 """
5292 """
5292 opts = pycompat.byteskwargs(opts)
5293 opts = pycompat.byteskwargs(opts)
5293 displayer = cmdutil.show_changeset(ui, repo, opts)
5294 displayer = cmdutil.show_changeset(ui, repo, opts)
5294 displayer.show(repo['tip'])
5295 displayer.show(repo['tip'])
5295 displayer.close()
5296 displayer.close()
5296
5297
5297 @command('unbundle',
5298 @command('unbundle',
5298 [('u', 'update', None,
5299 [('u', 'update', None,
5299 _('update to new branch head if changesets were unbundled'))],
5300 _('update to new branch head if changesets were unbundled'))],
5300 _('[-u] FILE...'))
5301 _('[-u] FILE...'))
5301 def unbundle(ui, repo, fname1, *fnames, **opts):
5302 def unbundle(ui, repo, fname1, *fnames, **opts):
5302 """apply one or more bundle files
5303 """apply one or more bundle files
5303
5304
5304 Apply one or more bundle files generated by :hg:`bundle`.
5305 Apply one or more bundle files generated by :hg:`bundle`.
5305
5306
5306 Returns 0 on success, 1 if an update has unresolved files.
5307 Returns 0 on success, 1 if an update has unresolved files.
5307 """
5308 """
5308 fnames = (fname1,) + fnames
5309 fnames = (fname1,) + fnames
5309
5310
5310 with repo.lock():
5311 with repo.lock():
5311 for fname in fnames:
5312 for fname in fnames:
5312 f = hg.openpath(ui, fname)
5313 f = hg.openpath(ui, fname)
5313 gen = exchange.readbundle(ui, f, fname)
5314 gen = exchange.readbundle(ui, f, fname)
5314 if isinstance(gen, bundle2.unbundle20):
5315 if isinstance(gen, bundle2.unbundle20):
5315 tr = repo.transaction('unbundle')
5316 tr = repo.transaction('unbundle')
5316 try:
5317 try:
5317 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5318 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5318 url='bundle:' + fname)
5319 url='bundle:' + fname)
5319 tr.close()
5320 tr.close()
5320 except error.BundleUnknownFeatureError as exc:
5321 except error.BundleUnknownFeatureError as exc:
5321 raise error.Abort(_('%s: unknown bundle feature, %s')
5322 raise error.Abort(_('%s: unknown bundle feature, %s')
5322 % (fname, exc),
5323 % (fname, exc),
5323 hint=_("see https://mercurial-scm.org/"
5324 hint=_("see https://mercurial-scm.org/"
5324 "wiki/BundleFeature for more "
5325 "wiki/BundleFeature for more "
5325 "information"))
5326 "information"))
5326 finally:
5327 finally:
5327 if tr:
5328 if tr:
5328 tr.release()
5329 tr.release()
5329 changes = [r.get('return', 0)
5330 changes = [r.get('return', 0)
5330 for r in op.records['changegroup']]
5331 for r in op.records['changegroup']]
5331 modheads = changegroup.combineresults(changes)
5332 modheads = changegroup.combineresults(changes)
5332 elif isinstance(gen, streamclone.streamcloneapplier):
5333 elif isinstance(gen, streamclone.streamcloneapplier):
5333 raise error.Abort(
5334 raise error.Abort(
5334 _('packed bundles cannot be applied with '
5335 _('packed bundles cannot be applied with '
5335 '"hg unbundle"'),
5336 '"hg unbundle"'),
5336 hint=_('use "hg debugapplystreamclonebundle"'))
5337 hint=_('use "hg debugapplystreamclonebundle"'))
5337 else:
5338 else:
5338 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
5339 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
5339
5340
5340 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5341 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5341
5342
5342 @command('^update|up|checkout|co',
5343 @command('^update|up|checkout|co',
5343 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5344 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5344 ('c', 'check', None, _('require clean working directory')),
5345 ('c', 'check', None, _('require clean working directory')),
5345 ('m', 'merge', None, _('merge uncommitted changes')),
5346 ('m', 'merge', None, _('merge uncommitted changes')),
5346 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5347 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5347 ('r', 'rev', '', _('revision'), _('REV'))
5348 ('r', 'rev', '', _('revision'), _('REV'))
5348 ] + mergetoolopts,
5349 ] + mergetoolopts,
5349 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5350 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5350 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5351 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5351 merge=None, tool=None):
5352 merge=None, tool=None):
5352 """update working directory (or switch revisions)
5353 """update working directory (or switch revisions)
5353
5354
5354 Update the repository's working directory to the specified
5355 Update the repository's working directory to the specified
5355 changeset. If no changeset is specified, update to the tip of the
5356 changeset. If no changeset is specified, update to the tip of the
5356 current named branch and move the active bookmark (see :hg:`help
5357 current named branch and move the active bookmark (see :hg:`help
5357 bookmarks`).
5358 bookmarks`).
5358
5359
5359 Update sets the working directory's parent revision to the specified
5360 Update sets the working directory's parent revision to the specified
5360 changeset (see :hg:`help parents`).
5361 changeset (see :hg:`help parents`).
5361
5362
5362 If the changeset is not a descendant or ancestor of the working
5363 If the changeset is not a descendant or ancestor of the working
5363 directory's parent and there are uncommitted changes, the update is
5364 directory's parent and there are uncommitted changes, the update is
5364 aborted. With the -c/--check option, the working directory is checked
5365 aborted. With the -c/--check option, the working directory is checked
5365 for uncommitted changes; if none are found, the working directory is
5366 for uncommitted changes; if none are found, the working directory is
5366 updated to the specified changeset.
5367 updated to the specified changeset.
5367
5368
5368 .. container:: verbose
5369 .. container:: verbose
5369
5370
5370 The -C/--clean, -c/--check, and -m/--merge options control what
5371 The -C/--clean, -c/--check, and -m/--merge options control what
5371 happens if the working directory contains uncommitted changes.
5372 happens if the working directory contains uncommitted changes.
5372 At most of one of them can be specified.
5373 At most of one of them can be specified.
5373
5374
5374 1. If no option is specified, and if
5375 1. If no option is specified, and if
5375 the requested changeset is an ancestor or descendant of
5376 the requested changeset is an ancestor or descendant of
5376 the working directory's parent, the uncommitted changes
5377 the working directory's parent, the uncommitted changes
5377 are merged into the requested changeset and the merged
5378 are merged into the requested changeset and the merged
5378 result is left uncommitted. If the requested changeset is
5379 result is left uncommitted. If the requested changeset is
5379 not an ancestor or descendant (that is, it is on another
5380 not an ancestor or descendant (that is, it is on another
5380 branch), the update is aborted and the uncommitted changes
5381 branch), the update is aborted and the uncommitted changes
5381 are preserved.
5382 are preserved.
5382
5383
5383 2. With the -m/--merge option, the update is allowed even if the
5384 2. With the -m/--merge option, the update is allowed even if the
5384 requested changeset is not an ancestor or descendant of
5385 requested changeset is not an ancestor or descendant of
5385 the working directory's parent.
5386 the working directory's parent.
5386
5387
5387 3. With the -c/--check option, the update is aborted and the
5388 3. With the -c/--check option, the update is aborted and the
5388 uncommitted changes are preserved.
5389 uncommitted changes are preserved.
5389
5390
5390 4. With the -C/--clean option, uncommitted changes are discarded and
5391 4. With the -C/--clean option, uncommitted changes are discarded and
5391 the working directory is updated to the requested changeset.
5392 the working directory is updated to the requested changeset.
5392
5393
5393 To cancel an uncommitted merge (and lose your changes), use
5394 To cancel an uncommitted merge (and lose your changes), use
5394 :hg:`update --clean .`.
5395 :hg:`update --clean .`.
5395
5396
5396 Use null as the changeset to remove the working directory (like
5397 Use null as the changeset to remove the working directory (like
5397 :hg:`clone -U`).
5398 :hg:`clone -U`).
5398
5399
5399 If you want to revert just one file to an older revision, use
5400 If you want to revert just one file to an older revision, use
5400 :hg:`revert [-r REV] NAME`.
5401 :hg:`revert [-r REV] NAME`.
5401
5402
5402 See :hg:`help dates` for a list of formats valid for -d/--date.
5403 See :hg:`help dates` for a list of formats valid for -d/--date.
5403
5404
5404 Returns 0 on success, 1 if there are unresolved files.
5405 Returns 0 on success, 1 if there are unresolved files.
5405 """
5406 """
5406 if rev and node:
5407 if rev and node:
5407 raise error.Abort(_("please specify just one revision"))
5408 raise error.Abort(_("please specify just one revision"))
5408
5409
5409 if ui.configbool('commands', 'update.requiredest'):
5410 if ui.configbool('commands', 'update.requiredest'):
5410 if not node and not rev and not date:
5411 if not node and not rev and not date:
5411 raise error.Abort(_('you must specify a destination'),
5412 raise error.Abort(_('you must specify a destination'),
5412 hint=_('for example: hg update ".::"'))
5413 hint=_('for example: hg update ".::"'))
5413
5414
5414 if rev is None or rev == '':
5415 if rev is None or rev == '':
5415 rev = node
5416 rev = node
5416
5417
5417 if date and rev is not None:
5418 if date and rev is not None:
5418 raise error.Abort(_("you can't specify a revision and a date"))
5419 raise error.Abort(_("you can't specify a revision and a date"))
5419
5420
5420 if len([x for x in (clean, check, merge) if x]) > 1:
5421 if len([x for x in (clean, check, merge) if x]) > 1:
5421 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5422 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5422 "or -m/merge"))
5423 "or -m/merge"))
5423
5424
5424 updatecheck = None
5425 updatecheck = None
5425 if check:
5426 if check:
5426 updatecheck = 'abort'
5427 updatecheck = 'abort'
5427 elif merge:
5428 elif merge:
5428 updatecheck = 'none'
5429 updatecheck = 'none'
5429
5430
5430 with repo.wlock():
5431 with repo.wlock():
5431 cmdutil.clearunfinished(repo)
5432 cmdutil.clearunfinished(repo)
5432
5433
5433 if date:
5434 if date:
5434 rev = cmdutil.finddate(ui, repo, date)
5435 rev = cmdutil.finddate(ui, repo, date)
5435
5436
5436 # if we defined a bookmark, we have to remember the original name
5437 # if we defined a bookmark, we have to remember the original name
5437 brev = rev
5438 brev = rev
5438 rev = scmutil.revsingle(repo, rev, rev).rev()
5439 rev = scmutil.revsingle(repo, rev, rev).rev()
5439
5440
5440 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5441 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5441
5442
5442 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5443 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5443 updatecheck=updatecheck)
5444 updatecheck=updatecheck)
5444
5445
5445 @command('verify', [])
5446 @command('verify', [])
5446 def verify(ui, repo):
5447 def verify(ui, repo):
5447 """verify the integrity of the repository
5448 """verify the integrity of the repository
5448
5449
5449 Verify the integrity of the current repository.
5450 Verify the integrity of the current repository.
5450
5451
5451 This will perform an extensive check of the repository's
5452 This will perform an extensive check of the repository's
5452 integrity, validating the hashes and checksums of each entry in
5453 integrity, validating the hashes and checksums of each entry in
5453 the changelog, manifest, and tracked files, as well as the
5454 the changelog, manifest, and tracked files, as well as the
5454 integrity of their crosslinks and indices.
5455 integrity of their crosslinks and indices.
5455
5456
5456 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5457 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5457 for more information about recovery from corruption of the
5458 for more information about recovery from corruption of the
5458 repository.
5459 repository.
5459
5460
5460 Returns 0 on success, 1 if errors are encountered.
5461 Returns 0 on success, 1 if errors are encountered.
5461 """
5462 """
5462 return hg.verify(repo)
5463 return hg.verify(repo)
5463
5464
5464 @command('version', [] + formatteropts, norepo=True)
5465 @command('version', [] + formatteropts, norepo=True)
5465 def version_(ui, **opts):
5466 def version_(ui, **opts):
5466 """output version and copyright information"""
5467 """output version and copyright information"""
5467 opts = pycompat.byteskwargs(opts)
5468 opts = pycompat.byteskwargs(opts)
5468 if ui.verbose:
5469 if ui.verbose:
5469 ui.pager('version')
5470 ui.pager('version')
5470 fm = ui.formatter("version", opts)
5471 fm = ui.formatter("version", opts)
5471 fm.startitem()
5472 fm.startitem()
5472 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5473 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5473 util.version())
5474 util.version())
5474 license = _(
5475 license = _(
5475 "(see https://mercurial-scm.org for more information)\n"
5476 "(see https://mercurial-scm.org for more information)\n"
5476 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5477 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5477 "This is free software; see the source for copying conditions. "
5478 "This is free software; see the source for copying conditions. "
5478 "There is NO\nwarranty; "
5479 "There is NO\nwarranty; "
5479 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5480 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5480 )
5481 )
5481 if not ui.quiet:
5482 if not ui.quiet:
5482 fm.plain(license)
5483 fm.plain(license)
5483
5484
5484 if ui.verbose:
5485 if ui.verbose:
5485 fm.plain(_("\nEnabled extensions:\n\n"))
5486 fm.plain(_("\nEnabled extensions:\n\n"))
5486 # format names and versions into columns
5487 # format names and versions into columns
5487 names = []
5488 names = []
5488 vers = []
5489 vers = []
5489 isinternals = []
5490 isinternals = []
5490 for name, module in extensions.extensions():
5491 for name, module in extensions.extensions():
5491 names.append(name)
5492 names.append(name)
5492 vers.append(extensions.moduleversion(module) or None)
5493 vers.append(extensions.moduleversion(module) or None)
5493 isinternals.append(extensions.ismoduleinternal(module))
5494 isinternals.append(extensions.ismoduleinternal(module))
5494 fn = fm.nested("extensions")
5495 fn = fm.nested("extensions")
5495 if names:
5496 if names:
5496 namefmt = " %%-%ds " % max(len(n) for n in names)
5497 namefmt = " %%-%ds " % max(len(n) for n in names)
5497 places = [_("external"), _("internal")]
5498 places = [_("external"), _("internal")]
5498 for n, v, p in zip(names, vers, isinternals):
5499 for n, v, p in zip(names, vers, isinternals):
5499 fn.startitem()
5500 fn.startitem()
5500 fn.condwrite(ui.verbose, "name", namefmt, n)
5501 fn.condwrite(ui.verbose, "name", namefmt, n)
5501 if ui.verbose:
5502 if ui.verbose:
5502 fn.plain("%s " % places[p])
5503 fn.plain("%s " % places[p])
5503 fn.data(bundled=p)
5504 fn.data(bundled=p)
5504 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5505 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5505 if ui.verbose:
5506 if ui.verbose:
5506 fn.plain("\n")
5507 fn.plain("\n")
5507 fn.end()
5508 fn.end()
5508 fm.end()
5509 fm.end()
5509
5510
5510 def loadcmdtable(ui, name, cmdtable):
5511 def loadcmdtable(ui, name, cmdtable):
5511 """Load command functions from specified cmdtable
5512 """Load command functions from specified cmdtable
5512 """
5513 """
5513 overrides = [cmd for cmd in cmdtable if cmd in table]
5514 overrides = [cmd for cmd in cmdtable if cmd in table]
5514 if overrides:
5515 if overrides:
5515 ui.warn(_("extension '%s' overrides commands: %s\n")
5516 ui.warn(_("extension '%s' overrides commands: %s\n")
5516 % (name, " ".join(overrides)))
5517 % (name, " ".join(overrides)))
5517 table.update(cmdtable)
5518 table.update(cmdtable)
General Comments 0
You need to be logged in to leave comments. Login now